diff --git a/discord-bot/bot.js b/discord-bot/bot.js index 8e2d0cec..78118d41 100644 --- a/discord-bot/bot.js +++ b/discord-bot/bot.js @@ -98,17 +98,6 @@ if (fs.existsSync(eventsPath)) { } } -// Bot ready event -client.once("ready", () => { - console.log(`✅ Bot logged in as ${client.user.tag}`); - console.log(`📡 Listening in ${client.guilds.cache.size} server(s)`); - - // Set bot status - client.user.setActivity("/verify to link your AeThex account", { - type: "LISTENING", - }); -}); - // Slash command interaction handler client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand()) return; diff --git a/discord-bot/events/messageCreate.js b/discord-bot/events/messageCreate.js index 6cbd2b7d..75abc33f 100644 --- a/discord-bot/events/messageCreate.js +++ b/discord-bot/events/messageCreate.js @@ -1,26 +1,15 @@ const { createClient } = require("@supabase/supabase-js"); -// Initialize Supabase const supabase = createClient( process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE, ); -const FEED_CHANNEL_ID = process.env.DISCORD_FEED_CHANNEL_ID; -const FEED_GUILD_ID = process.env.DISCORD_FEED_GUILD_ID; -const API_BASE = process.env.VITE_API_BASE || "https://api.aethex.dev"; +// Only sync messages from this specific channel +const FEED_CHANNEL_ID = process.env.DISCORD_MAIN_CHAT_CHANNELS + ? process.env.DISCORD_MAIN_CHAT_CHANNELS.split(",")[0].trim() + : null; -// Announcement channels to sync to feed -const ANNOUNCEMENT_CHANNELS = process.env.DISCORD_ANNOUNCEMENT_CHANNELS - ? process.env.DISCORD_ANNOUNCEMENT_CHANNELS.split(",").map((id) => id.trim()) - : []; - -// Main chat channels - sync ALL messages from these channels to the feed -const MAIN_CHAT_CHANNELS = process.env.DISCORD_MAIN_CHAT_CHANNELS - ? process.env.DISCORD_MAIN_CHAT_CHANNELS.split(",").map((id) => id.trim()) - : []; - -// Helper: Get arm affiliation from message context function getArmAffiliation(message) { const guildName = message.guild?.name?.toLowerCase() || ""; const channelName = message.channel?.name?.toLowerCase() || ""; @@ -37,14 +26,12 @@ function getArmAffiliation(message) { return "labs"; } -// Handle main chat messages - sync ALL messages to feed -async function handleMainChatSync(message) { +async function syncMessageToFeed(message) { try { console.log( - `[Main Chat] Processing from ${message.author.tag} in #${message.channel.name}`, + `[Feed Sync] Processing from ${message.author.tag} in #${message.channel.name}`, ); - // Check if user has linked account const { data: linkedAccount } = await supabase .from("discord_links") .select("user_id") @@ -55,7 +42,6 @@ async function handleMainChatSync(message) { let authorInfo = null; if (authorId) { - // Get linked user's profile const { data: profile } = await supabase .from("user_profiles") .select("id, username, full_name, avatar_url") @@ -64,9 +50,7 @@ async function handleMainChatSync(message) { authorInfo = profile; } - // If no linked account, use or create a Discord guest profile if (!authorId) { - // Check if we have a discord guest profile for this user const discordUsername = `discord-${message.author.id}`; let { data: guestProfile } = await supabase .from("user_profiles") @@ -75,7 +59,6 @@ async function handleMainChatSync(message) { .single(); if (!guestProfile) { - // Create guest profile const { data: newProfile, error: createError } = await supabase .from("user_profiles") .insert({ @@ -87,7 +70,7 @@ async function handleMainChatSync(message) { .single(); if (createError) { - console.error("[Main Chat] Could not create guest profile:", createError); + console.error("[Feed Sync] Could not create guest profile:", createError); return; } guestProfile = newProfile; @@ -98,11 +81,10 @@ async function handleMainChatSync(message) { } if (!authorId) { - console.error("[Main Chat] Could not get author ID"); + console.error("[Feed Sync] Could not get author ID"); return; } - // Prepare content let content = message.content || "Shared a message on Discord"; let mediaUrl = null; let mediaType = "none"; @@ -129,10 +111,8 @@ async function handleMainChatSync(message) { } } - // Determine arm affiliation const armAffiliation = getArmAffiliation(message); - // Prepare post content with Discord metadata const postContent = JSON.stringify({ text: content, mediaUrl: mediaUrl, @@ -149,343 +129,52 @@ async function handleMainChatSync(message) { is_linked_user: !!linkedAccount, }); - // Create post - const { data: createdPost, error: insertError } = await supabase + const { error: insertError } = await supabase .from("community_posts") .insert({ title: content.substring(0, 100) || "Discord Message", content: postContent, arm_affiliation: armAffiliation, author_id: authorId, - tags: ["discord", "main-chat"], + tags: ["discord", "feed"], category: "discord", is_published: true, likes_count: 0, comments_count: 0, - }) - .select("id"); + }); if (insertError) { - console.error("[Main Chat] Post creation failed:", insertError); + console.error("[Feed Sync] Post creation failed:", insertError); return; } console.log( - `[Main Chat] ✅ Synced message from ${message.author.tag} to AeThex feed`, + `[Feed Sync] ✅ Synced message from ${message.author.tag} to AeThex feed`, ); } catch (error) { - console.error("[Main Chat] Error:", error); - } -} - -// Handle announcements from designated channels -async function handleAnnouncementSync(message) { - try { - console.log( - `[Announcements] Processing from ${message.author.tag} in #${message.channel.name}`, - ); - - // Get or create system announcement user - let { data: adminUser } = await supabase - .from("user_profiles") - .select("id") - .eq("username", "aethex-announcements") - .single(); - - let authorId = adminUser?.id; - - if (!authorId) { - const { data: newUser } = await supabase - .from("user_profiles") - .insert({ - username: "aethex-announcements", - full_name: "AeThex Announcements", - avatar_url: "https://aethex.dev/logo.png", - }) - .select("id"); - - authorId = newUser?.[0]?.id; - } - - if (!authorId) { - console.error("[Announcements] Could not get author ID"); - await message.react("❌"); - return; - } - - // Prepare content - let content = message.content || "Announcement from Discord"; - - // Handle embeds - if (message.embeds.length > 0) { - const embed = message.embeds[0]; - if (embed.title) content = `**${embed.title}**\n\n${content}`; - if (embed.description) content += `\n\n${embed.description}`; - } - - // Handle attachments - let mediaUrl = null; - let mediaType = "none"; - - if (message.attachments.size > 0) { - const attachment = message.attachments.first(); - if (attachment) { - mediaUrl = attachment.url; - const attachmentLower = attachment.name.toLowerCase(); - - if ( - [".jpg", ".jpeg", ".png", ".gif", ".webp"].some((ext) => - attachmentLower.endsWith(ext), - ) - ) { - mediaType = "image"; - } else if ( - [".mp4", ".webm", ".mov", ".avi"].some((ext) => - attachmentLower.endsWith(ext), - ) - ) { - mediaType = "video"; - } - } - } - - // Determine arm - const armAffiliation = getArmAffiliation(message); - - // Prepare post content - const postContent = JSON.stringify({ - text: content, - mediaUrl: mediaUrl, - mediaType: mediaType, - source: "discord", - discord_message_id: message.id, - discord_channel: message.channel.name, - }); - - // Create post - const { data: createdPost, error: insertError } = await supabase - .from("community_posts") - .insert({ - title: content.substring(0, 100) || "Discord Announcement", - content: postContent, - arm_affiliation: armAffiliation, - author_id: authorId, - tags: ["discord", "announcement"], - category: "announcement", - is_published: true, - likes_count: 0, - comments_count: 0, - }) - .select( - `id, title, content, arm_affiliation, author_id, created_at, likes_count, comments_count, - user_profiles!community_posts_author_id_fkey (id, username, full_name, avatar_url)`, - ); - - if (insertError) { - console.error("[Announcements] Post creation failed:", insertError); - await message.react("❌"); - return; - } - - console.log(`[Announcements] ✅ Synced to AeThex (${armAffiliation} arm)`); - - await message.react("✅"); - } catch (error) { - console.error("[Announcements] Error:", error); - try { - await message.react("⚠️"); - } catch (e) { - console.warn("[Announcements] Could not react:", e); - } + console.error("[Feed Sync] Error:", error); } } module.exports = { name: "messageCreate", async execute(message, client) { - // Ignore bot messages and empty messages + // Ignore bot messages if (message.author.bot) return; + + // Ignore empty messages if (!message.content && message.attachments.size === 0) return; - // Debug: Log channel info for troubleshooting - const isMainChat = MAIN_CHAT_CHANNELS.includes(message.channelId); - const isAnnouncement = ANNOUNCEMENT_CHANNELS.includes(message.channelId); - const isFeedChannel = FEED_CHANNEL_ID && message.channelId === FEED_CHANNEL_ID; - - // Only log if the message is from a channel we care about - if (isMainChat || isAnnouncement || isFeedChannel) { - console.log(`[MessageCreate] Channel: ${message.channel.name} (${message.channelId}) | Main: ${isMainChat} | Announcement: ${isAnnouncement} | Feed: ${isFeedChannel}`); - } - - // Check if this is an announcement to sync - if ( - ANNOUNCEMENT_CHANNELS.length > 0 && - ANNOUNCEMENT_CHANNELS.includes(message.channelId) - ) { - return handleAnnouncementSync(message); - } - - // Check if this is a main chat channel - sync ALL messages - if ( - MAIN_CHAT_CHANNELS.length > 0 && - MAIN_CHAT_CHANNELS.includes(message.channelId) - ) { - return handleMainChatSync(message); - } - - // If no specific channels are configured for sync, exit early - // This prevents syncing ALL messages from every channel + // Only process messages from the configured feed channel if (!FEED_CHANNEL_ID) { - // No feed channel configured - only process main chat and announcement channels - return; + return; // No channel configured } - // Check if this is in the feed channel (for user-generated posts with linked accounts) if (message.channelId !== FEED_CHANNEL_ID) { - return; + return; // Not the feed channel } - if (FEED_GUILD_ID && message.guildId !== FEED_GUILD_ID) { - return; - } - - try { - // Get user's linked AeThex account - const { data: linkedAccount, error } = await supabase - .from("discord_links") - .select("user_id") - .eq("discord_id", message.author.id) - .single(); - - if (error || !linkedAccount) { - try { - await message.author.send( - "To have your message posted to AeThex, please link your Discord account! Use `/verify` command.", - ); - } catch (dmError) { - console.warn("[Feed Sync] Could not send DM to user:", dmError); - } - return; - } - - // Get user profile - const { data: userProfile, error: profileError } = await supabase - .from("user_profiles") - .select("id, username, full_name, avatar_url") - .eq("id", linkedAccount.user_id) - .single(); - - if (profileError || !userProfile) { - console.error( - "[Feed Sync] Could not fetch user profile:", - profileError, - ); - return; - } - - // Prepare content - let content = message.content || "Shared a message on Discord"; - let mediaUrl = null; - let mediaType = "none"; - - if (message.attachments.size > 0) { - const attachment = message.attachments.first(); - if (attachment) { - mediaUrl = attachment.url; - const attachmentLower = attachment.name.toLowerCase(); - - if ( - [".jpg", ".jpeg", ".png", ".gif", ".webp"].some((ext) => - attachmentLower.endsWith(ext), - ) - ) { - mediaType = "image"; - } else if ( - [".mp4", ".webm", ".mov", ".avi"].some((ext) => - attachmentLower.endsWith(ext), - ) - ) { - mediaType = "video"; - } - } - } - - // Determine arm affiliation - let armAffiliation = "labs"; - const guild = message.guild; - if (guild) { - const guildNameLower = guild.name.toLowerCase(); - if (guildNameLower.includes("gameforge")) armAffiliation = "gameforge"; - else if (guildNameLower.includes("corp")) armAffiliation = "corp"; - else if (guildNameLower.includes("foundation")) - armAffiliation = "foundation"; - else if (guildNameLower.includes("devlink")) armAffiliation = "devlink"; - else if (guildNameLower.includes("nexus")) armAffiliation = "nexus"; - else if (guildNameLower.includes("staff")) armAffiliation = "staff"; - } - - // Prepare post content - const postContent = JSON.stringify({ - text: content, - mediaUrl: mediaUrl, - mediaType: mediaType, - }); - - // Create post - const { data: createdPost, error: insertError } = await supabase - .from("community_posts") - .insert({ - title: content.substring(0, 100) || "Discord Shared Message", - content: postContent, - arm_affiliation: armAffiliation, - author_id: userProfile.id, - tags: ["discord"], - category: null, - is_published: true, - likes_count: 0, - comments_count: 0, - }) - .select( - `id, title, content, arm_affiliation, author_id, created_at, updated_at, likes_count, comments_count, - user_profiles!community_posts_author_id_fkey (id, username, full_name, avatar_url)`, - ); - - if (insertError) { - console.error("[Feed Sync] Failed to create post:", insertError); - try { - await message.react("❌"); - } catch (e) { - console.warn("[Feed Sync] Could not add reaction:", e); - } - return; - } - - console.log(`[Feed Sync] ✅ Posted from ${message.author.tag} to AeThex`); - - try { - await message.react("✅"); - } catch (reactionError) { - console.warn( - "[Feed Sync] Could not add success reaction:", - reactionError, - ); - } - - try { - await message.author.send( - `✅ Your message was posted to AeThex! Check it out at https://aethex.dev/feed`, - ); - } catch (dmError) { - console.warn("[Feed Sync] Could not send confirmation DM:", dmError); - } - } catch (error) { - console.error("[Feed Sync] Unexpected error:", error); - - try { - await message.react("⚠️"); - } catch (e) { - console.warn("[Feed Sync] Could not add warning reaction:", e); - } - } + // Sync this message to AeThex feed + await syncMessageToFeed(message); }, };