Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ban system #1784

Merged
merged 3 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,11 @@
"alreadyEliminated": "You've already been eliminated this round.",
"bookmarkOutsideGame": "You can only bookmark songs during a game.",
"eliminated": "You've been eliminated this round.",
"guildBanned": "This server is banned from using KMQ. For appeals, contact the official KMQ support server at {{supportServer}}.",
"guildOnly": "KMQ can only be used in servers.",
"invalidBookmark": "You can only bookmark songs recently played in the last {{BOOKMARK_MESSAGE_SIZE}} rounds. You must bookmark the message sent by the bot containing the song.",
"optionFromPreviousRound": "You are attempting to pick an option from an already completed round."
"optionFromPreviousRound": "You are attempting to pick an option from an already completed round.",
"playerBanned": "You are banned from using KMQ. For appeals, contact the official KMQ support server at {{supportServer}}."
},
"maintenanceMode": {
"description": "KMQ is currently under maintenance, and new games cannot be started. Check back later!",
Expand Down
4 changes: 3 additions & 1 deletion i18n/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,11 @@
"alreadyEliminated": "Ya has sido eliminado en esta ronda.",
"bookmarkOutsideGame": "Solo puedes marcar canciones durante un juego.",
"eliminated": "Has sido eliminado en esta ronda.",
"guildBanned": "Este servidor está prohibido de usar KMQ. Para apelaciones, contacta al servidor de soporte oficial de KMQ en {{supportServer}}.",
"guildOnly": "KMQ sólo puede ser utilizado en servidores.",
"invalidBookmark": "Solo puedes marcar canciones que hayan sido reproducidas en las últimas {{BOOKMARK_MESSAGE_SIZE}} rondas. Debes marcar el mensaje enviado por el bot que contiene la canción.",
"optionFromPreviousRound": "Estás intentando elegir una opción de una ronda que ya ha sido completada."
"optionFromPreviousRound": "Estás intentando elegir una opción de una ronda que ya ha sido completada.",
"playerBanned": "Estás prohibido de usar KMQ. Para apelaciones, contacta al servidor de soporte oficial de KMQ en {{supportServer}}."
},
"maintenanceMode": {
"description": "KMQ está actualmente en mantenimiento y no se pueden iniciar nuevos juegos. ¡Vuelve más tarde!",
Expand Down
4 changes: 3 additions & 1 deletion i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,11 @@
"alreadyEliminated": "Vous avez déjà été éliminé(e) ce tour.",
"bookmarkOutsideGame": "Vous ne pouvez marquer des chansons que pendant une partie.",
"eliminated": "Vous avez été éliminé(e) ce tour.",
"guildBanned": "Ce serveur est banni de l'utilisation de KMQ. Pour faire appel, contactez le serveur de support officiel de KMQ à {{supportServer}}.",
"guildOnly": "KMQ ne peut être utilisé que dans les serveurs.",
"invalidBookmark": "Vous ne pouvez marquer que les chansons récemment jouées dans les {{BOOKMARK_MESSAGE_SIZE}} derniers tours. Vous devez marquer le message envoyé par le bot contenant la chanson.",
"optionFromPreviousRound": "Vous essayez de choisir une option d'un tour déjà terminé."
"optionFromPreviousRound": "Vous essayez de choisir une option d'un tour déjà terminé.",
"playerBanned": "Vous êtes banni de l'utilisation de KMQ. Pour faire appel, contactez le serveur de support officiel de KMQ à {{supportServer}}."
},
"maintenanceMode": {
"description": "KMQ est actuellement en maintenance, et de nouvelles parties ne peuvent pas être lancées. Revenez plus tard!",
Expand Down
4 changes: 3 additions & 1 deletion i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,11 @@
"alreadyEliminated": "あなたはすでにこのラウンドで敗退しています。",
"bookmarkOutsideGame": "ゲーム中には曲をブックマークできません。",
"eliminated": "あなたはこのラウンドで敗退しました。",
"guildBanned": "このサーバーはKMQの使用が禁止されています。異議申し立ては、{{supportServer}}の公式KMQサポートサーバーまでご連絡ください。",
"guildOnly": "KMQはサーバーでのみ使用できます。",
"invalidBookmark": "最近の{{BOOKMARK_MESSAGE_SIZE}}ラウンドで再生された曲のみブックマークできます。ボットから送信されたメッセージに含まれる曲をブックマークする必要があります。",
"optionFromPreviousRound": "すでに完了したラウンドからオプションを選択しようとしています。"
"optionFromPreviousRound": "すでに完了したラウンドからオプションを選択しようとしています。",
"playerBanned": "あなたはKMQの使用が禁止されています。異議申し立ては、{{supportServer}}の公式KMQサポートサーバーまでご連絡ください。"
},
"maintenanceMode": {
"description": "KMQは現在メンテナンス中であり、新しいゲームを開始することはできません。後で再度確認してください!",
Expand Down
4 changes: 3 additions & 1 deletion i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,11 @@
"alreadyEliminated": "당신은 이미 이번 라운드에서 탈락했습니다.",
"bookmarkOutsideGame": "게임 중에만 노래를 북마크할 수 있습니다.",
"eliminated": "당신은 이번 라운드에서 탈락했습니다.",
"guildBanned": "이 서버는 KMQ 사용이 금지되었습니다. 항소하려면 {{supportServer}}의 공식 KMQ 지원 서버에 연락하세요.",
"guildOnly": "KMQ는 서버에서만 사용할 수 있습니다.",
"invalidBookmark": "최근 {{BOOKMARK_MESSAGE_SIZE}} 라운드에서 재생한 노래만 북마크에 추가할 수 있습니다. 노래가 포함된 봇이 보낸 메시지를 북마크해야 합니다.",
"optionFromPreviousRound": "이미 완료된 라운드에서 옵션을 선택하려고 합니다."
"optionFromPreviousRound": "이미 완료된 라운드에서 옵션을 선택하려고 합니다.",
"playerBanned": "당신은 KMQ 사용이 금지되었습니다. 항소하려면 {{supportServer}}의 공식 KMQ 지원 서버에 연락하세요."
},
"maintenanceMode": {
"description": "KMQ는 현재 점검 중이며 새 게임을 시작할 수 없습니다. 나중에 다시 확인하세요!",
Expand Down
4 changes: 3 additions & 1 deletion i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,11 @@
"alreadyEliminated": "你已经在本轮比赛中被淘汰了。",
"bookmarkOutsideGame": "你只能在游戏期间收藏歌曲。",
"eliminated": "你已经在本轮比赛中被淘汰了。",
"guildBanned": "此服务器被禁止使用KMQ。如需申诉,请联系{{supportServer}}的官方KMQ支持服务器。",
"guildOnly": "KMQ只能在服务器中使用。",
"invalidBookmark": "你只能收藏最近播放的{{BOOKMARK_MESSAGE_SIZE}}轮歌曲。你必须收藏机器人发送的包含歌曲的消息。",
"optionFromPreviousRound": "您正在尝试从已经完成的回合中选择选项。"
"optionFromPreviousRound": "您正在尝试从已经完成的回合中选择选项。",
"playerBanned": "您被禁止使用KMQ。如需申诉,请联系{{supportServer}}的官方KMQ支持服务器。"
},
"maintenanceMode": {
"description": "KMQ目前正在维护中,无法启动新游戏。请稍后再来!",
Expand Down
58 changes: 58 additions & 0 deletions src/events/client/interactionCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,64 @@ export default async function interactionCreateHandler(
return;
}

if (State.bannedServers.has(interaction.guildID)) {
logger.warn(
`Banned server attempted to execute interaction. id = ${interaction.guildID}`,
);

if (
interaction instanceof Eris.ComponentInteraction ||
interaction instanceof Eris.CommandInteraction
) {
tryCreateInteractionErrorAcknowledgement(
interaction,
i18n.translate(
interaction.guildID,
"misc.interaction.title.failure",
),
i18n.translate(
interaction.guildID,
"misc.failure.interaction.guildBanned",
{
supportServer: "https://discord.gg/RCuzwYV",
},
),
);
}

return;
}

if (State.bannedPlayers.has(interaction.member!.id)) {
logger.warn(
`Banned player attempted to execute interaction. id = ${
interaction.member!.id
}`,
);

if (
interaction instanceof Eris.ComponentInteraction ||
interaction instanceof Eris.CommandInteraction
) {
tryCreateInteractionErrorAcknowledgement(
interaction,
i18n.translate(
interaction.guildID,
"misc.interaction.title.failure",
),
i18n.translate(
interaction.guildID,
"misc.failure.interaction.playerBanned",
{
supportServer: "https://discord.gg/RCuzwYV",
},
),
);
}

return;
}

const member = new KmqMember(interaction.member!.id);
const messageContext = new MessageContext(
interaction.channel.id,
Expand Down
42 changes: 42 additions & 0 deletions src/events/client/messageCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,48 @@ export default async function messageCreateHandler(

const session = Session.getSession(message.guildID);
if (parsedMessage && invokedCommand) {
if (State.bannedServers.has(message.guildID)) {
logger.warn(
`Banned server attempted to execute command. id = ${message.guildID}`,
);

await sendErrorMessage(messageContext, {
title: i18n.translate(
messageContext.guildID,
"misc.interaction.title.failure",
),
description: i18n.translate(
messageContext.guildID,
"misc.failure.interaction.guildBanned",
{
supportServer: "https://discord.gg/RCuzwYV",
},
),
});
return;
}

if (State.bannedPlayers.has(message.author.id)) {
logger.warn(
`Banned player attempted to execute command. id = ${message.author.id}`,
);

await sendErrorMessage(messageContext, {
title: i18n.translate(
messageContext.guildID,
"misc.interaction.title.failure",
),
description: i18n.translate(
messageContext.guildID,
"misc.failure.interaction.playerBanned",
{
supportServer: "https://discord.gg/RCuzwYV",
},
),
});
return;
}

if (!State.rateLimiter.check(message.author.id)) {
logger.error(
`User ${
Expand Down
17 changes: 17 additions & 0 deletions src/helpers/management_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,20 @@ export function clearRestartNotification(): void {
State.restartNotification = null;
}

async function reloadBanData(): Promise<void> {
const bannedServers = (
await dbContext.kmq.selectFrom("banned_servers").select("id").execute()
).map((x) => x.id);

State.bannedServers = new Set(bannedServers);

const bannedPlayers = (
await dbContext.kmq.selectFrom("banned_players").select("id").execute()
).map((x) => x.id);

State.bannedPlayers = new Set(bannedPlayers);
}

/**
* @param clusterID - The cluster ID
* Sets up recurring cron-based tasks
Expand Down Expand Up @@ -466,6 +480,8 @@ export function registerIntervals(clusterID: number): void {
updateBotStatus();
// Clear any guilds stuck in parsing Spotify state
cleanupSpotifyParsingLocks();
// Reload ban data
reloadBanData();
});

// Every 5 minutes
Expand Down Expand Up @@ -506,4 +522,5 @@ export function reloadCaches(): void {
reloadBonusGroups();
reloadLocales();
reloadSongs();
reloadBanData();
}
6 changes: 6 additions & 0 deletions src/migrations/2023-12-09T01:55:52.217Z-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Kysely } from "kysely";
import { KmqDB } from "../typings/kmq_db";

export async function up(db: Kysely<KmqDB>): Promise<void> {}

export async function down(db: Kysely<KmqDB>): Promise<void> {}
15 changes: 15 additions & 0 deletions src/migrations/2023-12-09T01:59:54.013Z-banned-servers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Kysely } from "kysely";
import { KmqDB } from "../typings/kmq_db";

export async function up(db: Kysely<KmqDB>): Promise<void> {
await db.schema
.createTable("banned_servers")
.addColumn("id", "varchar(100)", (col) => col.notNull().primaryKey())
.addColumn("created_at", "timestamp", (col) => col.notNull())
.addColumn("reason", "varchar(100)", (col) => col.notNull())
.execute();
}

export async function down(db: Kysely<KmqDB>): Promise<void> {
await db.schema.dropTable("banned_servers").execute();
}
15 changes: 15 additions & 0 deletions src/migrations/2023-12-09T02:05:14.147Z-banned-players.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Kysely } from "kysely";
import { KmqDB } from "../typings/kmq_db";

export async function up(db: Kysely<KmqDB>): Promise<void> {
await db.schema
.createTable("banned_players")
.addColumn("id", "varchar(100)", (col) => col.notNull().primaryKey())
.addColumn("created_at", "timestamp", (col) => col.notNull())
.addColumn("reason", "varchar(100)", (col) => col.notNull())
.execute();
}

export async function down(db: Kysely<KmqDB>): Promise<void> {
await db.schema.dropTable("banned_players").execute();
}
62 changes: 62 additions & 0 deletions src/seed/migrate_down.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { FileMigrationProvider, Migrator, NO_MIGRATIONS } from "kysely";
import { IPCLogger } from "../logger";
import { promises as fsp } from "fs";
import { getNewConnection } from "../database_context";
import path from "path";
import type { DatabaseContext } from "../database_context";

const logger = new IPCLogger("messageCreate");

async function performMigrationDown(
db: DatabaseContext,
migrationName: string | undefined,
): Promise<void> {
logger.info("Performing migrations (down)...");
const migrator = new Migrator({
db: db.kmq,
provider: new FileMigrationProvider({
fs: fsp,
path,
// This needs to be an absolute path.
migrationFolder: path.join(__dirname, "../migrations"),
}),
});

const { error, results } = await migrator.migrateTo(
migrationName === undefined ? NO_MIGRATIONS : migrationName,
);

for (const result of results || []) {
if (result.status === "Success") {
logger.info(
`Migration (down) "${result.migrationName}" was executed successfully`,
);
} else if (result.status === "Error") {
logger.error(
`Failed to execute migration: "${result.migrationName}"`,
);
}
}

if (error) {
throw new Error(`Failed to migrate, err: ${error}`);
}
}

(async () => {
if (require.main === module) {
const args = process.argv.slice(2);
const migrationName = args[0];
if (!migrationName) {
logger.error("Target migration not specified");
process.exit(1);
}

const db = getNewConnection();
await performMigrationDown(
db,
migrationName === "EMPTY" ? undefined : migrationName,
);
await db.destroy();
}
})();
2 changes: 2 additions & 0 deletions src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export default class State {
song: {},
};

static bannedServers: Set<string> = new Set();
static bannedPlayers: Set<string> = new Set();
static processStartTime: number = Date.now();
static ipc: IPC;
static rateLimiter = new RateLimiter(15, 30);
Expand Down
Loading
Loading