feat(config): add BOOSTER_GREETING_CHANNEL_ID environment variable

This commit is contained in:
MythEclipse
2026-06-06 03:29:21 +07:00
parent 0f6d9110a3
commit 898ae2dd56
4 changed files with 67 additions and 2 deletions
+1
View File
@@ -4,4 +4,5 @@ DISCORD_GUILD_ID=
DATABASE_URL=postgresql://booster_role:booster_role@localhost:5432/booster_role DATABASE_URL=postgresql://booster_role:booster_role@localhost:5432/booster_role
BOOSTER_ROLE_ANCHOR_ROLE_ID= BOOSTER_ROLE_ANCHOR_ROLE_ID=
BOOSTER_ELIGIBILITY_ROLE_ID=1206431347925852162 BOOSTER_ELIGIBILITY_ROLE_ID=1206431347925852162
BOOSTER_GREETING_CHANNEL_ID=
LOG_LEVEL=info LOG_LEVEL=info
+3 -1
View File
@@ -73,12 +73,13 @@ jobs:
DATABASE_URL: ${{ secrets.DATABASE_URL }} DATABASE_URL: ${{ secrets.DATABASE_URL }}
BOOSTER_ROLE_ANCHOR_ROLE_ID: ${{ secrets.BOOSTER_ROLE_ANCHOR_ROLE_ID }} BOOSTER_ROLE_ANCHOR_ROLE_ID: ${{ secrets.BOOSTER_ROLE_ANCHOR_ROLE_ID }}
BOOSTER_ELIGIBILITY_ROLE_ID: ${{ secrets.BOOSTER_ELIGIBILITY_ROLE_ID }} BOOSTER_ELIGIBILITY_ROLE_ID: ${{ secrets.BOOSTER_ELIGIBILITY_ROLE_ID }}
BOOSTER_GREETING_CHANNEL_ID: ${{ secrets.BOOSTER_GREETING_CHANNEL_ID }}
LOG_LEVEL: ${{ secrets.LOG_LEVEL }} LOG_LEVEL: ${{ secrets.LOG_LEVEL }}
with: with:
host: ${{ secrets.VPS_HOST }} host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }} username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }} key: ${{ secrets.VPS_SSH_KEY }}
envs: IMAGE_NAME,GHCR_USERNAME,GHCR_TOKEN,DISCORD_TOKEN,DISCORD_CLIENT_ID,DISCORD_GUILD_ID,DATABASE_URL,BOOSTER_ROLE_ANCHOR_ROLE_ID,BOOSTER_ELIGIBILITY_ROLE_ID,LOG_LEVEL envs: IMAGE_NAME,GHCR_USERNAME,GHCR_TOKEN,DISCORD_TOKEN,DISCORD_CLIENT_ID,DISCORD_GUILD_ID,DATABASE_URL,BOOSTER_ROLE_ANCHOR_ROLE_ID,BOOSTER_ELIGIBILITY_ROLE_ID,BOOSTER_GREETING_CHANNEL_ID,LOG_LEVEL
script: | script: |
set -eu set -eu
mkdir -p /opt/booster-role mkdir -p /opt/booster-role
@@ -91,6 +92,7 @@ jobs:
DATABASE_URL=${DATABASE_URL:?DATABASE_URL is required} DATABASE_URL=${DATABASE_URL:?DATABASE_URL is required}
BOOSTER_ROLE_ANCHOR_ROLE_ID=${BOOSTER_ROLE_ANCHOR_ROLE_ID} BOOSTER_ROLE_ANCHOR_ROLE_ID=${BOOSTER_ROLE_ANCHOR_ROLE_ID}
BOOSTER_ELIGIBILITY_ROLE_ID=${BOOSTER_ELIGIBILITY_ROLE_ID:-1206431347925852162} BOOSTER_ELIGIBILITY_ROLE_ID=${BOOSTER_ELIGIBILITY_ROLE_ID:-1206431347925852162}
BOOSTER_GREETING_CHANNEL_ID=${BOOSTER_GREETING_CHANNEL_ID}
LOG_LEVEL=${LOG_LEVEL:-info} LOG_LEVEL=${LOG_LEVEL:-info}
EOF EOF
+3 -1
View File
@@ -6,6 +6,7 @@ export type AppConfig = {
boosterRoleAnchorRoleId: string | null; boosterRoleAnchorRoleId: string | null;
boosterEligibilityRoleId: string; boosterEligibilityRoleId: string;
boostCleanupIntervalMs: number; boostCleanupIntervalMs: number;
boosterGreetingChannelId: string | null;
}; };
export function loadConfig(env: Record<string, string | undefined> = process.env): AppConfig { export function loadConfig(env: Record<string, string | undefined> = process.env): AppConfig {
@@ -16,7 +17,8 @@ export function loadConfig(env: Record<string, string | undefined> = process.env
databaseUrl: env.DATABASE_URL ?? "postgresql://booster_role:booster_role@localhost:5432/booster_role", databaseUrl: env.DATABASE_URL ?? "postgresql://booster_role:booster_role@localhost:5432/booster_role",
boosterRoleAnchorRoleId: env.BOOSTER_ROLE_ANCHOR_ROLE_ID ?? null, boosterRoleAnchorRoleId: env.BOOSTER_ROLE_ANCHOR_ROLE_ID ?? null,
boosterEligibilityRoleId: env.BOOSTER_ELIGIBILITY_ROLE_ID ?? "1206431347925852162", boosterEligibilityRoleId: env.BOOSTER_ELIGIBILITY_ROLE_ID ?? "1206431347925852162",
boostCleanupIntervalMs: Number(env.BOOST_CLEANUP_INTERVAL_MS) || 5 * 60 * 1000 boostCleanupIntervalMs: Number(env.BOOST_CLEANUP_INTERVAL_MS) || 5 * 60 * 1000,
boosterGreetingChannelId: env.BOOSTER_GREETING_CHANNEL_ID ?? null
}; };
} }
+60
View File
@@ -0,0 +1,60 @@
import { logger } from "../../logger";
type GuildTextChannel = {
send(input: { content: string }): Promise<unknown>;
};
type GuildMember = {
id: string;
guild: {
id: string;
channels: {
cache: {
get(channelId: string): GuildTextChannel | undefined;
};
};
};
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.
*/
export async function sendBoostGreeting(
member: { id: string; guild: { id: string; channels: { cache: { get(channelId: string): { send(input: { content: string }): Promise<unknown> } | undefined } } } },
greetingChannelId: string,
): Promise<void> {
const channel = member.guild.channels.cache.get(greetingChannelId);
if (!channel) {
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}>.`
});
logger.info("Boost greeting sent", {
userId: member.user?.id ?? member.id,
guildId: member.guild.id,
channelId: greetingChannelId,
});
}