Skip to content

feat(Client): add request soundboard sounds #10608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 110 additions & 11 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ import {
type GatewayVoiceStateUpdateData,
type GatewayVoiceStateUpdateDispatchData,
type GatewayWebhooksUpdateDispatchData,
type GatewayRequestSoundboardSoundsData,
type GatewaySoundboardSoundsDispatchData,
} from 'discord-api-types/v10';
import type { Gateway } from './Gateway.js';
import { API } from './api/index.js';
Expand Down Expand Up @@ -142,6 +144,7 @@ export interface MappedEvents {
[GatewayDispatchEvents.GuildSoundboardSoundDelete]: [ToEventProps<GatewayGuildSoundboardSoundDeleteDispatch>];
[GatewayDispatchEvents.GuildSoundboardSoundUpdate]: [ToEventProps<GatewayGuildSoundboardSoundUpdateDispatch>];
[GatewayDispatchEvents.GuildSoundboardSoundsUpdate]: [ToEventProps<GatewayGuildSoundboardSoundsUpdateDispatch>];
[GatewayDispatchEvents.SoundboardSounds]: [ToEventProps<GatewaySoundboardSoundsDispatchData>];
[GatewayDispatchEvents.GuildStickersUpdate]: [ToEventProps<GatewayGuildStickersUpdateDispatchData>];
[GatewayDispatchEvents.GuildUpdate]: [ToEventProps<GatewayGuildUpdateDispatchData>];
[GatewayDispatchEvents.IntegrationCreate]: [ToEventProps<GatewayIntegrationCreateDispatchData>];
Expand Down Expand Up @@ -196,6 +199,10 @@ export interface RequestGuildMembersResult {
presences: NonNullable<GatewayGuildMembersChunkDispatchData['presences']>;
}

function createTimer(controller: AbortController, timeout: number) {
return setTimeout(() => controller.abort(), timeout);
}

export class Client extends AsyncEventEmitter<MappedEvents> {
public readonly rest: REST;

Expand Down Expand Up @@ -234,12 +241,7 @@ export class Client extends AsyncEventEmitter<MappedEvents> {

const controller = new AbortController();

const createTimer = () =>
setTimeout(() => {
controller.abort();
}, timeout);

let timer: NodeJS.Timeout | undefined = createTimer();
let timer: NodeJS.Timeout | undefined = createTimer(controller, timeout);

await this.gateway.send(shardId, {
op: GatewayOpcodes.RequestGuildMembers,
Expand Down Expand Up @@ -270,11 +272,9 @@ export class Client extends AsyncEventEmitter<MappedEvents> {
chunkCount: data.chunk_count,
};

if (data.chunk_index >= data.chunk_count - 1) {
break;
} else {
timer = createTimer();
}
if (data.chunk_index >= data.chunk_count - 1) break;

timer = createTimer(controller, timeout);
}
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
Expand Down Expand Up @@ -316,6 +316,105 @@ export class Client extends AsyncEventEmitter<MappedEvents> {
return { members, nonce, notFound, presences };
}

/**
* Requests soundboard sounds from the gateway and returns an async iterator that yields the data from each soundboard sounds event.
*
* @see {@link https://discord.com/developers/docs/topics/gateway-events#request-soundboard-sounds}
* @param options - The options for the request
* @param timeout - The timeout for waiting for each soundboard sounds
* @example
* Requesting soundboard sounds for specific guilds
* ```ts
* for await (const { guildId, soundboardSounds } of this.requestSoundboardSoundsIterator({
* guild_ids: ['1234567890', '9876543210'],
* })) {
* console.log(`Soundboard sounds for guild ${guildId}:`, soundboardSounds);
* }
* ```
*/
public async *requestSoundboardSoundsIterator(options: GatewayRequestSoundboardSoundsData, timeout = 10_000) {
const shardCount = await this.gateway.getShardCount();
const shardIds = Map.groupBy(options.guild_ids, (guildId) => calculateShardId(guildId, shardCount));

const controller = new AbortController();

let timer: NodeJS.Timeout | undefined = createTimer(controller, timeout);

for (const [shardId, guildIds] of shardIds) {
await this.gateway.send(shardId, {
op: GatewayOpcodes.RequestSoundboardSounds,
// eslint-disable-next-line id-length
d: {
...options,
guild_ids: guildIds,
},
});
}

try {
const iterator = AsyncEventEmitter.on(this, GatewayDispatchEvents.SoundboardSounds, {
signal: controller.signal,
});

const guildIds = new Set(options.guild_ids);

for await (const [{ data }] of iterator) {
if (!guildIds.has(data.guild_id)) continue;

clearTimeout(timer);
timer = undefined;

yield {
guildId: data.guild_id,
soundboardSounds: data.soundboard_sounds,
};

guildIds.delete(data.guild_id);

if (guildIds.size === 0) break;

timer = createTimer(controller, timeout);
}
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
throw new Error('Request timed out');
}

throw error;
} finally {
if (timer) {
clearTimeout(timer);
}
}
}

/**
* Requests soundboard sounds from the gateway.
*
* @see {@link https://discord.com/developers/docs/topics/gateway-events#request-soundboard-sounds}
* @param options - The options for the request
* @param timeout - The timeout for waiting for each soundboard sounds event
* @example
* Requesting soundboard sounds for specific guilds
* ```ts
* const soundboardSounds = await client.requestSoundboardSounds({ guild_ids: ['1234567890', '9876543210'], });
*
* console.log(soundboardSounds.get('1234567890'));
* ```
*/
public async requestSoundboardSounds(options: GatewayRequestSoundboardSoundsData, timeout = 10_000) {
const soundboardSounds = new Map<
GatewaySoundboardSoundsDispatchData['guild_id'],
GatewaySoundboardSoundsDispatchData['soundboard_sounds']
>();

for await (const data of this.requestSoundboardSoundsIterator(options, timeout)) {
soundboardSounds.set(data.guildId, data.soundboardSounds);
}

return soundboardSounds;
}

/**
* Updates the voice state of the bot user
*
Expand Down
Loading