Skip to content

Commit a5afc40

Browse files
jaw0r3kJiralitesdanialrazavladfrangu
authored
feat: super reactions (#9336)
* feat: super reactions * docs: Touch-up * feat: count super reactions in events * feat: document me_burst property Co-authored-by: Danial Raza <[email protected]> * feat: document type query for fetching reaction users * fix: cover case when burstColors can be undefined at init of a reaction * Update packages/discord.js/src/structures/MessageReaction.js Co-authored-by: Vlad Frangu <[email protected]> * chore: futureproof so use an object --------- Co-authored-by: Jiralite <[email protected]> Co-authored-by: Danial Raza <[email protected]> Co-authored-by: Vlad Frangu <[email protected]> Co-authored-by: Vlad Frangu <[email protected]>
1 parent 4374374 commit a5afc40

File tree

5 files changed

+95
-16
lines changed

5 files changed

+95
-16
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const Partials = require('../../util/Partials');
99
message_id: 'id',
1010
emoji: { name: '�', id: null },
1111
channel_id: 'id',
12+
burst: boolean
1213
// If originating from a guild
1314
guild_id: 'id',
1415
member: { ..., user: { ... } } }
@@ -36,17 +37,24 @@ class MessageReactionAdd extends Action {
3637
emoji: data.emoji,
3738
count: message.partial ? null : 0,
3839
me: user.id === this.client.user.id,
40+
burst_colors: data.burst_colors,
3941
});
4042
if (!reaction) return false;
41-
reaction._add(user);
43+
reaction._add(user, data.burst);
4244
if (fromStructure) return { message, reaction, user };
45+
/**
46+
* Provides additional information about altered reaction
47+
* @typedef {Object} MessageReactionEventDetails
48+
* @property {boolean} burst Determines whether a super reaction was used
49+
*/
4350
/**
4451
* Emitted whenever a reaction is added to a cached message.
4552
* @event Client#messageReactionAdd
4653
* @param {MessageReaction} messageReaction The reaction object
4754
* @param {User} user The user that applied the guild or reaction emoji
55+
* @param {MessageReactionEventDetails} details Details of adding the reaction
4856
*/
49-
this.client.emit(Events.MessageReactionAdd, reaction, user);
57+
this.client.emit(Events.MessageReactionAdd, reaction, user, { burst: data.burst });
5058

5159
return { message, reaction, user };
5260
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ class MessageReactionRemove extends Action {
2929
// Verify reaction
3030
const reaction = this.getReaction(data, message, user);
3131
if (!reaction) return false;
32-
reaction._remove(user);
32+
reaction._remove(user, data.burst);
3333
/**
3434
* Emitted whenever a reaction is removed from a cached message.
3535
* @event Client#messageReactionRemove
3636
* @param {MessageReaction} messageReaction The reaction object
3737
* @param {User} user The user whose emoji or reaction emoji was removed
38+
* @param {MessageReactionEventDetails} details Details of removing the reaction
3839
*/
39-
this.client.emit(Events.MessageReactionRemove, reaction, user);
40+
this.client.emit(Events.MessageReactionRemove, reaction, user, { burst: data.burst });
4041

4142
return { message, reaction, user };
4243
}

packages/discord.js/src/managers/ReactionUserManager.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const { Collection } = require('@discordjs/collection');
44
const { makeURLSearchParams } = require('@discordjs/rest');
5-
const { Routes } = require('discord-api-types/v10');
5+
const { ReactionType, Routes } = require('discord-api-types/v10');
66
const CachedManager = require('./CachedManager');
77
const { DiscordjsError, ErrorCodes } = require('../errors');
88
const User = require('../structures/User');
@@ -31,6 +31,7 @@ class ReactionUserManager extends CachedManager {
3131
/**
3232
* Options used to fetch users who gave a reaction.
3333
* @typedef {Object} FetchReactionUsersOptions
34+
* @property {ReactionType} [type=ReactionType.Normal] The reaction type to fetch
3435
* @property {number} [limit=100] The maximum amount of users to fetch, defaults to `100`
3536
* @property {Snowflake} [after] Limit fetching users to those with an id greater than the supplied id
3637
*/
@@ -40,9 +41,9 @@ class ReactionUserManager extends CachedManager {
4041
* @param {FetchReactionUsersOptions} [options] Options for fetching the users
4142
* @returns {Promise<Collection<Snowflake, User>>}
4243
*/
43-
async fetch({ limit = 100, after } = {}) {
44+
async fetch({ type = ReactionType.Normal, limit = 100, after } = {}) {
4445
const message = this.reaction.message;
45-
const query = makeURLSearchParams({ limit, after });
46+
const query = makeURLSearchParams({ limit, after, type });
4647
const data = await this.client.rest.get(
4748
Routes.channelMessageReaction(message.channelId, message.id, this.reaction.emoji.identifier),
4849
{ query },

packages/discord.js/src/structures/MessageReaction.js

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class MessageReaction {
3131
*/
3232
this.me = data.me;
3333

34+
/**
35+
* Whether the client has super-reacted using this emoji
36+
* @type {boolean}
37+
*/
38+
this.meBurst = data.me_burst;
39+
3440
/**
3541
* A manager of the users that have given this reaction
3642
* @type {ReactionUserManager}
@@ -39,17 +45,45 @@ class MessageReaction {
3945

4046
this._emoji = new ReactionEmoji(this, data.emoji);
4147

48+
this.burstColors = null;
49+
4250
this._patch(data);
4351
}
4452

4553
_patch(data) {
54+
if ('burst_colors' in data) {
55+
/**
56+
* Hexadecimal colors used for this super reaction
57+
* @type {?string[]}
58+
*/
59+
this.burstColors = data.burst_colors;
60+
}
61+
4662
if ('count' in data) {
4763
/**
4864
* The number of people that have given the same reaction
4965
* @type {?number}
5066
*/
5167
this.count ??= data.count;
5268
}
69+
70+
if ('count_details' in data) {
71+
/**
72+
* The reaction count details object contains information about super and normal reaction counts.
73+
* @typedef {Object} ReactionCountDetailsData
74+
* @property {number} burst Count of super reactions
75+
* @property {number} normal Count of normal reactions
76+
*/
77+
78+
/**
79+
* The reaction count details object contains information about super and normal reaction counts.
80+
* @type {ReactionCountDetailsData}
81+
*/
82+
this.countDetails = {
83+
burst: data.count_details.burst,
84+
normal: data.count_details.normal,
85+
};
86+
}
5387
}
5488

5589
/**
@@ -121,18 +155,31 @@ class MessageReaction {
121155
return this._emoji.id ?? this._emoji.name;
122156
}
123157

124-
_add(user) {
158+
_add(user, burst) {
125159
if (this.partial) return;
126160
this.users.cache.set(user.id, user);
127-
if (!this.me || user.id !== this.message.client.user.id || this.count === 0) this.count++;
128-
this.me ||= user.id === this.message.client.user.id;
161+
if (!this.me || user.id !== this.message.client.user.id || this.count === 0) {
162+
this.count++;
163+
if (burst) this.countDetails.burst++;
164+
else this.countDetails.normal++;
165+
}
166+
if (user.id === this.message.client.user.id) {
167+
if (burst) this.meBurst = true;
168+
else this.me = true;
169+
}
129170
}
130-
131-
_remove(user) {
171+
_remove(user, burst) {
132172
if (this.partial) return;
133173
this.users.cache.delete(user.id);
134-
if (!this.me || user.id !== this.message.client.user.id) this.count--;
135-
if (user.id === this.message.client.user.id) this.me = false;
174+
if (!this.me || user.id !== this.message.client.user.id) {
175+
this.count--;
176+
if (burst) this.countDetails.burst--;
177+
else this.countDetails.normal--;
178+
}
179+
if (user.id === this.message.client.user.id) {
180+
if (burst) this.meBurst = false;
181+
else this.me = false;
182+
}
136183
if (this.count <= 0 && this.users.cache.size === 0) {
137184
this.message.reactions.cache.delete(this.emoji.id ?? this.emoji.name);
138185
}

packages/discord.js/typings/index.d.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ import {
181181
APISelectMenuDefaultValue,
182182
SelectMenuDefaultValueType,
183183
InviteType,
184+
ReactionType,
184185
} from 'discord-api-types/v10';
185186
import { ChildProcess } from 'node:child_process';
186187
import { EventEmitter } from 'node:events';
@@ -2414,10 +2415,13 @@ export class MessageReaction {
24142415
private constructor(client: Client<true>, data: RawMessageReactionData, message: Message);
24152416
private _emoji: GuildEmoji | ReactionEmoji;
24162417

2418+
public burstColors: string[] | null;
24172419
public readonly client: Client<true>;
24182420
public count: number;
2421+
public countDetails: ReactionCountDetailsData;
24192422
public get emoji(): GuildEmoji | ReactionEmoji;
24202423
public me: boolean;
2424+
public meBurst: boolean;
24212425
public message: Message | PartialMessage;
24222426
public get partial(): false;
24232427
public users: ReactionUserManager;
@@ -2428,6 +2432,10 @@ export class MessageReaction {
24282432
public valueOf(): Snowflake | string;
24292433
}
24302434

2435+
export interface MessageReactionEventDetails {
2436+
burst: boolean;
2437+
}
2438+
24312439
export interface ModalComponentData {
24322440
customId: string;
24332441
title: string;
@@ -5294,8 +5302,16 @@ export interface ClientEvents {
52945302
messages: ReadonlyCollection<Snowflake, Message | PartialMessage>,
52955303
channel: GuildTextBasedChannel,
52965304
];
5297-
messageReactionAdd: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser];
5298-
messageReactionRemove: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser];
5305+
messageReactionAdd: [
5306+
reaction: MessageReaction | PartialMessageReaction,
5307+
user: User | PartialUser,
5308+
details: MessageReactionEventDetails,
5309+
];
5310+
messageReactionRemove: [
5311+
reaction: MessageReaction | PartialMessageReaction,
5312+
user: User | PartialUser,
5313+
details: MessageReactionEventDetails,
5314+
];
52995315
messageUpdate: [oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage];
53005316
presenceUpdate: [oldPresence: Presence | null, newPresence: Presence];
53015317
ready: [client: Client<true>];
@@ -5740,6 +5756,7 @@ export interface FetchMessagesOptions {
57405756
}
57415757

57425758
export interface FetchReactionUsersOptions {
5759+
type?: ReactionType;
57435760
limit?: number;
57445761
after?: Snowflake;
57455762
}
@@ -6475,6 +6492,11 @@ export interface MessageSelectOption {
64756492
value: string;
64766493
}
64776494

6495+
export interface ReactionCountDetailsData {
6496+
burst: number;
6497+
normal: number;
6498+
}
6499+
64786500
export interface SelectMenuComponentOptionData {
64796501
default?: boolean;
64806502
description?: string;

0 commit comments

Comments
 (0)