summaryrefslogtreecommitdiff
path: root/packages/gateway
diff options
context:
space:
mode:
Diffstat (limited to 'packages/gateway')
-rw-r--r--packages/gateway/src/commands/delete.ts17
-rw-r--r--packages/gateway/src/listeners/aiCommandHandler.ts169
-rw-r--r--packages/gateway/src/listeners/messageCreate.ts2
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),
]);
});
};