Skip to content

Commit 2d8a202

Browse files
committed
refactor(discord): use built-in discord.js type guards
- Replace custom isSendableChannel with channel.isSendable() - Add isTextBased() check when fetching channels - Use proper type guards for channel name access - Remove hacky 'as unknown as' casts - Update test mocks to include isTextBased/isSendable methods
1 parent 85c7ee6 commit 2d8a202

File tree

3 files changed

+33
-21
lines changed

3 files changed

+33
-21
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opensea-activity-bot",
3-
"version": "3.5.3",
3+
"version": "3.5.4",
44
"description": "A bot that shares new OpenSea events for a collection to Discord and Twitter.",
55
"author": "Ryan Ghods <ryan@ryanio.com>",
66
"license": "MIT",

src/platforms/discord.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,6 @@ const log = prefixedLogger("Discord");
4444
const groupConfig = getDefaultEventGroupConfig("DISCORD");
4545
const groupManager = new EventGroupManager(groupConfig);
4646

47-
// Type guard for sendable channels
48-
const isSendableChannel = (
49-
ch: TextBasedChannel
50-
): ch is TextBasedChannel & {
51-
send: (options: MessageCreateOptions) => Promise<unknown>;
52-
} => {
53-
const maybe = ch as unknown as {
54-
send?: (options: MessageCreateOptions) => Promise<unknown>;
55-
};
56-
return typeof maybe.send === "function";
57-
};
58-
5947
type ChannelEvents = [
6048
channelId: string,
6149
eventTypes: (EventType | BotEvent)[],
@@ -417,6 +405,15 @@ const login = (client: Client): Promise<void> =>
417405
client.login(process.env.DISCORD_TOKEN);
418406
});
419407

408+
// Helper to get channel name using proper type guards
409+
const getChannelName = (channel: TextBasedChannel): string => {
410+
// Guild channels have names, DMs don't
411+
if ("name" in channel && channel.name) {
412+
return channel.name;
413+
}
414+
return channel.id;
415+
};
416+
420417
const getChannels = async (
421418
client: Client,
422419
channelEvents: ChannelEvents
@@ -426,10 +423,12 @@ const getChannels = async (
426423
log.info("📡 Active channels:");
427424
for (const [channelId, events] of channelEvents) {
428425
const channel = await client.channels.fetch(channelId);
429-
channels[channelId] = channel as TextBasedChannel;
430-
const channelName =
431-
(channel as unknown as { name?: string; channelId?: string }).name ??
432-
(channel as unknown as { name?: string; channelId?: string }).channelId;
426+
if (!channel?.isTextBased()) {
427+
log.warn(`Channel ${channelId} is not a text channel, skipping`);
428+
continue;
429+
}
430+
channels[channelId] = channel;
431+
const channelName = getChannelName(channel);
433432
log.info(` • #${channelName}`);
434433
log.info(` └─ Events: ${events.join(", ")}`);
435434
}
@@ -461,7 +460,7 @@ const processGroupMessages = async (
461460
const allChannels = Object.values(discordChannels);
462461

463462
for (const channel of allChannels) {
464-
if (!isSendableChannel(channel)) {
463+
if (!channel.isSendable()) {
465464
continue;
466465
}
467466
await channel.send(message);
@@ -508,7 +507,7 @@ const processIndividualMessages = async (
508507
log.info("💬 Sending event notification");
509508

510509
for (const channel of channels) {
511-
if (!isSendableChannel(channel)) {
510+
if (!channel.isSendable()) {
512511
continue;
513512
}
514513
await channel.send(message);

test/platforms/discord.test.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import { jest } from "@jest/globals";
44
process.env.DISCORD_TOKEN = "x";
55

66
// Mock discord.js runtime API minimally
7-
const channelsMap: Record<string, { send: jest.Mock; id: string }> = {};
7+
const channelsMap: Record<
8+
string,
9+
{
10+
send: jest.Mock;
11+
id: string;
12+
isTextBased: () => boolean;
13+
isSendable: () => boolean;
14+
}
15+
> = {};
816
const setColorArgs: Record<string, string[]> = {};
917

1018
jest.mock("discord.js", () => {
@@ -22,7 +30,12 @@ jest.mock("discord.js", () => {
2230
channels: {
2331
fetch: (id: string) => {
2432
if (!channelsMap[id]) {
25-
channelsMap[id] = { send: jest.fn(), id };
33+
channelsMap[id] = {
34+
send: jest.fn(),
35+
id,
36+
isTextBased: () => true,
37+
isSendable: () => true,
38+
};
2639
}
2740
return Promise.resolve(channelsMap[id]);
2841
},

0 commit comments

Comments
 (0)