feat: initial Papo bot scaffold

This commit is contained in:
Pascal.P
2025-11-30 11:04:41 +01:00
commit 000481a3b0
12168 changed files with 1584750 additions and 0 deletions

143
node_modules/discord.js/src/client/actions/Action.js generated vendored Normal file
View File

@@ -0,0 +1,143 @@
'use strict';
const { Poll } = require('../../structures/Poll.js');
const { PollAnswer } = require('../../structures/PollAnswer.js');
const Partials = require('../../util/Partials.js');
/*
ABOUT ACTIONS
Actions are similar to WebSocket Packet Handlers, but since introducing
the REST API methods, in order to prevent rewriting code to handle data,
"actions" have been introduced. They're basically what Packet Handlers
used to be but they're strictly for manipulating data and making sure
that WebSocket events don't clash with REST methods.
*/
class GenericAction {
constructor(client) {
this.client = client;
}
handle(data) {
return data;
}
getPayload(data, manager, id, partialType, cache) {
return this.client.options.partials.includes(partialType) ? manager._add(data, cache) : manager.cache.get(id);
}
getChannel(data) {
const payloadData = {};
const id = data.channel_id ?? data.id;
if (!('recipients' in data)) {
// Try to resolve the recipient, but do not add the client user.
const recipient = data.author ?? data.user ?? { id: data.user_id };
if (recipient.id !== this.client.user.id) payloadData.recipients = [recipient];
}
if (id !== undefined) payloadData.id = id;
return (
data[this.client.actions.injectedChannel] ??
this.getPayload({ ...data, ...payloadData }, this.client.channels, id, Partials.Channel)
);
}
getMessage(data, channel, cache) {
const id = data.message_id ?? data.id;
return (
data[this.client.actions.injectedMessage] ??
this.getPayload(
{
id,
channel_id: channel.id,
guild_id: data.guild_id ?? channel.guild?.id,
},
channel.messages,
id,
Partials.Message,
cache,
)
);
}
getPoll(data, message, channel) {
const includePollPartial = this.client.options.partials.includes(Partials.Poll);
const includePollAnswerPartial = this.client.options.partials.includes(Partials.PollAnswer);
if (message.partial && (!includePollPartial || !includePollAnswerPartial)) return null;
if (!message.poll && includePollPartial) {
message.poll = new Poll(this.client, data, message, channel);
}
if (message.poll && !message.poll.answers.has(data.answer_id) && includePollAnswerPartial) {
const pollAnswer = new PollAnswer(this.client, data, message.poll);
message.poll.answers.set(data.answer_id, pollAnswer);
}
return message.poll;
}
getReaction(data, message, user) {
const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
return this.getPayload(
{
emoji: data.emoji,
count: message.partial ? null : 0,
me: user?.id === this.client.user.id,
},
message.reactions,
id,
Partials.Reaction,
);
}
getMember(data, guild) {
return this.getPayload(data, guild.members, data.user.id, Partials.GuildMember);
}
getUser(data) {
const id = data.user_id;
return data[this.client.actions.injectedUser] ?? this.getPayload({ id }, this.client.users, id, Partials.User);
}
getUserFromMember(data) {
if (data.guild_id && data.member?.user) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (guild) {
return guild.members._add(data.member).user;
} else {
return this.client.users._add(data.member.user);
}
}
return this.getUser(data);
}
getScheduledEvent(data, guild) {
const id = data.guild_scheduled_event_id ?? data.id;
return this.getPayload(
{ id, guild_id: data.guild_id ?? guild.id },
guild.scheduledEvents,
id,
Partials.GuildScheduledEvent,
);
}
getThreadMember(id, manager) {
return this.getPayload({ user_id: id }, manager, id, Partials.ThreadMember, false);
}
getSoundboardSound(data, guild) {
return this.getPayload(data, guild.soundboardSounds, data.sound_id, Partials.SoundboardSound);
}
spreadInjectedData(data) {
return Object.fromEntries(Object.getOwnPropertySymbols(data).map(symbol => [symbol, data[symbol]]));
}
}
module.exports = GenericAction;

View File

@@ -0,0 +1,85 @@
'use strict';
class ActionsManager {
// These symbols represent fully built data that we inject at times when calling actions manually.
// Action#getUser, for example, will return the injected data (which is assumed to be a built structure)
// instead of trying to make it from provided data
injectedUser = Symbol('djs.actions.injectedUser');
injectedChannel = Symbol('djs.actions.injectedChannel');
injectedMessage = Symbol('djs.actions.injectedMessage');
constructor(client) {
this.client = client;
this.ApplicationCommandPermissionsUpdate = this.load(require('./ApplicationCommandPermissionsUpdate.js'));
this.AutoModerationActionExecution = this.load(require('./AutoModerationActionExecution.js'));
this.AutoModerationRuleCreate = this.load(require('./AutoModerationRuleCreate.js'));
this.AutoModerationRuleDelete = this.load(require('./AutoModerationRuleDelete.js'));
this.AutoModerationRuleUpdate = this.load(require('./AutoModerationRuleUpdate.js'));
this.ChannelCreate = this.load(require('./ChannelCreate.js'));
this.ChannelDelete = this.load(require('./ChannelDelete.js'));
this.ChannelUpdate = this.load(require('./ChannelUpdate.js'));
this.EntitlementCreate = this.load(require('./EntitlementCreate.js'));
this.EntitlementDelete = this.load(require('./EntitlementDelete.js'));
this.EntitlementUpdate = this.load(require('./EntitlementUpdate.js'));
this.GuildAuditLogEntryCreate = this.load(require('./GuildAuditLogEntryCreate.js'));
this.GuildBanAdd = this.load(require('./GuildBanAdd.js'));
this.GuildBanRemove = this.load(require('./GuildBanRemove.js'));
this.GuildChannelsPositionUpdate = this.load(require('./GuildChannelsPositionUpdate.js'));
this.GuildDelete = this.load(require('./GuildDelete.js'));
this.GuildEmojiCreate = this.load(require('./GuildEmojiCreate.js'));
this.GuildEmojiDelete = this.load(require('./GuildEmojiDelete.js'));
this.GuildEmojiUpdate = this.load(require('./GuildEmojiUpdate.js'));
this.GuildEmojisUpdate = this.load(require('./GuildEmojisUpdate.js'));
this.GuildIntegrationsUpdate = this.load(require('./GuildIntegrationsUpdate.js'));
this.GuildMemberRemove = this.load(require('./GuildMemberRemove.js'));
this.GuildMemberUpdate = this.load(require('./GuildMemberUpdate.js'));
this.GuildRoleCreate = this.load(require('./GuildRoleCreate.js'));
this.GuildRoleDelete = this.load(require('./GuildRoleDelete.js'));
this.GuildRoleUpdate = this.load(require('./GuildRoleUpdate.js'));
this.GuildRolesPositionUpdate = this.load(require('./GuildRolesPositionUpdate.js'));
this.GuildScheduledEventCreate = this.load(require('./GuildScheduledEventCreate.js'));
this.GuildScheduledEventDelete = this.load(require('./GuildScheduledEventDelete.js'));
this.GuildScheduledEventUpdate = this.load(require('./GuildScheduledEventUpdate.js'));
this.GuildScheduledEventUserAdd = this.load(require('./GuildScheduledEventUserAdd.js'));
this.GuildScheduledEventUserRemove = this.load(require('./GuildScheduledEventUserRemove.js'));
this.GuildSoundboardSoundDelete = this.load(require('./GuildSoundboardSoundDelete.js'));
this.GuildStickerCreate = this.load(require('./GuildStickerCreate.js'));
this.GuildStickerDelete = this.load(require('./GuildStickerDelete.js'));
this.GuildStickerUpdate = this.load(require('./GuildStickerUpdate.js'));
this.GuildStickersUpdate = this.load(require('./GuildStickersUpdate.js'));
this.GuildUpdate = this.load(require('./GuildUpdate.js'));
this.InteractionCreate = this.load(require('./InteractionCreate.js'));
this.InviteCreate = this.load(require('./InviteCreate.js'));
this.InviteDelete = this.load(require('./InviteDelete.js'));
this.MessageCreate = this.load(require('./MessageCreate.js'));
this.MessageDelete = this.load(require('./MessageDelete.js'));
this.MessageDeleteBulk = this.load(require('./MessageDeleteBulk.js'));
this.MessagePollVoteAdd = this.load(require('./MessagePollVoteAdd.js'));
this.MessagePollVoteRemove = this.load(require('./MessagePollVoteRemove.js'));
this.MessageReactionAdd = this.load(require('./MessageReactionAdd.js'));
this.MessageReactionRemove = this.load(require('./MessageReactionRemove.js'));
this.MessageReactionRemoveAll = this.load(require('./MessageReactionRemoveAll.js'));
this.MessageReactionRemoveEmoji = this.load(require('./MessageReactionRemoveEmoji.js'));
this.MessageUpdate = this.load(require('./MessageUpdate.js'));
this.PresenceUpdate = this.load(require('./PresenceUpdate.js'));
this.StageInstanceCreate = this.load(require('./StageInstanceCreate.js'));
this.StageInstanceDelete = this.load(require('./StageInstanceDelete.js'));
this.StageInstanceUpdate = this.load(require('./StageInstanceUpdate.js'));
this.ThreadCreate = this.load(require('./ThreadCreate.js'));
this.ThreadDelete = this.load(require('./ThreadDelete.js'));
this.ThreadListSync = this.load(require('./ThreadListSync.js'));
this.ThreadMemberUpdate = this.load(require('./ThreadMemberUpdate.js'));
this.ThreadMembersUpdate = this.load(require('./ThreadMembersUpdate.js'));
this.TypingStart = this.load(require('./TypingStart.js'));
this.UserUpdate = this.load(require('./UserUpdate.js'));
this.VoiceStateUpdate = this.load(require('./VoiceStateUpdate.js'));
this.WebhooksUpdate = this.load(require('./WebhooksUpdate.js'));
}
load(Action) {
return new Action(this.client);
}
}
module.exports = ActionsManager;

View File

@@ -0,0 +1,34 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
/**
* The data received in the {@link Client#event:applicationCommandPermissionsUpdate} event
* @typedef {Object} ApplicationCommandPermissionsUpdateData
* @property {Snowflake} id The id of the command or global entity that was updated
* @property {Snowflake} guildId The id of the guild in which permissions were updated
* @property {Snowflake} applicationId The id of the application that owns the command or entity being updated
* @property {ApplicationCommandPermissions[]} permissions The updated permissions
*/
class ApplicationCommandPermissionsUpdateAction extends Action {
handle(data) {
const client = this.client;
/**
* Emitted whenever permissions for an application command in a guild were updated.
* <warn>This includes permission updates for other applications in addition to the logged in client,
* check `data.applicationId` to verify which application the update is for</warn>
* @event Client#applicationCommandPermissionsUpdate
* @param {ApplicationCommandPermissionsUpdateData} data The updated permissions
*/
client.emit(Events.ApplicationCommandPermissionsUpdate, {
permissions: data.permissions,
id: data.id,
guildId: data.guild_id,
applicationId: data.application_id,
});
}
}
module.exports = ApplicationCommandPermissionsUpdateAction;

View File

@@ -0,0 +1,26 @@
'use strict';
const Action = require('./Action');
const AutoModerationActionExecution = require('../../structures/AutoModerationActionExecution');
const Events = require('../../util/Events');
class AutoModerationActionExecutionAction extends Action {
handle(data) {
const { client } = this;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
/**
* Emitted whenever an auto moderation rule is triggered.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
* @event Client#autoModerationActionExecution
* @param {AutoModerationActionExecution} autoModerationActionExecution The data of the execution
*/
client.emit(Events.AutoModerationActionExecution, new AutoModerationActionExecution(data, guild));
}
return {};
}
}
module.exports = AutoModerationActionExecutionAction;

View File

@@ -0,0 +1,27 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class AutoModerationRuleCreateAction extends Action {
handle(data) {
const { client } = this;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const autoModerationRule = guild.autoModerationRules._add(data);
/**
* Emitted whenever an auto moderation rule is created.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
* @event Client#autoModerationRuleCreate
* @param {AutoModerationRule} autoModerationRule The created auto moderation rule
*/
client.emit(Events.AutoModerationRuleCreate, autoModerationRule);
}
return {};
}
}
module.exports = AutoModerationRuleCreateAction;

View File

@@ -0,0 +1,31 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class AutoModerationRuleDeleteAction extends Action {
handle(data) {
const { client } = this;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const autoModerationRule = guild.autoModerationRules.cache.get(data.id);
if (autoModerationRule) {
guild.autoModerationRules.cache.delete(autoModerationRule.id);
/**
* Emitted whenever an auto moderation rule is deleted.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
* @event Client#autoModerationRuleDelete
* @param {AutoModerationRule} autoModerationRule The deleted auto moderation rule
*/
client.emit(Events.AutoModerationRuleDelete, autoModerationRule);
}
}
return {};
}
}
module.exports = AutoModerationRuleDeleteAction;

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class AutoModerationRuleUpdateAction extends Action {
handle(data) {
const { client } = this;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const oldAutoModerationRule = guild.autoModerationRules.cache.get(data.id)?._clone() ?? null;
const newAutoModerationRule = guild.autoModerationRules._add(data);
/**
* Emitted whenever an auto moderation rule gets updated.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
* @event Client#autoModerationRuleUpdate
* @param {?AutoModerationRule} oldAutoModerationRule The auto moderation rule before the update
* @param {AutoModerationRule} newAutoModerationRule The auto moderation rule after the update
*/
client.emit(Events.AutoModerationRuleUpdate, oldAutoModerationRule, newAutoModerationRule);
}
return {};
}
}
module.exports = AutoModerationRuleUpdateAction;

View File

@@ -0,0 +1,23 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class ChannelCreateAction extends Action {
handle(data) {
const client = this.client;
const existing = client.channels.cache.has(data.id);
const channel = client.channels._add(data);
if (!existing && channel) {
/**
* Emitted whenever a guild channel is created.
* @event Client#channelCreate
* @param {GuildChannel} channel The channel that was created
*/
client.emit(Events.ChannelCreate, channel);
}
return { channel };
}
}
module.exports = ChannelCreateAction;

View File

@@ -0,0 +1,23 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class ChannelDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.id);
if (channel) {
client.channels._remove(channel.id);
/**
* Emitted whenever a channel is deleted.
* @event Client#channelDelete
* @param {DMChannel|GuildChannel} channel The channel that was deleted
*/
client.emit(Events.ChannelDelete, channel);
}
}
}
module.exports = ChannelDeleteAction;

View File

@@ -0,0 +1,42 @@
'use strict';
const Action = require('./Action');
const { createChannel } = require('../../util/Channels');
class ChannelUpdateAction extends Action {
handle(data) {
const client = this.client;
let channel = client.channels.cache.get(data.id);
if (channel) {
const old = channel._update(data);
if (channel.type !== data.type) {
const newChannel = createChannel(this.client, data, channel.guild);
if (!newChannel) {
this.client.channels.cache.delete(channel.id);
return {};
}
if (channel.isTextBased() && newChannel.isTextBased()) {
for (const [id, message] of channel.messages.cache) newChannel.messages.cache.set(id, message);
}
channel = newChannel;
this.client.channels.cache.set(channel.id, channel);
}
return {
old,
updated: channel,
};
} else {
client.channels._add(data);
}
return {};
}
}
module.exports = ChannelUpdateAction;

View File

@@ -0,0 +1,23 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class EntitlementCreateAction extends Action {
handle(data) {
const client = this.client;
const entitlement = client.application.entitlements._add(data);
/**
* Emitted whenever an entitlement is created.
* @event Client#entitlementCreate
* @param {Entitlement} entitlement The entitlement that was created
*/
client.emit(Events.EntitlementCreate, entitlement);
return {};
}
}
module.exports = EntitlementCreateAction;

View File

@@ -0,0 +1,27 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class EntitlementDeleteAction extends Action {
handle(data) {
const client = this.client;
const entitlement = client.application.entitlements._add(data, false);
client.application.entitlements.cache.delete(entitlement.id);
/**
* Emitted whenever an entitlement is deleted.
* <warn>Entitlements are not deleted when they expire.
* This is only triggered when Discord issues a refund or deletes the entitlement manually.</warn>
* @event Client#entitlementDelete
* @param {Entitlement} entitlement The entitlement that was deleted
*/
client.emit(Events.EntitlementDelete, entitlement);
return {};
}
}
module.exports = EntitlementDeleteAction;

View File

@@ -0,0 +1,25 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class EntitlementUpdateAction extends Action {
handle(data) {
const client = this.client;
const oldEntitlement = client.application.entitlements.cache.get(data.id)?._clone() ?? null;
const newEntitlement = client.application.entitlements._add(data);
/**
* Emitted whenever an entitlement is updated - i.e. when a user's subscription renews.
* @event Client#entitlementUpdate
* @param {?Entitlement} oldEntitlement The entitlement before the update
* @param {Entitlement} newEntitlement The entitlement after the update
*/
client.emit(Events.EntitlementUpdate, oldEntitlement, newEntitlement);
return {};
}
}
module.exports = EntitlementUpdateAction;

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action');
const GuildAuditLogsEntry = require('../../structures/GuildAuditLogsEntry');
const Events = require('../../util/Events');
class GuildAuditLogEntryCreateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
let auditLogEntry;
if (guild) {
auditLogEntry = new GuildAuditLogsEntry(guild, data);
/**
* Emitted whenever a guild audit log entry is created.
* @event Client#guildAuditLogEntryCreate
* @param {GuildAuditLogsEntry} auditLogEntry The entry that was created
* @param {Guild} guild The guild where the entry was created
*/
client.emit(Events.GuildAuditLogEntryCreate, auditLogEntry, guild);
}
return { auditLogEntry };
}
}
module.exports = GuildAuditLogEntryCreateAction;

View File

@@ -0,0 +1,20 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildBanAdd extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a member is banned from a guild.
* @event Client#guildBanAdd
* @param {GuildBan} ban The ban that occurred
*/
if (guild) client.emit(Events.GuildBanAdd, guild.bans._add(data));
}
}
module.exports = GuildBanAdd;

View File

@@ -0,0 +1,25 @@
'use strict';
const Action = require('./Action');
const GuildBan = require('../../structures/GuildBan');
const Events = require('../../util/Events');
class GuildBanRemove extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a member is unbanned from a guild.
* @event Client#guildBanRemove
* @param {GuildBan} ban The ban that was removed
*/
if (guild) {
const ban = guild.bans.cache.get(data.user.id) ?? new GuildBan(client, data, guild);
guild.bans.cache.delete(ban.user.id);
client.emit(Events.GuildBanRemove, ban);
}
}
}
module.exports = GuildBanRemove;

View File

@@ -0,0 +1,21 @@
'use strict';
const Action = require('./Action');
class GuildChannelsPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
for (const partialChannel of data.channels) {
const channel = guild.channels.cache.get(partialChannel.id);
if (channel) channel.rawPosition = partialChannel.position;
}
}
return { guild };
}
}
module.exports = GuildChannelsPositionUpdate;

View File

@@ -0,0 +1,44 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildDeleteAction extends Action {
handle(data) {
const client = this.client;
let guild = client.guilds.cache.get(data.id);
if (guild) {
if (data.unavailable) {
// Guild is unavailable
guild.available = false;
/**
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
* @event Client#guildUnavailable
* @param {Guild} guild The guild that has become unavailable
*/
client.emit(Events.GuildUnavailable, guild);
// Stops the GuildDelete packet thinking a guild was actually deleted,
// handles emitting of event itself
return;
}
for (const channel of guild.channels.cache.values()) this.client.channels._remove(channel.id);
client.voice.adapters.get(data.id)?.destroy();
// Delete guild
client.guilds.cache.delete(guild.id);
/**
* Emitted whenever a guild kicks the client or the guild is deleted/left.
* @event Client#guildDelete
* @param {Guild} guild The guild that was deleted
*/
client.emit(Events.GuildDelete, guild);
}
}
}
module.exports = GuildDeleteAction;

View File

@@ -0,0 +1,20 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildEmojiCreateAction extends Action {
handle(guild, createdEmoji) {
const already = guild.emojis.cache.has(createdEmoji.id);
const emoji = guild.emojis._add(createdEmoji);
/**
* Emitted whenever a custom emoji is created in a guild.
* @event Client#emojiCreate
* @param {GuildEmoji} emoji The emoji that was created
*/
if (!already) this.client.emit(Events.GuildEmojiCreate, emoji);
return { emoji };
}
}
module.exports = GuildEmojiCreateAction;

View File

@@ -0,0 +1,19 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildEmojiDeleteAction extends Action {
handle(emoji) {
emoji.guild.emojis.cache.delete(emoji.id);
/**
* Emitted whenever a custom emoji is deleted in a guild.
* @event Client#emojiDelete
* @param {GuildEmoji} emoji The emoji that was deleted
*/
this.client.emit(Events.GuildEmojiDelete, emoji);
return { emoji };
}
}
module.exports = GuildEmojiDeleteAction;

View File

@@ -0,0 +1,20 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildEmojiUpdateAction extends Action {
handle(current, data) {
const old = current._update(data);
/**
* Emitted whenever a custom emoji is updated in a guild.
* @event Client#emojiUpdate
* @param {GuildEmoji} oldEmoji The old emoji
* @param {GuildEmoji} newEmoji The new emoji
*/
this.client.emit(Events.GuildEmojiUpdate, old, current);
return { emoji: current };
}
}
module.exports = GuildEmojiUpdateAction;

View File

@@ -0,0 +1,34 @@
'use strict';
const Action = require('./Action');
class GuildEmojisUpdateAction extends Action {
handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild?.emojis) return;
const deletions = new Map(guild.emojis.cache);
for (const emoji of data.emojis) {
// Determine type of emoji event
const cachedEmoji = guild.emojis.cache.get(emoji.id);
if (cachedEmoji) {
deletions.delete(emoji.id);
if (!cachedEmoji.equals(emoji)) {
// Emoji updated
this.client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji);
}
} else {
// Emoji added
this.client.actions.GuildEmojiCreate.handle(guild, emoji);
}
}
for (const emoji of deletions.values()) {
// Emoji deleted
this.client.actions.GuildEmojiDelete.handle(emoji);
}
}
}
module.exports = GuildEmojisUpdateAction;

View File

@@ -0,0 +1,19 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildIntegrationsUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a guild integration is updated
* @event Client#guildIntegrationsUpdate
* @param {Guild} guild The guild whose integrations were updated
*/
if (guild) client.emit(Events.GuildIntegrationsUpdate, guild);
}
}
module.exports = GuildIntegrationsUpdate;

View File

@@ -0,0 +1,31 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
const Status = require('../../util/Status');
class GuildMemberRemoveAction extends Action {
handle(data, shard) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
let member = null;
if (guild) {
member = this.getMember({ user: data.user }, guild);
guild.memberCount--;
if (member) {
guild.members.cache.delete(member.id);
/**
* Emitted whenever a member leaves a guild, or is kicked.
* @event Client#guildMemberRemove
* @param {GuildMember} member The member that has left/been kicked from the guild
*/
if (shard.status === Status.Ready) client.emit(Events.GuildMemberRemove, member);
}
guild.presences.cache.delete(data.user.id);
guild.voiceStates.cache.delete(data.user.id);
}
return { guild, member };
}
}
module.exports = GuildMemberRemoveAction;

View File

@@ -0,0 +1,44 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
const Status = require('../../util/Status');
class GuildMemberUpdateAction extends Action {
handle(data, shard) {
const { client } = this;
if (data.user.username) {
const user = client.users.cache.get(data.user.id);
if (!user) {
client.users._add(data.user);
} else if (!user._equals(data.user)) {
client.actions.UserUpdate.handle(data.user);
}
}
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const member = this.getMember({ user: data.user }, guild);
if (member) {
const old = member._update(data);
/**
* Emitted whenever a guild member changes - i.e. new role, removed role, nickname.
* @event Client#guildMemberUpdate
* @param {GuildMember} oldMember The member before the update
* @param {GuildMember} newMember The member after the update
*/
if (shard.status === Status.Ready && !member.equals(old)) client.emit(Events.GuildMemberUpdate, old, member);
} else {
const newMember = guild.members._add(data);
/**
* Emitted whenever a member becomes available.
* @event Client#guildMemberAvailable
* @param {GuildMember} member The member that became available
*/
this.client.emit(Events.GuildMemberAvailable, newMember);
}
}
}
}
module.exports = GuildMemberUpdateAction;

View File

@@ -0,0 +1,25 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildRoleCreate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
let role;
if (guild) {
const already = guild.roles.cache.has(data.role.id);
role = guild.roles._add(data.role);
/**
* Emitted whenever a role is created.
* @event Client#roleCreate
* @param {Role} role The role that was created
*/
if (!already) client.emit(Events.GuildRoleCreate, role);
}
return { role };
}
}
module.exports = GuildRoleCreate;

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildRoleDeleteAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
let role;
if (guild) {
role = guild.roles.cache.get(data.role_id);
if (role) {
guild.roles.cache.delete(data.role_id);
/**
* Emitted whenever a guild role is deleted.
* @event Client#roleDelete
* @param {Role} role The role that was deleted
*/
client.emit(Events.GuildRoleDelete, role);
}
}
return { role };
}
}
module.exports = GuildRoleDeleteAction;

View File

@@ -0,0 +1,39 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildRoleUpdateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
let old = null;
const role = guild.roles.cache.get(data.role.id);
if (role) {
old = role._update(data.role);
/**
* Emitted whenever a guild role is updated.
* @event Client#roleUpdate
* @param {Role} oldRole The role before the update
* @param {Role} newRole The role after the update
*/
client.emit(Events.GuildRoleUpdate, old, role);
}
return {
old,
updated: role,
};
}
return {
old: null,
updated: null,
};
}
}
module.exports = GuildRoleUpdateAction;

View File

@@ -0,0 +1,21 @@
'use strict';
const Action = require('./Action');
class GuildRolesPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
for (const partialRole of data.roles) {
const role = guild.roles.cache.get(partialRole.id);
if (role) role.rawPosition = partialRole.position;
}
}
return { guild };
}
}
module.exports = GuildRolesPositionUpdate;

View File

@@ -0,0 +1,27 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildScheduledEventCreateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const guildScheduledEvent = guild.scheduledEvents._add(data);
/**
* Emitted whenever a guild scheduled event is created.
* @event Client#guildScheduledEventCreate
* @param {GuildScheduledEvent} guildScheduledEvent The created guild scheduled event
*/
client.emit(Events.GuildScheduledEventCreate, guildScheduledEvent);
return { guildScheduledEvent };
}
return {};
}
}
module.exports = GuildScheduledEventCreateAction;

View File

@@ -0,0 +1,31 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildScheduledEventDeleteAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const guildScheduledEvent = this.getScheduledEvent(data, guild);
if (guildScheduledEvent) {
guild.scheduledEvents.cache.delete(guildScheduledEvent.id);
/**
* Emitted whenever a guild scheduled event is deleted.
* @event Client#guildScheduledEventDelete
* @param {GuildScheduledEvent} guildScheduledEvent The deleted guild scheduled event
*/
client.emit(Events.GuildScheduledEventDelete, guildScheduledEvent);
return { guildScheduledEvent };
}
}
return {};
}
}
module.exports = GuildScheduledEventDeleteAction;

View File

@@ -0,0 +1,30 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildScheduledEventUpdateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const oldGuildScheduledEvent = guild.scheduledEvents.cache.get(data.id)?._clone() ?? null;
const newGuildScheduledEvent = guild.scheduledEvents._add(data);
/**
* Emitted whenever a guild scheduled event gets updated.
* @event Client#guildScheduledEventUpdate
* @param {?GuildScheduledEvent} oldGuildScheduledEvent The guild scheduled event object before the update
* @param {GuildScheduledEvent} newGuildScheduledEvent The guild scheduled event object after the update
*/
client.emit(Events.GuildScheduledEventUpdate, oldGuildScheduledEvent, newGuildScheduledEvent);
return { oldGuildScheduledEvent, newGuildScheduledEvent };
}
return {};
}
}
module.exports = GuildScheduledEventUpdateAction;

View File

@@ -0,0 +1,32 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildScheduledEventUserAddAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const guildScheduledEvent = this.getScheduledEvent(data, guild);
const user = this.getUser(data);
if (guildScheduledEvent && user) {
/**
* Emitted whenever a user subscribes to a guild scheduled event
* @event Client#guildScheduledEventUserAdd
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
* @param {User} user The user who subscribed
*/
client.emit(Events.GuildScheduledEventUserAdd, guildScheduledEvent, user);
return { guildScheduledEvent, user };
}
}
return {};
}
}
module.exports = GuildScheduledEventUserAddAction;

View File

@@ -0,0 +1,32 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildScheduledEventUserRemoveAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const guildScheduledEvent = this.getScheduledEvent(data, guild);
const user = this.getUser(data);
if (guildScheduledEvent && user) {
/**
* Emitted whenever a user unsubscribes from a guild scheduled event
* @event Client#guildScheduledEventUserRemove
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
* @param {User} user The user who unsubscribed
*/
client.emit(Events.GuildScheduledEventUserRemove, guildScheduledEvent, user);
return { guildScheduledEvent, user };
}
}
return {};
}
}
module.exports = GuildScheduledEventUserRemoveAction;

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action.js');
const Events = require('../../util/Events.js');
class GuildSoundboardSoundDeleteAction extends Action {
handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild) return {};
const soundboardSound = this.getSoundboardSound(data, guild);
if (soundboardSound) {
guild.soundboardSounds.cache.delete(soundboardSound.soundId);
/**
* Emitted whenever a soundboard sound is deleted in a guild.
* @event Client#guildSoundboardSoundDelete
* @param {SoundboardSound} soundboardSound The soundboard sound that was deleted
*/
this.client.emit(Events.GuildSoundboardSoundDelete, soundboardSound);
}
return { soundboardSound };
}
}
module.exports = GuildSoundboardSoundDeleteAction;

View File

@@ -0,0 +1,20 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildStickerCreateAction extends Action {
handle(guild, createdSticker) {
const already = guild.stickers.cache.has(createdSticker.id);
const sticker = guild.stickers._add(createdSticker);
/**
* Emitted whenever a custom sticker is created in a guild.
* @event Client#stickerCreate
* @param {Sticker} sticker The sticker that was created
*/
if (!already) this.client.emit(Events.GuildStickerCreate, sticker);
return { sticker };
}
}
module.exports = GuildStickerCreateAction;

View File

@@ -0,0 +1,19 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildStickerDeleteAction extends Action {
handle(sticker) {
sticker.guild.stickers.cache.delete(sticker.id);
/**
* Emitted whenever a custom sticker is deleted in a guild.
* @event Client#stickerDelete
* @param {Sticker} sticker The sticker that was deleted
*/
this.client.emit(Events.GuildStickerDelete, sticker);
return { sticker };
}
}
module.exports = GuildStickerDeleteAction;

View File

@@ -0,0 +1,20 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildStickerUpdateAction extends Action {
handle(current, data) {
const old = current._update(data);
/**
* Emitted whenever a custom sticker is updated in a guild.
* @event Client#stickerUpdate
* @param {Sticker} oldSticker The old sticker
* @param {Sticker} newSticker The new sticker
*/
this.client.emit(Events.GuildStickerUpdate, old, current);
return { sticker: current };
}
}
module.exports = GuildStickerUpdateAction;

View File

@@ -0,0 +1,34 @@
'use strict';
const Action = require('./Action');
class GuildStickersUpdateAction extends Action {
handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild?.stickers) return;
const deletions = new Map(guild.stickers.cache);
for (const sticker of data.stickers) {
// Determine type of sticker event
const cachedSticker = guild.stickers.cache.get(sticker.id);
if (cachedSticker) {
deletions.delete(sticker.id);
if (!cachedSticker.equals(sticker)) {
// Sticker updated
this.client.actions.GuildStickerUpdate.handle(cachedSticker, sticker);
}
} else {
// Sticker added
this.client.actions.GuildStickerCreate.handle(guild, sticker);
}
}
for (const sticker of deletions.values()) {
// Sticker deleted
this.client.actions.GuildStickerDelete.handle(sticker);
}
}
}
module.exports = GuildStickersUpdateAction;

View File

@@ -0,0 +1,33 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class GuildUpdateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.id);
if (guild) {
const old = guild._update(data);
/**
* Emitted whenever a guild is updated - e.g. name change.
* @event Client#guildUpdate
* @param {Guild} oldGuild The guild before the update
* @param {Guild} newGuild The guild after the update
*/
client.emit(Events.GuildUpdate, old, guild);
return {
old,
updated: guild,
};
}
return {
old: null,
updated: null,
};
}
}
module.exports = GuildUpdateAction;

View File

@@ -0,0 +1,105 @@
'use strict';
const { InteractionType, ComponentType, ApplicationCommandType } = require('discord-api-types/v10');
const Action = require('./Action');
const AutocompleteInteraction = require('../../structures/AutocompleteInteraction');
const ButtonInteraction = require('../../structures/ButtonInteraction');
const ChannelSelectMenuInteraction = require('../../structures/ChannelSelectMenuInteraction');
const ChatInputCommandInteraction = require('../../structures/ChatInputCommandInteraction');
const MentionableSelectMenuInteraction = require('../../structures/MentionableSelectMenuInteraction');
const MessageContextMenuCommandInteraction = require('../../structures/MessageContextMenuCommandInteraction');
const ModalSubmitInteraction = require('../../structures/ModalSubmitInteraction');
const PrimaryEntryPointCommandInteraction = require('../../structures/PrimaryEntryPointCommandInteraction');
const RoleSelectMenuInteraction = require('../../structures/RoleSelectMenuInteraction');
const StringSelectMenuInteraction = require('../../structures/StringSelectMenuInteraction');
const UserContextMenuCommandInteraction = require('../../structures/UserContextMenuCommandInteraction');
const UserSelectMenuInteraction = require('../../structures/UserSelectMenuInteraction');
const Events = require('../../util/Events');
class InteractionCreateAction extends Action {
handle(data) {
const client = this.client;
// Resolve and cache partial channels for Interaction#channel getter
const channel = data.channel && this.getChannel(data.channel);
// Do not emit this for interactions that cache messages that are non-text-based.
let InteractionClass;
switch (data.type) {
case InteractionType.ApplicationCommand:
switch (data.data.type) {
case ApplicationCommandType.ChatInput:
InteractionClass = ChatInputCommandInteraction;
break;
case ApplicationCommandType.User:
InteractionClass = UserContextMenuCommandInteraction;
break;
case ApplicationCommandType.Message:
if (channel && !channel.isTextBased()) return;
InteractionClass = MessageContextMenuCommandInteraction;
break;
case ApplicationCommandType.PrimaryEntryPoint:
InteractionClass = PrimaryEntryPointCommandInteraction;
break;
default:
client.emit(
Events.Debug,
`[INTERACTION] Received application command interaction with unknown type: ${data.data.type}`,
);
return;
}
break;
case InteractionType.MessageComponent:
if (channel && !channel.isTextBased()) return;
switch (data.data.component_type) {
case ComponentType.Button:
InteractionClass = ButtonInteraction;
break;
case ComponentType.StringSelect:
InteractionClass = StringSelectMenuInteraction;
break;
case ComponentType.UserSelect:
InteractionClass = UserSelectMenuInteraction;
break;
case ComponentType.RoleSelect:
InteractionClass = RoleSelectMenuInteraction;
break;
case ComponentType.MentionableSelect:
InteractionClass = MentionableSelectMenuInteraction;
break;
case ComponentType.ChannelSelect:
InteractionClass = ChannelSelectMenuInteraction;
break;
default:
client.emit(
Events.Debug,
`[INTERACTION] Received component interaction with unknown type: ${data.data.component_type}`,
);
return;
}
break;
case InteractionType.ApplicationCommandAutocomplete:
InteractionClass = AutocompleteInteraction;
break;
case InteractionType.ModalSubmit:
InteractionClass = ModalSubmitInteraction;
break;
default:
client.emit(Events.Debug, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
return;
}
const interaction = new InteractionClass(client, data);
/**
* Emitted when an interaction is created.
* @event Client#interactionCreate
* @param {BaseInteraction} interaction The interaction which was created
*/
client.emit(Events.InteractionCreate, interaction);
}
}
module.exports = InteractionCreateAction;

View File

@@ -0,0 +1,27 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class InviteCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
const guild = client.guilds.cache.get(data.guild_id);
if (!channel) return false;
const inviteData = Object.assign(data, { channel, guild });
const invite = guild.invites._add(inviteData);
/**
* Emitted when an invite is created.
* <info>This event requires the {@link PermissionFlagsBits.ManageChannels} permission for the channel.</info>
* @event Client#inviteCreate
* @param {Invite} invite The invite that was created
*/
client.emit(Events.InviteCreate, invite);
return { invite };
}
}
module.exports = InviteCreateAction;

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action');
const Invite = require('../../structures/Invite');
const Events = require('../../util/Events');
class InviteDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
const guild = client.guilds.cache.get(data.guild_id);
if (!channel) return false;
const inviteData = Object.assign(data, { channel, guild });
const invite = new Invite(client, inviteData);
guild.invites.cache.delete(invite.code);
/**
* Emitted when an invite is deleted.
* <info>This event requires the {@link PermissionFlagsBits.ManageChannels} permission for the channel.</info>
* @event Client#inviteDelete
* @param {Invite} invite The invite that was deleted
*/
client.emit(Events.InviteDelete, invite);
return { invite };
}
}
module.exports = InviteDeleteAction;

View File

@@ -0,0 +1,41 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class MessageCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({
id: data.channel_id,
author: data.author,
...('guild_id' in data && { guild_id: data.guild_id }),
});
if (channel) {
if (!channel.isTextBased()) return {};
if (channel.isThread()) {
channel.messageCount++;
channel.totalMessageSent++;
}
const existing = channel.messages.cache.get(data.id);
if (existing && existing.author?.id !== this.client.user.id) return { message: existing };
const message = existing ?? channel.messages._add(data);
channel.lastMessageId = data.id;
/**
* Emitted whenever a message is created.
* @event Client#messageCreate
* @param {Message} message The created message
*/
client.emit(Events.MessageCreate, message);
return { message };
}
return {};
}
}
module.exports = MessageCreateAction;

View File

@@ -0,0 +1,32 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class MessageDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
let message;
if (channel) {
if (!channel.isTextBased()) return {};
if (channel.isThread()) channel.messageCount--;
message = this.getMessage(data, channel);
if (message) {
channel.messages.cache.delete(message.id);
/**
* Emitted whenever a message is deleted.
* @event Client#messageDelete
* @param {Message} message The deleted message
*/
client.emit(Events.MessageDelete, message);
}
}
return { message };
}
}
module.exports = MessageDeleteAction;

View File

@@ -0,0 +1,47 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Action = require('./Action');
const Events = require('../../util/Events');
class MessageDeleteBulkAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
if (channel) {
if (!channel.isTextBased()) return {};
if (channel.isThread()) channel.messageCount -= data.ids.length;
const ids = data.ids;
const messages = new Collection();
for (const id of ids) {
const message = this.getMessage(
{
id,
guild_id: data.guild_id,
},
channel,
false,
);
if (message) {
messages.set(message.id, message);
channel.messages.cache.delete(id);
}
}
/**
* Emitted whenever messages are deleted in bulk.
* @event Client#messageDeleteBulk
* @param {Collection<Snowflake, Message>} messages The deleted messages, mapped by their id
* @param {GuildTextBasedChannel} channel The channel that the messages were deleted in
*/
if (messages.size > 0) client.emit(Events.MessageBulkDelete, messages, channel);
return { messages };
}
return {};
}
}
module.exports = MessageDeleteBulkAction;

View File

@@ -0,0 +1,40 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class MessagePollVoteAddAction extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
const message = this.getMessage(data, channel);
if (!message) return false;
const poll = this.getPoll(data, message, channel);
if (!poll) return false;
const answer = poll.answers.get(data.answer_id);
if (!answer) return false;
const user = this.getUser(data);
if (user) {
answer.voters._add(user);
}
answer.voteCount++;
/**
* Emitted whenever a user votes in a poll.
* @event Client#messagePollVoteAdd
* @param {PollAnswer} pollAnswer The answer that was voted on
* @param {Snowflake} userId The id of the user that voted
*/
this.client.emit(Events.MessagePollVoteAdd, answer, data.user_id);
return { poll };
}
}
module.exports = MessagePollVoteAddAction;

View File

@@ -0,0 +1,38 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class MessagePollVoteRemoveAction extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
const message = this.getMessage(data, channel);
if (!message) return false;
const poll = this.getPoll(data, message, channel);
if (!poll) return false;
const answer = poll.answers.get(data.answer_id);
if (!answer) return false;
answer.voters.cache.delete(data.user_id);
if (answer.voteCount > 0) {
answer.voteCount--;
}
/**
* Emitted whenever a user removes their vote in a poll.
* @event Client#messagePollVoteRemove
* @param {PollAnswer} pollAnswer The answer where the vote was removed
* @param {Snowflake} userId The id of the user that removed their vote
*/
this.client.emit(Events.MessagePollVoteRemove, answer, data.user_id);
return { poll };
}
}
module.exports = MessagePollVoteRemoveAction;

View File

@@ -0,0 +1,70 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
const Partials = require('../../util/Partials');
/*
{ user_id: 'id',
message_id: 'id',
emoji: { name: '<27>', id: null },
channel_id: 'id',
burst: boolean
// If originating from a guild
guild_id: 'id',
member: { ..., user: { ... } } }
*/
class MessageReactionAdd extends Action {
handle(data, fromStructure = false) {
if (!data.emoji) return false;
const user = this.getUserFromMember(data);
if (!user) return false;
// Verify channel
const channel = this.getChannel({
id: data.channel_id,
...('guild_id' in data && { guild_id: data.guild_id }),
user_id: data.user_id,
...this.spreadInjectedData(data),
});
if (!channel?.isTextBased()) return false;
// Verify message
const message = this.getMessage(data, channel);
if (!message) return false;
// Verify reaction
const includePartial = this.client.options.partials.includes(Partials.Reaction);
if (message.partial && !includePartial) return false;
const reaction = message.reactions._add({
emoji: data.emoji,
count: message.partial ? null : 0,
me: user.id === this.client.user.id,
burst_colors: data.burst_colors,
});
if (!reaction) return false;
reaction._add(user, data.burst);
if (fromStructure) return { message, reaction, user };
/**
* Provides additional information about altered reaction
* @typedef {Object} MessageReactionEventDetails
* @property {ReactionType} type The type of the reaction
* @property {boolean} burst Determines whether a super reaction was used
*/
/**
* Emitted whenever a reaction is added to a cached message.
* @event Client#messageReactionAdd
* @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user that applied the guild or reaction emoji
* @param {MessageReactionEventDetails} details Details of adding the reaction
*/
this.client.emit(Events.MessageReactionAdd, reaction, user, { type: data.type, burst: data.burst });
return { message, reaction, user };
}
}
module.exports = MessageReactionAdd;

View File

@@ -0,0 +1,50 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
/*
{ user_id: 'id',
message_id: 'id',
emoji: { name: '<27>', id: null },
channel_id: 'id',
guild_id: 'id' }
*/
class MessageReactionRemove extends Action {
handle(data) {
if (!data.emoji) return false;
const user = this.getUser(data);
if (!user) return false;
// Verify channel
const channel = this.getChannel({
id: data.channel_id,
...('guild_id' in data && { guild_id: data.guild_id }),
user_id: data.user_id,
});
if (!channel?.isTextBased()) return false;
// Verify message
const message = this.getMessage(data, channel);
if (!message) return false;
// Verify reaction
const reaction = this.getReaction(data, message, user);
if (!reaction) return false;
reaction._remove(user, data.burst);
/**
* Emitted whenever a reaction is removed from a cached message.
* @event Client#messageReactionRemove
* @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user whose emoji or reaction emoji was removed
* @param {MessageReactionEventDetails} details Details of removing the reaction
*/
this.client.emit(Events.MessageReactionRemove, reaction, user, { type: data.type, burst: data.burst });
return { message, reaction, user };
}
}
module.exports = MessageReactionRemove;

View File

@@ -0,0 +1,33 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class MessageReactionRemoveAll extends Action {
handle(data) {
// Verify channel
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
// Verify message
const message = this.getMessage(data, channel);
if (!message) return false;
// Copy removed reactions to emit for the event.
const removed = message.reactions.cache.clone();
message.reactions.cache.clear();
this.client.emit(Events.MessageReactionRemoveAll, message, removed);
return { message };
}
}
/**
* Emitted whenever all reactions are removed from a cached message.
* @event Client#messageReactionRemoveAll
* @param {Message} message The message the reactions were removed from
* @param {Collection<string|Snowflake, MessageReaction>} reactions The cached message reactions that were removed.
*/
module.exports = MessageReactionRemoveAll;

View File

@@ -0,0 +1,28 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class MessageReactionRemoveEmoji extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
const message = this.getMessage(data, channel);
if (!message) return false;
const reaction = this.getReaction(data, message);
if (!reaction) return false;
if (!message.partial) message.reactions.cache.delete(reaction.emoji.id ?? reaction.emoji.name);
/**
* Emitted when a bot removes an emoji reaction from a cached message.
* @event Client#messageReactionRemoveEmoji
* @param {MessageReaction} reaction The reaction that was removed
*/
this.client.emit(Events.MessageReactionRemoveEmoji, reaction);
return { reaction };
}
}
module.exports = MessageReactionRemoveEmoji;

View File

@@ -0,0 +1,26 @@
'use strict';
const Action = require('./Action');
class MessageUpdateAction extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (channel) {
if (!channel.isTextBased()) return {};
const { id, channel_id, guild_id, author, timestamp, type } = data;
const message = this.getMessage({ id, channel_id, guild_id, author, timestamp, type }, channel);
if (message) {
const old = message._update(data);
return {
old,
updated: message,
};
}
}
return {};
}
}
module.exports = MessageUpdateAction;

View File

@@ -0,0 +1,45 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
const Partials = require('../../util/Partials');
class PresenceUpdateAction extends Action {
handle(data) {
let user = this.client.users.cache.get(data.user.id);
if (!user && ('username' in data.user || this.client.options.partials.includes(Partials.User))) {
user = this.client.users._add(data.user);
}
if (!user) return;
if (data.user.username) {
if (!user._equals(data.user)) this.client.actions.UserUpdate.handle(data.user);
}
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild) return;
const oldPresence = guild.presences.cache.get(user.id)?._clone() ?? null;
let member = guild.members.cache.get(user.id);
if (!member && data.status !== 'offline') {
member = guild.members._add({
user,
deaf: false,
mute: false,
});
this.client.emit(Events.GuildMemberAvailable, member);
}
const newPresence = guild.presences._add(Object.assign(data, { guild }));
if (this.client.listenerCount(Events.PresenceUpdate) && !newPresence.equals(oldPresence)) {
/**
* Emitted whenever a guild member's presence (e.g. status, activity) is changed.
* @event Client#presenceUpdate
* @param {?Presence} oldPresence The presence before the update, if one at all
* @param {Presence} newPresence The presence after the update
*/
this.client.emit(Events.PresenceUpdate, oldPresence, newPresence);
}
}
}
module.exports = PresenceUpdateAction;

View File

@@ -0,0 +1,28 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class StageInstanceCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
if (channel) {
const stageInstance = channel.guild.stageInstances._add(data);
/**
* Emitted whenever a stage instance is created.
* @event Client#stageInstanceCreate
* @param {StageInstance} stageInstance The created stage instance
*/
client.emit(Events.StageInstanceCreate, stageInstance);
return { stageInstance };
}
return {};
}
}
module.exports = StageInstanceCreateAction;

View File

@@ -0,0 +1,31 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class StageInstanceDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
if (channel) {
const stageInstance = channel.guild.stageInstances._add(data);
if (stageInstance) {
channel.guild.stageInstances.cache.delete(stageInstance.id);
/**
* Emitted whenever a stage instance is deleted.
* @event Client#stageInstanceDelete
* @param {StageInstance} stageInstance The deleted stage instance
*/
client.emit(Events.StageInstanceDelete, stageInstance);
return { stageInstance };
}
}
return {};
}
}
module.exports = StageInstanceDeleteAction;

View File

@@ -0,0 +1,30 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class StageInstanceUpdateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
if (channel) {
const oldStageInstance = channel.guild.stageInstances.cache.get(data.id)?._clone() ?? null;
const newStageInstance = channel.guild.stageInstances._add(data);
/**
* Emitted whenever a stage instance gets updated - e.g. change in topic or privacy level
* @event Client#stageInstanceUpdate
* @param {?StageInstance} oldStageInstance The stage instance before the update
* @param {StageInstance} newStageInstance The stage instance after the update
*/
client.emit(Events.StageInstanceUpdate, oldStageInstance, newStageInstance);
return { oldStageInstance, newStageInstance };
}
return {};
}
}
module.exports = StageInstanceUpdateAction;

View File

@@ -0,0 +1,24 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class ThreadCreateAction extends Action {
handle(data) {
const client = this.client;
const existing = client.channels.cache.has(data.id);
const thread = client.channels._add(data);
if (!existing && thread) {
/**
* Emitted whenever a thread is created or when the client user is added to a thread.
* @event Client#threadCreate
* @param {ThreadChannel} thread The thread that was created
* @param {boolean} newlyCreated Whether the thread was newly created
*/
client.emit(Events.ThreadCreate, thread, data.newly_created ?? false);
}
return { thread };
}
}
module.exports = ThreadCreateAction;

View File

@@ -0,0 +1,26 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class ThreadDeleteAction extends Action {
handle(data) {
const client = this.client;
const thread = client.channels.cache.get(data.id);
if (thread) {
client.channels._remove(thread.id);
/**
* Emitted whenever a thread is deleted.
* @event Client#threadDelete
* @param {ThreadChannel} thread The thread that was deleted
*/
client.emit(Events.ThreadDelete, thread);
}
return { thread };
}
}
module.exports = ThreadDeleteAction;

View File

@@ -0,0 +1,60 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Action = require('./Action');
const Events = require('../../util/Events');
class ThreadListSyncAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return {};
if (data.channel_ids) {
for (const id of data.channel_ids) {
const channel = client.channels.cache.get(id);
if (channel) this.removeStale(channel);
}
} else {
for (const channel of guild.channels.cache.values()) {
this.removeStale(channel);
}
}
const syncedThreads = data.threads.reduce((coll, rawThread) => {
const thread = client.channels._add(rawThread);
return coll.set(thread.id, thread);
}, new Collection());
for (const rawMember of Object.values(data.members)) {
// Discord sends the thread id as id in this object
const thread = client.channels.cache.get(rawMember.id);
if (thread) {
thread.members._add(rawMember);
}
}
/**
* Emitted whenever the client user gains access to a text or news channel that contains threads
* @event Client#threadListSync
* @param {Collection<Snowflake, ThreadChannel>} threads The threads that were synced
* @param {Guild} guild The guild that the threads were synced in
*/
client.emit(Events.ThreadListSync, syncedThreads, guild);
return {
syncedThreads,
};
}
removeStale(channel) {
channel.threads?.cache.forEach(thread => {
if (!thread.archived) {
this.client.channels._remove(thread.id);
}
});
}
}
module.exports = ThreadListSyncAction;

View File

@@ -0,0 +1,30 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class ThreadMemberUpdateAction extends Action {
handle(data) {
const client = this.client;
// Discord sends the thread id as id in this object
const thread = client.channels.cache.get(data.id);
if (thread) {
const member = thread.members.cache.get(data.user_id);
if (!member) {
const newMember = thread.members._add(data);
return { newMember };
}
const old = member._update(data);
/**
* Emitted whenever the client user's thread member is updated.
* @event Client#threadMemberUpdate
* @param {ThreadMember} oldMember The member before the update
* @param {ThreadMember} newMember The member after the update
*/
client.emit(Events.ThreadMemberUpdate, old, member);
}
return {};
}
}
module.exports = ThreadMemberUpdateAction;

View File

@@ -0,0 +1,47 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Action = require('./Action');
const Events = require('../../util/Events');
class ThreadMembersUpdateAction extends Action {
handle(data) {
const client = this.client;
const thread = client.channels.cache.get(data.id);
if (thread) {
thread.memberCount = data.member_count;
const addedMembers = new Collection();
const removedMembers = new Collection();
data.added_members?.reduce(
(_addedMembers, addedMember) => _addedMembers.set(addedMember.user_id, thread.members._add(addedMember)),
addedMembers,
);
data.removed_member_ids?.reduce((removedMembersIds, removedMembersId) => {
const threadMember = this.getThreadMember(removedMembersId, thread.members);
if (threadMember) removedMembersIds.set(threadMember.id, threadMember);
thread.members.cache.delete(removedMembersId);
return removedMembersIds;
}, removedMembers);
if (addedMembers.size === 0 && removedMembers.size === 0) {
// Uncached thread member(s) left.
return {};
}
/**
* Emitted whenever members are added or removed from a thread.
* <info>This event requires the {@link GatewayIntentBits.GuildMembers} privileged gateway intent.</info>
* @event Client#threadMembersUpdate
* @param {Collection<Snowflake, ThreadMember>} addedMembers The members that were added
* @param {Collection<Snowflake, ThreadMember>} removedMembers The members that were removed
* @param {ThreadChannel} thread The thread where members got updated
*/
client.emit(Events.ThreadMembersUpdate, addedMembers, removedMembers, thread);
}
return {};
}
}
module.exports = ThreadMembersUpdateAction;

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action');
const Typing = require('../../structures/Typing');
const Events = require('../../util/Events');
class TypingStart extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel) return;
if (!channel.isTextBased()) {
this.client.emit(Events.Warn, `Discord sent a typing packet to a ${channel.type} channel ${channel.id}`);
return;
}
const user = this.getUserFromMember(data);
if (user) {
/**
* Emitted whenever a user starts typing in a channel.
* @event Client#typingStart
* @param {Typing} typing The typing state
*/
this.client.emit(Events.TypingStart, new Typing(channel, user, data));
}
}
}
module.exports = TypingStart;

View File

@@ -0,0 +1,36 @@
'use strict';
const Action = require('./Action');
const Events = require('../../util/Events');
class UserUpdateAction extends Action {
handle(data) {
const client = this.client;
const newUser = data.id === client.user.id ? client.user : client.users.cache.get(data.id);
const oldUser = newUser._update(data);
if (!oldUser.equals(newUser)) {
/**
* Emitted whenever a user's details (e.g. username) are changed.
* Triggered by the Discord gateway events {@link Events.UserUpdate},
* {@link Events.GuildMemberUpdate}, and {@link Events.PresenceUpdate}.
* @event Client#userUpdate
* @param {User} oldUser The user before the update
* @param {User} newUser The user after the update
*/
client.emit(Events.UserUpdate, oldUser, newUser);
return {
old: oldUser,
updated: newUser,
};
}
return {
old: null,
updated: null,
};
}
}
module.exports = UserUpdateAction;

View File

@@ -0,0 +1,43 @@
'use strict';
const Action = require('./Action');
const VoiceState = require('../../structures/VoiceState');
const Events = require('../../util/Events');
class VoiceStateUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
// Update the state
const oldState =
guild.voiceStates.cache.get(data.user_id)?._clone() ?? new VoiceState(guild, { user_id: data.user_id });
const newState = guild.voiceStates._add(data);
// Get the member
let member = guild.members.cache.get(data.user_id);
if (member && data.member) {
member._patch(data.member);
} else if (data.member?.user && data.member.joined_at) {
member = guild.members._add(data.member);
}
// Emit event
if (member?.user.id === client.user.id) {
client.emit('debug', `[VOICE] received voice state update: ${JSON.stringify(data)}`);
client.voice.onVoiceStateUpdate(data);
}
/**
* Emitted whenever a member changes voice state - e.g. joins/leaves a channel, mutes/unmutes.
* @event Client#voiceStateUpdate
* @param {VoiceState} oldState The voice state before the update
* @param {VoiceState} newState The voice state after the update
*/
client.emit(Events.VoiceStateUpdate, oldState, newState);
}
}
}
module.exports = VoiceStateUpdate;

View File

@@ -0,0 +1,37 @@
'use strict';
const process = require('node:process');
const Action = require('./Action');
let deprecationEmitted = false;
class WebhooksUpdate extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
if (!channel) return;
// TODO: change to Events.WebhooksUpdate in the next major version
/**
* Emitted whenever a channel has its webhooks changed.
* @event Client#webhooksUpdate
* @param {TextChannel|NewsChannel|VoiceChannel|StageChannel|ForumChannel|MediaChannel} channel
* The channel that had a webhook update
*/
client.emit('webhooksUpdate', channel);
/**
* Emitted whenever a channel has its webhooks changed.
* @event Client#webhookUpdate
* @param {TextChannel|NewsChannel|VoiceChannel|StageChannel|ForumChannel|MediaChannel} channel
* The channel that had a webhook update
* @deprecated Use {@link Client#event:webhooksUpdate} instead.
*/
if (client.emit('webhookUpdate', channel) && !deprecationEmitted) {
deprecationEmitted = true;
process.emitWarning('The webhookUpdate event is deprecated. Use webhooksUpdate instead.', 'DeprecationWarning');
}
}
}
module.exports = WebhooksUpdate;