feat: add booster eligibility role and logging functionality

- Added BOOSTER_ELIGIBILITY_ROLE_ID and LOG_LEVEL to .env.example and README.md
- Updated GitHub Actions workflow to include new environment variables
- Enhanced configuration loading to support booster eligibility role
- Implemented role checking logic in bot handlers and guild member updates
- Introduced logging using Winston for better traceability of actions
- Created tests for guild member update handling to ensure correct role removal
- Refactored DiscordRoleRepository to simplify role position resolution
This commit is contained in:
MythEclipse
2026-05-21 15:27:10 +07:00
parent bfa91063ff
commit cc6975825f
13 changed files with 162 additions and 22 deletions
+22 -4
View File
@@ -21,7 +21,7 @@ export function attachBotHandlers(client: Client, config: AppConfig): void {
);
await handleInteraction(interaction, service, {
isBoosting: async () => Boolean(interaction.member && "premiumSince" in interaction.member && interaction.member.premiumSince)
isBoosting: async () => memberHasRole(interaction.member, config.boosterEligibilityRoleId)
});
});
@@ -32,13 +32,31 @@ export function attachBotHandlers(client: Client, config: AppConfig): void {
{ anchorPosition: resolveAnchorPosition(newMember.guild, config.boosterRoleAnchorRoleId) }
);
await handleGuildMemberUpdate(oldMember, newMember, service);
await handleGuildMemberUpdate(oldMember, newMember, service, config.boosterEligibilityRoleId);
});
}
function memberHasRole(member: unknown, roleId: string): boolean {
return Boolean(
member &&
typeof member === "object" &&
"roles" in member &&
member.roles &&
typeof member.roles === "object" &&
"cache" in member.roles &&
member.roles.cache &&
typeof member.roles.cache === "object" &&
"has" in member.roles.cache &&
typeof member.roles.cache.has === "function" &&
member.roles.cache.has(roleId)
);
}
function resolveAnchorPosition(guild: { members: { me: { roles: { highest: { position: number } } } | null }; roles: { cache: { get(id: string): { position: number } | undefined } } }, anchorRoleId: string | null): number {
if (!anchorRoleId) return botRolePosition(guild);
return guild.roles.cache.get(anchorRoleId)?.position ?? botRolePosition(guild);
const botPosition = botRolePosition(guild);
if (!anchorRoleId) return botPosition;
const anchorPosition = guild.roles.cache.get(anchorRoleId)?.position ?? botPosition;
return Math.min(anchorPosition, botPosition);
}
function botRolePosition(guild: { members: { me: { roles: { highest: { position: number } } } | null } }): number {