diff --git a/src/bot/commands/unban.ts b/src/bot/commands/unban.ts new file mode 100644 index 0000000..ec5ffba --- /dev/null +++ b/src/bot/commands/unban.ts @@ -0,0 +1,76 @@ +import { FindResult } from "monk"; +import { client } from "../.."; +import Command from "../../struct/Command"; +import MessageCommandContext from "../../struct/MessageCommandContext"; +import TempBan from "../../struct/TempBan"; +import { removeTempBan } from "../modules/tempbans"; +import { isModerator, NO_MANAGER_MSG, parseUser, ULID_REGEX, USER_MENTION_REGEX } from "../util"; + +export default { + name: 'unban', + aliases: [ 'pardon' ], + description: 'Unbans a user', + syntax: '/unban [@user or ID]', + run: async (message: MessageCommandContext, args: string[]) => { + if (!isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); + + let checkTempBans = async (id: string): Promise => { + let tempbans: FindResult = await client.db.get('tempbans').find({ bannedUser: id, server: message.serverContext._id }); + if (tempbans.length > 0) { + for (const ban of tempbans) { + await removeTempBan(ban.id); + } + } + return tempbans.length; + } + + try { + let [msg, bans] = await Promise.all([ + message.reply('Fetching bans...')!, + message.serverContext.fetchBans(), + ]); + + let target = args[0]; + let id: string|undefined = undefined; + + try { + id = (await parseUser(target))?._id; + } catch(e) { + if (USER_MENTION_REGEX.test(target)) { + id = target + .replace('<@', '') + .replace('>', ''); + } else if (ULID_REGEX.test(target)) { + id = target; + } else { + let user = bans.users.find(u => u.username.toLowerCase() == target.toLowerCase()); + if (user) id = user._id; + } + } + + if (!id) { + let tempnum = await checkTempBans(target); + if (tempnum > 0) { + return msg.edit({ content: 'The user could not be found, but leftover database entries have been cleaned up.' }); + } else return msg.edit({ content: 'The user could not be found.' }); + } + + let bannedUser = bans.users.find(u => u._id == id); + + if (!bannedUser) { + let tempnum = await checkTempBans(id); + if (tempnum > 0) { + return msg.edit({ content: 'This user is not banned, but leftover database entries have been cleaned up.' }); + } else return msg.edit({ content: 'This user is not banned.' }); + } + + await Promise.all([ + msg.edit({ content: `User found: @${bannedUser.username}, unbanning...` }), + message.serverContext.unbanUser(id), + checkTempBans(id), + ]); + + await msg.edit({ content: `@${bannedUser.username} has been unbanned.` }); + } catch(e) { console.error(e) } + } +} as Command; diff --git a/src/bot/modules/tempbans.ts b/src/bot/modules/tempbans.ts index 2b9c3cb..4ab48e5 100644 --- a/src/bot/modules/tempbans.ts +++ b/src/bot/modules/tempbans.ts @@ -5,6 +5,7 @@ import logger from "../logger"; // Array of ban IDs which should not get processed in this session let dontProcess: string[] = []; +let expired: string[] = []; async function tick() { let found: FindResult = await client.db.get('tempbans').find({ until: { $lt: Date.now() + 60000 } }); @@ -25,6 +26,8 @@ new Promise((r: (value: void) => void) => { async function processUnban(ban: TempBan) { try { + if (expired.includes(ban.id)) return; + let server = client.servers.get(ban.server) || await client.servers.fetch(ban.server); let serverBans = await server.fetchBans(); @@ -54,4 +57,13 @@ async function storeTempBan(ban: TempBan): Promise { client.db.get('tempbans').insert(ban); } -export { storeTempBan }; +async function removeTempBan(banID: string): Promise { + let ban: TempBan = await client.db.get('tempbans').findOneAndDelete({ id: banID }); + if (Date.now() >= ban.until - 120000) { + expired.push(ban.id); + expired = expired.filter(id => id != ban.id); + }; + return ban; +} + +export { storeTempBan, removeTempBan }; diff --git a/src/bot/util.ts b/src/bot/util.ts index 657f8e8..f54863a 100644 --- a/src/bot/util.ts +++ b/src/bot/util.ts @@ -21,6 +21,7 @@ let ServerPermissions = { } const NO_MANAGER_MSG = '🔒 Missing permission'; +const ULID_REGEX = /^[0-9A-HJ-KM-NP-TV-Z]{26}$/i; const USER_MENTION_REGEX = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i; const CHANNEL_MENTION_REGEX = /^<#[0-9A-HJ-KM-NP-TV-Z]{26}>$/i; let autumn_url: string|null = null; @@ -163,6 +164,7 @@ export { uploadFile, sanitizeMessageContent, NO_MANAGER_MSG, + ULID_REGEX, USER_MENTION_REGEX, CHANNEL_MENTION_REGEX, }