Skip to content

Commit 2ef1a3a

Browse files
committed
feat: add soundboard
1 parent e2df0e0 commit 2ef1a3a

16 files changed

+340
-1
lines changed

packages/discord.js/src/client/Client.js

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const ActionsManager = require('./actions/ActionsManager');
1111
const ClientVoiceManager = require('./voice/ClientVoiceManager');
1212
const PacketHandlers = require('./websocket/handlers');
1313
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors');
14+
// TODO: Uncomment after finishing the manager
15+
// const { BaseSoundboardSoundManager } = require('../managers/BaseSoundboardSoundManager');
1416
const BaseGuildEmojiManager = require('../managers/BaseGuildEmojiManager');
1517
const ChannelManager = require('../managers/ChannelManager');
1618
const GuildManager = require('../managers/GuildManager');
@@ -176,6 +178,13 @@ class Client extends BaseClient {
176178
*/
177179
this.voice = new ClientVoiceManager(this);
178180

181+
// TODO: Uncomment after finishing the manager
182+
// /**
183+
// * The soundboard sound manager of the client
184+
// * @type {BaseSoundboardSoundManager}
185+
// */
186+
// this.soundboardSounds = new BaseSoundboardSoundManager(this);
187+
179188
/**
180189
* User that the client is logged in as
181190
* @type {?ClientUser}

packages/discord.js/src/client/actions/ActionsManager.js

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class ActionsManager {
4343
this.register(require('./GuildScheduledEventUpdate'));
4444
this.register(require('./GuildScheduledEventUserAdd'));
4545
this.register(require('./GuildScheduledEventUserRemove'));
46+
this.register(require('./GuildSoundboardSoundCreate'));
47+
this.register(require('./GuildSoundboardSoundDelete'));
48+
this.register(require('./GuildSoundboardSoundUpdate'));
4649
this.register(require('./GuildStickerCreate'));
4750
this.register(require('./GuildStickerDelete'));
4851
this.register(require('./GuildStickerUpdate'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const Action = require('./Action');
4+
const Events = require('../../util/Events');
5+
6+
class GuildSoundboardSoundCreateAction extends Action {
7+
handle(data) {
8+
const guild = this.client.guilds.cache.get(data.guild_id);
9+
10+
let soundboardSound;
11+
12+
if (guild) {
13+
const already = guild.soundboardSounds.cache.has(data.sound_id);
14+
15+
soundboardSound = guild.soundboardSounds._add(data);
16+
17+
/**
18+
* Emitted whenever a soundboard sound is created in a guild.
19+
* @event Client#guildSoundboardSoundCreate
20+
* @param {SoundboardSound} soundboardSound The soundboard sound that was created
21+
*/
22+
if (!already) this.client.emit(Events.GuildSoundboardSoundCreate, soundboardSound);
23+
}
24+
25+
return { soundboardSound };
26+
}
27+
}
28+
29+
module.exports = GuildSoundboardSoundCreateAction;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const Action = require('./Action');
4+
const Events = require('../../util/Events');
5+
6+
class GuildSoundboardSoundDeleteAction extends Action {
7+
handle(data) {
8+
const guild = this.client.guilds.cache.get(data.guild_id);
9+
10+
let soundboardSound;
11+
12+
if (guild) {
13+
soundboardSound = guild.soundboardSounds.cache._add(data, false);
14+
15+
guild.soundboardSounds.cache.delete(soundboardSound.id);
16+
17+
/**
18+
* Emitted whenever a soundboard sound is deleted in a guild.
19+
* @event Client#guildSoundboardSoundDelete
20+
* @param {SoundboardSound} soundboardSound The soundboard sound that was deleted
21+
*/
22+
this.client.emit(Events.GuildSoundboardSoundDelete, soundboardSound);
23+
}
24+
25+
return { soundboardSound };
26+
}
27+
}
28+
29+
module.exports = GuildSoundboardSoundDeleteAction;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
3+
const Action = require('./Action');
4+
const Events = require('../../util/Events');
5+
6+
class GuildSoundboardSoundUpdateAction extends Action {
7+
handle(data) {
8+
const guild = this.client.guilds.cache.get(data.guild_id);
9+
10+
if (guild) {
11+
let oldSoundboardSound = null;
12+
13+
const newSoundboardSound = guild.soundboardSounds.cache.get(data.sound_id);
14+
15+
if (newSoundboardSound) {
16+
oldSoundboardSound = newSoundboardSound._update(data);
17+
18+
/**
19+
* Emitted whenever a soundboard sound is updated in a guild.
20+
* @event Client#guildSoundboardSoundUpdate
21+
* @param {?SoundboardSound} oldSoundboardSound The soundboard sound before the update
22+
* @param {SoundboardSound} newSoundboardSound The soundboard sound after the update
23+
*/
24+
this.client.emit(Events.GuildSoundboardSoundUpdate, oldSoundboardSound, newSoundboardSound);
25+
}
26+
27+
return { oldSoundboardSound, newSoundboardSound };
28+
}
29+
30+
return { oldSoundboardSound: null, newSoundboardSound: null };
31+
}
32+
}
33+
34+
module.exports = GuildSoundboardSoundUpdateAction;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = (client, { d: data }) => {
4+
client.actions.GuildSoundboardSoundCreate.handle(data);
5+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = (client, { d: data }) => {
4+
client.actions.GuildSoundboardSoundDelete.handle(data);
5+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = (client, { d: data }) => {
4+
client.actions.GuildSoundboardSoundUpdate.handle(data);
5+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
const { Collection } = require('@discordjs/collection');
4+
const Events = require('../../../util/Events');
5+
6+
module.exports = (client, { d: data }) => {
7+
const guild = client.guilds.cache.get(data.guild_id);
8+
9+
if (!guild) return;
10+
11+
const soundboardSounds = new Collection();
12+
13+
for (const soundboardSound of data.soundboard_sounds) {
14+
soundboardSounds.set(soundboardSound.sound_id, guild.soundboardSounds._add(soundboardSound));
15+
}
16+
17+
/**
18+
* Emitted whenever soundboard sounds are received (all soundboard sounds come from the same guild).
19+
* @event Client#soundboardSounds
20+
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The sounds received
21+
* @param {Guild} guild The guild related to the soundboard sounds
22+
*/
23+
client.emit(Events.SoundboardSounds, soundboardSounds, guild);
24+
};

packages/discord.js/src/client/websocket/handlers/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ const handlers = Object.fromEntries([
3232
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
3333
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
3434
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
35+
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE')],
36+
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE')],
37+
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE')],
38+
// TODO: Uncomment this line after finishing the GUILD_SOUNDBOARD_SOUNDS_UPDATE handler
39+
// ['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE')],
3540
['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')],
3641
['GUILD_UPDATE', require('./GUILD_UPDATE')],
3742
['INTERACTION_CREATE', require('./INTERACTION_CREATE')],
@@ -49,6 +54,7 @@ const handlers = Object.fromEntries([
4954
['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')],
5055
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
5156
['READY', require('./READY')],
57+
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS')],
5258
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
5359
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
5460
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],

packages/discord.js/src/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ exports.ApplicationEmojiManager = require('./managers/ApplicationEmojiManager');
5656
exports.ApplicationCommandPermissionsManager = require('./managers/ApplicationCommandPermissionsManager');
5757
exports.AutoModerationRuleManager = require('./managers/AutoModerationRuleManager');
5858
exports.BaseGuildEmojiManager = require('./managers/BaseGuildEmojiManager');
59+
// TODO: Uncomment after finishing the manager
60+
// exports.BaseSoundboardSoundManager = require('./managers/BaseSoundboardSoundManager').BaseSoundboardSoundManager;
5961
exports.CachedManager = require('./managers/CachedManager');
6062
exports.ChannelManager = require('./managers/ChannelManager');
6163
exports.ClientVoiceManager = require('./client/voice/ClientVoiceManager');
@@ -73,6 +75,8 @@ exports.GuildManager = require('./managers/GuildManager');
7375
exports.GuildMemberManager = require('./managers/GuildMemberManager');
7476
exports.GuildMemberRoleManager = require('./managers/GuildMemberRoleManager');
7577
exports.GuildMessageManager = require('./managers/GuildMessageManager');
78+
// Uncomment after finishing the manager
79+
// exports.GuildSoundboardSoundManager = require('./managers/GuildSoundboardSoundManager').GuildSoundboardSoundManager;
7680
exports.GuildScheduledEventManager = require('./managers/GuildScheduledEventManager');
7781
exports.GuildStickerManager = require('./managers/GuildStickerManager');
7882
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
@@ -190,6 +194,7 @@ exports.RoleSelectMenuInteraction = require('./structures/RoleSelectMenuInteract
190194
exports.StringSelectMenuInteraction = require('./structures/StringSelectMenuInteraction');
191195
exports.UserSelectMenuInteraction = require('./structures/UserSelectMenuInteraction');
192196
exports.SKU = require('./structures/SKU').SKU;
197+
exports.SoundboardSound = require('./structures/SoundboardSound').SoundboardSound;
193198
exports.StringSelectMenuOptionBuilder = require('./structures/StringSelectMenuOptionBuilder');
194199
exports.StageChannel = require('./structures/StageChannel');
195200
exports.StageInstance = require('./structures/StageInstance').StageInstance;

packages/discord.js/src/structures/Guild.js

+9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ const VoiceStateManager = require('../managers/VoiceStateManager');
2929
const { resolveImage } = require('../util/DataResolver');
3030
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
3131
const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util');
32+
// TODO: Uncomment this after finishing the manager
33+
// const { GuildSoundboardSoundManager } = require('../managers/GuildSoundboardSoundManager');
3234

3335
/**
3436
* Represents a guild (or a server) on Discord.
@@ -106,6 +108,13 @@ class Guild extends AnonymousGuild {
106108
*/
107109
this.autoModerationRules = new AutoModerationRuleManager(this);
108110

111+
// TODO: Remove this after finishing the manager
112+
// /**
113+
// * A manager of the soundboard sounds of this guild.
114+
// * @type {GuildSoundboardSoundManager}
115+
// */
116+
// this.soundboardSounds = new GuildSoundboardSoundManager(this);
117+
109118
if (!data) return;
110119
if (data.unavailable) {
111120
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
'use strict';
2+
3+
const Base = require('./Base');
4+
5+
/**
6+
* Represents a soundboard sound.
7+
* @extends {Base}
8+
*/
9+
class SoundboardSound extends Base {
10+
constructor(client, data) {
11+
super(client);
12+
13+
/**
14+
* The id of the soundboard sound
15+
* @type {Snowflake|number}
16+
*/
17+
this.soundId = data.sound_id;
18+
19+
this._patch(data);
20+
}
21+
22+
_patch(data) {
23+
/**
24+
* Whether this soundboard sound is available
25+
* @type {boolean}
26+
*/
27+
this.available = data.available;
28+
29+
/**
30+
* The name of the soundboard sound
31+
* @type {string}
32+
*/
33+
this.name = data.name;
34+
35+
/**
36+
* The volume of the soundboard sound
37+
* @type {number}
38+
*/
39+
this.volume = data.volume;
40+
41+
if ('emoji_id' in data) {
42+
/**
43+
* The emoji id of the soundboard sound
44+
* @type {?Snowflake}
45+
*/
46+
this.emojiId = data.emojiId;
47+
} else {
48+
this.emojiId ??= null;
49+
}
50+
51+
if ('emoji_name' in data) {
52+
/**
53+
* The emoji name of the soundboard sound
54+
* @type {?string}
55+
*/
56+
this.emojiName = data.emojiName;
57+
} else {
58+
this.emojiName ??= null;
59+
}
60+
61+
if ('guild_id' in data) {
62+
/**
63+
* The guild id of the soundboard sound
64+
* @type {?Snowflake}
65+
*/
66+
this.guildId = data.guildId;
67+
} else {
68+
this.guildId ??= null;
69+
}
70+
71+
if ('user' in data) {
72+
/**
73+
* The user who created this soundboard sound
74+
* @type {?User}
75+
*/
76+
this.user = this.client.users._add(data.user);
77+
} else {
78+
this.user ??= null;
79+
}
80+
}
81+
82+
/**
83+
* The guild this soundboard sound is part of
84+
* @type {?Guild}
85+
* @readonly
86+
*/
87+
get guild() {
88+
return this.client.guilds.resolve(this.guildId);
89+
}
90+
91+
/**
92+
* Whether this soundboard sound is the same as another one.
93+
* @param {SoundboardSound|APISoundboardSound} other The soundboard sound to compare it to
94+
* @returns {boolean}
95+
*/
96+
equals(other) {
97+
if (other instanceof SoundboardSound) {
98+
return (
99+
this.id === other.id &&
100+
this.name === other.name &&
101+
this.volume === other.volume &&
102+
this.emojiId === other.emojiId &&
103+
this.emojiName === other.emojiName &&
104+
this.guildId === other.guildId &&
105+
this.user?.id === other.user?.id
106+
);
107+
}
108+
109+
return (
110+
this.id === other.sound_id &&
111+
this.name === other.name &&
112+
this.volume === other.volume &&
113+
this.emojiId === other.emoji_id &&
114+
this.emojiName === other.emoji_name &&
115+
this.guildId === other.guild_id &&
116+
this.user?.id === other.user?.id
117+
);
118+
}
119+
}
120+
121+
exports.SoundboardSound = SoundboardSound;

packages/discord.js/src/structures/VoiceChannel.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { PermissionFlagsBits } = require('discord-api-types/v10');
3+
const { PermissionFlagsBits, Routes } = require('discord-api-types/v10');
44
const BaseGuildVoiceChannel = require('./BaseGuildVoiceChannel');
55

66
/**
@@ -35,6 +35,21 @@ class VoiceChannel extends BaseGuildVoiceChannel {
3535
permissions.has(PermissionFlagsBits.Speak, false)
3636
);
3737
}
38+
39+
/**
40+
* Send a soundboard sound to a voice channel the user is connected to.
41+
* Fires a Voice Channel Effect Send Gateway event.
42+
* @param {SoundboardSound} sound the sound to send
43+
* @returns {void}
44+
*/
45+
async sendSoundboardSound(sound) {
46+
await this.client.rest.post(Routes.sendSoundboardSound(this.id), {
47+
body: {
48+
sound_id: sound.id,
49+
source_guild_id: sound.guildId ?? undefined,
50+
},
51+
});
52+
}
3853
}
3954

4055
/**

0 commit comments

Comments
 (0)