diff options
Diffstat (limited to 'packages/gateway')
| -rw-r--r-- | packages/gateway/src/commands/delete.ts | 17 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/aiCommandHandler.ts | 169 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/messageCreate.ts | 2 |
3 files changed, 182 insertions, 6 deletions
diff --git a/packages/gateway/src/commands/delete.ts b/packages/gateway/src/commands/delete.ts index 78b17be..8b36eb9 100644 --- a/packages/gateway/src/commands/delete.ts +++ b/packages/gateway/src/commands/delete.ts @@ -17,7 +17,7 @@ export const handleDeleteCommand = async (message: Message) => { if (parameters.length < 1) { await message.reply( - "❌ Usage: `uma!delete <message_id> [channel_id]`\nExamples:\n- `uma!delete 1234567890123456789` (current channel)\n- `uma!delete 1234567890123456789 9876543210987654321` (specific channel)" + "❌ Usage: `uma!delete <message_id> [channel_id]`\nExamples:\n- `uma!delete 1234567890123456789` (current channel)\n- `uma!delete 1234567890123456789 9876543210987654321` (specific channel)", ); return; @@ -27,13 +27,17 @@ export const handleDeleteCommand = async (message: Message) => { const channelId = parameters[1]; if (!/^\d{17,19}$/.test(messageId)) { - await message.reply("❌ Invalid message ID format. Please provide a valid Discord message ID."); + await message.reply( + "❌ Invalid message ID format. Please provide a valid Discord message ID.", + ); return; } if (channelId && !/^\d{17,19}$/.test(channelId)) { - await message.reply("❌ Invalid channel ID format. Please provide a valid Discord channel ID."); + await message.reply( + "❌ Invalid channel ID format. Please provide a valid Discord channel ID.", + ); return; } @@ -44,7 +48,7 @@ export const handleDeleteCommand = async (message: Message) => { if (channelId) { targetChannel = message.client.channels.cache.get(channelId); - + if (!targetChannel || !targetChannel.isTextBased()) { await message.reply("❌ Channel not found or is not a text channel."); @@ -62,12 +66,13 @@ export const handleDeleteCommand = async (message: Message) => { return; } - await targetMessage.delete(); await message.delete(); } catch (error) { console.error("Error deleting message:", error); - await message.reply("❌ Failed to delete the message. Check bot permissions and try again."); + await message.reply( + "❌ Failed to delete the message. Check bot permissions and try again.", + ); } } }; diff --git a/packages/gateway/src/listeners/aiCommandHandler.ts b/packages/gateway/src/listeners/aiCommandHandler.ts new file mode 100644 index 0000000..652069e --- /dev/null +++ b/packages/gateway/src/listeners/aiCommandHandler.ts @@ -0,0 +1,169 @@ +import { Message } from "discord.js"; + +const MODERATOR_ROLE_IDS = [ + "1406422617765712095", + "1406422617765712094", + "1406422617765712093", + "1406422617724026910", +]; + +interface AICommandResponse { + command: string; + action: string; + value?: number; +} + +export const handleAICommand = async (message: Message) => { + if (message.author.bot) return; + + if (!message.mentions.has(message.client.user!)) return; + + if (!message.member) return; + + const hasModeratorRole = message.member.roles.cache.some((role) => + MODERATOR_ROLE_IDS.includes(role.id), + ); + const application = await message.client.application?.fetch(); + const ownerId = application?.owner?.id; + const isOwner = message.author.id === ownerId; + + if (!hasModeratorRole && !isOwner) return; + + const content = message.content.replace(/<@!?\d+>/g, "").trim(); + + if (!content) return; + + try { + const response = await fetch( + "https://openrouter.ai/api/v1/chat/completions", + { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: "anthropic/claude-3.5-haiku", + messages: [ + { + role: "system", + content: `You are a Discord bot command interpreter. Parse the user's natural language into a JSON command. + +Available commands: +- slowmode: Toggle, enable, or disable slowmode in the current channel + +Respond with ONLY a JSON object in this exact format: +{"command": "slowmode", "action": "toggle|enable|disable", "value": number} + +For slowmode: +- "toggle" = switch current state +- "enable" = turn on (default 5 seconds) +- "disable" = turn off (0 seconds) +- "value" = seconds (optional, defaults to 5 for enable) + +Examples: +"toggle slowmode" → {"command": "slowmode", "action": "toggle"} +"enable slowmode" → {"command": "slowmode", "action": "enable", "value": 5} +"set slowmode to 10" → {"command": "slowmode", "action": "enable", "value": 10} +"disable slowmode" → {"command": "slowmode", "action": "disable", "value": 0} + +Keep responses minimal and accurate.`, + }, + { + role: "user", + content: content, + }, + ], + max_tokens: 100, + temperature: 0.1, + }), + }, + ); + + if (!response.ok) { + console.error( + "OpenRouter API error:", + response.status, + response.statusText, + ); + + return; + } + + const data = await response.json(); + const aiResponse = data.choices?.[0]?.message?.content?.trim(); + + if (!aiResponse) { + console.error("No response from OpenRouter"); + + return; + } + + let commandData: AICommandResponse; + + try { + commandData = JSON.parse(aiResponse); + } catch { + console.error("Failed to parse AI response as JSON:", aiResponse); + + return; + } + + await executeAICommand(message, commandData); + } catch (error) { + console.error("Error in AI command handler:", error); + } +}; + +const executeAICommand = async ( + message: Message, + commandData: AICommandResponse, +) => { + if (commandData.command === "slowmode") + await handleSlowmodeCommand(message, commandData); +}; + +const handleSlowmodeCommand = async ( + message: Message, + commandData: AICommandResponse, +) => { + if (!message.guild || !message.channel.isTextBased()) return; + + const channel = message.channel; + + if (!("rateLimitPerUser" in channel)) { + await message.react("❌"); + + return; + } + + const currentSlowmode = channel.rateLimitPerUser || 0; + let newSlowmode = 0; + + switch (commandData.action) { + case "toggle": + newSlowmode = currentSlowmode > 0 ? 0 : commandData.value || 5; + + break; + case "enable": + newSlowmode = commandData.value || 5; + + break; + case "disable": + newSlowmode = 0; + + break; + default: + await message.react("❌"); + + return; + } + + try { + await (channel as any).setRateLimitPerUser(newSlowmode); + await message.react("✅"); + } catch (error) { + console.error("Error setting slowmode:", error); + await message.react("❌"); + } +}; diff --git a/packages/gateway/src/listeners/messageCreate.ts b/packages/gateway/src/listeners/messageCreate.ts index 72f4f2b..3c4f68a 100644 --- a/packages/gateway/src/listeners/messageCreate.ts +++ b/packages/gateway/src/listeners/messageCreate.ts @@ -5,6 +5,7 @@ import { handleArtMediaModeration } from "./artMediaModeration"; import { handleAIModeration } from "./moderationAgent"; import { handleAnnouncementReaction } from "./announcementReaction"; import { handleRoleMentionCooldown } from "./roleMentionCooldown"; +import { handleAICommand } from "./aiCommandHandler"; export const handleMessageCreate = (client: Client) => { client.on(Events.MessageCreate, async (message: Message) => { @@ -15,6 +16,7 @@ export const handleMessageCreate = (client: Client) => { handleAIModeration(message), handleAnnouncementReaction(message), handleRoleMentionCooldown(message), + handleAICommand(message), ]); }); }; |