diff --git a/src/discord/bot.ts b/src/discord/bot.ts index 4cfe2dd..350042f 100644 --- a/src/discord/bot.ts +++ b/src/discord/bot.ts @@ -5,6 +5,7 @@ import { BoosterRoleService } from "../services/boosterRoleService"; import { DrizzleBoosterRoleStore } from "../services/drizzleBoosterRoleStore"; import { DiscordRoleRepository } from "../services/discordRoleRepository"; import { handleGuildMemberUpdate } from "./events/guildMemberUpdate"; +import { sendBoostGreeting } from "./events/boostGreeting"; import { handleInteraction } from "./interactionHandler"; export function attachBotHandlers(client: Client, config: AppConfig): void { @@ -26,13 +27,25 @@ export function attachBotHandlers(client: Client, config: AppConfig): void { }); client.on("guildMemberUpdate", async (oldMember, newMember) => { + const eligibilityRoleId = config.boosterEligibilityRoleId; + + // Detect boost gain: member just acquired the booster eligibility role + const hadBooster = oldMember.roles.cache.has(eligibilityRoleId); + const hasBooster = newMember.roles.cache.has(eligibilityRoleId); + if (!hadBooster && hasBooster && config.boosterGreetingChannelId) { + await sendBoostGreeting(newMember, config.boosterGreetingChannelId).catch((err) => { + logger.error("Failed to send boost greeting", { error: String(err), userId: newMember.id }); + }); + } + + // Handle boost loss (existing behaviour) const service = new BoosterRoleService( store, new DiscordRoleRepository(newMember.guild), { anchorPosition: resolveAnchorPosition(newMember.guild, config.boosterRoleAnchorRoleId) } ); - await handleGuildMemberUpdate(oldMember, newMember, service, config.boosterEligibilityRoleId); + await handleGuildMemberUpdate(oldMember, newMember, service, eligibilityRoleId); }); } diff --git a/src/discord/events/boostGreeting.ts b/src/discord/events/boostGreeting.ts index a66baa0..107604a 100644 --- a/src/discord/events/boostGreeting.ts +++ b/src/discord/events/boostGreeting.ts @@ -4,8 +4,9 @@ type GuildTextChannel = { send(input: { content: string }): Promise; }; -type GuildMember = { +type GuildMemberLike = { id: string; + user?: { id: string }; guild: { id: string; channels: { @@ -14,46 +15,35 @@ type GuildMember = { }; }; }; - user?: { id: string }; }; /** - * Sends a welcome message to the configured greeting channel when a member - * newly acquires the booster eligibility role (i.e. just boosted the server). - * - * Only fires on a *gain* transition (hadBoosterRole = false → hasBoosterRole = true). - */ -export function handleBoostGreeting( - oldMember: GuildMember, - newMember: GuildMember, - greetingChannelId: string | null, -): void { - if (!greetingChannelId) return; - - const hadBoosterRole = oldMember.roles.cache.has(/** @type {any} */ (null) as any); - // Actually detect the gain: we need the actual role ID. The caller should pass it. -} - -/** - * Sends a welcome message when a member newly acquires the booster eligibility role. - * Must be called with the booster eligibility role ID to check correctly. + * Sends a welcome message to the configured greeting channel. + * Call this after confirming the member newly gained the booster eligibility role. */ export async function sendBoostGreeting( - member: { id: string; guild: { id: string; channels: { cache: { get(channelId: string): { send(input: { content: string }): Promise } | undefined } } } }, + member: GuildMemberLike, greetingChannelId: string, ): Promise { const channel = member.guild.channels.cache.get(greetingChannelId); + if (!channel) { - logger.warn("Boost greeting channel not found", { channelId: greetingChannelId, guildId: member.guild.id }); + logger.warn("Boost greeting channel not found", { + channelId: greetingChannelId, + guildId: member.guild.id, + }); return; } - await channel.send({ - content: `🎉 Thank you for boosting <@${member.user?.id ?? member.id}>! You can now claim a custom role using \`/booster-role claim\` in <#${greetingChannelId}>.` - }); + const mention = member.user?.id ?? member.id; + const message = + `🎉 Thank you for boosting <@${mention}>! ` + + `You can now claim a custom role using \`/booster-role claim\` in <#${greetingChannelId}>.`; + + await channel.send({ content: message }); logger.info("Boost greeting sent", { - userId: member.user?.id ?? member.id, + userId: member.id, guildId: member.guild.id, channelId: greetingChannelId, });