|
|
|
|
@@ -1,66 +1,74 @@
|
|
|
|
|
import { setTimeout } from 'node:timers';
|
|
|
|
|
import type { REST } from '@discordjs/rest';
|
|
|
|
|
import { calculateShardId } from '@discordjs/util';
|
|
|
|
|
import { WebSocketShardEvents, type WebSocketManager } from '@discordjs/ws';
|
|
|
|
|
import { DiscordSnowflake } from '@sapphire/snowflake';
|
|
|
|
|
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
|
|
|
|
import type {
|
|
|
|
|
GatewayAutoModerationActionExecutionDispatchData,
|
|
|
|
|
GatewayAutoModerationRuleCreateDispatchData,
|
|
|
|
|
GatewayAutoModerationRuleDeleteDispatchData,
|
|
|
|
|
GatewayAutoModerationRuleUpdateDispatchData,
|
|
|
|
|
GatewayChannelCreateDispatchData,
|
|
|
|
|
GatewayChannelDeleteDispatchData,
|
|
|
|
|
GatewayChannelPinsUpdateDispatchData,
|
|
|
|
|
GatewayChannelUpdateDispatchData,
|
|
|
|
|
import {
|
|
|
|
|
GatewayDispatchEvents,
|
|
|
|
|
GatewayGuildBanAddDispatchData,
|
|
|
|
|
GatewayGuildBanRemoveDispatchData,
|
|
|
|
|
GatewayGuildCreateDispatchData,
|
|
|
|
|
GatewayGuildDeleteDispatchData,
|
|
|
|
|
GatewayGuildEmojisUpdateDispatchData,
|
|
|
|
|
GatewayGuildIntegrationsUpdateDispatchData,
|
|
|
|
|
GatewayGuildMemberAddDispatchData,
|
|
|
|
|
GatewayGuildMemberRemoveDispatchData,
|
|
|
|
|
GatewayGuildMembersChunkDispatchData,
|
|
|
|
|
GatewayGuildMemberUpdateDispatchData,
|
|
|
|
|
GatewayGuildRoleCreateDispatchData,
|
|
|
|
|
GatewayGuildRoleDeleteDispatchData,
|
|
|
|
|
GatewayGuildRoleUpdateDispatchData,
|
|
|
|
|
GatewayGuildScheduledEventCreateDispatchData,
|
|
|
|
|
GatewayGuildScheduledEventDeleteDispatchData,
|
|
|
|
|
GatewayGuildScheduledEventUpdateDispatchData,
|
|
|
|
|
GatewayGuildScheduledEventUserAddDispatchData,
|
|
|
|
|
GatewayGuildScheduledEventUserRemoveDispatchData,
|
|
|
|
|
GatewayGuildStickersUpdateDispatchData,
|
|
|
|
|
GatewayGuildUpdateDispatchData,
|
|
|
|
|
GatewayIntegrationCreateDispatchData,
|
|
|
|
|
GatewayIntegrationDeleteDispatchData,
|
|
|
|
|
GatewayIntegrationUpdateDispatchData,
|
|
|
|
|
GatewayInteractionCreateDispatchData,
|
|
|
|
|
GatewayInviteCreateDispatchData,
|
|
|
|
|
GatewayInviteDeleteDispatchData,
|
|
|
|
|
GatewayMessageCreateDispatchData,
|
|
|
|
|
GatewayMessageDeleteBulkDispatchData,
|
|
|
|
|
GatewayMessageDeleteDispatchData,
|
|
|
|
|
GatewayMessageReactionAddDispatchData,
|
|
|
|
|
GatewayMessageReactionRemoveAllDispatchData,
|
|
|
|
|
GatewayMessageReactionRemoveDispatchData,
|
|
|
|
|
GatewayMessageReactionRemoveEmojiDispatchData,
|
|
|
|
|
GatewayMessageUpdateDispatchData,
|
|
|
|
|
GatewayPresenceUpdateDispatchData,
|
|
|
|
|
GatewayReadyDispatchData,
|
|
|
|
|
GatewayStageInstanceCreateDispatchData,
|
|
|
|
|
GatewayStageInstanceDeleteDispatchData,
|
|
|
|
|
GatewayStageInstanceUpdateDispatchData,
|
|
|
|
|
GatewayThreadCreateDispatchData,
|
|
|
|
|
GatewayThreadDeleteDispatchData,
|
|
|
|
|
GatewayThreadListSyncDispatchData,
|
|
|
|
|
GatewayThreadMembersUpdateDispatchData,
|
|
|
|
|
GatewayThreadMemberUpdateDispatchData,
|
|
|
|
|
GatewayThreadUpdateDispatchData,
|
|
|
|
|
GatewayTypingStartDispatchData,
|
|
|
|
|
GatewayUserUpdateDispatchData,
|
|
|
|
|
GatewayVoiceServerUpdateDispatchData,
|
|
|
|
|
GatewayVoiceStateUpdateDispatchData,
|
|
|
|
|
GatewayWebhooksUpdateDispatchData,
|
|
|
|
|
GatewayOpcodes,
|
|
|
|
|
type GatewayVoiceStateUpdateData,
|
|
|
|
|
type APIGuildMember,
|
|
|
|
|
type GatewayAutoModerationActionExecutionDispatchData,
|
|
|
|
|
type GatewayAutoModerationRuleCreateDispatchData,
|
|
|
|
|
type GatewayAutoModerationRuleDeleteDispatchData,
|
|
|
|
|
type GatewayAutoModerationRuleUpdateDispatchData,
|
|
|
|
|
type GatewayChannelCreateDispatchData,
|
|
|
|
|
type GatewayChannelDeleteDispatchData,
|
|
|
|
|
type GatewayChannelPinsUpdateDispatchData,
|
|
|
|
|
type GatewayChannelUpdateDispatchData,
|
|
|
|
|
type GatewayGuildBanAddDispatchData,
|
|
|
|
|
type GatewayGuildBanRemoveDispatchData,
|
|
|
|
|
type GatewayGuildCreateDispatchData,
|
|
|
|
|
type GatewayGuildDeleteDispatchData,
|
|
|
|
|
type GatewayGuildEmojisUpdateDispatchData,
|
|
|
|
|
type GatewayGuildIntegrationsUpdateDispatchData,
|
|
|
|
|
type GatewayGuildMemberAddDispatchData,
|
|
|
|
|
type GatewayGuildMemberRemoveDispatchData,
|
|
|
|
|
type GatewayGuildMembersChunkDispatchData,
|
|
|
|
|
type GatewayGuildMemberUpdateDispatchData,
|
|
|
|
|
type GatewayGuildRoleCreateDispatchData,
|
|
|
|
|
type GatewayGuildRoleDeleteDispatchData,
|
|
|
|
|
type GatewayGuildRoleUpdateDispatchData,
|
|
|
|
|
type GatewayGuildScheduledEventCreateDispatchData,
|
|
|
|
|
type GatewayGuildScheduledEventDeleteDispatchData,
|
|
|
|
|
type GatewayGuildScheduledEventUpdateDispatchData,
|
|
|
|
|
type GatewayGuildScheduledEventUserAddDispatchData,
|
|
|
|
|
type GatewayGuildScheduledEventUserRemoveDispatchData,
|
|
|
|
|
type GatewayGuildStickersUpdateDispatchData,
|
|
|
|
|
type GatewayGuildUpdateDispatchData,
|
|
|
|
|
type GatewayIntegrationCreateDispatchData,
|
|
|
|
|
type GatewayIntegrationDeleteDispatchData,
|
|
|
|
|
type GatewayIntegrationUpdateDispatchData,
|
|
|
|
|
type GatewayInteractionCreateDispatchData,
|
|
|
|
|
type GatewayInviteCreateDispatchData,
|
|
|
|
|
type GatewayInviteDeleteDispatchData,
|
|
|
|
|
type GatewayMessageCreateDispatchData,
|
|
|
|
|
type GatewayMessageDeleteBulkDispatchData,
|
|
|
|
|
type GatewayMessageDeleteDispatchData,
|
|
|
|
|
type GatewayMessageReactionAddDispatchData,
|
|
|
|
|
type GatewayMessageReactionRemoveAllDispatchData,
|
|
|
|
|
type GatewayMessageReactionRemoveDispatchData,
|
|
|
|
|
type GatewayMessageReactionRemoveEmojiDispatchData,
|
|
|
|
|
type GatewayMessageUpdateDispatchData,
|
|
|
|
|
type GatewayPresenceUpdateDispatchData,
|
|
|
|
|
type GatewayReadyDispatchData,
|
|
|
|
|
type GatewayRequestGuildMembersData,
|
|
|
|
|
type GatewayStageInstanceCreateDispatchData,
|
|
|
|
|
type GatewayStageInstanceDeleteDispatchData,
|
|
|
|
|
type GatewayStageInstanceUpdateDispatchData,
|
|
|
|
|
type GatewayThreadCreateDispatchData,
|
|
|
|
|
type GatewayThreadDeleteDispatchData,
|
|
|
|
|
type GatewayThreadListSyncDispatchData,
|
|
|
|
|
type GatewayThreadMembersUpdateDispatchData,
|
|
|
|
|
type GatewayThreadMemberUpdateDispatchData,
|
|
|
|
|
type GatewayThreadUpdateDispatchData,
|
|
|
|
|
type GatewayTypingStartDispatchData,
|
|
|
|
|
type GatewayUserUpdateDispatchData,
|
|
|
|
|
type GatewayVoiceServerUpdateDispatchData,
|
|
|
|
|
type GatewayVoiceStateUpdateDispatchData,
|
|
|
|
|
type GatewayWebhooksUpdateDispatchData,
|
|
|
|
|
type GatewayPresenceUpdateData,
|
|
|
|
|
} from 'discord-api-types/v10';
|
|
|
|
|
import { API } from './api/index.js';
|
|
|
|
|
|
|
|
|
|
@@ -158,22 +166,106 @@ export interface ClientOptions {
|
|
|
|
|
ws: WebSocketManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createClient({ rest, ws }: ClientOptions) {
|
|
|
|
|
const api = new API(rest);
|
|
|
|
|
const emitter = new AsyncEventEmitter<ManagerShardEventsMap>();
|
|
|
|
|
export class Client extends AsyncEventEmitter<ManagerShardEventsMap> {
|
|
|
|
|
public readonly rest: REST;
|
|
|
|
|
|
|
|
|
|
function wrapIntrinsicProps<T>(obj: T, shardId: number): WithIntrinsicProps<T> {
|
|
|
|
|
public readonly ws: WebSocketManager;
|
|
|
|
|
|
|
|
|
|
public readonly api: API;
|
|
|
|
|
|
|
|
|
|
public constructor({ rest, ws }: ClientOptions) {
|
|
|
|
|
super();
|
|
|
|
|
this.rest = rest;
|
|
|
|
|
this.ws = ws;
|
|
|
|
|
this.api = new API(rest);
|
|
|
|
|
|
|
|
|
|
this.ws.on(WebSocketShardEvents.Dispatch, ({ data: dispatch, shardId }) => {
|
|
|
|
|
// @ts-expect-error event props can't be resolved properly, but they are correct
|
|
|
|
|
this.emit(dispatch.t, this.wrapIntrinsicProps(dispatch.d, shardId));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Requests guild members from the gateway.
|
|
|
|
|
*
|
|
|
|
|
* @see {@link https://discord.com/developers/docs/topics/gateway-events#request-guild-members}
|
|
|
|
|
* @param options - The options for the request
|
|
|
|
|
* @param timeout - The timeout for waiting for each guild members chunk event
|
|
|
|
|
*/
|
|
|
|
|
public async requestGuildMembers(options: GatewayRequestGuildMembersData, timeout = 10_000) {
|
|
|
|
|
const shardId = calculateShardId(options.guild_id, await this.ws.getShardCount());
|
|
|
|
|
const nonce = options.nonce ?? DiscordSnowflake.generate().toString();
|
|
|
|
|
|
|
|
|
|
const promise = new Promise<APIGuildMember[]>((resolve, reject) => {
|
|
|
|
|
const guildMembers: APIGuildMember[] = [];
|
|
|
|
|
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
reject(new Error('Request timed out'));
|
|
|
|
|
}, timeout);
|
|
|
|
|
|
|
|
|
|
const handler = ({ data }: MappedEvents[GatewayDispatchEvents.GuildMembersChunk][0]) => {
|
|
|
|
|
timer.refresh();
|
|
|
|
|
|
|
|
|
|
if (data.nonce !== nonce) return;
|
|
|
|
|
|
|
|
|
|
guildMembers.push(...data.members);
|
|
|
|
|
|
|
|
|
|
if (data.chunk_index >= data.chunk_count - 1) {
|
|
|
|
|
this.off(GatewayDispatchEvents.GuildMembersChunk, handler);
|
|
|
|
|
resolve(guildMembers);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.on(GatewayDispatchEvents.GuildMembersChunk, handler);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await this.ws.send(shardId, {
|
|
|
|
|
op: GatewayOpcodes.RequestGuildMembers,
|
|
|
|
|
// eslint-disable-next-line id-length
|
|
|
|
|
d: {
|
|
|
|
|
...options,
|
|
|
|
|
nonce,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the voice state of the bot user
|
|
|
|
|
*
|
|
|
|
|
* @see {@link https://discord.com/developers/docs/topics/gateway-events#update-voice-state}
|
|
|
|
|
* @param options - The options for updating the voice state
|
|
|
|
|
*/
|
|
|
|
|
public async updateVoiceState(options: GatewayVoiceStateUpdateData) {
|
|
|
|
|
const shardId = calculateShardId(options.guild_id, await this.ws.getShardCount());
|
|
|
|
|
|
|
|
|
|
await this.ws.send(shardId, {
|
|
|
|
|
op: GatewayOpcodes.VoiceStateUpdate,
|
|
|
|
|
// eslint-disable-next-line id-length
|
|
|
|
|
d: options,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the presence of the bot user
|
|
|
|
|
*
|
|
|
|
|
* @param shardId - The id of the shard to update the presence in
|
|
|
|
|
* @param options - The options for updating the presence
|
|
|
|
|
*/
|
|
|
|
|
public async updatePresence(shardId: number, options: GatewayPresenceUpdateData) {
|
|
|
|
|
await this.ws.send(shardId, {
|
|
|
|
|
op: GatewayOpcodes.PresenceUpdate,
|
|
|
|
|
// eslint-disable-next-line id-length
|
|
|
|
|
d: options,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private wrapIntrinsicProps<T>(obj: T, shardId: number): WithIntrinsicProps<T> {
|
|
|
|
|
return {
|
|
|
|
|
api,
|
|
|
|
|
api: this.api,
|
|
|
|
|
shardId,
|
|
|
|
|
data: obj,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ws.on(WebSocketShardEvents.Dispatch, ({ data: dispatch, shardId }) => {
|
|
|
|
|
// @ts-expect-error event props can't be resolved properly, but they are correct
|
|
|
|
|
emitter.emit(dispatch.t, wrapIntrinsicProps(dispatch.d, shardId));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return emitter;
|
|
|
|
|
}
|
|
|
|
|
|