Remove duplicate bot startup and simplify message syncing logic
Removes the duplicate `client.once("ready", ...)` handler from `bot.js` to prevent duplicate startup messages and processes. Simplifies the message handling in `messageCreate.js` to exclusively sync messages from the configured `DISCORD_MAIN_CHAT_CHANNELS`, ignoring other channel types.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: cda164c8-9ba9-4933-8c84-17f4dcdb866a
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/duiWnI1
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
commit
209ad1f93f
2 changed files with 22 additions and 344 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue