-
Notifications
You must be signed in to change notification settings - Fork 1
feat(bot): add guild events #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements guild event handling for a Discord bot by adding database tracking for guild joins/leaves and synchronization logic for guilds that changed while the bot was offline. The implementation includes real-time event handlers and a startup reconciliation process.
- Adds Discord guild event handlers for
GuildCreateandGuildDeleteevents with database integration - Implements startup synchronization to detect and update guilds that were added/removed while offline
- Extends the database schema with a
memberCountfield and adds helper functions for guild management
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/discord/updateGuildsOnStartup.ts | Startup reconciliation logic to sync guild state between Discord cache and database |
| src/index.ts | Integration of startup guild synchronization into bot initialization |
| src/events/guildDelete.ts | Event handler for when the bot leaves a guild |
| src/events/guildCreate.ts | Event handler for when the bot joins a new guild |
| src/db/schema.ts | Database schema update adding memberCount field |
| src/db/discord.ts | Helper functions for adding and removing guilds from database tracking |
| missingGuilds.forEach(async (guild) => { | ||
| console.log(`Removing guild from tracking: ${guild.guildId}`); | ||
| const result = await db | ||
| .update(dbDiscordTable) | ||
| .set({ isInServer: false }) | ||
| .where(eq(dbDiscordTable.guildId, guild.guildId)) | ||
| .returning(); | ||
|
|
||
| if (result) { | ||
| console.log( | ||
| `Successfully removed guild ${guild.guildId} from tracking.`, | ||
| ); | ||
| } else { | ||
| console.error( | ||
| `Failed to remove guild ${guild.guildId} from tracking.`, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| newGuilds.forEach(async (guildId) => { | ||
| console.log(`Adding new guild to tracking: ${guildId}`); | ||
| const result = await db | ||
| .insert(dbDiscordTable) | ||
| .values({ | ||
| guildId, | ||
| allowedPublicSharing: false, | ||
| feedrUpdatesChannelId: null, | ||
| isInServer: true, | ||
| memberCount: 0, | ||
| }) | ||
| .returning(); | ||
|
|
||
| if (result) { | ||
| console.log(`Successfully added guild ${guildId} to tracking.`); | ||
| } else { | ||
| console.error(`Failed to add guild ${guildId} to tracking.`); | ||
| } | ||
| }); |
Copilot
AI
Jul 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using forEach with async callbacks can lead to unhandled promise rejections and doesn't properly handle errors. Use Promise.all with map or a for...of loop instead.
| missingGuilds.forEach(async (guild) => { | |
| console.log(`Removing guild from tracking: ${guild.guildId}`); | |
| const result = await db | |
| .update(dbDiscordTable) | |
| .set({ isInServer: false }) | |
| .where(eq(dbDiscordTable.guildId, guild.guildId)) | |
| .returning(); | |
| if (result) { | |
| console.log( | |
| `Successfully removed guild ${guild.guildId} from tracking.`, | |
| ); | |
| } else { | |
| console.error( | |
| `Failed to remove guild ${guild.guildId} from tracking.`, | |
| ); | |
| } | |
| }); | |
| newGuilds.forEach(async (guildId) => { | |
| console.log(`Adding new guild to tracking: ${guildId}`); | |
| const result = await db | |
| .insert(dbDiscordTable) | |
| .values({ | |
| guildId, | |
| allowedPublicSharing: false, | |
| feedrUpdatesChannelId: null, | |
| isInServer: true, | |
| memberCount: 0, | |
| }) | |
| .returning(); | |
| if (result) { | |
| console.log(`Successfully added guild ${guildId} to tracking.`); | |
| } else { | |
| console.error(`Failed to add guild ${guildId} to tracking.`); | |
| } | |
| }); | |
| for (const guild of missingGuilds) { | |
| try { | |
| console.log(`Removing guild from tracking: ${guild.guildId}`); | |
| const result = await db | |
| .update(dbDiscordTable) | |
| .set({ isInServer: false }) | |
| .where(eq(dbDiscordTable.guildId, guild.guildId)) | |
| .returning(); | |
| if (result) { | |
| console.log( | |
| `Successfully removed guild ${guild.guildId} from tracking.`, | |
| ); | |
| } else { | |
| console.error( | |
| `Failed to remove guild ${guild.guildId} from tracking.`, | |
| ); | |
| } | |
| } catch (error) { | |
| console.error( | |
| `Error while removing guild ${guild.guildId} from tracking:`, | |
| error, | |
| ); | |
| } | |
| } | |
| for (const guildId of newGuilds) { | |
| try { | |
| console.log(`Adding new guild to tracking: ${guildId}`); | |
| const result = await db | |
| .insert(dbDiscordTable) | |
| .values({ | |
| guildId, | |
| allowedPublicSharing: false, | |
| feedrUpdatesChannelId: null, | |
| isInServer: true, | |
| memberCount: 0, | |
| }) | |
| .returning(); | |
| if (result) { | |
| console.log(`Successfully added guild ${guildId} to tracking.`); | |
| } else { | |
| console.error(`Failed to add guild ${guildId} to tracking.`); | |
| } | |
| } catch (error) { | |
| console.error( | |
| `Error while adding guild ${guildId} to tracking:`, | |
| error, | |
| ); | |
| } | |
| } |
| .where(eq(dbDiscordTable.guildId, guild.guildId)) | ||
| .returning(); | ||
|
|
||
| if (result) { |
Copilot
AI
Jul 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition if (result) is incorrect. Database update operations return an array, which is always truthy even when empty. Check result.length > 0 instead.
| if (result) { | |
| if (result.length > 0) { |
| }) | ||
| .returning(); | ||
|
|
||
| if (result) { |
Copilot
AI
Jul 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition if (result) is incorrect. Database insert operations return an array, which is always truthy even when empty. Check result.length > 0 instead.
| if (result) { | |
| if (result.length > 0) { |
| console.log("Waiting for guilds to load..."); | ||
| currentGuilds = client.guilds.cache.map((guild) => guild.id); | ||
| if (currentGuilds.length === 0) { | ||
| await new Promise((resolve) => setTimeout(resolve, 10000)); |
Copilot
AI
Jul 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The 10-second timeout is a magic number. Consider extracting this to a named constant like GUILD_LOAD_RETRY_INTERVAL for better maintainability.
| await new Promise((resolve) => setTimeout(resolve, 10000)); | |
| await new Promise((resolve) => setTimeout(resolve, GUILD_LOAD_RETRY_INTERVAL)); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This PR just adds guild events into the database and bot. Merging into the refactor/database branch because well.. it updates the database