diff options
| author | Fuwn <[email protected]> | 2025-10-27 22:20:07 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-10-27 22:20:07 -0700 |
| commit | fc6278e4cbe00acba0c8bdbe645df85e607af667 (patch) | |
| tree | 3d6b2d128aca38064911cf8cb2032c019ffb21e9 | |
| parent | feat(listeners:autoDeletion): Add additional banned strings (diff) | |
| download | umabotdiscord-fc6278e4cbe00acba0c8bdbe645df85e607af667.tar.xz umabotdiscord-fc6278e4cbe00acba0c8bdbe645df85e607af667.zip | |
feat(gateway:commands): Add timeout command
| -rw-r--r-- | packages/gateway/src/commands/commandHandler.ts | 2 | ||||
| -rw-r--r-- | packages/gateway/src/commands/timeout.ts | 120 |
2 files changed, 122 insertions, 0 deletions
diff --git a/packages/gateway/src/commands/commandHandler.ts b/packages/gateway/src/commands/commandHandler.ts index 163568e..7442c47 100644 --- a/packages/gateway/src/commands/commandHandler.ts +++ b/packages/gateway/src/commands/commandHandler.ts @@ -11,6 +11,7 @@ import { handleVerbalGatesCommand } from "./verbalGates"; import { handleWebhookCommand } from "./webhook"; import { handleDeleteWebhookCommand } from "./deleteWebhook"; import { handleCharacterClaimUsageCommand } from "./characterClaimUsage"; +import { handleTimeoutCommand } from "./timeout"; export const handleCommandHandler = (client: Client) => { client.on(Events.MessageCreate, async (message: Message) => { @@ -33,6 +34,7 @@ export const handleCommandHandler = (client: Client) => { handleWebhookCommand(message), handleDeleteWebhookCommand(message), handleCharacterClaimUsageCommand(message), + handleTimeoutCommand(message), ]); }); }; diff --git a/packages/gateway/src/commands/timeout.ts b/packages/gateway/src/commands/timeout.ts new file mode 100644 index 0000000..d43d6bd --- /dev/null +++ b/packages/gateway/src/commands/timeout.ts @@ -0,0 +1,120 @@ +import { Message } from "discord.js"; +import { CENTRAL_GUILD_ID, ROLEPLAY_GUILD_ID } from "../constants"; +import { logUnexpectedDiscordAPIError, replyWithCleanup } from "../utilities"; + +const parseDuration = (duration: string): number | null => { + const match = duration.match(/^(\d+)([smhd])$/); + + if (!match) return null; + + const value = parseInt(match[1]); + const unit = match[2]; + + switch (unit) { + case "s": + return value * 1000; + case "m": + return value * 60 * 1000; + case "h": + return value * 60 * 60 * 1000; + case "d": + return value * 24 * 60 * 60 * 1000; + default: + return null; + } +}; + +const getUserId = (input: string): string | null => { + const mentionMatch = input.match(/^<@(\d+)>$/); + + if (mentionMatch) return mentionMatch[1]; + + const userIdMatch = input.match(/^\d+$/); + + if (userIdMatch) return input; + + return null; +}; + +const getGuild = (client: any, serverId: string) => { + if (serverId === "central" || serverId === CENTRAL_GUILD_ID) + return client.guilds.cache.get(CENTRAL_GUILD_ID); + + if (serverId === "roleplay" || serverId === ROLEPLAY_GUILD_ID) + return client.guilds.cache.get(ROLEPLAY_GUILD_ID); + + return null; +}; + +export const handleTimeoutCommand = async (message: Message) => { + if (message.author.bot) return; + + if (message.content.toLowerCase().startsWith("uma!timeout")) { + const application = await message.client.application?.fetch(); + const ownerId = application?.owner?.id; + + if (message.author.id !== ownerId) return; + + const parameters = message.content.split(" ").slice(1); + + if (parameters.length < 3) { + await replyWithCleanup( + message, + "❌ Usage: `uma!timeout <server_id|central|roleplay> <user_id|@mention> <duration> [reason]`\n\n**Examples:**\n- `uma!timeout central @user 1h Being toxic`\n- `uma!timeout roleplay 1234567890123456789 6h`\n- `uma!timeout 1406422617724026901 @user 1d Spam`\n\n**Duration format:** Use suffix: `s` (seconds), `m` (minutes), `h` (hours), `d` (days)", + ); + + return; + } + + const [serverInput, userInput, durationInput, ...reasonParts] = parameters; + const reason = reasonParts.join(" ") || undefined; + const durationMs = parseDuration(durationInput); + + if (!durationMs) { + await replyWithCleanup( + message, + "❌ Invalid duration format. Use: `<number><s|m|h|d>`\nExamples: `1m`, `6h`, `1d`", + ); + + return; + } + + const guild = getGuild(message.client, serverInput); + + if (!guild) { + await replyWithCleanup( + message, + "❌ Invalid server. Use `central`, `roleplay`, or a guild ID.", + ); + + return; + } + + const userId = getUserId(userInput); + + if (!userId) { + await replyWithCleanup( + message, + "❌ Invalid user. Use a user ID or mention: `<@userId>`", + ); + + return; + } + + try { + const member = await guild.members.fetch(userId); + + await member.timeout(durationMs, reason); + await replyWithCleanup( + message, + `✅ Successfully timed out ${member.user.tag} in ${guild.name} for ${durationInput}${reason ? ` - ${reason}` : ""}`, + ); + } catch (error) { + logUnexpectedDiscordAPIError(error); + await replyWithCleanup( + message, + `❌ Failed to timeout user. ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + } +}; |