1
- / **
2
- * 🏺 Ritual Scroll: badgeEmitter.ts
3
- * Description: Listens for sponsorPing events, logs relic drops, and broadcasts
4
- * badge-ceremony embeds across configured Discord channels.
5
- * /
6
-
7
- import { Client, GatewayIntentBits, TextChannel, ThreadChannel, ChannelType } from 'discord.js';
1
+ import {
2
+ Client,
3
+ GatewayIntentBits,
4
+ TextChannel,
5
+ ThreadChannel,
6
+ ChannelType
7
+ } from 'discord.js';
8
8
import { BadgeClient } from 'kypria-badge-sdk';
9
9
import { promises as fs } from 'fs';
10
10
import path from 'path';
@@ -28,62 +28,58 @@ type Relic = SponsorPingPayload & {
28
28
};
29
29
30
30
(async () => {
31
- // 1️⃣ Load & validate badge-locations.yml
31
+ // Load badge config
32
32
const cfgPath = path.resolve(__ dirname, '../config/badge-locations.yml');
33
33
let config: Config = {};
34
-
35
34
try {
36
35
const raw = await fs.readFile(cfgPath, 'utf8');
37
36
config = (yaml.load(raw) as Config) ?? {};
38
37
} catch (err) {
39
- console.error('❌ Failed to load badge-locations.yml:', err);
38
+ console.error('❌ badge-locations.yml failed to load :', err);
40
39
process.exit(1);
41
40
}
42
41
43
- // 2️⃣ Initialize clients
42
+ // Initialize clients
44
43
const badgeClient = new BadgeClient({ apiToken: process.env.BADGE_API_TOKEN! });
45
- const discord = new Client({ intents: [ GatewayIntentBits.Guilds] });
44
+ const discord = new Client({ intents: [ GatewayIntentBits.Guilds] });
46
45
47
46
discord.once('ready', () => {
48
- console.log(` ✅ Discord bot ready as ${discord.user?.tag} ` );
47
+ console.log(` ✅ Discord bot logged in as ${discord.user?.tag} ` );
49
48
});
50
49
51
- // 3️⃣ Handle sponsorPing events
50
+ // Handle sponsorPing
52
51
badgeClient.on('sponsorPing', async ({ sponsorId, badgeName }: SponsorPingPayload) => {
53
- const channels = config[ badgeName] ?.drop_channels ?? [ ] ;
52
+ const channels = config[ badgeName] ?.drop_channels ?? [ ] ;
54
53
const timestamp = new Date().toISOString();
55
- const threadId = ` thread-${timestamp.replace(/[:.]/g, '-')} ` ;
54
+ const threadId = ` thread-${timestamp.replace(/[:.]/g, '-')} ` ;
56
55
const relic: Relic = { sponsorId, badgeName, timestamp, threadId };
57
56
58
- // 3a️⃣ Audit-log the relic
59
- const logDir = path.resolve(__dirname, '../threads');
57
+ // Write audit log
58
+ const logDir = path.resolve(__dirname, '../threads');
60
59
await fs.mkdir(logDir, { recursive: true });
61
- const fileName = `relic-drop--${badgeName}--${timestamp}.json`;
62
- const outPath = path.join(logDir, fileName);
63
- await fs.writeFile(outPath, JSON.stringify(relic, null, 2));
60
+ const logPath = path.join(logDir, `relic-drop--${badgeName}--${timestamp}.json`);
61
+ await fs.writeFile(logPath, JSON.stringify(relic, null, 2));
64
62
65
- // 3b️⃣ Broadcast the badge ceremony
63
+ // Broadcast drop
66
64
for (const chId of channels) {
67
65
try {
68
66
const channel = await discord.channels.fetch(chId);
69
67
if (
70
68
channel &&
71
- (channel.type === ChannelType.GuildText ||
72
- channel.type === ChannelType.GuildPublicThread)
69
+ (channel.type === ChannelType.GuildText || channel.type === ChannelType.GuildPublicThread)
73
70
) {
74
71
await dropBadge(channel as TextChannel | ThreadChannel, {
75
72
...relic,
76
- title: `🏅 ${badgeName}`,
73
+ title: `🏅 ${badgeName}`
77
74
});
78
75
} else {
79
- console.warn(`⚠️ Skipping non-text/thread channel: ${chId}`);
76
+ console.warn(`⚠️ Skipped invalid channel: ${chId}`);
80
77
}
81
78
} catch (err) {
82
- console.error(`❌ Error dropping badge in ${chId}:`, err);
79
+ console.error(`❌ Drop failed in channel ${chId}:`, err);
83
80
}
84
81
}
85
82
});
86
83
87
- // 4️⃣ Connect to Discord
88
84
await discord.login(process.env.DISCORD_TOKEN);
89
85
})();
0 commit comments