WIP votekick

This commit is contained in:
janderedev 2022-03-16 23:55:50 +01:00
parent e26803a17c
commit 117e3b0f12
Signed by: Lea
GPG key ID: 5D5E18ACB990F57A
6 changed files with 129 additions and 7 deletions

View file

@ -87,7 +87,7 @@ export default {
await Promise.all([ await Promise.all([
message.reply(`### ${targetName} has been ${Math.random() > 0.8 ? 'ejected' : 'banned'}.\n` message.reply(`### ${targetName} has been ${Math.random() > 0.8 ? 'ejected' : 'banned'}.\n`
+ `Infraction ID: \`${infId}\` (**#${userWarnCount}** for this user)`), + `Infraction ID: \`${infId}\` (**#${userWarnCount}** for this user)`),
logModAction('ban', message.serverContext, message.member!, targetUser._id, reason, infraction, `Ban duration: **Permanent**`), logModAction('ban', message.serverContext, message.member!, targetUser._id, reason, infraction._id, `Ban duration: **Permanent**`),
]); ]);
} else { } else {
let banUntil = Date.now() + banDuration; let banUntil = Date.now() + banDuration;
@ -119,7 +119,7 @@ export default {
await Promise.all([ await Promise.all([
message.reply(`### ${targetName} has been temporarily banned.\n` message.reply(`### ${targetName} has been temporarily banned.\n`
+ `Infraction ID: \`${infId}\` (**#${userWarnCount}** for this user)`), + `Infraction ID: \`${infId}\` (**#${userWarnCount}** for this user)`),
logModAction('ban', message.serverContext, message.member!, targetUser._id, reason, infraction, `Ban duration: **${Day(banUntil).fromNow(true)}**`), logModAction('ban', message.serverContext, message.member!, targetUser._id, reason, infraction._id, `Ban duration: **${Day(banUntil).fromNow(true)}**`),
]); ]);
} }
} }

View file

@ -64,7 +64,7 @@ export default {
await Promise.all([ await Promise.all([
message.reply(`### @${targetUser.username} has been ${Math.random() > 0.8 ? 'yeeted' : 'kicked'}.\n` message.reply(`### @${targetUser.username} has been ${Math.random() > 0.8 ? 'yeeted' : 'kicked'}.\n`
+ `Infraction ID: \`${infId}\` (**#${userWarnCount}** for this user)`), + `Infraction ID: \`${infId}\` (**#${userWarnCount}** for this user)`),
logModAction('kick', message.serverContext, message.member!, targetUser._id, reason, infraction), logModAction('kick', message.serverContext, message.member!, targetUser._id, reason, infraction._id),
]); ]);
} }
} as Command; } as Command;

View file

@ -0,0 +1,116 @@
import { FindResult } from "monk";
import { ulid } from "ulid";
import { client } from "../..";
import Command from "../../struct/Command";
import MessageCommandContext from "../../struct/MessageCommandContext";
import ServerConfig from "../../struct/ServerConfig";
import { logModAction } from "../modules/mod_logs";
import { storeTempBan } from "../modules/tempbans";
import { getPermissionLevel, isModerator, parseUser } from "../util";
type VoteEntry = {
id: string;
target: string;
user: string; // Whoever issued the vote kick
server: string;
time: number;
ignore: boolean;
}
export default {
name: 'votekick',
aliases: [ 'voteban' ],
description: 'Allow trusted users to vote kick users',
category: 'moderation',
run: async (message: MessageCommandContext, args: string[]) => {
try {
const serverConfig: ServerConfig = await client.db.get('servers').findOne({ id: message.serverContext._id });
if (!serverConfig?.votekick?.enabled) return message.reply('Vote kick is not enabled for this server.');
if (!message.member!.roles?.filter(r => serverConfig.votekick?.trustedRoles.includes(r)).length
&& !(await isModerator(message))) {
return message.reply('🔒 Access denied');
}
if (args.length == 0) return message.reply(`**Votekick configuration:**\n`
+ `Votes required: **${serverConfig.votekick.votesRequired}**\n`
+ `Ban duration: **${serverConfig.votekick.banDuration}** (In minutes, -1 = Kick, 0: Permanent)\n`
+ `Trusted role IDs: \`${serverConfig.votekick.trustedRoles.join('\`, \`')}\`\n\n`
+ `Run \`/votekick [Username, ID or @mention]\` to votekick someone.`);
const target = await parseUser(args[0]);
if (!target) return message.reply('Sorry, I can\'t find this user.');
const targetMember = await message.serverContext.fetchMember(target);
if (await getPermissionLevel(target, message.serverContext) > 0
|| targetMember.roles?.filter(r => serverConfig.votekick?.trustedRoles.includes(r)).length) {
return message.reply('This target can not be votekicked.');
}
const vote: VoteEntry = {
id: ulid(),
target: target._id,
user: message.author_id,
server: message.serverContext._id,
time: Date.now(),
ignore: false,
}
const votes: FindResult<VoteEntry> = await client.db.get('votekicks').find({
server: message.serverContext._id,
target: target._id,
time: {
$gt: Date.now() - 1000 * 60 * 30, // Last 30 minutes
},
ignore: false,
});
if (votes.find(v => v.user == message.author_id)) return message.reply('You can\'t vote twice for this user.');
await client.db.get('votekicks').insert(vote);
votes.push({ _id: '' as any, ...vote });
await logModAction(
"votekick",
message.serverContext,
message.member!,
target._id,
`n/a`,
vote.id,
`This is vote ${votes.length}/${serverConfig.votekick.votesRequired} for this user.`,
);
if (votes.length >= serverConfig.votekick.votesRequired) {
if (serverConfig.votekick.banDuration == -1) {
targetMember.kick();
} else if (serverConfig.votekick.banDuration == 0) {
message.serverContext.banUser(target._id, { reason: 'Automatic permanent ban triggered by /votekick' });
} else {
message.serverContext.banUser(target._id, { reason: `Automatic temporary ban triggered by /votekick `
+ `(${serverConfig.votekick.banDuration} minutes)` });
await storeTempBan({
id: ulid(),
bannedUser: target._id,
server: message.serverContext._id,
until: Date.now() + (1000 * 60 * serverConfig.votekick.banDuration),
});
}
message.reply(`**${votes.length}/${serverConfig.votekick.votesRequired}** votes - `
+ `Banned @${target.username} for ${serverConfig.votekick.banDuration} minutes.`); // Todo: display ban duration properly (Permban, kick, etc)
await client.db.get('votekicks').update({
server: message.serverContext._id,
target: target._id,
time: { $gt: Date.now() - 1000 * 60 * 30 },
ignore: false,
}, { $set: { ignore: true } });
} else {
message.reply(`Voted to temporarily remove **@${target.username}**. `
+ `**${votes.length}/${serverConfig.votekick.votesRequired}** votes.`);
}
} catch(e) {
console.error(e);
message.reply('Oops, something happened: ' + e);
}
}
} as Command;

View file

@ -43,7 +43,7 @@ export default {
+ ` for ${await fetchUsername(user._id)}.\n` + ` for ${await fetchUsername(user._id)}.\n`
+ `**Infraction ID:** \`${infraction._id}\`\n` + `**Infraction ID:** \`${infraction._id}\`\n`
+ `**Reason:** \`${infraction.reason}\``), + `**Reason:** \`${infraction.reason}\``),
logModAction('warn', message.serverContext, message.member!, user._id, reason, infraction, `This is warn number ${userWarnCount} for this user.`), logModAction('warn', message.serverContext, message.member!, user._id, reason, infraction._id, `This is warn number ${userWarnCount} for this user.`),
]); ]);
} }
} as Command; } as Command;

View file

@ -114,7 +114,7 @@ client.on('packet', async (packet) => {
} }
}); });
async function logModAction(type: 'warn'|'kick'|'ban', server: Server, mod: Member, target: string, reason: string|null, infraction: Infraction, extraText?: string): Promise<void> { async function logModAction(type: 'warn'|'kick'|'ban'|'votekick', server: Server, mod: Member, target: string, reason: string|null, infractionID: string, extraText?: string): Promise<void> {
try { try {
let config: ServerConfig = await client.db.get('servers').findOne({ id: server._id }) ?? {}; let config: ServerConfig = await client.db.get('servers').findOne({ id: server._id }) ?? {};
@ -130,7 +130,7 @@ async function logModAction(type: 'warn'|'kick'|'ban', server: Server, mod: Memb
description: `\`@${mod.user?.username}\` **${aType}** \`` description: `\`@${mod.user?.username}\` **${aType}** \``
+ `${await fetchUsername(target)}\`${type == 'warn' ? '.' : ` from ${server.name}.`}\n` + `${await fetchUsername(target)}\`${type == 'warn' ? '.' : ` from ${server.name}.`}\n`
+ `**Reason**: \`${reason ? reason : 'No reason provided.'}\`\n` + `**Reason**: \`${reason ? reason : 'No reason provided.'}\`\n`
+ `**Warn ID**: \`${infraction._id}\`\n` + `**Warn ID**: \`${infractionID}\`\n`
+ (extraText ?? ''), + (extraText ?? ''),
color: embedColor, color: embedColor,
overrides: { overrides: {
@ -138,7 +138,7 @@ async function logModAction(type: 'warn'|'kick'|'ban', server: Server, mod: Memb
description: `@${mod.user?.username} ${aType} ` description: `@${mod.user?.username} ${aType} `
+ `${await fetchUsername(target)}${type == 'warn' ? '.' : ` from ${server.name}.`}\n` + `${await fetchUsername(target)}${type == 'warn' ? '.' : ` from ${server.name}.`}\n`
+ `Reason: ${reason ? reason : 'No reason provided.'}\n` + `Reason: ${reason ? reason : 'No reason provided.'}\n`
+ `Warn ID: ${infraction._id}\n` + `Warn ID: ${infractionID}\n`
+ (extraText ?? ''), + (extraText ?? ''),
} }
} }

View file

@ -8,6 +8,12 @@ class ServerConfig {
automodSettings: AutomodSettings | undefined; automodSettings: AutomodSettings | undefined;
botManagers: string[] | undefined; botManagers: string[] | undefined;
moderators: string[] | undefined; moderators: string[] | undefined;
votekick: {
enabled: boolean;
votesRequired: number;
banDuration: number; // -1: Only kick, 0: Permanent, >0: Ban duration in minutes
trustedRoles: string[];
} | undefined;
linkedServer: string | undefined; linkedServer: string | undefined;
whitelist: { whitelist: {
users: string[] | undefined, users: string[] | undefined,