mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
feat(Partials): add DMChannel/MessageReaction#fetch() and PartialTypes.REACTION (#3474)
* add DMChannel#fetch() & Action#getChannel({recipients})
* ref for MessageReaction partial
* typings
* add PartialTypes.REACTION
* accommodate for fully removed reactions
* fix incorrect wording and typo
* typings: MessageReaction#count is nullable
* typings: mark MessageReaction#partial as readonly
Co-Authored-By: Vlad Frangu <kingdgrizzle@gmail.com>
* fix(User): fetch dm channel if cached one is partial
* docs: add missing comma
Co-Authored-By: Antonio Román <kyradiscord@gmail.com>
* fix: accomodate for new reactions
* fix: updating existing/new count on _patch
* docs: typo
* for consistency
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: SpaceEEC <spaceeec@yahoo.com>
Co-authored-by: Antonio Román <kyradiscord@gmail.com>
This commit is contained in:
@@ -9,8 +9,8 @@ discard the event. With partials, you're able to receive the event, with a Messa
|
||||
Partials are opt-in, and you can enable them in the Client options by specifying [PartialTypes](/#/docs/main/master/typedef/PartialType):
|
||||
|
||||
```js
|
||||
// Accept partial messages and DM channels when emitting events
|
||||
new Client({ partials: ['MESSAGE', 'CHANNEL'] });
|
||||
// Accept partial messages, DM channels, and reactions when emitting events
|
||||
new Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] });
|
||||
```
|
||||
|
||||
## Usage & warnings
|
||||
@@ -45,6 +45,10 @@ client.on('messageReactionAdd', async (reaction, user) => {
|
||||
if (reaction.message.partial) await reaction.message.fetch();
|
||||
// Now the message has been cached and is fully available:
|
||||
console.log(`${reaction.message.author}'s message "${reaction.message.content}" gained a reaction!`);
|
||||
// Fetches and caches the reaction itself, updating resources that were possibly defunct.
|
||||
if (reaction.partial) await reaction.fetch();
|
||||
// Now the reaction is fully available and the properties will be reflected accurately:
|
||||
console.log(`${reaction.count} user(s) have given the same reaction to this message!`);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ class GenericAction {
|
||||
return data.channel || this.getPayload({
|
||||
id,
|
||||
guild_id: data.guild_id,
|
||||
recipients: [data.author || { id: data.user_id }],
|
||||
}, this.client.channels, id, PartialTypes.CHANNEL);
|
||||
}
|
||||
|
||||
@@ -52,9 +53,9 @@ class GenericAction {
|
||||
const id = data.emoji.id || decodeURIComponent(data.emoji.name);
|
||||
return this.getPayload({
|
||||
emoji: data.emoji,
|
||||
count: 0,
|
||||
count: message.partial ? null : 0,
|
||||
me: user.id === this.client.user.id,
|
||||
}, message.reactions, id, PartialTypes.MESSAGE);
|
||||
}, message.reactions, id, PartialTypes.REACTION);
|
||||
}
|
||||
|
||||
getMember(data, guild) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
const { PartialTypes } = require('../../util/Constants');
|
||||
|
||||
/*
|
||||
{ user_id: 'id',
|
||||
@@ -26,11 +27,13 @@ class MessageReactionAdd extends Action {
|
||||
if (!message) return false;
|
||||
|
||||
// Verify reaction
|
||||
if (message.partial && !this.client.options.partials.includes(PartialTypes.REACTION)) return false;
|
||||
const reaction = message.reactions.add({
|
||||
emoji: data.emoji,
|
||||
count: 0,
|
||||
count: message.partial ? null : 0,
|
||||
me: user.id === this.client.user.id,
|
||||
});
|
||||
if (!reaction) return false;
|
||||
reaction._add(user);
|
||||
/**
|
||||
* Emitted whenever a reaction is added to a cached message.
|
||||
|
||||
@@ -50,6 +50,28 @@ class ReactionStore extends DataStore {
|
||||
return this.client.api.channels(this.message.channel.id).messages(this.message.id).reactions.delete()
|
||||
.then(() => this.message);
|
||||
}
|
||||
|
||||
_partial(emoji) {
|
||||
const id = emoji.id || emoji.name;
|
||||
const existing = this.get(id);
|
||||
return !existing || existing.partial;
|
||||
}
|
||||
|
||||
async _fetchReaction(reactionEmoji, cache) {
|
||||
const id = reactionEmoji.id || reactionEmoji.name;
|
||||
const existing = this.get(id);
|
||||
if (!this._partial(reactionEmoji)) return existing;
|
||||
const data = await this.client.api.channels(this.message.channel.id).messages(this.message.id).get();
|
||||
if (!data.reactions || !data.reactions.some(r => (r.emoji.id || r.emoji.name) === id)) {
|
||||
reactionEmoji.reaction._patch({ count: 0 });
|
||||
this.message.reactions.remove(id);
|
||||
return existing;
|
||||
}
|
||||
for (const reaction of data.reactions) {
|
||||
if (this._partial(reaction.emoji)) this.add(reaction, cache);
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactionStore;
|
||||
|
||||
@@ -56,7 +56,15 @@ class DMChannel extends Channel {
|
||||
* @readonly
|
||||
*/
|
||||
get partial() {
|
||||
return !this.recipient;
|
||||
return typeof this.lastMessageID === 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch this DMChannel.
|
||||
* @returns {Promise<DMChannel>}
|
||||
*/
|
||||
fetch() {
|
||||
return this.recipient.createDM();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,12 +27,6 @@ class MessageReaction {
|
||||
*/
|
||||
this.me = data.me;
|
||||
|
||||
/**
|
||||
* The number of people that have given the same reaction
|
||||
* @type {number}
|
||||
*/
|
||||
this.count = data.count || 0;
|
||||
|
||||
/**
|
||||
* The users that have given this reaction, mapped by their ID
|
||||
* @type {ReactionUserStore<Snowflake, User>}
|
||||
@@ -40,6 +34,17 @@ class MessageReaction {
|
||||
this.users = new ReactionUserStore(client, undefined, this);
|
||||
|
||||
this._emoji = new ReactionEmoji(this, data.emoji);
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
/**
|
||||
* The number of people that have given the same reaction
|
||||
* @type {?number}
|
||||
*/
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (this.count == undefined) this.count = data.count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,18 +68,36 @@ class MessageReaction {
|
||||
return this._emoji;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this reaction is a partial
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get partial() {
|
||||
return this.count === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch this reaction.
|
||||
* @returns {Promise<MessageReaction>}
|
||||
*/
|
||||
fetch() {
|
||||
return this.message.reactions._fetchReaction(this.emoji, true);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return Util.flatten(this, { emoji: 'emojiID', message: 'messageID' });
|
||||
}
|
||||
|
||||
_add(user) {
|
||||
if (this.partial) return;
|
||||
this.users.set(user.id, user);
|
||||
if (!this.me || user.id !== this.message.client.user.id || this.count === 0) this.count++;
|
||||
if (!this.me) this.me = user.id === this.message.client.user.id;
|
||||
}
|
||||
|
||||
_remove(user) {
|
||||
if (this.partial) return;
|
||||
this.users.delete(user.id);
|
||||
if (!this.me || user.id !== this.message.client.user.id) this.count--;
|
||||
if (user.id === this.message.client.user.id) this.me = false;
|
||||
|
||||
@@ -218,7 +218,7 @@ class User extends Base {
|
||||
*/
|
||||
async createDM() {
|
||||
const { dmChannel } = this;
|
||||
if (dmChannel) return dmChannel;
|
||||
if (dmChannel && !dmChannel.partial) return dmChannel;
|
||||
const data = await this.client.api.users(this.client.user.id).channels.post({ data: {
|
||||
recipient_id: this.id,
|
||||
} });
|
||||
|
||||
@@ -294,6 +294,7 @@ exports.ShardEvents = {
|
||||
* * CHANNEL (only affects DMChannels)
|
||||
* * GUILD_MEMBER
|
||||
* * MESSAGE
|
||||
* * REACTION
|
||||
* <warn>Partials require you to put checks in place when handling data, read the Partials topic listed in the
|
||||
* sidebar for more information.</warn>
|
||||
* @typedef {string} PartialType
|
||||
@@ -303,6 +304,7 @@ exports.PartialTypes = keyMirror([
|
||||
'CHANNEL',
|
||||
'GUILD_MEMBER',
|
||||
'MESSAGE',
|
||||
'REACTION',
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
8
typings/index.d.ts
vendored
8
typings/index.d.ts
vendored
@@ -649,6 +649,7 @@ declare module 'discord.js' {
|
||||
public messages: MessageStore;
|
||||
public recipient: User;
|
||||
public readonly partial: false;
|
||||
public fetch(): Promise<DMChannel>;
|
||||
}
|
||||
|
||||
export class Emoji extends Base {
|
||||
@@ -1100,11 +1101,13 @@ declare module 'discord.js' {
|
||||
constructor(client: Client, data: object, message: Message);
|
||||
private _emoji: GuildEmoji | ReactionEmoji;
|
||||
|
||||
public count: number;
|
||||
public count: number | null;
|
||||
public readonly emoji: GuildEmoji | ReactionEmoji;
|
||||
public me: boolean;
|
||||
public message: Message;
|
||||
public readonly partial: boolean;
|
||||
public users: ReactionUserStore;
|
||||
public fetch(): Promise<MessageReaction>;
|
||||
public toJSON(): object;
|
||||
}
|
||||
|
||||
@@ -2539,7 +2542,8 @@ declare module 'discord.js' {
|
||||
type PartialTypes = 'USER'
|
||||
| 'CHANNEL'
|
||||
| 'GUILD_MEMBER'
|
||||
| 'MESSAGE';
|
||||
| 'MESSAGE'
|
||||
| 'REACTION';
|
||||
|
||||
type Partialize<T> = {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user