feat(boost-greeting): integrate greeting into guild member update and clean up handler

This commit is contained in:
MythEclipse
2026-06-06 03:30:16 +07:00
parent 898ae2dd56
commit 9c193c3530
2 changed files with 31 additions and 28 deletions
+14 -1
View File
@@ -5,6 +5,7 @@ import { BoosterRoleService } from "../services/boosterRoleService";
import { DrizzleBoosterRoleStore } from "../services/drizzleBoosterRoleStore"; import { DrizzleBoosterRoleStore } from "../services/drizzleBoosterRoleStore";
import { DiscordRoleRepository } from "../services/discordRoleRepository"; import { DiscordRoleRepository } from "../services/discordRoleRepository";
import { handleGuildMemberUpdate } from "./events/guildMemberUpdate"; import { handleGuildMemberUpdate } from "./events/guildMemberUpdate";
import { sendBoostGreeting } from "./events/boostGreeting";
import { handleInteraction } from "./interactionHandler"; import { handleInteraction } from "./interactionHandler";
export function attachBotHandlers(client: Client, config: AppConfig): void { 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) => { 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( const service = new BoosterRoleService(
store, store,
new DiscordRoleRepository(newMember.guild), new DiscordRoleRepository(newMember.guild),
{ anchorPosition: resolveAnchorPosition(newMember.guild, config.boosterRoleAnchorRoleId) } { anchorPosition: resolveAnchorPosition(newMember.guild, config.boosterRoleAnchorRoleId) }
); );
await handleGuildMemberUpdate(oldMember, newMember, service, config.boosterEligibilityRoleId); await handleGuildMemberUpdate(oldMember, newMember, service, eligibilityRoleId);
}); });
} }
+17 -27
View File
@@ -4,8 +4,9 @@ type GuildTextChannel = {
send(input: { content: string }): Promise<unknown>; send(input: { content: string }): Promise<unknown>;
}; };
type GuildMember = { type GuildMemberLike = {
id: string; id: string;
user?: { id: string };
guild: { guild: {
id: string; id: string;
channels: { channels: {
@@ -14,46 +15,35 @@ type GuildMember = {
}; };
}; };
}; };
user?: { id: string };
}; };
/** /**
* Sends a welcome message to the configured greeting channel when a member * Sends a welcome message to the configured greeting channel.
* newly acquires the booster eligibility role (i.e. just boosted the server). * Call this after confirming the member newly gained the booster eligibility role.
*
* 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.
*/ */
export async function sendBoostGreeting( export async function sendBoostGreeting(
member: { id: string; guild: { id: string; channels: { cache: { get(channelId: string): { send(input: { content: string }): Promise<unknown> } | undefined } } } }, member: GuildMemberLike,
greetingChannelId: string, greetingChannelId: string,
): Promise<void> { ): Promise<void> {
const channel = member.guild.channels.cache.get(greetingChannelId); const channel = member.guild.channels.cache.get(greetingChannelId);
if (!channel) { 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; return;
} }
await channel.send({ const mention = member.user?.id ?? member.id;
content: `🎉 Thank you for boosting <@${member.user?.id ?? member.id}>! You can now claim a custom role using \`/booster-role claim\` in <#${greetingChannelId}>.` 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", { logger.info("Boost greeting sent", {
userId: member.user?.id ?? member.id, userId: member.id,
guildId: member.guild.id, guildId: member.guild.id,
channelId: greetingChannelId, channelId: greetingChannelId,
}); });