feat: super reactions

#9336 backport
This commit is contained in:
Elysia
2024-09-17 19:18:21 +07:00
parent d6d768488b
commit 8dfa37fbd9
7 changed files with 78 additions and 33 deletions

View File

@@ -9,6 +9,7 @@ const { PartialTypes } = require('../../util/Constants');
message_id: 'id', message_id: 'id',
emoji: { name: '<27>', id: null }, emoji: { name: '<27>', id: null },
channel_id: 'id', channel_id: 'id',
burst: boolean
// If originating from a guild // If originating from a guild
guild_id: 'id', guild_id: 'id',
member: { ..., user: { ... } } } member: { ..., user: { ... } } }
@@ -38,16 +39,23 @@ class MessageReactionAdd extends Action {
me: user.id === this.client.user.id, me: user.id === this.client.user.id,
...data, ...data,
}); });
console.log(reaction);
if (!reaction) return false; if (!reaction) return false;
reaction._add(user); reaction._add(user, data.burst);
if (fromStructure) return { message, reaction, user }; if (fromStructure) return { message, reaction, user };
/**
* Provides additional information about altered reaction
* @typedef {Object} MessageReactionEventDetails
* @property {boolean} burst Determines whether a super reaction was used
*/
/** /**
* Emitted whenever a reaction is added to a cached message. * Emitted whenever a reaction is added to a cached message.
* @event Client#messageReactionAdd * @event Client#messageReactionAdd
* @param {MessageReaction} messageReaction The reaction object * @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user that applied the guild or reaction emoji * @param {User} user The user that applied the guild or reaction emoji
* @param {MessageReactionEventDetails} details Details of adding the reaction
*/ */
this.client.emit(Events.MESSAGE_REACTION_ADD, reaction, user); this.client.emit(Events.MESSAGE_REACTION_ADD, reaction, user, { burst: data.burst });
return { message, reaction, user }; return { message, reaction, user };
} }

View File

@@ -29,14 +29,15 @@ class MessageReactionRemove extends Action {
// Verify reaction // Verify reaction
const reaction = this.getReaction(data, message, user); const reaction = this.getReaction(data, message, user);
if (!reaction) return false; if (!reaction) return false;
reaction._remove(user); reaction._remove(user, data.burst);
/** /**
* Emitted whenever a reaction is removed from a cached message. * Emitted whenever a reaction is removed from a cached message.
* @event Client#messageReactionRemove * @event Client#messageReactionRemove
* @param {MessageReaction} messageReaction The reaction object * @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user whose emoji or reaction emoji was removed * @param {User} user The user whose emoji or reaction emoji was removed
* @param {MessageReactionEventDetails} details Details of removing the reaction
*/ */
this.client.emit(Events.MESSAGE_REACTION_REMOVE, reaction, user); this.client.emit(Events.MESSAGE_REACTION_REMOVE, reaction, user, { burst: data.burst });
return { message, reaction, user }; return { message, reaction, user };
} }

View File

@@ -4,6 +4,7 @@ const { Collection } = require('@discordjs/collection');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { Error } = require('../errors'); const { Error } = require('../errors');
const User = require('../structures/User'); const User = require('../structures/User');
const { ReactionTypes } = require('../util/Constants');
/** /**
* Manages API methods for users who reacted to a reaction and stores their cache. * Manages API methods for users who reacted to a reaction and stores their cache.
@@ -29,6 +30,7 @@ class ReactionUserManager extends CachedManager {
/** /**
* Options used to fetch users who gave a reaction. * Options used to fetch users who gave a reaction.
* @typedef {Object} FetchReactionUsersOptions * @typedef {Object} FetchReactionUsersOptions
* @property {ReactionType} [type='NORMAL'] The reaction type to fetch
* @property {number} [limit=100] The maximum amount of users to fetch, defaults to `100` * @property {number} [limit=100] The maximum amount of users to fetch, defaults to `100`
* @property {Snowflake} [after] Limit fetching users to those with an id greater than the supplied id * @property {Snowflake} [after] Limit fetching users to those with an id greater than the supplied id
*/ */
@@ -38,11 +40,11 @@ class ReactionUserManager extends CachedManager {
* @param {FetchReactionUsersOptions} [options] Options for fetching the users * @param {FetchReactionUsersOptions} [options] Options for fetching the users
* @returns {Promise<Collection<Snowflake, User>>} * @returns {Promise<Collection<Snowflake, User>>}
*/ */
async fetch({ limit = 100, after } = {}) { async fetch({ limit = 100, after, type = 'NORMAL' } = {}) {
const message = this.reaction.message; const message = this.reaction.message;
const data = await this.client.api.channels[message.channelId].messages[message.id].reactions[ const data = await this.client.api.channels[message.channelId].messages[message.id].reactions[
this.reaction.emoji.identifier this.reaction.emoji.identifier
].get({ query: { limit, after } }); ].get({ query: { limit, after, type: typeof type == 'number' ? type : ReactionTypes[type] } });
const users = new Collection(); const users = new Collection();
for (const rawUser of data) { for (const rawUser of data) {
const user = this.client.users._add(rawUser); const user = this.client.users._add(rawUser);

View File

@@ -28,13 +28,13 @@ class MessageReaction {
* Whether the client has given this reaction * Whether the client has given this reaction
* @type {boolean} * @type {boolean}
*/ */
this.me = data.me || data.me_burst; this.me = data.me;
/** /**
* Is super reaction * Whether the client has super-reacted using this emoji
* @type {boolean} * @type {boolean}
*/ */
this.isBurst = Boolean(data.me_burst || data.burst); this.meBurst = data.me_burst;
/** /**
* A manager of the users that have given this reaction * A manager of the users that have given this reaction
@@ -44,6 +44,8 @@ class MessageReaction {
this._emoji = new ReactionEmoji(this, data.emoji); this._emoji = new ReactionEmoji(this, data.emoji);
this.burstColors = null;
this._patch(data); this._patch(data);
} }
@@ -56,18 +58,10 @@ class MessageReaction {
this.count ??= data.count; this.count ??= data.count;
} }
if ('burst_count' in data) {
/**
* The number of people that have given the same super reaction
* @type {?number}
*/
this.burstCount ??= data.burst_count;
}
if ('burst_colors' in data) { if ('burst_colors' in data) {
/** /**
* HEX colors used for super reaction * Hexadecimal colors used for this super reaction
* @type {string[]} * @type {?string[]}
*/ */
this.burstColors = data.burst_colors; this.burstColors = data.burst_colors;
} }
@@ -76,13 +70,13 @@ class MessageReaction {
/** /**
* The reaction count details object contains information about super and normal reaction counts. * The reaction count details object contains information about super and normal reaction counts.
* @typedef {Object} ReactionCountDetailsData * @typedef {Object} ReactionCountDetailsData
* @property {number} burst Count of super reaction * @property {number} burst Count of super reactions
* @property {number} normal Count of normal reaction * @property {number} normal Count of normal reactions
*/ */
/** /**
* The reaction count details object contains information about super and normal reaction counts. * The reaction count details object contains information about super and normal reaction counts.
* @type {?ReactionCountDetailsData} * @type {ReactionCountDetailsData}
*/ */
this.countDetails = { this.countDetails = {
burst: data.count_details.burst, burst: data.count_details.burst,
@@ -150,18 +144,32 @@ class MessageReaction {
return Util.flatten(this, { emoji: 'emojiId', message: 'messageId' }); return Util.flatten(this, { emoji: 'emojiId', message: 'messageId' });
} }
_add(user) { _add(user, burst) {
if (this.partial) return; if (this.partial) return;
this.users.cache.set(user.id, user); this.users.cache.set(user.id, user);
if (!this.me || user.id !== this.message.client.user.id || this.count === 0) this.count++; if (!this.me || user.id !== this.message.client.user.id || this.count === 0) {
this.me ||= user.id === this.message.client.user.id; this.count++;
if (burst) this.countDetails.burst++;
else this.countDetails.normal++;
}
if (user.id === this.message.client.user.id) {
if (burst) this.meBurst = true;
else this.me = true;
}
} }
_remove(user) { _remove(user, burst) {
if (this.partial) return; if (this.partial) return;
this.users.cache.delete(user.id); this.users.cache.delete(user.id);
if (!this.me || user.id !== this.message.client.user.id) this.count--; if (!this.me || user.id !== this.message.client.user.id) {
if (user.id === this.message.client.user.id) this.me = false; this.count--;
if (burst) this.countDetails.burst--;
else this.countDetails.normal--;
}
if (user.id === this.message.client.user.id) {
if (burst) this.meBurst = false;
else this.me = false;
}
if (this.count <= 0 && this.users.cache.size === 0) { if (this.count <= 0 && this.users.cache.size === 0) {
this.message.reactions.cache.delete(this.emoji.id ?? this.emoji.name); this.message.reactions.cache.delete(this.emoji.id ?? this.emoji.name);
} }

View File

@@ -1677,6 +1677,15 @@ exports.GuildScheduledEventEntityTypes = createEnum([null, 'STAGE_INSTANCE', 'VO
*/ */
exports.VideoQualityModes = createEnum([null, 'AUTO', 'FULL']); exports.VideoQualityModes = createEnum([null, 'AUTO', 'FULL']);
/**
* The type of reaction
* * NORMAL
* * BURST
* @typedef {string} ReactionType
* @see {@link https://discord.com/developers/docs/resources/channel#channel-object-video-quality-modes}
*/
exports.ReactionTypes = createEnum(['NORMAL', 'BURST']);
/** /**
* Sort {@link ThreadOnlyChannel} posts by creation time or activity * Sort {@link ThreadOnlyChannel} posts by creation time or activity
* * LATEST_ACTIVITY * * LATEST_ACTIVITY

5
typings/enums.d.ts vendored
View File

@@ -271,6 +271,11 @@ export const enum VideoQualityModes {
FULL = 2, FULL = 2,
} }
export const enum ReactionTypes {
NORMAL = 0,
BURST = 1,
}
export const enum WebhookTypes { export const enum WebhookTypes {
Incoming = 1, Incoming = 1,
'Channel Follower' = 2, 'Channel Follower' = 2,

22
typings/index.d.ts vendored
View File

@@ -103,6 +103,7 @@ import {
SelectMenuComponentTypes, SelectMenuComponentTypes,
InviteType, InviteType,
MessagePollLayoutType, MessagePollLayoutType,
ReactionTypes,
} from './enums'; } from './enums';
import { import {
APIApplicationRoleConnectionMetadata, APIApplicationRoleConnectionMetadata,
@@ -2438,14 +2439,13 @@ export class MessageReaction {
private constructor(client: Client, data: RawMessageReactionData, message: Message); private constructor(client: Client, data: RawMessageReactionData, message: Message);
private _emoji: GuildEmoji | ReactionEmoji; private _emoji: GuildEmoji | ReactionEmoji;
public burstColors: string[]; public burstColors: string[] | null;
public readonly client: Client<true>; public readonly client: Client<true>;
public count: number; public count: number;
public burstCount: number;
public countDetails: ReactionCountDetailsData; public countDetails: ReactionCountDetailsData;
public isBurst: boolean;
public readonly emoji: GuildEmoji | ReactionEmoji; public readonly emoji: GuildEmoji | ReactionEmoji;
public me: boolean; public me: boolean;
public meBurst: boolean;
public message: Message | PartialMessage; public message: Message | PartialMessage;
public readonly partial: false; public readonly partial: false;
public users: ReactionUserManager; public users: ReactionUserManager;
@@ -2458,6 +2458,9 @@ export interface ReactionCountDetailsData {
burst: number; burst: number;
normal: number; normal: number;
} }
export interface MessageReactionEventDetails {
burst: boolean;
}
export class MessageSelectMenu extends BaseMessageComponent { export class MessageSelectMenu extends BaseMessageComponent {
public constructor(data?: MessageSelectMenu | MessageSelectMenuOptions | APISelectMenuComponent); public constructor(data?: MessageSelectMenu | MessageSelectMenuOptions | APISelectMenuComponent);
@@ -5501,8 +5504,16 @@ export interface ClientEvents extends BaseClientEvents {
]; ];
messageReactionRemoveEmoji: [reaction: MessageReaction | PartialMessageReaction]; messageReactionRemoveEmoji: [reaction: MessageReaction | PartialMessageReaction];
messageDeleteBulk: [messages: Collection<Snowflake, Message | PartialMessage>]; messageDeleteBulk: [messages: Collection<Snowflake, Message | PartialMessage>];
messageReactionAdd: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser]; messageReactionAdd: [
messageReactionRemove: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser]; reaction: MessageReaction | PartialMessageReaction,
user: User | PartialUser,
details: MessageReactionEventDetails,
];
messageReactionRemove: [
reaction: MessageReaction | PartialMessageReaction,
user: User | PartialUser,
details: MessageReactionEventDetails,
];
messageUpdate: [oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage]; messageUpdate: [oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage];
presenceUpdate: [oldPresence: Presence | null, newPresence: Presence]; presenceUpdate: [oldPresence: Presence | null, newPresence: Presence];
ready: [client: Client<true>]; ready: [client: Client<true>];
@@ -6122,6 +6133,7 @@ export interface FetchMembersOptions {
} }
export interface FetchReactionUsersOptions { export interface FetchReactionUsersOptions {
type?: ReactionTypes;
limit?: number; limit?: number;
after?: Snowflake; after?: Snowflake;
} }