diff --git a/src/client/actions/ActionsManager.js b/src/client/actions/ActionsManager.js index 65415dd20af8..6cacfa2b8a10 100644 --- a/src/client/actions/ActionsManager.js +++ b/src/client/actions/ActionsManager.js @@ -67,6 +67,7 @@ class ActionsManager { this.register(require('./ThreadMembersUpdate')); this.register(require('./TypingStart')); this.register(require('./UserUpdate')); + this.register(require('./VoiceChannelEffectSend')); this.register(require('./VoiceStateUpdate')); this.register(require('./WebhooksUpdate')); } diff --git a/src/client/actions/VoiceChannelEffectSend.js b/src/client/actions/VoiceChannelEffectSend.js new file mode 100644 index 000000000000..2961c93b9509 --- /dev/null +++ b/src/client/actions/VoiceChannelEffectSend.js @@ -0,0 +1,22 @@ +'use strict'; + +const Action = require('./Action'); +const VoiceChannelEffect = require('../../structures/VoiceChannelEffect'); +const { Events } = require('../../util/Constants'); + +class VoiceChannelEffectSendAction extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + if (!guild) return; + /** + * Emmited when someone sends an effect, such as an emoji reaction, + * in a voice channel the current user is connected to. + * @event Client#voiceChannelEffectSend + * @param {VoiceChannelEffect} voiceChannelEffect The sent voice channel effect + */ + client.emit(Events.VOICE_CHANNEL_EFFECT_SEND, new VoiceChannelEffect(data, guild)); + } +} + +module.exports = VoiceChannelEffectSendAction; diff --git a/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js b/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js new file mode 100644 index 000000000000..2047e7597e35 --- /dev/null +++ b/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.VoiceChannelEffectSend.handle(packet.d); +}; diff --git a/src/client/websocket/handlers/index.js b/src/client/websocket/handlers/index.js index df3c2ea2deca..89c7b22d1fd8 100644 --- a/src/client/websocket/handlers/index.js +++ b/src/client/websocket/handlers/index.js @@ -49,6 +49,7 @@ const handlers = Object.fromEntries([ ['USER_UPDATE', require('./USER_UPDATE')], ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')], ['TYPING_START', require('./TYPING_START')], + ['VOICE_CHANNEL_EFFECT_SEND', require('./VOICE_CHANNEL_EFFECT_SEND')], ['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')], ['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')], ['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')], diff --git a/src/index.js b/src/index.js index c8bb9803110b..441573604370 100644 --- a/src/index.js +++ b/src/index.js @@ -155,6 +155,7 @@ exports.Typing = require('./structures/Typing'); exports.User = require('./structures/User'); exports.UserContextMenuInteraction = require('./structures/UserContextMenuInteraction'); exports.VoiceChannel = require('./structures/VoiceChannel'); +exports.VoiceChannelEffect = require('./structures/VoiceChannelEffect'); exports.VoiceRegion = require('./structures/VoiceRegion'); exports.VoiceState = require('./structures/VoiceState'); exports.Webhook = require('./structures/Webhook'); diff --git a/src/structures/VoiceChannelEffect.js b/src/structures/VoiceChannelEffect.js new file mode 100644 index 000000000000..300f3050eb97 --- /dev/null +++ b/src/structures/VoiceChannelEffect.js @@ -0,0 +1,67 @@ +'use strict'; + +const { Emoji } = require('./Emoji'); +const { AnimationTypes } = require('../util/Constants'); + +/** + * Represents an effect used in a {@link VoiceChannel}. + */ +class VoiceChannelEffect { + constructor(data, guild) { + /** + * The guild where the effect was sent from. + * @type {Guild} + */ + this.guild = guild; + + /** + * The id of the channel the effect was sent in. + * @type {Snowflake} + */ + this.channelId = data.channel_id; + + /** + * The id of the user that sent the effect. + * @type {Snowflake} + */ + this.userId = data.user_id; + + /** + * The emoji of the effect. + * @type {?Emoji} + */ + this.emoji = data.emoji ? new Emoji(guild.client, data.emoji) : null; + + /** + * The animation type of the effect. + * @type {?AnimationType} + */ + this.animationType = data.animation_type ? AnimationTypes[data.animation_type] : null; + + /** + * The animation id of the effect. + * @type {?number} + */ + this.animationId = data.animation_id ?? null; + } + + /** + * The channel the effect was sent in. + * @type {?VoiceChannel} + * @readonly + */ + get channel() { + return this.guild.channels.cache.get(this.channelId) ?? null; + } + + /** + * The member that sent the effect. + * @type {?GuildMember} + * @readonly + */ + get member() { + return this.guild.members.cache.get(this.userId) ?? null; + } +} + +module.exports = VoiceChannelEffect; diff --git a/src/util/Constants.js b/src/util/Constants.js index 6129da41713a..47744e93056c 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -221,6 +221,7 @@ exports.Opcodes = { * * THREAD_MEMBERS_UPDATE: threadMembersUpdate * * USER_UPDATE: userUpdate * * PRESENCE_UPDATE: presenceUpdate + * * VOICE_CHANNEL_EFFECT_SEND: voiceChannelEffectSend * * VOICE_SERVER_UPDATE: voiceServerUpdate * * VOICE_STATE_UPDATE: voiceStateUpdate * * TYPING_START: typingStart @@ -305,6 +306,7 @@ exports.Events = { THREAD_MEMBERS_UPDATE: 'threadMembersUpdate', USER_UPDATE: 'userUpdate', PRESENCE_UPDATE: 'presenceUpdate', + VOICE_CHANNEL_EFFECT_SEND: 'voiceChannelEffectSend', VOICE_SERVER_UPDATE: 'voiceServerUpdate', VOICE_STATE_UPDATE: 'voiceStateUpdate', TYPING_START: 'typingStart', @@ -417,6 +419,7 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', * * USER_UPDATE * * PRESENCE_UPDATE * * TYPING_START + * * VOICE_CHANNEL_EFFECT_SEND * * VOICE_STATE_UPDATE * * VOICE_SERVER_UPDATE * * WEBHOOKS_UPDATE @@ -482,6 +485,7 @@ exports.WSEvents = keyMirror([ 'USER_UPDATE', 'PRESENCE_UPDATE', 'TYPING_START', + 'VOICE_CHANNEL_EFFECT_SEND', 'VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE', 'WEBHOOKS_UPDATE', @@ -1510,6 +1514,15 @@ exports.SortOrderTypes = createEnum([null, 'LATEST_ACTIVITY', 'CREATION_DATE']); */ exports.ForumLayoutTypes = createEnum(['NOT_SET', 'LIST_VIEW', 'GALLERY_VIEW']); +/** + * The animation type of the voice channel effect + * * PREMIUM + * * BASIC + * @typedef {string} AnimationType + * @see {@link https://discord.com/developers/docs/topics/gateway-events#voice-channel-effect-send-animation-types} + */ +exports.AnimationTypes = createEnum(['PREMIUM', 'BASIC']); + exports._cleanupSymbol = Symbol('djsCleanup'); function keyMirror(arr) { @@ -1531,6 +1544,7 @@ function createEnum(keys) { /** * @typedef {Object} Constants Constants that can be used in an enum or object-like way. * @property {Object} ActivityTypes The type of an activity of a users presence. + * @property {Object} AnimationTypes The animation type of the voice channel effect. * @property {Object} APIErrors An error encountered while performing an API request. * @property {Object} ApplicationCommandOptionTypes * The type of an {@link ApplicationCommandOption} object. diff --git a/typings/enums.d.ts b/typings/enums.d.ts index 92a86675da52..44543fb9be84 100644 --- a/typings/enums.d.ts +++ b/typings/enums.d.ts @@ -10,6 +10,11 @@ export const enum ActivityTypes { COMPETING = 5, } +export const enum AnimationTypes { + PREMIUM = 0, + BASIC = 1, +} + export const enum ApplicationCommandTypes { CHAT_INPUT = 1, USER = 2, diff --git a/typings/index.d.ts b/typings/index.d.ts index d1eff3bfaf95..a0bcd5c78a62 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -97,6 +97,7 @@ import { SortOrderType, ForumLayoutType, ApplicationRoleConnectionMetadataTypes, + AnimationTypes, } from './enums'; import { APIApplicationRoleConnectionMetadata, @@ -160,6 +161,7 @@ import { RawWelcomeScreenData, RawWidgetData, RawWidgetMemberData, + VoiceChannelEffectData, } from './rawDataTypes'; //#region Classes @@ -2809,6 +2811,18 @@ export class VoiceChannel extends TextBasedChannelMixin(BaseGuildVoiceChannel, [ public setVideoQualityMode(videoQualityMode: VideoQualityMode | number, reason?: string): Promise; } +export class VoiceChannelEffect { + private constructor(data: VoiceChannelEffectData, guild: Guild); + public animationType: AnimationTypes | null; + public animationId: number | null; + public readonly channel: VoiceChannel | null; + public channelId: Snowflake; + public emoji: Emoji | null; + public guild: Guild; + public readonly member: GuildMember | null; + public userId: Snowflake; +} + export class VoiceRegion { private constructor(data: RawVoiceRegionData); public custom: boolean; @@ -3154,6 +3168,7 @@ export const Constants: { DefaultMessageNotificationLevels: EnumHolder; VerificationLevels: EnumHolder; MembershipStates: EnumHolder; + AnimationTypes: EnumHolder; AutoModerationRuleTriggerTypes: EnumHolder; AutoModerationRuleKeywordPresetTypes: EnumHolder; AutoModerationActionTypes: EnumHolder; @@ -4569,6 +4584,7 @@ export interface ClientEvents extends BaseClientEvents { threadUpdate: [oldThread: ThreadChannel, newThread: ThreadChannel]; typingStart: [typing: Typing]; userUpdate: [oldUser: User | PartialUser, newUser: User]; + voiceChannelEffectSend: [voiceChannelEffect: VoiceChannelEffect]; voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel | ForumChannel]; /** @deprecated Use interactionCreate instead */ @@ -4827,6 +4843,7 @@ export interface ConstantsEvents { THREAD_MEMBERS_UPDATE: 'threadMembersUpdate'; USER_UPDATE: 'userUpdate'; PRESENCE_UPDATE: 'presenceUpdate'; + VOICE_CHANNEL_EFFECT_SEND: 'voiceChaannelEffectSend'; VOICE_SERVER_UPDATE: 'voiceServerUpdate'; VOICE_STATE_UPDATE: 'voiceStateUpdate'; TYPING_START: 'typingStart'; @@ -6546,6 +6563,7 @@ export type WSEventType = | 'USER_UPDATE' | 'PRESENCE_UPDATE' | 'TYPING_START' + | 'VOICE_CHANNEL_EFFECT_SEND' | 'VOICE_STATE_UPDATE' | 'VOICE_SERVER_UPDATE' | 'WEBHOOKS_UPDATE' diff --git a/typings/rawDataTypes.d.ts b/typings/rawDataTypes.d.ts index 5b7aa1757cc3..eaf8a6f79b45 100644 --- a/typings/rawDataTypes.d.ts +++ b/typings/rawDataTypes.d.ts @@ -90,7 +90,8 @@ import type { AutoModerationRuleTriggerTypes, InteractionTypes, MessageComponentTypes, - ApplicationRoleConnectionMetadataTypes + ApplicationRoleConnectionMetadataTypes, + AnimationTypes } from './enums'; export type RawActivityData = GatewayActivity; @@ -279,3 +280,12 @@ export interface APIApplicationRoleConnectionMetadata { description: string; description_localizations?: LocalizationMap; } + +export interface VoiceChannelEffectData { + channel_id: Snowflake; + guild_id: Snowflake; + user_id: Snowflake; + emoji?: APIEmoji | null; + animation_type?: AnimationTypes; + animation_id?: number; +}