Skip to content

Commit 66497f1

Browse files
committed
refactor(core): allow easily supporting redis gateway
1 parent 87dee70 commit 66497f1

File tree

10 files changed

+126
-172
lines changed

10 files changed

+126
-172
lines changed

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
},
5555
"homepage": "https://discord.js.org",
5656
"dependencies": {
57+
"@discordjs/brokers": "workspace:^",
5758
"@discordjs/rest": "workspace:^",
5859
"@discordjs/util": "workspace:^",
5960
"@discordjs/ws": "workspace:^",

packages/core/src/client.ts

Lines changed: 22 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -4,75 +4,16 @@ import { calculateShardId } from '@discordjs/util';
44
import { WebSocketShardEvents } from '@discordjs/ws';
55
import { DiscordSnowflake } from '@sapphire/snowflake';
66
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
7-
import {
8-
GatewayDispatchEvents,
9-
GatewayOpcodes,
10-
type APIGuildMember,
11-
type GatewayAutoModerationActionExecutionDispatchData,
12-
type GatewayAutoModerationRuleCreateDispatchData,
13-
type GatewayAutoModerationRuleDeleteDispatchData,
14-
type GatewayAutoModerationRuleUpdateDispatchData,
15-
type GatewayChannelCreateDispatchData,
16-
type GatewayChannelDeleteDispatchData,
17-
type GatewayChannelPinsUpdateDispatchData,
18-
type GatewayChannelUpdateDispatchData,
19-
type GatewayGuildAuditLogEntryCreateDispatchData,
20-
type GatewayGuildBanAddDispatchData,
21-
type GatewayGuildBanRemoveDispatchData,
22-
type GatewayGuildCreateDispatchData,
23-
type GatewayGuildDeleteDispatchData,
24-
type GatewayGuildEmojisUpdateDispatchData,
25-
type GatewayGuildIntegrationsUpdateDispatchData,
26-
type GatewayGuildMemberAddDispatchData,
27-
type GatewayGuildMemberRemoveDispatchData,
28-
type GatewayGuildMemberUpdateDispatchData,
29-
type GatewayGuildMembersChunkDispatchData,
30-
type GatewayGuildRoleCreateDispatchData,
31-
type GatewayGuildRoleDeleteDispatchData,
32-
type GatewayGuildRoleUpdateDispatchData,
33-
type GatewayGuildScheduledEventCreateDispatchData,
34-
type GatewayGuildScheduledEventDeleteDispatchData,
35-
type GatewayGuildScheduledEventUpdateDispatchData,
36-
type GatewayGuildScheduledEventUserAddDispatchData,
37-
type GatewayGuildScheduledEventUserRemoveDispatchData,
38-
type GatewayGuildStickersUpdateDispatchData,
39-
type GatewayGuildUpdateDispatchData,
40-
type GatewayIntegrationCreateDispatchData,
41-
type GatewayIntegrationDeleteDispatchData,
42-
type GatewayIntegrationUpdateDispatchData,
43-
type GatewayInteractionCreateDispatchData,
44-
type GatewayInviteCreateDispatchData,
45-
type GatewayInviteDeleteDispatchData,
46-
type GatewayMessageCreateDispatchData,
47-
type GatewayMessageDeleteBulkDispatchData,
48-
type GatewayMessageDeleteDispatchData,
49-
type GatewayMessageReactionAddDispatchData,
50-
type GatewayMessageReactionRemoveAllDispatchData,
51-
type GatewayMessageReactionRemoveDispatchData,
52-
type GatewayMessageReactionRemoveEmojiDispatchData,
53-
type GatewayMessageUpdateDispatchData,
54-
type GatewayPresenceUpdateData,
55-
type GatewayPresenceUpdateDispatchData,
56-
type GatewayReadyDispatchData,
57-
type GatewayRequestGuildMembersData,
58-
type GatewayStageInstanceCreateDispatchData,
59-
type GatewayStageInstanceDeleteDispatchData,
60-
type GatewayStageInstanceUpdateDispatchData,
61-
type GatewayThreadCreateDispatchData,
62-
type GatewayThreadDeleteDispatchData,
63-
type GatewayThreadListSyncDispatchData,
64-
type GatewayThreadMemberUpdateDispatchData,
65-
type GatewayThreadMembersUpdateDispatchData,
66-
type GatewayThreadUpdateDispatchData,
67-
type GatewayTypingStartDispatchData,
68-
type GatewayUserUpdateDispatchData,
69-
type GatewayVoiceServerUpdateDispatchData,
70-
type GatewayVoiceStateUpdateData,
71-
type GatewayVoiceStateUpdateDispatchData,
72-
type GatewayWebhooksUpdateDispatchData,
7+
import { GatewayDispatchEvents, GatewayOpcodes } from 'discord-api-types/v10';
8+
import type {
9+
GatewayDispatchPayload,
10+
APIGuildMember,
11+
GatewayRequestGuildMembersData,
12+
GatewayPresenceUpdateData,
13+
GatewayVoiceStateUpdateData,
7314
} from 'discord-api-types/v10';
74-
import type { Gateway } from './Gateway.js';
7515
import { API } from './api/index.js';
16+
import type { Gateway } from './gateway/Gateway.js';
7617

7718
export interface IntrinsicProps {
7819
/**
@@ -89,87 +30,28 @@ export interface WithIntrinsicProps<T> extends IntrinsicProps {
8930
data: T;
9031
}
9132

92-
export interface MappedEvents {
93-
[GatewayDispatchEvents.AutoModerationActionExecution]: [
94-
WithIntrinsicProps<GatewayAutoModerationActionExecutionDispatchData>,
95-
];
96-
[GatewayDispatchEvents.AutoModerationRuleCreate]: [WithIntrinsicProps<GatewayAutoModerationRuleCreateDispatchData>];
97-
[GatewayDispatchEvents.AutoModerationRuleDelete]: [WithIntrinsicProps<GatewayAutoModerationRuleDeleteDispatchData>];
98-
[GatewayDispatchEvents.AutoModerationRuleUpdate]: [WithIntrinsicProps<GatewayAutoModerationRuleUpdateDispatchData>];
99-
[GatewayDispatchEvents.ChannelCreate]: [WithIntrinsicProps<GatewayChannelCreateDispatchData>];
100-
[GatewayDispatchEvents.ChannelDelete]: [WithIntrinsicProps<GatewayChannelDeleteDispatchData>];
101-
[GatewayDispatchEvents.ChannelPinsUpdate]: [WithIntrinsicProps<GatewayChannelPinsUpdateDispatchData>];
102-
[GatewayDispatchEvents.ChannelUpdate]: [WithIntrinsicProps<GatewayChannelUpdateDispatchData>];
103-
[GatewayDispatchEvents.GuildAuditLogEntryCreate]: [WithIntrinsicProps<GatewayGuildAuditLogEntryCreateDispatchData>];
104-
[GatewayDispatchEvents.GuildBanAdd]: [WithIntrinsicProps<GatewayGuildBanAddDispatchData>];
105-
[GatewayDispatchEvents.GuildBanRemove]: [WithIntrinsicProps<GatewayGuildBanRemoveDispatchData>];
106-
[GatewayDispatchEvents.GuildCreate]: [WithIntrinsicProps<GatewayGuildCreateDispatchData>];
107-
[GatewayDispatchEvents.GuildDelete]: [WithIntrinsicProps<GatewayGuildDeleteDispatchData>];
108-
[GatewayDispatchEvents.GuildEmojisUpdate]: [WithIntrinsicProps<GatewayGuildEmojisUpdateDispatchData>];
109-
[GatewayDispatchEvents.GuildIntegrationsUpdate]: [WithIntrinsicProps<GatewayGuildIntegrationsUpdateDispatchData>];
110-
[GatewayDispatchEvents.GuildMemberAdd]: [WithIntrinsicProps<GatewayGuildMemberAddDispatchData>];
111-
[GatewayDispatchEvents.GuildMemberRemove]: [WithIntrinsicProps<GatewayGuildMemberRemoveDispatchData>];
112-
[GatewayDispatchEvents.GuildMemberUpdate]: [WithIntrinsicProps<GatewayGuildMemberUpdateDispatchData>];
113-
[GatewayDispatchEvents.GuildMembersChunk]: [WithIntrinsicProps<GatewayGuildMembersChunkDispatchData>];
114-
[GatewayDispatchEvents.GuildRoleCreate]: [WithIntrinsicProps<GatewayGuildRoleCreateDispatchData>];
115-
[GatewayDispatchEvents.GuildRoleDelete]: [WithIntrinsicProps<GatewayGuildRoleDeleteDispatchData>];
116-
[GatewayDispatchEvents.GuildRoleUpdate]: [WithIntrinsicProps<GatewayGuildRoleUpdateDispatchData>];
117-
[GatewayDispatchEvents.GuildScheduledEventCreate]: [WithIntrinsicProps<GatewayGuildScheduledEventCreateDispatchData>];
118-
[GatewayDispatchEvents.GuildScheduledEventDelete]: [WithIntrinsicProps<GatewayGuildScheduledEventDeleteDispatchData>];
119-
[GatewayDispatchEvents.GuildScheduledEventUpdate]: [WithIntrinsicProps<GatewayGuildScheduledEventUpdateDispatchData>];
120-
[GatewayDispatchEvents.GuildScheduledEventUserAdd]: [
121-
WithIntrinsicProps<GatewayGuildScheduledEventUserAddDispatchData>,
122-
];
123-
[GatewayDispatchEvents.GuildScheduledEventUserRemove]: [
124-
WithIntrinsicProps<GatewayGuildScheduledEventUserRemoveDispatchData>,
125-
];
126-
[GatewayDispatchEvents.GuildStickersUpdate]: [WithIntrinsicProps<GatewayGuildStickersUpdateDispatchData>];
127-
[GatewayDispatchEvents.GuildUpdate]: [WithIntrinsicProps<GatewayGuildUpdateDispatchData>];
128-
[GatewayDispatchEvents.IntegrationCreate]: [WithIntrinsicProps<GatewayIntegrationCreateDispatchData>];
129-
[GatewayDispatchEvents.IntegrationDelete]: [WithIntrinsicProps<GatewayIntegrationDeleteDispatchData>];
130-
[GatewayDispatchEvents.IntegrationUpdate]: [WithIntrinsicProps<GatewayIntegrationUpdateDispatchData>];
131-
[GatewayDispatchEvents.InteractionCreate]: [WithIntrinsicProps<GatewayInteractionCreateDispatchData>];
132-
[GatewayDispatchEvents.InviteCreate]: [WithIntrinsicProps<GatewayInviteCreateDispatchData>];
133-
[GatewayDispatchEvents.InviteDelete]: [WithIntrinsicProps<GatewayInviteDeleteDispatchData>];
134-
[GatewayDispatchEvents.MessageCreate]: [WithIntrinsicProps<GatewayMessageCreateDispatchData>];
135-
[GatewayDispatchEvents.MessageDelete]: [WithIntrinsicProps<GatewayMessageDeleteDispatchData>];
136-
[GatewayDispatchEvents.MessageDeleteBulk]: [WithIntrinsicProps<GatewayMessageDeleteBulkDispatchData>];
137-
[GatewayDispatchEvents.MessageReactionAdd]: [WithIntrinsicProps<GatewayMessageReactionAddDispatchData>];
138-
[GatewayDispatchEvents.MessageReactionRemove]: [WithIntrinsicProps<GatewayMessageReactionRemoveDispatchData>];
139-
[GatewayDispatchEvents.MessageReactionRemoveAll]: [WithIntrinsicProps<GatewayMessageReactionRemoveAllDispatchData>];
140-
[GatewayDispatchEvents.MessageReactionRemoveEmoji]: [
141-
WithIntrinsicProps<GatewayMessageReactionRemoveEmojiDispatchData>,
142-
];
143-
[GatewayDispatchEvents.MessageUpdate]: [WithIntrinsicProps<GatewayMessageUpdateDispatchData>];
144-
[GatewayDispatchEvents.PresenceUpdate]: [WithIntrinsicProps<GatewayPresenceUpdateDispatchData>];
145-
[GatewayDispatchEvents.Ready]: [WithIntrinsicProps<GatewayReadyDispatchData>];
146-
[GatewayDispatchEvents.Resumed]: [WithIntrinsicProps<never>];
147-
[GatewayDispatchEvents.StageInstanceCreate]: [WithIntrinsicProps<GatewayStageInstanceCreateDispatchData>];
148-
[GatewayDispatchEvents.StageInstanceDelete]: [WithIntrinsicProps<GatewayStageInstanceDeleteDispatchData>];
149-
[GatewayDispatchEvents.StageInstanceUpdate]: [WithIntrinsicProps<GatewayStageInstanceUpdateDispatchData>];
150-
[GatewayDispatchEvents.ThreadCreate]: [WithIntrinsicProps<GatewayThreadCreateDispatchData>];
151-
[GatewayDispatchEvents.ThreadDelete]: [WithIntrinsicProps<GatewayThreadDeleteDispatchData>];
152-
[GatewayDispatchEvents.ThreadListSync]: [WithIntrinsicProps<GatewayThreadListSyncDispatchData>];
153-
[GatewayDispatchEvents.ThreadMemberUpdate]: [WithIntrinsicProps<GatewayThreadMemberUpdateDispatchData>];
154-
[GatewayDispatchEvents.ThreadMembersUpdate]: [WithIntrinsicProps<GatewayThreadMembersUpdateDispatchData>];
155-
[GatewayDispatchEvents.ThreadUpdate]: [WithIntrinsicProps<GatewayThreadUpdateDispatchData>];
156-
[GatewayDispatchEvents.TypingStart]: [WithIntrinsicProps<GatewayTypingStartDispatchData>];
157-
[GatewayDispatchEvents.UserUpdate]: [WithIntrinsicProps<GatewayUserUpdateDispatchData>];
158-
[GatewayDispatchEvents.VoiceServerUpdate]: [WithIntrinsicProps<GatewayVoiceServerUpdateDispatchData>];
159-
[GatewayDispatchEvents.VoiceStateUpdate]: [WithIntrinsicProps<GatewayVoiceStateUpdateDispatchData>];
160-
[GatewayDispatchEvents.WebhooksUpdate]: [WithIntrinsicProps<GatewayWebhooksUpdateDispatchData>];
161-
}
33+
// need this to be its own type for some reason, the compiler doesn't behave the same way if we in-line it
34+
type _DiscordEvents = {
35+
[K in GatewayDispatchEvents]: GatewayDispatchPayload & {
36+
t: K;
37+
};
38+
};
39+
40+
export type DiscordEvents = {
41+
// @ts-expect-error - unclear why this is an error, this behaves as expected
42+
[K in keyof _DiscordEvents]: _DiscordEvents[K]['d'];
43+
};
16244

163-
export type ManagerShardEventsMap = {
164-
[K in keyof MappedEvents]: MappedEvents[K];
45+
export type MappedEvents = {
46+
[K in keyof DiscordEvents]: [WithIntrinsicProps<DiscordEvents[K]>];
16547
};
16648

16749
export interface ClientOptions {
16850
gateway: Gateway;
16951
rest: REST;
17052
}
17153

172-
export class Client extends AsyncEventEmitter<ManagerShardEventsMap> {
54+
export class Client extends AsyncEventEmitter<MappedEvents> {
17355
public readonly rest: REST;
17456

17557
public readonly gateway: Gateway;

packages/core/src/Gateway.ts renamed to packages/core/src/gateway/Gateway.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ import type { GatewaySendPayload } from 'discord-api-types/v10';
66
* A Discord gateway-like interface that can be used to send & recieve events.
77
*/
88
export interface Gateway {
9-
/**
10-
* Gets how many shards your bot is running.
11-
*/
129
getShardCount(): Awaitable<number>;
1310
on(
1411
event: WebSocketShardEvents.Dispatch,
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { PubSubRedisBroker } from '@discordjs/brokers';
2+
import type { ManagerShardEventsMap, WebSocketShardEvents } from '@discordjs/ws';
3+
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
4+
import type { GatewaySendPayload, GatewayDispatchEvents } from 'discord-api-types/v10';
5+
import type { DiscordEvents } from '../client.js';
6+
import type { Gateway } from './Gateway.js';
7+
8+
interface BrokerIntrinsicProps {
9+
shardId: number;
10+
}
11+
12+
interface Events extends DiscordEvents {
13+
gateway_send: GatewaySendPayload;
14+
}
15+
16+
export type RedisBrokerDiscordEvents = {
17+
[K in keyof Events]: BrokerIntrinsicProps & { payload: Events[K] };
18+
};
19+
20+
export class RedisGateway
21+
extends AsyncEventEmitter<{ dispatch: ManagerShardEventsMap[WebSocketShardEvents.Dispatch] }>
22+
implements Gateway
23+
{
24+
public constructor(
25+
private readonly broker: PubSubRedisBroker<RedisBrokerDiscordEvents>,
26+
private readonly shardCount: number,
27+
) {
28+
super();
29+
}
30+
31+
public getShardCount(): number {
32+
return this.shardCount;
33+
}
34+
35+
public async send(shardId: number, payload: GatewaySendPayload): Promise<void> {
36+
await this.broker.publish('gateway_send', { payload, shardId });
37+
}
38+
39+
public async init(group: string, events: GatewayDispatchEvents[]) {
40+
for (const event of events) {
41+
this.broker.on(event, ({ data: { payload, shardId }, ack }) => {
42+
// @ts-expect-error - Union shenanigans
43+
this.emit('dispatch', { shardId, data: payload });
44+
void ack();
45+
});
46+
}
47+
48+
await this.broker.subscribe(group, events);
49+
}
50+
}

packages/core/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export * from './api/index.js';
2-
export * from './client.js';
2+
export * from './gateway/Gateway.js';
3+
export * from './gateway/RedisGateway.js';
34
export * from './util/index.js';
5+
export * from './client.js';
46

57
export * from 'discord-api-types/v10';
68

packages/redis-gateway/README.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,50 @@ await broker.publish('gateway_send', {
7070
status: PresenceUpdateStatus.Online,
7171
},
7272
});
73+
74+
// if you were to start this process multiple times (e.g. multiple apps using 'work_balancing_group'),
75+
// they would automatically work balance those interaction create events
76+
await broker.subscribe('work_balancing_group', [GatewayDispatchEvents.InteractionCreate]);
7377
```
7478

75-
For TypeScript usage, you can pass in a gereric type to the `PubSubRedisBroker` to map out all the events,
76-
refer to [this container's implementation](https://github.com/discordjs/discord.js/tree/main/packages/redis-gateway/src/index.ts#L15) for reference.
79+
For TypeScript usage, you can pass in a gereric type to the `PubSubRedisBroker` to map out all the events, a mapped
80+
interface is available in `@discordjs/core` as `RedisBrokerDiscordEvents`.
81+
82+
If you wish, you can also just use `@discordjs/core`:
83+
84+
```ts
85+
import { REST } from '@discordjs/rest';
86+
import Redis from 'ioredis';
87+
import { PubSubRedisBroker } from '@discordjs/brokers';
88+
import {
89+
GatewayDispatchEvents,
90+
GatewayIntentBits,
91+
InteractionType,
92+
MessageFlags,
93+
Client,
94+
RedisGateway,
95+
} from '@discordjs/core';
96+
97+
const rest = new REST({ version: '10' }).setToken(token);
98+
99+
const redis = new Redis();
100+
// you can get retrieve your shard count however you want, it's used for some calculations and should be your bot's TOTAL shard count
101+
// across "clusters" or anything else.
102+
const broker = new PubSubRedisBroker({ redisClient: redis }, Number(process.env.SHARD_COUNT!));
103+
const gateway = new RedisGateway(broker);
77104

78-
Also note that [core](https://github.com/discordjs/discord.js/tree/main/packages/core) supports an
79-
abstract `gateway` property that can be easily implemented, making this pretty comfortable to
80-
use in conjunction. Refer to the [Gateway documentation](https://discord.js.org/docs/packages/core/main/Gateway:Interface).
105+
const client = new Client({ rest, gateway });
106+
107+
client.on(GatewayDispatchEvents.InteractionCreate, async ({ data: interaction, api }) => {
108+
if (interaction.type !== InteractionType.ApplicationCommand || interaction.data.name !== 'ping') {
109+
return;
110+
}
111+
112+
await api.interactions.reply(interaction.id, interaction.token, { content: 'Pong!', flags: MessageFlags.Ephemeral });
113+
});
114+
115+
await gateway.init('work_balancing_group', [GatewayDispatchEvents.InteractionCreate]);
116+
```
81117

82118
## Links
83119

packages/redis-gateway/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"undici": "^5.22.0"
5454
},
5555
"devDependencies": {
56+
"@discordjs/core": "workspace:^",
5657
"@types/node": "16.18.25",
5758
"cross-env": "^7.0.3",
5859
"eslint": "^8.39.0",

packages/redis-gateway/src/discordEvents.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

packages/redis-gateway/src/index.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { randomBytes } from 'node:crypto';
22
import { PubSubRedisBroker } from '@discordjs/brokers';
3+
import type { RedisBrokerDiscordEvents } from '@discordjs/core';
34
import type { RESTOptions } from '@discordjs/rest';
45
import { REST } from '@discordjs/rest';
56
import type { OptionalWebSocketManagerOptions, RequiredWebSocketManagerOptions } from '@discordjs/ws';
67
import { WorkerShardingStrategy, CompressionMethod, WebSocketManager, WebSocketShardEvents } from '@discordjs/ws';
78
import Redis from 'ioredis';
89
import { ProxyAgent } from 'undici';
9-
import type { DiscordEvents } from './discordEvents.js';
1010
import { Env } from './env.js';
1111

1212
const env = new Env();
1313

1414
const redisClient = new Redis(env.redisUrl);
15-
const broker = new PubSubRedisBroker<DiscordEvents>({
15+
const broker = new PubSubRedisBroker<RedisBrokerDiscordEvents>({
1616
redisClient,
1717
});
1818

@@ -46,13 +46,11 @@ gateway
4646
.on(WebSocketShardEvents.Hello, ({ shardId }) => console.log(`[WS Shard ${shardId}] [HELLO]`))
4747
.on(WebSocketShardEvents.Ready, ({ shardId }) => console.log(`[WS Shard ${shardId}] [READY]`))
4848
.on(WebSocketShardEvents.Resumed, ({ shardId }) => console.log(`[WS Shard ${shardId}] [RESUMED]`))
49-
.on(WebSocketShardEvents.Dispatch, ({ data }) => void broker.publish(data.t, data.d));
50-
51-
broker.on('gateway_send', async ({ data, ack }) => {
52-
for (const shardId of await gateway.getShardIds()) {
53-
await gateway.send(shardId, data);
54-
}
49+
// @ts-expect-error - Union shenanigans
50+
.on(WebSocketShardEvents.Dispatch, ({ data, shardId }) => void broker.publish(data.t, { shardId, payload: data.d }));
5551

52+
broker.on('gateway_send', async ({ data: { payload, shardId }, ack }) => {
53+
await gateway.send(shardId, payload);
5654
await ack();
5755
});
5856

yarn.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,7 @@ __metadata:
20962096
version: 0.0.0-use.local
20972097
resolution: "@discordjs/core@workspace:packages/core"
20982098
dependencies:
2099+
"@discordjs/brokers": "workspace:^"
20992100
"@discordjs/rest": "workspace:^"
21002101
"@discordjs/util": "workspace:^"
21012102
"@discordjs/ws": "workspace:^"
@@ -2318,6 +2319,7 @@ __metadata:
23182319
resolution: "@discordjs/redis-gateway@workspace:packages/redis-gateway"
23192320
dependencies:
23202321
"@discordjs/brokers": "workspace:^"
2322+
"@discordjs/core": "workspace:^"
23212323
"@discordjs/rest": "workspace:^"
23222324
"@discordjs/ws": "workspace:^"
23232325
"@types/node": 16.18.25

0 commit comments

Comments
 (0)