From ef56d82a27b21e2022b4c218b296e497b28de94b Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Mon, 31 Aug 2020 14:26:29 +0300 Subject: [PATCH 01/48] fix(Nullable): added null type to it --- src/types/types.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/types/types.ts b/src/types/types.ts index 48d4de374..1fd290e00 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -9,9 +9,11 @@ export type ShardId = number; /** * Turns all fields of a given type to be nullable */ -export type Nullable = { - [P in keyof T]: T[P] | null; -}; +export type Nullable = + | { + [P in keyof T]: T[P] | null; + } + | null; /** * Width and height dimensions. Mostly used for images and/or videos From 7f2d6213451229d454921bb36685fc649ea661ed Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 00:05:57 +0300 Subject: [PATCH 02/48] chore(Vscode): custom workspace rules To strict eslint rules, I've added vscode rules too --- .vscode/settings.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0e0a2dc96 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.tabCompletion": "onlySnippets", + "editor.tabSize": 2, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "files.eol": "\n", + "typescript.format.semicolons": "insert", + "javascript.format.semicolons": "insert" +} \ No newline at end of file From 2dddbd1d521d99054d823f711533fb29795b6e9b Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 00:34:27 +0300 Subject: [PATCH 03/48] feat(VoiceSupport): add partial voice support Client can only receive sounds but not send right now --- package-lock.json | 33 ++++- package.json | 11 +- src/bot/Bot.ts | 10 +- src/bot/BotConnection.ts | 8 +- src/bot/handlers/events/events.ts | 24 ++++ src/bot/index.ts | 1 - src/socket/BotSocket.ts | 18 ++- src/socket/BotSocketShard.ts | 14 +- src/socket/constants.ts | 4 +- src/socket/handlers/guildCreate.ts | 2 +- src/socket/handlers/guildMemberUpdate.ts | 4 +- src/socket/handlers/index.ts | 2 + src/socket/handlers/voiceServerUpdate.ts | 6 + src/socket/handlers/voiceStateUpdate.ts | 12 ++ src/structures/channels/GuildVoiceChannel.ts | 17 +++ src/structures/channels/index.ts | 2 +- src/structures/guild/Guild.ts | 43 +++++- src/structures/guild/GuildUnavailable.ts | 9 +- src/structures/member/Member.ts | 22 ++- src/structures/voice/Connection.ts | 52 +++++++ src/structures/voice/GuildVoice.ts | 57 ++++++++ src/structures/voice/UDPSocket.ts | 137 +++++++++++++++++++ src/structures/voice/VoiceHeartbeats.ts | 76 ++++++++++ src/structures/voice/VoiceState.ts | 63 +++++++++ src/structures/voice/VoiceWebSocket.ts | 118 ++++++++++++++++ src/structures/voice/index.ts | 6 + 26 files changed, 705 insertions(+), 46 deletions(-) create mode 100644 src/socket/handlers/voiceServerUpdate.ts create mode 100644 src/socket/handlers/voiceStateUpdate.ts create mode 100644 src/structures/channels/GuildVoiceChannel.ts create mode 100644 src/structures/voice/Connection.ts create mode 100644 src/structures/voice/GuildVoice.ts create mode 100644 src/structures/voice/UDPSocket.ts create mode 100644 src/structures/voice/VoiceHeartbeats.ts create mode 100644 src/structures/voice/VoiceState.ts create mode 100644 src/structures/voice/VoiceWebSocket.ts create mode 100644 src/structures/voice/index.ts diff --git a/package-lock.json b/package-lock.json index 9768e2125..4001c692e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1847,8 +1847,7 @@ "@types/node": { "version": "14.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz", - "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==", - "dev": true + "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==" }, "@types/node-fetch": { "version": "2.5.7", @@ -1891,6 +1890,14 @@ "integrity": "sha512-IkVfat549ggtkZUthUzEX49562eGikhSYeVGX97SkMFn+sTZrgRewXjQ4tPKFPCykZHkX1Zfd9OoELGqKU2jJA==", "dev": true }, + "@types/sodium-native": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/sodium-native/-/sodium-native-2.3.5.tgz", + "integrity": "sha512-a3DAIpW8+36XAY8aIR36JBQQsfOabxHuJwx11DL/PTvnbwEWPAXW66b8QbMi0r2vUnkOfREsketxdvjBmQxqDQ==", + "requires": { + "@types/node": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -4688,8 +4695,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "interpret": { "version": "1.4.0", @@ -7964,6 +7970,11 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, + "node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8169,6 +8180,11 @@ "word-wrap": "^1.2.3" } }, + "opusscript": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.7.tgz", + "integrity": "sha512-DcBadTdYTUuH9zQtepsLjQn4Ll6rs3dmeFvN+SD0ThPnxRBRm/WC1zXWPg+wgAJimB784gdZvUMA57gDP7FdVg==" + }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -9210,6 +9226,15 @@ } } }, + "sodium-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.2.0.tgz", + "integrity": "sha512-8aq/vQSegLwsRch8Sb/Bpf9aAqlNe5dp0+NVhb9UjHv42zDZ0D5zX3wBRUbXK9Ejum9uZE6DUgT4vVLlUFRBWg==", + "requires": { + "ini": "^1.3.5", + "node-gyp-build": "^4.2.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index f73ce2e63..b9129f801 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,9 @@ }, "peerDependencies": { "erlpack": "^0.1.3", - "zlib-sync": "^0.1.7" + "zlib-sync": "^0.1.7", + "opusscript": "0.0.7", + "sodium-native": "^3.2.0" }, "peerDependenciesMeta": { "erlpack": { @@ -56,9 +58,16 @@ }, "zlib-sync": { "optional": true + }, + "opusscript": { + "optional": true + }, + "sodium-native": { + "optional": true } }, "devDependencies": { + "@types/sodium-native": "^2.3.5", "@commitlint/cli": "^9.1.2", "@commitlint/config-conventional": "^9.1.2", "@types/jest": "^26.0.10", diff --git a/src/bot/Bot.ts b/src/bot/Bot.ts index 942fcdf35..c6fb6ad28 100644 --- a/src/bot/Bot.ts +++ b/src/bot/Bot.ts @@ -18,7 +18,7 @@ export interface ShardOptions { /** * Shard ID */ - id?: ShardId; + id: ShardId; /** * Number of shards this instance of the bot uses @@ -151,12 +151,12 @@ export class Bot { this.api = new BotAPI(this, this.token); // Sets bot sharding data - const shardId = Number(process.env.SHARD_ID); - const shardAmount = Number(process.env.SHARDS_AMOUNT); + const shardId = parseInt(process.env.SHARD_ID as string); + const shardAmount = parseInt(process.env.SHARDS_AMOUNT as string); this.shardOptions = { - id: Number.isNaN(shardId) ? undefined : shardId, - amount: Number.isNaN(shardAmount) ? undefined : shardAmount, + id: shardId, + amount: shardAmount, }; this.commands = new CommandsHandler(); diff --git a/src/bot/BotConnection.ts b/src/bot/BotConnection.ts index 51f62053a..f3c66ff08 100644 --- a/src/bot/BotConnection.ts +++ b/src/bot/BotConnection.ts @@ -1,7 +1,9 @@ import { Bot } from './Bot'; +import Collection from '../Collection'; import { ShardCommunicationAction, ShardDisconnectAllRequest } from '../sharding'; -import { BotSocket, GatewayCloseCode } from '../socket'; +import { BotSocket, BotSocketShard, GatewayCloseCode } from '../socket'; import { GatewayStruct } from '../structures'; +import { ShardId } from '../types'; /** * Responsible for the creation and closure of the WebSocket connection to the Discord API gateway @@ -56,4 +58,8 @@ export class BotConnection { public modifyPresence(presence: GatewayStruct): void { this.socket.modifyPresence(presence); } + + public get shards(): Collection { + return this.socket.shards; + } } diff --git a/src/bot/handlers/events/events.ts b/src/bot/handlers/events/events.ts index 3674adcaf..c46b67f22 100644 --- a/src/bot/handlers/events/events.ts +++ b/src/bot/handlers/events/events.ts @@ -14,6 +14,7 @@ import { Channel, GuildChannel } from '../../../structures/channels'; import { Guild, GuildUnavailable, GuildBan } from '../../../structures/guild'; import { Member, MemberPresence } from '../../../structures/member'; import { Message, PartialMessage, MessageReaction } from '../../../structures/message'; +import VoiceState from '../../../structures/voice/VoiceState'; import { Snowflake } from '../../../types'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -351,6 +352,27 @@ declare function USER_UPDATE(before: User, after: User): any; */ declare function WEBHOOKS_UPDATE(channel: GuildChannel): any; +/** + * Sent whenever a new voice server update comes + * @param {Guild} guild The guild that happens in it + * @param {{ token: string; endpoint: string }} voiceServerStats The new voice server information + * @asMemberOf EventsHandler + * @event BotEventsHandler#VOICE_SERVER_UPDATE + */ +declare function VOICE_SERVER_UPDATE( + guild: Guild, + voiceServer: { token: string; endpoint: string }, +): any; + +/** + * Sent whenever a voice state changes + * @param {VoiceState} old The old one + * @param {VoiceState} new The new one + * @asMemberOf EventHandler + * @event BotEventsHandler#VOICE_STATE_UPDATE + */ +declare function VOICE_STATE_UPDATE(oldState: VoiceState, newState: VoiceState): any; + /** * Events that are called when all Bot shards change their state. * These events take no arguments @@ -399,4 +421,6 @@ export interface Events { [BotEvent.TypingStart]: typeof TYPING_START; [BotEvent.UserUpdate]: typeof USER_UPDATE; [BotEvent.WebhooksUpdate]: typeof WEBHOOKS_UPDATE; + [BotEvent.VoiceServerUpdate]: typeof VOICE_SERVER_UPDATE; + [BotEvent.VoiceStateUpdate]: typeof VOICE_STATE_UPDATE; } diff --git a/src/bot/index.ts b/src/bot/index.ts index 579f344c6..94836eec7 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -1,4 +1,3 @@ export * from './handlers'; - export * from './Bot'; export * from './BotConnection'; diff --git a/src/socket/BotSocket.ts b/src/socket/BotSocket.ts index d8fda46e3..c7d06ab90 100644 --- a/src/socket/BotSocket.ts +++ b/src/socket/BotSocket.ts @@ -26,7 +26,7 @@ interface GatewayBot { */ export class BotSocket { private readonly token: string; - private readonly shards: Collection; + public readonly shards: Collection; public readonly bot: Bot; public gatewayURL!: string; public sessionStartLimit!: SessionStartLimit; @@ -135,14 +135,26 @@ export class BotSocket { /** * Modifies the presence of the bot * @param {GatewayStruct} presence The new presence for the bot + * @param {number} [shardId] The shard id thats gonna be affected * @returns {void} */ - public modifyPresence(presence: GatewayStruct): void { - for (const [, shard] of this.shards) { + public modifyPresence(presence: GatewayStruct, shardId?: number): void { + if (shardId) { + const shard = this.shards.get(shardId); + + if (!shard) return; + shard.send({ op: OPCode.PresenceUpdate, d: presence, } as Payload); + } else { + for (const [, shard] of this.shards) { + shard.send({ + op: OPCode.PresenceUpdate, + d: presence, + } as Payload); + } } } diff --git a/src/socket/BotSocketShard.ts b/src/socket/BotSocketShard.ts index 941d86ae9..4efd1de80 100644 --- a/src/socket/BotSocketShard.ts +++ b/src/socket/BotSocketShard.ts @@ -35,11 +35,17 @@ export type PayloadData = any; export type OptionalLibrary = any; /** - * A payload received from the Discord API gateway + * A payload thats gonna be sent to the Discord API */ -export interface Payload { +export interface GatewayCommand { op: OPCode; d: PayloadData; +} + +/** + * A payload received from the Discord API gateway + */ +export interface Payload extends GatewayCommand { s: number; t: GatewayEvent; } @@ -459,7 +465,7 @@ export class BotSocketShard { * @param {unknown} data The data * @returns {void} */ - public send(data: unknown): void { + public send(data: GatewayCommand): void { return this.ws.send(this.pack(data)); } @@ -483,7 +489,7 @@ export class BotSocketShard { * @param {any} data Data to be transferred and sent to the gateway * @returns {string} */ - public pack(data: unknown): Buffer | string | undefined { + public pack(data: GatewayCommand): Buffer | string | undefined { return this.options.encoding === 'etf' ? erlpack?.pack(data) : JSON.stringify(data); } diff --git a/src/socket/constants.ts b/src/socket/constants.ts index 1db1a68a2..fbfe7ec9b 100644 --- a/src/socket/constants.ts +++ b/src/socket/constants.ts @@ -104,8 +104,8 @@ export enum BotEvent { ShardClose = 'SHARD_CLOSE', TypingStart = 'TYPING_START', UserUpdate = 'USER_UPDATE', - /*VoiceStateUpdate = 'VOICE_STATE_UPDATE', - VoiceServerUpdate = 'VOICE_SERVER_UPDATE',*/ + VoiceStateUpdate = 'VOICE_STATE_UPDATE', + VoiceServerUpdate = 'VOICE_SERVER_UPDATE', WebhooksUpdate = 'WEBHOOKS_UPDATE', } diff --git a/src/socket/handlers/guildCreate.ts b/src/socket/handlers/guildCreate.ts index 009849cbd..b0e060406 100644 --- a/src/socket/handlers/guildCreate.ts +++ b/src/socket/handlers/guildCreate.ts @@ -8,7 +8,7 @@ export default ({ d }: Payload, bot: Bot, socket: BotSocketShard): void => { socket.sessionId = sessionId; - const guild = Guild.create(bot, d); + const guild = Guild.create(bot, d, socket.shard.id); if (guild instanceof Guild) { // Delete the guild from the unavailable guilds collection if exists diff --git a/src/socket/handlers/guildMemberUpdate.ts b/src/socket/handlers/guildMemberUpdate.ts index 9aeb8bc0d..9aa956463 100644 --- a/src/socket/handlers/guildMemberUpdate.ts +++ b/src/socket/handlers/guildMemberUpdate.ts @@ -11,9 +11,7 @@ export default async ({ d }: Payload, bot: Bot): Promise => { const { before, after } = member.update({ nick: member.nick, - joined_at: member.joinedAt.date, - deaf: member.deaf, - mute: member.mute, + joined_at: member.joinedAt.date, // Removed deaf and muted from since they belong to the voice state ...d, }); diff --git a/src/socket/handlers/index.ts b/src/socket/handlers/index.ts index 815a9d219..d4842fe06 100644 --- a/src/socket/handlers/index.ts +++ b/src/socket/handlers/index.ts @@ -31,3 +31,5 @@ export { default as PRESENCE_UPDATE } from './presenceUpdate'; export { default as TYPING_START } from './typingStart'; export { default as USER_UPDATE } from './userUpdate'; export { default as WEBHOOKS_UPDATE } from './webhooksUpdate'; +export { default as VOICE_SERVER_UPDATE } from './voiceServerUpdate'; +export { default as VOICE_STATE_UPDATE } from './voiceStateUpdate'; diff --git a/src/socket/handlers/voiceServerUpdate.ts b/src/socket/handlers/voiceServerUpdate.ts new file mode 100644 index 000000000..8cb35ec45 --- /dev/null +++ b/src/socket/handlers/voiceServerUpdate.ts @@ -0,0 +1,6 @@ +import { Payload, BotEvent } from '..'; +import { Bot } from '../..'; + +export default async ({ d }: Payload, bot: Bot): Promise => { + bot.events.emit(BotEvent.VoiceServerUpdate, bot.guilds.cache.get(d.guild_id)!, d); +}; diff --git a/src/socket/handlers/voiceStateUpdate.ts b/src/socket/handlers/voiceStateUpdate.ts new file mode 100644 index 000000000..89078420f --- /dev/null +++ b/src/socket/handlers/voiceStateUpdate.ts @@ -0,0 +1,12 @@ +import { Payload, BotEvent } from '..'; +import { Bot } from '../..'; +import VoiceState from '../../structures/voice/VoiceState'; + +export default async ({ d }: Payload, bot: Bot): Promise => { + const oldVoiceState = bot.guilds.cache.get(d.guild_id)!.voiceStates.get(d.user_id)!; + const newVoiceState = new VoiceState(bot, oldVoiceState.member, d); + + bot.events.emit(BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); + + bot.guilds.cache.get(d.guild_id)!.voiceStates.set(d.user_id, newVoiceState); +}; diff --git a/src/structures/channels/GuildVoiceChannel.ts b/src/structures/channels/GuildVoiceChannel.ts new file mode 100644 index 000000000..162ab2885 --- /dev/null +++ b/src/structures/channels/GuildVoiceChannel.ts @@ -0,0 +1,17 @@ +import { Guild, GuildChannel } from '..'; +import { Bot } from '../../bot'; +import { GatewayStruct } from '../base'; +import Connection from '../voice/Connection'; + +/** + * Represents a Voice channel + */ +export default class GuildVoiceChannel extends GuildChannel { + constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild) { + super(bot, guildChannel, guild); + } + + join(mute?: boolean, deaf?: boolean): Promise { + return this.guild.voice.join(this.id, { mute, deaf }); + } +} diff --git a/src/structures/channels/index.ts b/src/structures/channels/index.ts index 0720e6229..b64eb6a85 100644 --- a/src/structures/channels/index.ts +++ b/src/structures/channels/index.ts @@ -4,5 +4,5 @@ export * from './GuildCategoryChannel'; export * from './GuildChannel'; export * from './GuildTextChannel'; export * from './TextChannel'; - +export * from './GuildVoiceChannel'; export * from './utils'; diff --git a/src/structures/guild/Guild.ts b/src/structures/guild/Guild.ts index 6c6f0bf34..4c272cb98 100644 --- a/src/structures/guild/Guild.ts +++ b/src/structures/guild/Guild.ts @@ -16,7 +16,8 @@ import { GuildRolesController, } from '../../controllers/guild'; import { GuildWebhooksController } from '../../controllers/guild/GuildWebhooksController'; -import { Snowflake } from '../../types'; +import { BotSocketShard } from '../../socket'; +import { ShardId, Snowflake } from '../../types'; import { Avatar, GuildBannerFormat } from '../Avatar'; import { ImageURI } from '../ImageURI'; import { Role } from '../Role'; @@ -25,6 +26,8 @@ import { GuildChannel, GuildTextChannel } from '../channels'; import { ChannelUtils } from '../channels/utils'; import { GuildSystemChannelFlags, PermissionFlags } from '../flags'; import { Member, MemberPresence } from '../member'; +import GuildVoice from '../voice/GuildVoice'; +import VoiceState from '../voice/VoiceState'; /** * Guild verification levels @@ -360,8 +363,7 @@ export class Guild extends GuildPreview { */ public memberCount: number | undefined; - // TODO: implement this along with voice support - // public voiceStates: TEMP | undefined; + public voiceStates!: Collection; /** * Presences of the members in the guild. @@ -430,8 +432,16 @@ export class Guild extends GuildPreview { */ public webhooks!: GuildWebhooksController; - constructor(bot: Bot, guild: GatewayStruct) { + /** + * The id of the shard which belongs to this guild + */ + public shardId?: ShardId; + + public voice!: GuildVoice; + + constructor(bot: Bot, guild: GatewayStruct, shardId?: ShardId) { super(bot, guild); + this.shardId = shardId; } /** @@ -460,6 +470,10 @@ export class Guild extends GuildPreview { this.webhooks = new GuildWebhooksController(this); + this.voice = new GuildVoice(this); + + this.voiceStates = new Collection(); + if (guild.channels) { this.channels.cache.addMany( guild.channels.map((channel: GatewayStruct) => @@ -491,6 +505,15 @@ export class Guild extends GuildPreview { ); } + if (guild.voice_states) { + for (const voicestate of guild.voice_states) { + this.voiceStates.set( + voicestate.user_id, + new VoiceState(this.bot, this.members.cache.get(voicestate.user_id)!, voicestate), + ); + } + } + this.owner = this.members.cache.get(guild.owner_id); this.ownerId = guild.owner_id; @@ -631,15 +654,23 @@ export class Guild extends GuildPreview { return this.bot.api.leaveGuild(this.id); } + // Returns undefined if the guilds fetched via REST and not received by gateway + public get shard(): BotSocketShard { + return this.bot.connection.shards.get(this.shardId!)!; + } + /** * Creates a {@link Guild} or {@link GuildUnavailable} * @param {Bot} bot The bot instance * @param {GatewayStruct} guild The guild data + * @param {number} [shardId] The shard id that belongs to that guild * @returns {Guild | GuildUnavailable} * @ignore */ - public static create(bot: Bot, guild: GatewayStruct): Guild | GuildUnavailable { - return guild.unavailable ? new GuildUnavailable(bot, guild) : new Guild(bot, guild); + public static create(bot: Bot, guild: GatewayStruct, shardId: number): Guild | GuildUnavailable { + return guild.unavailable + ? new GuildUnavailable(bot, guild, shardId) + : new Guild(bot, guild, shardId); } /** diff --git a/src/structures/guild/GuildUnavailable.ts b/src/structures/guild/GuildUnavailable.ts index 0b19b44de..aefc83048 100644 --- a/src/structures/guild/GuildUnavailable.ts +++ b/src/structures/guild/GuildUnavailable.ts @@ -19,9 +19,16 @@ export class GuildUnavailable extends BaseStruct { */ public unavailable: boolean | undefined; - constructor(bot: Bot, guild: GatewayStruct) { + /** + * The id of the shard which belongs to this guild + */ + public shardId: number | undefined; + + constructor(bot: Bot, guild: GatewayStruct, shardId?: number) { super(bot, guild); + this.shardId = shardId; + this.init(guild); } diff --git a/src/structures/member/Member.ts b/src/structures/member/Member.ts index 656cd75cf..85decfc5f 100644 --- a/src/structures/member/Member.ts +++ b/src/structures/member/Member.ts @@ -7,6 +7,7 @@ import { Timestamp } from '../Timestamp'; import { User } from '../User'; import { GatewayStruct, BaseGuildStruct } from '../base'; import { Guild } from '../guild'; +import VoiceState from '../voice/VoiceState'; /** * Options used when modifying a guild member @@ -94,16 +95,6 @@ export class Member extends BaseGuildStruct { */ public boostingSince!: Timestamp | null; - /** - * Whether the member is deafened in voice channels - */ - public deaf!: boolean; - - /** - * Whether the member is muted in voice channels - */ - public mute!: boolean; - /** * The member's user presence data */ @@ -153,9 +144,6 @@ export class Member extends BaseGuildStruct { this.boostingSince = member.premium_since ? new Timestamp(member.premium_since) : null; - this.deaf = member.deaf; - this.mute = member.mute; - return this; } @@ -202,4 +190,12 @@ export class Member extends BaseGuildStruct { public toString(): string | undefined { return this.user?.toString(); } + + public get voice(): VoiceState { + return this.guild.voiceStates.get(this.id)!; + } + + public set voice(val: VoiceState) { + this.guild.voiceStates.set(this.id, val); + } } diff --git a/src/structures/voice/Connection.ts b/src/structures/voice/Connection.ts new file mode 100644 index 000000000..f9d211fd0 --- /dev/null +++ b/src/structures/voice/Connection.ts @@ -0,0 +1,52 @@ +import GuildVoice from './GuildVoice'; +import UDPSocket from './UDPSocket'; +import VoiceWebSocket from './VoiceWebSocket'; + +export default class Connection { + public sockets!: { + ws: VoiceWebSocket; + udp: UDPSocket; + }; + + public token!: string; + + public active = false; + + private _endpoint = ''; + + /** + * The endpoint voice websocket is going to connect + * @type {string} + */ + + public set endpoint(val: string) { + if (this._endpoint === val) return; + else { + if (this.sockets.ws) { + this.sockets.ws.close(); + this.sockets.ws.removeAllListeners(); + } + if (this.sockets.udp) { + this.sockets.udp.close(); + this.sockets.udp.removeAllListeners(); + } + + this.active = true; + + this.sockets = { + ws: new VoiceWebSocket(this), + udp: new UDPSocket(this), + }; + } + } + + public get endpoint(): string { + return this._endpoint; + } + + public voice: GuildVoice; + + constructor(voice: GuildVoice) { + this.voice = voice; + } +} diff --git a/src/structures/voice/GuildVoice.ts b/src/structures/voice/GuildVoice.ts new file mode 100644 index 000000000..46b9205ac --- /dev/null +++ b/src/structures/voice/GuildVoice.ts @@ -0,0 +1,57 @@ +import Connection from './Connection'; +import { Guild } from '..'; +import { Bot } from '../../bot'; +import { BotEvent } from '../../socket'; +import { Snowflake } from '../../types'; + +/** + * Represents a voice connection of a guild + */ +export default class GuildVoice { + /** + * The {@link Bot} operating this structure + */ + public bot: Bot; + + /** + * The {@link Guild} that this voice connection belongs + * @param guild Guild + */ + public guild: Guild; + + public connection = new Connection(this); + + constructor(guild: Guild) { + this.guild = guild; + + this.bot = guild.bot; + } + + join(channelId: Snowflake, options: { mute?: boolean; deaf?: boolean }): Promise { + this.guild.shard.send({ + op: 2, + d: { + guild_id: this.guild.id, + channel_id: channelId, + self_mute: !!options.mute, + self_deaf: !!options.deaf, + }, + }); + + return new Promise(resolve => { + const listener = ((guild: Guild, voiceServer: { token: string; endpoint: string }) => { + if (guild.id === this.guild.id) { + this.connection.endpoint = voiceServer.endpoint; + + this.connection.token = voiceServer.token; + + this.bot.events.removeListener(BotEvent.VoiceServerUpdate, listener); + + resolve(this.connection); + } + }).bind(this); + + this.bot.events.on(BotEvent.VoiceServerUpdate, listener); + }); + } +} diff --git a/src/structures/voice/UDPSocket.ts b/src/structures/voice/UDPSocket.ts new file mode 100644 index 000000000..90448258b --- /dev/null +++ b/src/structures/voice/UDPSocket.ts @@ -0,0 +1,137 @@ +import { createSocket, Socket } from 'dgram'; +import { EventEmitter } from 'events'; +import { Readable } from 'stream'; +import Connection from './Connection'; +import { BotEvent } from '../../socket'; + +let LibSodium: typeof import('sodium-native'); +let Opusscript: typeof import('opusscript'); + +try { + LibSodium = require("sodium-native") //eslint-disable-line +} catch (err) {} //eslint-disable-line + +try { + Opusscript = require("opusscript") //eslint-disable-line +} catch (err) {} //eslint-disable-line + +export default class UDPSocket extends EventEmitter { + public connection: Connection; + + public socket: Socket; + + private auth?: { ip: string; port: number; ssrc: number }; + + private nonce = Buffer.alloc(24); + + public secretKeys!: Buffer; + + /** + * PCM Raw + */ + public PCMOut = new Readable(); + + /** + * Opus Encoded + */ + public OpusOut = new Readable(); + + private OpusEncoder = new Opusscript( + Opusscript.VALID_SAMPLING_RATES[4], + 2, + Opusscript.Application.AUDIO, + ); + + constructor(connection: Connection) { + super(); + this.connection = connection; + + this.OpusEncoder.encoderCTL(4002, 64000); + + this.socket = createSocket('udp4'); + } + + // This method should be called before the udp connection started! + async discoverIP(server: { + ip: string; + port: number; + ssrc: number; + }): Promise<{ ip: string; port: number }> { + this.auth = server; + + return new Promise(resolve => { + const message = Buffer.alloc(70); + + message.writeUIntBE(this.auth!.ssrc, 0, 4); + + this.socket.send(message, 0, message.length, this.auth!.port, this.auth!.ip); + + this.socket.once('message', message => { + const local = { ip: '', port: 0 }; + + for (let i = 4; i < message.indexOf(0, i); i++) { + local.ip += String.fromCharCode(message[i]); + } + + local.port = parseInt(message.readUIntBE(message.length - 2, 2).toString(10)); + + resolve(local); + }); + }); + } + + start(): void { + this.socket.on('message', message => { + const data = this.decryptPackage(message); + + if (data instanceof Error) return this.connection.voice.bot.events.emit(BotEvent.Debug, data); + + this.OpusOut.push(data); + + this.PCMOut.push(this.OpusEncoder.decode(data)); + }); + } + + stop(): void { + this.socket.removeAllListeners(); + } + + decryptPackage(buffer: Buffer): Buffer | Error { + buffer.copy(this.nonce, 0, 0, 12); + + const nonce = Buffer.alloc(24); + buffer.copy(nonce, 0, 0, 12); + let data; + data = Buffer.allocUnsafe(buffer.length - 12 - LibSodium.crypto_secretbox_MACBYTES); + + try { + LibSodium.crypto_secretbox_open_easy( + data, + buffer.slice(12), + this.nonce, + Buffer.from(this.secretKeys), + ); + } catch (err) { + return err; + } + + if ((buffer[0] & 0b1111) > 0) { + data = data.slice(buffer[0] & (0b1111 * 4)); + } + + if (buffer[0] & 0b10000) { + const l = (data[2] << 8) | data[3]; + let index = 4 + l * 4; + while (data[index] == 0) { + ++index; + } + data = data.slice(index); + } + + return data; + } + + close(): void { + this.socket.close(); + } +} diff --git a/src/structures/voice/VoiceHeartbeats.ts b/src/structures/voice/VoiceHeartbeats.ts new file mode 100644 index 000000000..ff3a83729 --- /dev/null +++ b/src/structures/voice/VoiceHeartbeats.ts @@ -0,0 +1,76 @@ +import VoiceWebSocket from './VoiceWebSocket'; + +interface HeartbeatData { + op: 3; + d: number; +} + +interface HeartbeatInterval { + timeout: number; + executor?: NodeJS.Timeout; +} + +/** + * Handles the sending and receiving of Discord heartbeats + */ +export default class VoiceHeartbeats { + private readonly ws: VoiceWebSocket; + private readonly sequence: number | null; + private acked: boolean; + public interval: HeartbeatInterval; + + constructor(ws: VoiceWebSocket) { + this.ws = ws; + + this.sequence = ws.sequnce; + + this.acked = true; + + this.interval = { + timeout: 0, + }; + } + + /** + * Starts the heartbeat interval + */ + public start(): void { + this.interval.executor = setInterval(this.sendHeartbeat.bind(this), this.interval.timeout); + } + + /** + * Sends a heartbeat and checks if the last one acked + */ + public sendHeartbeat(): void { + if (!this.acked) return; // Instead of reconnecting to the voice ws it just skips this heartbeat. + + this.acked = false; + this.ws.send(this.heartbeatData); + } + + /** + * Resets the interval timeout and stops the interval + */ + public stopHeartbeat(): void { + this.interval.timeout = -1; + + if (this.interval.executor) { + clearInterval(this.interval.executor); + } + } + + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + private get heartbeatData(): HeartbeatData { + return { op: 3, d: this.interval.timeout }; + } + + /** + * Called when a heartbeat is acked + */ + public receivedAck(): void { + this.acked = true; + } +} diff --git a/src/structures/voice/VoiceState.ts b/src/structures/voice/VoiceState.ts new file mode 100644 index 000000000..0b889b6cf --- /dev/null +++ b/src/structures/voice/VoiceState.ts @@ -0,0 +1,63 @@ +import { Bot } from '../../bot'; +import { Nullable, Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +import GuildVoiceChannel from '../channels/GuildVoiceChannel'; +import { Member } from '../member'; + +interface VoiceStateData { + guild_id: Snowflake; + channel_id: Nullable; + user_id: Snowflake; + session_id: string; + deaf: boolean; + mute: boolean; + self_deaf: boolean; + self_mute: boolean; + self_stream: boolean; + self_video: boolean; + suppress: boolean; +} + +enum MUTE_STATE { + SELF, + FORCE, + NONE, +} + +export default class VoiceState extends BaseStruct { + private channelId!: Nullable; + public sessionId!: string; + public deafen!: MUTE_STATE; + public muted!: MUTE_STATE; + public member: Member; + + static MUTE_STATE = MUTE_STATE; + + constructor(bot: Bot, member: Member, voiceState: GatewayStruct) { + super(bot, voiceState); + + this.member = member; + + this.init(voiceState as VoiceStateData); + } + + public init(voiceState: VoiceStateData): this { + if (voiceState.self_mute) this.muted = MUTE_STATE.SELF; + else if (voiceState.mute) this.muted = MUTE_STATE.FORCE; + else this.muted = MUTE_STATE.NONE; + + if (voiceState.self_deaf) this.deafen = MUTE_STATE.SELF; + else if (voiceState.deaf) this.deafen = MUTE_STATE.FORCE; + else this.deafen = MUTE_STATE.NONE; + + this.channelId = voiceState.channel_id; + + return this; + } + + get currentChannel(): Nullable { + const channel = this.bot.channels.cache.get(this.channelId!) as GuildVoiceChannel | undefined; + + return channel ? channel : null; + } +} diff --git a/src/structures/voice/VoiceWebSocket.ts b/src/structures/voice/VoiceWebSocket.ts new file mode 100644 index 000000000..945d5b896 --- /dev/null +++ b/src/structures/voice/VoiceWebSocket.ts @@ -0,0 +1,118 @@ +import { EventEmitter } from 'events'; +import WebSocket from 'ws'; +import Connection from './Connection'; +import VoiceHeartbeats from './VoiceHeartbeats'; +import { PayloadData } from '../../socket'; + +export enum VOICE_OPCODES { + IDENTIFY, + SELECT_PROTOCOL, + READY, + HEARTBEAT, + SESSION_DESCRIPTION, + SPEAKING, + HEARTBEAT_ACK, + RESUME, + HELLO, + RESUMED, + DISCONNECT = 13, +} + +/** + * A payload thats gonna be sent to the Discord Voice Server + */ +export interface VoiceCommand { + op: VOICE_OPCODES; + d: PayloadData; +} + +/** + * A payload received from the Discord Voice Server gateway + */ +export interface VoicePayload extends VoiceCommand { + s: number; +} + +export default class VoiceWebSocket extends EventEmitter { + public connection: Connection; + + private ws?: WebSocket; + private hearbeat?: VoiceHeartbeats; + + public sequnce!: number; + + constructor(connection: Connection) { + super(); + + this.connection = connection; + } + + public open(): void { + if (!this.connection.endpoint) + throw new Error( + 'A voice gateway connection was tried to be established before endpoint declaration.', + ); + + this.ws = new WebSocket(`wss://${this.connection.endpoint.split(':')[0]}/?v=4`); + + this.ws.on('message', this.onMessage.bind(this)); + + this.hearbeat = new VoiceHeartbeats(this); + + this.send({ + op: VOICE_OPCODES.IDENTIFY, + d: { + server_id: this.connection.voice.guild.id, + user_id: this.connection.voice.bot.user!.id, + session_id: this.connection.voice.guild.shard.sessionId, + token: this.connection.token, + }, + }); + } + + private async onMessage(message: VoicePayload) { + this.sequnce = message.s; + switch (message.op) { + case VOICE_OPCODES.HELLO: { + this.hearbeat!.interval.timeout = message.d.heartbeat_interval; + this.hearbeat!.start(); + break; + } + + case VOICE_OPCODES.READY: { + const ip = await this.connection.sockets.udp.discoverIP({ + ip: message.d.ip, + port: message.d.port, + ssrc: message.d.ssrc, + }); + + this.send({ + op: VOICE_OPCODES.SELECT_PROTOCOL, + d: { + protocol: 'udp', + data: { + address: ip.ip, + port: ip.port, + mode: 'xsalsa20_poly1305', + }, + }, + }); + break; + } + + case VOICE_OPCODES.SESSION_DESCRIPTION: { + this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys as number[]); + break; + } + } + } + + send(data: VoiceCommand): void { + if (this.ws) this.ws.send(JSON.stringify(data)); + } + + close(): void { + this.ws?.close(); + this.hearbeat?.stopHeartbeat(); + } +} diff --git a/src/structures/voice/index.ts b/src/structures/voice/index.ts new file mode 100644 index 000000000..3babae3d0 --- /dev/null +++ b/src/structures/voice/index.ts @@ -0,0 +1,6 @@ +export * from './Connection'; +export * from './GuildVoice'; +export * from './UDPSocket'; +export * from './VoiceHeartbeats'; +export * from './VoiceState'; +export * from './VoiceWebSocket'; From f5481afad8121c57ad79985f1e9ef147be69dd5c Mon Sep 17 00:00:00 2001 From: Sardonyx Date: Tue, 1 Sep 2020 00:41:37 +0300 Subject: [PATCH 04/48] revert(Vscode) remove workspace settings I just realised .idea is ignored by .gitignore, if its ignored this should be too --- .vscode/settings.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0e0a2dc96..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "editor.tabCompletion": "onlySnippets", - "editor.tabSize": 2, - "editor.detectIndentation": false, - "editor.insertSpaces": true, - "files.eol": "\n", - "typescript.format.semicolons": "insert", - "javascript.format.semicolons": "insert" -} \ No newline at end of file From 22ce4021616ceec414ca21826e70cdfdb640f3b8 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 02:01:54 +0300 Subject: [PATCH 05/48] build(Npm): switched to discord/erlpack Other one is outdated --- package-lock.json | 52 ++++++++++++++++++++++++++++------------------- package.json | 4 ++-- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4001c692e..10cb55226 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1847,7 +1847,8 @@ "@types/node": { "version": "14.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz", - "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==" + "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==", + "dev": true }, "@types/node-fetch": { "version": "2.5.7", @@ -1894,6 +1895,7 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@types/sodium-native/-/sodium-native-2.3.5.tgz", "integrity": "sha512-a3DAIpW8+36XAY8aIR36JBQQsfOabxHuJwx11DL/PTvnbwEWPAXW66b8QbMi0r2vUnkOfREsketxdvjBmQxqDQ==", + "dev": true, "requires": { "@types/node": "*" } @@ -2449,6 +2451,14 @@ "tweetnacl": "^0.14.3" } }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3194,6 +3204,14 @@ "ansi-colors": "^4.1.1" } }, + "erlpack": { + "version": "git+https://github.com/discord/erlpack.git#e27db8f82892bdb9b28a0547cc394d68b5d2242d", + "from": "git+https://github.com/discord/erlpack.git", + "requires": { + "bindings": "^1.5.0", + "nan": "^2.14.0" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4066,6 +4084,11 @@ "flat-cache": "^2.0.1" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4695,7 +4718,8 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true }, "interpret": { "version": "1.4.0", @@ -7928,6 +7952,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -7970,11 +7999,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, - "node-gyp-build": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", - "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8180,11 +8204,6 @@ "word-wrap": "^1.2.3" } }, - "opusscript": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.7.tgz", - "integrity": "sha512-DcBadTdYTUuH9zQtepsLjQn4Ll6rs3dmeFvN+SD0ThPnxRBRm/WC1zXWPg+wgAJimB784gdZvUMA57gDP7FdVg==" - }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -9226,15 +9245,6 @@ } } }, - "sodium-native": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.2.0.tgz", - "integrity": "sha512-8aq/vQSegLwsRch8Sb/Bpf9aAqlNe5dp0+NVhb9UjHv42zDZ0D5zX3wBRUbXK9Ejum9uZE6DUgT4vVLlUFRBWg==", - "requires": { - "ini": "^1.3.5", - "node-gyp-build": "^4.2.0" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index b9129f801..f4afd8d1c 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "ws": "^7.3.1" }, "peerDependencies": { - "erlpack": "^0.1.3", + "erlpack": "git+https://github.com/discord/erlpack.git", "zlib-sync": "^0.1.7", "opusscript": "0.0.7", "sodium-native": "^3.2.0" @@ -67,13 +67,13 @@ } }, "devDependencies": { - "@types/sodium-native": "^2.3.5", "@commitlint/cli": "^9.1.2", "@commitlint/config-conventional": "^9.1.2", "@types/jest": "^26.0.10", "@types/mime-types": "^2.1.0", "@types/node": "^14.6.0", "@types/node-fetch": "^2.5.7", + "@types/sodium-native": "^2.3.5", "@types/ws": "^7.2.6", "@typescript-eslint/eslint-plugin": "^3.10.0", "@typescript-eslint/parser": "^3.10.0", From fa35d50ef4535d971590855b08b8c345b9e76727 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 02:02:49 +0300 Subject: [PATCH 06/48] docs(Readme): add other optional packages --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d137b6b0b..bdd51acdc 100644 --- a/README.md +++ b/README.md @@ -56,5 +56,7 @@ bot.connect(); # Optional Libraries There are multiple optional libraries which could be installed in addition to node-discord to improve performance. -- **erlpack** - Provides fast encoding and decoding for WebSocket payloads. -- **zlib-sync** - Compresses and decompresses WebSocket payloads before attempting to parse them. +- **[discord/erlpack](https://github.com/discord/erlpack)** - Provides fast encoding and decoding for WebSocket payloads. +- **[zlib-sync](https://www.npmjs.com/package/zlib-sync)** - Compresses and decompresses WebSocket payloads before attempting to parse them. +- **[OpusScript](https://www.npmjs.com/package/opusscript)** - Decodes and encodes opus encoded voice packets. **Needed for voice support** +- **[sodium-native](https://www.npmjs.com/package/sodium-native)** - Decodes incoming voice packets from Discord. **Needed for voice support** \ No newline at end of file From cd74de671ce9da9107ae682702736feab4de035c Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 02:03:56 +0300 Subject: [PATCH 07/48] fix(OptionalDependencies): added modules Otherwise it caused errors --- src/socket/BotSocketShard.ts | 11 +-- src/structures/voice/UDPSocket.ts | 27 ++++-- src/types/optionalDependencies/erlpack.d.ts | 6 ++ .../optionalDependencies/opusscript.d.ts | 94 +++++++++++++++++++ src/types/optionalDependencies/zlib-sync.d.ts | 53 +++++++++++ 5 files changed, 174 insertions(+), 17 deletions(-) create mode 100644 src/types/optionalDependencies/erlpack.d.ts create mode 100644 src/types/optionalDependencies/opusscript.d.ts create mode 100644 src/types/optionalDependencies/zlib-sync.d.ts diff --git a/src/socket/BotSocketShard.ts b/src/socket/BotSocketShard.ts index 4efd1de80..db72168ba 100644 --- a/src/socket/BotSocketShard.ts +++ b/src/socket/BotSocketShard.ts @@ -31,9 +31,6 @@ export const enum BotSocketShardState { // eslint-disable-next-line @typescript-eslint/no-explicit-any export type PayloadData = any; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type OptionalLibrary = any; - /** * A payload thats gonna be sent to the Discord API */ @@ -56,8 +53,8 @@ export type EventHandlers = Record< >; // Initializes variables for optional libraries -let erlpack: OptionalLibrary | undefined; -let zlib: OptionalLibrary | undefined; +let erlpack: typeof import('erlpack') | undefined; +let zlib: typeof import('zlib-sync') | undefined; /** * Connects every bot shard to a {@link WebSocket} with the Discord Gateway. @@ -133,7 +130,7 @@ export class BotSocketShard { /** * The Inflate context used to compress incoming payloads */ - private zlib: OptionalLibrary | undefined; + private zlib: import('zlib-sync').Inflate | undefined; constructor(botSocket: BotSocket, token: string, shard: ShardOptions) { this.botSocket = botSocket; @@ -169,7 +166,7 @@ export class BotSocketShard { zlib = require('zlib-sync'); // Create new Inflate context - this.zlib = new zlib.Inflate({ + this.zlib = new zlib!.Inflate({ chunkSize: 128 * 1024, windowBits: 32, }); diff --git a/src/structures/voice/UDPSocket.ts b/src/structures/voice/UDPSocket.ts index 90448258b..7be8ad1a7 100644 --- a/src/structures/voice/UDPSocket.ts +++ b/src/structures/voice/UDPSocket.ts @@ -5,14 +5,14 @@ import Connection from './Connection'; import { BotEvent } from '../../socket'; let LibSodium: typeof import('sodium-native'); -let Opusscript: typeof import('opusscript'); +let OpusScript: typeof import('opusscript').OpusScript; try { - LibSodium = require("sodium-native") //eslint-disable-line + LibSodium = require("sodium-native") as typeof import('sodium-native'); //eslint-disable-line } catch (err) {} //eslint-disable-line try { - Opusscript = require("opusscript") //eslint-disable-line + OpusScript = require("opusscript").OpusScript //eslint-disable-line } catch (err) {} //eslint-disable-line export default class UDPSocket extends EventEmitter { @@ -36,18 +36,12 @@ export default class UDPSocket extends EventEmitter { */ public OpusOut = new Readable(); - private OpusEncoder = new Opusscript( - Opusscript.VALID_SAMPLING_RATES[4], - 2, - Opusscript.Application.AUDIO, - ); + private OpusEncoder!: import('opusscript').OpusScript; constructor(connection: Connection) { super(); this.connection = connection; - this.OpusEncoder.encoderCTL(4002, 64000); - this.socket = createSocket('udp4'); } @@ -81,6 +75,19 @@ export default class UDPSocket extends EventEmitter { } start(): void { + if (!OpusScript) throw new Error('OpusScript not found!'); + if (!LibSodium) throw new Error('LibSodium not found!'); + + if (!this.OpusEncoder) { + this.OpusEncoder = new OpusScript( + OpusScript.VALID_SAMPLING_RATES[4], + 2, + OpusScript.Application.AUDIO, + ); + + this.OpusEncoder.encoderCTL(4002, 64000); + } + this.socket.on('message', message => { const data = this.decryptPackage(message); diff --git a/src/types/optionalDependencies/erlpack.d.ts b/src/types/optionalDependencies/erlpack.d.ts new file mode 100644 index 000000000..cb97f3afb --- /dev/null +++ b/src/types/optionalDependencies/erlpack.d.ts @@ -0,0 +1,6 @@ +// https://github.com/discord/erlpack +/* eslint-disable */ +declare module 'erlpack' { + export function pack(data: any): Buffer; + export function unpack(data: Buffer): any; +} \ No newline at end of file diff --git a/src/types/optionalDependencies/opusscript.d.ts b/src/types/optionalDependencies/opusscript.d.ts new file mode 100644 index 000000000..1b2b69bba --- /dev/null +++ b/src/types/optionalDependencies/opusscript.d.ts @@ -0,0 +1,94 @@ +// https://github.com/abalabahaha/opusscript +/* eslint-disable */ +declare module 'opusscript' { + /** + * Opus application type + */ + enum OpusApplication { + /** + * Voice Over IP + */ + VOIP = 2048, + /** + * Audio + */ + AUDIO = 2049, + /** + * Restricted Low-Delay + */ + RESTRICTED_LOWDELAY = 2051 + } + enum OpusError { + "OK" = 0, + "Bad argument" = -1, + "Buffer too small" = -2, + "Internal error" = -3, + "Invalid packet" = -4, + "Unimplemented" = -5, + "Invalid state" = -6, + "Memory allocation fail" = -7 + } + /** + * Valid audio sampling rates + */ + type VALID_SAMPLING_RATES = 8000 | 12000 | 16000 | 24000 | 48000; + /** + * Maximum bytes in a frame + */ + type MAX_FRAME_SIZE = 2880; + /** + * Maximum bytes in a packet + */ + type MAX_PACKET_SIZE = 3828; + /** + * Constructor options for OpusScript + */ + interface OpusScriptOptions { + /** + * Whether or not to use the WASM-compiled version of OpusScript. This is true by default. + */ + wasm?: boolean; + } + class OpusScript { + /** + * Different Opus application types + */ + static Application: typeof OpusApplication; + /** + * Opus Error codes + */ + static Error: typeof OpusError; + /** + * Array of sampling rates that Opus can use + */ + static VALID_SAMPLING_RATES: [8000, 12000, 16000, 24000, 48000]; + /** + * The maximum size (in bytes) to send in a packet + */ + static MAX_PACKET_SIZE: MAX_PACKET_SIZE; + + /** + * OpusScript options being used + */ + options: OpusScriptOptions; + + /** + * Create a new Opus en/decoder + */ + constructor(samplingRate: VALID_SAMPLING_RATES, channels?: number, application?: OpusApplication, options?: OpusScriptOptions); + /** + * Encode a buffer into Opus + */ + encode(buffer: Buffer, frameSize: number): Buffer; + /** + * Decode an opus buffer + */ + decode(buffer: Buffer): Buffer; + encoderCTL(ctl: number, arg: number): void; + decoderCTL(ctl: number, arg: number): void; + /** + * Delete the opus object + */ + delete(): void; + } +} \ No newline at end of file diff --git a/src/types/optionalDependencies/zlib-sync.d.ts b/src/types/optionalDependencies/zlib-sync.d.ts new file mode 100644 index 000000000..b8d79f3e7 --- /dev/null +++ b/src/types/optionalDependencies/zlib-sync.d.ts @@ -0,0 +1,53 @@ +// https://github.com/abalabahaha/zlib-sync +/* eslint-disable */ +declare module 'zlib-sync' { + export interface InflateOptions { + chunkSize?: number; + to?: 'string'; + windowBits?: number; + } + + export class Inflate { + readonly windowBits: number; + readonly result: Buffer | string | null; + readonly msg: string | null; + readonly err: number; + readonly chunkSize: number; + public constructor(options?: InflateOptions); + public push(buffer: Buffer, flush?: boolean | number): void; + } + + export const Z_NO_FLUSH: number; + export const Z_PARTIAL_FLUSH: number; + export const Z_SYNC_FLUSH: number; + export const Z_FULL_FLUSH: number; + export const Z_FINISH: number; + export const Z_BLOCK: number; + export const Z_TREES: number; + export const Z_OK: number; + export const Z_STREAM_END: number; + export const Z_NEED_DICT: number; + export const Z_ERRNO: number; + export const Z_STREAM_ERROR: number; + export const Z_DATA_ERROR: number; + export const Z_MEM_ERROR: number; + export const Z_BUF_ERROR: number; + export const Z_VERSION_ERROR: number; + export const Z_NO_COMPRESSION: number; + export const Z_BEST_SPEED: number; + export const Z_BEST_COMPRESSION: number; + export const Z_DEFAULT_COMPRESSION: number; + export const Z_FILTERED: number; + export const Z_HUFFMAN_ONLY: number; + export const Z_RLE: number; + export const Z_FIXED: number; + export const Z_DEFAULT_STRATEGY: number; + export const Z_BINARY: number; + export const Z_TEXT: number; + export const Z_ASCII: number; + export const Z_UNKNOWN: number; + export const Z_DEFLATED: number; + export const Z_NULL: number; + + export const ZLIB_VERSION: string; +} \ No newline at end of file From b71e02b413cac7ff970c393c4d6c361d4fa1150c Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 02:04:39 +0300 Subject: [PATCH 08/48] chore(Gitignore): add .vscode --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 56d3637aa..b98c7cc9d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules # Workspace configuration .idea +.vscode # Images used for testing files tests/*.png From 4d953fee78c8e89b978d4a0dded0ec45fd72be25 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 02:41:14 +0300 Subject: [PATCH 09/48] chore(Npmignore): created --- .npmignore | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..40a35d314 --- /dev/null +++ b/.npmignore @@ -0,0 +1,10 @@ +.github +src +tests +.commitlintrc.js +.eslintrc +.gitignore +.prettierrc +jest.config.js +typedoc.json +tsconfig.json \ No newline at end of file From aa69d93b48691a9d64cbfeae95d720b39a8c5cb8 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 03:07:43 +0300 Subject: [PATCH 10/48] ci(Compile): add ci for compiling --- .github/workflows/compile.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/compile.yml diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml new file mode 100644 index 000000000..1129516ba --- /dev/null +++ b/.github/workflows/compile.yml @@ -0,0 +1,33 @@ +name: Compile + +on: [push] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install Node v12 + uses: actions/setup-node@v1 + with: + node-version: 12 + + - name: Install dependencies + run: npm ci + + - name: Build package + run: npm run build + + - name: Commit + uses: EndBug/add-and-commit@v4 + with: + add: 'lib' + force: true + message: 'ci(Compile): compiled latest push' + author_email: 41898282+github-actions[bot]@users.noreply.github.com + author_name: github-actions[bot] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 2068777b96c61fd2fb5f0d84d2c8ad1429aa17d6 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 03:09:16 +0300 Subject: [PATCH 11/48] ci(Deploy): changed default commit author --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 69998b3be..7ec7a71d9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -28,3 +28,5 @@ jobs: ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} BRANCH: gh-pages FOLDER: docs + GIT_CONFIG_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com + GIT_CONFIG_NAME: github-actions[bot] From c16345c9d37e2bc2806581684a457e119215fdd4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 00:10:19 +0000 Subject: [PATCH 12/48] ci(Compile): compiled latest push --- lib/Collection.d.ts | 135 ++ lib/Collection.js | 253 ++++ lib/api/APIFile.d.ts | 16 + lib/api/APIFile.js | 58 + lib/api/APIRequest.d.ts | 52 + lib/api/APIRequest.js | 137 ++ lib/api/APISerializer.d.ts | 185 +++ lib/api/APISerializer.js | 385 ++++++ lib/api/BotAPI.d.ts | 644 ++++++++++ lib/api/BotAPI.js | 1118 +++++++++++++++++ lib/api/constants.d.ts | 18 + lib/api/constants.js | 22 + lib/api/endpoints.d.ts | 93 ++ lib/api/endpoints.js | 141 +++ lib/api/index.d.ts | 5 + lib/api/index.js | 17 + lib/api/rateLimit/RateLimitBucket.d.ts | 67 + lib/api/rateLimit/RateLimitBucket.js | 132 ++ lib/api/rateLimit/RateLimitQueue.d.ts | 48 + lib/api/rateLimit/RateLimitQueue.js | 61 + lib/api/rateLimit/Requests.d.ts | 72 ++ lib/api/rateLimit/Requests.js | 59 + lib/api/rateLimit/index.d.ts | 3 + lib/api/rateLimit/index.js | 15 + lib/bot/Bot.d.ts | 137 ++ lib/bot/Bot.js | 85 ++ lib/bot/BotConnection.d.ts | 37 + lib/bot/BotConnection.js | 63 + lib/bot/handlers/Handler.d.ts | 30 + lib/bot/handlers/Handler.js | 28 + lib/bot/handlers/HandlerItem.d.ts | 56 + lib/bot/handlers/HandlerItem.js | 65 + lib/bot/handlers/command/Command.d.ts | 55 + lib/bot/handlers/command/Command.js | 57 + lib/bot/handlers/command/CommandsHandler.d.ts | 25 + lib/bot/handlers/command/CommandsHandler.js | 50 + lib/bot/handlers/command/index.d.ts | 2 + lib/bot/handlers/command/index.js | 14 + lib/bot/handlers/events/Event.d.ts | 54 + lib/bot/handlers/events/Event.js | 57 + lib/bot/handlers/events/EventsHandler.d.ts | 29 + lib/bot/handlers/events/EventsHandler.js | 51 + lib/bot/handlers/events/events.d.ts | 351 ++++++ lib/bot/handlers/events/events.js | 3 + lib/bot/handlers/events/index.d.ts | 2 + lib/bot/handlers/events/index.js | 14 + lib/bot/handlers/index.d.ts | 4 + lib/bot/handlers/index.js | 16 + lib/bot/index.d.ts | 3 + lib/bot/index.js | 15 + lib/controllers/ControllerCache.d.ts | 19 + lib/controllers/ControllerCache.js | 30 + lib/controllers/base/BaseController.d.ts | 18 + lib/controllers/base/BaseController.js | 21 + .../base/BaseCreateController.d.ts | 14 + lib/controllers/base/BaseCreateController.js | 12 + .../base/BaseDeleteController.d.ts | 15 + lib/controllers/base/BaseDeleteController.js | 11 + .../base/BaseFetchAllController.d.ts | 15 + .../base/BaseFetchAllController.js | 11 + lib/controllers/base/BaseFetchController.d.ts | 21 + lib/controllers/base/BaseFetchController.js | 30 + .../base/BaseFetchSomeController.d.ts | 15 + .../base/BaseFetchSomeController.js | 11 + lib/controllers/base/index.d.ts | 6 + lib/controllers/base/index.js | 18 + .../bot/BotChannelsController.d.ts | 35 + lib/controllers/bot/BotChannelsController.js | 81 ++ lib/controllers/bot/BotGuildsController.d.ts | 31 + lib/controllers/bot/BotGuildsController.js | 41 + lib/controllers/bot/BotUsersController.d.ts | 22 + lib/controllers/bot/BotUsersController.js | 52 + lib/controllers/bot/index.d.ts | 3 + lib/controllers/bot/index.js | 15 + .../channel/ChannelMessagesController.d.ts | 55 + .../channel/ChannelMessagesController.js | 56 + .../channel/ChannelPermissionsController.d.ts | 31 + .../channel/ChannelPermissionsController.js | 34 + .../channel/ChannelPinsController.d.ts | 39 + .../channel/ChannelPinsController.js | 62 + lib/controllers/channel/index.d.ts | 3 + lib/controllers/channel/index.js | 15 + .../guild/GuildBansController.d.ts | 33 + lib/controllers/guild/GuildBansController.js | 56 + .../guild/GuildChannelInvitesController.d.ts | 28 + .../guild/GuildChannelInvitesController.js | 45 + .../guild/GuildChannelWebhooksController.d.ts | 41 + .../guild/GuildChannelWebhooksController.js | 73 ++ .../guild/GuildChannelsController.d.ts | 61 + .../guild/GuildChannelsController.js | 103 ++ .../guild/GuildEmojisController.d.ts | 32 + .../guild/GuildEmojisController.js | 55 + .../guild/GuildIntegrationsController.d.ts | 35 + .../guild/GuildIntegrationsController.js | 54 + .../guild/GuildInvitesController.d.ts | 44 + .../guild/GuildInvitesController.js | 57 + .../guild/GuildMembersController.d.ts | 52 + .../guild/GuildMembersController.js | 65 + .../guild/GuildRolesController.d.ts | 43 + lib/controllers/guild/GuildRolesController.js | 62 + .../guild/GuildWebhooksController.d.ts | 20 + .../guild/GuildWebhooksController.js | 35 + lib/controllers/guild/index.d.ts | 10 + lib/controllers/guild/index.js | 22 + lib/controllers/index.d.ts | 8 + lib/controllers/index.js | 20 + .../member/MemberRolesController.d.ts | 31 + .../member/MemberRolesController.js | 32 + lib/controllers/member/index.d.ts | 1 + lib/controllers/member/index.js | 13 + .../message/MessageReactionsController.d.ts | 36 + .../message/MessageReactionsController.js | 42 + lib/controllers/message/index.d.ts | 1 + lib/controllers/message/index.js | 13 + .../reaction/ReactionUsersController.d.ts | 43 + .../reaction/ReactionUsersController.js | 48 + lib/controllers/reaction/index.d.ts | 1 + lib/controllers/reaction/index.js | 13 + lib/index.d.ts | 8 + lib/index.js | 32 + lib/sharding/BotCommunication.d.ts | 253 ++++ lib/sharding/BotCommunication.js | 138 ++ lib/sharding/BotShard.d.ts | 53 + lib/sharding/BotShard.js | 158 +++ lib/sharding/BotShardManager.d.ts | 53 + lib/sharding/BotShardManager.js | 96 ++ lib/sharding/index.d.ts | 3 + lib/sharding/index.js | 15 + lib/socket/BotHeartbeats.d.ts | 43 + lib/socket/BotHeartbeats.js | 66 + lib/socket/BotSocket.d.ts | 69 + lib/socket/BotSocket.js | 140 +++ lib/socket/BotSocketShard.d.ts | 186 +++ lib/socket/BotSocketShard.js | 339 +++++ lib/socket/constants.d.ts | 129 ++ lib/socket/constants.js | 65 + lib/socket/handlers/channelCreate.d.ts | 4 + lib/socket/handlers/channelCreate.js | 18 + lib/socket/handlers/channelDelete.d.ts | 4 + lib/socket/handlers/channelDelete.js | 18 + lib/socket/handlers/channelPinsUpdate.d.ts | 4 + lib/socket/handlers/channelPinsUpdate.js | 20 + lib/socket/handlers/channelUpdate.d.ts | 4 + lib/socket/handlers/channelUpdate.js | 18 + lib/socket/handlers/guildBanAdd.d.ts | 4 + lib/socket/handlers/guildBanAdd.js | 21 + lib/socket/handlers/guildBanRemove.d.ts | 4 + lib/socket/handlers/guildBanRemove.js | 21 + lib/socket/handlers/guildCreate.d.ts | 4 + lib/socket/handlers/guildCreate.js | 31 + lib/socket/handlers/guildDelete.d.ts | 4 + lib/socket/handlers/guildDelete.js | 19 + lib/socket/handlers/guildEmojisUpdate.d.ts | 4 + lib/socket/handlers/guildEmojisUpdate.js | 23 + .../handlers/guildIntegrationsUpdate.d.ts | 4 + .../handlers/guildIntegrationsUpdate.js | 17 + lib/socket/handlers/guildMemberAdd.d.ts | 4 + lib/socket/handlers/guildMemberAdd.js | 25 + lib/socket/handlers/guildMemberRemove.d.ts | 4 + lib/socket/handlers/guildMemberRemove.js | 21 + lib/socket/handlers/guildMemberUpdate.d.ts | 4 + lib/socket/handlers/guildMemberUpdate.js | 19 + lib/socket/handlers/guildMembersChunk.d.ts | 17 + lib/socket/handlers/guildMembersChunk.js | 47 + lib/socket/handlers/guildRoleCreate.d.ts | 4 + lib/socket/handlers/guildRoleCreate.js | 21 + lib/socket/handlers/guildRoleDelete.d.ts | 4 + lib/socket/handlers/guildRoleDelete.js | 22 + lib/socket/handlers/guildRoleUpdate.d.ts | 4 + lib/socket/handlers/guildRoleUpdate.js | 21 + lib/socket/handlers/guildUpdate.d.ts | 4 + lib/socket/handlers/guildUpdate.js | 21 + lib/socket/handlers/index.d.ts | 35 + lib/socket/handlers/index.js | 72 ++ lib/socket/handlers/inviteCreate.d.ts | 4 + lib/socket/handlers/inviteCreate.js | 13 + lib/socket/handlers/inviteDelete.d.ts | 4 + lib/socket/handlers/inviteDelete.js | 30 + lib/socket/handlers/messageCreate.d.ts | 4 + lib/socket/handlers/messageCreate.js | 23 + lib/socket/handlers/messageDelete.d.ts | 4 + lib/socket/handlers/messageDelete.js | 30 + lib/socket/handlers/messageDeleteBulk.d.ts | 4 + lib/socket/handlers/messageDeleteBulk.js | 26 + lib/socket/handlers/messageReactionAdd.d.ts | 4 + lib/socket/handlers/messageReactionAdd.js | 44 + .../handlers/messageReactionRemove.d.ts | 4 + lib/socket/handlers/messageReactionRemove.js | 46 + .../handlers/messageReactionRemoveAll.d.ts | 4 + .../handlers/messageReactionRemoveAll.js | 19 + .../handlers/messageReactionRemoveEmoji.d.ts | 4 + .../handlers/messageReactionRemoveEmoji.js | 22 + lib/socket/handlers/messageUpdate.d.ts | 4 + lib/socket/handlers/messageUpdate.js | 19 + lib/socket/handlers/presenceUpdate.d.ts | 4 + lib/socket/handlers/presenceUpdate.js | 23 + lib/socket/handlers/ready.d.ts | 4 + lib/socket/handlers/ready.js | 21 + lib/socket/handlers/typingStart.d.ts | 4 + lib/socket/handlers/typingStart.js | 23 + lib/socket/handlers/userUpdate.d.ts | 4 + lib/socket/handlers/userUpdate.js | 20 + lib/socket/handlers/voiceServerUpdate.d.ts | 4 + lib/socket/handlers/voiceServerUpdate.js | 15 + lib/socket/handlers/voiceStateUpdate.d.ts | 4 + lib/socket/handlers/voiceStateUpdate.js | 22 + lib/socket/handlers/webhooksUpdate.d.ts | 4 + lib/socket/handlers/webhooksUpdate.js | 18 + lib/socket/index.d.ts | 6 + lib/socket/index.js | 18 + lib/socket/properties.d.ts | 34 + lib/socket/properties.js | 19 + lib/socket/utils/HandlersUtils.d.ts | 16 + lib/socket/utils/HandlersUtils.js | 13 + lib/socket/utils/ReactionHandlersUtils.d.ts | 17 + lib/socket/utils/ReactionHandlersUtils.js | 38 + lib/socket/utils/index.d.ts | 2 + lib/socket/utils/index.js | 14 + lib/structures/Avatar.d.ts | 133 ++ lib/structures/Avatar.js | 164 +++ lib/structures/BotPresence.d.ts | 28 + lib/structures/BotPresence.js | 57 + lib/structures/BotUser.d.ts | 112 ++ lib/structures/BotUser.js | 39 + lib/structures/Emoji.d.ts | 70 ++ lib/structures/Emoji.js | 57 + lib/structures/ImageURI.d.ts | 32 + lib/structures/ImageURI.js | 62 + lib/structures/Invite.d.ts | 100 ++ lib/structures/Invite.js | 50 + lib/structures/PermissionOverwrite.d.ts | 35 + lib/structures/PermissionOverwrite.js | 41 + lib/structures/Role.d.ts | 89 ++ lib/structures/Role.js | 47 + lib/structures/Timestamp.d.ts | 20 + lib/structures/Timestamp.js | 34 + lib/structures/User.d.ts | 113 ++ lib/structures/User.js | 104 ++ lib/structures/Webhook.d.ts | 89 ++ lib/structures/Webhook.js | 59 + lib/structures/base/BaseGuildStruct.d.ts | 14 + lib/structures/base/BaseGuildStruct.js | 15 + lib/structures/base/BaseStruct.d.ts | 50 + lib/structures/base/BaseStruct.js | 39 + lib/structures/base/index.d.ts | 2 + lib/structures/base/index.js | 14 + lib/structures/channels/Channel.d.ts | 45 + lib/structures/channels/Channel.js | 51 + lib/structures/channels/DMChannel.d.ts | 34 + lib/structures/channels/DMChannel.js | 38 + .../channels/GuildCategoryChannel.d.ts | 13 + .../channels/GuildCategoryChannel.js | 17 + lib/structures/channels/GuildChannel.d.ts | 123 ++ lib/structures/channels/GuildChannel.js | 53 + lib/structures/channels/GuildTextChannel.d.ts | 41 + lib/structures/channels/GuildTextChannel.js | 48 + .../channels/GuildVoiceChannel.d.ts | 11 + lib/structures/channels/GuildVoiceChannel.js | 15 + lib/structures/channels/TextChannel.d.ts | 54 + lib/structures/channels/TextChannel.js | 2 + lib/structures/channels/index.d.ts | 8 + lib/structures/channels/index.js | 20 + .../channels/utils/ChannelUtils.d.ts | 54 + lib/structures/channels/utils/ChannelUtils.js | 123 ++ lib/structures/channels/utils/index.d.ts | 1 + lib/structures/channels/utils/index.js | 13 + lib/structures/flags/Flags.d.ts | 28 + lib/structures/flags/Flags.js | 37 + .../flags/GuildSystemChannelFlags.d.ts | 17 + .../flags/GuildSystemChannelFlags.js | 22 + lib/structures/flags/MessageFlags.d.ts | 29 + lib/structures/flags/MessageFlags.js | 34 + lib/structures/flags/PermissionFlags.d.ts | 82 ++ lib/structures/flags/PermissionFlags.js | 63 + .../flags/PresenceActivityFlags.d.ts | 15 + lib/structures/flags/PresenceActivityFlags.js | 20 + lib/structures/flags/UserFlags.d.ts | 23 + lib/structures/flags/UserFlags.js | 28 + lib/structures/flags/index.d.ts | 6 + lib/structures/flags/index.js | 18 + lib/structures/guild/Guild.d.ts | 446 +++++++ lib/structures/guild/Guild.js | 283 +++++ lib/structures/guild/GuildBan.d.ts | 32 + lib/structures/guild/GuildBan.js | 33 + lib/structures/guild/GuildEmoji.d.ts | 81 ++ lib/structures/guild/GuildEmoji.js | 53 + lib/structures/guild/GuildIntegration.d.ts | 137 ++ lib/structures/guild/GuildIntegration.js | 64 + lib/structures/guild/GuildPreview.d.ts | 128 ++ lib/structures/guild/GuildPreview.js | 124 ++ lib/structures/guild/GuildUnavailable.d.ts | 30 + lib/structures/guild/GuildUnavailable.js | 28 + lib/structures/guild/GuildWidget.d.ts | 41 + lib/structures/guild/GuildWidget.js | 28 + lib/structures/guild/index.d.ts | 7 + lib/structures/guild/index.js | 19 + lib/structures/index.d.ts | 16 + lib/structures/index.js | 28 + lib/structures/member/Member.d.ts | 127 ++ lib/structures/member/Member.js | 97 ++ lib/structures/member/MemberPresence.d.ts | 229 ++++ lib/structures/member/MemberPresence.js | 92 ++ lib/structures/member/index.d.ts | 2 + lib/structures/member/index.js | 14 + lib/structures/message/Message.d.ts | 295 +++++ lib/structures/message/Message.js | 174 +++ lib/structures/message/MessageAttachment.d.ts | 46 + lib/structures/message/MessageAttachment.js | 34 + lib/structures/message/MessageEmbed.d.ts | 234 ++++ lib/structures/message/MessageEmbed.js | 151 +++ lib/structures/message/MessageMentions.d.ts | 54 + lib/structures/message/MessageMentions.js | 75 ++ lib/structures/message/MessageReaction.d.ts | 55 + lib/structures/message/MessageReaction.js | 51 + lib/structures/message/index.d.ts | 5 + lib/structures/message/index.js | 17 + lib/structures/voice/Connection.d.ts | 20 + lib/structures/voice/Connection.js | 41 + lib/structures/voice/GuildVoice.d.ts | 24 + lib/structures/voice/GuildVoice.js | 40 + lib/structures/voice/UDPSocket.d.ts | 34 + lib/structures/voice/UDPSocket.js | 109 ++ lib/structures/voice/VoiceHeartbeats.d.ts | 38 + lib/structures/voice/VoiceHeartbeats.js | 53 + lib/structures/voice/VoiceState.d.ts | 35 + lib/structures/voice/VoiceState.js | 38 + lib/structures/voice/VoiceWebSocket.d.ts | 41 + lib/structures/voice/VoiceWebSocket.js | 99 ++ lib/structures/voice/index.d.ts | 6 + lib/structures/voice/index.js | 18 + lib/types/index.d.ts | 1 + lib/types/index.js | 13 + lib/types/types.d.ts | 19 + lib/types/types.js | 2 + 334 files changed, 17125 insertions(+) create mode 100644 lib/Collection.d.ts create mode 100644 lib/Collection.js create mode 100644 lib/api/APIFile.d.ts create mode 100644 lib/api/APIFile.js create mode 100644 lib/api/APIRequest.d.ts create mode 100644 lib/api/APIRequest.js create mode 100644 lib/api/APISerializer.d.ts create mode 100644 lib/api/APISerializer.js create mode 100644 lib/api/BotAPI.d.ts create mode 100644 lib/api/BotAPI.js create mode 100644 lib/api/constants.d.ts create mode 100644 lib/api/constants.js create mode 100644 lib/api/endpoints.d.ts create mode 100644 lib/api/endpoints.js create mode 100644 lib/api/index.d.ts create mode 100644 lib/api/index.js create mode 100644 lib/api/rateLimit/RateLimitBucket.d.ts create mode 100644 lib/api/rateLimit/RateLimitBucket.js create mode 100644 lib/api/rateLimit/RateLimitQueue.d.ts create mode 100644 lib/api/rateLimit/RateLimitQueue.js create mode 100644 lib/api/rateLimit/Requests.d.ts create mode 100644 lib/api/rateLimit/Requests.js create mode 100644 lib/api/rateLimit/index.d.ts create mode 100644 lib/api/rateLimit/index.js create mode 100644 lib/bot/Bot.d.ts create mode 100644 lib/bot/Bot.js create mode 100644 lib/bot/BotConnection.d.ts create mode 100644 lib/bot/BotConnection.js create mode 100644 lib/bot/handlers/Handler.d.ts create mode 100644 lib/bot/handlers/Handler.js create mode 100644 lib/bot/handlers/HandlerItem.d.ts create mode 100644 lib/bot/handlers/HandlerItem.js create mode 100644 lib/bot/handlers/command/Command.d.ts create mode 100644 lib/bot/handlers/command/Command.js create mode 100644 lib/bot/handlers/command/CommandsHandler.d.ts create mode 100644 lib/bot/handlers/command/CommandsHandler.js create mode 100644 lib/bot/handlers/command/index.d.ts create mode 100644 lib/bot/handlers/command/index.js create mode 100644 lib/bot/handlers/events/Event.d.ts create mode 100644 lib/bot/handlers/events/Event.js create mode 100644 lib/bot/handlers/events/EventsHandler.d.ts create mode 100644 lib/bot/handlers/events/EventsHandler.js create mode 100644 lib/bot/handlers/events/events.d.ts create mode 100644 lib/bot/handlers/events/events.js create mode 100644 lib/bot/handlers/events/index.d.ts create mode 100644 lib/bot/handlers/events/index.js create mode 100644 lib/bot/handlers/index.d.ts create mode 100644 lib/bot/handlers/index.js create mode 100644 lib/bot/index.d.ts create mode 100644 lib/bot/index.js create mode 100644 lib/controllers/ControllerCache.d.ts create mode 100644 lib/controllers/ControllerCache.js create mode 100644 lib/controllers/base/BaseController.d.ts create mode 100644 lib/controllers/base/BaseController.js create mode 100644 lib/controllers/base/BaseCreateController.d.ts create mode 100644 lib/controllers/base/BaseCreateController.js create mode 100644 lib/controllers/base/BaseDeleteController.d.ts create mode 100644 lib/controllers/base/BaseDeleteController.js create mode 100644 lib/controllers/base/BaseFetchAllController.d.ts create mode 100644 lib/controllers/base/BaseFetchAllController.js create mode 100644 lib/controllers/base/BaseFetchController.d.ts create mode 100644 lib/controllers/base/BaseFetchController.js create mode 100644 lib/controllers/base/BaseFetchSomeController.d.ts create mode 100644 lib/controllers/base/BaseFetchSomeController.js create mode 100644 lib/controllers/base/index.d.ts create mode 100644 lib/controllers/base/index.js create mode 100644 lib/controllers/bot/BotChannelsController.d.ts create mode 100644 lib/controllers/bot/BotChannelsController.js create mode 100644 lib/controllers/bot/BotGuildsController.d.ts create mode 100644 lib/controllers/bot/BotGuildsController.js create mode 100644 lib/controllers/bot/BotUsersController.d.ts create mode 100644 lib/controllers/bot/BotUsersController.js create mode 100644 lib/controllers/bot/index.d.ts create mode 100644 lib/controllers/bot/index.js create mode 100644 lib/controllers/channel/ChannelMessagesController.d.ts create mode 100644 lib/controllers/channel/ChannelMessagesController.js create mode 100644 lib/controllers/channel/ChannelPermissionsController.d.ts create mode 100644 lib/controllers/channel/ChannelPermissionsController.js create mode 100644 lib/controllers/channel/ChannelPinsController.d.ts create mode 100644 lib/controllers/channel/ChannelPinsController.js create mode 100644 lib/controllers/channel/index.d.ts create mode 100644 lib/controllers/channel/index.js create mode 100644 lib/controllers/guild/GuildBansController.d.ts create mode 100644 lib/controllers/guild/GuildBansController.js create mode 100644 lib/controllers/guild/GuildChannelInvitesController.d.ts create mode 100644 lib/controllers/guild/GuildChannelInvitesController.js create mode 100644 lib/controllers/guild/GuildChannelWebhooksController.d.ts create mode 100644 lib/controllers/guild/GuildChannelWebhooksController.js create mode 100644 lib/controllers/guild/GuildChannelsController.d.ts create mode 100644 lib/controllers/guild/GuildChannelsController.js create mode 100644 lib/controllers/guild/GuildEmojisController.d.ts create mode 100644 lib/controllers/guild/GuildEmojisController.js create mode 100644 lib/controllers/guild/GuildIntegrationsController.d.ts create mode 100644 lib/controllers/guild/GuildIntegrationsController.js create mode 100644 lib/controllers/guild/GuildInvitesController.d.ts create mode 100644 lib/controllers/guild/GuildInvitesController.js create mode 100644 lib/controllers/guild/GuildMembersController.d.ts create mode 100644 lib/controllers/guild/GuildMembersController.js create mode 100644 lib/controllers/guild/GuildRolesController.d.ts create mode 100644 lib/controllers/guild/GuildRolesController.js create mode 100644 lib/controllers/guild/GuildWebhooksController.d.ts create mode 100644 lib/controllers/guild/GuildWebhooksController.js create mode 100644 lib/controllers/guild/index.d.ts create mode 100644 lib/controllers/guild/index.js create mode 100644 lib/controllers/index.d.ts create mode 100644 lib/controllers/index.js create mode 100644 lib/controllers/member/MemberRolesController.d.ts create mode 100644 lib/controllers/member/MemberRolesController.js create mode 100644 lib/controllers/member/index.d.ts create mode 100644 lib/controllers/member/index.js create mode 100644 lib/controllers/message/MessageReactionsController.d.ts create mode 100644 lib/controllers/message/MessageReactionsController.js create mode 100644 lib/controllers/message/index.d.ts create mode 100644 lib/controllers/message/index.js create mode 100644 lib/controllers/reaction/ReactionUsersController.d.ts create mode 100644 lib/controllers/reaction/ReactionUsersController.js create mode 100644 lib/controllers/reaction/index.d.ts create mode 100644 lib/controllers/reaction/index.js create mode 100644 lib/index.d.ts create mode 100644 lib/index.js create mode 100644 lib/sharding/BotCommunication.d.ts create mode 100644 lib/sharding/BotCommunication.js create mode 100644 lib/sharding/BotShard.d.ts create mode 100644 lib/sharding/BotShard.js create mode 100644 lib/sharding/BotShardManager.d.ts create mode 100644 lib/sharding/BotShardManager.js create mode 100644 lib/sharding/index.d.ts create mode 100644 lib/sharding/index.js create mode 100644 lib/socket/BotHeartbeats.d.ts create mode 100644 lib/socket/BotHeartbeats.js create mode 100644 lib/socket/BotSocket.d.ts create mode 100644 lib/socket/BotSocket.js create mode 100644 lib/socket/BotSocketShard.d.ts create mode 100644 lib/socket/BotSocketShard.js create mode 100644 lib/socket/constants.d.ts create mode 100644 lib/socket/constants.js create mode 100644 lib/socket/handlers/channelCreate.d.ts create mode 100644 lib/socket/handlers/channelCreate.js create mode 100644 lib/socket/handlers/channelDelete.d.ts create mode 100644 lib/socket/handlers/channelDelete.js create mode 100644 lib/socket/handlers/channelPinsUpdate.d.ts create mode 100644 lib/socket/handlers/channelPinsUpdate.js create mode 100644 lib/socket/handlers/channelUpdate.d.ts create mode 100644 lib/socket/handlers/channelUpdate.js create mode 100644 lib/socket/handlers/guildBanAdd.d.ts create mode 100644 lib/socket/handlers/guildBanAdd.js create mode 100644 lib/socket/handlers/guildBanRemove.d.ts create mode 100644 lib/socket/handlers/guildBanRemove.js create mode 100644 lib/socket/handlers/guildCreate.d.ts create mode 100644 lib/socket/handlers/guildCreate.js create mode 100644 lib/socket/handlers/guildDelete.d.ts create mode 100644 lib/socket/handlers/guildDelete.js create mode 100644 lib/socket/handlers/guildEmojisUpdate.d.ts create mode 100644 lib/socket/handlers/guildEmojisUpdate.js create mode 100644 lib/socket/handlers/guildIntegrationsUpdate.d.ts create mode 100644 lib/socket/handlers/guildIntegrationsUpdate.js create mode 100644 lib/socket/handlers/guildMemberAdd.d.ts create mode 100644 lib/socket/handlers/guildMemberAdd.js create mode 100644 lib/socket/handlers/guildMemberRemove.d.ts create mode 100644 lib/socket/handlers/guildMemberRemove.js create mode 100644 lib/socket/handlers/guildMemberUpdate.d.ts create mode 100644 lib/socket/handlers/guildMemberUpdate.js create mode 100644 lib/socket/handlers/guildMembersChunk.d.ts create mode 100644 lib/socket/handlers/guildMembersChunk.js create mode 100644 lib/socket/handlers/guildRoleCreate.d.ts create mode 100644 lib/socket/handlers/guildRoleCreate.js create mode 100644 lib/socket/handlers/guildRoleDelete.d.ts create mode 100644 lib/socket/handlers/guildRoleDelete.js create mode 100644 lib/socket/handlers/guildRoleUpdate.d.ts create mode 100644 lib/socket/handlers/guildRoleUpdate.js create mode 100644 lib/socket/handlers/guildUpdate.d.ts create mode 100644 lib/socket/handlers/guildUpdate.js create mode 100644 lib/socket/handlers/index.d.ts create mode 100644 lib/socket/handlers/index.js create mode 100644 lib/socket/handlers/inviteCreate.d.ts create mode 100644 lib/socket/handlers/inviteCreate.js create mode 100644 lib/socket/handlers/inviteDelete.d.ts create mode 100644 lib/socket/handlers/inviteDelete.js create mode 100644 lib/socket/handlers/messageCreate.d.ts create mode 100644 lib/socket/handlers/messageCreate.js create mode 100644 lib/socket/handlers/messageDelete.d.ts create mode 100644 lib/socket/handlers/messageDelete.js create mode 100644 lib/socket/handlers/messageDeleteBulk.d.ts create mode 100644 lib/socket/handlers/messageDeleteBulk.js create mode 100644 lib/socket/handlers/messageReactionAdd.d.ts create mode 100644 lib/socket/handlers/messageReactionAdd.js create mode 100644 lib/socket/handlers/messageReactionRemove.d.ts create mode 100644 lib/socket/handlers/messageReactionRemove.js create mode 100644 lib/socket/handlers/messageReactionRemoveAll.d.ts create mode 100644 lib/socket/handlers/messageReactionRemoveAll.js create mode 100644 lib/socket/handlers/messageReactionRemoveEmoji.d.ts create mode 100644 lib/socket/handlers/messageReactionRemoveEmoji.js create mode 100644 lib/socket/handlers/messageUpdate.d.ts create mode 100644 lib/socket/handlers/messageUpdate.js create mode 100644 lib/socket/handlers/presenceUpdate.d.ts create mode 100644 lib/socket/handlers/presenceUpdate.js create mode 100644 lib/socket/handlers/ready.d.ts create mode 100644 lib/socket/handlers/ready.js create mode 100644 lib/socket/handlers/typingStart.d.ts create mode 100644 lib/socket/handlers/typingStart.js create mode 100644 lib/socket/handlers/userUpdate.d.ts create mode 100644 lib/socket/handlers/userUpdate.js create mode 100644 lib/socket/handlers/voiceServerUpdate.d.ts create mode 100644 lib/socket/handlers/voiceServerUpdate.js create mode 100644 lib/socket/handlers/voiceStateUpdate.d.ts create mode 100644 lib/socket/handlers/voiceStateUpdate.js create mode 100644 lib/socket/handlers/webhooksUpdate.d.ts create mode 100644 lib/socket/handlers/webhooksUpdate.js create mode 100644 lib/socket/index.d.ts create mode 100644 lib/socket/index.js create mode 100644 lib/socket/properties.d.ts create mode 100644 lib/socket/properties.js create mode 100644 lib/socket/utils/HandlersUtils.d.ts create mode 100644 lib/socket/utils/HandlersUtils.js create mode 100644 lib/socket/utils/ReactionHandlersUtils.d.ts create mode 100644 lib/socket/utils/ReactionHandlersUtils.js create mode 100644 lib/socket/utils/index.d.ts create mode 100644 lib/socket/utils/index.js create mode 100644 lib/structures/Avatar.d.ts create mode 100644 lib/structures/Avatar.js create mode 100644 lib/structures/BotPresence.d.ts create mode 100644 lib/structures/BotPresence.js create mode 100644 lib/structures/BotUser.d.ts create mode 100644 lib/structures/BotUser.js create mode 100644 lib/structures/Emoji.d.ts create mode 100644 lib/structures/Emoji.js create mode 100644 lib/structures/ImageURI.d.ts create mode 100644 lib/structures/ImageURI.js create mode 100644 lib/structures/Invite.d.ts create mode 100644 lib/structures/Invite.js create mode 100644 lib/structures/PermissionOverwrite.d.ts create mode 100644 lib/structures/PermissionOverwrite.js create mode 100644 lib/structures/Role.d.ts create mode 100644 lib/structures/Role.js create mode 100644 lib/structures/Timestamp.d.ts create mode 100644 lib/structures/Timestamp.js create mode 100644 lib/structures/User.d.ts create mode 100644 lib/structures/User.js create mode 100644 lib/structures/Webhook.d.ts create mode 100644 lib/structures/Webhook.js create mode 100644 lib/structures/base/BaseGuildStruct.d.ts create mode 100644 lib/structures/base/BaseGuildStruct.js create mode 100644 lib/structures/base/BaseStruct.d.ts create mode 100644 lib/structures/base/BaseStruct.js create mode 100644 lib/structures/base/index.d.ts create mode 100644 lib/structures/base/index.js create mode 100644 lib/structures/channels/Channel.d.ts create mode 100644 lib/structures/channels/Channel.js create mode 100644 lib/structures/channels/DMChannel.d.ts create mode 100644 lib/structures/channels/DMChannel.js create mode 100644 lib/structures/channels/GuildCategoryChannel.d.ts create mode 100644 lib/structures/channels/GuildCategoryChannel.js create mode 100644 lib/structures/channels/GuildChannel.d.ts create mode 100644 lib/structures/channels/GuildChannel.js create mode 100644 lib/structures/channels/GuildTextChannel.d.ts create mode 100644 lib/structures/channels/GuildTextChannel.js create mode 100644 lib/structures/channels/GuildVoiceChannel.d.ts create mode 100644 lib/structures/channels/GuildVoiceChannel.js create mode 100644 lib/structures/channels/TextChannel.d.ts create mode 100644 lib/structures/channels/TextChannel.js create mode 100644 lib/structures/channels/index.d.ts create mode 100644 lib/structures/channels/index.js create mode 100644 lib/structures/channels/utils/ChannelUtils.d.ts create mode 100644 lib/structures/channels/utils/ChannelUtils.js create mode 100644 lib/structures/channels/utils/index.d.ts create mode 100644 lib/structures/channels/utils/index.js create mode 100644 lib/structures/flags/Flags.d.ts create mode 100644 lib/structures/flags/Flags.js create mode 100644 lib/structures/flags/GuildSystemChannelFlags.d.ts create mode 100644 lib/structures/flags/GuildSystemChannelFlags.js create mode 100644 lib/structures/flags/MessageFlags.d.ts create mode 100644 lib/structures/flags/MessageFlags.js create mode 100644 lib/structures/flags/PermissionFlags.d.ts create mode 100644 lib/structures/flags/PermissionFlags.js create mode 100644 lib/structures/flags/PresenceActivityFlags.d.ts create mode 100644 lib/structures/flags/PresenceActivityFlags.js create mode 100644 lib/structures/flags/UserFlags.d.ts create mode 100644 lib/structures/flags/UserFlags.js create mode 100644 lib/structures/flags/index.d.ts create mode 100644 lib/structures/flags/index.js create mode 100644 lib/structures/guild/Guild.d.ts create mode 100644 lib/structures/guild/Guild.js create mode 100644 lib/structures/guild/GuildBan.d.ts create mode 100644 lib/structures/guild/GuildBan.js create mode 100644 lib/structures/guild/GuildEmoji.d.ts create mode 100644 lib/structures/guild/GuildEmoji.js create mode 100644 lib/structures/guild/GuildIntegration.d.ts create mode 100644 lib/structures/guild/GuildIntegration.js create mode 100644 lib/structures/guild/GuildPreview.d.ts create mode 100644 lib/structures/guild/GuildPreview.js create mode 100644 lib/structures/guild/GuildUnavailable.d.ts create mode 100644 lib/structures/guild/GuildUnavailable.js create mode 100644 lib/structures/guild/GuildWidget.d.ts create mode 100644 lib/structures/guild/GuildWidget.js create mode 100644 lib/structures/guild/index.d.ts create mode 100644 lib/structures/guild/index.js create mode 100644 lib/structures/index.d.ts create mode 100644 lib/structures/index.js create mode 100644 lib/structures/member/Member.d.ts create mode 100644 lib/structures/member/Member.js create mode 100644 lib/structures/member/MemberPresence.d.ts create mode 100644 lib/structures/member/MemberPresence.js create mode 100644 lib/structures/member/index.d.ts create mode 100644 lib/structures/member/index.js create mode 100644 lib/structures/message/Message.d.ts create mode 100644 lib/structures/message/Message.js create mode 100644 lib/structures/message/MessageAttachment.d.ts create mode 100644 lib/structures/message/MessageAttachment.js create mode 100644 lib/structures/message/MessageEmbed.d.ts create mode 100644 lib/structures/message/MessageEmbed.js create mode 100644 lib/structures/message/MessageMentions.d.ts create mode 100644 lib/structures/message/MessageMentions.js create mode 100644 lib/structures/message/MessageReaction.d.ts create mode 100644 lib/structures/message/MessageReaction.js create mode 100644 lib/structures/message/index.d.ts create mode 100644 lib/structures/message/index.js create mode 100644 lib/structures/voice/Connection.d.ts create mode 100644 lib/structures/voice/Connection.js create mode 100644 lib/structures/voice/GuildVoice.d.ts create mode 100644 lib/structures/voice/GuildVoice.js create mode 100644 lib/structures/voice/UDPSocket.d.ts create mode 100644 lib/structures/voice/UDPSocket.js create mode 100644 lib/structures/voice/VoiceHeartbeats.d.ts create mode 100644 lib/structures/voice/VoiceHeartbeats.js create mode 100644 lib/structures/voice/VoiceState.d.ts create mode 100644 lib/structures/voice/VoiceState.js create mode 100644 lib/structures/voice/VoiceWebSocket.d.ts create mode 100644 lib/structures/voice/VoiceWebSocket.js create mode 100644 lib/structures/voice/index.d.ts create mode 100644 lib/structures/voice/index.js create mode 100644 lib/types/index.d.ts create mode 100644 lib/types/index.js create mode 100644 lib/types/types.d.ts create mode 100644 lib/types/types.js diff --git a/lib/Collection.d.ts b/lib/Collection.d.ts new file mode 100644 index 000000000..f3aeda0e9 --- /dev/null +++ b/lib/Collection.d.ts @@ -0,0 +1,135 @@ +/** + * Collections serve as data holders throughout the library. + * They are a combination of JavaScript Maps and Arrays with the + * ability to hold large amount of data. + * @template K, V + */ +declare class Collection extends Map { + /** + * The maximum number of items allowed in this Collection + */ + readonly limit: number | null | undefined; + constructor(entries?: Iterable | null, limit?: number); + /** + * Whether the given argument is a Collection + * @param {unknown} collection Data to check if collection + * @returns {boolean} + */ + static isCollection(collection: unknown): collection is Collection; + /** + * Maps the {@link Collection} values into an array + * @type {V[]} + */ + get toArray(): V[]; + /** + * Maps the {@link Collection} keys into an array + * @type {K[]} + */ + get toArrayKeys(): K[]; + /** + * Maps the {@link Collection} entries an array + * @type {[K, V][]} + */ + get toArrayEntries(): [K, V][]; + /** + * Creates a new Collection with all elements that pass the test implemented by the provided callback. + * Identical to {@link Array.prototype.filter} + * @param {function(value: V, key: K, collection: this): boolean} cb Callback function. Return true to keep the element, false otherwise + * @returns {Collection} + */ + filter(cb: (value: V, key?: K, collection?: this) => boolean): Collection; + /** + * Get the first value in the {@link Collection} + * @type {V | undefined} + */ + get first(): V | undefined; + /** + * Get he first key in the {@link Collection} + * @returns {K} + */ + get firstKey(): K | undefined; + /** + * Get the last value in the {@link Collection} + * @type {V} + */ + get last(): V | undefined; + /** + * Get he last key in the {@link Collection} + * @returns {K} + */ + get lastKey(): K | undefined; + /** + * Returns the item if exists or creates a new one and caches it. + * @param {K} key The key of the item + * @param {V} value The value of the item + * @returns {V} + */ + getOrSet(key: K, value: V): V; + /** + * Ö›Merges collection(s) on top of this one. Replaces existing keys by newer Collections + * @param {...(Collection | [K, V][])[]} collections The collection(s) to be merged on top of this one + */ + merge(...collections: (Collection | [K, V][])[]): void; + /** + * Create a new Collection populated with the results of calling a provided callback on every element in the calling Collection. + * Identical to {@link Array.prototype.map} + * @param {function(value: V, key: K, collection: this): R} cb Callback function. The returned value is added to the new Collection + * @returns {Collection} + * @template K, V + * @template R - is the type of the values the new Collection will contain + */ + map(cb: (value: V, key: K, collection: this) => R): Collection; + /** + * Checks if the Collection has reached its limit and sets the item using {@link Map.prototype.set} + * @param {K} key The key to set + * @param {V} value The value to set + * @param {boolean} force Whether to add the item to the Collection if its limit was reached + * @returns {this} + */ + set(key: K, value: V, force?: boolean): this; + /** + * Returns the matching values for the given keys inside the Collection. + * @param {K[]} keys Array of all keys to look for + * @returns {V[]} + */ + getMany(keys: K[]): (V | undefined)[]; + /** + * Removes the last item from the Collection and returns that item + * Equivalent to Array#pop() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop + * @returns {[K, V] | undefined} + */ + pop(): [K, V] | undefined; + /** + * Removes the first item from a Collection and returns that removed item + * Equivalent to Array#shift() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + * @returns {[K, V] | undefined} + */ + shift(): [K, V] | undefined; + /** + * Tests whether all items in the Collection pass the test implemented by the provided function + * Equivalent to Array#every() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + every(cb: (value: V, key: K, collection: this) => boolean): boolean; + /** + * Tests whether at least one element in the array passes the test implemented by the provided function + * Equivalent to Array#some() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + some(cb: (value: V, key: K, collection: this) => boolean): boolean; + /** + * Returns the value of the item in this collection that satisfies the provided testing callback function + * Equivalent to Array#find() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {V | undefined} + */ + find(cb: (value: V, key: K, collection: this) => boolean): V | undefined; + /** + * @ignore + * @returns {Serializable} + */ + toJSON(): V[]; +} +export default Collection; diff --git a/lib/Collection.js b/lib/Collection.js new file mode 100644 index 000000000..0976331f7 --- /dev/null +++ b/lib/Collection.js @@ -0,0 +1,253 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Collections serve as data holders throughout the library. + * They are a combination of JavaScript Maps and Arrays with the + * ability to hold large amount of data. + * @template K, V + */ +class Collection extends Map { + constructor(entries, limit) { + if (entries) { + super(entries); + } + else { + super(); + } + if (limit) { + this.limit = limit > 0 ? limit : null; + } + } + /** + * Whether the given argument is a Collection + * @param {unknown} collection Data to check if collection + * @returns {boolean} + */ + static isCollection(collection) { + return collection instanceof Collection; + } + /** + * Maps the {@link Collection} values into an array + * @type {V[]} + */ + get toArray() { + return [...this.values()]; + } + /** + * Maps the {@link Collection} keys into an array + * @type {K[]} + */ + get toArrayKeys() { + return [...this.keys()]; + } + /** + * Maps the {@link Collection} entries an array + * @type {[K, V][]} + */ + get toArrayEntries() { + return [...this.entries()]; + } + /** + * Creates a new Collection with all elements that pass the test implemented by the provided callback. + * Identical to {@link Array.prototype.filter} + * @param {function(value: V, key: K, collection: this): boolean} cb Callback function. Return true to keep the element, false otherwise + * @returns {Collection} + */ + filter(cb) { + const collection = new Collection(); + for (const [key, value] of this) { + if (cb(value, key, this)) { + collection.set(key, value); + } + } + return collection; + } + /** + * Get the first value in the {@link Collection} + * @type {V | undefined} + */ + get first() { + if (!this.size) + return undefined; + return this.values().next().value; + } + /** + * Get he first key in the {@link Collection} + * @returns {K} + */ + get firstKey() { + if (!this.size) + return undefined; + return this.keys().next().value; + } + /** + * Get the last value in the {@link Collection} + * @type {V} + */ + get last() { + if (!this.size) + return undefined; + return this.toArray[this.size - 1]; + } + /** + * Get he last key in the {@link Collection} + * @returns {K} + */ + get lastKey() { + if (!this.size) + return undefined; + return this.toArrayKeys[this.size - 1]; + } + /** + * Returns the item if exists or creates a new one and caches it. + * @param {K} key The key of the item + * @param {V} value The value of the item + * @returns {V} + */ + getOrSet(key, value) { + if (this.has(key)) { + return this.get(key); + } + else { + this.set(key, value); + return value; + } + } + /** + * Ö›Merges collection(s) on top of this one. Replaces existing keys by newer Collections + * @param {...(Collection | [K, V][])[]} collections The collection(s) to be merged on top of this one + */ + merge(...collections) { + for (const collection of collections) { + for (const [key, value] of collection) { + this.set(key, value); + } + } + } + /** + * Create a new Collection populated with the results of calling a provided callback on every element in the calling Collection. + * Identical to {@link Array.prototype.map} + * @param {function(value: V, key: K, collection: this): R} cb Callback function. The returned value is added to the new Collection + * @returns {Collection} + * @template K, V + * @template R - is the type of the values the new Collection will contain + */ + map(cb) { + const collection = new Collection(); + for (const [key, value] of this) { + collection.set(key, cb(value, key, this)); + } + return collection; + } + /** + * Checks if the Collection has reached its limit and sets the item using {@link Map.prototype.set} + * @param {K} key The key to set + * @param {V} value The value to set + * @param {boolean} force Whether to add the item to the Collection if its limit was reached + * @returns {this} + */ + set(key, value, force) { + // If the key already exists, a new item won't be added, thus keeping the size at the limit + if (!force && this.limit && this.limit <= this.size && !this.has(key)) { + if (this.firstKey) { + this.delete(this.firstKey); + } + } + return super.set(key, value); + } + /** + * Returns the matching values for the given keys inside the Collection. + * @param {K[]} keys Array of all keys to look for + * @returns {V[]} + */ + getMany(keys) { + const values = []; + for (const key of keys) { + values.push(this.get(key)); + } + return values; + } + /** + * Removes the last item from the Collection and returns that item + * Equivalent to Array#pop() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop + * @returns {[K, V] | undefined} + */ + pop() { + if (!this.lastKey || !this.last) + return; + const popped = [this.lastKey, this.last]; + this.delete(this.lastKey); + return popped; + } + /** + * Removes the first item from a Collection and returns that removed item + * Equivalent to Array#shift() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + * @returns {[K, V] | undefined} + */ + shift() { + if (!this.first || !this.firstKey) + return; + const shifted = [this.firstKey, this.first]; + this.delete(this.firstKey); + return shifted; + } + /** + * Tests whether all items in the Collection pass the test implemented by the provided function + * Equivalent to Array#every() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + every(cb) { + let flag = true; + for (const [key, value] of this) { + const result = cb(value, key, this); + if (!result) { + flag = false; + break; + } + } + return flag; + } + /** + * Tests whether at least one element in the array passes the test implemented by the provided function + * Equivalent to Array#some() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + some(cb) { + let flag = false; + for (const [key, value] of this) { + const result = cb(value, key, this); + if (result) { + flag = true; + break; + } + } + return flag; + } + /** + * Returns the value of the item in this collection that satisfies the provided testing callback function + * Equivalent to Array#find() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {V | undefined} + */ + find(cb) { + let item; + for (const [key, value] of this) { + const result = cb(value, key, this); + if (result) { + item = value; + break; + } + } + return item; + } + /** + * @ignore + * @returns {Serializable} + */ + toJSON() { + return this.toArray; + } +} +exports.default = Collection; diff --git a/lib/api/APIFile.d.ts b/lib/api/APIFile.d.ts new file mode 100644 index 000000000..364f19096 --- /dev/null +++ b/lib/api/APIFile.d.ts @@ -0,0 +1,16 @@ +/// +import { RequestFile } from './rateLimit'; +/** + * Represents a file sent directly to the API + */ +export declare class APIFile { + private readonly type; + readonly path: string; + readonly name: string; + constructor(file: RequestFile); + /** + * Reads the content of this file as a readable stream + * @returns {Promise} + */ + read(): Promise; +} diff --git a/lib/api/APIFile.js b/lib/api/APIFile.js new file mode 100644 index 000000000..5dbaec6f2 --- /dev/null +++ b/lib/api/APIFile.js @@ -0,0 +1,58 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.APIFile = void 0; +const fs_1 = __importDefault(require("fs")); +const node_fetch_1 = __importDefault(require("node-fetch")); +/** + * The type of the API file + */ +var APIFileType; +(function (APIFileType) { + APIFileType[APIFileType["File"] = 0] = "File"; + APIFileType[APIFileType["URL"] = 1] = "URL"; +})(APIFileType || (APIFileType = {})); +/** + * Represents a file sent directly to the API + */ +class APIFile { + constructor(file) { + if (typeof file === 'string') { + this.type = APIFileType.URL; + this.path = file; + this.name = file.substring(file.lastIndexOf('/') + 1); + } + else { + this.type = APIFileType.File; + this.path = file.path; + this.name = file.name; + } + } + /** + * Reads the content of this file as a readable stream + * @returns {Promise} + */ + read() { + return __awaiter(this, void 0, void 0, function* () { + if (this.type === APIFileType.File) { + return fs_1.default.createReadStream(this.path); + } + else { + const { body } = yield node_fetch_1.default(this.path); + return body; + } + }); + } +} +exports.APIFile = APIFile; diff --git a/lib/api/APIRequest.d.ts b/lib/api/APIRequest.d.ts new file mode 100644 index 000000000..b4b54c743 --- /dev/null +++ b/lib/api/APIRequest.d.ts @@ -0,0 +1,52 @@ +import { Response } from 'node-fetch'; +import { APIFile } from './APIFile'; +import { HttpMethod } from './constants'; +import { Params, RequestFile } from './rateLimit'; +/** + * Enum containing common request headers + */ +export declare enum Header { + ContentType = "content-type" +} +/** + * Creates and sends an HTTP request to Discord's API + */ +export declare class APIRequest { + private readonly token; + private readonly endpoint; + private readonly params; + private readonly method; + private readonly files; + constructor(token: string, endpoint: string, params: Params, method: HttpMethod, files?: APIFile[]); + /** + * Sends the API request + * @returns {Promise} + */ + send(): Promise; + /** + * Returns the full URL after adding the parameters if required + * @type {string} + */ + private get url(); + /** + * Returns the body of the request + * @returns {string | Promise | undefined} + */ + private body; + /** + * Returns a form-data body if files need to be sent + * @returns {Promise} + */ + private bodyFiles; + /** + * Returns the headers required for the request + * @type {Record} + */ + private get headers(); + /** + * Parses the given files into {@link APIFile} objects + * @param {RequestFile[]} files The files + * @returns {APIFile[] | undefined} + */ + static parseFiles(files?: RequestFile[]): APIFile[] | undefined; +} diff --git a/lib/api/APIRequest.js b/lib/api/APIRequest.js new file mode 100644 index 000000000..0dc99f460 --- /dev/null +++ b/lib/api/APIRequest.js @@ -0,0 +1,137 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.APIRequest = exports.Header = void 0; +const querystring_1 = __importDefault(require("querystring")); +const form_data_1 = __importDefault(require("form-data")); +const node_fetch_1 = __importDefault(require("node-fetch")); +const APIFile_1 = require("./APIFile"); +const constants_1 = require("./constants"); +const paramsMethods = [constants_1.HttpMethod.Get]; +const bodyMethods = [constants_1.HttpMethod.Post, constants_1.HttpMethod.Patch, constants_1.HttpMethod.Put]; +/** + * Enum containing common request headers + */ +var Header; +(function (Header) { + Header["ContentType"] = "content-type"; +})(Header = exports.Header || (exports.Header = {})); +/** + * Creates and sends an HTTP request to Discord's API + */ +class APIRequest { + constructor(token, endpoint, params, method, files) { + this.token = token; + this.endpoint = endpoint; + this.method = method; + this.files = files; + // Clear all nullish params + if (params) { + if (Array.isArray(params)) { + this.params = params.filter(param => Object.entries(param).every(([, value]) => value !== undefined && value !== null)); + } + else { + this.params = Object.entries(params) + .filter(([, value]) => value !== undefined) + .reduce((params, [key, value]) => (Object.assign(Object.assign({}, params), { [key]: value })), {}); + } + } + } + /** + * Sends the API request + * @returns {Promise} + */ + send() { + return __awaiter(this, void 0, void 0, function* () { + const { url, headers } = this; + const body = yield this.body(); + // Adds the headers from the form-data body + if (body && body instanceof form_data_1.default) { + Object.assign(headers, body.getHeaders()); + } + return node_fetch_1.default(url, { + method: this.method, + body, + headers, + }); + }); + } + /** + * Returns the full URL after adding the parameters if required + * @type {string} + */ + get url() { + let url = `${constants_1.baseURL}${this.endpoint}`; + // Add the parameters to the URL if the HTTP method is included in 'paramsMethods' + if (this.params && paramsMethods.includes(this.method) && Object.keys(this.params).length) { + url += `?${querystring_1.default.encode(this.params)}`; + } + return url; + } + /** + * Returns the body of the request + * @returns {string | Promise | undefined} + */ + body() { + // Only return a body if the HTTP method is included in 'bodyMethods' + if (bodyMethods.includes(this.method) && this.params) { + return this.files ? this.bodyFiles() : JSON.stringify(this.params); + } + } + /** + * Returns a form-data body if files need to be sent + * @returns {Promise} + */ + bodyFiles() { + return __awaiter(this, void 0, void 0, function* () { + const { files, params } = this; + if (!files) + throw new Error('No files found!'); + const body = new form_data_1.default(); + // Adds all the files to the form-data + for (const file of files) { + body.append(file.name, yield file.read(), file.name); + } + // Adds additional params to the 'payload_json' field + if (params) { + body.append('payload_json', JSON.stringify(params)); + } + return body; + }); + } + /** + * Returns the headers required for the request + * @type {Record} + */ + get headers() { + // Default headers required for every request + const headers = { + 'X-RateLimit-Precision': 'millisecond', + Authorization: `Bot ${this.token}`, + }; + // Adds the Content-Type header if the request contains a body + if (this.body() && !this.files) + headers[Header.ContentType] = 'application/json'; + return headers; + } + /** + * Parses the given files into {@link APIFile} objects + * @param {RequestFile[]} files The files + * @returns {APIFile[] | undefined} + */ + static parseFiles(files) { + return files === null || files === void 0 ? void 0 : files.map(file => new APIFile_1.APIFile(file)); + } +} +exports.APIRequest = APIRequest; diff --git a/lib/api/APISerializer.d.ts b/lib/api/APISerializer.d.ts new file mode 100644 index 000000000..a374c2f65 --- /dev/null +++ b/lib/api/APISerializer.d.ts @@ -0,0 +1,185 @@ +import { Positions } from './BotAPI'; +import { Params } from './rateLimit'; +import { FetchGuildOptions, FetchInviteOptions, FetchReactionUsersOptions, FetchSomeMembersOptions, FetchSomeMessagesOptions } from '../controllers'; +import { InviteOptions, FetchGuildsOptions, ModifyBotUserOptions, RoleOptions, CreateWebhookOptions, ModifyWebhookOptions } from '../structures'; +import { CreateGuildChannelOptions, GuildChannelOptions } from '../structures/channels'; +import { Permissible, PermissionOverwriteFlags } from '../structures/flags'; +import { ModifyGuildOptions, PruneCountOptions, PruneOptions, CreateEmojiOptions, ModifyEmojiOptions } from '../structures/guild'; +import { CreateIntegrationOptions, ModifyIntegrationOptions, ModifyWidgetOptions } from '../structures/guild'; +import { MemberBanOptions, ModifyMemberOptions } from '../structures/member'; +import { MessageData } from '../structures/message'; +import { Snowflake } from '../types'; +/** + * Serializes API options and data into the API format + */ +export declare class APISerializer { + /** + * Serializes an array of role IDs and role instances into an array of role IDs + * @param {(Role | Snowflake)[]} roles The roles array + * @returns {Snowflake[]} + */ + private static roleIds; + /** + * Returns the serialized guild channel options for when modifying a guild channel + * @param {GuildChannelOptions} options The guild channel options + * @returns {Params} + */ + static guildChannelOptions(options: GuildChannelOptions): Params; + /** + * Returns the serialized fetch some messages options for when fetching some messages in a text channel + * @param {FetchSomeMessagesOptions} options The fetch some messages options + * @returns {Params} + */ + static fetchSomeMessagesOptions(options?: FetchSomeMessagesOptions): Params; + /** + * Returns the serialized message data for when sending or editing messages + * @param {MessageData} data The message data + * @returns {Params} + */ + static messageData(data: MessageData): Params; + /** + * Returns the serialized fetch reactions options for when fetching all users that reacted with a reaction + * @param {FetchReactionUsersOptions} options The fetch reaction users options + * @returns {Params} + */ + static fetchReactionUsersOptions(options?: FetchReactionUsersOptions): Params; + /** + * Returns the serialized guild channel permissions for when modifying a guild channel's permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The modified permissions + * @returns {Params} + */ + static guildChannelPermissions(permissible: Permissible, flags: PermissionOverwriteFlags): Params; + /** + * Returns the serialized create guild channel options for creating new guild channels + * @param {CreateGuildChannelOptions} options The create guild channel options + * @returns {Params} + */ + static createGuildChannelOptions(options: CreateGuildChannelOptions): Params; + /** + * Returns the serialized positions for when modifying lists positions + * @param {Positions} positions The new positions + * @returns {Params} + */ + static positions(positions: Positions): Params; + /** + * Returns the serialized invite options for when creating a guild channel invite + * @param {InviteOptions} options The invite options + * @returns {Params} + */ + static inviteOptions(options?: InviteOptions): Params; + /** + * Returns the serialized modify emoji options for modifying emojis + * @param {ModifyEmojiOptions} options The modify emoji options + * @returns {Params} + */ + static modifyEmojiOptions(options: ModifyEmojiOptions): Params; + /** + * Returns the serialized create emoji options for when creating emojis + * @param {CreateEmojiOptions} options The create emoji options + * @returns {Promise} + */ + static createEmojiOptions(options: CreateEmojiOptions): Promise; + /** + * Returns the serialized fetch guild options for when fetching a guild + * @param {FetchGuildOptions} options The fetch guild options + * @returns {Params} + */ + static fetchGuildOptions(options?: FetchGuildOptions): Params; + /** + * Returns the serialized modify guild options for when modifying a guild + * @param {ModifyGuildOptions} options The modify guild options + * @returns {Promise} + */ + static modifyGuildOptions(options: ModifyGuildOptions): Promise; + /** + * Returns the serialized fetch all members options for when fetching all members in a guild + * @param {ModifyGuildOptions} options The fetch all members options + * @returns {Params} + */ + static fetchSomeMembersOptions(options?: FetchSomeMembersOptions): Params; + /** + * Returns the serialized modify member options for when modifying guild members + * @param {ModifyMemberOptions} options The modify member options + * @returns {Params} + */ + static modifyMemberOptions(options: ModifyMemberOptions): Params; + /** + * Returns the serialized ban member options for when banning guild members + * @param {MemberBanOptions} options The ban member options + * @returns {Params} + */ + static banMemberOptions(options: MemberBanOptions): Params; + /** + * Returns the serialized role options for when creating or modifying roles + * @param {RoleOptions} options The role options + * @returns {Params} + */ + static roleOptions(options?: RoleOptions): Params; + /** + * Returns the serialized prune count options for when getting a guild prune count + * @param {PruneCountOptions} options The prune count options + * @returns {Params} + */ + static pruneCountOptions(options?: PruneCountOptions): Params; + /** + * Returns the serialized prune options for when beginning a guild prune operation + * @param {PruneOptions} options The prune options + * @returns {Params} + */ + static pruneOptions(options?: PruneOptions): Params; + /** + * Returns the serialized create integration options for when creating new guild integrations + * @param {CreateIntegrationOptions} options The create integration options + * @returns {Params} + */ + static createIntegrationOptions(options: CreateIntegrationOptions): Params; + /** + * Returns the serialized modify integration options for when modifying guild integrations + * @param {ModifyIntegrationOptions} options The modify integration options + * @returns {Params} + */ + static modifyIntegrationOptions(options: ModifyIntegrationOptions): Params; + /** + * Returns the serialized modify widget options for when modifying a guild widget + * @param {ModifyWidgetOptions} options The modify widget options + * @returns {Params} + */ + static modifyWidgetOptions(options: ModifyWidgetOptions): Params; + /** + * Returns the serialized modify bot user options for when modifying this bot's user + * @param {ModifyBotUserOptions} options The modify bot user options + * @returns {Promise} + */ + static modifyBotUserOptions(options: ModifyBotUserOptions): Promise; + /** + * Returns the serialized fetch guilds options for when fetching the guilds the bot's user is in + * @param {FetchGuildsOptions} options The fetch guilds options + * @returns {Params} + */ + static fetchGuildsOptions(options?: FetchGuildsOptions): Params; + /** + * Returns the serialized parameters for when creating a new DM channel + * @param {Snowflake} userId The ID of the DM channel recipient user + * @returns {Params} + */ + static createDM(userId: Snowflake): Params; + /** + * Returns the serialized fetch invite options for when fetching an invite + * @param {FetchInviteOptions} options The fetch invite options + * @returns {Params} + */ + static fetchInviteOptions(options?: FetchInviteOptions): Params; + /** + * Returns the serialized create webhook options for when creating webhooks + * @param {CreateWebhookOptions} options The create webhook options + * @returns {Promise} + */ + static createWebhookOptions(options: CreateWebhookOptions): Promise; + /** + * Returns the serialized modify webhook options for when modifying webhooks + * @param {ModifyWebhookOptions} options The modify webhook options + * @returns {Promise} + */ + static modifyWebhookOptions(options: ModifyWebhookOptions): Promise; +} diff --git a/lib/api/APISerializer.js b/lib/api/APISerializer.js new file mode 100644 index 000000000..92caaff87 --- /dev/null +++ b/lib/api/APISerializer.js @@ -0,0 +1,385 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.APISerializer = void 0; +const structures_1 = require("../structures"); +const message_1 = require("../structures/message"); +/** + * Serializes API options and data into the API format + */ +class APISerializer { + /** + * Serializes an array of role IDs and role instances into an array of role IDs + * @param {(Role | Snowflake)[]} roles The roles array + * @returns {Snowflake[]} + */ + static roleIds(roles) { + return roles.map((role) => (role instanceof structures_1.Role ? role.id : role)); + } + /** + * Returns the serialized guild channel options for when modifying a guild channel + * @param {GuildChannelOptions} options The guild channel options + * @returns {Params} + */ + static guildChannelOptions(options) { + return { + name: options.name, + type: options.type, + topic: options.topic, + nsfw: options.nsfw, + rate_limit_per_user: options.slowModeTimeout, + bitrate: options.bitrate, + user_limit: options.userLimit, + }; + } + /** + * Returns the serialized fetch some messages options for when fetching some messages in a text channel + * @param {FetchSomeMessagesOptions} options The fetch some messages options + * @returns {Params} + */ + static fetchSomeMessagesOptions(options) { + return (options && { + around: options.around, + before: options.before, + after: options.after, + limit: options.limit, + }); + } + /** + * Returns the serialized message data for when sending or editing messages + * @param {MessageData} data The message data + * @returns {Params} + */ + static messageData(data) { + const { content, embed } = data; + return { + content, + embed: embed && + (embed instanceof message_1.MessageEmbed ? embed.structure : message_1.MessageEmbed.dataToStructure(embed)), + }; + } + /** + * Returns the serialized fetch reactions options for when fetching all users that reacted with a reaction + * @param {FetchReactionUsersOptions} options The fetch reaction users options + * @returns {Params} + */ + static fetchReactionUsersOptions(options) { + return options && options; + } + /** + * Returns the serialized guild channel permissions for when modifying a guild channel's permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The modified permissions + * @returns {Params} + */ + static guildChannelPermissions(permissible, flags) { + var _a, _b; + return { + type: permissible.type, + allow: (_a = flags.allow) === null || _a === void 0 ? void 0 : _a.bits, + deny: (_b = flags.deny) === null || _b === void 0 ? void 0 : _b.bits, + }; + } + /** + * Returns the serialized create guild channel options for creating new guild channels + * @param {CreateGuildChannelOptions} options The create guild channel options + * @returns {Params} + */ + static createGuildChannelOptions(options) { + return { + name: options.name, + type: options.type, + topic: options.topic, + bitrate: options.bitrate, + user_limit: options.userLimit, + rate_limit_per_user: options.slowModeTimeout, + position: options.position, + permission_overwrites: options.permissions && + Object.entries(options.permissions).map(([id, overwrite]) => { + var _a, _b; + return ({ + id, + type: overwrite.type, + allow: (_a = overwrite.allow) === null || _a === void 0 ? void 0 : _a.bits, + deny: (_b = overwrite.deny) === null || _b === void 0 ? void 0 : _b.bits, + }); + }), + parent_id: options.parentId, + nsfw: options.nsfw, + }; + } + /** + * Returns the serialized positions for when modifying lists positions + * @param {Positions} positions The new positions + * @returns {Params} + */ + static positions(positions) { + return Object.entries(positions).map(([id, position]) => ({ id, position })); + } + /** + * Returns the serialized invite options for when creating a guild channel invite + * @param {InviteOptions} options The invite options + * @returns {Params} + */ + static inviteOptions(options) { + var _a, _b; + return options + ? { + max_age: (_a = options.max) === null || _a === void 0 ? void 0 : _a.age, + max_uses: (_b = options.max) === null || _b === void 0 ? void 0 : _b.uses, + temporary: options.temporary, + unique: options.unique, + } + : {}; + } + /** + * Returns the serialized modify emoji options for modifying emojis + * @param {ModifyEmojiOptions} options The modify emoji options + * @returns {Params} + */ + static modifyEmojiOptions(options) { + return { + name: options.name, + // Serialize the role IDs + roles: options.roles && APISerializer.roleIds(options.roles), + }; + } + /** + * Returns the serialized create emoji options for when creating emojis + * @param {CreateEmojiOptions} options The create emoji options + * @returns {Promise} + */ + static createEmojiOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + image: yield options.image.stringify(), + roles: options.roles, + }; + }); + } + /** + * Returns the serialized fetch guild options for when fetching a guild + * @param {FetchGuildOptions} options The fetch guild options + * @returns {Params} + */ + static fetchGuildOptions(options) { + return (options && { + with_counts: options.withCounts, + }); + } + /** + * Returns the serialized modify guild options for when modifying a guild + * @param {ModifyGuildOptions} options The modify guild options + * @returns {Promise} + */ + static modifyGuildOptions(options) { + var _a, _b, _c, _d, _e, _f; + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + region: options.region, + verification_level: (_a = options.levels) === null || _a === void 0 ? void 0 : _a.verification, + default_message_notifications: (_b = options.levels) === null || _b === void 0 ? void 0 : _b.notifications, + explicit_content_filter: (_c = options.levels) === null || _c === void 0 ? void 0 : _c.explicitContent, + afk_channel_id: (_e = (_d = options.afk) === null || _d === void 0 ? void 0 : _d.channel) === null || _e === void 0 ? void 0 : _e.id, + afk_timeout: (_f = options.afk) === null || _f === void 0 ? void 0 : _f.timeout, + icon: options.icon && (yield options.icon.stringify()), + owner_id: options.ownerId, + splash: options.splash && (yield options.splash.stringify()), + banner: options.banner && (yield options.banner.stringify()), + system_channel_id: options.systemChannelId, + rules_channel_id: options.rulesChannelId, + public_updates_channel_id: options.updatesChannelId, + preferred_locale: options.locale, + }; + }); + } + /** + * Returns the serialized fetch all members options for when fetching all members in a guild + * @param {ModifyGuildOptions} options The fetch all members options + * @returns {Params} + */ + static fetchSomeMembersOptions(options) { + return (options && { + limit: options.limit, + after: options.after, + }); + } + /** + * Returns the serialized modify member options for when modifying guild members + * @param {ModifyMemberOptions} options The modify member options + * @returns {Params} + */ + static modifyMemberOptions(options) { + return { + nick: options.nick, + // Serialize the role IDs + roles: options.roles && APISerializer.roleIds(options.roles), + mute: options.mute, + deaf: options.deaf, + channel_id: options.channelId, + }; + } + /** + * Returns the serialized ban member options for when banning guild members + * @param {MemberBanOptions} options The ban member options + * @returns {Params} + */ + static banMemberOptions(options) { + return { + reason: options.reason, + delete_message_days: options.deleteMessageDays, + }; + } + /** + * Returns the serialized role options for when creating or modifying roles + * @param {RoleOptions} options The role options + * @returns {Params} + */ + static roleOptions(options) { + var _a; + return (options && { + name: options.name, + permissions: (_a = options.permissions) === null || _a === void 0 ? void 0 : _a.bits, + color: options.color, + hoist: options.listedSeparately, + mentionable: options.mentionable, + }); + } + /** + * Returns the serialized prune count options for when getting a guild prune count + * @param {PruneCountOptions} options The prune count options + * @returns {Params} + */ + static pruneCountOptions(options) { + return (options && { + days: options.days, + include_roles: options.includeRoles, + }); + } + /** + * Returns the serialized prune options for when beginning a guild prune operation + * @param {PruneOptions} options The prune options + * @returns {Params} + */ + static pruneOptions(options) { + return (options && Object.assign(Object.assign({}, APISerializer.pruneCountOptions(options)), { compute_prune_count: options.computePruneCount })); + } + /** + * Returns the serialized create integration options for when creating new guild integrations + * @param {CreateIntegrationOptions} options The create integration options + * @returns {Params} + */ + static createIntegrationOptions(options) { + return { + type: options.type, + id: options.id, + }; + } + /** + * Returns the serialized modify integration options for when modifying guild integrations + * @param {ModifyIntegrationOptions} options The modify integration options + * @returns {Params} + */ + static modifyIntegrationOptions(options) { + var _a, _b; + return { + expire_behavior: (_a = options.expire) === null || _a === void 0 ? void 0 : _a.behavior, + expire_grace_period: (_b = options.expire) === null || _b === void 0 ? void 0 : _b.gracePeriod, + enable_emoticons: options.enableEmoticons, + }; + } + /** + * Returns the serialized modify widget options for when modifying a guild widget + * @param {ModifyWidgetOptions} options The modify widget options + * @returns {Params} + */ + static modifyWidgetOptions(options) { + return { + enabled: options.enabled, + channel_id: options.channelId, + }; + } + /** + * Returns the serialized modify bot user options for when modifying this bot's user + * @param {ModifyBotUserOptions} options The modify bot user options + * @returns {Promise} + */ + static modifyBotUserOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + username: options.username, + avatar: options.avatar && (yield options.avatar.stringify()), + }; + }); + } + /** + * Returns the serialized fetch guilds options for when fetching the guilds the bot's user is in + * @param {FetchGuildsOptions} options The fetch guilds options + * @returns {Params} + */ + static fetchGuildsOptions(options) { + return (options && { + before: options.before, + after: options.after, + limit: options.limit, + }); + } + /** + * Returns the serialized parameters for when creating a new DM channel + * @param {Snowflake} userId The ID of the DM channel recipient user + * @returns {Params} + */ + static createDM(userId) { + return { + recipient_id: userId, + }; + } + /** + * Returns the serialized fetch invite options for when fetching an invite + * @param {FetchInviteOptions} options The fetch invite options + * @returns {Params} + */ + static fetchInviteOptions(options) { + return (options && { + with_counts: options.withCounts, + }); + } + /** + * Returns the serialized create webhook options for when creating webhooks + * @param {CreateWebhookOptions} options The create webhook options + * @returns {Promise} + */ + static createWebhookOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + avatar: options.avatar && (yield options.avatar.stringify()), + }; + }); + } + /** + * Returns the serialized modify webhook options for when modifying webhooks + * @param {ModifyWebhookOptions} options The modify webhook options + * @returns {Promise} + */ + static modifyWebhookOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + avatar: options.avatar && (yield options.avatar.stringify()), + channel_id: options.channelId, + }; + }); + } +} +exports.APISerializer = APISerializer; diff --git a/lib/api/BotAPI.d.ts b/lib/api/BotAPI.d.ts new file mode 100644 index 000000000..082e99f79 --- /dev/null +++ b/lib/api/BotAPI.d.ts @@ -0,0 +1,644 @@ +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { FetchGuildOptions, FetchInviteOptions, FetchReactionUsersOptions, FetchSomeMembersOptions, FetchSomeMessagesOptions } from '../controllers'; +import { EmojiResolvable, BotUser, FetchGuildsOptions, ModifyBotUserOptions, PartialGuild, Invite, InviteOptions, PermissionOverwrite, Role, RoleOptions, User, CreateWebhookOptions, Webhook, ModifyWebhookOptions } from '../structures'; +import { Channel, CreateGuildChannelOptions, DMChannel, GuildChannel, GuildChannelOptions } from '../structures/channels'; +import { Permissible, PermissionOverwriteFlags } from '../structures/flags'; +import { CreateEmojiOptions, Guild, GuildEmoji, GuildPreview, GuildVanityInvite, ModifyEmojiOptions, ModifyGuildOptions, PruneCountOptions, PruneOptions } from '../structures/guild'; +import { GuildBan, CreateIntegrationOptions, GuildIntegration, ModifyIntegrationOptions, GuildWidget, ModifyWidgetOptions } from '../structures/guild'; +import { Member, MemberBanOptions, ModifyMemberOptions } from '../structures/member'; +import { Message, MessageData, MessageEditData, MessageEmbed, MessageOptions } from '../structures/message'; +import { Snowflake } from '../types'; +/** + * New positions for a orderly listed values on Discord, such as guild channels or roles + * The key is the item's ID. + * The value is the item's new position. + * + * The positions are in a descending order ending at 0 + * @example + * // Guild channels positions + * { '702476896008405005': 1, '702476896008405005': 2 } + * @example + * // Roles positions + * { '706861476752785461': 2 } + */ +export declare type Positions = Record; +/** + * Creates all outgoing API requests + */ +export declare class BotAPI { + /** + * The bot instance + */ + private readonly bot; + /** + * The bot's token + */ + private readonly token; + /** + * Manages all outgoing API requests + */ + private readonly requests; + constructor(bot: Bot, token: string); + /** + * Fetches a channel by its ID + * @param {Snowflake} channelId The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetchChannel(channelId: Snowflake): Promise; + /** + * Fetches a guild channel by its ID + * @param {Snowflake} channelId The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetchGuildChannel(channelId: Snowflake): Promise; + /** + * Fetches a DM channel by its ID + * @param {Snowflake} channelId The ID of the DM channel you wish to fetch + * @returns {Promise} + */ + fetchDMChannel(channelId: Snowflake): Promise; + /** + * Updates a {@link GuildChannel}'s settings. Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the modified channel + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modifyGuildChannel(channelId: Snowflake, options: GuildChannelOptions): Promise; + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise} + */ + deleteChannel(channelId: Snowflake): Promise; + /** + * Deletes a {@link GuildChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the guild channel you wish to delete + * @returns {Promise} + */ + deleteGuildChannel(channelId: Snowflake): Promise; + /** + * Fetches some messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMessages(channelId: Snowflake, options?: FetchSomeMessagesOptions): Promise>; + /** + * Fetches a message in a text channel by their IDs + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message you wish to fetch + * @returns {Promise} + */ + fetchMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. + * If operating on a {@link GuildTextChannel}, this requires the {@link Permission.SendMessages} permission. + * If the {@link MessageOptions.tts} field is set to true, the {@link Permission.SendTTSMessages} permission is required + * @param {Snowflake} channelId The ID of the channel to send the message in + * @param {string | MessageData | MessageEmbed} data The message data. + * Can be: + * 1. Raw content to be sent as a message + * @example ```typescript + * channel.sendMessage('Hello World!'); + * ``` + * 2. A {@link MessageData} object, containing content and/or embed + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', embed: { title: 'My Embed!' } }); + * ``` + * 3. A {@link MessageEmbed} instance + * @param {MessageOptions} options The message's options + * @returns {Promise} + */ + sendMessage(channelId: Snowflake, data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** + * Creates a reaction for a message. This method requires the {@link Permission.ReadMessageHistory} permission to be present on the Bot. Additionally, if nobody else has reacted to the message using this emoji, this method requires the {@link Permission.AddReactions} permission to be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {string} emoji The emoji to react with to the message + * @returns {Promise} + */ + addMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: EmojiResolvable): Promise; + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {EmojiResolvable} emoji The emoji to delete from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + removeMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: EmojiResolvable, userId?: Snowflake): Promise; + /** + * Fetches a list of users that reacted with a particular emoji on a message + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message + * @param {string} emoji The emoji the users reacted with + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise>} + */ + fetchReactionUsers(channelId: Snowflake, messageId: Snowflake, emoji: string, options?: FetchReactionUsersOptions): Promise>; + /** + * Removes all reactions on a message. This method requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions + * @returns {Promise} + */ + removeMessageReactions(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Deletes all reactions for an emoji. This method requires the {@link Permission.ManageMessages} permission ot be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions for a given emoji + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + removeMessageReactionsEmoji(channelId: Snowflake, messageId: Snowflake, emoji: EmojiResolvable): Promise; + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to edit + * @param {Snowflake} messageId The ID of the message you wish to edit + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + editMessage(channelId: Snowflake, messageId: Snowflake, data: string | MessageEditData): Promise; + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to delete + * @param {Snowflake} messageId The ID of the message you wish to delete + * @returns {Promise} + */ + deleteMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The channel ID that contains the messages you wish to delete + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(channelId: Snowflake, messages: Snowflake[]): Promise; + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel for which to overwrite the permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions you wish to modify + * @returns {Promise} + */ + modifyGuildChannelPermissions(channelId: Snowflake, permissible: Permissible, flags: PermissionOverwriteFlags): Promise; + /** + * Fetches a list of invites for a channel. + * Requires the {@link Permission.ManageChannels} permission + * @param {Snowflake} channelId The ID of the channel to fetch invites in + * @returns {Promise>} + */ + fetchChannelInvites(channelId: Snowflake): Promise>; + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {Snowflake} channelId The ID of the channel to create the invite for + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + createChannelInvite(channelId: Snowflake, options?: InviteOptions): Promise; + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel that contains the permission overwrite you wish to delete + * @param {Snowflake} permissible The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + deleteGuildChannelPermission(channelId: Snowflake, permissible: Snowflake): Promise; + /** + * Posts a typing indicator for a specified text channel. + * Useful when the bot is responding to a command and expects the computation to take a few seconds. + * This method may be called to let the user know that the bot is processing their message. + * @param {Snowflake} channelId The ID of the text channel to trigger typing in + * @returns {Promise} + */ + triggerTextChannelTyping(channelId: Snowflake): Promise; + /** + * Fetches all pinned messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise>} + */ + fetchChannelPins(channelId: Snowflake): Promise>; + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to pin + * @param {Snowflake} messageId The ID of the message you wish to pin + * @returns {Promise} + */ + pinMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to unpin + * @param {Snowflake} messageId The ID of the message you wish to unpin + * @returns {Promise} + */ + unpinMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Fetches all emojis in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildEmojis(guildId: Snowflake): Promise>; + /** + * Fetches an emoji in a given guild + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @returns {Promise} + */ + fetchGuildEmoji(guildId: Snowflake, emojiId: Snowflake): Promise; + /** + * Creates a new emoji for a guild. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateEmojiOptions} options The options for the new emoji + * @returns {Promise} + */ + createGuildEmoji(guildId: Snowflake, options: CreateEmojiOptions): Promise; + /** + * Modifies a given guild emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} The updated emoji + */ + modifyGuildEmoji(guildId: Snowflake, emojiId: Snowflake, options: ModifyEmojiOptions): Promise; + /** + * Deletes a given guild emoji + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji to delete + * @returns {Promise} + */ + deleteGuildEmoji(guildId: Snowflake, emojiId: Snowflake): Promise; + /** + * Fetches a guild by its ID and additional options + * @param {Snowflake} guildId The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetchGuild(guildId: Snowflake, options?: FetchGuildOptions): Promise; + /** + * Fetches a guild preview by its guild ID. + * This is only available for public guilds + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildPreview(guildId: Snowflake): Promise; + /** + * Modifies a guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modifyGuild(guildId: Snowflake, options: ModifyGuildOptions): Promise; + /** + * Fetches all guild channels in a given guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildChannels(guildId: Snowflake): Promise>; + /** + * Creates a new guild channel in a guild. + * Requires the {@link Permission.ManageChannels} + * @param {Snowflake} guildId The ID of the guild to create the channel in + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + createGuildChannel(guildId: Snowflake, options: CreateGuildChannelOptions): Promise; + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyGuildChannelsPositions(guildId: Snowflake, positions: Positions): Promise; + /** + * Fetches a guild member by its user ID + * @param {Snowflake} guildId The ID of the guild this member is in + * @param {Snowflake} userId The ID of the member user + * @returns {Promise} + */ + fetchMember(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Fetches all members in a guild + * @param {Snowflake} guildId The ID of the guild + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMembers(guildId: Snowflake, options?: FetchSomeMembersOptions): Promise>; + /** + * Modifies attributes of a guild member + * @param {Snowflake} guildId The ID of the guild that contains this member + * @param {Snowflake} userId The ID of the member user + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modifyMember(guildId: Snowflake, userId: Snowflake, options: ModifyMemberOptions): Promise; + /** + * Modify a guild member's nickname. + * Returns the modified nickname when changing this bot's nickname or void when changing another member's nickname + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyMemberNickname(guildId: Snowflake, userId: Snowflake, nick?: string): Promise; + /** + * Modifies the nickname of the bot user in a guild. + * Returns the modified nickname + * @param {Snowflake} guildId The ID of the guild + * @param {string} nick The new nickname for the bot + * @returns {Promise} + */ + modifyBotNickname(guildId: Snowflake, nick?: string): Promise; + /** + * Adds a role to a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberAddRole(guildId: Snowflake, userId: Snowflake, roleId: Snowflake): Promise; + /** + * Removes a role from a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberRemoveRole(guildId: Snowflake, userId: Snowflake, roleId: Snowflake): Promise; + /** + * Removes a member from a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to remove + * @returns {Promise} + */ + removeMember(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Fetches all bans in a guild by a guild ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildBans(guildId: Snowflake): Promise>; + /** + * Fetches a ban in a guild by a user ID + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + fetchGuildBan(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Bans a member from a guild, and optionally deletes the previous messages sent by the banner member. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + banMember(guildId: Snowflake, userId: Snowflake, options: MemberBanOptions): Promise; + /** + * Unbans a member from a guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to unban + * @returns {Promise} + */ + unbanMember(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Fetches all roles in a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchRoles(guildId: Snowflake): Promise>; + /** + * Creates a new role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {RoleOptions} options The options for the created role + * @returns {Promise} + */ + createRole(guildId: Snowflake, options?: RoleOptions): Promise; + /** + * Modifies the positions of a set of roles for a guild. + * Requires the {@link Permission.ManageRoles} + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyRolesPositions(guildId: Snowflake, positions: Positions): Promise>; + /** + * Modifies a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @param {RoleOptions} options The options for the updated role + * @returns {Promise} The updated role + */ + modifyRole(guildId: Snowflake, roleId: Snowflake, options: RoleOptions): Promise; + /** + * Deletes a role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + deleteRole(guildId: Snowflake, roleId: Snowflake): Promise; + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {Snowflake} guildId The Id of the guild + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + guildPruneCount(guildId: Snowflake, options?: PruneCountOptions): Promise; + /** + * Begins a prune operation on a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + guildPrune(guildId: Snowflake, options?: PruneOptions): Promise; + /** + * Fetches all invites (with metadata) in a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildInvites(guildId: Snowflake): Promise>; + /** + * Fetches all guild integrations in a guild + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildIntegrations(guildId: Snowflake): Promise>; + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + createGuildIntegration(guildId: Snowflake, options: CreateIntegrationOptions): Promise; + /** + * Modifies the behavior and settings of a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the integration + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modifyGuildIntegration(guildId: Snowflake, integrationId: Snowflake, options: ModifyIntegrationOptions): Promise; + /** + * Deletes the attached integration for a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + deleteGuildIntegration(guildId: Snowflake, integrationId: Snowflake): Promise; + /** + * Syncs a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + syncGuildIntegration(guildId: Snowflake, integrationId: Snowflake): Promise; + /** + * Fetches a guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildWidget(guildId: Snowflake): Promise; + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modifyGuildWidget(guildId: Snowflake, options: ModifyWidgetOptions): Promise; + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildVanityURL(guildId: Snowflake): Promise; + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBotUser(): Promise; + /** + * Fetches a user by its ID + * @param {Snowflake} userId The user ID + * @returns {Promise} + */ + fetchUser(userId: Snowflake): Promise; + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modifyBotUser(options: ModifyBotUserOptions): Promise; + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchBotGuilds(options?: FetchGuildsOptions): Promise>; + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId: Snowflake): Promise; + /** + * Creates a new DM channel between a user and the bot user + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + createDM(userId: Snowflake): Promise; + /** + * Fetches an invite by its invite code + * @param {string} inviteCode The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetchInvite(inviteCode: string, options?: FetchInviteOptions): Promise; + /** + * Deletes an invite by its invite code. + * Requires the {@link Permission.ManageChannels} permission on the channel this invite belongs to, or {@link Permission.ManageGuild} to remove any invite across the guild + * @param {string} inviteCode The invite code + * @returns {Promise} + */ + deleteInvite(inviteCode: string): Promise; + /** + * Creates a new webhook for a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + createWebhook(channelId: Snowflake, options: CreateWebhookOptions): Promise; + /** + * Fetches all webhooks in a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @returns {Promise>} + */ + fetchWebhooks(channelId: Snowflake): Promise>; + /** + * Fetches all webhooks in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildWebhooks(guildId: Snowflake): Promise>; + /** + * Fetches a webhook by its ID + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + fetchWebhook(webhookId: Snowflake): Promise; + /** + * Modifies a webhook by its ID + * @param {Snowflake} webhookId The webhook ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modifyWebhook(webhookId: Snowflake, options: ModifyWebhookOptions): Promise; + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + deleteWebhook(webhookId: Snowflake): Promise; +} diff --git a/lib/api/BotAPI.js b/lib/api/BotAPI.js new file mode 100644 index 000000000..57a82d3ed --- /dev/null +++ b/lib/api/BotAPI.js @@ -0,0 +1,1118 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotAPI = void 0; +const APISerializer_1 = require("./APISerializer"); +const constants_1 = require("./constants"); +const endpoints_1 = require("./endpoints"); +const rateLimit_1 = require("./rateLimit"); +const Collection_1 = __importDefault(require("../Collection")); +const structures_1 = require("../structures"); +const channels_1 = require("../structures/channels"); +const utils_1 = require("../structures/channels/utils"); +const flags_1 = require("../structures/flags"); +const guild_1 = require("../structures/guild"); +const guild_2 = require("../structures/guild"); +const member_1 = require("../structures/member"); +const message_1 = require("../structures/message"); +/** + * Creates all outgoing API requests + */ +class BotAPI { + constructor(bot, token) { + this.bot = bot; + this.token = token; + this.requests = new rateLimit_1.Requests(this.bot, this.token); + } + /** + * Fetches a channel by its ID + * @param {Snowflake} channelId The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetchChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Get); + return new channels_1.Channel(this.bot, channel); + }); + } + /** + * Fetches a guild channel by its ID + * @param {Snowflake} channelId The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetchGuildChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.fetchChannel(channelId); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channel.structure, yield utils_1.ChannelUtils.getChannelGuild(this.bot, channel)); + }); + } + /** + * Fetches a DM channel by its ID + * @param {Snowflake} channelId The ID of the DM channel you wish to fetch + * @returns {Promise} + */ + fetchDMChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.fetchChannel(channelId); + return utils_1.ChannelUtils.createDMChannel(this.bot, channel.structure); + }); + } + /** + * Updates a {@link GuildChannel}'s settings. Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the modified channel + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modifyGuildChannel(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const channelData = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.guildChannelOptions(options)); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channelData, yield utils_1.ChannelUtils.getChannelGuild(this.bot, channelData)); + }); + } + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise} + */ + deleteChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channelData = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Delete); + return utils_1.ChannelUtils.create(this.bot, channelData); + }); + } + /** + * Deletes a {@link GuildChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the guild channel you wish to delete + * @returns {Promise} + */ + deleteGuildChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.deleteChannel(channelId); + if (!(channel instanceof channels_1.GuildChannel)) { + throw new TypeError('The deleted channel is not a guild channel'); + } + return channel; + }); + } + /** + * Fetches some messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMessages(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const messages = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMessagesOptions(options))); + const channel = yield this.bot.channels.getText(channelId); + return new Collection_1.default(messages.map(message => [message.id, new message_1.Message(this.bot, message, channel)])); + }); + } + /** + * Fetches a message in a text channel by their IDs + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message you wish to fetch + * @returns {Promise} + */ + fetchMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + const message = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Get); + const channel = yield this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, message, channel); + }); + } + /** + * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. + * If operating on a {@link GuildTextChannel}, this requires the {@link Permission.SendMessages} permission. + * If the {@link MessageOptions.tts} field is set to true, the {@link Permission.SendTTSMessages} permission is required + * @param {Snowflake} channelId The ID of the channel to send the message in + * @param {string | MessageData | MessageEmbed} data The message data. + * Can be: + * 1. Raw content to be sent as a message + * @example ```typescript + * channel.sendMessage('Hello World!'); + * ``` + * 2. A {@link MessageData} object, containing content and/or embed + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', embed: { title: 'My Embed!' } }); + * ``` + * 3. A {@link MessageEmbed} instance + * @param {MessageOptions} options The message's options + * @returns {Promise} + */ + sendMessage(channelId, data, options) { + return __awaiter(this, void 0, void 0, function* () { + // Default params to be sent in the request + let params = Object.assign({}, options); + let files; + if (typeof data === 'string') { + // The params should only include the raw content + params['content'] = data; + } + else if (data instanceof message_1.MessageEmbed) { + // The params should only include the given embed structure + params['embed'] = data.structure; + } + else { + // The params should include all given data fields + params = yield APISerializer_1.APISerializer.messageData(data); + files = data.files; + } + const messageData = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Post, params, files); + const channel = yield this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, messageData, channel); + }); + } + /** + * Creates a reaction for a message. This method requires the {@link Permission.ReadMessageHistory} permission to be present on the Bot. Additionally, if nobody else has reacted to the message using this emoji, this method requires the {@link Permission.AddReactions} permission to be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {string} emoji The emoji to react with to the message + * @returns {Promise} + */ + addMessageReaction(channelId, messageId, emoji) { + return __awaiter(this, void 0, void 0, function* () { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for addMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); + } + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { channelId, messageId, emoji: encodeURI(identifier) }, constants_1.HttpMethod.Put); + }); + } + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {EmojiResolvable} emoji The emoji to delete from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + removeMessageReaction(channelId, messageId, emoji, userId = '@me') { + return __awaiter(this, void 0, void 0, function* () { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for removeMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji}) user ${userId}`); + } + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { + channelId, + messageId, + emoji: encodeURI(identifier), + userId, + }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches a list of users that reacted with a particular emoji on a message + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message + * @param {string} emoji The emoji the users reacted with + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise>} + */ + fetchReactionUsers(channelId, messageId, emoji, options) { + return __awaiter(this, void 0, void 0, function* () { + const users = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { + channelId, + messageId, + emoji, + }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchReactionUsersOptions(options))); + return new Collection_1.default(users.map(user => [user.id, new structures_1.User(this.bot, user)])); + }); + } + /** + * Removes all reactions on a message. This method requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions + * @returns {Promise} + */ + removeMessageReactions(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactions, { + channelId, + messageId, + }, constants_1.HttpMethod.Delete); + }); + } + /** + * Deletes all reactions for an emoji. This method requires the {@link Permission.ManageMessages} permission ot be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions for a given emoji + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + removeMessageReactionsEmoji(channelId, messageId, emoji) { + return __awaiter(this, void 0, void 0, function* () { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for removeMessageReactionsEmoji request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); + } + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { + channelId, + messageId, + emoji: encodeURI(identifier), + }, constants_1.HttpMethod.Delete); + }); + } + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to edit + * @param {Snowflake} messageId The ID of the message you wish to edit + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + editMessage(channelId, messageId, data) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const params = {}; + if (typeof data === 'string') { + // The given data is the new message content + params['content'] = data; + } + else { + // The given data should be passed to the endpoint + Object.assign(params, Object.assign(Object.assign({}, APISerializer_1.APISerializer.messageData(data)), { flags: (_a = data.flags) === null || _a === void 0 ? void 0 : _a.bits })); + } + const messageData = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Patch, params); + const channel = yield this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, messageData, channel); + }); + } + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to delete + * @param {Snowflake} messageId The ID of the message you wish to delete + * @returns {Promise} + */ + deleteMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The channel ID that contains the messages you wish to delete + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(channelId, messages) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesBulkDelete, { channelId }, constants_1.HttpMethod.Post, { + messages, + }); + }); + } + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel for which to overwrite the permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions you wish to modify + * @returns {Promise} + */ + modifyGuildChannelPermissions(channelId, permissible, flags) { + return __awaiter(this, void 0, void 0, function* () { + const params = APISerializer_1.APISerializer.guildChannelPermissions(permissible, flags); + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible.id }, constants_1.HttpMethod.Put, params); + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.PermissionOverwrite(this.bot, Object.assign(Object.assign({}, params), { id: permissible.id }), channel); + }); + } + /** + * Fetches a list of invites for a channel. + * Requires the {@link Permission.ManageChannels} permission + * @param {Snowflake} channelId The ID of the channel to fetch invites in + * @returns {Promise>} + */ + fetchChannelInvites(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const invites = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Get)); + return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite)])); + }); + } + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {Snowflake} channelId The ID of the channel to create the invite for + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + createChannelInvite(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.inviteOptions(options)); + return new structures_1.Invite(this.bot, invite); + }); + } + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel that contains the permission overwrite you wish to delete + * @param {Snowflake} permissible The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + deleteGuildChannelPermission(channelId, permissible) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible }, constants_1.HttpMethod.Delete); + }); + } + /** + * Posts a typing indicator for a specified text channel. + * Useful when the bot is responding to a command and expects the computation to take a few seconds. + * This method may be called to let the user know that the bot is processing their message. + * @param {Snowflake} channelId The ID of the text channel to trigger typing in + * @returns {Promise} + */ + triggerTextChannelTyping(channelId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelTyping, { channelId }, constants_1.HttpMethod.Post); + }); + } + /** + * Fetches all pinned messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise>} + */ + fetchChannelPins(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const pins = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelPins, { channelId }, constants_1.HttpMethod.Get)); + const channel = yield this.bot.channels.getText(channelId); + return new Collection_1.default(pins.map(pin => [pin.id, new message_1.Message(this.bot, pin, channel)])); + }); + } + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to pin + * @param {Snowflake} messageId The ID of the message you wish to pin + * @returns {Promise} + */ + pinMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Put); + }); + } + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to unpin + * @param {Snowflake} messageId The ID of the message you wish to unpin + * @returns {Promise} + */ + unpinMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches all emojis in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildEmojis(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const emojis = (yield this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Get)); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(emojis.map(emoji => [emoji.id, new guild_1.GuildEmoji(this.bot, emoji, guild)])); + }); + } + /** + * Fetches an emoji in a given guild + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @returns {Promise} + */ + fetchGuildEmoji(guildId, emojiId) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); + }); + } + /** + * Creates a new emoji for a guild. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateEmojiOptions} options The options for the new emoji + * @returns {Promise} + */ + createGuildEmoji(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Post, yield APISerializer_1.APISerializer.createEmojiOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); + }); + } + /** + * Modifies a given guild emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} The updated emoji + */ + modifyGuildEmoji(guildId, emojiId, options) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyEmojiOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); + }); + } + /** + * Deletes a given guild emoji + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji to delete + * @returns {Promise} + */ + deleteGuildEmoji(guildId, emojiId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches a guild by its ID and additional options + * @param {Snowflake} guildId The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetchGuild(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const guild = yield this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildOptions(options)); + return new guild_1.Guild(this.bot, guild); + }); + } + /** + * Fetches a guild preview by its guild ID. + * This is only available for public guilds + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildPreview(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const preview = yield this.requests.send(endpoints_1.EndpointRoute.GuildPreview, { guildId }, constants_1.HttpMethod.Get); + return new guild_1.GuildPreview(this.bot, preview); + }); + } + /** + * Modifies a guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modifyGuild(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const guild = yield this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyGuildOptions(options)); + return new guild_1.Guild(this.bot, guild); + }); + } + /** + * Fetches all guild channels in a given guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildChannels(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const channels = (yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Get)); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(channels.map(channel => [ + channel.id, + utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild), + ])); + }); + } + /** + * Creates a new guild channel in a guild. + * Requires the {@link Permission.ManageChannels} + * @param {Snowflake} guildId The ID of the guild to create the channel in + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + createGuildChannel(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createGuildChannelOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild); + }); + } + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyGuildChannelsPositions(guildId, positions) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); + }); + } + /** + * Fetches a guild member by its user ID + * @param {Snowflake} guildId The ID of the guild this member is in + * @param {Snowflake} userId The ID of the member user + * @returns {Promise} + */ + fetchMember(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const member = yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new member_1.Member(this.bot, member, guild); + }); + } + /** + * Fetches all members in a guild + * @param {Snowflake} guildId The ID of the guild + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMembers(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const members = (yield this.requests.send(endpoints_1.EndpointRoute.GuildMembers, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMembersOptions(options))); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(members.map(member => [member.user.id, new member_1.Member(this.bot, member, guild)])); + }); + } + /** + * Modifies attributes of a guild member + * @param {Snowflake} guildId The ID of the guild that contains this member + * @param {Snowflake} userId The ID of the member user + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modifyMember(guildId, userId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyMemberOptions(options)); + }); + } + /** + * Modify a guild member's nickname. + * Returns the modified nickname when changing this bot's nickname or void when changing another member's nickname + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyMemberNickname(guildId, userId, nick) { + var _a; + if (userId === ((_a = this.bot.user) === null || _a === void 0 ? void 0 : _a.id)) { + return this.modifyBotNickname(guildId, nick); + } + else { + return this.modifyMember(guildId, userId, { nick }); + } + } + /** + * Modifies the nickname of the bot user in a guild. + * Returns the modified nickname + * @param {Snowflake} guildId The ID of the guild + * @param {string} nick The new nickname for the bot + * @returns {Promise} + */ + modifyBotNickname(guildId, nick) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberBotNick, { guildId }, constants_1.HttpMethod.Patch, { + nick, + }); + }); + } + /** + * Adds a role to a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberAddRole(guildId, userId, roleId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Put); + }); + } + /** + * Removes a role from a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberRemoveRole(guildId, userId, roleId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Removes a member from a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to remove + * @returns {Promise} + */ + removeMember(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches all bans in a guild by a guild ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildBans(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const bans = yield this.requests.send(endpoints_1.EndpointRoute.GuildBans, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(bans.map(ban => [ban.user.id, new guild_2.GuildBan(this.bot, ban, guild)])); + }); + } + /** + * Fetches a ban in a guild by a user ID + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + fetchGuildBan(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const ban = yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new guild_2.GuildBan(this.bot, ban, guild); + }); + } + /** + * Bans a member from a guild, and optionally deletes the previous messages sent by the banner member. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + banMember(guildId, userId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Put, APISerializer_1.APISerializer.banMemberOptions(options)); + }); + } + /** + * Unbans a member from a guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to unban + * @returns {Promise} + */ + unbanMember(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches all roles in a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchRoles(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const roles = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); + }); + } + /** + * Creates a new role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {RoleOptions} options The options for the created role + * @returns {Promise} + */ + createRole(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const role = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.roleOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new structures_1.Role(this.bot, role, guild); + }); + } + /** + * Modifies the positions of a set of roles for a guild. + * Requires the {@link Permission.ManageRoles} + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyRolesPositions(guildId, positions) { + return __awaiter(this, void 0, void 0, function* () { + const roles = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); + }); + } + /** + * Modifies a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @param {RoleOptions} options The options for the updated role + * @returns {Promise} The updated role + */ + modifyRole(guildId, roleId, options) { + return __awaiter(this, void 0, void 0, function* () { + const role = yield this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.roleOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new structures_1.Role(this.bot, role, guild); + }); + } + /** + * Deletes a role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + deleteRole(guildId, roleId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {Snowflake} guildId The Id of the guild + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + guildPruneCount(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const { pruned } = yield this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.pruneCountOptions(options)); + return pruned; + }); + } + /** + * Begins a prune operation on a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + guildPrune(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const { pruned } = yield this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.pruneOptions(options)); + return pruned; + }); + } + /** + * Fetches all invites (with metadata) in a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildInvites(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const invites = yield this.requests.send(endpoints_1.EndpointRoute.GuildInvites, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite, guild)])); + }); + } + /** + * Fetches all guild integrations in a guild + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildIntegrations(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const integrations = yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegrations, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(integrations.map(integration => [ + integration.id, + new guild_2.GuildIntegration(this.bot, integration, guild), + ])); + }); + } + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + createGuildIntegration(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createIntegrationOptions(options)); + }); + } + /** + * Modifies the behavior and settings of a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the integration + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modifyGuildIntegration(guildId, integrationId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyIntegrationOptions(options)); + }); + } + /** + * Deletes the attached integration for a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + deleteGuildIntegration(guildId, integrationId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Syncs a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + syncGuildIntegration(guildId, integrationId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegrationSync, { guildId, integrationId }, constants_1.HttpMethod.Post); + }); + } + /** + * Fetches a guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildWidget(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const widget = yield this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new guild_2.GuildWidget(this.bot, widget, guild); + }); + } + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modifyGuildWidget(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const widget = yield this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyWidgetOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new guild_2.GuildWidget(this.bot, widget, guild); + }); + } + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildVanityURL(guildId) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.requests.send(endpoints_1.EndpointRoute.GuildVanityURL, { guildId }, constants_1.HttpMethod.Get); + }); + } + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBotUser() { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Get); + return new structures_1.BotUser(this.bot, user); + }); + } + /** + * Fetches a user by its ID + * @param {Snowflake} userId The user ID + * @returns {Promise} + */ + fetchUser(userId) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.requests.send(endpoints_1.EndpointRoute.User, { userId }, constants_1.HttpMethod.Get); + return new structures_1.User(this.bot, user); + }); + } + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modifyBotUser(options) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyBotUserOptions(options)); + return new structures_1.BotUser(this.bot, user); + }); + } + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchBotGuilds(options) { + return __awaiter(this, void 0, void 0, function* () { + const guilds = yield this.requests.send(endpoints_1.EndpointRoute.UserBotGuilds, {}, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildsOptions(options)); + return new Collection_1.default(guilds.map(guild => [ + guild.id, + { + id: guild.id, + name: guild.name, + icon: guild.icon, + owner: guild.owner, + permissions: new flags_1.PermissionFlags(guild.permissions_new), + }, + ])); + }); + } + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.UserBotGuild, { guildId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Creates a new DM channel between a user and the bot user + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + createDM(userId) { + return __awaiter(this, void 0, void 0, function* () { + const dmChannel = yield this.requests.send(endpoints_1.EndpointRoute.UserBotChannels, {}, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createDM(userId)); + return new channels_1.DMChannel(this.bot, dmChannel); + }); + } + /** + * Fetches an invite by its invite code + * @param {string} inviteCode The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetchInvite(inviteCode, options) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchInviteOptions(options)); + return new structures_1.Invite(this.bot, invite); + }); + } + /** + * Deletes an invite by its invite code. + * Requires the {@link Permission.ManageChannels} permission on the channel this invite belongs to, or {@link Permission.ManageGuild} to remove any invite across the guild + * @param {string} inviteCode The invite code + * @returns {Promise} + */ + deleteInvite(inviteCode) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Delete); + return new structures_1.Invite(this.bot, invite); + }); + } + /** + * Creates a new webhook for a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + createWebhook(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Post, yield APISerializer_1.APISerializer.createWebhookOptions(options)); + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); + }); + } + /** + * Fetches all webhooks in a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @returns {Promise>} + */ + fetchWebhooks(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Get); + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new Collection_1.default(webhooks.map(webhook => [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)])); + }); + } + /** + * Fetches all webhooks in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildWebhooks(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.requests.send(endpoints_1.EndpointRoute.GuildWebhooks, { guildId }, constants_1.HttpMethod.Get); + return new Collection_1.default(yield Promise.all(webhooks.map((webhook) => __awaiter(this, void 0, void 0, function* () { + const { channel_id: channelId } = webhook; + // Fetch the guild channel associated to this webhook + const channel = yield this.bot.channels.getGuildChannel(channelId); + return [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)]; + })))); + }); + } + /** + * Fetches a webhook by its ID + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + fetchWebhook(webhookId) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Get); + const { channel_id: channelId } = webhook; + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); + }); + } + /** + * Modifies a webhook by its ID + * @param {Snowflake} webhookId The webhook ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modifyWebhook(webhookId, options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyWebhookOptions(options)); + const { channel_id: channelId } = webhook; + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); + }); + } + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + deleteWebhook(webhookId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Delete); + }); + } +} +exports.BotAPI = BotAPI; diff --git a/lib/api/constants.d.ts b/lib/api/constants.d.ts new file mode 100644 index 000000000..97607bc4a --- /dev/null +++ b/lib/api/constants.d.ts @@ -0,0 +1,18 @@ +/** + * The latest version of the Discord API + */ +export declare const version = 6; +/** + * The base URL for the Discord API + */ +export declare const baseURL: string; +/** + * All HTTP methods the API supports + */ +export declare enum HttpMethod { + Get = "GET", + Post = "POST", + Put = "PUT", + Patch = "PATCH", + Delete = "DELETE" +} diff --git a/lib/api/constants.js b/lib/api/constants.js new file mode 100644 index 000000000..e58fc5e7c --- /dev/null +++ b/lib/api/constants.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HttpMethod = exports.baseURL = exports.version = void 0; +/** + * The latest version of the Discord API + */ +exports.version = 6; +/** + * The base URL for the Discord API + */ +exports.baseURL = `https://discord.com/api/v${exports.version}`; +/** + * All HTTP methods the API supports + */ +var HttpMethod; +(function (HttpMethod) { + HttpMethod["Get"] = "GET"; + HttpMethod["Post"] = "POST"; + HttpMethod["Put"] = "PUT"; + HttpMethod["Patch"] = "PATCH"; + HttpMethod["Delete"] = "DELETE"; +})(HttpMethod = exports.HttpMethod || (exports.HttpMethod = {})); diff --git a/lib/api/endpoints.d.ts b/lib/api/endpoints.d.ts new file mode 100644 index 000000000..3ce099640 --- /dev/null +++ b/lib/api/endpoints.d.ts @@ -0,0 +1,93 @@ +/** + * All Endpoint routes for the Discord API. + * Every route is appropriate for all HTTP methods that the API supports + */ +export declare enum EndpointRoute { + Channel = "/channels/{channel.id}", + ChannelMessage = "/channels/{channel.id}/messages/{message.id}", + ChannelMessages = "/channels/{channel.id}/messages", + ChannelMessagesReactions = "/channels/{channel.id}/messages/{message.id}/reactions", + ChannelMessagesReactionsEmoji = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}", + ChannelMessagesReactionsEmojiUser = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id}", + ChannelMessagesBulkDelete = "/channels/{channel.id}/messages/bulk-delete", + ChannelPermissionsOverwrite = "/channels/{channel.id}/permissions/{overwrite.id}", + ChannelInvites = "/channels/{channel.id}/invites", + ChannelTyping = "/channels/{channel.id}/typing", + ChannelPins = "/channels/{channel.id}/pins", + ChannelPinsMessage = "/channels/{channel.id}/pins/{message.id}", + GuildEmojis = "/guilds/{guild.id}/emojis", + GuildEmoji = "/guilds/{guild.id}/emojis/{emoji.id}", + Guild = "/guilds/{guild.id}", + GuildPreview = "/guilds/{guild.id}/preview", + GuildChannels = "/guilds/{guild.id}/channels", + GuildMembers = "/guilds/{guild.id}/members", + GuildMember = "/guilds/{guild.id}/members/{user.id}", + GuildMemberBotNick = "/guilds/{guild.id}/members/@me/nick", + GuildMemberRole = "/guilds/{guild.id}/members/{user.id}/roles/{role.id}", + GuildBans = "/guilds/{guild.id}/bans", + GuildBan = "/guilds/{guild.id}/bans/{user.id}", + GuildRoles = "/guilds/{guild.id}/roles", + GuildRole = "/guilds/{guild.id}/roles/{role.id}", + GuildPrune = "/guilds/{guild.id}/prune", + GuildInvites = "/guilds/{guild.id}/invites", + GuildIntegrations = "/guilds/{guild.id}/integrations", + GuildIntegration = "/guilds/{guild.id}/integrations/{integration.id}", + GuildIntegrationSync = "/guilds/{guild.id}/integrations/{integration.id}/sync", + GuildWidget = "/guilds/{guild.id}/widget", + GuildVanityURL = "/guilds/{guild.id}/vanity-url", + UserBot = "/users/@me", + User = "/users/{user.id}", + UserBotGuilds = "/users/@me/guilds", + UserBotGuild = "/users/@me/guild/{guild.id}", + UserBotChannels = "/users/@me/channels", + Invite = "/invites/{invite.code}", + ChannelWebhooks = "/channels/{channel.id}/webhooks", + GuildWebhooks = "/guilds/{guild.id}/webhooks", + Webhook = "/webhooks/{webhook.id}" +} +/** + * All endpoints mapped by their route name + * @type {Record string>} + */ +export declare const Endpoints: Record string>; +/** + * All headers used to identifier the rate limit information of the request + */ +export declare enum RateLimitHeaders { + Global = "x-ratelimit-global", + Limit = "x-ratelimit-limit", + Remaining = "x-ratelimit-remaining", + Reset = "x-ratelimit-reset", + ResetAfter = "x-ratelimit-reset-after", + Bucket = "x-ratelimit-bucket" +} +/** + * All status codes that might be returned in response to an API request + */ +export declare enum StatusCode { + /** + * The request was successful + */ + OK = 200, + /** + * The request was successful and led to the creation of a resource + */ + Created = 201, + /** + * The request succeeded with no content as response + * @type {number} + */ + NoContent = 204, + /** + * The token is no longer valid + */ + UnAuthorized = 401, + /** + * The bot has insufficient permissions to send this request + */ + Forbidden = 403, + /** + * The rate limit has been reached + */ + TooManyRequests = 429 +} diff --git a/lib/api/endpoints.js b/lib/api/endpoints.js new file mode 100644 index 000000000..8a06fb9c5 --- /dev/null +++ b/lib/api/endpoints.js @@ -0,0 +1,141 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StatusCode = exports.RateLimitHeaders = exports.Endpoints = exports.EndpointRoute = void 0; +/** + * All Endpoint routes for the Discord API. + * Every route is appropriate for all HTTP methods that the API supports + */ +var EndpointRoute; +(function (EndpointRoute) { + EndpointRoute["Channel"] = "/channels/{channel.id}"; + EndpointRoute["ChannelMessage"] = "/channels/{channel.id}/messages/{message.id}"; + EndpointRoute["ChannelMessages"] = "/channels/{channel.id}/messages"; + EndpointRoute["ChannelMessagesReactions"] = "/channels/{channel.id}/messages/{message.id}/reactions"; + EndpointRoute["ChannelMessagesReactionsEmoji"] = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}"; + EndpointRoute["ChannelMessagesReactionsEmojiUser"] = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id}"; + EndpointRoute["ChannelMessagesBulkDelete"] = "/channels/{channel.id}/messages/bulk-delete"; + EndpointRoute["ChannelPermissionsOverwrite"] = "/channels/{channel.id}/permissions/{overwrite.id}"; + EndpointRoute["ChannelInvites"] = "/channels/{channel.id}/invites"; + EndpointRoute["ChannelTyping"] = "/channels/{channel.id}/typing"; + EndpointRoute["ChannelPins"] = "/channels/{channel.id}/pins"; + EndpointRoute["ChannelPinsMessage"] = "/channels/{channel.id}/pins/{message.id}"; + EndpointRoute["GuildEmojis"] = "/guilds/{guild.id}/emojis"; + EndpointRoute["GuildEmoji"] = "/guilds/{guild.id}/emojis/{emoji.id}"; + EndpointRoute["Guild"] = "/guilds/{guild.id}"; + EndpointRoute["GuildPreview"] = "/guilds/{guild.id}/preview"; + EndpointRoute["GuildChannels"] = "/guilds/{guild.id}/channels"; + EndpointRoute["GuildMembers"] = "/guilds/{guild.id}/members"; + EndpointRoute["GuildMember"] = "/guilds/{guild.id}/members/{user.id}"; + EndpointRoute["GuildMemberBotNick"] = "/guilds/{guild.id}/members/@me/nick"; + EndpointRoute["GuildMemberRole"] = "/guilds/{guild.id}/members/{user.id}/roles/{role.id}"; + EndpointRoute["GuildBans"] = "/guilds/{guild.id}/bans"; + EndpointRoute["GuildBan"] = "/guilds/{guild.id}/bans/{user.id}"; + EndpointRoute["GuildRoles"] = "/guilds/{guild.id}/roles"; + EndpointRoute["GuildRole"] = "/guilds/{guild.id}/roles/{role.id}"; + EndpointRoute["GuildPrune"] = "/guilds/{guild.id}/prune"; + EndpointRoute["GuildInvites"] = "/guilds/{guild.id}/invites"; + EndpointRoute["GuildIntegrations"] = "/guilds/{guild.id}/integrations"; + EndpointRoute["GuildIntegration"] = "/guilds/{guild.id}/integrations/{integration.id}"; + EndpointRoute["GuildIntegrationSync"] = "/guilds/{guild.id}/integrations/{integration.id}/sync"; + EndpointRoute["GuildWidget"] = "/guilds/{guild.id}/widget"; + EndpointRoute["GuildVanityURL"] = "/guilds/{guild.id}/vanity-url"; + EndpointRoute["UserBot"] = "/users/@me"; + EndpointRoute["User"] = "/users/{user.id}"; + EndpointRoute["UserBotGuilds"] = "/users/@me/guilds"; + EndpointRoute["UserBotGuild"] = "/users/@me/guild/{guild.id}"; + EndpointRoute["UserBotChannels"] = "/users/@me/channels"; + EndpointRoute["Invite"] = "/invites/{invite.code}"; + EndpointRoute["ChannelWebhooks"] = "/channels/{channel.id}/webhooks"; + EndpointRoute["GuildWebhooks"] = "/guilds/{guild.id}/webhooks"; + EndpointRoute["Webhook"] = "/webhooks/{webhook.id}"; +})(EndpointRoute = exports.EndpointRoute || (exports.EndpointRoute = {})); +/** + * All endpoints mapped by their route name + * @type {Record string>} + */ +exports.Endpoints = { + [EndpointRoute.Channel]: (channelId) => `/channels/${channelId}`, + [EndpointRoute.ChannelMessage]: (channelId, messageId) => `/channels/${channelId}/messages/${messageId}`, + [EndpointRoute.ChannelMessages]: (channelId) => `/channels/${channelId}/messages`, + [EndpointRoute.ChannelMessagesReactions]: (channelId, messageId) => `/channels/${channelId}/messages/${messageId}/reactions`, + [EndpointRoute.ChannelMessagesReactionsEmoji]: (channelId, messageId, emoji) => `/channels/${channelId}/messages/${messageId}/reactions/${emoji}`, + [EndpointRoute.ChannelMessagesReactionsEmojiUser]: (channelId, messageId, emoji, userId = '@me') => `/channels/${channelId}/messages/${messageId}/reactions/${emoji}/${userId}`, + [EndpointRoute.ChannelMessagesBulkDelete]: (channelId) => `/channels/${channelId}/messages/bulk-delete`, + [EndpointRoute.ChannelPermissionsOverwrite]: (channelId, overwriteId) => `/channels/${channelId}/permissions/${overwriteId}`, + [EndpointRoute.ChannelInvites]: (channelId) => `/channels/${channelId}/invites`, + [EndpointRoute.ChannelTyping]: (channelId) => `/channels/${channelId}/typing`, + [EndpointRoute.ChannelPins]: (channelId) => `/channels/${channelId}/pins`, + [EndpointRoute.ChannelPinsMessage]: (channelId, messageId) => `/channels/${channelId}/pins/${messageId}`, + [EndpointRoute.GuildEmojis]: (guildId) => `/guilds/${guildId}/emojis`, + [EndpointRoute.GuildEmoji]: (guildId, emojiId) => `/guilds/${guildId}/emojis/${emojiId}`, + [EndpointRoute.Guild]: (guildId) => `/guilds/${guildId}`, + [EndpointRoute.GuildPreview]: (guildId) => `/guilds/${guildId}/preview`, + [EndpointRoute.GuildChannels]: (guildId) => `/guilds/${guildId}/channels`, + [EndpointRoute.GuildMembers]: (guildId) => `/guilds/${guildId}/members`, + [EndpointRoute.GuildMember]: (guildId, userId) => `/guilds/${guildId}/members/${userId}`, + [EndpointRoute.GuildMemberBotNick]: (guildId) => `/guilds/${guildId}/members/@me/nick`, + [EndpointRoute.GuildMemberRole]: (guildId, userId, roleId) => `/guilds/${guildId}/members/${userId}/roles/${roleId}`, + [EndpointRoute.GuildBans]: (guildId) => `/guilds/${guildId}/bans`, + [EndpointRoute.GuildBan]: (guildId, userId) => `/guilds/${guildId}/bans/${userId}`, + [EndpointRoute.GuildRoles]: (guildId) => `/guilds/${guildId}/roles`, + [EndpointRoute.GuildRole]: (guildId, roleId) => `/guilds/${guildId}/roles/${roleId}`, + [EndpointRoute.GuildPrune]: (guildId) => `/guilds/${guildId}/prune`, + [EndpointRoute.GuildInvites]: (guildId) => `/guilds/${guildId}/invites`, + [EndpointRoute.GuildIntegrations]: (guildId) => `/guilds/${guildId}/integrations`, + [EndpointRoute.GuildIntegration]: (guildId, integrationId) => `/guilds/${guildId}/integrations/${integrationId}`, + [EndpointRoute.GuildIntegrationSync]: (guildId, integrationId) => `/guilds/${guildId}/integrations/${integrationId}/sync`, + [EndpointRoute.GuildWidget]: (guildId) => `/guilds/${guildId}/widget`, + [EndpointRoute.GuildVanityURL]: (guildId) => `/guilds/${guildId}/vanity-url`, + [EndpointRoute.UserBot]: () => `/users/@me`, + [EndpointRoute.User]: (userId) => `/users/${userId}`, + [EndpointRoute.UserBotGuilds]: () => `/users/@me/guilds`, + [EndpointRoute.UserBotGuild]: (guildId) => `/users/@me/guilds/${guildId}`, + [EndpointRoute.UserBotChannels]: () => `/users/@me/channels`, + [EndpointRoute.Invite]: (inviteCode) => `/invites/${inviteCode}`, + [EndpointRoute.ChannelWebhooks]: (channelId) => `/channels/${channelId}/webhooks`, + [EndpointRoute.GuildWebhooks]: (guildId) => `/guilds/${guildId}/webhooks`, + [EndpointRoute.Webhook]: (webhookId) => `/webhooks/${webhookId}`, +}; +/** + * All headers used to identifier the rate limit information of the request + */ +var RateLimitHeaders; +(function (RateLimitHeaders) { + RateLimitHeaders["Global"] = "x-ratelimit-global"; + RateLimitHeaders["Limit"] = "x-ratelimit-limit"; + RateLimitHeaders["Remaining"] = "x-ratelimit-remaining"; + RateLimitHeaders["Reset"] = "x-ratelimit-reset"; + RateLimitHeaders["ResetAfter"] = "x-ratelimit-reset-after"; + RateLimitHeaders["Bucket"] = "x-ratelimit-bucket"; +})(RateLimitHeaders = exports.RateLimitHeaders || (exports.RateLimitHeaders = {})); +/** + * All status codes that might be returned in response to an API request + */ +var StatusCode; +(function (StatusCode) { + /** + * The request was successful + */ + StatusCode[StatusCode["OK"] = 200] = "OK"; + /** + * The request was successful and led to the creation of a resource + */ + StatusCode[StatusCode["Created"] = 201] = "Created"; + /** + * The request succeeded with no content as response + * @type {number} + */ + StatusCode[StatusCode["NoContent"] = 204] = "NoContent"; + /** + * The token is no longer valid + */ + StatusCode[StatusCode["UnAuthorized"] = 401] = "UnAuthorized"; + /** + * The bot has insufficient permissions to send this request + */ + StatusCode[StatusCode["Forbidden"] = 403] = "Forbidden"; + /** + * The rate limit has been reached + */ + StatusCode[StatusCode["TooManyRequests"] = 429] = "TooManyRequests"; +})(StatusCode = exports.StatusCode || (exports.StatusCode = {})); diff --git a/lib/api/index.d.ts b/lib/api/index.d.ts new file mode 100644 index 000000000..9adf92c90 --- /dev/null +++ b/lib/api/index.d.ts @@ -0,0 +1,5 @@ +export * from './rateLimit'; +export * from './constants'; +export * from './endpoints'; +export * from './APISerializer'; +export * from './BotAPI'; diff --git a/lib/api/index.js b/lib/api/index.js new file mode 100644 index 000000000..1d3e5cbcc --- /dev/null +++ b/lib/api/index.js @@ -0,0 +1,17 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./rateLimit"), exports); +__exportStar(require("./constants"), exports); +__exportStar(require("./endpoints"), exports); +__exportStar(require("./APISerializer"), exports); +__exportStar(require("./BotAPI"), exports); diff --git a/lib/api/rateLimit/RateLimitBucket.d.ts b/lib/api/rateLimit/RateLimitBucket.d.ts new file mode 100644 index 000000000..64ed0ae79 --- /dev/null +++ b/lib/api/rateLimit/RateLimitBucket.d.ts @@ -0,0 +1,67 @@ +import { Params, RequestFile, ReturnedData } from './Requests'; +import { Bot } from '../../bot'; +import { HttpMethod } from '../constants'; +import { StatusCode, EndpointRoute } from '../endpoints'; +export declare const ValidCodes: StatusCode[]; +export declare class RateLimitBucket { + /** + * The bot instance + */ + private readonly bot; + /** + * The bot's token + */ + private readonly token; + /** + * The queue associated to this bucket + */ + private readonly queue; + /** + * The route this bucket references + */ + private readonly route; + /** + * The HTTP method this bucket references + */ + private readonly method; + /** + * The number of remaining requests this bucket has until the next reset {@link reset} + */ + remaining: number | undefined; + /** + * The number of total requests that can be made + */ + private limit; + /** + * The Unix timestamp of the next refill for this bucket + */ + private reset; + /** + * The current setTimeout that will run once the next refill is reached + */ + private timeout; + constructor(bot: Bot, token: string, route: EndpointRoute, method: HttpMethod); + /** + * Creates a new API request and sends it if the bucket is capable of doing so + * @param {string} endpoint The request endpoint + * @param {Params} params The request params / body + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(endpoint: string, params: Params, files?: RequestFile[]): Promise; + /** + * Refill this bucket + */ + private refillBucket; + /** + * Validates the response. Throws an error in case it is not valid + * @param {Response} response The response received from sending an API request + * @param {ReturnedData} json The parsed response in JSON + */ + private validateResponse; + /** + * Sets the rate limit information given from the response's headers + * @param {Headers} headers The response's headers + */ + private setLimits; +} diff --git a/lib/api/rateLimit/RateLimitBucket.js b/lib/api/rateLimit/RateLimitBucket.js new file mode 100644 index 000000000..ec1726f7f --- /dev/null +++ b/lib/api/rateLimit/RateLimitBucket.js @@ -0,0 +1,132 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RateLimitBucket = exports.ValidCodes = void 0; +const util_1 = __importDefault(require("util")); +const RateLimitQueue_1 = require("./RateLimitQueue"); +const APIRequest_1 = require("../APIRequest"); +const endpoints_1 = require("../endpoints"); +exports.ValidCodes = [endpoints_1.StatusCode.OK, endpoints_1.StatusCode.Created, endpoints_1.StatusCode.NoContent]; +class RateLimitBucket { + constructor(bot, token, route, method) { + this.bot = bot; + this.token = token; + this.queue = new RateLimitQueue_1.RateLimitQueue(this); + this.route = route; + this.method = method; + /* + Initialize the number of remaining requests to 1 + in order to determine the 'limit' and 'remaining' values after sending the first request. + */ + this.remaining = 1; + } + /** + * Creates a new API request and sends it if the bucket is capable of doing so + * @param {string} endpoint The request endpoint + * @param {Params} params The request params / body + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(endpoint, params, files) { + return __awaiter(this, void 0, void 0, function* () { + // The rate limit is reached. Add request to the queue + if (this.remaining !== undefined && this.remaining <= 0) { + this.bot.debug(`You reached the rate limit for ${endpoint}. Your request will still go through, but make sure you don't do this again!`); + return this.queue.add(endpoint, params, files); + } + // Decrements the remaining number of requests (to later be updated by the fixed value) + if (this.remaining) { + this.remaining--; + } + // Creates a new API request + const apiRequest = new APIRequest_1.APIRequest(this.token, endpoint, params, this.method, APIRequest_1.APIRequest.parseFiles(files)); + const response = yield apiRequest.send(); + // Sets the rate limit information given from the response's headers + this.setLimits(response.headers); + const json = response.status !== endpoints_1.StatusCode.NoContent + ? (yield response.json()) + : undefined; + if (json) { + // Validates the response before proceeding + this.validateResponse(response, json); + } + return json; + }); + } + /** + * Refill this bucket + */ + refillBucket() { + return __awaiter(this, void 0, void 0, function* () { + // Set the remaining number of requests in this bucket back to its limit + this.remaining = this.limit; + // Delete the stored setTimeout function + this.timeout = undefined; + // Empties the queue + yield this.queue.free(); + }); + } + /** + * Validates the response. Throws an error in case it is not valid + * @param {Response} response The response received from sending an API request + * @param {ReturnedData} json The parsed response in JSON + */ + validateResponse(response, json) { + switch (response.status) { + case endpoints_1.StatusCode.UnAuthorized: + this.bot.connection.disconnectAll(4004 /* AuthenticationFailed */); + throw new Error('Your Bot token is invalid! This connection has been terminated'); + case endpoints_1.StatusCode.Forbidden: + throw new Error(`The request to ${response.url} was rejected to to insufficient bot permissions`); + case endpoints_1.StatusCode.TooManyRequests: + throw new Error("You have reached Discord's API rate limit. Please slow down"); + } + if (!exports.ValidCodes.includes(response.status)) { + // Debug the json response + this.bot.debug('Error!', json); + if (Array.isArray(json)) { + throw new TypeError(`${response.url} - an error has occurred with an array response type - ${util_1.default.inspect(json)}`); + } + else { + throw new Error(`${response.url} (${response.status} code) - ${json.message || json.content || util_1.default.inspect(json)}`); + } + } + } + // TODO: Global rate limit + /** + * Sets the rate limit information given from the response's headers + * @param {Headers} headers The response's headers + */ + setLimits(headers) { + // Retrieve all the rate limit information from the request headers + const remaining = headers.get(endpoints_1.RateLimitHeaders.Remaining); + const limit = headers.get(endpoints_1.RateLimitHeaders.Limit); + const reset = headers.get(endpoints_1.RateLimitHeaders.Reset); + const resetAfter = headers.get(endpoints_1.RateLimitHeaders.ResetAfter); + // Set the parsed value of the rate limit information + // Use the cached remaining number if this is not the first request for async requests + this.remaining = this.limit ? this.remaining : remaining ? parseInt(remaining) : undefined; + this.limit = limit ? parseInt(limit) : undefined; + this.reset = reset ? parseFloat(reset) : undefined; + if (!this.reset && !resetAfter) + return; + // Initialize the timeout for the bucket's refill if one hasn't been initialized before + if (!this.timeout) { + // The timeout until the bucket refills + const refillTimeout = ((resetAfter && parseFloat(resetAfter)) || Date.now() - this.reset) * 1000; + this.timeout = setTimeout(this.refillBucket.bind(this), refillTimeout); + } + } +} +exports.RateLimitBucket = RateLimitBucket; diff --git a/lib/api/rateLimit/RateLimitQueue.d.ts b/lib/api/rateLimit/RateLimitQueue.d.ts new file mode 100644 index 000000000..be5936161 --- /dev/null +++ b/lib/api/rateLimit/RateLimitQueue.d.ts @@ -0,0 +1,48 @@ +import { RateLimitBucket } from './RateLimitBucket'; +import { Params, RequestFile, ReturnedData } from './Requests'; +/** + * The items that the rate limit queue stores + */ +export interface RateLimitQueueItem { + /** + * The endpoint for this API request + */ + endpoint: string; + /** + * The params / body for this API request + */ + params: Params; + /** + * The files sent in the API request + */ + files?: RequestFile[]; + /** + * Callback function to be called whenever the request is made + * @param {never} data + */ + callback: (data: never) => void; +} +/** + * The rate limit queue + * Receives all overlapping API requests and ultimately sends them after the the bucket refills + */ +export declare class RateLimitQueue extends Array { + /** + * The bucket instance that initialized this queue + */ + private readonly bucket; + constructor(bucket: RateLimitBucket); + /** + * Adds a new API request to the queue and waits until it executes + * @param {string} endpoint The endpoint for the added API request + * @param {Params} params The params / body for the added API request + * @param {RequestFile[]} files The files sent in the API request + * @returns {Promise} + */ + add(endpoint: string, params: Params, files?: RequestFile[]): Promise; + /** + * Frees the queue for the remaining capacity of the bucket + * @returns {Promise} + */ + free(): Promise; +} diff --git a/lib/api/rateLimit/RateLimitQueue.js b/lib/api/rateLimit/RateLimitQueue.js new file mode 100644 index 000000000..712c2341f --- /dev/null +++ b/lib/api/rateLimit/RateLimitQueue.js @@ -0,0 +1,61 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RateLimitQueue = void 0; +/** + * The rate limit queue + * Receives all overlapping API requests and ultimately sends them after the the bucket refills + */ +class RateLimitQueue extends Array { + constructor(bucket) { + super(); + this.bucket = bucket; + } + /** + * Adds a new API request to the queue and waits until it executes + * @param {string} endpoint The endpoint for the added API request + * @param {Params} params The params / body for the added API request + * @param {RequestFile[]} files The files sent in the API request + * @returns {Promise} + */ + add(endpoint, params, files) { + return new Promise(resolve => { + this.push({ + endpoint, + params, + files, + callback: (data) => { + resolve(data); + }, + }); + }); + } + /** + * Frees the queue for the remaining capacity of the bucket + * @returns {Promise} + */ + free() { + return __awaiter(this, void 0, void 0, function* () { + // Runs until either the queue's or the bucket's capacity empties + while (this.length && this.bucket.remaining && this.bucket.remaining > 0) { + const nextRequest = this.shift(); + if (!nextRequest) + break; + const { endpoint, params, files, callback } = nextRequest; + // Sends the request + const data = yield this.bucket.send(endpoint, params, files); + // Executes the callback function + callback(data); + } + }); + } +} +exports.RateLimitQueue = RateLimitQueue; diff --git a/lib/api/rateLimit/Requests.d.ts b/lib/api/rateLimit/Requests.d.ts new file mode 100644 index 000000000..4f9a7c125 --- /dev/null +++ b/lib/api/rateLimit/Requests.d.ts @@ -0,0 +1,72 @@ +/// +import { Serializable } from 'child_process'; +import { Bot } from '../../bot'; +import { HttpMethod } from '../constants'; +import { Endpoints } from '../endpoints'; +/** + * Rate limit buckets are indexed by the Endpoint Route, Http Method, and all Major Parameters + * @example + * [EndpointRoute.Channel, HttpMethod.Get, [channelId]].join(' '); + */ +export declare type BucketIndex = string; +/** + * Dictionary type for request Params and Body + */ +export declare type Data = Record; +/** + * The type of data returned from the API + */ +export declare type ReturnedData = Data | Array; +/** + * The request's Body type + */ +export declare type Body = Data | Data[]; +/** + * The request's Params type + */ +export declare type Params = Data | Data[] | undefined; +/** + * The route's arguments type (as a parameter) + */ +export declare type RouteArgs = Record; +/** + * Represents a file attachment sent to the API + */ +export declare type RequestFile = { + path: string; + name: string; +} | string; +/** + * Manager for sending requests according to the Discord API rate limit + */ +export declare class Requests { + /** + * The bot instance + */ + private readonly bot; + /** + * The Bot's token + */ + private readonly token; + /** + * Rate limit buckets are unique identifiers for every API route + */ + private readonly buckets; + constructor(bot: Bot, token: string); + /** + * Tells the bucket that is responsible for this request to send this request + * @param {EndpointRoute} route The route this request should go to + * @param {RouteArgs} routeArgs The arguments this route requires + * @param {HttpMethod} method The http method for this request + * @param {Params} params The params / body for this request + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(route: T, routeArgs: RouteArgs, method: HttpMethod, params?: Params, files?: RequestFile[]): Promise; + /** + * Retrieves the major params from the route arguments + * @param {RouteArgs} routeArgs The route arguments + * @returns {string[]} + */ + private getMajorParams; +} diff --git a/lib/api/rateLimit/Requests.js b/lib/api/rateLimit/Requests.js new file mode 100644 index 000000000..1388a59fc --- /dev/null +++ b/lib/api/rateLimit/Requests.js @@ -0,0 +1,59 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Requests = void 0; +const RateLimitBucket_1 = require("./RateLimitBucket"); +const Collection_1 = __importDefault(require("../../Collection")); +const endpoints_1 = require("../endpoints"); +/** + * All major param keys + * https://discord.com/developers/docs/topics/rate-limits#rate-limits + */ +const majorKeys = ['channelId', 'guildId', 'webhookId']; +/** + * Manager for sending requests according to the Discord API rate limit + */ +class Requests { + constructor(bot, token) { + this.bot = bot; + this.token = token; + this.buckets = new Collection_1.default(); + } + /** + * Tells the bucket that is responsible for this request to send this request + * @param {EndpointRoute} route The route this request should go to + * @param {RouteArgs} routeArgs The arguments this route requires + * @param {HttpMethod} method The http method for this request + * @param {Params} params The params / body for this request + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(route, routeArgs, method, params, files) { + // Retrieve the major params of this request + const majorParams = this.getMajorParams(routeArgs); + // Set this bucket's identifier + const identifier = [route, method, majorParams].join(' '); + if (!this.buckets.has(identifier)) { + // Creates a new bucket if one is not cached + this.buckets.set(identifier, new RateLimitBucket_1.RateLimitBucket(this.bot, this.token, route, method)); + } + const bucket = this.buckets.get(identifier); + // Retrieves this request's endpoint + const endpoint = endpoints_1.Endpoints[route](...Object.values(routeArgs)); + // Tells the bucket to send this request + return bucket.send(endpoint, params, files); + } + /** + * Retrieves the major params from the route arguments + * @param {RouteArgs} routeArgs The route arguments + * @returns {string[]} + */ + getMajorParams(routeArgs) { + return Object.keys(routeArgs).reduce((keys, key) => { + return majorKeys.includes(key) ? [...keys, key] : keys; + }, []); + } +} +exports.Requests = Requests; diff --git a/lib/api/rateLimit/index.d.ts b/lib/api/rateLimit/index.d.ts new file mode 100644 index 000000000..86414c3ca --- /dev/null +++ b/lib/api/rateLimit/index.d.ts @@ -0,0 +1,3 @@ +export * from './RateLimitBucket'; +export * from './RateLimitQueue'; +export * from './Requests'; diff --git a/lib/api/rateLimit/index.js b/lib/api/rateLimit/index.js new file mode 100644 index 000000000..7f6c24cf4 --- /dev/null +++ b/lib/api/rateLimit/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./RateLimitBucket"), exports); +__exportStar(require("./RateLimitQueue"), exports); +__exportStar(require("./Requests"), exports); diff --git a/lib/bot/Bot.d.ts b/lib/bot/Bot.d.ts new file mode 100644 index 000000000..987e66306 --- /dev/null +++ b/lib/bot/Bot.d.ts @@ -0,0 +1,137 @@ +/// +import { Serializable } from 'child_process'; +import { BotConnection } from './BotConnection'; +import { CommandsHandler } from './handlers/command'; +import { EventsHandler } from './handlers/events'; +import Collection from '../Collection'; +import { BotAPI } from '../api'; +import { BotChannelsController, BotGuildsController, BotUsersController } from '../controllers/bot'; +import { BotCommunication } from '../sharding'; +import { WebsocketOptions } from '../socket'; +import { BotUser } from '../structures'; +import { GuildEmoji, GuildUnavailable } from '../structures/guild'; +import { ShardId, Snowflake } from '../types'; +/** + * The options given to every Bot shard + */ +export interface ShardOptions { + /** + * Shard ID + */ + id: ShardId; + /** + * Number of shards this instance of the bot uses + */ + amount?: number; +} +/** + * Bot cache options + */ +export interface CacheOptions { + /** + * The limit of messages cached in every channel. Set to `0` for no limit + */ + messagesLimit: number; +} +/** + * The default options used to initialize a Bot instance + * @type {BotOptions} + */ +export declare const botOptions: BotOptions; +/** + * The options used to initialize the Bot + */ +export interface BotOptions { + /** + * Websocket connection options + */ + websocket: Partial; + /** + * Bot cache options + */ + cache: CacheOptions; +} +/** + * The bot is the main operator of the API. + * It handles the events, and properties for all structures. + */ +export declare class Bot { + /** + * Bot token + */ + private readonly token; + /** + * Options used to determine how the Bot operates + */ + readonly options: BotOptions; + /** + * {@link ShardOptions} object containing sharding information + */ + readonly shardOptions: ShardOptions; + /** + * Creates all outgoing API requests + */ + readonly api: BotAPI; + /** + * Responsible for handling all of the Bot's commands + */ + commands: CommandsHandler; + /** + * Responsible for handling all of the Bot's events + */ + events: EventsHandler; + /** + * Responsible for managing the bot connection to the Discord gateway + */ + connection: BotConnection; + /** + * Responsible for the communication between shards created by {@link BotShardManager} + */ + communication: BotCommunication; + /** + * This bot's Discord user + * Initializes right before the Bot READY event + */ + user: BotUser | undefined; + /** + * The bot's guilds controller + */ + guilds: BotGuildsController; + /** + * {@link Collection} of all {@link GuildUnavailable}s associated to the Bot + */ + unavailableGuilds: Collection; + /** + * The bot's channels controller + */ + channels: BotChannelsController; + /** + * The bot's users controller + */ + users: BotUsersController; + /** + * {@link Collection} of all {@link GuildEmoji}s found in all guilds the Bot is part of + */ + emojis: Collection; + constructor(token: string, options?: Partial); + /** + * Connects the bot to the Discord gateway + * @returns {Promise} + */ + connect(): Promise; + /** + * Sends debug messages to the {@link BotEvent.Debug} event + * @example ```typescript + * bot.on(BotEvents.Debug, console.log); + * + * bot.debug('Hello World!'); // 'Hello World' + * ``` + * @param {...unknown[]} messages The debug messages + */ + debug(...messages: unknown[]): void; + /** + * @ignore + * @returns {Serializable} + */ + toJSON(): Serializable; +} diff --git a/lib/bot/Bot.js b/lib/bot/Bot.js new file mode 100644 index 000000000..62df8eebb --- /dev/null +++ b/lib/bot/Bot.js @@ -0,0 +1,85 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Bot = exports.botOptions = void 0; +const BotConnection_1 = require("./BotConnection"); +const command_1 = require("./handlers/command"); +const events_1 = require("./handlers/events"); +const Collection_1 = __importDefault(require("../Collection")); +const api_1 = require("../api"); +const bot_1 = require("../controllers/bot"); +const sharding_1 = require("../sharding"); +const socket_1 = require("../socket"); +/** + * The default options used to initialize a Bot instance + * @type {BotOptions} + */ +exports.botOptions = { + cache: { + messagesLimit: 100, + }, + websocket: { + v: api_1.version, + }, +}; +/** + * The bot is the main operator of the API. + * It handles the events, and properties for all structures. + */ +class Bot { + constructor(token, options) { + this.token = token; + this.options = Object.assign(Object.assign({}, exports.botOptions), options); + this.api = new api_1.BotAPI(this, this.token); + // Sets bot sharding data + const shardId = parseInt(process.env.SHARD_ID); + const shardAmount = parseInt(process.env.SHARDS_AMOUNT); + this.shardOptions = { + id: shardId, + amount: shardAmount, + }; + this.commands = new command_1.CommandsHandler(); + this.events = new events_1.EventsHandler(); + this.connection = new BotConnection_1.BotConnection(this, token); + this.communication = new sharding_1.BotCommunication(this); + this.guilds = new bot_1.BotGuildsController(this); + this.unavailableGuilds = new Collection_1.default(); + this.channels = new bot_1.BotChannelsController(this); + this.users = new bot_1.BotUsersController(this); + this.emojis = new Collection_1.default(); + } + /** + * Connects the bot to the Discord gateway + * @returns {Promise} + */ + connect() { + return this.connection.connect(); + } + /** + * Sends debug messages to the {@link BotEvent.Debug} event + * @example ```typescript + * bot.on(BotEvents.Debug, console.log); + * + * bot.debug('Hello World!'); // 'Hello World' + * ``` + * @param {...unknown[]} messages The debug messages + */ + debug(...messages) { + this.events.emit(socket_1.BotEvent.Debug, ...messages); + } + /** + * @ignore + * @returns {Serializable} + */ + toJSON() { + return { + token: this.token, + shardOptions: this.shardOptions, + commands: this.commands, + events: this.events, + }; + } +} +exports.Bot = Bot; diff --git a/lib/bot/BotConnection.d.ts b/lib/bot/BotConnection.d.ts new file mode 100644 index 000000000..ee5a586d2 --- /dev/null +++ b/lib/bot/BotConnection.d.ts @@ -0,0 +1,37 @@ +import { Bot } from './Bot'; +import Collection from '../Collection'; +import { BotSocketShard, GatewayCloseCode } from '../socket'; +import { GatewayStruct } from '../structures'; +import { ShardId } from '../types'; +/** + * Responsible for the creation and closure of the WebSocket connection to the Discord API gateway + */ +export declare class BotConnection { + /** + * Bot socket connection (may split into shards) + */ + private readonly socket; + constructor(bot: Bot, token: string); + /** + * Creates a new bot connection + * @returns {Promise} + */ + connect(): Promise; + /** + * Closes the currently running connection + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnect(code?: GatewayCloseCode): void; + /** + * Closes all currently running connections for all shards + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnectAll(code?: GatewayCloseCode): void; + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @returns {void} + */ + modifyPresence(presence: GatewayStruct): void; + get shards(): Collection; +} diff --git a/lib/bot/BotConnection.js b/lib/bot/BotConnection.js new file mode 100644 index 000000000..309b60dec --- /dev/null +++ b/lib/bot/BotConnection.js @@ -0,0 +1,63 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotConnection = void 0; +const socket_1 = require("../socket"); +/** + * Responsible for the creation and closure of the WebSocket connection to the Discord API gateway + */ +class BotConnection { + constructor(bot, token) { + this.socket = new socket_1.BotSocket(bot, token); + } + /** + * Creates a new bot connection + * @returns {Promise} + */ + connect() { + return __awaiter(this, void 0, void 0, function* () { + yield this.socket.startShards(); + }); + } + /** + * Closes the currently running connection + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnect(code = 3000 /* ManualClosure */) { + this.socket.stopShards(code); + } + /** + * Closes all currently running connections for all shards + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnectAll(code = 3000 /* ManualClosure */) { + if (process.send) { + const request = { + action: "disconnectAll" /* DisconnectAll */, + payload: code, + identifier: Date.now(), + }; + process.send(request); + } + } + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @returns {void} + */ + modifyPresence(presence) { + this.socket.modifyPresence(presence); + } + get shards() { + return this.socket.shards; + } +} +exports.BotConnection = BotConnection; diff --git a/lib/bot/handlers/Handler.d.ts b/lib/bot/handlers/Handler.d.ts new file mode 100644 index 000000000..4599526ea --- /dev/null +++ b/lib/bot/handlers/Handler.d.ts @@ -0,0 +1,30 @@ +/** + * All possible events for a handler to listen to. + * You can register a listener method for any of these events by using the {@link RegisterCommandHandler} or {@link RegisterEventHandler} decorators or by using the {@link Handler.registerHandler} method + */ +export declare enum HandlerEvent { + /** + * This will fire right before the handler is executed + */ + Before = "before", + /** + * This will fire when the handler is executed + */ + Execute = "execute", + /** + * This will fire right after the handler is executed + */ + After = "after" +} +/** + * @template T + */ +export declare abstract class Handler { + /** + * Registers an item to this handler + * @param {T} item The item to register + */ + abstract register(item: T): void; +} diff --git a/lib/bot/handlers/Handler.js b/lib/bot/handlers/Handler.js new file mode 100644 index 000000000..05d0a75d5 --- /dev/null +++ b/lib/bot/handlers/Handler.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Handler = exports.HandlerEvent = void 0; +/** + * All possible events for a handler to listen to. + * You can register a listener method for any of these events by using the {@link RegisterCommandHandler} or {@link RegisterEventHandler} decorators or by using the {@link Handler.registerHandler} method + */ +var HandlerEvent; +(function (HandlerEvent) { + /** + * This will fire right before the handler is executed + */ + HandlerEvent["Before"] = "before"; + /** + * This will fire when the handler is executed + */ + HandlerEvent["Execute"] = "execute"; + /** + * This will fire right after the handler is executed + */ + HandlerEvent["After"] = "after"; +})(HandlerEvent = exports.HandlerEvent || (exports.HandlerEvent = {})); +/** + * @template T + */ +class Handler { +} +exports.Handler = Handler; diff --git a/lib/bot/handlers/HandlerItem.d.ts b/lib/bot/handlers/HandlerItem.d.ts new file mode 100644 index 000000000..ebfd429ea --- /dev/null +++ b/lib/bot/handlers/HandlerItem.d.ts @@ -0,0 +1,56 @@ +import { HandlerEvent } from './Handler'; +import { Bot } from '../Bot'; +/** + * Default handler function + */ +export declare type HandlerFunction = (...args: any[]) => void; +/** + * Default handler attributes + */ +export declare type HandlerAttributes = { + name: T; +}; +/** + * Represents a handler for commands and events. + * Contains inner handlers for every {@link HandlerEvent} event + */ +export declare class HandlerItem, TEvents extends Record> { + /** + * The bot instance + */ + protected bot: Bot; + /** + * The attributes for the handler item + */ + attributes: TAttrs; + /** + * The event handlers applied by the {@link RegisterCommandHandler} and {@link RegisterEventHandler} decorators. + * The handlers will be asynchronously executed in order when the handler item is called + * @ignore + */ + handlers: Record; + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot: Bot, attributes?: TAttrs); + /** + * Runs all handlers for a specific event + * @param {T} event The event + * @param {any} args The arguments for this event's listener function + * @returns {Promise} + * @ignore + */ + runAll(event: T, ...args: Parameters): Promise; + /** + * Registers an event handler in the {@link EventEmitter} associated to this Command + * @param {T} name The name of the event + * @param {Function} callback The listener for this callback + */ + protected registerHandler(name: T, callback: TEvents[T]): void; + /** + * Returns this command's name + * @type {string} + */ + get name(): TName; +} diff --git a/lib/bot/handlers/HandlerItem.js b/lib/bot/handlers/HandlerItem.js new file mode 100644 index 000000000..8ef116cf7 --- /dev/null +++ b/lib/bot/handlers/HandlerItem.js @@ -0,0 +1,65 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HandlerItem = void 0; +/** + * Represents a handler for commands and events. + * Contains inner handlers for every {@link HandlerEvent} event + */ +class HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot, attributes) { + this.bot = bot; + // Applies the attributes given by the constructor if the class register decorator wasn't used + if (attributes) { + this.attributes = attributes; + } + if (!this.attributes) { + throw new Error('No attributes were provided for this command'); + } + if (!this.handlers) { + this.handlers = { before: [], after: [], execute: [] }; + } + } + /** + * Runs all handlers for a specific event + * @param {T} event The event + * @param {any} args The arguments for this event's listener function + * @returns {Promise} + * @ignore + */ + runAll(event, ...args) { + return __awaiter(this, void 0, void 0, function* () { + for (const handler of this.handlers[event]) { + yield handler.bind(this)(...args); + } + }); + } + /** + * Registers an event handler in the {@link EventEmitter} associated to this Command + * @param {T} name The name of the event + * @param {Function} callback The listener for this callback + */ + registerHandler(name, callback) { + this.handlers[name].push(callback); + } + /** + * Returns this command's name + * @type {string} + */ + get name() { + return this.attributes.name; + } +} +exports.HandlerItem = HandlerItem; diff --git a/lib/bot/handlers/command/Command.d.ts b/lib/bot/handlers/command/Command.d.ts new file mode 100644 index 000000000..ffdc00544 --- /dev/null +++ b/lib/bot/handlers/command/Command.d.ts @@ -0,0 +1,55 @@ +import { Message } from '../../../structures/message'; +import { Bot } from '../../Bot'; +import { HandlerEvent } from '../Handler'; +import { HandlerItem, HandlerFunction } from '../HandlerItem'; +export declare type CommandExecuteFunction = (message: Message, ...args: any[]) => T; +/** + * The {@link HandlerEvent}s listeners signatures for commands + */ +export interface CommandEvents extends Record { + [HandlerEvent.Execute]: CommandExecuteFunction; + [HandlerEvent.Before]: () => T; + [HandlerEvent.After]: () => T; +} +/** + * The attributes for a command + */ +export interface CommandAttributes { + /** + * The name of the command. + * This is used for mapping the commands in the Bot's commands Collection {@link CommandsHandler.commands} + */ + name: string; +} +/** + * Represents an abstract bot command for commands to extend from + */ +export declare abstract class Command extends HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot: Bot, attributes?: CommandAttributes); +} +/** + * Decorator for registering a command + * @example ```typescript + * @RegisterCommand({ name: 'ping' }) + * export class Ping extends Command { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Command): void} + */ +export declare function RegisterCommand(attributes: CommandAttributes): (constructor: typeof Command) => void; +/** + * Decorator for registering an event handler for a command + * @example ```typescript + * @RegisterCommandHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log('Pong!', message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +export declare function RegisterCommandHandler(name: THandlerName): (target: T, propertyKey: keyof T, descriptor: TypedPropertyDescriptor[THandlerName]>) => void; diff --git a/lib/bot/handlers/command/Command.js b/lib/bot/handlers/command/Command.js new file mode 100644 index 000000000..95b3c32ae --- /dev/null +++ b/lib/bot/handlers/command/Command.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RegisterCommandHandler = exports.RegisterCommand = exports.Command = void 0; +const Handler_1 = require("../Handler"); +const HandlerItem_1 = require("../HandlerItem"); +/** + * Represents an abstract bot command for commands to extend from + */ +class Command extends HandlerItem_1.HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot, attributes) { + super(bot, attributes); + // Register this command + bot.commands.register(this); + } +} +exports.Command = Command; +/** + * Decorator for registering a command + * @example ```typescript + * @RegisterCommand({ name: 'ping' }) + * export class Ping extends Command { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Command): void} + */ +function RegisterCommand(attributes) { + return function (constructor) { + constructor.prototype.attributes = attributes; + }; +} +exports.RegisterCommand = RegisterCommand; +/** + * Decorator for registering an event handler for a command + * @example ```typescript + * @RegisterCommandHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log('Pong!', message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +function RegisterCommandHandler(name) { + return function (target, propertyKey, descriptor) { + if (!descriptor.value) + return; + if (!target.handlers) { + target.handlers = { before: [], after: [], execute: [] }; + } + target.handlers[name].push(descriptor.value); + }; +} +exports.RegisterCommandHandler = RegisterCommandHandler; diff --git a/lib/bot/handlers/command/CommandsHandler.d.ts b/lib/bot/handlers/command/CommandsHandler.d.ts new file mode 100644 index 000000000..05f6f6412 --- /dev/null +++ b/lib/bot/handlers/command/CommandsHandler.d.ts @@ -0,0 +1,25 @@ +import { Command, CommandEvents } from './Command'; +import Collection from '../../../Collection'; +import { Handler, HandlerEvent } from '../Handler'; +/** + * Handles the commands associated to a bot + */ +export declare class CommandsHandler implements Handler { + /** + * The commands associated to this handler mapped by their names + */ + readonly commands: Collection; + constructor(); + /** + * Executes a command and runs its event handlers + * @param {string} name The name of the command + * @param {any} args The arguments to give the command's {@link HandlerEvent.Execute} event handler + * @returns {boolean} + */ + execute(name: string, ...args: Parameters): Promise; + /** + * Registers a command to this handler + * @param {Command} command The command to register + */ + register(command: Command): void; +} diff --git a/lib/bot/handlers/command/CommandsHandler.js b/lib/bot/handlers/command/CommandsHandler.js new file mode 100644 index 000000000..c0cec94ea --- /dev/null +++ b/lib/bot/handlers/command/CommandsHandler.js @@ -0,0 +1,50 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CommandsHandler = void 0; +const Collection_1 = __importDefault(require("../../../Collection")); +const Handler_1 = require("../Handler"); +/** + * Handles the commands associated to a bot + */ +class CommandsHandler { + constructor() { + this.commands = new Collection_1.default(); + } + /** + * Executes a command and runs its event handlers + * @param {string} name The name of the command + * @param {any} args The arguments to give the command's {@link HandlerEvent.Execute} event handler + * @returns {boolean} + */ + execute(name, ...args) { + return __awaiter(this, void 0, void 0, function* () { + if (!this.commands.has(name)) + return false; + const command = this.commands.get(name); + yield command.runAll(Handler_1.HandlerEvent.Before); + yield command.runAll(Handler_1.HandlerEvent.Execute, ...args); + yield command.runAll(Handler_1.HandlerEvent.After); + return true; + }); + } + /** + * Registers a command to this handler + * @param {Command} command The command to register + */ + register(command) { + this.commands.set(command.name, command); + } +} +exports.CommandsHandler = CommandsHandler; diff --git a/lib/bot/handlers/command/index.d.ts b/lib/bot/handlers/command/index.d.ts new file mode 100644 index 000000000..f13e079a8 --- /dev/null +++ b/lib/bot/handlers/command/index.d.ts @@ -0,0 +1,2 @@ +export * from './Command'; +export * from './CommandsHandler'; diff --git a/lib/bot/handlers/command/index.js b/lib/bot/handlers/command/index.js new file mode 100644 index 000000000..ee87b6888 --- /dev/null +++ b/lib/bot/handlers/command/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Command"), exports); +__exportStar(require("./CommandsHandler"), exports); diff --git a/lib/bot/handlers/events/Event.d.ts b/lib/bot/handlers/events/Event.d.ts new file mode 100644 index 000000000..6daa7894c --- /dev/null +++ b/lib/bot/handlers/events/Event.d.ts @@ -0,0 +1,54 @@ +import { Events } from './events'; +import { BotEvent } from '../../../socket'; +import { Bot } from '../../Bot'; +import { HandlerEvent } from '../Handler'; +import { HandlerFunction, HandlerItem } from '../HandlerItem'; +/** + * The {@link HandlerEvent}s listeners signatures for events + */ +export interface EventEvents extends Record { + [HandlerEvent.Execute]: Events[T]; + [HandlerEvent.Before]: () => TReturn; + [HandlerEvent.After]: () => TReturn; +} +/** + * The attributes for a event + */ +export interface EventAttributes { + /** + * The name of the event + */ + name: T; +} +/** + * Represents an abstract bot event for events to extend from + */ +export declare class Event extends HandlerItem, EventEvents> { + /** + * @param {Bot} bot The bot instance + * @param {EventAttributes} attributes The attributes for this event if the RegisterEvent decorator wasn't used + */ + constructor(bot: Bot, attributes?: EventAttributes); +} +/** + * Decorator for registering an event + * @example ```typescript + * @RegisterCommand({ name: BotEvent.Ready }) + * export class Ready extends Event { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Event): void} + */ +export declare function RegisterEvent(attributes: EventAttributes): (constructor: Function) => void; +/** + * Decorator for registering an event handler for an event + * @example ```typescript + * @RegisterEventHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log(message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +export declare function RegisterEventHandler(name: THandlerName): , TName extends T extends Event ? N : never, TReturn>(target: T, propertyKey: keyof T, descriptor: TypedPropertyDescriptor[THandlerName]>) => void; diff --git a/lib/bot/handlers/events/Event.js b/lib/bot/handlers/events/Event.js new file mode 100644 index 000000000..ba14358e2 --- /dev/null +++ b/lib/bot/handlers/events/Event.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RegisterEventHandler = exports.RegisterEvent = exports.Event = void 0; +const Handler_1 = require("../Handler"); +const HandlerItem_1 = require("../HandlerItem"); +/** + * Represents an abstract bot event for events to extend from + */ +class Event extends HandlerItem_1.HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {EventAttributes} attributes The attributes for this event if the RegisterEvent decorator wasn't used + */ + constructor(bot, attributes) { + super(bot, attributes); + // Register this command + bot.events.register(this); + } +} +exports.Event = Event; +/** + * Decorator for registering an event + * @example ```typescript + * @RegisterCommand({ name: BotEvent.Ready }) + * export class Ready extends Event { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Event): void} + */ +function RegisterEvent(attributes) { + return function (constructor) { + constructor.prototype.attributes = attributes; + }; +} +exports.RegisterEvent = RegisterEvent; +/** + * Decorator for registering an event handler for an event + * @example ```typescript + * @RegisterEventHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log(message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +function RegisterEventHandler(name) { + return function (target, propertyKey, descriptor) { + if (!descriptor.value) + return; + if (!target.handlers) { + target.handlers = { before: [], after: [], execute: [] }; + } + target.handlers[name].push(descriptor.value); + }; +} +exports.RegisterEventHandler = RegisterEventHandler; diff --git a/lib/bot/handlers/events/EventsHandler.d.ts b/lib/bot/handlers/events/EventsHandler.d.ts new file mode 100644 index 000000000..2afd8c9b1 --- /dev/null +++ b/lib/bot/handlers/events/EventsHandler.d.ts @@ -0,0 +1,29 @@ +import TypedEventEmitter from 'typed-emitter'; +import { Event } from './Event'; +import { Events } from './events'; +import { BotEvent } from '../../../socket'; +import { Handler } from '../Handler'; +declare const EventsHandler_base: new () => TypedEventEmitter; +/** + * Responsible for handling all of the Bot's events + */ +export declare class EventsHandler extends EventsHandler_base implements Handler> { + /** + * Registers an event to be fired when the event's name is dispatched by the API + * @param {Event} event The event to be fired + */ + register(event: Event): void; + /** + * Asynchronously waits until an event is executed, and returns its arguments in an array + * @example ```typescript + * // Waits until the event is called + * const [ message ] = await bot.events.wait(BotEvents.MessageCreate); + * // You can now use 'message'... + * ``` + * @param {T} name The name of the event + * @returns {Promise} The arguments of the event in an array + * @template E - the event name + */ + wait>(name: T): Promise; +} +export {}; diff --git a/lib/bot/handlers/events/EventsHandler.js b/lib/bot/handlers/events/EventsHandler.js new file mode 100644 index 000000000..c032815e6 --- /dev/null +++ b/lib/bot/handlers/events/EventsHandler.js @@ -0,0 +1,51 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventsHandler = void 0; +const events_1 = require("events"); +const Handler_1 = require("../Handler"); +/** + * Responsible for handling all of the Bot's events + */ +class EventsHandler extends events_1.EventEmitter { + /** + * Registers an event to be fired when the event's name is dispatched by the API + * @param {Event} event The event to be fired + */ + register(event) { + this.on(event.name, ((...args) => __awaiter(this, void 0, void 0, function* () { + yield event.runAll(Handler_1.HandlerEvent.Before); + yield event.runAll(Handler_1.HandlerEvent.Execute, ...args); + yield event.runAll(Handler_1.HandlerEvent.After); + }))); + } + /** + * Asynchronously waits until an event is executed, and returns its arguments in an array + * @example ```typescript + * // Waits until the event is called + * const [ message ] = await bot.events.wait(BotEvents.MessageCreate); + * // You can now use 'message'... + * ``` + * @param {T} name The name of the event + * @returns {Promise} The arguments of the event in an array + * @template E - the event name + */ + wait(name) { + return new Promise(resolve => { + const listener = ((...args) => { + resolve(args); + this.removeListener(name, listener); + }); + this.on(name, listener); + }); + } +} +exports.EventsHandler = EventsHandler; diff --git a/lib/bot/handlers/events/events.d.ts b/lib/bot/handlers/events/events.d.ts new file mode 100644 index 000000000..e61dd9518 --- /dev/null +++ b/lib/bot/handlers/events/events.d.ts @@ -0,0 +1,351 @@ +import Collection from '../../../Collection'; +import { BotSocketShard, BotEvent } from '../../../socket'; +import { GuildMembersChunk } from '../../../socket/handlers/guildMembersChunk'; +import { Emoji, Invite, PartialInvite, Role, Timestamp, User, TextBasedChannel } from '../../../structures'; +import { Channel, GuildChannel } from '../../../structures/channels'; +import { Guild, GuildUnavailable, GuildBan } from '../../../structures/guild'; +import { Member, MemberPresence } from '../../../structures/member'; +import { Message, PartialMessage, MessageReaction } from '../../../structures/message'; +import VoiceState from '../../../structures/voice/VoiceState'; +import { Snowflake } from '../../../types'; +/** + * Sent when all shards become ready + * @asMemberOf EventsHandler + * @event BotEventsHandler#READY + */ +declare function READY(): any; +/** + * Sent when a shard becomes ready + * @param {BotSocketShard} shard + * @asMemberOf EventsHandler + * @event BotEventsHandler#SHARD_READY + */ +declare function SHARD_READY(shard: BotSocketShard): any; +/** + * Sent when a shard closes (disconnects) + * @param {BotSocketShard} shard + * @asMemberOf EventsHandler + * @event BotEventsHandler#SHARD_CLOSE + */ +declare function SHARD_CLOSE(shard: BotSocketShard): any; +/** + * Sent when a new channel is created + * @param {Channel} channel The new channel + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_CREATE + */ +declare function CHANNEL_CREATE(channel: Channel): any; +/** + * Sent when a channel is updated. + * This is not sent when the field {@link GuildTextChannel.lastMessageId} is altered. + * To keep track of the lastMessageId changes, you must listen for {@link MESSAGE_CREATE} events + * @param {Channel} before The channel before being updated + * @param {Channel} after The channel after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_UPDATE + */ +declare function CHANNEL_UPDATE(before: Channel, after: Channel): any; +/** + * Sent when a channel is deleted + * @param {Channel} channel The deleted channel + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_DELETE + */ +declare function CHANNEL_DELETE(channel: Channel): any; +/** + * Sent when a message is pinned or unpinned in a text channel. + * This is not sent when a pinned message is deleted + * @param {TextBasedChannel} channel The channel which pins were updated + * @param {number | undefined} oldPinTimestamp The previous last pin timestamp + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_PINS_UPDATE + */ +declare function CHANNEL_PINS_UPDATE(channel: TextBasedChannel, oldPinTimestamp: Timestamp | undefined): any; +/** + * Sent when the Bot joins a guild, or a guild becomes available to the Bot + * @param {Guild | GuildUnavailable} guild The guild that was created + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_CREATE + */ +declare function GUILD_CREATE(guild: Guild | GuildUnavailable): any; +/** + * Sent when a guild is updated + * @param {Guild | GuildUnavailable} before The guild before being updated + * @param {Guild | GuildUnavailable} after The guild after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_UPDATE + */ +declare function GUILD_UPDATE(before: Guild | GuildUnavailable, after: Guild | GuildUnavailable): any; +/** + * Sent when a guild becomes unavailable during a guild outage, or when the user leaves or is removed from a guild + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_DELETE + */ +declare function GUILD_DELETE(guild: Guild | GuildUnavailable): any; +/** + * Sent when a user is banned from a guild + * @param {GuildBan} ban The guild ban object + * @asMemberOf EventsHandler + * @event BotEventHandler#GUILD_BAN_ADD + */ +declare function GUILD_BAN_ADD(ban: GuildBan): any; +/** + * Sent when a user is unbanned from a guild. + * @param {GuildBan} ban The guild ban object. Possibly undefined if the ban is yet to be cached + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_BAN_ADD + */ +declare function GUILD_BAN_REMOVE(ban: GuildBan | undefined): any; +/** + * Sent when a guild's emojis have been updated. + * @param {Collection} before {@link Collection} of {@link Emoji}s before the update + * @param {Collection} after {@link Collection} of {@link Emoji}s after the update + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_EMOJIS_UPDATE + */ +declare function GUILD_EMOJIS_UPDATE(before: Collection, after: Collection): any; +/** + * Sent when a guild integration is updated. + * @param {Guild} guild The guild whose integrations were updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_INTEGRATIONS_UPDATE + */ +declare function GUILD_INTEGRATIONS_UPDATE(guild: Guild): any; +/** + * Sent when a new user joins a guild + * @param {Member} member The member that joined the guild + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBER_ADD + */ +declare function GUILD_MEMBER_ADD(member: Member): any; +/** + * Sent when a user is removed from a guild (leave/kick/ban). + * @param {Member | User} member The member that left the guild + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBER_REMOVE + */ +declare function GUILD_MEMBER_REMOVE(member: Member | User): any; +/** + * Sent when a guild member is updated. This will also fire when the user object of a guild member changes. + * @param {Member} before The member before being updated + * @param {Member} after The member after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBER_UPDATE + */ +declare function GUILD_MEMBER_UPDATE(before: Member, after: Member): any; +/** + * Sent in response to a Guild Members request + * @param {Guild} guild The guild whose members were requested + * @param {string} nonce The nonce used in the Guild Members Request + * @param {GuildMembersChunk} chunk The information for the chunk that activated this event + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBERS_CHUNK + */ +declare function GUILD_MEMBERS_CHUNK(guild: Guild, nonce: string | undefined, chunk: GuildMembersChunk): any; +/** + * Sent when all Guild Member Chunks for a request are collected + * @param {Guild} guild The guild whose members were requested + * @param {string | undefined} nonce The nonce used in the Guild Members Request + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBERS_CHUNK_FINISH + */ +declare function GUILD_MEMBERS_CHUNK_FINISH(guild: Guild, nonce: string | undefined): any; +/** + * Sent when a guild role is created + * @param {Role} role The newly created role + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_ROLE_CREATE + */ +declare function GUILD_ROLE_CREATE(role: Role): any; +/** + * Sent when a guild role is updated + * @param {Role} before The role before being updated + * @param {Role} after The role after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_ROLE_UPDATE + */ +declare function GUILD_ROLE_UPDATE(before: Role, after: Role): any; +/** + * Sent when a guild role is deleted + * @param {Role} role The role that has been deleted + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_ROLE_DELETE + */ +declare function GUILD_ROLE_DELETE(role: Role): any; +/** + * Sent when a new invite to a channel is created. + * @param {Invite} invite The invite that has been created + * @asMemberOf EventsHandler + * @event BotEventsHandler#INVITE_CREATE + */ +declare function INVITE_CREATE(invite: Invite): any; +/** + * Sent when an invite is deleted. + * @param {Invite | PartialInvite} invite The {@link Invite} that has been deleted. Possibly {@link PartialInvite} if the invite has not been cached. + * @asMemberOf EventsHandler + * @event BotEventsHandler#INVITE_DELETE + */ +declare function INVITE_DELETE(invite: Invite | PartialInvite): any; +/** + * Sent when a message is created + * @param {Message} message The message that has been created + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_CREATE + */ +declare function MESSAGE_CREATE(message: Message): any; +/** + * Sent when a message is updated + * @param {Message | undefined} before The message before being updated + * @param {Message} after The message after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_UPDATE + */ +declare function MESSAGE_UPDATE(before: Message | undefined, after: Message): any; +/** + * Sent when a message is deleted + * @param {Message | PartialMessage} message The deleted message + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_DELETE + */ +declare function MESSAGE_DELETE(message: Message | PartialMessage): any; +/** + * Sent when multiple messages are deleted at once. + * @param {TextBasedChannel} channel The channel the messages were deleted in + * @param {(Message | Snowflake)[]} messages Array of the deleted messages. + * Cached messages will show as {@link Message}s while the rest will show as {@link Snowflake}s + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_DELETE_BULK + */ +declare function MESSAGE_DELETE_BULK(channel: TextBasedChannel, messages: (Message | Snowflake)[]): any; +/** + * Sent when a user adds a reaction to a message. + * @param {MessageReaction} reaction The reaction the user has added to the message + * @param {Member | User} user The user that added the reaction + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_ADD + */ +declare function MESSAGE_REACTION_ADD(reaction: MessageReaction, user: Member | User | undefined): any; +/** + * Sent when a user removes a reaction from a message. + * @param {MessageReaction} reaction The reaction the user has removed from the message + * @param {Member | User | undefined} user The user that removed the reaction + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_REMOVE + */ +declare function MESSAGE_REACTION_REMOVE(reaction: MessageReaction, user: Member | User | undefined): any; +/** + * Sent when a user explicitly removes all reactions from a message. + * @param {Message} message The message of which reactions were removed + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_REMOVE_ALL + */ +declare function MESSAGE_REACTION_REMOVE_ALL(message: Message): any; +/** + * Sent when a bot removes all instances of a given emoji from the reactions of a message. + * @param {MessageReaction} reaction The reaction associated to the emoji which was removed in its original state. + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_REMOVE_EMOJI + */ +declare function MESSAGE_REACTION_REMOVE_EMOJI(reaction: MessageReaction | undefined): any; +/** + * Sent when a member's presence or info, such as name or avatar, is updated. + * @param {MemberPresence | undefined} before The member's presence before being updated + * @param {MemberPresence} after The member's presence after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#PRESENCE_UPDATE + */ +declare function PRESENCE_UPDATE(before: MemberPresence | undefined, after: MemberPresence): any; +/** + * Sent when a user starts typing in a channel. + * @param {TextBasedChannel} channel The channel the user started typing in + * @param {Member | User} user The user that started typing + * @param {number} startedAt The unix time (in seconds) of when the user started typing + * @asMemberOf EventsHandler + * @event BotEventsHandler#TYPING_START + */ +declare function TYPING_START(channel: TextBasedChannel | undefined, user: Member | User | undefined, startedAt: number): any; +/** + * Sent when properties about the Bot's user change + * @param {User} before The user before being updated + * @param {User} after The user after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#USER_UPDATE + */ +declare function USER_UPDATE(before: User, after: User): any; +/** + * Sent when a guild channel's webhook is created, updated, or deleted. + * @param {GuildChannel} channel The guild channel the updated webhook is associated to + * @asMemberOf EventsHandler + * @event BotEventsHandler#WEBHOOKS_UPDATE + */ +declare function WEBHOOKS_UPDATE(channel: GuildChannel): any; +/** + * Sent whenever a new voice server update comes + * @param {Guild} guild The guild that happens in it + * @param {{ token: string; endpoint: string }} voiceServerStats The new voice server information + * @asMemberOf EventsHandler + * @event BotEventsHandler#VOICE_SERVER_UPDATE + */ +declare function VOICE_SERVER_UPDATE(guild: Guild, voiceServer: { + token: string; + endpoint: string; +}): any; +/** + * Sent whenever a voice state changes + * @param {VoiceState} old The old one + * @param {VoiceState} new The new one + * @asMemberOf EventHandler + * @event BotEventsHandler#VOICE_STATE_UPDATE + */ +declare function VOICE_STATE_UPDATE(oldState: VoiceState, newState: VoiceState): any; +/** + * Events that are called when all Bot shards change their state. + * These events take no arguments + */ +export declare type BotStateEvents = BotEvent.Ready | BotEvent.Close; +/** + * All possible events for the bot and their callback function + */ +export interface Events { + [BotEvent.Debug]: typeof console.log; + [BotEvent.Ready]: typeof READY; + [BotEvent.Close]: () => any; + [BotEvent.ChannelCreate]: typeof CHANNEL_CREATE; + [BotEvent.ChannelUpdate]: typeof CHANNEL_UPDATE; + [BotEvent.ChannelDelete]: typeof CHANNEL_DELETE; + [BotEvent.ChannelPinsUpdate]: typeof CHANNEL_PINS_UPDATE; + [BotEvent.GuildCreate]: typeof GUILD_CREATE; + [BotEvent.GuildUpdate]: typeof GUILD_UPDATE; + [BotEvent.GuildDelete]: typeof GUILD_DELETE; + [BotEvent.GuildBanAdd]: typeof GUILD_BAN_ADD; + [BotEvent.GuildBanRemove]: typeof GUILD_BAN_REMOVE; + [BotEvent.GuildEmojisUpdate]: typeof GUILD_EMOJIS_UPDATE; + [BotEvent.GuildIntegrationsUpdate]: typeof GUILD_INTEGRATIONS_UPDATE; + [BotEvent.GuildMemberAdd]: typeof GUILD_MEMBER_ADD; + [BotEvent.GuildMemberRemove]: typeof GUILD_MEMBER_REMOVE; + [BotEvent.GuildMemberUpdate]: typeof GUILD_MEMBER_UPDATE; + [BotEvent.GuildMembersChunk]: typeof GUILD_MEMBERS_CHUNK; + [BotEvent.GuildMembersChunkFinish]: typeof GUILD_MEMBERS_CHUNK_FINISH; + [BotEvent.GuildRoleCreate]: typeof GUILD_ROLE_CREATE; + [BotEvent.GuildRoleUpdate]: typeof GUILD_ROLE_UPDATE; + [BotEvent.GuildRoleDelete]: typeof GUILD_ROLE_DELETE; + [BotEvent.InviteCreate]: typeof INVITE_CREATE; + [BotEvent.InviteDelete]: typeof INVITE_DELETE; + [BotEvent.MessageCreate]: typeof MESSAGE_CREATE; + [BotEvent.MessageUpdate]: typeof MESSAGE_UPDATE; + [BotEvent.MessageDelete]: typeof MESSAGE_DELETE; + [BotEvent.MessageDeleteBulk]: typeof MESSAGE_DELETE_BULK; + [BotEvent.MessageReactionAdd]: typeof MESSAGE_REACTION_ADD; + [BotEvent.MessageReactionRemove]: typeof MESSAGE_REACTION_REMOVE; + [BotEvent.MessageReactionRemoveAll]: typeof MESSAGE_REACTION_REMOVE_ALL; + [BotEvent.MessageReactionRemoveEmoji]: typeof MESSAGE_REACTION_REMOVE_EMOJI; + [BotEvent.PresenceUpdate]: typeof PRESENCE_UPDATE; + [BotEvent.ShardReady]: typeof SHARD_READY; + [BotEvent.ShardClose]: typeof SHARD_CLOSE; + [BotEvent.TypingStart]: typeof TYPING_START; + [BotEvent.UserUpdate]: typeof USER_UPDATE; + [BotEvent.WebhooksUpdate]: typeof WEBHOOKS_UPDATE; + [BotEvent.VoiceServerUpdate]: typeof VOICE_SERVER_UPDATE; + [BotEvent.VoiceStateUpdate]: typeof VOICE_STATE_UPDATE; +} +export {}; diff --git a/lib/bot/handlers/events/events.js b/lib/bot/handlers/events/events.js new file mode 100644 index 000000000..a93d2f4ee --- /dev/null +++ b/lib/bot/handlers/events/events.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const socket_1 = require("../../../socket"); diff --git a/lib/bot/handlers/events/index.d.ts b/lib/bot/handlers/events/index.d.ts new file mode 100644 index 000000000..b8cfc73fc --- /dev/null +++ b/lib/bot/handlers/events/index.d.ts @@ -0,0 +1,2 @@ +export * from './Event'; +export * from './EventsHandler'; diff --git a/lib/bot/handlers/events/index.js b/lib/bot/handlers/events/index.js new file mode 100644 index 000000000..f19d9d0b4 --- /dev/null +++ b/lib/bot/handlers/events/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Event"), exports); +__exportStar(require("./EventsHandler"), exports); diff --git a/lib/bot/handlers/index.d.ts b/lib/bot/handlers/index.d.ts new file mode 100644 index 000000000..d3808239f --- /dev/null +++ b/lib/bot/handlers/index.d.ts @@ -0,0 +1,4 @@ +export * from './command'; +export * from './events'; +export * from './Handler'; +export * from './HandlerItem'; diff --git a/lib/bot/handlers/index.js b/lib/bot/handlers/index.js new file mode 100644 index 000000000..6136a0649 --- /dev/null +++ b/lib/bot/handlers/index.js @@ -0,0 +1,16 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./command"), exports); +__exportStar(require("./events"), exports); +__exportStar(require("./Handler"), exports); +__exportStar(require("./HandlerItem"), exports); diff --git a/lib/bot/index.d.ts b/lib/bot/index.d.ts new file mode 100644 index 000000000..94836eec7 --- /dev/null +++ b/lib/bot/index.d.ts @@ -0,0 +1,3 @@ +export * from './handlers'; +export * from './Bot'; +export * from './BotConnection'; diff --git a/lib/bot/index.js b/lib/bot/index.js new file mode 100644 index 000000000..700537422 --- /dev/null +++ b/lib/bot/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./handlers"), exports); +__exportStar(require("./Bot"), exports); +__exportStar(require("./BotConnection"), exports); diff --git a/lib/controllers/ControllerCache.d.ts b/lib/controllers/ControllerCache.d.ts new file mode 100644 index 000000000..95666b6ad --- /dev/null +++ b/lib/controllers/ControllerCache.d.ts @@ -0,0 +1,19 @@ +import Collection from '../Collection'; +import { BaseStructWithId } from '../structures/base'; +/** + * Cache holder for controllers. + * @template T + */ +export declare class ControllerCache extends Collection { + /** + * Adds an item to the cache mapped by its ID + * @param {T} item The item you wish to add + * @returns {T} + */ + add(item: T): T; + /** + * Adds multiple items to the cache + * @param {T[]} items The items you wish to add + */ + addMany(items: T[]): void; +} diff --git a/lib/controllers/ControllerCache.js b/lib/controllers/ControllerCache.js new file mode 100644 index 000000000..94a5cb55d --- /dev/null +++ b/lib/controllers/ControllerCache.js @@ -0,0 +1,30 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ControllerCache = void 0; +const Collection_1 = __importDefault(require("../Collection")); +/** + * Cache holder for controllers. + * @template T + */ +class ControllerCache extends Collection_1.default { + /** + * Adds an item to the cache mapped by its ID + * @param {T} item The item you wish to add + * @returns {T} + */ + add(item) { + this.set(item.id, item); + return item; + } + /** + * Adds multiple items to the cache + * @param {T[]} items The items you wish to add + */ + addMany(items) { + this.merge(items.map(i => [i.id, i])); + } +} +exports.ControllerCache = ControllerCache; diff --git a/lib/controllers/base/BaseController.d.ts b/lib/controllers/base/BaseController.d.ts new file mode 100644 index 000000000..559c87172 --- /dev/null +++ b/lib/controllers/base/BaseController.d.ts @@ -0,0 +1,18 @@ +import { Bot } from '../../bot'; +import { BaseStruct, BaseStructWithId } from '../../structures/base'; +import { ControllerCache } from '../ControllerCache'; +/** + * Provides a base interface for the bot's cached data + * @template T + */ +export declare abstract class BaseController { + /** + * The bot instance + */ + protected readonly bot: Bot; + /** + * The cached data this controller contains + */ + cache: ControllerCache; + constructor(struct: BaseStruct | Bot, limit?: number); +} diff --git a/lib/controllers/base/BaseController.js b/lib/controllers/base/BaseController.js new file mode 100644 index 000000000..9d24a89d2 --- /dev/null +++ b/lib/controllers/base/BaseController.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseController = void 0; +const base_1 = require("../../structures/base"); +const ControllerCache_1 = require("../ControllerCache"); +/** + * Provides a base interface for the bot's cached data + * @template T + */ +class BaseController { + constructor(struct, limit) { + if (struct instanceof base_1.BaseStruct) { + this.bot = struct.bot; + } + else { + this.bot = struct; + } + this.cache = new ControllerCache_1.ControllerCache(null, limit); + } +} +exports.BaseController = BaseController; diff --git a/lib/controllers/base/BaseCreateController.d.ts b/lib/controllers/base/BaseCreateController.d.ts new file mode 100644 index 000000000..44710efcd --- /dev/null +++ b/lib/controllers/base/BaseCreateController.d.ts @@ -0,0 +1,14 @@ +import { BaseController } from './BaseController'; +import { BaseStructWithId } from '../../structures/base'; +/** + * Base controller with create capabilities + * @template TStruct + * @template TOptions + */ +export declare abstract class BaseCreateController extends BaseController { + /** + * Creates a new item and caches it + * @returns {Promise} + */ + abstract create(options?: TOptions): Promise; +} diff --git a/lib/controllers/base/BaseCreateController.js b/lib/controllers/base/BaseCreateController.js new file mode 100644 index 000000000..b5e6ef30c --- /dev/null +++ b/lib/controllers/base/BaseCreateController.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseCreateController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with create capabilities + * @template TStruct + * @template TOptions + */ +class BaseCreateController extends BaseController_1.BaseController { +} +exports.BaseCreateController = BaseCreateController; diff --git a/lib/controllers/base/BaseDeleteController.d.ts b/lib/controllers/base/BaseDeleteController.d.ts new file mode 100644 index 000000000..f4ca20a69 --- /dev/null +++ b/lib/controllers/base/BaseDeleteController.d.ts @@ -0,0 +1,15 @@ +import { BaseController } from './BaseController'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with delete capabilities + * @template T + */ +export declare abstract class BaseDeleteController extends BaseController { + /** + * Deletes a cached item + * @param {Snowflake} id The ID of the item you wish to delete + * @returns {Promise} + */ + abstract delete(id: Snowflake | string): Promise; +} diff --git a/lib/controllers/base/BaseDeleteController.js b/lib/controllers/base/BaseDeleteController.js new file mode 100644 index 000000000..277fec95c --- /dev/null +++ b/lib/controllers/base/BaseDeleteController.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseDeleteController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with delete capabilities + * @template T + */ +class BaseDeleteController extends BaseController_1.BaseController { +} +exports.BaseDeleteController = BaseDeleteController; diff --git a/lib/controllers/base/BaseFetchAllController.d.ts b/lib/controllers/base/BaseFetchAllController.d.ts new file mode 100644 index 000000000..a43107fde --- /dev/null +++ b/lib/controllers/base/BaseFetchAllController.d.ts @@ -0,0 +1,15 @@ +import { BaseController } from './BaseController'; +import Collection from '../../Collection'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with fetch all capabilities + * @template T + */ +export declare abstract class BaseFetchAllController extends BaseController { + /** + * Fetches all items associated to the controller and caches them + * @returns {Promise} + */ + abstract fetchAll(): Promise>; +} diff --git a/lib/controllers/base/BaseFetchAllController.js b/lib/controllers/base/BaseFetchAllController.js new file mode 100644 index 000000000..0eda90751 --- /dev/null +++ b/lib/controllers/base/BaseFetchAllController.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseFetchAllController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with fetch all capabilities + * @template T + */ +class BaseFetchAllController extends BaseController_1.BaseController { +} +exports.BaseFetchAllController = BaseFetchAllController; diff --git a/lib/controllers/base/BaseFetchController.d.ts b/lib/controllers/base/BaseFetchController.d.ts new file mode 100644 index 000000000..2abe03765 --- /dev/null +++ b/lib/controllers/base/BaseFetchController.d.ts @@ -0,0 +1,21 @@ +import { BaseController } from './BaseController'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with fetch capabilities + * @template T + */ +export declare abstract class BaseFetchController extends BaseController { + /** + * Fetches a new item and caches it + * @param {Snowflake | string} id The ID of the item you wish to fetch + * @returns {Promise} + */ + abstract fetch(id: Snowflake | string): Promise; + /** + * Returns an already cached item or fetches it + * @param {Snowflake | string} id The ID of the item you wish to get or fetch + * @returns {Promise} + */ + get(id: Snowflake | string): Promise; +} diff --git a/lib/controllers/base/BaseFetchController.js b/lib/controllers/base/BaseFetchController.js new file mode 100644 index 000000000..be9f73dfd --- /dev/null +++ b/lib/controllers/base/BaseFetchController.js @@ -0,0 +1,30 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseFetchController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with fetch capabilities + * @template T + */ +class BaseFetchController extends BaseController_1.BaseController { + /** + * Returns an already cached item or fetches it + * @param {Snowflake | string} id The ID of the item you wish to get or fetch + * @returns {Promise} + */ + get(id) { + return __awaiter(this, void 0, void 0, function* () { + return this.cache.get(id) || this.fetch(id); + }); + } +} +exports.BaseFetchController = BaseFetchController; diff --git a/lib/controllers/base/BaseFetchSomeController.d.ts b/lib/controllers/base/BaseFetchSomeController.d.ts new file mode 100644 index 000000000..340f53ee0 --- /dev/null +++ b/lib/controllers/base/BaseFetchSomeController.d.ts @@ -0,0 +1,15 @@ +import { BaseController } from './BaseController'; +import Collection from '../../Collection'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with fetch some capabilities + * @template T + */ +export declare abstract class BaseFetchSomeController extends BaseController { + /** + * Fetches some items associated to the controller and caches them + * @returns {Promise} + */ + abstract fetchSome(): Promise>; +} diff --git a/lib/controllers/base/BaseFetchSomeController.js b/lib/controllers/base/BaseFetchSomeController.js new file mode 100644 index 000000000..384ab5c9b --- /dev/null +++ b/lib/controllers/base/BaseFetchSomeController.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseFetchSomeController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with fetch some capabilities + * @template T + */ +class BaseFetchSomeController extends BaseController_1.BaseController { +} +exports.BaseFetchSomeController = BaseFetchSomeController; diff --git a/lib/controllers/base/index.d.ts b/lib/controllers/base/index.d.ts new file mode 100644 index 000000000..548726bab --- /dev/null +++ b/lib/controllers/base/index.d.ts @@ -0,0 +1,6 @@ +export * from './BaseController'; +export * from './BaseCreateController'; +export * from './BaseDeleteController'; +export * from './BaseFetchAllController'; +export * from './BaseFetchSomeController'; +export * from './BaseFetchController'; diff --git a/lib/controllers/base/index.js b/lib/controllers/base/index.js new file mode 100644 index 000000000..2c306d371 --- /dev/null +++ b/lib/controllers/base/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BaseController"), exports); +__exportStar(require("./BaseCreateController"), exports); +__exportStar(require("./BaseDeleteController"), exports); +__exportStar(require("./BaseFetchAllController"), exports); +__exportStar(require("./BaseFetchSomeController"), exports); +__exportStar(require("./BaseFetchController"), exports); diff --git a/lib/controllers/bot/BotChannelsController.d.ts b/lib/controllers/bot/BotChannelsController.d.ts new file mode 100644 index 000000000..387d23edc --- /dev/null +++ b/lib/controllers/bot/BotChannelsController.d.ts @@ -0,0 +1,35 @@ +import { Channel, GuildChannel, TextBasedChannel } from '../../structures/channels'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchController } from '../base'; +/** + * Provides an interface for the bot's channels cache. + * The channels are mapped by their IDs + */ +export declare class BotChannelsController extends BaseFetchController implements BaseDeleteController { + /** + * Deletes a channel + * @param {Snowflake} id The ID of the channel you wish to delete + * @returns {Promise} + */ + delete(id: Snowflake): Promise; + /** + * Fetches a channel + * @param {Snowflake} id The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** @inheritDoc */ + get(id: Snowflake): Promise; + /** + * Gets or fetches a text channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getText(id: Snowflake): Promise; + /** + * Gets or fetches a guild channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getGuildChannel(id: Snowflake): Promise; +} diff --git a/lib/controllers/bot/BotChannelsController.js b/lib/controllers/bot/BotChannelsController.js new file mode 100644 index 000000000..dc7514d86 --- /dev/null +++ b/lib/controllers/bot/BotChannelsController.js @@ -0,0 +1,81 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotChannelsController = void 0; +const channels_1 = require("../../structures/channels"); +const base_1 = require("../base"); +/** + * Provides an interface for the bot's channels cache. + * The channels are mapped by their IDs + */ +class BotChannelsController extends base_1.BaseFetchController { + /** + * Deletes a channel + * @param {Snowflake} id The ID of the channel you wish to delete + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteChannel(id); + } + /** + * Fetches a channel + * @param {Snowflake} id The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetch(id) { + return this.bot.api.fetchChannel(id); + } + /** @inheritDoc */ + get(id) { + const _super = Object.create(null, { + get: { get: () => super.get } + }); + return __awaiter(this, void 0, void 0, function* () { + const channel = yield _super.get.call(this, id); + if (channel instanceof channels_1.GuildChannel) { + channel.guild.channels.cache.add(channel); + } + else if (channel instanceof channels_1.DMChannel) { + channel.recipient.dm = channel; + } + return channel; + }); + } + /** + * Gets or fetches a text channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getText(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildTextChannel || channel instanceof channels_1.DMChannel)) { + throw new TypeError('The channel is not a valid text channel'); + } + return channel; + }); + } + /** + * Gets or fetches a guild channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getGuildChannel(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildChannel)) { + throw new TypeError('The channel is not a valid guild channel'); + } + return channel; + }); + } +} +exports.BotChannelsController = BotChannelsController; diff --git a/lib/controllers/bot/BotGuildsController.d.ts b/lib/controllers/bot/BotGuildsController.d.ts new file mode 100644 index 000000000..d1510f081 --- /dev/null +++ b/lib/controllers/bot/BotGuildsController.d.ts @@ -0,0 +1,31 @@ +import { Guild, GuildPreview } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseFetchController } from '../base'; +/** + * Options for when fetching guilds + */ +export interface FetchGuildOptions { + /** + * When `true`, the fetched guild will contain a full {@link GuildApproximates} object + */ + withCounts?: boolean; +} +/** + * Interface for the bot's guilds cache. + * The guilds are mapped by their IDs + */ +export declare class BotGuildsController extends BaseFetchController { + /** + * Fetches a guild by its ID and caches it + * @param {Snowflake} id The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetch(id: Snowflake, options?: FetchGuildOptions): Promise; + /** + * Fetches a guild preview by its guild ID + * @param {Snowflake} id The ID of the guild + * @returns {Promise} + */ + fetchPreview(id: Snowflake): Promise; +} diff --git a/lib/controllers/bot/BotGuildsController.js b/lib/controllers/bot/BotGuildsController.js new file mode 100644 index 000000000..4daac7b1a --- /dev/null +++ b/lib/controllers/bot/BotGuildsController.js @@ -0,0 +1,41 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotGuildsController = void 0; +const base_1 = require("../base"); +/** + * Interface for the bot's guilds cache. + * The guilds are mapped by their IDs + */ +class BotGuildsController extends base_1.BaseFetchController { + /** + * Fetches a guild by its ID and caches it + * @param {Snowflake} id The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetch(id, options) { + return __awaiter(this, void 0, void 0, function* () { + const guild = yield this.bot.api.fetchGuild(id, options); + this.cache.add(guild); + return guild; + }); + } + /** + * Fetches a guild preview by its guild ID + * @param {Snowflake} id The ID of the guild + * @returns {Promise} + */ + fetchPreview(id) { + return this.bot.api.fetchGuildPreview(id); + } +} +exports.BotGuildsController = BotGuildsController; diff --git a/lib/controllers/bot/BotUsersController.d.ts b/lib/controllers/bot/BotUsersController.d.ts new file mode 100644 index 000000000..cc18ed5fd --- /dev/null +++ b/lib/controllers/bot/BotUsersController.d.ts @@ -0,0 +1,22 @@ +import { Bot } from '../../bot'; +import { BotUser, User } from '../../structures'; +import { Snowflake } from '../../types'; +import { BaseFetchController } from '../base'; +/** + * Provides an interface for the bot's users cache. + * The users are mapped by their IDs + */ +export declare class BotUsersController extends BaseFetchController { + constructor(bot: Bot); + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBot(): Promise; + /** + * Fetches a user by its ID + * @param {Snowflake} id The user ID + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; +} diff --git a/lib/controllers/bot/BotUsersController.js b/lib/controllers/bot/BotUsersController.js new file mode 100644 index 000000000..e11665e51 --- /dev/null +++ b/lib/controllers/bot/BotUsersController.js @@ -0,0 +1,52 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotUsersController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for the bot's users cache. + * The users are mapped by their IDs + */ +class BotUsersController extends base_1.BaseFetchController { + constructor(bot) { + super(bot); + } + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBot() { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.bot.api.fetchBotUser(); + this.cache.add(user); + if (this.bot.user) { + this.bot.user.update(user.structure); + } + else { + this.bot.user = user; + } + return user; + }); + } + /** + * Fetches a user by its ID + * @param {Snowflake} id The user ID + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.bot.api.fetchUser(id); + this.cache.add(user); + return user; + }); + } +} +exports.BotUsersController = BotUsersController; diff --git a/lib/controllers/bot/index.d.ts b/lib/controllers/bot/index.d.ts new file mode 100644 index 000000000..f8f21ddee --- /dev/null +++ b/lib/controllers/bot/index.d.ts @@ -0,0 +1,3 @@ +export * from './BotChannelsController'; +export * from './BotGuildsController'; +export * from './BotUsersController'; diff --git a/lib/controllers/bot/index.js b/lib/controllers/bot/index.js new file mode 100644 index 000000000..9f2364ef0 --- /dev/null +++ b/lib/controllers/bot/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BotChannelsController"), exports); +__exportStar(require("./BotGuildsController"), exports); +__exportStar(require("./BotUsersController"), exports); diff --git a/lib/controllers/channel/ChannelMessagesController.d.ts b/lib/controllers/channel/ChannelMessagesController.d.ts new file mode 100644 index 000000000..c0b864495 --- /dev/null +++ b/lib/controllers/channel/ChannelMessagesController.d.ts @@ -0,0 +1,55 @@ +import Collection from '../../Collection'; +import { TextBasedChannel } from '../../structures/channels'; +import { Message } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchController, BaseFetchSomeController } from '../base'; +/** + * Options for when fetching some messages in a text channel + */ +export interface FetchSomeMessagesOptions { + /** + * Get messages around this message ID + */ + around?: Snowflake; + /** + * Get messages before this message ID + */ + before?: Snowflake; + /** + * Get messages after this message ID + */ + after?: Snowflake; + /** + * The max number of messages to return (1-100) + */ + limit?: number; +} +/** + * Provides an interface for a text channel's messages cache. + * The messages are mapped by their IDs + */ +export declare class ChannelMessagesController extends BaseFetchController implements BaseDeleteController, BaseFetchSomeController { + /** + * The guild this controller is associated to + */ + readonly channel: TextBasedChannel; + constructor(channel: TextBasedChannel); + /** + * Deletes a message in the channel + * @param {Snowflake} id The ID of the message you wish to delete + * @returns {Promise} + */ + delete(id: Snowflake): Promise; + /** + * Fetches and caches a message in the channel + * @param {Snowflake} id The ID of the message you wish to fetch + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches and caches some messages in the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options?: FetchSomeMessagesOptions): Promise>; +} diff --git a/lib/controllers/channel/ChannelMessagesController.js b/lib/controllers/channel/ChannelMessagesController.js new file mode 100644 index 000000000..ee6175334 --- /dev/null +++ b/lib/controllers/channel/ChannelMessagesController.js @@ -0,0 +1,56 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelMessagesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a text channel's messages cache. + * The messages are mapped by their IDs + */ +class ChannelMessagesController extends base_1.BaseFetchController { + constructor(channel) { + super(channel, channel.bot.options.cache.messagesLimit); + this.channel = channel; + } + /** + * Deletes a message in the channel + * @param {Snowflake} id The ID of the message you wish to delete + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteMessage(this.channel.id, id); + } + /** + * Fetches and caches a message in the channel + * @param {Snowflake} id The ID of the message you wish to fetch + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const message = yield this.bot.api.fetchMessage(this.channel.id, id); + this.cache.add(message); + return message; + }); + } + /** + * Fetches and caches some messages in the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options) { + return __awaiter(this, void 0, void 0, function* () { + const messages = yield this.bot.api.fetchSomeMessages(this.channel.id, options); + this.cache.merge(messages); + return messages; + }); + } +} +exports.ChannelMessagesController = ChannelMessagesController; diff --git a/lib/controllers/channel/ChannelPermissionsController.d.ts b/lib/controllers/channel/ChannelPermissionsController.d.ts new file mode 100644 index 000000000..aadc90589 --- /dev/null +++ b/lib/controllers/channel/ChannelPermissionsController.d.ts @@ -0,0 +1,31 @@ +import { PermissionOverwrite } from '../../structures'; +import { GuildChannel } from '../../structures/channels'; +import { Permissible, PermissionOverwriteFlags } from '../../structures/flags'; +import { Snowflake } from '../../types'; +import { BaseDeleteController } from '../base'; +/** + * Interface for a guild channel's permission overwrites cache. + * The permission overwrites are mapped by their Permissible's ID + */ +export declare class ChannelPermissionsController extends BaseDeleteController { + /** + * The guild channel this controller is associated to + */ + channel: GuildChannel; + constructor(channel: GuildChannel); + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions flags you wish to modify + * @returns {Promise} + */ + modify(permissible: Permissible, flags: PermissionOverwriteFlags): Promise; + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/channel/ChannelPermissionsController.js b/lib/controllers/channel/ChannelPermissionsController.js new file mode 100644 index 000000000..609016e8e --- /dev/null +++ b/lib/controllers/channel/ChannelPermissionsController.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelPermissionsController = void 0; +const base_1 = require("../base"); +/** + * Interface for a guild channel's permission overwrites cache. + * The permission overwrites are mapped by their Permissible's ID + */ +class ChannelPermissionsController extends base_1.BaseDeleteController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions flags you wish to modify + * @returns {Promise} + */ + modify(permissible, flags) { + return this.bot.api.modifyGuildChannelPermissions(this.channel.id, permissible, flags); + } + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteGuildChannelPermission(this.channel.id, id); + } +} +exports.ChannelPermissionsController = ChannelPermissionsController; diff --git a/lib/controllers/channel/ChannelPinsController.d.ts b/lib/controllers/channel/ChannelPinsController.d.ts new file mode 100644 index 000000000..43530fa29 --- /dev/null +++ b/lib/controllers/channel/ChannelPinsController.d.ts @@ -0,0 +1,39 @@ +import Collection from '../../Collection'; +import { Timestamp, TextBasedChannel } from '../../structures'; +import { Message } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseFetchAllController } from '../base'; +/** + * Interface for a text channel's pinned messages cache. + * The pinned messages are mapped by their IDs + */ +export declare class ChannelPinsController extends BaseFetchAllController { + /** + * The channel associated to this controller + */ + channel: TextBasedChannel; + /** + * Timestamp of when the last pinned message was pinned + */ + lastPinTimestamp: Timestamp | undefined; + constructor(channel: TextBasedChannel); + /** + * Fetches all messages in the text channel and caches them + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + pin(id: Snowflake): Promise; + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + unpin(id: Snowflake): Promise; +} diff --git a/lib/controllers/channel/ChannelPinsController.js b/lib/controllers/channel/ChannelPinsController.js new file mode 100644 index 000000000..aece5d1d2 --- /dev/null +++ b/lib/controllers/channel/ChannelPinsController.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelPinsController = void 0; +const base_1 = require("../base"); +/** + * Interface for a text channel's pinned messages cache. + * The pinned messages are mapped by their IDs + */ +class ChannelPinsController extends base_1.BaseFetchAllController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Fetches all messages in the text channel and caches them + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const pins = yield this.bot.api.fetchChannelPins(this.channel.id); + this.cache.merge(pins); + return pins; + }); + } + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + pin(id) { + return __awaiter(this, void 0, void 0, function* () { + yield this.bot.api.pinMessage(this.channel.id, id); + // Cache the pinned message + const message = yield this.channel.messages.get(id); + this.cache.add(message); + }); + } + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + unpin(id) { + return __awaiter(this, void 0, void 0, function* () { + yield this.bot.api.unpinMessage(this.channel.id, id); + // Remove the unpinned message from the cache + this.cache.delete(id); + }); + } +} +exports.ChannelPinsController = ChannelPinsController; diff --git a/lib/controllers/channel/index.d.ts b/lib/controllers/channel/index.d.ts new file mode 100644 index 000000000..10411a20e --- /dev/null +++ b/lib/controllers/channel/index.d.ts @@ -0,0 +1,3 @@ +export * from './ChannelMessagesController'; +export * from './ChannelPermissionsController'; +export * from './ChannelPinsController'; diff --git a/lib/controllers/channel/index.js b/lib/controllers/channel/index.js new file mode 100644 index 000000000..a8d567df7 --- /dev/null +++ b/lib/controllers/channel/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./ChannelMessagesController"), exports); +__exportStar(require("./ChannelPermissionsController"), exports); +__exportStar(require("./ChannelPinsController"), exports); diff --git a/lib/controllers/guild/GuildBansController.d.ts b/lib/controllers/guild/GuildBansController.d.ts new file mode 100644 index 000000000..f2e0c340f --- /dev/null +++ b/lib/controllers/guild/GuildBansController.d.ts @@ -0,0 +1,33 @@ +import Collection from '../../Collection'; +import { Guild, GuildBan } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild's bans cache. + * The bans are mapped by their banned user IDs + */ +export declare class GuildBansController extends BaseFetchController implements BaseFetchAllController, BaseDeleteController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches all bans in the guilds + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Fetches a guild ban by a user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Unbans a member from the guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} id The ID of the member + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildBansController.js b/lib/controllers/guild/GuildBansController.js new file mode 100644 index 000000000..e81ff71b5 --- /dev/null +++ b/lib/controllers/guild/GuildBansController.js @@ -0,0 +1,56 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildBansController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's bans cache. + * The bans are mapped by their banned user IDs + */ +class GuildBansController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all bans in the guilds + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const bans = yield this.bot.api.fetchGuildBans(this.guild.id); + this.cache.merge(bans); + return bans; + }); + } + /** + * Fetches a guild ban by a user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const ban = yield this.bot.api.fetchGuildBan(this.guild.id, id); + this.cache.add(ban); + return ban; + }); + } + /** + * Unbans a member from the guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} id The ID of the member + * @returns {Promise} + */ + delete(id) { + return this.bot.api.unbanMember(this.guild.id, id); + } +} +exports.GuildBansController = GuildBansController; diff --git a/lib/controllers/guild/GuildChannelInvitesController.d.ts b/lib/controllers/guild/GuildChannelInvitesController.d.ts new file mode 100644 index 000000000..f5480126c --- /dev/null +++ b/lib/controllers/guild/GuildChannelInvitesController.d.ts @@ -0,0 +1,28 @@ +import Collection from '../../Collection'; +import { Invite, InviteOptions } from '../../structures'; +import { GuildChannel } from '../../structures/channels'; +import { BaseCreateController, BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild channel's invites cache. + * The invites are mapped by their invite codes + */ +export declare class GuildChannelInvitesController extends BaseFetchAllController implements BaseCreateController { + /** + * The channel this controller is associated to + */ + readonly channel: GuildChannel; + constructor(channel: GuildChannel); + /** + * Fetches all invites for this channel. + * Requires the {@link Permission.ManageChannels} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + create(options?: InviteOptions): Promise; +} diff --git a/lib/controllers/guild/GuildChannelInvitesController.js b/lib/controllers/guild/GuildChannelInvitesController.js new file mode 100644 index 000000000..6ad0c7a25 --- /dev/null +++ b/lib/controllers/guild/GuildChannelInvitesController.js @@ -0,0 +1,45 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannelInvitesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild channel's invites cache. + * The invites are mapped by their invite codes + */ +class GuildChannelInvitesController extends base_1.BaseFetchAllController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Fetches all invites for this channel. + * Requires the {@link Permission.ManageChannels} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const invites = yield this.bot.api.fetchChannelInvites(this.channel.id); + this.cache.merge(invites); + return invites; + }); + } + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + create(options) { + return this.bot.api.createChannelInvite(this.channel.id, options); + } +} +exports.GuildChannelInvitesController = GuildChannelInvitesController; diff --git a/lib/controllers/guild/GuildChannelWebhooksController.d.ts b/lib/controllers/guild/GuildChannelWebhooksController.d.ts new file mode 100644 index 000000000..d73eba01e --- /dev/null +++ b/lib/controllers/guild/GuildChannelWebhooksController.d.ts @@ -0,0 +1,41 @@ +import Collection from '../../Collection'; +import { Webhook, CreateWebhookOptions, GuildChannel } from '../../structures'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild channel's webhooks cache. + * The webhooks are mapped by their IDs + */ +export declare class GuildChannelWebhooksController extends BaseFetchController implements BaseCreateController, BaseFetchAllController, BaseDeleteController { + /** + * The guild channel associated to this controller + */ + readonly channel: GuildChannel; + constructor(channel: GuildChannel); + /** + * Creates a new webhook for this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + create(options: CreateWebhookOptions): Promise; + /** + * Fetches all webhooks in this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Fetches a webhook by its ID + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildChannelWebhooksController.js b/lib/controllers/guild/GuildChannelWebhooksController.js new file mode 100644 index 000000000..4f3b2837e --- /dev/null +++ b/lib/controllers/guild/GuildChannelWebhooksController.js @@ -0,0 +1,73 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannelWebhooksController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild channel's webhooks cache. + * The webhooks are mapped by their IDs + */ +class GuildChannelWebhooksController extends base_1.BaseFetchController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Creates a new webhook for this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + create(options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.bot.api.createWebhook(this.channel.id, options); + this.cache.add(webhook); + return webhook; + }); + } + /** + * Fetches all webhooks in this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.bot.api.fetchWebhooks(this.channel.id); + this.cache.merge(webhooks); + return webhooks; + }); + } + /** + * Fetches a webhook by its ID + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.bot.api.fetchWebhook(id); + this.cache.add(webhook); + return webhook; + }); + } + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + delete(id) { + return __awaiter(this, void 0, void 0, function* () { + yield this.bot.api.deleteWebhook(id); + this.cache.delete(id); + }); + } +} +exports.GuildChannelWebhooksController = GuildChannelWebhooksController; diff --git a/lib/controllers/guild/GuildChannelsController.d.ts b/lib/controllers/guild/GuildChannelsController.d.ts new file mode 100644 index 000000000..45fd2dcaf --- /dev/null +++ b/lib/controllers/guild/GuildChannelsController.d.ts @@ -0,0 +1,61 @@ +import Collection from '../../Collection'; +import { Positions } from '../../api'; +import { GuildChannel, CreateGuildChannelOptions, GuildTextChannel } from '../../structures/channels'; +import { Guild } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild's channels cache. + * The guild channels a mapped by their IDs + */ +export declare class GuildChannelsController extends BaseFetchController implements BaseCreateController, BaseDeleteController, BaseFetchAllController { + /** + * The guild this controller is associated to + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Gets or fetches a guild text channel by its ID + * @param {Snowflake} id The ID of the guild text channel + * @returns {Promise} + */ + getText(id: Snowflake): Promise; + /** + * Creates a new guild channel in the guild associated to this controller. + * Requires the {@link Permission.ManageChannels} + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + create(options: CreateGuildChannelOptions): Promise; + /** + * Deletes a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to delete + * @returns {Promise} + */ + delete(id: Snowflake): Promise; + /** + * Fetches a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches and caches all channels the guild associated to this controller + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Swaps the positions of 2 guild channels with one another + * @param {GuildChannel} channel1 The first guild channel + * @param {GuildChannel} channel2 The second guild channel + * @returns {Promise} + */ + swap(channel1: GuildChannel, channel2: GuildChannel): Promise; + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyPositions(positions: Positions): Promise; +} diff --git a/lib/controllers/guild/GuildChannelsController.js b/lib/controllers/guild/GuildChannelsController.js new file mode 100644 index 000000000..761abaa2f --- /dev/null +++ b/lib/controllers/guild/GuildChannelsController.js @@ -0,0 +1,103 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannelsController = void 0; +const channels_1 = require("../../structures/channels"); +const base_1 = require("../base"); +/** + * Provides an interface for a guild's channels cache. + * The guild channels a mapped by their IDs + */ +class GuildChannelsController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Gets or fetches a guild text channel by its ID + * @param {Snowflake} id The ID of the guild text channel + * @returns {Promise} + */ + getText(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildTextChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + return channel; + }); + } + /** + * Creates a new guild channel in the guild associated to this controller. + * Requires the {@link Permission.ManageChannels} + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + create(options) { + return this.bot.api.createGuildChannel(this.guild.id, options); + } + /** + * Deletes a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to delete + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteGuildChannel(id); + } + /** + * Fetches a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.bot.api.fetchGuildChannel(id); + this.cache.add(channel); + this.bot.channels.cache.add(channel); + return channel; + }); + } + /** + * Fetches and caches all channels the guild associated to this controller + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const channels = yield this.bot.api.fetchGuildChannels(this.guild.id); + this.cache.merge(channels); + return channels; + }); + } + /** + * Swaps the positions of 2 guild channels with one another + * @param {GuildChannel} channel1 The first guild channel + * @param {GuildChannel} channel2 The second guild channel + * @returns {Promise} + */ + swap(channel1, channel2) { + return __awaiter(this, void 0, void 0, function* () { + const positions = { [channel1.id]: channel2.position, [channel2.id]: channel1.position }; + return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); + }); + } + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyPositions(positions) { + return __awaiter(this, void 0, void 0, function* () { + return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); + }); + } +} +exports.GuildChannelsController = GuildChannelsController; diff --git a/lib/controllers/guild/GuildEmojisController.d.ts b/lib/controllers/guild/GuildEmojisController.d.ts new file mode 100644 index 000000000..65ce58384 --- /dev/null +++ b/lib/controllers/guild/GuildEmojisController.d.ts @@ -0,0 +1,32 @@ +import Collection from '../../Collection'; +import { Guild, GuildEmoji, CreateEmojiOptions } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild's emojis cache. + * The emojis are mapped by their IDs + */ +export declare class GuildEmojisController extends BaseFetchController implements BaseFetchAllController, BaseCreateController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Creates a new guild emoji + * @param {CreateEmojiOptions} options The options for the new guild emoji + * @returns {Promise} + */ + create(options: CreateEmojiOptions): Promise; + /** + * Fetches a guild emoji by its ID + * @param {Snowflake} id The ID of the guild emoji + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches all emojis in a guild + * @returns {Promise>} + */ + fetchAll(): Promise>; +} diff --git a/lib/controllers/guild/GuildEmojisController.js b/lib/controllers/guild/GuildEmojisController.js new file mode 100644 index 000000000..d20459b82 --- /dev/null +++ b/lib/controllers/guild/GuildEmojisController.js @@ -0,0 +1,55 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildEmojisController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's emojis cache. + * The emojis are mapped by their IDs + */ +class GuildEmojisController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Creates a new guild emoji + * @param {CreateEmojiOptions} options The options for the new guild emoji + * @returns {Promise} + */ + create(options) { + return this.bot.api.createGuildEmoji(this.guild.id, options); + } + /** + * Fetches a guild emoji by its ID + * @param {Snowflake} id The ID of the guild emoji + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.bot.api.fetchGuildEmoji(this.guild.id, id); + this.cache.add(emoji); + return emoji; + }); + } + /** + * Fetches all emojis in a guild + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const emojis = yield this.bot.api.fetchGuildEmojis(this.guild.id); + this.cache.merge(emojis); + return emojis; + }); + } +} +exports.GuildEmojisController = GuildEmojisController; diff --git a/lib/controllers/guild/GuildIntegrationsController.d.ts b/lib/controllers/guild/GuildIntegrationsController.d.ts new file mode 100644 index 000000000..4bb2deed5 --- /dev/null +++ b/lib/controllers/guild/GuildIntegrationsController.d.ts @@ -0,0 +1,35 @@ +import Collection from '../../Collection'; +import { Guild, GuildIntegration, CreateIntegrationOptions } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild's integrations cache. + * The integrations are mapped by their IDs + */ +export declare class GuildIntegrationsController extends BaseFetchAllController implements BaseDeleteController { + /** + * The guild associated to this controller + */ + guild: Guild; + constructor(guild: Guild); + /** + * Fetches all guild integrations in this guild + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + create(options: CreateIntegrationOptions): Promise; + /** + * Deletes the attached integration for this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} id The ID of the guild integration + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildIntegrationsController.js b/lib/controllers/guild/GuildIntegrationsController.js new file mode 100644 index 000000000..0f27ba138 --- /dev/null +++ b/lib/controllers/guild/GuildIntegrationsController.js @@ -0,0 +1,54 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildIntegrationsController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's integrations cache. + * The integrations are mapped by their IDs + */ +class GuildIntegrationsController extends base_1.BaseFetchAllController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all guild integrations in this guild + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const integrations = yield this.bot.api.fetchGuildIntegrations(this.guild.id); + this.cache.merge(integrations); + return integrations; + }); + } + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + create(options) { + return this.bot.api.createGuildIntegration(this.guild.id, options); + } + /** + * Deletes the attached integration for this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} id The ID of the guild integration + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteGuildIntegration(this.guild.id, id); + } +} +exports.GuildIntegrationsController = GuildIntegrationsController; diff --git a/lib/controllers/guild/GuildInvitesController.d.ts b/lib/controllers/guild/GuildInvitesController.d.ts new file mode 100644 index 000000000..61131a510 --- /dev/null +++ b/lib/controllers/guild/GuildInvitesController.d.ts @@ -0,0 +1,44 @@ +import Collection from '../../Collection'; +import { Invite } from '../../structures'; +import { Guild } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Options for when fetching an invite + */ +export interface FetchInviteOptions { + /** + * Whether the invite should contain approximate member counts + */ + withCounts?: boolean; +} +/** + * Provides an interface for a guild's invites cache + * The invites are mapped by their invite codes + */ +export declare class GuildInvitesController extends BaseFetchController implements BaseDeleteController, BaseFetchAllController { + /** + * The guild this controller is associated to + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Delete an invite by its invite code + * @param {Snowflake} code The invite code + * @returns {Promise} + */ + delete(code: Snowflake): Promise; + /** + * Fetches an invite by its invite code + * @param {string} code The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetch(code: string, options?: FetchInviteOptions): Promise; + /** + * Fetches all invites (with metadata) in this guild. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; +} diff --git a/lib/controllers/guild/GuildInvitesController.js b/lib/controllers/guild/GuildInvitesController.js new file mode 100644 index 000000000..f6834301a --- /dev/null +++ b/lib/controllers/guild/GuildInvitesController.js @@ -0,0 +1,57 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildInvitesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's invites cache + * The invites are mapped by their invite codes + */ +class GuildInvitesController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Delete an invite by its invite code + * @param {Snowflake} code The invite code + * @returns {Promise} + */ + delete(code) { + return this.bot.api.deleteInvite(code); + } + /** + * Fetches an invite by its invite code + * @param {string} code The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetch(code, options) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.bot.api.fetchInvite(code, options); + this.cache.add(invite); + return invite; + }); + } + /** + * Fetches all invites (with metadata) in this guild. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const invites = yield this.bot.api.fetchGuildInvites(this.guild.id); + this.cache.merge(invites); + return invites; + }); + } +} +exports.GuildInvitesController = GuildInvitesController; diff --git a/lib/controllers/guild/GuildMembersController.d.ts b/lib/controllers/guild/GuildMembersController.d.ts new file mode 100644 index 000000000..4a4923481 --- /dev/null +++ b/lib/controllers/guild/GuildMembersController.d.ts @@ -0,0 +1,52 @@ +import Collection from '../../Collection'; +import { Guild } from '../../structures/guild'; +import { Member } from '../../structures/member/Member'; +import { Snowflake } from '../../types'; +import { BaseFetchSomeController, BaseFetchController } from '../base'; +/** + * Options for when fetching some guild members in a guild + */ +export interface FetchSomeMembersOptions { + /** + * The max number of members to return (1-1000) + */ + limit?: number; + /** + * The highest user ID in the previous page + */ + after?: Snowflake; +} +/** + * Provides an interface for a guild's members cache. + * The members are mapped by their IDs + */ +export declare class GuildMembersController extends BaseFetchController implements BaseFetchSomeController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches a guild member and caches it + * @param {Snowflake} id The ID of the guild member + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches some guild members in this guild and caches them + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options?: FetchSomeMembersOptions): Promise>; + /** + * Removes a user from this guild by its user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + remove(id: Snowflake): Promise; + /** + * Returns the bot member in the guild + * @type {Member | undefined} + */ + get me(): Member | undefined; +} diff --git a/lib/controllers/guild/GuildMembersController.js b/lib/controllers/guild/GuildMembersController.js new file mode 100644 index 000000000..beb8241ab --- /dev/null +++ b/lib/controllers/guild/GuildMembersController.js @@ -0,0 +1,65 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildMembersController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's members cache. + * The members are mapped by their IDs + */ +class GuildMembersController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches a guild member and caches it + * @param {Snowflake} id The ID of the guild member + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const member = yield this.bot.api.fetchMember(this.guild.id, id); + this.cache.add(member); + return member; + }); + } + /** + * Fetches some guild members in this guild and caches them + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options) { + return __awaiter(this, void 0, void 0, function* () { + const members = yield this.bot.api.fetchSomeMembers(this.guild.id, options); + this.cache.merge(members); + return members; + }); + } + /** + * Removes a user from this guild by its user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + remove(id) { + return __awaiter(this, void 0, void 0, function* () { + return this.bot.api.removeMember(this.guild.id, id); + }); + } + /** + * Returns the bot member in the guild + * @type {Member | undefined} + */ + get me() { + return this.bot.user && this.cache.get(this.bot.user.id); + } +} +exports.GuildMembersController = GuildMembersController; diff --git a/lib/controllers/guild/GuildRolesController.d.ts b/lib/controllers/guild/GuildRolesController.d.ts new file mode 100644 index 000000000..f38121d1f --- /dev/null +++ b/lib/controllers/guild/GuildRolesController.d.ts @@ -0,0 +1,43 @@ +import Collection from '../../Collection'; +import { Positions } from '../../api'; +import { Role, RoleOptions } from '../../structures'; +import { Guild } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild's roles cache. + * The roles are mapped by their IDs + */ +export declare class GuildRolesController extends BaseFetchAllController implements BaseCreateController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches all roles in this guild + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Creates a new role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions | undefined} options The options for the created role + * @returns {Promise} + */ + create(options?: RoleOptions): Promise; + /** + * Modifies the positions of a set of roles for this guild. + * Requires the {@link Permission.ManageRoles} + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyPositions(positions: Positions): Promise>; + /** + * Deletes a role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the role + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildRolesController.js b/lib/controllers/guild/GuildRolesController.js new file mode 100644 index 000000000..ad86f30e8 --- /dev/null +++ b/lib/controllers/guild/GuildRolesController.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildRolesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's roles cache. + * The roles are mapped by their IDs + */ +class GuildRolesController extends base_1.BaseFetchAllController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all roles in this guild + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const roles = yield this.bot.api.fetchRoles(this.guild.id); + this.cache.merge(roles); + return roles; + }); + } + /** + * Creates a new role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions | undefined} options The options for the created role + * @returns {Promise} + */ + create(options) { + return this.bot.api.createRole(this.guild.id, options); + } + /** + * Modifies the positions of a set of roles for this guild. + * Requires the {@link Permission.ManageRoles} + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyPositions(positions) { + return this.bot.api.modifyRolesPositions(this.guild.id, positions); + } + /** + * Deletes a role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the role + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteRole(this.guild.id, id); + } +} +exports.GuildRolesController = GuildRolesController; diff --git a/lib/controllers/guild/GuildWebhooksController.d.ts b/lib/controllers/guild/GuildWebhooksController.d.ts new file mode 100644 index 000000000..da24bcb50 --- /dev/null +++ b/lib/controllers/guild/GuildWebhooksController.d.ts @@ -0,0 +1,20 @@ +import Collection from '../../Collection'; +import { Guild, Webhook } from '../../structures'; +import { Snowflake } from '../../types'; +import { BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild's webhooks cache. + * The webhooks are mapped by their IDs + */ +export declare class GuildWebhooksController extends BaseFetchAllController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches all webhooks in this guild + * @returns {Promise>} + */ + fetchAll(): Promise>; +} diff --git a/lib/controllers/guild/GuildWebhooksController.js b/lib/controllers/guild/GuildWebhooksController.js new file mode 100644 index 000000000..1fbf70de3 --- /dev/null +++ b/lib/controllers/guild/GuildWebhooksController.js @@ -0,0 +1,35 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildWebhooksController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's webhooks cache. + * The webhooks are mapped by their IDs + */ +class GuildWebhooksController extends base_1.BaseFetchAllController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all webhooks in this guild + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.bot.api.fetchGuildWebhooks(this.guild.id); + this.cache.merge(webhooks); + return webhooks; + }); + } +} +exports.GuildWebhooksController = GuildWebhooksController; diff --git a/lib/controllers/guild/index.d.ts b/lib/controllers/guild/index.d.ts new file mode 100644 index 000000000..b9b3734d6 --- /dev/null +++ b/lib/controllers/guild/index.d.ts @@ -0,0 +1,10 @@ +export * from './GuildBansController'; +export * from './GuildChannelInvitesController'; +export * from './GuildChannelsController'; +export * from './GuildChannelWebhooksController'; +export * from './GuildEmojisController'; +export * from './GuildIntegrationsController'; +export * from './GuildInvitesController'; +export * from './GuildMembersController'; +export * from './GuildRolesController'; +export * from './GuildWebhooksController'; diff --git a/lib/controllers/guild/index.js b/lib/controllers/guild/index.js new file mode 100644 index 000000000..82436c7e2 --- /dev/null +++ b/lib/controllers/guild/index.js @@ -0,0 +1,22 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./GuildBansController"), exports); +__exportStar(require("./GuildChannelInvitesController"), exports); +__exportStar(require("./GuildChannelsController"), exports); +__exportStar(require("./GuildChannelWebhooksController"), exports); +__exportStar(require("./GuildEmojisController"), exports); +__exportStar(require("./GuildIntegrationsController"), exports); +__exportStar(require("./GuildInvitesController"), exports); +__exportStar(require("./GuildMembersController"), exports); +__exportStar(require("./GuildRolesController"), exports); +__exportStar(require("./GuildWebhooksController"), exports); diff --git a/lib/controllers/index.d.ts b/lib/controllers/index.d.ts new file mode 100644 index 000000000..208155bb9 --- /dev/null +++ b/lib/controllers/index.d.ts @@ -0,0 +1,8 @@ +export * from './base'; +export * from './bot'; +export * from './channel'; +export * from './guild'; +export * from './member'; +export * from './message'; +export * from './reaction'; +export * from './ControllerCache'; diff --git a/lib/controllers/index.js b/lib/controllers/index.js new file mode 100644 index 000000000..622ad3492 --- /dev/null +++ b/lib/controllers/index.js @@ -0,0 +1,20 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./base"), exports); +__exportStar(require("./bot"), exports); +__exportStar(require("./channel"), exports); +__exportStar(require("./guild"), exports); +__exportStar(require("./member"), exports); +__exportStar(require("./message"), exports); +__exportStar(require("./reaction"), exports); +__exportStar(require("./ControllerCache"), exports); diff --git a/lib/controllers/member/MemberRolesController.d.ts b/lib/controllers/member/MemberRolesController.d.ts new file mode 100644 index 000000000..2bb620f3f --- /dev/null +++ b/lib/controllers/member/MemberRolesController.d.ts @@ -0,0 +1,31 @@ +import { Role } from '../../structures'; +import { Member } from '../../structures/member/Member'; +import { Snowflake } from '../../types'; +import { BaseController } from '../base'; +/** + * Provides an interface for a member's roles cache. + * The roles are mapped by their IDs + */ +export declare class MemberRolesController extends BaseController { + /** + * The member associated to this controller + */ + readonly member: Member; + /** + * The guild this member is in + */ + private readonly guild; + constructor(member: Member); + /** + * Adds a role to this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + add(roleId: Snowflake): Promise; + /** + * Removes a role from this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + remove(roleId: Snowflake): Promise; +} diff --git a/lib/controllers/member/MemberRolesController.js b/lib/controllers/member/MemberRolesController.js new file mode 100644 index 000000000..f6c25bb2e --- /dev/null +++ b/lib/controllers/member/MemberRolesController.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MemberRolesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a member's roles cache. + * The roles are mapped by their IDs + */ +class MemberRolesController extends base_1.BaseController { + constructor(member) { + super(member); + this.member = member; + this.guild = member.guild; + } + /** + * Adds a role to this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + add(roleId) { + return this.bot.api.memberAddRole(this.guild.id, this.member.id, roleId); + } + /** + * Removes a role from this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + remove(roleId) { + return this.bot.api.memberRemoveRole(this.guild.id, this.member.id, roleId); + } +} +exports.MemberRolesController = MemberRolesController; diff --git a/lib/controllers/member/index.d.ts b/lib/controllers/member/index.d.ts new file mode 100644 index 000000000..905339dc9 --- /dev/null +++ b/lib/controllers/member/index.d.ts @@ -0,0 +1 @@ +export * from './MemberRolesController'; diff --git a/lib/controllers/member/index.js b/lib/controllers/member/index.js new file mode 100644 index 000000000..ef97e9693 --- /dev/null +++ b/lib/controllers/member/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./MemberRolesController"), exports); diff --git a/lib/controllers/message/MessageReactionsController.d.ts b/lib/controllers/message/MessageReactionsController.d.ts new file mode 100644 index 000000000..fdf782839 --- /dev/null +++ b/lib/controllers/message/MessageReactionsController.d.ts @@ -0,0 +1,36 @@ +import { EmojiResolvable } from '../../structures'; +import { Message, MessageReaction } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseDeleteController } from '../base'; +/** + * Provides an interface for a message's reactions cache. + * The reactions are mapped by the emoji name or emoji ID + */ +export declare class MessageReactionsController extends BaseDeleteController { + /** + * The message this controller is associated to + */ + readonly message: Message; + constructor(message: Message); + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {EmojiResolvable} emoji The emoji to remove from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + delete(emoji: EmojiResolvable, userId?: Snowflake): Promise; + /** + * Deletes all reactions for an emoji. + * Requires the {@link Permission.ManageMessages} permission + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + deleteEmoji(emoji: EmojiResolvable): Promise; + /** + * Removes all reactions on the message associated to this controller. + * Requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @returns {Promise} + */ + deleteAll(): Promise; +} diff --git a/lib/controllers/message/MessageReactionsController.js b/lib/controllers/message/MessageReactionsController.js new file mode 100644 index 000000000..878dafe4d --- /dev/null +++ b/lib/controllers/message/MessageReactionsController.js @@ -0,0 +1,42 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageReactionsController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a message's reactions cache. + * The reactions are mapped by the emoji name or emoji ID + */ +class MessageReactionsController extends base_1.BaseDeleteController { + constructor(message) { + super(message); + this.message = message; + } + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {EmojiResolvable} emoji The emoji to remove from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + delete(emoji, userId = '@me') { + return this.bot.api.removeMessageReaction(this.message.channel.id, this.message.id, emoji, userId); + } + /** + * Deletes all reactions for an emoji. + * Requires the {@link Permission.ManageMessages} permission + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + deleteEmoji(emoji) { + return this.bot.api.removeMessageReactionsEmoji(this.message.channel.id, this.message.id, emoji); + } + /** + * Removes all reactions on the message associated to this controller. + * Requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @returns {Promise} + */ + deleteAll() { + return this.bot.api.removeMessageReactions(this.message.channel.id, this.message.id); + } +} +exports.MessageReactionsController = MessageReactionsController; diff --git a/lib/controllers/message/index.d.ts b/lib/controllers/message/index.d.ts new file mode 100644 index 000000000..7541f63b7 --- /dev/null +++ b/lib/controllers/message/index.d.ts @@ -0,0 +1 @@ +export * from './MessageReactionsController'; diff --git a/lib/controllers/message/index.js b/lib/controllers/message/index.js new file mode 100644 index 000000000..022a3f75e --- /dev/null +++ b/lib/controllers/message/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./MessageReactionsController"), exports); diff --git a/lib/controllers/reaction/ReactionUsersController.d.ts b/lib/controllers/reaction/ReactionUsersController.d.ts new file mode 100644 index 000000000..ae929d3f9 --- /dev/null +++ b/lib/controllers/reaction/ReactionUsersController.d.ts @@ -0,0 +1,43 @@ +import Collection from '../../Collection'; +import { User } from '../../structures'; +import { MessageReaction } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseFetchAllController } from '../base'; +/** + * Options for when fetching the users that reacted with a particular emoji + */ +export interface FetchReactionUsersOptions { + /** + * Get users before this user ID + */ + before?: Snowflake; + /** + * Get users after this user ID + */ + after?: Snowflake; + /** + * Max number of users to return (1-100) + */ + limit?: number; +} +/** + * Interface for the users that added a reaction identified by its emoji. + * The users are mapped by their IDs + */ +export declare class ReactionUsersController extends BaseFetchAllController { + /** + * The reaction this controller is associated to + */ + readonly reaction: MessageReaction; + /** + * The message associated to the reaction + */ + private readonly message; + constructor(reaction: MessageReaction); + /** + * Fetches all users that reacted with the reaction emoji associated to this controller + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise} + */ + fetchAll(options?: FetchReactionUsersOptions): Promise>; +} diff --git a/lib/controllers/reaction/ReactionUsersController.js b/lib/controllers/reaction/ReactionUsersController.js new file mode 100644 index 000000000..3ee5eac42 --- /dev/null +++ b/lib/controllers/reaction/ReactionUsersController.js @@ -0,0 +1,48 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ReactionUsersController = void 0; +const base_1 = require("../base"); +/** + * Interface for the users that added a reaction identified by its emoji. + * The users are mapped by their IDs + */ +class ReactionUsersController extends base_1.BaseFetchAllController { + constructor(reaction) { + super(reaction); + this.reaction = reaction; + this.message = reaction.message; + } + /** + * Fetches all users that reacted with the reaction emoji associated to this controller + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise} + */ + fetchAll(options) { + return __awaiter(this, void 0, void 0, function* () { + const users = yield this.bot.api.fetchReactionUsers(this.message.channel.id, this.message.id, this.reaction.id, options); + this.cache.merge(users); + if (this.bot.user && users.has(this.bot.user.id)) { + // The bot reacted to this reaction + this.reaction.botReacted = true; + } + if (this.message.guild) { + // The message was sent in a guild + // All users are also members in that guild + this.reaction.members.merge(users + .filter(user => this.message.guild.members.cache.has(user.id)) + .map(user => this.message.guild.members.cache.get(user.id))); + } + return users; + }); + } +} +exports.ReactionUsersController = ReactionUsersController; diff --git a/lib/controllers/reaction/index.d.ts b/lib/controllers/reaction/index.d.ts new file mode 100644 index 000000000..f0b59f3fa --- /dev/null +++ b/lib/controllers/reaction/index.d.ts @@ -0,0 +1 @@ +export * from './ReactionUsersController'; diff --git a/lib/controllers/reaction/index.js b/lib/controllers/reaction/index.js new file mode 100644 index 000000000..61264c8be --- /dev/null +++ b/lib/controllers/reaction/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./ReactionUsersController"), exports); diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 000000000..d45c805f0 --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,8 @@ +export * from './api'; +export * from './bot'; +export * from './controllers'; +export * from './sharding'; +export * from './socket'; +export * from './structures'; +export * from './types'; +export * as Collection from './Collection'; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 000000000..f6471494d --- /dev/null +++ b/lib/index.js @@ -0,0 +1,32 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./api"), exports); +__exportStar(require("./bot"), exports); +__exportStar(require("./controllers"), exports); +__exportStar(require("./sharding"), exports); +__exportStar(require("./socket"), exports); +__exportStar(require("./structures"), exports); +__exportStar(require("./types"), exports); +exports.Collection = __importStar(require("./Collection")); diff --git a/lib/sharding/BotCommunication.d.ts b/lib/sharding/BotCommunication.d.ts new file mode 100644 index 000000000..d62d9018d --- /dev/null +++ b/lib/sharding/BotCommunication.d.ts @@ -0,0 +1,253 @@ +/// +import { Serializable } from 'child_process'; +import { EventEmitter } from 'events'; +import { Arguments } from 'typed-emitter'; +import { BotShardState } from './BotShard'; +import { Bot } from '../bot'; +import { Events, BotStateEvents } from '../bot/handlers/events/events'; +import { GatewayCloseCode } from '../socket'; +import { ShardId } from '../types'; +/** + * Abstract typing for all shard requests + */ +export interface ShardRequest { + /** + * The action to dispatch + */ + action: string; + /** + * The matching payload for the action + */ + payload: Serializable; + /** + * The Unix date of when this request was sent. + * Used to identify the response of this request + */ + identifier: number; +} +/** + * Abstract typing for all shard responses + */ +export interface ShardResponse { + /** + * Data to be sent in response to a request + */ + payload: Serializable; + /** + * The Unix date of when this request was sent. + * Used to identify the request that led to this response + */ + identifier: number; +} +/** + * Actions that can be sent to the shard manager to be evaluated in specific / all shards + */ +export declare const enum ShardCommunicationAction { + /** + * Updates the presence status of all shards + */ + UpdatePresence = "updatePresence", + /** + * Emits an event on all shards + */ + Broadcast = "broadcast", + /** + * Emits an event on a specific shard + */ + Send = "send", + /** + * A shard has changed its state + */ + ShardChangedState = "shardChangedState", + /** + * A shard requested to disconnect all currently active shards + */ + DisconnectAll = "disconnectAll" +} +/** + * The type of result sent in response to an action ({@link ShardCommunicationAction}) from the shard manager + */ +export declare const enum ShardCommunicationActionResponses { + /** + * Responses sent in response to a {@link ShardCommunicationAction.Broadcast} request + */ + BroadcastResponses = "broadcastResponses", + /** + * Response sent in response to a {@link ShardCommunicationAction.Send} request + */ + SendResponse = "sendResponse" +} +/** + * Actions to emit communication / bot events on specific / all shards + */ +export declare const enum ShardCommunicationEmitEvents { + /** + * Emit a specific communication event + */ + EmitCommunicationEvent = "emitCommunicationEvent", + /** + * The response for {@link ShardCommunicationEmitEvents.EmitCommunicationEvent} + */ + EmitCommunicationEventResponse = "emitCommunicationEventResponse", + /** + * Emit a specific Bot event (registered under {@link EventsHandler}) + */ + EmitBotEvent = "emitBotEvent", + /** + * Tells the shard to disconnect from their gateway connection + */ + EmitDisconnect = "emitDisconnect" +} +/** + * Format for the request to emit an event on all shards + * {@link ShardCommunicationAction.Broadcast} + */ +export interface ShardBroadcastRequest extends ShardRequest { + action: ShardCommunicationAction.Broadcast; + /** + * The name of the event to emit + */ + payload: string; +} +/** + * Format for the request to emit an event to a specific shard + * {@link ShardCommunicationAction.Send} + */ +export interface ShardSendRequest extends ShardRequest { + action: ShardCommunicationAction.Send; + payload: { + /** + * The name of the event to emit + */ + event: string; + /** + * The shard ID to emit this event on + */ + shardId: ShardId; + }; +} +/** + * Request sent to the shard manager when a shard has changed its state + */ +export interface ShardChangedStateRequest extends ShardRequest { + action: ShardCommunicationAction.ShardChangedState; + payload: { + /** + * The new shard state + */ + state: BotShardState; + /** + * The Bot event ({@link EventsHandler}) to emit if all remaining shards share the same state + */ + botEvent: BotStateEvents; + }; +} +export interface ShardDisconnectAllRequest extends ShardRequest { + action: ShardCommunicationAction.DisconnectAll; + /** + * The close code for all shards + */ + payload: GatewayCloseCode; +} +/** + * Request to emit a Bot event on a shard. + * Sent if all shards share the same state {@link ShardChangedStateRequest} + */ +export interface ShardEmitBotEventRequest extends ShardRequest { + action: ShardCommunicationEmitEvents.EmitBotEvent; + payload: { + /** + * The Bot Event to emit + */ + event: E; + /** + * The arguments this event requires + */ + args: Arguments; + }; +} +/** + * Request a communication event to be emitted on a specific / all shards + */ +export interface ShardEmitCommunicationEventRequest extends ShardRequest { + action: ShardCommunicationEmitEvents.EmitCommunicationEvent; + payload: { + /** + * The event name + */ + event: string; + }; +} +export interface ShardEmitDisconnectRequest extends ShardRequest { + action: ShardCommunicationEmitEvents.EmitDisconnect; + /** + * The close code for the shard + */ + payload: GatewayCloseCode; +} +/** + * Response for a {@link ShardBroadcastRequest} / {@link ShardSendRequest} request + */ +export interface ShardCommunicationActionResponse extends ShardResponse { + payload: { + event: string; + data?: Serializable | Serializable[]; + }; +} +/** + * Response for a {@link ShardEmitCommunicationEventRequest} + */ +export interface ShardEmitCommunicationEventResponse extends ShardResponse { + payload: { + /** + * Data returned from the event + */ + data: Serializable | Serializable[]; + }; +} +export declare class BotCommunication extends EventEmitter { + /** + * The Bot instance + */ + private readonly bot; + constructor(bot: Bot); + /** + * Listener function for new messages coming from the parent process + * @param {ShardEmitCommunicationEventRequest | ShardEmitBotEventRequest} message The message received from the parent process + * @returns {Promise} + */ + private onMessage; + /** + * {@link EventEmitter} 'on' override to resolve with the listener's returned value + * @param {string | symbol} event Event name + * @param {function(bot: Bot)} listener Callback to be executed when this event is emitted + * @returns {this} + */ + on(event: string | symbol, listener: (bot: Bot) => void): this; + /** + * {@link EventEmitter} 'emit' override to return a {@link Promise} with the return value of the registered listener + * @param {string | symbol} event + * @param args + * @returns {Promise} + */ + emit(event: string | symbol, ...args: unknown[]): boolean; + emit(event: string | symbol, ...args: unknown[]): Promise; + /** + * Send and receive the data returned from the registered event for each shard + * @param {string} event The registered event to be emitted + * @returns {Promise} + */ + broadcast(event: string): Promise; + /** + * Send and receive the data returned from the registered event for the shard matching the supplied ID + * @param {string} event The registered event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise} + */ + send(event: string, shardId: ShardId): Promise; + /** + * Generates a random identifier to provide to cross-shard requests + * @type {number} + */ + static get identifier(): number; +} diff --git a/lib/sharding/BotCommunication.js b/lib/sharding/BotCommunication.js new file mode 100644 index 000000000..0d7a34728 --- /dev/null +++ b/lib/sharding/BotCommunication.js @@ -0,0 +1,138 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotCommunication = void 0; +const events_1 = require("events"); +class BotCommunication extends events_1.EventEmitter { + constructor(bot) { + super(); + this.bot = bot; + process.on('message', this.onMessage.bind(this)); + } + /** + * Listener function for new messages coming from the parent process + * @param {ShardEmitCommunicationEventRequest | ShardEmitBotEventRequest} message The message received from the parent process + * @returns {Promise} + */ + onMessage(message) { + return __awaiter(this, void 0, void 0, function* () { + switch (message.action) { + // Tells the Bot to dispatch an event and return its result + case "emitCommunicationEvent" /* EmitCommunicationEvent */: + if (process.send) { + const data = yield this.emit(message.payload.event); + const reply = { + payload: { + data, + }, + identifier: message.identifier, + }; + process.send(reply); + } + break; + // Tells the Bot to emit an event to BotEvents + case "emitBotEvent" /* EmitBotEvent */: + this.bot.events.emit(message.payload.event, ...message.payload.args); + break; + // Tells the Bot to disconnect from its current connection + case "emitDisconnect" /* EmitDisconnect */: + this.bot.connection.disconnect(message.payload); + break; + } + }); + } + /** + * {@link EventEmitter} 'on' override to resolve with the listener's returned value + * @param {string | symbol} event Event name + * @param {function(bot: Bot)} listener Callback to be executed when this event is emitted + * @returns {this} + */ + on(event, listener) { + super.on(event, ([resolve, reject]) => { + try { + resolve(listener.bind(this)(this.bot)); + } + catch (err) { + reject(err); + } + }); + return this; + } + emit(event, ...args) { + return new Promise((resolve, reject) => { + super.emit(event, [resolve, reject], ...args); + }); + } + /** + * Send and receive the data returned from the registered event for each shard + * @param {string} event The registered event to be emitted + * @returns {Promise} + */ + broadcast(event) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => { + const { identifier } = BotCommunication; + const listener = ({ payload: { event: event, data }, identifier: responseIdentifier, }) => { + if (event === "broadcastResponses" /* BroadcastResponses */ && + identifier === responseIdentifier && + Array.isArray(data)) { + resolve(data); + } + }; + process.on('message', listener); + const request = { + action: "broadcast" /* Broadcast */, + payload: event, + identifier, + }; + if (process.send) { + process.send(request); + } + }); + }); + } + /** + * Send and receive the data returned from the registered event for the shard matching the supplied ID + * @param {string} event The registered event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise} + */ + send(event, shardId) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => { + const { identifier } = BotCommunication; + const listener = ({ payload: { event, data }, identifier: responseIdentifier, }) => { + if (event === "sendResponse" /* SendResponse */ && + identifier === responseIdentifier) { + resolve(data); + } + }; + process.on('message', listener); + const message = { + action: "send" /* Send */, + payload: { event, shardId }, + identifier, + }; + if (process.send) { + process.send(message); + } + }); + }); + } + /** + * Generates a random identifier to provide to cross-shard requests + * @type {number} + */ + static get identifier() { + return Math.random(); + } +} +exports.BotCommunication = BotCommunication; diff --git a/lib/sharding/BotShard.d.ts b/lib/sharding/BotShard.d.ts new file mode 100644 index 000000000..63bd9c6a2 --- /dev/null +++ b/lib/sharding/BotShard.d.ts @@ -0,0 +1,53 @@ +/// +import { Serializable } from 'child_process'; +import { Arguments } from 'typed-emitter'; +import { ShardBroadcastRequest, ShardChangedStateRequest, ShardDisconnectAllRequest, ShardSendRequest } from './BotCommunication'; +import { BotShardManager } from './BotShardManager'; +import { Events } from '../bot/handlers/events/events'; +import { GatewayCloseCode } from '../socket'; +import { ShardId } from '../types'; +/** + * The shard state + */ +export declare enum BotShardState { + Ready = 0, + Closed = 1 +} +/** + * Creates and handles the communication of a shard + */ +export declare class BotShard { + private readonly manager; + private process; + readonly id: ShardId; + state: BotShardState; + constructor(manager: BotShardManager, id: ShardId); + /** + * Spawns a new child according to the given file path. + * Sets the environmental variables for the process with the sharding information + */ + spawn(): void; + /** + * Listener for child process messages + * @param {ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest} request The request received from the child process + * @returns {Promise} + */ + onMessage(request: ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest | ShardDisconnectAllRequest): Promise; + /** + * Sends a message to the child process in order to emit a registered given event + * @param {string} event The event to be emitted in the child process + * @returns {Promise} + */ + communicate(event: string): Promise; + /** + * Sends the child process a message to emit the given event to {@link EventsHandler} + * @param {E} event The event to be emitted + * @param {Array} args The arguments of the events + */ + emitEvent(event: E, args: Arguments): void; + /** + * Disconnects this shard + * @param {GatewayCloseCode} code The shard close code + */ + disconnect(code: GatewayCloseCode): void; +} diff --git a/lib/sharding/BotShard.js b/lib/sharding/BotShard.js new file mode 100644 index 000000000..79235d41e --- /dev/null +++ b/lib/sharding/BotShard.js @@ -0,0 +1,158 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotShard = exports.BotShardState = void 0; +const child_process_1 = require("child_process"); +const path_1 = __importDefault(require("path")); +const BotCommunication_1 = require("./BotCommunication"); +/** + * The shard state + */ +var BotShardState; +(function (BotShardState) { + BotShardState[BotShardState["Ready"] = 0] = "Ready"; + BotShardState[BotShardState["Closed"] = 1] = "Closed"; +})(BotShardState = exports.BotShardState || (exports.BotShardState = {})); +/** + * Creates and handles the communication of a shard + */ +class BotShard { + constructor(manager, id) { + this.manager = manager; + this.id = id; + this.state = BotShardState.Closed; + } + /** + * Spawns a new child according to the given file path. + * Sets the environmental variables for the process with the sharding information + */ + spawn() { + this.process = child_process_1.fork(path_1.default.resolve(this.manager.file), [], { + env: { + SHARD_ID: this.id.toString(), + SHARDS_AMOUNT: this.manager.shardsAmount.toString(), + }, + }); + this.process.on('message', this.onMessage.bind(this)); + } + /** + * Listener for child process messages + * @param {ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest} request The request received from the child process + * @returns {Promise} + */ + onMessage(request) { + return __awaiter(this, void 0, void 0, function* () { + const { identifier } = request; + switch (request.action) { + // Broadcast requested by the shard + case "broadcast" /* Broadcast */: { + const results = yield this.manager.broadcast(request.payload); + const response = { + payload: { + event: "broadcastResponses" /* BroadcastResponses */, + data: results, + }, + identifier, + }; + this.process.send(response); + break; + } + // Single shard send requested by the shard + case "send" /* Send */: { + const { event, shardId } = request.payload; + const result = yield this.manager.send(event, shardId); + const response = { + payload: { + event: "sendResponse" /* SendResponse */, + data: result, + }, + identifier, + }; + this.process.send(response); + break; + } + // The shard changed its state + case "shardChangedState" /* ShardChangedState */: { + this.state = request.payload.state; + if (this.manager.checkShardsState(request.payload.state)) { + this.manager.emitEvent(request.payload.botEvent, []); + } + break; + } + // The shard requests all connected shards to disconnect + case "disconnectAll" /* DisconnectAll */: { + yield this.manager.disconnectAll(request.payload); + break; + } + } + }); + } + /** + * Sends a message to the child process in order to emit a registered given event + * @param {string} event The event to be emitted in the child process + * @returns {Promise} + */ + communicate(event) { + return new Promise((resolve, reject) => { + const { identifier } = BotCommunication_1.BotCommunication; + const listener = ({ payload: { data }, identifier: responseIdentifier, }) => { + // Check if the received message is indeed identified by our identifier + if (identifier === responseIdentifier) { + // Resolve with the message data + resolve(data); + this.process.removeListener('message', listener); + } + }; + this.process.on('message', listener); + const request = { + action: "emitCommunicationEvent" /* EmitCommunicationEvent */, + payload: { event }, + identifier, + }; + this.process.send(request, err => { + if (err) + reject(err); + }); + }); + } + /** + * Sends the child process a message to emit the given event to {@link EventsHandler} + * @param {E} event The event to be emitted + * @param {Array} args The arguments of the events + */ + emitEvent(event, args) { + const request = { + action: "emitBotEvent" /* EmitBotEvent */, + payload: { + event, + args, + }, + identifier: Date.now(), + }; + this.process.send(request); + } + /** + * Disconnects this shard + * @param {GatewayCloseCode} code The shard close code + */ + disconnect(code) { + const request = { + action: "emitDisconnect" /* EmitDisconnect */, + payload: code, + identifier: Date.now(), + }; + this.process.send(request); + } +} +exports.BotShard = BotShard; diff --git a/lib/sharding/BotShardManager.d.ts b/lib/sharding/BotShardManager.d.ts new file mode 100644 index 000000000..119c4a0f1 --- /dev/null +++ b/lib/sharding/BotShardManager.d.ts @@ -0,0 +1,53 @@ +/// +import { Serializable } from 'child_process'; +import { Arguments } from 'typed-emitter'; +import { BotShardState } from './BotShard'; +import { Events } from '../bot/handlers/events/events'; +import { GatewayCloseCode } from '../socket'; +import { ShardId } from '../types'; +/** + * Creates and manages all bot shards + */ +export declare class BotShardManager { + private readonly token; + private readonly shards; + readonly file: string; + readonly shardsAmount: number; + constructor(file: string, token: string, shardsAmount: number); + /** + * Starts the shards and stores them inside a {@link Collection} + * @returns {Promise} + */ + start(): Promise; + /** + * Emits an event on all shards initiated with this manager + * @param {string} event The event to be emitted + * @returns {Promise} + */ + broadcast(event: string): Promise; + /** + * Emits a given event on all shards under this manager + * @param {E} event The event to emit + * @param {Array} args The arguments of the event + */ + emitEvent(event: E, args: Arguments): void; + /** + * Emits an event on a specific shard. + * Returns undefined if no shard matching the given ID was found + * @param {string} event The event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise | null} + */ + send(event: string, shardId: ShardId): Promise | undefined; + /** + * Checks if all shards under this manager match the given state + * @param {BotShardState} state The state to check if all shards match + * @returns {boolean} Whether all shards match that state + */ + checkShardsState(state: BotShardState): boolean; + /** + * Disconnects all active shards under this manager + * @param {GatewayCloseCode} code The shards' close code + */ + disconnectAll(code: GatewayCloseCode): void; +} diff --git a/lib/sharding/BotShardManager.js b/lib/sharding/BotShardManager.js new file mode 100644 index 000000000..0f4ca8db6 --- /dev/null +++ b/lib/sharding/BotShardManager.js @@ -0,0 +1,96 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotShardManager = void 0; +const BotShard_1 = require("./BotShard"); +const Collection_1 = __importDefault(require("../Collection")); +/** + * Creates and manages all bot shards + */ +class BotShardManager { + constructor(file, token, shardsAmount) { + this.file = file; + this.token = token; + this.shardsAmount = shardsAmount; + this.shards = new Collection_1.default(); + } + /** + * Starts the shards and stores them inside a {@link Collection} + * @returns {Promise} + */ + start() { + return __awaiter(this, void 0, void 0, function* () { + for (let i = 0; i < this.shardsAmount; i++) { + const shard = new BotShard_1.BotShard(this, i); + this.shards.set(shard.id, shard); + } + for (const [, shard] of this.shards) { + shard.spawn(); + } + }); + } + /** + * Emits an event on all shards initiated with this manager + * @param {string} event The event to be emitted + * @returns {Promise} + */ + broadcast(event) { + const results = []; + for (const [, shard] of this.shards) { + results.push(shard.communicate(event)); + } + return Promise.all(results); + } + /** + * Emits a given event on all shards under this manager + * @param {E} event The event to emit + * @param {Array} args The arguments of the event + */ + emitEvent(event, args) { + for (const [, shard] of this.shards) { + shard.emitEvent(event, args); + } + } + /** + * Emits an event on a specific shard. + * Returns undefined if no shard matching the given ID was found + * @param {string} event The event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise | null} + */ + send(event, shardId) { + const shard = this.shards.get(shardId); + if (!shard) + return undefined; + return shard.communicate(event); + } + /** + * Checks if all shards under this manager match the given state + * @param {BotShardState} state The state to check if all shards match + * @returns {boolean} Whether all shards match that state + */ + checkShardsState(state) { + return this.shards.toArray.every(value => value.state === state); + } + /** + * Disconnects all active shards under this manager + * @param {GatewayCloseCode} code The shards' close code + */ + disconnectAll(code) { + for (const [, shard] of this.shards) { + shard.disconnect(code); + } + } +} +exports.BotShardManager = BotShardManager; diff --git a/lib/sharding/index.d.ts b/lib/sharding/index.d.ts new file mode 100644 index 000000000..82e7f7e7d --- /dev/null +++ b/lib/sharding/index.d.ts @@ -0,0 +1,3 @@ +export * from './BotCommunication'; +export * from './BotShard'; +export * from './BotShardManager'; diff --git a/lib/sharding/index.js b/lib/sharding/index.js new file mode 100644 index 000000000..04885ac38 --- /dev/null +++ b/lib/sharding/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BotCommunication"), exports); +__exportStar(require("./BotShard"), exports); +__exportStar(require("./BotShardManager"), exports); diff --git a/lib/socket/BotHeartbeats.d.ts b/lib/socket/BotHeartbeats.d.ts new file mode 100644 index 000000000..4d02c57b1 --- /dev/null +++ b/lib/socket/BotHeartbeats.d.ts @@ -0,0 +1,43 @@ +/// +import { BotSocketShard } from './BotSocketShard'; +interface HeartbeatInterval { + timeout: number; + executor?: NodeJS.Timeout; +} +/** + * Handles the sending and receiving of Discord heartbeats + */ +export declare class BotHeartbeats { + private botSocketShard; + private readonly ws; + private readonly sequence; + private acked; + interval: HeartbeatInterval; + constructor(botSocket: BotSocketShard); + /** + * Starts the heartbeat interval + */ + start(): void; + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat(): void; + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat(): void; + /** + * Called when acking failed. Closes the socket and tries to reconnect + */ + private ackFailed; + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + private get heartbeatData(); + /** + * Called when a heartbeat is acked + */ + receivedAck(): void; +} +export {}; diff --git a/lib/socket/BotHeartbeats.js b/lib/socket/BotHeartbeats.js new file mode 100644 index 000000000..f7156024b --- /dev/null +++ b/lib/socket/BotHeartbeats.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotHeartbeats = void 0; +/** + * Handles the sending and receiving of Discord heartbeats + */ +class BotHeartbeats { + constructor(botSocket) { + this.botSocketShard = botSocket; + this.ws = botSocket.ws; + this.sequence = botSocket.sequence; + this.acked = true; + this.interval = { + timeout: 0, + }; + } + /** + * Starts the heartbeat interval + */ + start() { + this.interval.executor = setInterval(this.sendHeartbeat.bind(this), this.interval.timeout); + } + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat() { + if (!this.acked) { + this.ackFailed(); + return; + } + this.acked = false; + this.ws.send(this.botSocketShard.pack(this.heartbeatData), err => { + if (err) + throw err; + }); + } + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat() { + this.interval.timeout = -1; + if (this.interval.executor) { + clearInterval(this.interval.executor); + } + } + /** + * Called when acking failed. Closes the socket and tries to reconnect + */ + ackFailed() { + this.botSocketShard.close(); + } + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + get heartbeatData() { + return { op: 1 /* Heartbeat */, d: this.sequence || -1 }; + } + /** + * Called when a heartbeat is acked + */ + receivedAck() { + this.acked = true; + } +} +exports.BotHeartbeats = BotHeartbeats; diff --git a/lib/socket/BotSocket.d.ts b/lib/socket/BotSocket.d.ts new file mode 100644 index 000000000..2ce1a2f9f --- /dev/null +++ b/lib/socket/BotSocket.d.ts @@ -0,0 +1,69 @@ +import { BotSocketShard, BotSocketShardState } from './BotSocketShard'; +import { GatewayCloseCode } from './constants'; +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { BotStateEvents } from '../bot/handlers/events/events'; +import { BotShardState } from '../sharding'; +import { GatewayStruct } from '../structures'; +import { ShardId } from '../types'; +export interface SessionStartLimit { + total: number; + remaining: number; + reset_after: number; +} +/** + * Creates and manages socket shards + */ +export declare class BotSocket { + private readonly token; + readonly shards: Collection; + readonly bot: Bot; + gatewayURL: string; + sessionStartLimit: SessionStartLimit; + constructor(bot: Bot, token: string); + /** + * Start and connect every bot shard + * @param {number} [timeout=5500] Time in milliseconds to wait before establishing a new shard + * @returns {Promise} + */ + startShards(timeout?: number): Promise; + /** + * Stops and disconnects all active shards started by this process + * @param {GatewayCloseCode} code Gateway closure code + */ + stopShards(code: GatewayCloseCode): void; + /** + * Checks if all shards under this socket match a given state + * @param {BotSocketShardState} state The state to be checked for + * @returns {boolean} + */ + checkShardsState(state: BotSocketShardState): boolean; + /** + * Called when a shard under this socket changes its state. + * If all shards under this socket now have the same state, a message will be sent to the {@link BotShardManager} + * telling it to emit an event for all shards + * @param {BotSocketShardState} state The state to be checked for + * @param {BotShardState} shardState The state {@link BotShard} should be at after sending the message + * @param {BotStateEvents} botEvent The event that should be emitted to all shards + * @example ```typescript + * this.botSocket.shardChangedState( + * BotSocketShardState.Ready, + * BotShardState.Ready, + * BotEvents.Ready, + * ); + * ``` + */ + shardChangedState(state: BotSocketShardState, shardState: BotShardState, botEvent: BotStateEvents): void; + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @param {number} [shardId] The shard id thats gonna be affected + * @returns {void} + */ + modifyPresence(presence: GatewayStruct, shardId?: number): void; + /** + * Sends a request to the gateway in order to receive the connection information + * @returns {Promise} + */ + private get gateway(); +} diff --git a/lib/socket/BotSocket.js b/lib/socket/BotSocket.js new file mode 100644 index 000000000..01e2bbba8 --- /dev/null +++ b/lib/socket/BotSocket.js @@ -0,0 +1,140 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotSocket = void 0; +const node_fetch_1 = __importDefault(require("node-fetch")); +const BotSocketShard_1 = require("./BotSocketShard"); +const constants_1 = require("./constants"); +const Collection_1 = __importDefault(require("../Collection")); +const api_1 = require("../api"); +/** + * Creates and manages socket shards + */ +class BotSocket { + constructor(bot, token) { + this.bot = bot; + this.token = token; + this.shards = new Collection_1.default(); + } + /** + * Start and connect every bot shard + * @param {number} [timeout=5500] Time in milliseconds to wait before establishing a new shard + * @returns {Promise} + */ + startShards(timeout = constants_1.recommendedShardTimeout) { + return __awaiter(this, void 0, void 0, function* () { + const { url: gatewayURL, shards: suggestedShards, session_start_limit: sessionStartLimit, } = yield this.gateway; + this.gatewayURL = gatewayURL; + this.sessionStartLimit = sessionStartLimit; + const { id, amount = suggestedShards } = this.bot.shardOptions; + const shards = id !== undefined ? [id] : Array.from({ length: amount }).map((_, i) => i); + for (const shardId of shards) { + const botShard = new BotSocketShard_1.BotSocketShard(this, this.token, { + amount, + id: shardId, + }); + botShard.configure(); + this.shards.set(shardId, botShard); + botShard.connect(); + // eslint-disable-next-line no-await-in-loop + yield new Promise(resolve => setTimeout(resolve, timeout)); + } + }); + } + /** + * Stops and disconnects all active shards started by this process + * @param {GatewayCloseCode} code Gateway closure code + */ + stopShards(code) { + for (const [, shard] of this.shards) { + shard.close(code); + } + } + /** + * Checks if all shards under this socket match a given state + * @param {BotSocketShardState} state The state to be checked for + * @returns {boolean} + */ + checkShardsState(state) { + return this.shards.toArray.every(value => value.state === state); + } + /** + * Called when a shard under this socket changes its state. + * If all shards under this socket now have the same state, a message will be sent to the {@link BotShardManager} + * telling it to emit an event for all shards + * @param {BotSocketShardState} state The state to be checked for + * @param {BotShardState} shardState The state {@link BotShard} should be at after sending the message + * @param {BotStateEvents} botEvent The event that should be emitted to all shards + * @example ```typescript + * this.botSocket.shardChangedState( + * BotSocketShardState.Ready, + * BotShardState.Ready, + * BotEvents.Ready, + * ); + * ``` + */ + shardChangedState(state, shardState, botEvent) { + if (!this.checkShardsState(state)) + return; + if (process.send) { + const request = { + action: "shardChangedState" /* ShardChangedState */, + payload: { + state: shardState, + botEvent, + }, + identifier: Date.now(), + }; + process.send(request); + } + else { + this.bot.events.emit(botEvent); + } + } + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @param {number} [shardId] The shard id thats gonna be affected + * @returns {void} + */ + modifyPresence(presence, shardId) { + if (shardId) { + const shard = this.shards.get(shardId); + if (!shard) + return; + shard.send({ + op: 3 /* PresenceUpdate */, + d: presence, + }); + } + else { + for (const [, shard] of this.shards) { + shard.send({ + op: 3 /* PresenceUpdate */, + d: presence, + }); + } + } + } + /** + * Sends a request to the gateway in order to receive the connection information + * @returns {Promise} + */ + get gateway() { + return node_fetch_1.default(`${api_1.baseURL}/gateway/bot`, { + headers: { Authorization: `Bot ${this.token}` }, + }).then(res => res.json()); + } +} +exports.BotSocket = BotSocket; diff --git a/lib/socket/BotSocketShard.d.ts b/lib/socket/BotSocketShard.d.ts new file mode 100644 index 000000000..f7eab1cf5 --- /dev/null +++ b/lib/socket/BotSocketShard.d.ts @@ -0,0 +1,186 @@ +/// +import WebSocket from 'ws'; +import { BotSocket } from './BotSocket'; +import { GatewayCloseCode, GatewayEvent, OPCode } from './constants'; +import { Bot, ShardOptions } from '../bot'; +import { Snowflake } from '../types'; +/** + * The state of the socket shard + */ +export declare const enum BotSocketShardState { + Connecting = 0, + Processing = 1, + Ready = 2, + Closed = 3, + Terminated = 4 +} +export declare type PayloadData = any; +/** + * A payload thats gonna be sent to the Discord API + */ +export interface GatewayCommand { + op: OPCode; + d: PayloadData; +} +/** + * A payload received from the Discord API gateway + */ +export interface Payload extends GatewayCommand { + s: number; + t: GatewayEvent; +} +export declare type EventHandlers = Record void | Promise>; +/** + * Connects every bot shard to a {@link WebSocket} with the Discord Gateway. + * Handles gateway events and messages + */ +export declare class BotSocketShard { + /** + * The bot socket connection that initialized this shard + */ + private readonly botSocket; + /** + * The bot instance associated to this shard + */ + private readonly bot; + /** + * The token of the bot associated to this shard + */ + private readonly token; + /** + * Responsible for sending heartbeat payloads representing this shard + */ + private heartbeats; + /** + * Holds shard details + */ + readonly shard: ShardOptions; + /** + * Timeout before retrying to create a new connection after disconnection + * Resets on successful connection + */ + private retryTimeout; + /** + * All guilds pending to be cached from the gateway READY event + */ + pendingGuilds: Set; + /** + * This shard's state + */ + state: BotSocketShardState; + /** + * The WebSocket connection associated to this shard + */ + ws: WebSocket; + /** + * The session ID of this shard + */ + sessionId: string; + /** + * The sequence number of this shard + */ + sequence: number | null; + /** + * The number of the last sequence of this shard. Used to resume the connection from the last sequence in case of connection loss + */ + private lastSequence; + /** + * Holds options for the websocket connection + */ + private options; + /** + * The Inflate context used to compress incoming payloads + */ + private zlib; + constructor(botSocket: BotSocket, token: string, shard: ShardOptions); + /** + * Loads optional libraries and sets the options for the gateway websocket initialization + * @returns {void} + */ + configure(): void; + /** + * Connects to the Discord Gateway and resumes a previous connection if needed + * @param {boolean} resume Whether to resume a previous connection + * @returns {Promise} + */ + connect(resume?: boolean): Promise; + /** + * Decompresses data from the WebSocket if the compress option is sent to the gateway + * @param {Buffer} data The message received from the gateway + * @returns {Buffer | undefined} + */ + private decompressData; + /** + * Uses the right decoding and decompression to retrieve the payload object from the gateway message + * @param {WebSocket.Data} messageData The data of the message received from the gateway + * @returns {Payload | undefined} + */ + private retrievePayload; + /** + * Called when a new message is received from the gateway + * @param {Data} message WebSocket message event + * @returns {void} + */ + private onMessage; + /** + * Sends a new identify request to the gateway. + * Will use shards if needed + */ + private identify; + /** + * Calls the matching dispatch event from {@link events} + * @param {Payload} payload Dispatch payload + */ + private handleDispatch; + /** + * Close the connection between the bot and the gateway + * @param {GatewayCloseCode} code Socket close code https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + */ + close(code?: GatewayCloseCode): void; + /** + * Called when the bot is fully ready to proceed, after collecting + * all guilds from GUILD_CREATE events + */ + ready(): void; + /** + * Called when the close event from the {@link WebSocket} is emitted + * @param {number} code WebSocket closure code + * @param {string} reason The reason for the WebSocket closure + */ + private onClose; + /** + * Sends a request to the gateway in order to resume from the last connection + */ + private resume; + /** + * Waits if no further connections can be made at the time + * @param {SessionStartLimit} sessionLimit Session start limit object received + * from connecting to the gateway + * @returns {Promise} + */ + private handleSessionLimit; + /** + * Packs and sends the data to the WebSocket + * @param {unknown} data The data + * @returns {void} + */ + send(data: GatewayCommand): void; + /** + * Transfers the received data from the gateway into a {@link Payload} object + * @param {string} data Data received from the gateway + * @returns {Payload} + */ + private static parse; + /** + * Transfers the data into format which can be sent across the gateway + * @param {any} data Data to be transferred and sent to the gateway + * @returns {string} + */ + pack(data: GatewayCommand): Buffer | string | undefined; + /** + * Get the fully modified Socket URL to use when connecting to the gateway + * @param {string} url Socket URL + * @returns {string} + */ + private socketURL; +} diff --git a/lib/socket/BotSocketShard.js b/lib/socket/BotSocketShard.js new file mode 100644 index 000000000..965817291 --- /dev/null +++ b/lib/socket/BotSocketShard.js @@ -0,0 +1,339 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotSocketShard = void 0; +const querystring_1 = __importDefault(require("querystring")); +const ws_1 = __importDefault(require("ws")); +const BotHeartbeats_1 = require("./BotHeartbeats"); +const constants_1 = require("./constants"); +const events = __importStar(require("./handlers")); +const properties_1 = require("./properties"); +const api_1 = require("../api"); +const sharding_1 = require("../sharding"); +// Initializes variables for optional libraries +let erlpack; +let zlib; +/** + * Connects every bot shard to a {@link WebSocket} with the Discord Gateway. + * Handles gateway events and messages + */ +class BotSocketShard { + constructor(botSocket, token, shard) { + this.botSocket = botSocket; + this.bot = botSocket.bot; + this.token = token; + this.shard = shard; + this.retryTimeout = 0; + this.pendingGuilds = new Set(); + this.state = 3 /* Closed */; + this.sequence = null; + this.lastSequence = null; + } + /** + * Loads optional libraries and sets the options for the gateway websocket initialization + * @returns {void} + */ + configure() { + try { + erlpack = require('erlpack'); + } + catch (err) { + // Use json encoding + } + try { + zlib = require('zlib-sync'); + // Create new Inflate context + this.zlib = new zlib.Inflate({ + chunkSize: 128 * 1024, + windowBits: 32, + }); + } + catch (err) { + // Do not use data compressing + } + this.options = Object.assign({ v: api_1.version, encoding: erlpack ? 'etf' : 'json', compress: zlib && 'zlib-stream' }, this.bot.options.websocket); + this.bot.debug(this.options, 'options'); + } + /** + * Connects to the Discord Gateway and resumes a previous connection if needed + * @param {boolean} resume Whether to resume a previous connection + * @returns {Promise} + */ + connect(resume = false) { + return __awaiter(this, void 0, void 0, function* () { + this.bot.debug('Connecting...'); + if (this.state === 0 /* Connecting */) + return; + this.state = 0 /* Connecting */; + const { gatewayURL, sessionStartLimit } = this.botSocket; + const socketURL = this.socketURL(gatewayURL); + yield this.handleSessionLimit(sessionStartLimit); + this.ws = new ws_1.default(socketURL); + this.ws.on('message', this.onMessage.bind(this)); + this.ws.on('close', this.onClose.bind(this)); + this.heartbeats = new BotHeartbeats_1.BotHeartbeats(this); + this.botSocket.sessionStartLimit.remaining--; + if (resume && this.lastSequence) { + this.ws.on('open', this.resume.bind(this)); + } + }); + } + /** + * Decompresses data from the WebSocket if the compress option is sent to the gateway + * @param {Buffer} data The message received from the gateway + * @returns {Buffer | undefined} + */ + decompressData(data) { + if (!this.zlib || !zlib) + return; + if (data.length < 4 || data.readUInt32BE(data.length - 4) !== 0xffff) { + this.zlib.push(data, false); + return; + } + this.zlib.push(data, zlib.Z_SYNC_FLUSH); + if (this.zlib.err) { + this.bot.debug(`Zlib error: ${this.zlib.err} - ${this.zlib.msg}`); + return; + } + return Buffer.from(this.zlib.result); + } + /** + * Uses the right decoding and decompression to retrieve the payload object from the gateway message + * @param {WebSocket.Data} messageData The data of the message received from the gateway + * @returns {Payload | undefined} + */ + retrievePayload(messageData) { + let data; + if (messageData instanceof ArrayBuffer) { + // eslint-disable-next-line no-param-reassign + messageData = new Uint8Array(messageData); + } + if (messageData instanceof Buffer) { + /* + Payloads are served inside Buffer when: + 1. The ETF encoding is used + 2. Compression is used + + Decompress the message, and store it in the right format + */ + const decompressed = this.options.compress === 'zlib-stream' ? this.decompressData(messageData) : messageData; + if (!decompressed) + return; + data = this.options.encoding === 'etf' ? decompressed : decompressed.toString(); + } + else if (typeof messageData === 'string') { + // Payloads are served inside a string when the JSON encoding is used without compression + data = messageData; + } + if (!data) + return; + return BotSocketShard.parse(data); + } + /** + * Called when a new message is received from the gateway + * @param {Data} message WebSocket message event + * @returns {void} + */ + onMessage(message) { + const payload = this.retrievePayload(message); + if (!payload) + return; + const { op, t, d, s } = payload; + this.bot.debug(op, t, 'op - t'); + switch (op) { + case 0 /* Dispatch */: + this.sequence = s; + this.handleDispatch(payload); + break; + case 7 /* Reconnect */: + this.close(4000 /* UnknownError */); + break; + case 9 /* InvalidSession */: + // Wait 5 seconds and re-identify + setTimeout(this.identify.bind(this), 5000); + break; + case 10 /* Hello */: + this.heartbeats.interval.timeout = d.heartbeat_interval; + this.heartbeats.start(); + this.identify(); + this.state = 1 /* Processing */; + break; + case 11 /* HeartbeatACK */: + this.heartbeats.receivedAck(); + break; + } + } + /** + * Sends a new identify request to the gateway. + * Will use shards if needed + */ + identify() { + const { id, amount } = this.shard; + this.send({ + op: 2 /* Identify */, + d: Object.assign(Object.assign({}, properties_1.identify), { token: this.token, shard: [id, amount] }), + }); + } + /** + * Calls the matching dispatch event from {@link events} + * @param {Payload} payload Dispatch payload + */ + handleDispatch(payload) { + const { t } = payload; + // Set session ID in case of a Ready event + if (t === "READY" /* Ready */) { + this.sessionId = payload.d.session_id; + } + // Find the matching event and run it + const event = events[t]; + if (event) { + event(payload, this.bot, this); + } + } + /** + * Close the connection between the bot and the gateway + * @param {GatewayCloseCode} code Socket close code https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + */ + close(code = 1000 /* NormalClosure */) { + this.bot.debug('Closing connection!'); + this.state = 4 /* Terminated */; + // Stop sending heartbeats + this.heartbeats.stopHeartbeat(); + this.lastSequence = this.sequence; + // Close socket + this.ws.close(code); + } + /** + * Called when the bot is fully ready to proceed, after collecting + * all guilds from GUILD_CREATE events + */ + ready() { + this.state = 2 /* Ready */; + this.retryTimeout = 0; + this.bot.debug('Ready!', this.bot.guilds.cache.map(i => i.name)); + this.bot.events.emit(constants_1.BotEvent.ShardReady, this); + this.botSocket.shardChangedState(2 /* Ready */, sharding_1.BotShardState.Ready, constants_1.BotEvent.Ready); + } + /** + * Called when the close event from the {@link WebSocket} is emitted + * @param {number} code WebSocket closure code + * @param {string} reason The reason for the WebSocket closure + */ + onClose(code, reason) { + return __awaiter(this, void 0, void 0, function* () { + this.bot.debug('Close', code, reason); + this.state = 3 /* Closed */; + // Emit the 'ShardClose' event to the Bot + this.bot.events.emit(constants_1.BotEvent.ShardClose, this); + // Tell the BotSocket that the shard has been closed + this.botSocket.shardChangedState(3 /* Closed */, sharding_1.BotShardState.Closed, constants_1.BotEvent.Close); + this.heartbeats.stopHeartbeat(); + if (!constants_1.UnreconnectableGatewayCloseCodes.includes(code)) { + if (this.retryTimeout) { + yield new Promise(resolve => setTimeout(resolve, this.retryTimeout)); + } + this.retryTimeout += 1000; + yield this.connect(!constants_1.UnresumeableGatewayCloseCodes.includes(code)); + } + }); + } + /** + * Sends a request to the gateway in order to resume from the last connection + */ + resume() { + this.send({ + op: 6 /* Resume */, + d: { + token: this.token, + session_id: this.sessionId, + seq: this.lastSequence || this.sequence, + }, + }); + } + /** + * Waits if no further connections can be made at the time + * @param {SessionStartLimit} sessionLimit Session start limit object received + * from connecting to the gateway + * @returns {Promise} + */ + handleSessionLimit(sessionLimit) { + return __awaiter(this, void 0, void 0, function* () { + const { remaining, reset_after: resetAfter } = sessionLimit; + this.bot.debug(remaining, resetAfter, 'Handle session limit'); + if (remaining === 0) { + console.error(`Maximum number of daily Discord API connections exceeded! You will have to wait ${resetAfter}ms before attempting a new connection`); + yield new Promise(resolve => setTimeout(resolve, resetAfter)); + } + }); + } + /** + * Packs and sends the data to the WebSocket + * @param {unknown} data The data + * @returns {void} + */ + send(data) { + return this.ws.send(this.pack(data)); + } + /** + * Transfers the received data from the gateway into a {@link Payload} object + * @param {string} data Data received from the gateway + * @returns {Payload} + */ + static parse(data) { + if (data instanceof Buffer) { + if (!erlpack) + return; + return erlpack.unpack(data); + } + else { + return JSON.parse(data); + } + } + /** + * Transfers the data into format which can be sent across the gateway + * @param {any} data Data to be transferred and sent to the gateway + * @returns {string} + */ + pack(data) { + return this.options.encoding === 'etf' ? erlpack === null || erlpack === void 0 ? void 0 : erlpack.pack(data) : JSON.stringify(data); + } + /** + * Get the fully modified Socket URL to use when connecting to the gateway + * @param {string} url Socket URL + * @returns {string} + */ + socketURL(url) { + return `${url}/?${querystring_1.default.stringify(this.options)}`; + } +} +exports.BotSocketShard = BotSocketShard; diff --git a/lib/socket/constants.d.ts b/lib/socket/constants.d.ts new file mode 100644 index 000000000..de3c7590c --- /dev/null +++ b/lib/socket/constants.d.ts @@ -0,0 +1,129 @@ +/** + * https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes + */ +export declare const enum OPCode { + Dispatch = 0, + Heartbeat = 1, + Identify = 2, + PresenceUpdate = 3, + VoiceStateUpdate = 4, + Resume = 6, + Reconnect = 7, + RequestGuildMembers = 8, + InvalidSession = 9, + Hello = 10, + HeartbeatACK = 11 +} +/** + * https://discordapp.com/developers/docs/topics/gateway#commands-and-events-gateway-events + */ +export declare const enum GatewayEvent { + Ready = "READY", + Close = "CLOSE", + Resumed = "RESUMED", + Reconnect = "RECONNECT", + InvalidSession = "INVALID_SESSION", + ChannelCreate = "CHANNEL_CREATE", + ChannelUpdate = "CHANNEL_UPDATE", + ChannelDelete = "CHANNEL_DELETE", + ChannelPinsUpdate = "CHANNEL_PINS_UPDATE", + GuildCreate = "GUILD_CREATE", + GuildUpdate = "GUILD_UPDATE", + GuildDelete = "GUILD_DELETE", + GuildBanAdd = "GUILD_BAN_ADD", + GuildBanRemove = "GUILD_BAN_REMOVE", + GuildEmojisUpdate = "GUILD_EMOJIS_UPDATE", + GuildIntegrationsUpdate = "GUILD_INTEGRATIONS_UPDATE", + GuildMemberAdd = "GUILD_MEMBER_ADD", + GuildMemberRemove = "GUILD_MEMBER_REMOVE", + GuildMemberUpdate = "GUILD_MEMBER_UPDATE", + GuildMembersChunk = "GUILD_MEMBERS_CHUNK", + GuildRoleCreate = "GUILD_ROLE_CREATE", + GuildRoleUpdate = "GUILD_ROLE_UPDATE", + GuildRoleDelete = "GUILD_ROLE_DELETE", + InviteCreate = "INVITE_CREATE", + InviteDelete = "INVITE_DELETE", + MessageCreate = "MESSAGE_CREATE", + MessageUpdate = "MESSAGE_UPDATE", + MessageDelete = "MESSAGE_DELETE", + MessageDeleteBulk = "MESSAGE_DELETE_BULK", + MessageReactionAdd = "MESSAGE_REACTION_ADD", + MessageReactionRemove = "MESSAGE_REACTION_REMOVE", + MessageReactionRemoveAll = "MESSAGE_REACTION_REMOVE_ALL", + MessageReactionRemoveEmoji = "MESSAGE_REACTION_REMOVE_EMOJI", + PresenceUpdate = "PRESENCE_UPDATE", + TypingStart = "TYPING_START", + UserUpdate = "USER_UPDATE", + VoiceStateUpdate = "VOICE_STATE_UPDATE", + VoiceServerUpdate = "VOICE_SERVER_UPDATE", + WebhooksUpdate = "WEBHOOKS_UPDATE" +} +/** + * All Bot events + */ +export declare enum BotEvent { + Ready = "READY", + Close = "CLOSE", + Debug = "DEBUG", + ChannelCreate = "CHANNEL_CREATE", + ChannelUpdate = "CHANNEL_UPDATE", + ChannelDelete = "CHANNEL_DELETE", + ChannelPinsUpdate = "CHANNEL_PINS_UPDATE", + GuildCreate = "GUILD_CREATE", + GuildUpdate = "GUILD_UPDATE", + GuildDelete = "GUILD_DELETE", + GuildBanAdd = "GUILD_BAN_ADD", + GuildBanRemove = "GUILD_BAN_REMOVE", + GuildEmojisUpdate = "GUILD_EMOJIS_UPDATE", + GuildIntegrationsUpdate = "GUILD_INTEGRATIONS_UPDATE", + GuildMemberAdd = "GUILD_MEMBER_ADD", + GuildMemberRemove = "GUILD_MEMBER_REMOVE", + GuildMemberUpdate = "GUILD_MEMBER_UPDATE", + GuildMembersChunk = "GUILD_MEMBERS_CHUNK", + GuildMembersChunkFinish = "GUILD_MEMBERS_CHUNK_FINISH", + GuildRoleCreate = "GUILD_ROLE_CREATE", + GuildRoleUpdate = "GUILD_ROLE_UPDATE", + GuildRoleDelete = "GUILD_ROLE_DELETE", + InviteCreate = "INVITE_CREATE", + InviteDelete = "INVITE_DELETE", + MessageCreate = "MESSAGE_CREATE", + MessageUpdate = "MESSAGE_UPDATE", + MessageDelete = "MESSAGE_DELETE", + MessageDeleteBulk = "MESSAGE_DELETE_BULK", + MessageReactionAdd = "MESSAGE_REACTION_ADD", + MessageReactionRemove = "MESSAGE_REACTION_REMOVE", + MessageReactionRemoveAll = "MESSAGE_REACTION_REMOVE_ALL", + MessageReactionRemoveEmoji = "MESSAGE_REACTION_REMOVE_EMOJI", + PresenceUpdate = "PRESENCE_UPDATE", + ShardReady = "SHARD_READY", + ShardClose = "SHARD_CLOSE", + TypingStart = "TYPING_START", + UserUpdate = "USER_UPDATE", + VoiceStateUpdate = "VOICE_STATE_UPDATE", + VoiceServerUpdate = "VOICE_SERVER_UPDATE", + WebhooksUpdate = "WEBHOOKS_UPDATE" +} +/** + * https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + */ +export declare const enum GatewayCloseCode { + NormalClosure = 1000, + ManualClosure = 3000, + UnknownError = 4000, + UnknownOpcode = 4001, + DecodeError = 4002, + NotAuthenticated = 4003, + AuthenticationFailed = 4004, + AlreadyAuthenticated = 4005, + InvalidSeq = 4007, + RateLimited = 4008, + SessionTimedOut = 4009, + InvalidShard = 4010, + ShardingRequired = 4011, + InvalidAPIVersion = 4012, + InvalidIntent = 4013, + DisallowedIntent = 4014 +} +export declare const UnreconnectableGatewayCloseCodes: GatewayCloseCode[]; +export declare const UnresumeableGatewayCloseCodes: GatewayCloseCode[]; +export declare const recommendedShardTimeout = 5500; diff --git a/lib/socket/constants.js b/lib/socket/constants.js new file mode 100644 index 000000000..4b57435d4 --- /dev/null +++ b/lib/socket/constants.js @@ -0,0 +1,65 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.recommendedShardTimeout = exports.UnresumeableGatewayCloseCodes = exports.UnreconnectableGatewayCloseCodes = exports.BotEvent = void 0; +/** + * All Bot events + */ +var BotEvent; +(function (BotEvent) { + BotEvent["Ready"] = "READY"; + BotEvent["Close"] = "CLOSE"; + BotEvent["Debug"] = "DEBUG"; + /*Resumed = 'RESUMED', + Reconnect = 'RECONNECT', + InvalidSession = 'INVALID_SESSION',*/ + BotEvent["ChannelCreate"] = "CHANNEL_CREATE"; + BotEvent["ChannelUpdate"] = "CHANNEL_UPDATE"; + BotEvent["ChannelDelete"] = "CHANNEL_DELETE"; + BotEvent["ChannelPinsUpdate"] = "CHANNEL_PINS_UPDATE"; + BotEvent["GuildCreate"] = "GUILD_CREATE"; + BotEvent["GuildUpdate"] = "GUILD_UPDATE"; + BotEvent["GuildDelete"] = "GUILD_DELETE"; + BotEvent["GuildBanAdd"] = "GUILD_BAN_ADD"; + BotEvent["GuildBanRemove"] = "GUILD_BAN_REMOVE"; + BotEvent["GuildEmojisUpdate"] = "GUILD_EMOJIS_UPDATE"; + BotEvent["GuildIntegrationsUpdate"] = "GUILD_INTEGRATIONS_UPDATE"; + BotEvent["GuildMemberAdd"] = "GUILD_MEMBER_ADD"; + BotEvent["GuildMemberRemove"] = "GUILD_MEMBER_REMOVE"; + BotEvent["GuildMemberUpdate"] = "GUILD_MEMBER_UPDATE"; + BotEvent["GuildMembersChunk"] = "GUILD_MEMBERS_CHUNK"; + BotEvent["GuildMembersChunkFinish"] = "GUILD_MEMBERS_CHUNK_FINISH"; + BotEvent["GuildRoleCreate"] = "GUILD_ROLE_CREATE"; + BotEvent["GuildRoleUpdate"] = "GUILD_ROLE_UPDATE"; + BotEvent["GuildRoleDelete"] = "GUILD_ROLE_DELETE"; + BotEvent["InviteCreate"] = "INVITE_CREATE"; + BotEvent["InviteDelete"] = "INVITE_DELETE"; + BotEvent["MessageCreate"] = "MESSAGE_CREATE"; + BotEvent["MessageUpdate"] = "MESSAGE_UPDATE"; + BotEvent["MessageDelete"] = "MESSAGE_DELETE"; + BotEvent["MessageDeleteBulk"] = "MESSAGE_DELETE_BULK"; + BotEvent["MessageReactionAdd"] = "MESSAGE_REACTION_ADD"; + BotEvent["MessageReactionRemove"] = "MESSAGE_REACTION_REMOVE"; + BotEvent["MessageReactionRemoveAll"] = "MESSAGE_REACTION_REMOVE_ALL"; + BotEvent["MessageReactionRemoveEmoji"] = "MESSAGE_REACTION_REMOVE_EMOJI"; + BotEvent["PresenceUpdate"] = "PRESENCE_UPDATE"; + BotEvent["ShardReady"] = "SHARD_READY"; + BotEvent["ShardClose"] = "SHARD_CLOSE"; + BotEvent["TypingStart"] = "TYPING_START"; + BotEvent["UserUpdate"] = "USER_UPDATE"; + BotEvent["VoiceStateUpdate"] = "VOICE_STATE_UPDATE"; + BotEvent["VoiceServerUpdate"] = "VOICE_SERVER_UPDATE"; + BotEvent["WebhooksUpdate"] = "WEBHOOKS_UPDATE"; +})(BotEvent = exports.BotEvent || (exports.BotEvent = {})); +exports.UnreconnectableGatewayCloseCodes = [ + 3000 /* ManualClosure */, + 4004 /* AuthenticationFailed */, + 4010 /* InvalidShard */, + 4011 /* ShardingRequired */, + 4013 /* InvalidIntent */, + 4014 /* DisallowedIntent */, +]; +exports.UnresumeableGatewayCloseCodes = [ + 1000 /* NormalClosure */, + 4007 /* InvalidSeq */, +]; +exports.recommendedShardTimeout = 5500; diff --git a/lib/socket/handlers/channelCreate.d.ts b/lib/socket/handlers/channelCreate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelCreate.js b/lib/socket/handlers/channelCreate.js new file mode 100644 index 000000000..e4493b39a --- /dev/null +++ b/lib/socket/handlers/channelCreate.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils_1 = require("../../structures/channels/utils"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const channel = yield utils_1.ChannelUtils.create(bot, d); + utils_1.ChannelUtils.cache(bot, channel); + bot.events.emit(constants_1.BotEvent.ChannelCreate, channel); +}); diff --git a/lib/socket/handlers/channelDelete.d.ts b/lib/socket/handlers/channelDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelDelete.js b/lib/socket/handlers/channelDelete.js new file mode 100644 index 000000000..cc2fb6aeb --- /dev/null +++ b/lib/socket/handlers/channelDelete.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils_1 = require("../../structures/channels/utils"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const channel = yield utils_1.ChannelUtils.create(bot, d); + utils_1.ChannelUtils.delete(bot, channel); + bot.events.emit(constants_1.BotEvent.ChannelDelete, channel); +}); diff --git a/lib/socket/handlers/channelPinsUpdate.d.ts b/lib/socket/handlers/channelPinsUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelPinsUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelPinsUpdate.js b/lib/socket/handlers/channelPinsUpdate.js new file mode 100644 index 000000000..fab2c7278 --- /dev/null +++ b/lib/socket/handlers/channelPinsUpdate.js @@ -0,0 +1,20 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId, last_pin_timestamp: lastPinTimestamp } = d; + const channel = yield bot.channels.getText(channelId); + const oldPinTimestamp = channel.pins.lastPinTimestamp; + channel.pins.lastPinTimestamp = new structures_1.Timestamp(lastPinTimestamp); + bot.events.emit(constants_1.BotEvent.ChannelPinsUpdate, channel, oldPinTimestamp); +}); diff --git a/lib/socket/handlers/channelUpdate.d.ts b/lib/socket/handlers/channelUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelUpdate.js b/lib/socket/handlers/channelUpdate.js new file mode 100644 index 000000000..94dbaf0cf --- /dev/null +++ b/lib/socket/handlers/channelUpdate.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id } = d; + const channel = yield bot.channels.get(id); + const { before, after } = channel.update(d); + bot.events.emit(constants_1.BotEvent.ChannelUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildBanAdd.d.ts b/lib/socket/handlers/guildBanAdd.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildBanAdd.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildBanAdd.js b/lib/socket/handlers/guildBanAdd.js new file mode 100644 index 000000000..9f4184701 --- /dev/null +++ b/lib/socket/handlers/guildBanAdd.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + const ban = new guild_1.GuildBan(bot, { user }, guild); + // Add the member to the guild's bans controller + guild.bans.cache.add(ban); + bot.events.emit(constants_1.BotEvent.GuildBanAdd, ban); +}); diff --git a/lib/socket/handlers/guildBanRemove.d.ts b/lib/socket/handlers/guildBanRemove.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildBanRemove.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildBanRemove.js b/lib/socket/handlers/guildBanRemove.js new file mode 100644 index 000000000..08633a264 --- /dev/null +++ b/lib/socket/handlers/guildBanRemove.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + // Retrieve the ban if cached + const ban = guild.bans.cache.has(user.id) ? guild.bans.cache.get(user.id) : undefined; + // Remove the member from the Guild's bans Collection + guild.bans.cache.delete(user.id); + bot.events.emit(constants_1.BotEvent.GuildBanRemove, ban); +}); diff --git a/lib/socket/handlers/guildCreate.d.ts b/lib/socket/handlers/guildCreate.d.ts new file mode 100644 index 000000000..bdb96e9fd --- /dev/null +++ b/lib/socket/handlers/guildCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { BotSocketShard, Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot, socket: BotSocketShard) => void; +export default _default; diff --git a/lib/socket/handlers/guildCreate.js b/lib/socket/handlers/guildCreate.js new file mode 100644 index 000000000..f66dd5db3 --- /dev/null +++ b/lib/socket/handlers/guildCreate.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot, socket) => { + const { session_id: sessionId } = d; + socket.sessionId = sessionId; + const guild = guild_1.Guild.create(bot, d, socket.shard.id); + if (guild instanceof guild_1.Guild) { + // Delete the guild from the unavailable guilds collection if exists + if (bot.unavailableGuilds.has(guild.id)) { + bot.unavailableGuilds.delete(guild.id); + } + bot.guilds.cache.add(guild); + } + else { + bot.unavailableGuilds.set(guild.id, guild); + } + // The socket is still processing incoming GuildCreate events + if (socket.state === 1 /* Processing */) { + // Remove this guild from the pending guilds cache + socket.pendingGuilds.delete(guild.id); + if (!socket.pendingGuilds.size) { + // Fire the ready event if no guilds are pending + socket.ready(); + } + } + else { + bot.events.emit(constants_1.BotEvent.GuildCreate, guild); + } +}; diff --git a/lib/socket/handlers/guildDelete.d.ts b/lib/socket/handlers/guildDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildDelete.js b/lib/socket/handlers/guildDelete.js new file mode 100644 index 000000000..ee58a7569 --- /dev/null +++ b/lib/socket/handlers/guildDelete.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { unavailable, id } = d; + const guild = unavailable ? new guild_1.GuildUnavailable(bot, d) : yield bot.guilds.get(id); + guild_1.Guild.delete(bot, guild); + bot.events.emit(constants_1.BotEvent.GuildDelete, guild); +}); diff --git a/lib/socket/handlers/guildEmojisUpdate.d.ts b/lib/socket/handlers/guildEmojisUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildEmojisUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildEmojisUpdate.js b/lib/socket/handlers/guildEmojisUpdate.js new file mode 100644 index 000000000..050cc1ec4 --- /dev/null +++ b/lib/socket/handlers/guildEmojisUpdate.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const controllers_1 = require("../../controllers"); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, emojis } = d; + const guild = yield bot.guilds.get(guildId); + const before = guild.emojis.cache; + const after = new controllers_1.ControllerCache(emojis.map((emoji) => [emoji.id, new guild_1.GuildEmoji(bot, emoji, guild)])); + guild.emojis.cache = after; + bot.emojis.merge(guild.emojis.cache); + bot.events.emit(constants_1.BotEvent.GuildEmojisUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildIntegrationsUpdate.d.ts b/lib/socket/handlers/guildIntegrationsUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildIntegrationsUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildIntegrationsUpdate.js b/lib/socket/handlers/guildIntegrationsUpdate.js new file mode 100644 index 000000000..738f656e2 --- /dev/null +++ b/lib/socket/handlers/guildIntegrationsUpdate.js @@ -0,0 +1,17 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + bot.events.emit(constants_1.BotEvent.GuildIntegrationsUpdate, guild); +}); diff --git a/lib/socket/handlers/guildMemberAdd.d.ts b/lib/socket/handlers/guildMemberAdd.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildMemberAdd.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMemberAdd.js b/lib/socket/handlers/guildMemberAdd.js new file mode 100644 index 000000000..8bb3a8a6e --- /dev/null +++ b/lib/socket/handlers/guildMemberAdd.js @@ -0,0 +1,25 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Member_1 = require("../../structures/member/Member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + const member = new Member_1.Member(bot, d, guild); + // Cache the member in the guild's members cache + guild.members.cache.add(member); + if (member.user) { + // Cache the user in the Bot's users cache + bot.users.cache.add(member.user); + } + bot.events.emit(constants_1.BotEvent.GuildMemberAdd, member); +}); diff --git a/lib/socket/handlers/guildMemberRemove.d.ts b/lib/socket/handlers/guildMemberRemove.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildMemberRemove.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMemberRemove.js b/lib/socket/handlers/guildMemberRemove.js new file mode 100644 index 000000000..01a85efa2 --- /dev/null +++ b/lib/socket/handlers/guildMemberRemove.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + const member = guild.members.cache.get(user.id) || new structures_1.User(bot, user); + // Remove the member from the guild's members cache + guild.members.cache.delete(member.id); + bot.events.emit(constants_1.BotEvent.GuildMemberRemove, member); +}); diff --git a/lib/socket/handlers/guildMemberUpdate.d.ts b/lib/socket/handlers/guildMemberUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildMemberUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMemberUpdate.js b/lib/socket/handlers/guildMemberUpdate.js new file mode 100644 index 000000000..95d7d3ac1 --- /dev/null +++ b/lib/socket/handlers/guildMemberUpdate.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + const member = yield guild.members.get(user.id); + const { before, after } = member.update(Object.assign({ nick: member.nick, joined_at: member.joinedAt.date }, d)); + bot.events.emit(constants_1.BotEvent.GuildMemberUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildMembersChunk.d.ts b/lib/socket/handlers/guildMembersChunk.d.ts new file mode 100644 index 000000000..73c42dbdf --- /dev/null +++ b/lib/socket/handlers/guildMembersChunk.d.ts @@ -0,0 +1,17 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +/** + * Contains information about the current Guild Members Chunk + */ +export interface GuildMembersChunk { + /** + * The chunk index in the expected chunks for this response + */ + index: number; + /** + * The total number of expected chunks for this response + */ + count: number; +} +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMembersChunk.js b/lib/socket/handlers/guildMembersChunk.js new file mode 100644 index 000000000..6b6918205 --- /dev/null +++ b/lib/socket/handlers/guildMembersChunk.js @@ -0,0 +1,47 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const member_1 = require("../../structures/member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { not_found: notFound, guild_id: guildId, members, presences, nonce, chunk_index: chunkIndex, chunk_count: chunkCount, } = d; + if (notFound) { + throw new Error('An invalid ID was passed to the Guild Members request'); + } + const guild = yield bot.guilds.get(guildId); + // Add the new members to the guild's members cache + guild.members.cache.addMany(members.map((member) => new member_1.Member(bot, member, guild))); + // Assign the presence returned from the event + if (presences) { + for (const presence of presences) { + const { id } = presence.user; + const member = yield guild.members.get(id); + if (member.presence) { + // Re-initialize the member presence class with the updated presence + member.presence.init(presence); + } + else { + // Initialize the member presence class if not cached + member.presence = new member_1.MemberPresence(bot, presence, member); + } + } + } + // This chunk's information + const chunk = { + index: chunkIndex, + count: chunkCount, + }; + bot.events.emit(constants_1.BotEvent.GuildMembersChunk, guild, nonce, chunk); + if (chunk.index === chunk.count - 1) { + // This is the last chunk of the request, activate the GuildMembersChunkFinish event + bot.events.emit(constants_1.BotEvent.GuildMembersChunkFinish, guild, nonce); + } +}); diff --git a/lib/socket/handlers/guildRoleCreate.d.ts b/lib/socket/handlers/guildRoleCreate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildRoleCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildRoleCreate.js b/lib/socket/handlers/guildRoleCreate.js new file mode 100644 index 000000000..d4e5c33fd --- /dev/null +++ b/lib/socket/handlers/guildRoleCreate.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + const role = new structures_1.Role(bot, d.role, guild); + // Add role to the guild's roles cache + guild.roles.cache.add(role); + bot.events.emit(constants_1.BotEvent.GuildRoleCreate, role); +}); diff --git a/lib/socket/handlers/guildRoleDelete.d.ts b/lib/socket/handlers/guildRoleDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildRoleDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildRoleDelete.js b/lib/socket/handlers/guildRoleDelete.js new file mode 100644 index 000000000..2679d61d1 --- /dev/null +++ b/lib/socket/handlers/guildRoleDelete.js @@ -0,0 +1,22 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, role_id: roleId } = d; + const guild = yield bot.guilds.get(guildId); + const role = guild.roles.cache.get(roleId); + if (!role) + return; + // Remove the role from the guild's roles cache + guild.roles.cache.delete(role.id); + bot.events.emit(constants_1.BotEvent.GuildRoleDelete, role); +}); diff --git a/lib/socket/handlers/guildRoleUpdate.d.ts b/lib/socket/handlers/guildRoleUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildRoleUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildRoleUpdate.js b/lib/socket/handlers/guildRoleUpdate.js new file mode 100644 index 000000000..b6481b1e3 --- /dev/null +++ b/lib/socket/handlers/guildRoleUpdate.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + const role = guild.roles.cache.get(d.role.id); + if (!role) + return; + const { before, after } = role.update(d.role); + bot.events.emit(constants_1.BotEvent.GuildRoleUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildUpdate.d.ts b/lib/socket/handlers/guildUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildUpdate.js b/lib/socket/handlers/guildUpdate.js new file mode 100644 index 000000000..8d6bcf7cc --- /dev/null +++ b/lib/socket/handlers/guildUpdate.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id } = d; + const guild = guild_1.Guild.find(bot, id); + if (!guild) + return; + const { before, after } = guild.update(d); + bot.events.emit(constants_1.BotEvent.GuildUpdate, before, after); +}); diff --git a/lib/socket/handlers/index.d.ts b/lib/socket/handlers/index.d.ts new file mode 100644 index 000000000..d4842fe06 --- /dev/null +++ b/lib/socket/handlers/index.d.ts @@ -0,0 +1,35 @@ +export { default as CHANNEL_CREATE } from './channelCreate'; +export { default as CHANNEL_DELETE } from './channelDelete'; +export { default as CHANNEL_PINS_UPDATE } from './channelPinsUpdate'; +export { default as CHANNEL_UPDATE } from './channelUpdate'; +export { default as GUILD_BAN_ADD } from './guildBanAdd'; +export { default as GUILD_BAN_REMOVE } from './guildBanRemove'; +export { default as GUILD_CREATE } from './guildCreate'; +export { default as GUILD_DELETE } from './guildDelete'; +export { default as GUILD_EMOJIS_UPDATE } from './guildEmojisUpdate'; +export { default as GUILD_INTEGRATIONS_UPDATE } from './guildIntegrationsUpdate'; +export { default as GUILD_MEMBER_ADD } from './guildMemberAdd'; +export { default as GUILD_MEMBER_REMOVE } from './guildMemberRemove'; +export { default as GUILD_MEMBERS_CHUNK } from './guildMembersChunk'; +export { default as GUILD_MEMBER_UPDATE } from './guildMemberUpdate'; +export { default as GUILD_ROLE_CREATE } from './guildRoleCreate'; +export { default as GUILD_ROLE_DELETE } from './guildRoleDelete'; +export { default as GUILD_ROLE_UPDATE } from './guildRoleUpdate'; +export { default as GUILD_UPDATE } from './guildUpdate'; +export { default as INVITE_CREATE } from './inviteCreate'; +export { default as INVITE_DELETE } from './inviteDelete'; +export { default as MESSAGE_CREATE } from './messageCreate'; +export { default as MESSAGE_DELETE } from './messageDelete'; +export { default as MESSAGE_DELETE_BULK } from './messageDeleteBulk'; +export { default as MESSAGE_REACTION_ADD } from './messageReactionAdd'; +export { default as MESSAGE_REACTION_REMOVE } from './messageReactionRemove'; +export { default as MESSAGE_REACTION_REMOVE_ALL } from './messageReactionRemoveAll'; +export { default as MESSAGE_REACTION_REMOVE_EMOJI } from './messageReactionRemoveEmoji'; +export { default as MESSAGE_UPDATE } from './messageUpdate'; +export { default as READY } from './ready'; +export { default as PRESENCE_UPDATE } from './presenceUpdate'; +export { default as TYPING_START } from './typingStart'; +export { default as USER_UPDATE } from './userUpdate'; +export { default as WEBHOOKS_UPDATE } from './webhooksUpdate'; +export { default as VOICE_SERVER_UPDATE } from './voiceServerUpdate'; +export { default as VOICE_STATE_UPDATE } from './voiceStateUpdate'; diff --git a/lib/socket/handlers/index.js b/lib/socket/handlers/index.js new file mode 100644 index 000000000..874b84ae4 --- /dev/null +++ b/lib/socket/handlers/index.js @@ -0,0 +1,72 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var channelCreate_1 = require("./channelCreate"); +Object.defineProperty(exports, "CHANNEL_CREATE", { enumerable: true, get: function () { return channelCreate_1.default; } }); +var channelDelete_1 = require("./channelDelete"); +Object.defineProperty(exports, "CHANNEL_DELETE", { enumerable: true, get: function () { return channelDelete_1.default; } }); +var channelPinsUpdate_1 = require("./channelPinsUpdate"); +Object.defineProperty(exports, "CHANNEL_PINS_UPDATE", { enumerable: true, get: function () { return channelPinsUpdate_1.default; } }); +var channelUpdate_1 = require("./channelUpdate"); +Object.defineProperty(exports, "CHANNEL_UPDATE", { enumerable: true, get: function () { return channelUpdate_1.default; } }); +var guildBanAdd_1 = require("./guildBanAdd"); +Object.defineProperty(exports, "GUILD_BAN_ADD", { enumerable: true, get: function () { return guildBanAdd_1.default; } }); +var guildBanRemove_1 = require("./guildBanRemove"); +Object.defineProperty(exports, "GUILD_BAN_REMOVE", { enumerable: true, get: function () { return guildBanRemove_1.default; } }); +var guildCreate_1 = require("./guildCreate"); +Object.defineProperty(exports, "GUILD_CREATE", { enumerable: true, get: function () { return guildCreate_1.default; } }); +var guildDelete_1 = require("./guildDelete"); +Object.defineProperty(exports, "GUILD_DELETE", { enumerable: true, get: function () { return guildDelete_1.default; } }); +var guildEmojisUpdate_1 = require("./guildEmojisUpdate"); +Object.defineProperty(exports, "GUILD_EMOJIS_UPDATE", { enumerable: true, get: function () { return guildEmojisUpdate_1.default; } }); +var guildIntegrationsUpdate_1 = require("./guildIntegrationsUpdate"); +Object.defineProperty(exports, "GUILD_INTEGRATIONS_UPDATE", { enumerable: true, get: function () { return guildIntegrationsUpdate_1.default; } }); +var guildMemberAdd_1 = require("./guildMemberAdd"); +Object.defineProperty(exports, "GUILD_MEMBER_ADD", { enumerable: true, get: function () { return guildMemberAdd_1.default; } }); +var guildMemberRemove_1 = require("./guildMemberRemove"); +Object.defineProperty(exports, "GUILD_MEMBER_REMOVE", { enumerable: true, get: function () { return guildMemberRemove_1.default; } }); +var guildMembersChunk_1 = require("./guildMembersChunk"); +Object.defineProperty(exports, "GUILD_MEMBERS_CHUNK", { enumerable: true, get: function () { return guildMembersChunk_1.default; } }); +var guildMemberUpdate_1 = require("./guildMemberUpdate"); +Object.defineProperty(exports, "GUILD_MEMBER_UPDATE", { enumerable: true, get: function () { return guildMemberUpdate_1.default; } }); +var guildRoleCreate_1 = require("./guildRoleCreate"); +Object.defineProperty(exports, "GUILD_ROLE_CREATE", { enumerable: true, get: function () { return guildRoleCreate_1.default; } }); +var guildRoleDelete_1 = require("./guildRoleDelete"); +Object.defineProperty(exports, "GUILD_ROLE_DELETE", { enumerable: true, get: function () { return guildRoleDelete_1.default; } }); +var guildRoleUpdate_1 = require("./guildRoleUpdate"); +Object.defineProperty(exports, "GUILD_ROLE_UPDATE", { enumerable: true, get: function () { return guildRoleUpdate_1.default; } }); +var guildUpdate_1 = require("./guildUpdate"); +Object.defineProperty(exports, "GUILD_UPDATE", { enumerable: true, get: function () { return guildUpdate_1.default; } }); +var inviteCreate_1 = require("./inviteCreate"); +Object.defineProperty(exports, "INVITE_CREATE", { enumerable: true, get: function () { return inviteCreate_1.default; } }); +var inviteDelete_1 = require("./inviteDelete"); +Object.defineProperty(exports, "INVITE_DELETE", { enumerable: true, get: function () { return inviteDelete_1.default; } }); +var messageCreate_1 = require("./messageCreate"); +Object.defineProperty(exports, "MESSAGE_CREATE", { enumerable: true, get: function () { return messageCreate_1.default; } }); +var messageDelete_1 = require("./messageDelete"); +Object.defineProperty(exports, "MESSAGE_DELETE", { enumerable: true, get: function () { return messageDelete_1.default; } }); +var messageDeleteBulk_1 = require("./messageDeleteBulk"); +Object.defineProperty(exports, "MESSAGE_DELETE_BULK", { enumerable: true, get: function () { return messageDeleteBulk_1.default; } }); +var messageReactionAdd_1 = require("./messageReactionAdd"); +Object.defineProperty(exports, "MESSAGE_REACTION_ADD", { enumerable: true, get: function () { return messageReactionAdd_1.default; } }); +var messageReactionRemove_1 = require("./messageReactionRemove"); +Object.defineProperty(exports, "MESSAGE_REACTION_REMOVE", { enumerable: true, get: function () { return messageReactionRemove_1.default; } }); +var messageReactionRemoveAll_1 = require("./messageReactionRemoveAll"); +Object.defineProperty(exports, "MESSAGE_REACTION_REMOVE_ALL", { enumerable: true, get: function () { return messageReactionRemoveAll_1.default; } }); +var messageReactionRemoveEmoji_1 = require("./messageReactionRemoveEmoji"); +Object.defineProperty(exports, "MESSAGE_REACTION_REMOVE_EMOJI", { enumerable: true, get: function () { return messageReactionRemoveEmoji_1.default; } }); +var messageUpdate_1 = require("./messageUpdate"); +Object.defineProperty(exports, "MESSAGE_UPDATE", { enumerable: true, get: function () { return messageUpdate_1.default; } }); +var ready_1 = require("./ready"); +Object.defineProperty(exports, "READY", { enumerable: true, get: function () { return ready_1.default; } }); +var presenceUpdate_1 = require("./presenceUpdate"); +Object.defineProperty(exports, "PRESENCE_UPDATE", { enumerable: true, get: function () { return presenceUpdate_1.default; } }); +var typingStart_1 = require("./typingStart"); +Object.defineProperty(exports, "TYPING_START", { enumerable: true, get: function () { return typingStart_1.default; } }); +var userUpdate_1 = require("./userUpdate"); +Object.defineProperty(exports, "USER_UPDATE", { enumerable: true, get: function () { return userUpdate_1.default; } }); +var webhooksUpdate_1 = require("./webhooksUpdate"); +Object.defineProperty(exports, "WEBHOOKS_UPDATE", { enumerable: true, get: function () { return webhooksUpdate_1.default; } }); +var voiceServerUpdate_1 = require("./voiceServerUpdate"); +Object.defineProperty(exports, "VOICE_SERVER_UPDATE", { enumerable: true, get: function () { return voiceServerUpdate_1.default; } }); +var voiceStateUpdate_1 = require("./voiceStateUpdate"); +Object.defineProperty(exports, "VOICE_STATE_UPDATE", { enumerable: true, get: function () { return voiceStateUpdate_1.default; } }); diff --git a/lib/socket/handlers/inviteCreate.d.ts b/lib/socket/handlers/inviteCreate.d.ts new file mode 100644 index 000000000..13344373b --- /dev/null +++ b/lib/socket/handlers/inviteCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => void; +export default _default; diff --git a/lib/socket/handlers/inviteCreate.js b/lib/socket/handlers/inviteCreate.js new file mode 100644 index 000000000..53130e0f6 --- /dev/null +++ b/lib/socket/handlers/inviteCreate.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => { + var _a, _b; + const invite = new structures_1.Invite(bot, d); + // Add the invite to the guild's invites cache + (_a = invite.guild) === null || _a === void 0 ? void 0 : _a.invites.cache.add(invite); + // Add the invite to the guild channel's invites cache + (_b = invite.channel) === null || _b === void 0 ? void 0 : _b.invites.cache.add(invite); + bot.events.emit(constants_1.BotEvent.InviteCreate, invite); +}; diff --git a/lib/socket/handlers/inviteDelete.d.ts b/lib/socket/handlers/inviteDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/inviteDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/inviteDelete.js b/lib/socket/handlers/inviteDelete.js new file mode 100644 index 000000000..664beac9b --- /dev/null +++ b/lib/socket/handlers/inviteDelete.js @@ -0,0 +1,30 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const channels_1 = require("../../structures/channels"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, channel_id: channelId, code } = d; + const guild = yield bot.guilds.get(guildId); + const channel = yield bot.channels.get(channelId); + const invite = (guild === null || guild === void 0 ? void 0 : guild.invites.cache.get(code)) || { + channelId: channelId, + guild, + code, + }; + // Delete the invite from the guild's invites cache + guild === null || guild === void 0 ? void 0 : guild.invites.cache.delete(code); + if (channel instanceof channels_1.GuildChannel) { + // Delete the invite from the guild channel's invites cache + channel.invites.cache.delete(invite.code); + } + bot.events.emit(constants_1.BotEvent.InviteDelete, invite); +}); diff --git a/lib/socket/handlers/messageCreate.d.ts b/lib/socket/handlers/messageCreate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageCreate.js b/lib/socket/handlers/messageCreate.js new file mode 100644 index 000000000..92cda18bf --- /dev/null +++ b/lib/socket/handlers/messageCreate.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId } = d; + const channel = yield bot.channels.getText(channelId); + const message = new message_1.Message(bot, d, channel); + // Add the message to the cache + channel.messages.cache.add(message); + // Set the last message ID of the channel to that message + channel.lastMessageId = message.id; + bot.events.emit(constants_1.BotEvent.MessageCreate, message); +}); diff --git a/lib/socket/handlers/messageDelete.d.ts b/lib/socket/handlers/messageDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageDelete.js b/lib/socket/handlers/messageDelete.js new file mode 100644 index 000000000..7421447eb --- /dev/null +++ b/lib/socket/handlers/messageDelete.js @@ -0,0 +1,30 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const channels_1 = require("../../structures/channels"); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id, channel_id: channelId } = d; + const channel = yield bot.channels.getText(channelId); + const guild = channel instanceof channels_1.GuildTextChannel ? channel.guild : undefined; + const message = channel.messages.cache.get(id) || { + id, + guild, + channel, + }; + if (message instanceof message_1.Message) { + // Mark the message as deleted + message.deleted = true; + } + channel.messages.cache.delete(message.id); + bot.events.emit(constants_1.BotEvent.MessageDelete, message); +}); diff --git a/lib/socket/handlers/messageDeleteBulk.d.ts b/lib/socket/handlers/messageDeleteBulk.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageDeleteBulk.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageDeleteBulk.js b/lib/socket/handlers/messageDeleteBulk.js new file mode 100644 index 000000000..795d0fda9 --- /dev/null +++ b/lib/socket/handlers/messageDeleteBulk.js @@ -0,0 +1,26 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { ids, channel_id: channelId } = d; + const channel = yield bot.channels.getText(channelId); + // Use the Message class if the message is cached, otherwise use the message ID + const messages = ids.map((id) => channel.messages.cache.get(id) || id); + for (const message of messages) { + if (message instanceof message_1.Message) { + // Mark message as deleted + message.deleted = true; + } + } + bot.events.emit(constants_1.BotEvent.MessageDeleteBulk, channel, messages); +}); diff --git a/lib/socket/handlers/messageReactionAdd.d.ts b/lib/socket/handlers/messageReactionAdd.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionAdd.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionAdd.js b/lib/socket/handlers/messageReactionAdd.js new file mode 100644 index 000000000..9573c1a8f --- /dev/null +++ b/lib/socket/handlers/messageReactionAdd.js @@ -0,0 +1,44 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Member_1 = require("../../structures/member/Member"); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + var _a, _b; + const { user_id: userId } = d; + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const { emoji } = handlersUtils; + const message = yield handlersUtils.getMessage(); + const { guild } = message; + const { id } = emoji; + // Set the reaction object for this Emoji if one hasn't been set before + const reaction = message.reactions.cache.getOrSet(id, new message_1.MessageReaction(message, { + emoji, + botReacted: userId === ((_a = bot.user) === null || _a === void 0 ? void 0 : _a.id), + })); + // Change the reaction's botReacted state + reaction.botReacted = userId === ((_b = bot.user) === null || _b === void 0 ? void 0 : _b.id); + // Increment the count of the reaction + reaction.count++; + const user = yield bot.users.get(userId); + const member = d.member && guild ? new Member_1.Member(bot, d.member, guild) : undefined; + // Add the user to the Collection of users who reacted with this reaction + if (user) { + reaction.users.cache.add(user); + } + // Add the member to the Collection of members who reacted with this reaction + if (member) { + reaction.members.set(member.id, member); + } + bot.events.emit(constants_1.BotEvent.MessageReactionAdd, reaction, member || user); +}); diff --git a/lib/socket/handlers/messageReactionRemove.d.ts b/lib/socket/handlers/messageReactionRemove.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemove.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionRemove.js b/lib/socket/handlers/messageReactionRemove.js new file mode 100644 index 000000000..2be170147 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemove.js @@ -0,0 +1,46 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + var _a; + const { user_id: userId } = d; + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const { emoji } = handlersUtils; + const message = yield handlersUtils.getMessage(); + const { id } = emoji; + // Validates that the reaction is cached + if (!message.reactions.cache.has(id)) + return; + const reaction = message.reactions.cache.get(id); + const user = reaction.users.cache.get(userId); + const member = reaction.members.get(userId); + // Change the reaction's botReacted state + reaction.botReacted = userId === ((_a = bot.user) === null || _a === void 0 ? void 0 : _a.id); + // Decrements the count of the reaction + reaction.count--; + if (reaction.count > 0) { + // Removes the user from the Collection of users who reacted with this reaction + if (user) { + reaction.users.cache.delete(user.id); + } + // Removes the member from the Collection of members who reacted with this reaction + if (member) { + reaction.members.delete(member.id); + } + } + else { + // Terminate the reaction completely from the message cached reactions + message.reactions.cache.delete(id); + } + bot.events.emit(constants_1.BotEvent.MessageReactionRemove, reaction, member || user); +}); diff --git a/lib/socket/handlers/messageReactionRemoveAll.d.ts b/lib/socket/handlers/messageReactionRemoveAll.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveAll.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionRemoveAll.js b/lib/socket/handlers/messageReactionRemoveAll.js new file mode 100644 index 000000000..efc8bfefb --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveAll.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const message = yield handlersUtils.getMessage(); + message.reactions.cache.clear(); + bot.events.emit(constants_1.BotEvent.MessageReactionRemoveAll, message); +}); diff --git a/lib/socket/handlers/messageReactionRemoveEmoji.d.ts b/lib/socket/handlers/messageReactionRemoveEmoji.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveEmoji.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionRemoveEmoji.js b/lib/socket/handlers/messageReactionRemoveEmoji.js new file mode 100644 index 000000000..eb7d3e618 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveEmoji.js @@ -0,0 +1,22 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const { emoji } = handlersUtils; + const message = yield handlersUtils.getMessage(); + const { id } = emoji; + const reaction = message.reactions.cache.get(id); + message.reactions.cache.delete(id); + bot.events.emit(constants_1.BotEvent.MessageReactionRemoveEmoji, reaction); +}); diff --git a/lib/socket/handlers/messageUpdate.d.ts b/lib/socket/handlers/messageUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageUpdate.js b/lib/socket/handlers/messageUpdate.js new file mode 100644 index 000000000..fc3c304f3 --- /dev/null +++ b/lib/socket/handlers/messageUpdate.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId, id } = d; + const channel = yield bot.channels.getText(channelId); + const message = yield channel.messages.get(id); + const { before, after } = message.update(d); + bot.events.emit(constants_1.BotEvent.MessageUpdate, before, after); +}); diff --git a/lib/socket/handlers/presenceUpdate.d.ts b/lib/socket/handlers/presenceUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/presenceUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/presenceUpdate.js b/lib/socket/handlers/presenceUpdate.js new file mode 100644 index 000000000..644601677 --- /dev/null +++ b/lib/socket/handlers/presenceUpdate.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const member_1 = require("../../structures/member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { user: { id: memberId }, guild_id: guildId, } = d; + const guild = yield bot.guilds.get(guildId); + const member = yield guild.members.get(memberId); + const { presence } = member; + // Manual update due to presence's possibility of being undefined + const before = presence === null || presence === void 0 ? void 0 : presence.clone(); + const after = (presence === null || presence === void 0 ? void 0 : presence.init(d)) || (member.presence = new member_1.MemberPresence(bot, d, member)); + bot.events.emit(constants_1.BotEvent.PresenceUpdate, before, after); +}); diff --git a/lib/socket/handlers/ready.d.ts b/lib/socket/handlers/ready.d.ts new file mode 100644 index 000000000..bdb96e9fd --- /dev/null +++ b/lib/socket/handlers/ready.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { BotSocketShard, Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot, socket: BotSocketShard) => void; +export default _default; diff --git a/lib/socket/handlers/ready.js b/lib/socket/handlers/ready.js new file mode 100644 index 000000000..1fc22e559 --- /dev/null +++ b/lib/socket/handlers/ready.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +exports.default = ({ d }, bot, socket) => { + const { session_id: sessionId, user, guilds } = d; + socket.sessionId = sessionId; + bot.user = new structures_1.BotUser(bot, user); + bot.users.cache.add(bot.user); + if (guilds.length) { + // Store all pending guilds to later be retrieved from incoming gateway GUILD_CREATE events + for (const guild of guilds) { + bot.unavailableGuilds.set(guild.id, guild); + socket.pendingGuilds.add(guild.id); + } + } + else { + // No pending guilds - the bot is ready! + socket.ready(); + } + bot.debug(bot.guilds, 'bot guilds'); +}; diff --git a/lib/socket/handlers/typingStart.d.ts b/lib/socket/handlers/typingStart.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/typingStart.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/typingStart.js b/lib/socket/handlers/typingStart.js new file mode 100644 index 000000000..9f28464ec --- /dev/null +++ b/lib/socket/handlers/typingStart.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const channels_1 = require("../../structures/channels"); +const member_1 = require("../../structures/member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId, timestamp, member } = d; + const channel = yield bot.channels.getText(channelId); + // Retrieve user / member from the channel + const user = channel instanceof channels_1.GuildTextChannel + ? new member_1.Member(bot, member, channel.guild) + : channel.recipient; + bot.events.emit(constants_1.BotEvent.TypingStart, channel, user, timestamp); +}); diff --git a/lib/socket/handlers/userUpdate.d.ts b/lib/socket/handlers/userUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/userUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/userUpdate.js b/lib/socket/handlers/userUpdate.js new file mode 100644 index 000000000..28c715f92 --- /dev/null +++ b/lib/socket/handlers/userUpdate.js @@ -0,0 +1,20 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id } = d; + const user = bot.users.cache.get(id); + if (!user) + return; + const { before, after } = user.update(d); + bot.events.emit(constants_1.BotEvent.UserUpdate, before, after); +}); diff --git a/lib/socket/handlers/voiceServerUpdate.d.ts b/lib/socket/handlers/voiceServerUpdate.d.ts new file mode 100644 index 000000000..e5b7c949a --- /dev/null +++ b/lib/socket/handlers/voiceServerUpdate.d.ts @@ -0,0 +1,4 @@ +import { Payload } from '..'; +import { Bot } from '../..'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/voiceServerUpdate.js b/lib/socket/handlers/voiceServerUpdate.js new file mode 100644 index 000000000..9a8636bf5 --- /dev/null +++ b/lib/socket/handlers/voiceServerUpdate.js @@ -0,0 +1,15 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const __1 = require(".."); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + bot.events.emit(__1.BotEvent.VoiceServerUpdate, bot.guilds.cache.get(d.guild_id), d); +}); diff --git a/lib/socket/handlers/voiceStateUpdate.d.ts b/lib/socket/handlers/voiceStateUpdate.d.ts new file mode 100644 index 000000000..e5b7c949a --- /dev/null +++ b/lib/socket/handlers/voiceStateUpdate.d.ts @@ -0,0 +1,4 @@ +import { Payload } from '..'; +import { Bot } from '../..'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/voiceStateUpdate.js b/lib/socket/handlers/voiceStateUpdate.js new file mode 100644 index 000000000..22c7ba253 --- /dev/null +++ b/lib/socket/handlers/voiceStateUpdate.js @@ -0,0 +1,22 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const __1 = require(".."); +const VoiceState_1 = __importDefault(require("../../structures/voice/VoiceState")); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const oldVoiceState = bot.guilds.cache.get(d.guild_id).voiceStates.get(d.user_id); + const newVoiceState = new VoiceState_1.default(bot, oldVoiceState.member, d); + bot.events.emit(__1.BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); + bot.guilds.cache.get(d.guild_id).voiceStates.set(d.user_id, newVoiceState); +}); diff --git a/lib/socket/handlers/webhooksUpdate.d.ts b/lib/socket/handlers/webhooksUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/webhooksUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/webhooksUpdate.js b/lib/socket/handlers/webhooksUpdate.js new file mode 100644 index 000000000..70addf334 --- /dev/null +++ b/lib/socket/handlers/webhooksUpdate.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, channel_id: channelId } = d; + const guild = yield bot.guilds.get(guildId); + const channel = yield guild.channels.get(channelId); + bot.events.emit(constants_1.BotEvent.WebhooksUpdate, channel); +}); diff --git a/lib/socket/index.d.ts b/lib/socket/index.d.ts new file mode 100644 index 000000000..7dd03c537 --- /dev/null +++ b/lib/socket/index.d.ts @@ -0,0 +1,6 @@ +export * from './utils'; +export * from './BotHeartbeats'; +export * from './BotSocket'; +export * from './BotSocketShard'; +export * from './constants'; +export * from './properties'; diff --git a/lib/socket/index.js b/lib/socket/index.js new file mode 100644 index 000000000..b2fb56971 --- /dev/null +++ b/lib/socket/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./utils"), exports); +__exportStar(require("./BotHeartbeats"), exports); +__exportStar(require("./BotSocket"), exports); +__exportStar(require("./BotSocketShard"), exports); +__exportStar(require("./constants"), exports); +__exportStar(require("./properties"), exports); diff --git a/lib/socket/properties.d.ts b/lib/socket/properties.d.ts new file mode 100644 index 000000000..fc9946a5d --- /dev/null +++ b/lib/socket/properties.d.ts @@ -0,0 +1,34 @@ +/// +/** + * Websocket connection options + */ +export interface WebsocketOptions { + /** + * API Version + */ + v: number; + /** + * Gateway payload encoding type + */ + encoding: 'json' | 'etf'; + /** + * Gateway payload compression type + */ + compress?: 'zlib-stream'; + [key: string]: number | string | undefined; +} +/** + * All details that are sent in the Bot's 'IDENTIFY' request + */ +export declare const identify: { + properties: { + $os: NodeJS.Platform; + $browser: string; + $device: string; + }; + presence: { + status: string; + }; + large_threshold: number; + version: number; +}; diff --git a/lib/socket/properties.js b/lib/socket/properties.js new file mode 100644 index 000000000..68438a5e0 --- /dev/null +++ b/lib/socket/properties.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.identify = void 0; +const api_1 = require("../api"); +/** + * All details that are sent in the Bot's 'IDENTIFY' request + */ +exports.identify = { + properties: { + $os: process.platform, + $browser: 'node-discord', + $device: 'node-discord', + }, + presence: { + status: 'online', + }, + large_threshold: 250, + version: api_1.version, +}; diff --git a/lib/socket/utils/HandlersUtils.d.ts b/lib/socket/utils/HandlersUtils.d.ts new file mode 100644 index 000000000..8675021ec --- /dev/null +++ b/lib/socket/utils/HandlersUtils.d.ts @@ -0,0 +1,16 @@ +import { Bot } from '../../bot'; +import { PayloadData } from '../BotSocketShard'; +/** + * Main class for all util methods for socket event handlers. + */ +export declare class HandlersUtils { + /** + * The bot associated to this socket event + */ + protected readonly bot: Bot; + /** + * The data received from the socket event + */ + protected readonly data: PayloadData; + constructor(bot: Bot, data: PayloadData); +} diff --git a/lib/socket/utils/HandlersUtils.js b/lib/socket/utils/HandlersUtils.js new file mode 100644 index 000000000..fe4b1d225 --- /dev/null +++ b/lib/socket/utils/HandlersUtils.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HandlersUtils = void 0; +/** + * Main class for all util methods for socket event handlers. + */ +class HandlersUtils { + constructor(bot, data) { + this.bot = bot; + this.data = data; + } +} +exports.HandlersUtils = HandlersUtils; diff --git a/lib/socket/utils/ReactionHandlersUtils.d.ts b/lib/socket/utils/ReactionHandlersUtils.d.ts new file mode 100644 index 000000000..7c4f9739a --- /dev/null +++ b/lib/socket/utils/ReactionHandlersUtils.d.ts @@ -0,0 +1,17 @@ +import { HandlersUtils } from './HandlersUtils'; +import { Emoji, Message } from '../../structures'; +/** + * Provides util methods for all reactions-related handlers + */ +export declare class ReactionHandlersUtils extends HandlersUtils { + /** + * Returns the {@link Emoji} received from the event data + * @type {Emoji} + */ + get emoji(): Emoji; + /** + * Returns the message extracted from the event data + * @type {Message | undefined} + */ + getMessage(): Promise; +} diff --git a/lib/socket/utils/ReactionHandlersUtils.js b/lib/socket/utils/ReactionHandlersUtils.js new file mode 100644 index 000000000..25fc4deda --- /dev/null +++ b/lib/socket/utils/ReactionHandlersUtils.js @@ -0,0 +1,38 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ReactionHandlersUtils = void 0; +const HandlersUtils_1 = require("./HandlersUtils"); +const structures_1 = require("../../structures"); +/** + * Provides util methods for all reactions-related handlers + */ +class ReactionHandlersUtils extends HandlersUtils_1.HandlersUtils { + /** + * Returns the {@link Emoji} received from the event data + * @type {Emoji} + */ + get emoji() { + return new structures_1.Emoji(this.bot, this.data.emoji); + } + /** + * Returns the message extracted from the event data + * @type {Message | undefined} + */ + getMessage() { + return __awaiter(this, void 0, void 0, function* () { + const { channel_id: channelId, message_id: messageId } = this.data; + const channel = yield this.bot.channels.getText(channelId); + return channel.messages.get(messageId); + }); + } +} +exports.ReactionHandlersUtils = ReactionHandlersUtils; diff --git a/lib/socket/utils/index.d.ts b/lib/socket/utils/index.d.ts new file mode 100644 index 000000000..822a90546 --- /dev/null +++ b/lib/socket/utils/index.d.ts @@ -0,0 +1,2 @@ +export * from './HandlersUtils'; +export * from './ReactionHandlersUtils'; diff --git a/lib/socket/utils/index.js b/lib/socket/utils/index.js new file mode 100644 index 000000000..56f70b4bd --- /dev/null +++ b/lib/socket/utils/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./HandlersUtils"), exports); +__exportStar(require("./ReactionHandlersUtils"), exports); diff --git a/lib/structures/Avatar.d.ts b/lib/structures/Avatar.d.ts new file mode 100644 index 000000000..1a137b32d --- /dev/null +++ b/lib/structures/Avatar.d.ts @@ -0,0 +1,133 @@ +import { Snowflake } from '../types'; +/** + * The base URL for Discord API images + * https://discord.com/developers/docs/reference#image-formatting-image-base-url + * @type {string} + */ +export declare const cdnBaseURL = "https://cdn.discordapp.com"; +/** + * The allowed formats for guild emojis (better known as custom emojis) + */ +export declare enum GuildEmojiFormat { + PNG = "png", + GIF = "gif" +} +/** + * The allowed formats for guild icons + */ +export declare enum GuildIconFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp", + GIF = "gif" +} +/** + * The allowed formats for a guild's splash image + */ +export declare enum GuildSplashFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp" +} +/** + * The allowed formats for a guild's discovery splash image + */ +export declare enum GuildDiscoverySplashFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp" +} +/** + * The allowed formats for a guild's banner image + */ +export declare enum GuildBannerFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp" +} +/** + * The allowed formats for a user's avatar + */ +export declare enum UserAvatarFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp", + GIF = "gif" +} +/** + * All avatar format enums + */ +export declare type AvatarFormat = GuildEmojiFormat | GuildIconFormat | GuildSplashFormat | GuildDiscoverySplashFormat | GuildBannerFormat | UserAvatarFormat; +/** + * Generates and modifies avatar URLs according to the given arguments + */ +export declare class Avatar { + /** + * Adds the given modifiers to an avatar URL + * @param {string} url The original avatar URL + * @param {AvatarFormat} format The required avatar image format + * @param {number} size The required avatar image size + * @returns {string} + */ + private static enhanceURL; + /** + * Returns a guild emoji's image + * @param {Snowflake} id The ID of the emoji + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + static emojiURL(id: Snowflake, format?: GuildEmojiFormat, size?: number): string; + /** + * Returns a guild's icon + * @param {string} hash The guild's icon hash + * @param {Snowflake} id The ID of the guild + * @param {GuildIconFormat} format The format of the returned icon image + * @param {number} size The size of the returned icon image + * @returns {string} + */ + static guildURL(hash: string, id: Snowflake, format?: GuildIconFormat, size?: number): string; + /** + * Returns a guild's splash image URL + * @param {string} hash The splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildSplashFormat} format The format of the returned splash image + * @param {number} size The size of the returned splash image + * @returns {string} + */ + static splashURL(hash: string, id: Snowflake, format?: GuildSplashFormat, size?: number): string; + /** + * Returns a guild's discovery splash image URL + * @param {string} hash The discovery splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildDiscoverySplashFormat} format The format of the returned discovery splash image + * @param {number} size The size of the returned discovery splash image + * @returns {string} + */ + static discoverySplashURL(hash: string, id: Snowflake, format?: GuildDiscoverySplashFormat, size?: number): string; + /** + * Returns a guild's banner image URL + * @param {string} hash The banner hash + * @param {Snowflake} id The ID of the guild + * @param {GuildBannerFormat} format The format of the returned banner image + * @param {number} size The size of the returned banner image + * @returns {string} + */ + static bannerURL(hash: string, id: Snowflake, format?: GuildBannerFormat, size?: number): string; + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link Format.PNG}* + * @param {string | null} hash The user's avatar hash + * @param {Snowflake} id The user's ID + * @param {number} hashtag the user's hashtag + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + static userAvatarURL(hash: string | null, id: Snowflake, hashtag: string, format?: UserAvatarFormat, size?: number): string; +} diff --git a/lib/structures/Avatar.js b/lib/structures/Avatar.js new file mode 100644 index 000000000..181cca677 --- /dev/null +++ b/lib/structures/Avatar.js @@ -0,0 +1,164 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Avatar = exports.UserAvatarFormat = exports.GuildBannerFormat = exports.GuildDiscoverySplashFormat = exports.GuildSplashFormat = exports.GuildIconFormat = exports.GuildEmojiFormat = exports.cdnBaseURL = void 0; +/** + * The base URL for Discord API images + * https://discord.com/developers/docs/reference#image-formatting-image-base-url + * @type {string} + */ +exports.cdnBaseURL = 'https://cdn.discordapp.com'; +/** + * The allowed formats for guild emojis (better known as custom emojis) + */ +var GuildEmojiFormat; +(function (GuildEmojiFormat) { + GuildEmojiFormat["PNG"] = "png"; + GuildEmojiFormat["GIF"] = "gif"; +})(GuildEmojiFormat = exports.GuildEmojiFormat || (exports.GuildEmojiFormat = {})); +/** + * The allowed formats for guild icons + */ +var GuildIconFormat; +(function (GuildIconFormat) { + GuildIconFormat["JPEG"] = "jpeg"; + GuildIconFormat["JPG"] = "jpg"; + GuildIconFormat["PNG"] = "png"; + GuildIconFormat["WebP"] = "webp"; + GuildIconFormat["GIF"] = "gif"; +})(GuildIconFormat = exports.GuildIconFormat || (exports.GuildIconFormat = {})); +/** + * The allowed formats for a guild's splash image + */ +var GuildSplashFormat; +(function (GuildSplashFormat) { + GuildSplashFormat["JPEG"] = "jpeg"; + GuildSplashFormat["JPG"] = "jpg"; + GuildSplashFormat["PNG"] = "png"; + GuildSplashFormat["WebP"] = "webp"; +})(GuildSplashFormat = exports.GuildSplashFormat || (exports.GuildSplashFormat = {})); +/** + * The allowed formats for a guild's discovery splash image + */ +var GuildDiscoverySplashFormat; +(function (GuildDiscoverySplashFormat) { + GuildDiscoverySplashFormat["JPEG"] = "jpeg"; + GuildDiscoverySplashFormat["JPG"] = "jpg"; + GuildDiscoverySplashFormat["PNG"] = "png"; + GuildDiscoverySplashFormat["WebP"] = "webp"; +})(GuildDiscoverySplashFormat = exports.GuildDiscoverySplashFormat || (exports.GuildDiscoverySplashFormat = {})); +/** + * The allowed formats for a guild's banner image + */ +var GuildBannerFormat; +(function (GuildBannerFormat) { + GuildBannerFormat["JPEG"] = "jpeg"; + GuildBannerFormat["JPG"] = "jpg"; + GuildBannerFormat["PNG"] = "png"; + GuildBannerFormat["WebP"] = "webp"; +})(GuildBannerFormat = exports.GuildBannerFormat || (exports.GuildBannerFormat = {})); +/** + * The allowed formats for a user's avatar + */ +var UserAvatarFormat; +(function (UserAvatarFormat) { + UserAvatarFormat["JPEG"] = "jpeg"; + UserAvatarFormat["JPG"] = "jpg"; + UserAvatarFormat["PNG"] = "png"; + UserAvatarFormat["WebP"] = "webp"; + UserAvatarFormat["GIF"] = "gif"; +})(UserAvatarFormat = exports.UserAvatarFormat || (exports.UserAvatarFormat = {})); +/** + * Generates and modifies avatar URLs according to the given arguments + */ +class Avatar { + /** + * Adds the given modifiers to an avatar URL + * @param {string} url The original avatar URL + * @param {AvatarFormat} format The required avatar image format + * @param {number} size The required avatar image size + * @returns {string} + */ + static enhanceURL(url, format, size) { + if (size && (size < 16 || size > 4096 || (size & (size - 1)) !== 0)) { + throw new Error('You provided an invalid image size! The size must be any power of two between 16 and 4096'); + } + const avatarURL = `${url}.${format}`; + return size ? `${avatarURL}?size=${size}` : avatarURL; + } + /** + * Returns a guild emoji's image + * @param {Snowflake} id The ID of the emoji + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + static emojiURL(id, format = GuildEmojiFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/emojis/${id}`, format, size); + } + /** + * Returns a guild's icon + * @param {string} hash The guild's icon hash + * @param {Snowflake} id The ID of the guild + * @param {GuildIconFormat} format The format of the returned icon image + * @param {number} size The size of the returned icon image + * @returns {string} + */ + static guildURL(hash, id, format = GuildIconFormat.PNG, size) { + if (format === GuildIconFormat.GIF && !hash.startsWith('a_')) { + throw new Error(`The icon of guild ${id} is not a GIF as you requested! Choose a different image format`); + } + return Avatar.enhanceURL(`${exports.cdnBaseURL}/icons/${id}/${hash}`, format, size); + } + /** + * Returns a guild's splash image URL + * @param {string} hash The splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildSplashFormat} format The format of the returned splash image + * @param {number} size The size of the returned splash image + * @returns {string} + */ + static splashURL(hash, id, format = GuildSplashFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/splashes/${id}/${hash}`, format, size); + } + /** + * Returns a guild's discovery splash image URL + * @param {string} hash The discovery splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildDiscoverySplashFormat} format The format of the returned discovery splash image + * @param {number} size The size of the returned discovery splash image + * @returns {string} + */ + static discoverySplashURL(hash, id, format = GuildDiscoverySplashFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/discovery-splashes/${id}/${hash}`, format, size); + } + /** + * Returns a guild's banner image URL + * @param {string} hash The banner hash + * @param {Snowflake} id The ID of the guild + * @param {GuildBannerFormat} format The format of the returned banner image + * @param {number} size The size of the returned banner image + * @returns {string} + */ + static bannerURL(hash, id, format = GuildBannerFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/banners/${id}/${hash}`, format, size); + } + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link Format.PNG}* + * @param {string | null} hash The user's avatar hash + * @param {Snowflake} id The user's ID + * @param {number} hashtag the user's hashtag + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + static userAvatarURL(hash, id, hashtag, format = UserAvatarFormat.PNG, size) { + if (format === UserAvatarFormat.GIF && hash && !hash.startsWith('a_')) { + throw new Error(`The avatar of user ${id} is not a GIF as you requested! Choose a different image format`); + } + return hash + ? Avatar.enhanceURL(`${exports.cdnBaseURL}/avatars/${id}/${hash}`, format, size) + : Avatar.enhanceURL(`${exports.cdnBaseURL}/embed/avatars/${parseInt(hashtag) % 5}`, format, size); + } +} +exports.Avatar = Avatar; diff --git a/lib/structures/BotPresence.d.ts b/lib/structures/BotPresence.d.ts new file mode 100644 index 000000000..bf5a8e873 --- /dev/null +++ b/lib/structures/BotPresence.d.ts @@ -0,0 +1,28 @@ +import { UpdateStatus } from './BotUser'; +import { Bot } from '../bot'; +/** + * Represents the bot's presence + */ +export declare class BotPresence { + /** + * The bot instance + */ + readonly bot: Bot; + /** + * The current bot presence + */ + presence: UpdateStatus | undefined; + constructor(bot: Bot); + /** + * Modifies the presence of the bot + * @param {UpdateStatus} presence The new bot presence + * @returns {void} + */ + modify(presence: UpdateStatus): void; + /** + * Serializes the bot presence into a gateway structure + * @param {UpdateStatus} presence The bot presence + * @returns {GatewayStruct} + */ + private static serialize; +} diff --git a/lib/structures/BotPresence.js b/lib/structures/BotPresence.js new file mode 100644 index 000000000..4591187a6 --- /dev/null +++ b/lib/structures/BotPresence.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotPresence = void 0; +/** + * Represents the bot's presence + */ +class BotPresence { + constructor(bot) { + this.bot = bot; + } + /** + * Modifies the presence of the bot + * @param {UpdateStatus} presence The new bot presence + * @returns {void} + */ + modify(presence) { + this.presence = presence; + return this.bot.connection.modifyPresence(BotPresence.serialize(presence)); + } + /** + * Serializes the bot presence into a gateway structure + * @param {UpdateStatus} presence The bot presence + * @returns {GatewayStruct} + */ + static serialize(presence) { + var _a; + return { + status: presence.status, + afk: presence.afk || false, + since: presence.since || null, + game: presence.game + ? { + name: presence.game.name, + type: presence.game.type, + url: presence.game.url, + created_at: presence.game.createdAt, + timestamps: presence.game.timestamps, + application_id: presence.game.applicationId, + details: presence.game.details, + state: presence.game.state, + emoji: presence.game.emoji, + party: presence.game.party, + assets: presence.game.assets && { + large_image: presence.game.assets.largeImage, + large_text: presence.game.assets.largeText, + small_image: presence.game.assets.smallImage, + small_text: presence.game.assets.smallText, + }, + secrets: presence.game.secrets, + instance: presence.game.instance, + flags: (_a = presence.game.flags) === null || _a === void 0 ? void 0 : _a.bits, + } + : null, + }; + } +} +exports.BotPresence = BotPresence; diff --git a/lib/structures/BotUser.d.ts b/lib/structures/BotUser.d.ts new file mode 100644 index 000000000..61cd37783 --- /dev/null +++ b/lib/structures/BotUser.d.ts @@ -0,0 +1,112 @@ +import { BotPresence } from './BotPresence'; +import { ImageURI } from './ImageURI'; +import { User } from './User'; +import { GatewayStruct } from './base'; +import { PermissionFlags } from './flags'; +import { PresenceGame, PresenceStatus } from './member'; +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * Options for when modifying the bot user + */ +export interface ModifyBotUserOptions { + /** + * The modified bot user's username. If changed may cause the user's discriminator to be randomized + */ + username?: string; + /** + * If passed, modifies the user's avatar. + * Path to the new user's avatar image + */ + avatar?: ImageURI | null; +} +/** + * Options for when fetching guilds using the bot's user + */ +export interface FetchGuildsOptions { + /** + * Fetch guilds before this guild ID + */ + before?: Snowflake; + /** + * Fetch guilds after this guild ID + */ + after?: Snowflake; + /** + * Max number of guilds to return (1-100) + * @default 100 + */ + limit?: number; +} +/** + * Partial guild object + */ +export interface PartialGuild { + /** + * The ID of the guild + */ + id: Snowflake; + /** + * The name of the guild + */ + name: string; + /** + * The icon hash of the guild + */ + icon: string; + /** + * Whether the bot user is the owner of the guild + */ + owner: boolean; + /** + * The permissions for the bot in this guild + */ + permissions: PermissionFlags; +} +/** + * Sent by the bot to indicate a presence or status update + */ +export interface UpdateStatus { + /** + * UNIX time (in milliseconds) of when the client went idle, or null if the client is not idle + */ + since?: number; + /** + * Null, or the user's new activity + */ + game?: Partial; + /** + * Whether or not the client is AFK + */ + afk?: boolean; + /** + * The user's new status + */ + status: PresenceStatus; +} +/** + * Represents the bot's user account + */ +export declare class BotUser extends User { + presence: BotPresence; + constructor(bot: Bot, user: GatewayStruct); + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modify(options: ModifyBotUserOptions): Promise; + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchGuilds(options?: FetchGuildsOptions): Promise>; + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId: Snowflake): Promise; +} diff --git a/lib/structures/BotUser.js b/lib/structures/BotUser.js new file mode 100644 index 000000000..e7380b8f8 --- /dev/null +++ b/lib/structures/BotUser.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotUser = void 0; +const BotPresence_1 = require("./BotPresence"); +const User_1 = require("./User"); +/** + * Represents the bot's user account + */ +class BotUser extends User_1.User { + constructor(bot, user) { + super(bot, user); + this.presence = new BotPresence_1.BotPresence(this.bot); + } + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyBotUser(options); + } + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchGuilds(options) { + return this.bot.api.fetchBotGuilds(options); + } + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId) { + return this.bot.api.leaveGuild(guildId); + } +} +exports.BotUser = BotUser; diff --git a/lib/structures/Emoji.d.ts b/lib/structures/Emoji.d.ts new file mode 100644 index 000000000..d2f480253 --- /dev/null +++ b/lib/structures/Emoji.d.ts @@ -0,0 +1,70 @@ +import { Role } from './Role'; +import { User } from './User'; +import { BaseStruct, GatewayStruct } from './base'; +import { Guild } from './guild'; +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * Any kind of emoji. Could be its unicode name, ID or {@link Emoji} object + */ +export declare type EmojiResolvable = string | Snowflake | Emoji; +export declare class Emoji extends BaseStruct { + /** + * The ID of the emoji. Possibly null if the emoji class was generated from a standard emoji + */ + emojiId: Snowflake | null; + /** + * The guild this emoji was created. Possibly undefined if this is a standard emoji + */ + guild: Guild | undefined; + /** + * The name of the emoji. Possibly null in reaction emoji objects + */ + name: string | null; + /** + * {@link Collection} of {@link Role}s this emoji is whitelisted to + */ + roles: Collection | undefined; + /** + * The user that created this emoji + */ + user: User | undefined; + /** + * Whether this emoji must be wrapped in colons + */ + requiresColons: boolean | undefined; + /** + * Whether this emoji is managed + */ + managed: boolean | undefined; + /** + * Whether this emoji is animated + */ + animated: boolean | undefined; + /** + * Whether this emoji can be used, may be false due to loss of Server Boosts + */ + available: boolean | undefined; + constructor(bot: Bot, emoji: GatewayStruct, guild?: Guild); + /** + * @ignore + * @param {GatewayStruct} emoji The emoji data + * @returns {this} + */ + init(emoji: GatewayStruct): this; + /** + * Returns this emoji's identifier. + * An emoji identifier could be its name for built-in emojis, or a combination of its name and ID if it's a guild emoji. + * @returns {string} + */ + get id(): string; + /** + * Finds the identifier of the given emoji. + * The emoji can be a Guild emoji, meaning we would have to search for it in the Bot's cached emojis Collection + * @param {Collection} emojis An emojis cache to search for the emoji in + * @param {EmojiResolvable} emoji The emoji to resolve + * @returns {string | null} + */ + static resolve(emojis: Collection, emoji: EmojiResolvable): string | null; +} diff --git a/lib/structures/Emoji.js b/lib/structures/Emoji.js new file mode 100644 index 000000000..47b139037 --- /dev/null +++ b/lib/structures/Emoji.js @@ -0,0 +1,57 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Emoji = void 0; +const User_1 = require("./User"); +const base_1 = require("./base"); +const Collection_1 = __importDefault(require("../Collection")); +class Emoji extends base_1.BaseStruct { + constructor(bot, emoji, guild) { + super(bot, emoji); + this.guild = guild; + this.init(emoji); + } + /** + * @ignore + * @param {GatewayStruct} emoji The emoji data + * @returns {this} + */ + init(emoji) { + var _a; + this.emojiId = emoji.id; + this.name = emoji.name; + this.roles = new Collection_1.default((_a = this.guild) === null || _a === void 0 ? void 0 : _a.roles.cache.filter((_r, id) => emoji.roles.includes(id))); + if (emoji.user) { + this.user = new User_1.User(this.bot, emoji.user); + } + this.requiresColons = emoji.require_colons; + this.managed = emoji.managed; + this.animated = emoji.animated; + this.available = emoji.available; + return this; + } + /** + * Returns this emoji's identifier. + * An emoji identifier could be its name for built-in emojis, or a combination of its name and ID if it's a guild emoji. + * @returns {string} + */ + get id() { + if (this.emojiId) + return `${this.animated ? 'a:' : ''}${this.name}:${this.emojiId}`; + return this.name; + } + /** + * Finds the identifier of the given emoji. + * The emoji can be a Guild emoji, meaning we would have to search for it in the Bot's cached emojis Collection + * @param {Collection} emojis An emojis cache to search for the emoji in + * @param {EmojiResolvable} emoji The emoji to resolve + * @returns {string | null} + */ + static resolve(emojis, emoji) { + var _a; + return emoji instanceof Emoji ? emoji.id : ((_a = emojis.get(emoji)) === null || _a === void 0 ? void 0 : _a.id) || emoji; + } +} +exports.Emoji = Emoji; diff --git a/lib/structures/ImageURI.d.ts b/lib/structures/ImageURI.d.ts new file mode 100644 index 000000000..45027cd54 --- /dev/null +++ b/lib/structures/ImageURI.d.ts @@ -0,0 +1,32 @@ +/** + * Represents an image being sent to the Discord API as Image Data + * https://discord.com/developers/docs/reference#image-data + */ +export declare class ImageURI { + /** + * The path of the image + */ + private readonly path; + /** + * @param {string} path The image path + * @example ```typescript + * const image = new ImageURI('./image.png'); + * ``` + */ + constructor(path: string); + /** + * Returns the image mime and base64 data as a formatted string + * @returns {string} + */ + stringify(): Promise; + /** + * Returns the image as base64 + * @type {string} + */ + private get image(); + /** + * Returns the mime type of the image + * @type {string | boolean} + */ + private get mime(); +} diff --git a/lib/structures/ImageURI.js b/lib/structures/ImageURI.js new file mode 100644 index 000000000..09747726f --- /dev/null +++ b/lib/structures/ImageURI.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImageURI = void 0; +const fs_1 = __importDefault(require("fs")); +const util_1 = __importDefault(require("util")); +const mime_types_1 = __importDefault(require("mime-types")); +const readFile = util_1.default.promisify(fs_1.default.readFile); +/** + * Represents an image being sent to the Discord API as Image Data + * https://discord.com/developers/docs/reference#image-data + */ +class ImageURI { + /** + * @param {string} path The image path + * @example ```typescript + * const image = new ImageURI('./image.png'); + * ``` + */ + constructor(path) { + this.path = path; + } + /** + * Returns the image mime and base64 data as a formatted string + * @returns {string} + */ + stringify() { + return __awaiter(this, void 0, void 0, function* () { + const { image, mime } = this; + if (!mime) { + throw new Error(`Invalid mime type for image ${this.path}`); + } + return `data:${mime};base64,${yield image}`; + }); + } + /** + * Returns the image as base64 + * @type {string} + */ + get image() { + return readFile(this.path, { encoding: 'base64' }); + } + /** + * Returns the mime type of the image + * @type {string | boolean} + */ + get mime() { + return mime_types_1.default.lookup(this.path); + } +} +exports.ImageURI = ImageURI; diff --git a/lib/structures/Invite.d.ts b/lib/structures/Invite.d.ts new file mode 100644 index 000000000..04b4d16b3 --- /dev/null +++ b/lib/structures/Invite.d.ts @@ -0,0 +1,100 @@ +import { Timestamp } from './Timestamp'; +import { User } from './User'; +import { BaseStruct, GatewayStruct } from './base'; +import { GuildChannel } from './channels'; +import { Guild } from './guild'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +export declare type InviteCode = string; +/** + * Returned from the {@link INVITE_DELETE} event when the invite has not been cached + */ +export interface PartialInvite { + /** + * The ID of the channel this invite is for + */ + channelId: Snowflake; + /** + * The guild this invite is for + */ + guild: Guild | undefined; + /** + * The invite code (unique ID) + */ + code: InviteCode; +} +export interface InviteMax { + /** + * Duration (in seconds) after which the invite expires + */ + age: number; + /** + * Maximum number of times this invite can be used + */ + uses: number; +} +/** + * Options used when creating new invites for channels + */ +export interface InviteOptions { + /** + * The maximum data for the new invite + */ + max?: Partial; + /** + * Whether this invite only grants temporary membership + */ + temporary?: boolean; + /** + * If true, don't try to reuse a similar invite (useful for creating many unique one time use invites) + */ + unique?: boolean; +} +export declare class Invite extends BaseStruct { + /** + * The channel this invite is for + */ + channel: GuildChannel | undefined; + /** + * The invite code (unique ID) + */ + code: InviteCode; + /** + * The timestamp of when the invite was created + */ + createdAt: Timestamp; + /** + * The guild this invite is for + */ + guild: Guild | undefined; + /** + * The user who created the invite + */ + inviter: User | undefined; + /** + * {@link InviteMax} object containing the invite's maximum age and maximum uses + */ + max: InviteMax; + /** + * Whether this invite grants temporary membership + */ + temporary: boolean; + /** + * Number of times this invite has been used + */ + uses: number; + constructor(bot: Bot, invite: GatewayStruct, guild?: Guild); + /** + * @ignore + * @param {GatewayStruct} invite The invite data + * @param {Guild} guild The guild this invite is for + * @returns {this} + */ + init(invite: GatewayStruct, guild?: Guild): this; + /** + * The code this invite stores. + * Servers as an identifier for this invite + * @type {string} + */ + get id(): string; +} diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js new file mode 100644 index 000000000..9a61f086b --- /dev/null +++ b/lib/structures/Invite.js @@ -0,0 +1,50 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Invite = void 0; +const Timestamp_1 = require("./Timestamp"); +const User_1 = require("./User"); +const base_1 = require("./base"); +class Invite extends base_1.BaseStruct { + constructor(bot, invite, guild) { + super(bot, invite); + this.init(invite, guild); + } + /** + * @ignore + * @param {GatewayStruct} invite The invite data + * @param {Guild} guild The guild this invite is for + * @returns {this} + */ + init(invite, guild) { + this.code = invite.code; + this.createdAt = new Timestamp_1.Timestamp(invite.created_at); + if (guild) { + this.guild = guild; + } + else if (invite.guild) { + this.guild = this.bot.guilds.cache.get(invite.guild.id); + } + if (this.guild && invite.channel) { + this.channel = this.guild.channels.cache.get(invite.channel.id); + } + if (invite.inviter) { + this.inviter = new User_1.User(this.bot, invite.inviter); + } + this.max = { + age: invite.max_age, + uses: invite.max_uses, + }; + this.temporary = invite.temporary; + this.uses = invite.uses; + return this; + } + /** + * The code this invite stores. + * Servers as an identifier for this invite + * @type {string} + */ + get id() { + return this.code; + } +} +exports.Invite = Invite; diff --git a/lib/structures/PermissionOverwrite.d.ts b/lib/structures/PermissionOverwrite.d.ts new file mode 100644 index 000000000..28d550549 --- /dev/null +++ b/lib/structures/PermissionOverwrite.d.ts @@ -0,0 +1,35 @@ +import { BaseStruct, GatewayStruct } from './base'; +import { GuildChannel } from './channels'; +import { Permissible, PermissionOverwriteFlags } from './flags'; +import { Bot } from '../bot'; +/** + * A full permission overwrite entry. + * Contains full data about a guild channel's permission overwrite for a user or a role + */ +export declare class PermissionOverwrite extends BaseStruct { + /** + * The channel this overwrite is associated to + */ + readonly channel: GuildChannel; + /** + * The permission flags of this permission overwrite + */ + flags: Required; + /** + * The user or role this permission overwrite is for + */ + permissible: Permissible; + constructor(bot: Bot, permission: GatewayStruct, channel: GuildChannel); + /** + * @ignore + * @param {GatewayStruct} permission The permission data + * @returns {this} + */ + init(permission: GatewayStruct): this; + /** + * The permissible's ID. + * Servers as an identifier for this permission overwrite + * @type {string} + */ + get id(): string; +} diff --git a/lib/structures/PermissionOverwrite.js b/lib/structures/PermissionOverwrite.js new file mode 100644 index 000000000..5cdc930f2 --- /dev/null +++ b/lib/structures/PermissionOverwrite.js @@ -0,0 +1,41 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionOverwrite = void 0; +const base_1 = require("./base"); +const flags_1 = require("./flags"); +/** + * A full permission overwrite entry. + * Contains full data about a guild channel's permission overwrite for a user or a role + */ +class PermissionOverwrite extends base_1.BaseStruct { + constructor(bot, permission, channel) { + super(bot, permission); + this.channel = channel; + this.init(permission); + } + /** + * @ignore + * @param {GatewayStruct} permission The permission data + * @returns {this} + */ + init(permission) { + this.flags = { + allow: new flags_1.PermissionFlags(permission.allow_new), + deny: new flags_1.PermissionFlags(permission.deny_new), + }; + this.permissible = { + id: permission.id, + type: permission.type, + }; + return this; + } + /** + * The permissible's ID. + * Servers as an identifier for this permission overwrite + * @type {string} + */ + get id() { + return this.permissible.id; + } +} +exports.PermissionOverwrite = PermissionOverwrite; diff --git a/lib/structures/Role.d.ts b/lib/structures/Role.d.ts new file mode 100644 index 000000000..d33fa136b --- /dev/null +++ b/lib/structures/Role.d.ts @@ -0,0 +1,89 @@ +import { GatewayStruct, BaseGuildStruct } from './base'; +import { PermissionFlags } from './flags'; +import { Guild } from './guild'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +export interface RoleOptions { + /** + * The name of the created role + * @default "new role" + */ + name?: string; + /** + * The enabled permission flags for the created role + * @default Permissions for the @everyone role + */ + permissions?: PermissionFlags; + /** + * The hex color for the created role + * @default 0 + * @example 0x406fff // creates a blue role + */ + color?: number; + /** + * Whether the created role should be displayed separately in the sidebar + * @default false + */ + listedSeparately?: boolean; + /** + * Whether the created role should be mentionable + * @default false + */ + mentionable?: boolean; +} +/** + * Represents a role in a guild + */ +export declare class Role extends BaseGuildStruct { + /** + * The role's ID + */ + id: Snowflake; + /** + * The role name + */ + name: string; + /** + * Number representation of hexadecimal color code + */ + color: number; + /** + * Whether this role is listed separately on the guild members list + */ + listedSeparately: boolean; + /** + * The position of this role + */ + position: number; + /** + * The permissions of the role + */ + permissions: PermissionFlags; + /** + * Whether this role is managed by an integration + */ + managed: boolean; + /** + * Whether this role is mentionable + */ + mentionable: boolean; + constructor(bot: Bot, role: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} role The role data + * @returns {this} + */ + init(role: GatewayStruct): this; + /** + * Modifies this role. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions} options The options for the modified role + * @returns {Promise} The updated role + */ + modify(options: RoleOptions): Promise; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/Role.js b/lib/structures/Role.js new file mode 100644 index 000000000..fb0176045 --- /dev/null +++ b/lib/structures/Role.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Role = void 0; +const base_1 = require("./base"); +const flags_1 = require("./flags"); +/** + * Represents a role in a guild + */ +class Role extends base_1.BaseGuildStruct { + constructor(bot, role, guild) { + super(bot, guild, role); + this.init(role); + } + /** + * @ignore + * @param {GatewayStruct} role The role data + * @returns {this} + */ + init(role) { + this.id = role.id; + this.name = role.name; + this.color = role.color; + this.listedSeparately = role.hoist; + this.position = role.position; + this.permissions = new flags_1.PermissionFlags(role.permissions_new); + this.managed = role.managed; + this.mentionable = role.mentionable; + return this; + } + /** + * Modifies this role. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions} options The options for the modified role + * @returns {Promise} The updated role + */ + modify(options) { + return this.bot.api.modifyRole(this.guild.id, this.id, options); + } + /** + * @ignore + * @returns {string} + */ + toString() { + return `<@&${this.id}>`; + } +} +exports.Role = Role; diff --git a/lib/structures/Timestamp.d.ts b/lib/structures/Timestamp.d.ts new file mode 100644 index 000000000..cdc22d6e0 --- /dev/null +++ b/lib/structures/Timestamp.d.ts @@ -0,0 +1,20 @@ +/** + * Handles the conversion of timestamps received from the Discord API into UNIX timestamps + */ +export declare class Timestamp { + /** + * The ISO date of this timestamp + */ + date: string | undefined; + constructor(date: string | number | Date | undefined); + /** + * Returns the UNIX timestamp of this timestamp + * @returns {number | undefined} + */ + unix(): number | undefined; + /** + * Returns the ISO date of this timestamp + * @returns {string | undefined} + */ + get iso(): string | undefined; +} diff --git a/lib/structures/Timestamp.js b/lib/structures/Timestamp.js new file mode 100644 index 000000000..d10827d66 --- /dev/null +++ b/lib/structures/Timestamp.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Timestamp = void 0; +/** + * Handles the conversion of timestamps received from the Discord API into UNIX timestamps + */ +class Timestamp { + constructor(date) { + if (typeof date === 'number') { + this.date = new Date(date).toISOString(); + } + else if (date instanceof Date) { + this.date = date.toISOString(); + } + else { + this.date = date; + } + } + /** + * Returns the UNIX timestamp of this timestamp + * @returns {number | undefined} + */ + unix() { + return this.date ? Date.parse(this.date) : undefined; + } + /** + * Returns the ISO date of this timestamp + * @returns {string | undefined} + */ + get iso() { + return this.date ? new Date(this.date).toISOString() : undefined; + } +} +exports.Timestamp = Timestamp; diff --git a/lib/structures/User.d.ts b/lib/structures/User.d.ts new file mode 100644 index 000000000..a9e56b098 --- /dev/null +++ b/lib/structures/User.d.ts @@ -0,0 +1,113 @@ +import { UserAvatarFormat } from './Avatar'; +import { BaseStruct, GatewayStruct } from './base'; +import { DMChannel, TextChannel } from './channels'; +import { UserFlags } from './flags'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * User nitro types + */ +export declare enum NitroType { + None = 0, + NitroClassic = 1, + Nitro = 2 +} +/** + * Represents a user in the Discord platform + * @extends BaseStruct + */ +export declare class User extends BaseStruct { + /** + * The user's ID + */ + id: Snowflake; + /** + * User's username, not unique across the platform + */ + username: string; + /** + * User's 4-digit discord-tag + */ + hashtag: string; + /** + * User's avatar image. Possibly null if user does not have an avatar image + */ + avatarHash: string | null; + /** + * Whether the user is a bot + */ + isBot: boolean | undefined; + /** + * Whether the user is an Official Discord System user (part of urgent message system) + */ + system: boolean | undefined; + /** + * Whether the user has two factor enabled on their account + */ + mfaEnabled: boolean | undefined; + /** + * The user's chosen language option + */ + locale: string; + /** + * Whether the email on this user has been verified + */ + verified: boolean | undefined; + /** + * The user's email + */ + email: string | null | undefined; + /** + * {@link NitroType} object containing the type of nitro subscription on a user's account + */ + nitroType: NitroType | undefined; + /** + * The flags on a user's account + */ + flags: UserFlags | undefined; + /** + * The public flags on a user's account + */ + publicFlags: UserFlags | undefined; + /** + * This user's DM channel with the bot, if cached + */ + dm: DMChannel | undefined; + constructor(bot: Bot, user: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} user The user data + * @returns {this} + */ + init(user: GatewayStruct): this; + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link UserAvatarFormat.PNG}* + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + avatarURL(format?: UserAvatarFormat, size?: number): string; + /** + * Creates a new DM channel between this user and the bot user + * @returns {Promise} + */ + createDM(): Promise; + /** + * Sends a DM message to the user from the bot user + * @param {any} args Identical to the arguments of {@link TextChannel.sendMessage} + * @returns {any} Identical to the return type of {@link TextChannel.sendMessage} + */ + sendMessage(...args: Parameters): ReturnType; + /** + * Combines a user's username and hashtag and generates a full name + * @type {string} + * @example Day#0001 + */ + get fullName(): string; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/User.js b/lib/structures/User.js new file mode 100644 index 000000000..08f15aff0 --- /dev/null +++ b/lib/structures/User.js @@ -0,0 +1,104 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.User = exports.NitroType = void 0; +const Avatar_1 = require("./Avatar"); +const base_1 = require("./base"); +const flags_1 = require("./flags"); +/** + * User nitro types + */ +var NitroType; +(function (NitroType) { + NitroType[NitroType["None"] = 0] = "None"; + NitroType[NitroType["NitroClassic"] = 1] = "NitroClassic"; + NitroType[NitroType["Nitro"] = 2] = "Nitro"; +})(NitroType = exports.NitroType || (exports.NitroType = {})); +// TODO: replace "new User()" with cache get or create +/** + * Represents a user in the Discord platform + * @extends BaseStruct + */ +class User extends base_1.BaseStruct { + constructor(bot, user) { + super(bot, user); + this.init(user); + } + /** + * @ignore + * @param {GatewayStruct} user The user data + * @returns {this} + */ + init(user) { + this.id = user.id; + this.username = user.username; + this.hashtag = user.discriminator; + this.avatarHash = user.avatar; + this.isBot = user.bot; + this.system = user.system; + this.mfaEnabled = user.mfa_enabled; + this.locale = user.locale; + this.verified = user.verified; + this.email = user.email; + this.nitroType = user.premium_type; + if (user.flags) { + this.flags = new flags_1.UserFlags(user.flags); + } + if (user.public_flags) { + this.flags = new flags_1.UserFlags(user.public_flags); + } + return this; + } + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link UserAvatarFormat.PNG}* + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + avatarURL(format = Avatar_1.UserAvatarFormat.PNG, size) { + return Avatar_1.Avatar.userAvatarURL(this.avatarHash, this.id, this.hashtag, format, size); + } + /** + * Creates a new DM channel between this user and the bot user + * @returns {Promise} + */ + createDM() { + return this.bot.api.createDM(this.id); + } + /** + * Sends a DM message to the user from the bot user + * @param {any} args Identical to the arguments of {@link TextChannel.sendMessage} + * @returns {any} Identical to the return type of {@link TextChannel.sendMessage} + */ + sendMessage(...args) { + return __awaiter(this, void 0, void 0, function* () { + const dm = this.dm || (yield this.createDM()); + return dm.sendMessage(...args); + }); + } + /** + * Combines a user's username and hashtag and generates a full name + * @type {string} + * @example Day#0001 + */ + get fullName() { + return `${this.username}#${this.hashtag}`; + } + /** + * @ignore + * @returns {string} + */ + toString() { + return `<@${this.id}>`; + } +} +exports.User = User; diff --git a/lib/structures/Webhook.d.ts b/lib/structures/Webhook.d.ts new file mode 100644 index 000000000..82dbd8cef --- /dev/null +++ b/lib/structures/Webhook.d.ts @@ -0,0 +1,89 @@ +import { ImageURI } from './ImageURI'; +import { User } from './User'; +import { BaseGuildStruct, GatewayStruct } from './base'; +import { GuildChannel } from './channels'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * Options used for creating webhooks + */ +export interface CreateWebhookOptions { + /** + * The name of the webhook (1-80 characters) + */ + name: string; + /** + * The image for the default webhook avatar + */ + avatar?: ImageURI; +} +/** + * Options used for modifying webhooks + */ +export interface ModifyWebhookOptions { + /** + * The modified default name of the webhook + */ + name?: string; + /** + * The modified image for the default webhook avatar + */ + avatar?: ImageURI | null; + /** + * The modified channel ID this webhook should be moved to + */ + channelId?: Snowflake; +} +/** + * The type of a webhook + */ +export declare enum WebhookType { + Incoming = 1, + ChannelFollower = 2 +} +/** + * Webhooks are a low-effort way to post messages to channels in Discord + */ +export declare class Webhook extends BaseGuildStruct { + /** + * The ID of the webhook + */ + id: Snowflake; + /** + * The type of the webhook + */ + type: WebhookType; + /** + * The channel this webhook is for + */ + readonly channel: GuildChannel; + /** + * The user this webhook was created by + */ + user: User; + /** + * The default name of the webhook + */ + name: string | null; + /** + * The default avatar hash of the webhook + */ + avatarHash: string | null; + /** + * The secure token of the webhook. Returned for incoming webhooks + */ + token: string | undefined; + constructor(bot: Bot, webhook: GatewayStruct, channel: GuildChannel); + /** + * @param {GatewayStruct} webhook The webhook object + * @returns {this} + * @ignore + */ + init(webhook: GatewayStruct): this; + /** + * Modifies a webhook by its ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modify(options: ModifyWebhookOptions): Promise; +} diff --git a/lib/structures/Webhook.js b/lib/structures/Webhook.js new file mode 100644 index 000000000..7c94bebf7 --- /dev/null +++ b/lib/structures/Webhook.js @@ -0,0 +1,59 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Webhook = exports.WebhookType = void 0; +const User_1 = require("./User"); +const base_1 = require("./base"); +/** + * The type of a webhook + */ +var WebhookType; +(function (WebhookType) { + WebhookType[WebhookType["Incoming"] = 1] = "Incoming"; + WebhookType[WebhookType["ChannelFollower"] = 2] = "ChannelFollower"; +})(WebhookType = exports.WebhookType || (exports.WebhookType = {})); +/** + * Webhooks are a low-effort way to post messages to channels in Discord + */ +class Webhook extends base_1.BaseGuildStruct { + constructor(bot, webhook, channel) { + super(bot, channel.guild, webhook); + this.channel = channel; + this.init(webhook); + } + /** + * @param {GatewayStruct} webhook The webhook object + * @returns {this} + * @ignore + */ + init(webhook) { + this.id = webhook.id; + this.type = webhook.type; + this.user = new User_1.User(this.bot, webhook.user); + this.name = webhook.name; + this.avatarHash = webhook.avatar; + this.token = webhook.token; + return this; + } + /** + * Modifies a webhook by its ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modify(options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.bot.api.modifyWebhook(this.id, options); + this.update(webhook); + return webhook; + }); + } +} +exports.Webhook = Webhook; diff --git a/lib/structures/base/BaseGuildStruct.d.ts b/lib/structures/base/BaseGuildStruct.d.ts new file mode 100644 index 000000000..f17c9ff45 --- /dev/null +++ b/lib/structures/base/BaseGuildStruct.d.ts @@ -0,0 +1,14 @@ +import { BaseStruct, GatewayStruct } from './BaseStruct'; +import { Bot } from '../../bot'; +import { Guild } from '../guild'; +/** + * Basic structure every guild-related structure extends + * Handles the creation of a guild property and guild-related methods + */ +export declare class BaseGuildStruct extends BaseStruct { + /** + * The {@link Guild} associated to this structure + */ + guild: Guild; + constructor(bot: Bot, guild: Guild, structure: GatewayStruct); +} diff --git a/lib/structures/base/BaseGuildStruct.js b/lib/structures/base/BaseGuildStruct.js new file mode 100644 index 000000000..ff514e020 --- /dev/null +++ b/lib/structures/base/BaseGuildStruct.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseGuildStruct = void 0; +const BaseStruct_1 = require("./BaseStruct"); +/** + * Basic structure every guild-related structure extends + * Handles the creation of a guild property and guild-related methods + */ +class BaseGuildStruct extends BaseStruct_1.BaseStruct { + constructor(bot, guild, structure) { + super(bot, structure); + this.guild = guild; + } +} +exports.BaseGuildStruct = BaseGuildStruct; diff --git a/lib/structures/base/BaseStruct.d.ts b/lib/structures/base/BaseStruct.d.ts new file mode 100644 index 000000000..5020302ce --- /dev/null +++ b/lib/structures/base/BaseStruct.d.ts @@ -0,0 +1,50 @@ +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +/** + * Payload data received from the Discord gateway + */ +export declare type GatewayStruct = Record; +interface UpdateReturn { + before: T; + after: T; +} +/** + * A base structure with the ID field + */ +export declare type BaseStructWithId = BaseStruct & { + id: Snowflake | string; +}; +/** + * Basic structure that all other API-related structures extend + * Includes the bot property which every structure must have + */ +export declare class BaseStruct { + /** + * The {@link Bot} operating this structure + */ + bot: Bot; + /** + * The gateway structure that initialized this instance + * @ignore + */ + readonly structure: GatewayStruct; + constructor(bot: Bot, structure: GatewayStruct); + /** + * Virtual init method + * @param {GatewayStruct} _struct The structure to initialize from + * @returns {this} + */ + init(_struct: GatewayStruct): this; + /** + * Clone a structure + * @returns {this} + */ + clone(): this; + /** + * Update a structure and return its before and after versions + * @param {GatewayStruct} data The updated data + * @returns {UpdateReturn} + */ + update(data: GatewayStruct): UpdateReturn; +} +export {}; diff --git a/lib/structures/base/BaseStruct.js b/lib/structures/base/BaseStruct.js new file mode 100644 index 000000000..4f5151bf6 --- /dev/null +++ b/lib/structures/base/BaseStruct.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseStruct = void 0; +/** + * Basic structure that all other API-related structures extend + * Includes the bot property which every structure must have + */ +class BaseStruct { + constructor(bot, structure) { + this.bot = bot; + this.structure = structure; + } + /** + * Virtual init method + * @param {GatewayStruct} _struct The structure to initialize from + * @returns {this} + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + init(_struct) { + return this; + } + /** + * Clone a structure + * @returns {this} + */ + clone() { + return Object.assign(Object.create(this), this); + } + /** + * Update a structure and return its before and after versions + * @param {GatewayStruct} data The updated data + * @returns {UpdateReturn} + */ + update(data) { + const clone = this.clone(); + return { before: clone, after: this.init(Object.assign(Object.assign({}, this.structure), data)) }; + } +} +exports.BaseStruct = BaseStruct; diff --git a/lib/structures/base/index.d.ts b/lib/structures/base/index.d.ts new file mode 100644 index 000000000..2a38e4863 --- /dev/null +++ b/lib/structures/base/index.d.ts @@ -0,0 +1,2 @@ +export * from './BaseStruct'; +export * from './BaseGuildStruct'; diff --git a/lib/structures/base/index.js b/lib/structures/base/index.js new file mode 100644 index 000000000..bd6a0fae8 --- /dev/null +++ b/lib/structures/base/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BaseStruct"), exports); +__exportStar(require("./BaseGuildStruct"), exports); diff --git a/lib/structures/channels/Channel.d.ts b/lib/structures/channels/Channel.d.ts new file mode 100644 index 000000000..772e0a910 --- /dev/null +++ b/lib/structures/channels/Channel.d.ts @@ -0,0 +1,45 @@ +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * The type of a channel + */ +export declare enum ChannelType { + GuildText = 0, + DM = 1, + GuildVoice = 2, + GroupDM = 3, + GuildCategory = 4, + GuildNews = 5, + GuildStore = 6 +} +/** + * Represents a guild or DM channel within Discord. + */ +export declare class Channel extends BaseStruct { + /** + * The ID of this channel + */ + id: Snowflake; + /** + * The type of this channel + */ + type: ChannelType; + constructor(bot: Bot, channel: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} channel The channel data + * @returns {this} + */ + init(channel: GatewayStruct): this; + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. Requires the {@link Permission.ManageChannels} permission for the guild + * @returns {Promise} + */ + delete(): Promise; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/channels/Channel.js b/lib/structures/channels/Channel.js new file mode 100644 index 000000000..9e34a9958 --- /dev/null +++ b/lib/structures/channels/Channel.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Channel = exports.ChannelType = void 0; +const base_1 = require("../base"); +/** + * The type of a channel + */ +var ChannelType; +(function (ChannelType) { + ChannelType[ChannelType["GuildText"] = 0] = "GuildText"; + ChannelType[ChannelType["DM"] = 1] = "DM"; + ChannelType[ChannelType["GuildVoice"] = 2] = "GuildVoice"; + ChannelType[ChannelType["GroupDM"] = 3] = "GroupDM"; + ChannelType[ChannelType["GuildCategory"] = 4] = "GuildCategory"; + ChannelType[ChannelType["GuildNews"] = 5] = "GuildNews"; + ChannelType[ChannelType["GuildStore"] = 6] = "GuildStore"; +})(ChannelType = exports.ChannelType || (exports.ChannelType = {})); +/** + * Represents a guild or DM channel within Discord. + */ +class Channel extends base_1.BaseStruct { + constructor(bot, channel) { + super(bot, channel); + this.init(channel); + } + /** + * @ignore + * @param {GatewayStruct} channel The channel data + * @returns {this} + */ + init(channel) { + this.id = channel.id; + this.type = channel.type; + return this; + } + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. Requires the {@link Permission.ManageChannels} permission for the guild + * @returns {Promise} + */ + delete() { + return this.bot.api.deleteChannel(this.id); + } + /** + * @ignore + * @returns {string} + */ + toString() { + return `<#${this.id}>`; + } +} +exports.Channel = Channel; diff --git a/lib/structures/channels/DMChannel.d.ts b/lib/structures/channels/DMChannel.d.ts new file mode 100644 index 000000000..39a28caa5 --- /dev/null +++ b/lib/structures/channels/DMChannel.d.ts @@ -0,0 +1,34 @@ +import { Channel } from './Channel'; +import { TextChannel } from './TextChannel'; +import { Bot } from '../../bot'; +import { ChannelMessagesController, ChannelPinsController } from '../../controllers/channel'; +import { Snowflake } from '../../types'; +import { User } from '../User'; +import { GatewayStruct } from '../base'; +import { Message, MessageData, MessageOptions, MessageEmbed } from '../message'; +/** + * Represents a private channel between the Bot and a User + */ +export declare class DMChannel extends Channel implements TextChannel { + /** @inheritDoc */ + lastMessageId: Snowflake | null | undefined; + /** @inheritDoc */ + messages: ChannelMessagesController; + /** @inheritDoc */ + pins: ChannelPinsController; + /** + * The recipient of the DM + */ + recipient: User; + constructor(bot: Bot, dmChannel: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} dmChannel The DM channel data + * @returns {this} + */ + init(dmChannel: GatewayStruct): this; + /** @inheritDoc */ + sendMessage(data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** @inheritDoc */ + triggerTyping(): Promise; +} diff --git a/lib/structures/channels/DMChannel.js b/lib/structures/channels/DMChannel.js new file mode 100644 index 000000000..4c3da06b5 --- /dev/null +++ b/lib/structures/channels/DMChannel.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DMChannel = void 0; +const Channel_1 = require("./Channel"); +const channel_1 = require("../../controllers/channel"); +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +/** + * Represents a private channel between the Bot and a User + */ +class DMChannel extends Channel_1.Channel { + constructor(bot, dmChannel) { + super(bot, dmChannel); + this.messages = new channel_1.ChannelMessagesController(this); + } + /** + * @ignore + * @param {GatewayStruct} dmChannel The DM channel data + * @returns {this} + */ + init(dmChannel) { + super.init(dmChannel); + this.pins = new channel_1.ChannelPinsController(this); + this.lastMessageId = dmChannel.last_message_id; + this.pins.lastPinTimestamp = new Timestamp_1.Timestamp(dmChannel.last_pin_timestamp); + this.recipient = new User_1.User(this.bot, dmChannel.recipients[0]); + return this; + } + /** @inheritDoc */ + sendMessage(data, options) { + return this.bot.api.sendMessage(this.id, data, options); + } + /** @inheritDoc */ + triggerTyping() { + return this.bot.api.triggerTextChannelTyping(this.id); + } +} +exports.DMChannel = DMChannel; diff --git a/lib/structures/channels/GuildCategoryChannel.d.ts b/lib/structures/channels/GuildCategoryChannel.d.ts new file mode 100644 index 000000000..9259dcdfd --- /dev/null +++ b/lib/structures/channels/GuildCategoryChannel.d.ts @@ -0,0 +1,13 @@ +import { GuildChannel } from './GuildChannel'; +import Collection from '../../Collection'; +import { Snowflake } from '../../types'; +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildCategory} + */ +export declare class GuildCategoryChannel extends GuildChannel { + /** + * Returns all {@link GuildChannel}s under this category channel + * @type {Collection} + */ + get children(): Collection; +} diff --git a/lib/structures/channels/GuildCategoryChannel.js b/lib/structures/channels/GuildCategoryChannel.js new file mode 100644 index 000000000..724cae2b0 --- /dev/null +++ b/lib/structures/channels/GuildCategoryChannel.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildCategoryChannel = void 0; +const GuildChannel_1 = require("./GuildChannel"); +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildCategory} + */ +class GuildCategoryChannel extends GuildChannel_1.GuildChannel { + /** + * Returns all {@link GuildChannel}s under this category channel + * @type {Collection} + */ + get children() { + return this.guild.channels.cache.filter(c => { var _a; return ((_a = c.parent) === null || _a === void 0 ? void 0 : _a.id) === this.id; }); + } +} +exports.GuildCategoryChannel = GuildCategoryChannel; diff --git a/lib/structures/channels/GuildChannel.d.ts b/lib/structures/channels/GuildChannel.d.ts new file mode 100644 index 000000000..0a6542b6e --- /dev/null +++ b/lib/structures/channels/GuildChannel.d.ts @@ -0,0 +1,123 @@ +import { Channel, ChannelType } from './Channel'; +import { GuildCategoryChannel } from './GuildCategoryChannel'; +import { Bot } from '../../bot'; +import { ChannelPermissionsController } from '../../controllers/channel'; +import { GuildChannelInvitesController, GuildChannelWebhooksController } from '../../controllers/guild'; +import { Snowflake } from '../../types'; +import { GatewayStruct } from '../base'; +import { PermissibleType, PermissionOverwriteFlags } from '../flags'; +import { Guild } from '../guild'; +/** + * Options used when modifying a {@link GuildChannel} + */ +export interface GuildChannelOptions { + /** + * The new guild channel's name + */ + name?: string; + /** + * The new type of the guild channel. + * Can only change between {@link ChannelType.GuildText} and {@link ChannelType.GuildNews} + */ + type?: ChannelType.GuildText | ChannelType.GuildNews; + /** + * The new topic of the guild channel + */ + topic?: string | null; + /** + * Whether the guild channel should be marked as nsfw + */ + nsfw?: boolean | null; + /** + * The guild channel's slow mode timeout (for {@link GuildTextChannel}) + */ + slowModeTimeout?: number | null; + /** + * The guild channel's audio bit rate (for {@link GuildVoiceChannel}) + */ + bitrate?: number | null; + /** + * The guild channel's user limit (for {@link GuildVoiceChannel}) + */ + userLimit?: number | null; +} +/** + * Options for when creating new guild channels + */ +export interface CreateGuildChannelOptions extends GuildChannelOptions { + /** + * The name of the new channel + */ + name: string; + /** + * The position of the new channel + */ + position?: number; + /** + * The permissions of the new channel. + * Object of user / roles IDs and their permissions for the new channel + */ + permissions?: Record; + /** + * The ID of the parent category for the channel + */ + parentId?: Snowflake; +} +/** + * Represents a channel found in a guild of any type + */ +export declare class GuildChannel extends Channel { + /** + * The guild this channel is associated to + */ + guild: Guild; + /** + * Sorting position of the channel + */ + position: number; + /** + * This guild channel's permission overwrites controller + */ + permissions: ChannelPermissionsController; + /** + * The name of the channel + */ + name: string; + /** + * The topic of the channel. + * Possibly null if channel does not have a topic + */ + topic: string | null; + /** + * The guild channel's invites controller + */ + invites: GuildChannelInvitesController; + /** + * The ID of this channel's parent category + */ + parentId: Snowflake | undefined | null; + /** + * The guild channel's webhooks controller + */ + webhooks: GuildChannelWebhooksController; + constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} guildChannel The guild channel data + * @returns {this} + */ + init(guildChannel: GatewayStruct): this; + /** + * Parent {@link GuildCategoryChannel} of this channel. + * Possibly null if this channel does not have a parent category channel, or the category is not cached + */ + get parent(): GuildCategoryChannel | null; + /** + * Update a channel's settings. Requires the {@link Permission.ManageChannels} permission for the guild. + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modify(options: GuildChannelOptions): Promise; +} diff --git a/lib/structures/channels/GuildChannel.js b/lib/structures/channels/GuildChannel.js new file mode 100644 index 000000000..07740524c --- /dev/null +++ b/lib/structures/channels/GuildChannel.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannel = void 0; +const Channel_1 = require("./Channel"); +const channel_1 = require("../../controllers/channel"); +const guild_1 = require("../../controllers/guild"); +const PermissionOverwrite_1 = require("../PermissionOverwrite"); +/** + * Represents a channel found in a guild of any type + */ +class GuildChannel extends Channel_1.Channel { + constructor(bot, guildChannel, guild) { + super(bot, guildChannel); + this.guild = guild; + this.invites = new guild_1.GuildChannelInvitesController(this); + this.webhooks = new guild_1.GuildChannelWebhooksController(this); + } + /** + * @ignore + * @param {GatewayStruct} guildChannel The guild channel data + * @returns {this} + */ + init(guildChannel) { + super.init(guildChannel); + this.position = guildChannel.position; + this.permissions = new channel_1.ChannelPermissionsController(this); + if (guildChannel.permission_overwrites) { + this.permissions.cache.addMany(guildChannel.permission_overwrites.map((permission) => new PermissionOverwrite_1.PermissionOverwrite(this.bot, permission, this))); + } + this.name = guildChannel.name; + this.topic = guildChannel.topic; + this.parentId = guildChannel.parent_id; + return this; + } + /** + * Parent {@link GuildCategoryChannel} of this channel. + * Possibly null if this channel does not have a parent category channel, or the category is not cached + */ + get parent() { + if (!this.parentId) + return null; + return this.guild.channels.cache.get(this.parentId) || null; + } + /** + * Update a channel's settings. Requires the {@link Permission.ManageChannels} permission for the guild. + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuildChannel(this.id, options); + } +} +exports.GuildChannel = GuildChannel; diff --git a/lib/structures/channels/GuildTextChannel.d.ts b/lib/structures/channels/GuildTextChannel.d.ts new file mode 100644 index 000000000..11d1ac624 --- /dev/null +++ b/lib/structures/channels/GuildTextChannel.d.ts @@ -0,0 +1,41 @@ +import { GuildChannel } from './GuildChannel'; +import { TextChannel } from './TextChannel'; +import { Bot } from '../../bot'; +import { ChannelMessagesController, ChannelPinsController } from '../../controllers/channel'; +import { Snowflake } from '../../types'; +import { GatewayStruct } from '../base'; +import { Guild } from '../guild'; +import { Message, MessageOptions, MessageData, MessageEmbed } from '../message'; +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildText} + */ +export declare class GuildTextChannel extends GuildChannel implements TextChannel { + /** @inheritDoc */ + nsfw: boolean | undefined; + /** @inheritDoc */ + lastMessageId: Snowflake | null | undefined; + /** @inheritDoc */ + slowModeTimeout: number; + /** @inheritDoc */ + messages: ChannelMessagesController; + /** @inheritDoc */ + pins: ChannelPinsController; + constructor(bot: Bot, textChannel: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} textChannel The text channel data + * @returns {this} + */ + init(textChannel: GatewayStruct): this; + /** @inheritDoc */ + sendMessage(data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(messages: Snowflake[]): Promise; + /** @inheritDoc */ + triggerTyping(): Promise; +} diff --git a/lib/structures/channels/GuildTextChannel.js b/lib/structures/channels/GuildTextChannel.js new file mode 100644 index 000000000..11ada62a3 --- /dev/null +++ b/lib/structures/channels/GuildTextChannel.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildTextChannel = void 0; +const GuildChannel_1 = require("./GuildChannel"); +const channel_1 = require("../../controllers/channel"); +const Timestamp_1 = require("../Timestamp"); +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildText} + */ +class GuildTextChannel extends GuildChannel_1.GuildChannel { + // Guild parameter used when creating the channel from the Guild constructor + constructor(bot, textChannel, guild) { + super(bot, textChannel, guild); + this.messages = new channel_1.ChannelMessagesController(this); + } + /** + * @ignore + * @param {GatewayStruct} textChannel The text channel data + * @returns {this} + */ + init(textChannel) { + super.init(textChannel); + this.pins = new channel_1.ChannelPinsController(this); + this.nsfw = textChannel.nsfw; + this.lastMessageId = textChannel.last_message_id; + this.slowModeTimeout = textChannel.rate_limit_per_user; + this.pins.lastPinTimestamp = new Timestamp_1.Timestamp(textChannel.last_pin_timestamp); + return this; + } + /** @inheritDoc */ + sendMessage(data, options) { + return this.bot.api.sendMessage(this.id, data, options); + } + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(messages) { + return this.bot.api.bulkDeleteMessages(this.id, messages); + } + /** @inheritDoc */ + triggerTyping() { + return this.bot.api.triggerTextChannelTyping(this.id); + } +} +exports.GuildTextChannel = GuildTextChannel; diff --git a/lib/structures/channels/GuildVoiceChannel.d.ts b/lib/structures/channels/GuildVoiceChannel.d.ts new file mode 100644 index 000000000..3d323f470 --- /dev/null +++ b/lib/structures/channels/GuildVoiceChannel.d.ts @@ -0,0 +1,11 @@ +import { Guild, GuildChannel } from '..'; +import { Bot } from '../../bot'; +import { GatewayStruct } from '../base'; +import Connection from '../voice/Connection'; +/** + * Represents a Voice channel + */ +export default class GuildVoiceChannel extends GuildChannel { + constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild); + join(mute?: boolean, deaf?: boolean): Promise; +} diff --git a/lib/structures/channels/GuildVoiceChannel.js b/lib/structures/channels/GuildVoiceChannel.js new file mode 100644 index 000000000..76f4c8cae --- /dev/null +++ b/lib/structures/channels/GuildVoiceChannel.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const __1 = require(".."); +/** + * Represents a Voice channel + */ +class GuildVoiceChannel extends __1.GuildChannel { + constructor(bot, guildChannel, guild) { + super(bot, guildChannel, guild); + } + join(mute, deaf) { + return this.guild.voice.join(this.id, { mute, deaf }); + } +} +exports.default = GuildVoiceChannel; diff --git a/lib/structures/channels/TextChannel.d.ts b/lib/structures/channels/TextChannel.d.ts new file mode 100644 index 000000000..268530f63 --- /dev/null +++ b/lib/structures/channels/TextChannel.d.ts @@ -0,0 +1,54 @@ +import { DMChannel } from './DMChannel'; +import { GuildTextChannel } from './GuildTextChannel'; +import { ChannelMessagesController, ChannelPinsController } from '../../controllers/channel'; +import { Snowflake } from '../../types'; +import { Message, MessageData, MessageOptions, MessageEmbed } from '../message'; +/** + * Text based channels of Discord + */ +export declare type TextBasedChannel = GuildTextChannel | DMChannel; +/** + * Abstract class that all text-based channels implement + */ +export interface TextChannel { + /** + * The ID of the last message sent in this channel. + * May not point to an existing or valid message + */ + lastMessageId: Snowflake | null | undefined; + /** + * The text channel's messages controller + */ + messages: ChannelMessagesController; + /** + * The text channel's pinned messages controller + */ + pins: ChannelPinsController; + /** + * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. If operating on a {@link GuildTextChannel}, this endpoint requires the {@link Permission.SendMessages} permission to be present on the current user. If the {@link MessageOptions.tts} field is set to true, the {@link Permission.SendTTSMessages} permission is required for the message to be spoken + * @param {string | MessageData | MessageEmbed} data The message data. + * Can be: + * 1. Raw content to be sent as a message + * @example ```typescript + * channel.sendMessage('Hello World!'); + * ``` + * 2. A {@link MessageData} object, containing content and/or embed + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', embed: { title: 'My Embed!' } }); + * ``` + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', files: [{ path: './my_image.png', name: 'image.png' }] }); + * ``` + * 3. A {@link MessageEmbed} instance + * @param {MessageOptions} options + * @returns {Promise} + */ + sendMessage(data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** + * Posts a typing indicator for a specified text channel. + * Useful when the bot is responding to a command and expects the computation to take a few seconds. + * This method may be called to let the user know that the bot is processing their message. + * @returns {Promise} + */ + triggerTyping(): Promise; +} diff --git a/lib/structures/channels/TextChannel.js b/lib/structures/channels/TextChannel.js new file mode 100644 index 000000000..c8ad2e549 --- /dev/null +++ b/lib/structures/channels/TextChannel.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/structures/channels/index.d.ts b/lib/structures/channels/index.d.ts new file mode 100644 index 000000000..b64eb6a85 --- /dev/null +++ b/lib/structures/channels/index.d.ts @@ -0,0 +1,8 @@ +export * from './Channel'; +export * from './DMChannel'; +export * from './GuildCategoryChannel'; +export * from './GuildChannel'; +export * from './GuildTextChannel'; +export * from './TextChannel'; +export * from './GuildVoiceChannel'; +export * from './utils'; diff --git a/lib/structures/channels/index.js b/lib/structures/channels/index.js new file mode 100644 index 000000000..89c7e28f8 --- /dev/null +++ b/lib/structures/channels/index.js @@ -0,0 +1,20 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Channel"), exports); +__exportStar(require("./DMChannel"), exports); +__exportStar(require("./GuildCategoryChannel"), exports); +__exportStar(require("./GuildChannel"), exports); +__exportStar(require("./GuildTextChannel"), exports); +__exportStar(require("./TextChannel"), exports); +__exportStar(require("./GuildVoiceChannel"), exports); +__exportStar(require("./utils"), exports); diff --git a/lib/structures/channels/utils/ChannelUtils.d.ts b/lib/structures/channels/utils/ChannelUtils.d.ts new file mode 100644 index 000000000..b9b654956 --- /dev/null +++ b/lib/structures/channels/utils/ChannelUtils.d.ts @@ -0,0 +1,54 @@ +import { Bot } from '../../../bot'; +import { GatewayStruct } from '../../base'; +import { Guild } from '../../guild'; +import { Channel } from '../Channel'; +import { DMChannel } from '../DMChannel'; +import { GuildChannel } from '../GuildChannel'; +/** + * Handles channel-related util methods + */ +export declare class ChannelUtils { + /** + * Creates a new {@link Channel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild | undefined} guild_ The guild associated to the channel + * @returns {Promise} + */ + static create(bot: Bot, data: GatewayStruct, guild_?: Guild): Promise; + /** + * Creates a new {@link GuildChannel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild} guild The guild associated to the channel + * @returns {Promise} + */ + static createGuildChannel(bot: Bot, data: GatewayStruct, guild: Guild): GuildChannel; + /** + * Creates a new {@link DMChannel} instance + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @returns {Promise} + */ + static createDMChannel(bot: Bot, data: GatewayStruct): DMChannel; + /** + * Retrieves the guild hidden in a channel instance's structure + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel instance + * @returns {Promise} + */ + static getChannelGuild(bot: Bot, channel: Channel | GatewayStruct): Promise; + /** + * Caches a channel in the correct Collection + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to cache + * @param {boolean} force Whether or not to force cache DM channels if already cached + */ + static cache(bot: Bot, channel: Channel, force?: boolean): void; + /** + * Deletes a channel from the cache + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to delete + */ + static delete(bot: Bot, channel: Channel): void; +} diff --git a/lib/structures/channels/utils/ChannelUtils.js b/lib/structures/channels/utils/ChannelUtils.js new file mode 100644 index 000000000..18e249471 --- /dev/null +++ b/lib/structures/channels/utils/ChannelUtils.js @@ -0,0 +1,123 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelUtils = void 0; +const Channel_1 = require("../Channel"); +const DMChannel_1 = require("../DMChannel"); +const GuildCategoryChannel_1 = require("../GuildCategoryChannel"); +const GuildChannel_1 = require("../GuildChannel"); +const GuildTextChannel_1 = require("../GuildTextChannel"); +/** + * Handles channel-related util methods + */ +class ChannelUtils { + /** + * Creates a new {@link Channel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild | undefined} guild_ The guild associated to the channel + * @returns {Promise} + */ + static create(bot, data, guild_) { + return __awaiter(this, void 0, void 0, function* () { + const { guild_id: guildId } = data; + const guild = guild_ || (guildId && (yield bot.guilds.get(guildId))); + return guild + ? ChannelUtils.createGuildChannel(bot, data, guild) + : ChannelUtils.createDMChannel(bot, data); + }); + } + /** + * Creates a new {@link GuildChannel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild} guild The guild associated to the channel + * @returns {Promise} + */ + static createGuildChannel(bot, data, guild) { + let channel; + switch (data.type) { + case Channel_1.ChannelType.GuildText: + channel = new GuildTextChannel_1.GuildTextChannel(bot, data, guild); + break; + case Channel_1.ChannelType.GuildCategory: + channel = new GuildCategoryChannel_1.GuildCategoryChannel(bot, data, guild); + break; + case Channel_1.ChannelType.GuildVoice: + case Channel_1.ChannelType.GuildNews: + case Channel_1.ChannelType.GuildStore: + channel = new GuildChannel_1.GuildChannel(bot, data, guild); + } + if (!channel) { + throw new TypeError('Invalid guild channel type!'); + } + return channel; + } + /** + * Creates a new {@link DMChannel} instance + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @returns {Promise} + */ + static createDMChannel(bot, data) { + const { guild_id: guildId } = data; + if (guildId) { + throw new TypeError('DM channels cannot have a guild ID!'); + } + return new DMChannel_1.DMChannel(bot, data); + } + /** + * Retrieves the guild hidden in a channel instance's structure + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel instance + * @returns {Promise} + */ + static getChannelGuild(bot, channel) { + const { guild_id: guildId } = channel instanceof Channel_1.Channel ? channel.structure : channel; + if (!guildId) { + throw new TypeError('No guild ID specified for channel!'); + } + return bot.guilds.get(guildId); + } + /** + * Caches a channel in the correct Collection + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to cache + * @param {boolean} force Whether or not to force cache DM channels if already cached + */ + static cache(bot, channel, force = false) { + if (channel instanceof GuildChannel_1.GuildChannel) { + channel.guild.channels.cache.add(channel); + } + if (channel instanceof GuildChannel_1.GuildChannel || + (channel instanceof DMChannel_1.DMChannel && (force || !bot.channels.cache.has(channel.id)))) { + bot.channels.cache.add(channel); + } + if (channel instanceof DMChannel_1.DMChannel) { + const recipient = bot.users.cache.get(channel.recipient.id); + if (!recipient) + return; + recipient.dm = channel; + } + } + /** + * Deletes a channel from the cache + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to delete + */ + static delete(bot, channel) { + if (channel instanceof GuildChannel_1.GuildChannel) { + channel.guild.channels.cache.delete(channel.id); + } + bot.channels.cache.delete(channel.id); + } +} +exports.ChannelUtils = ChannelUtils; diff --git a/lib/structures/channels/utils/index.d.ts b/lib/structures/channels/utils/index.d.ts new file mode 100644 index 000000000..8ecfb6ea6 --- /dev/null +++ b/lib/structures/channels/utils/index.d.ts @@ -0,0 +1 @@ +export * from './ChannelUtils'; diff --git a/lib/structures/channels/utils/index.js b/lib/structures/channels/utils/index.js new file mode 100644 index 000000000..2863faa30 --- /dev/null +++ b/lib/structures/channels/utils/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./ChannelUtils"), exports); diff --git a/lib/structures/flags/Flags.d.ts b/lib/structures/flags/Flags.d.ts new file mode 100644 index 000000000..964fadcd6 --- /dev/null +++ b/lib/structures/flags/Flags.d.ts @@ -0,0 +1,28 @@ +/** + * Manager class responsible for retrieving data off of bit-wise flags + * @template T + */ +export declare class Flags { + /** + * Integer of the flags + */ + protected readonly flags: number; + constructor(flags: number); + /** + * Whether a specific flag is included in this instance's flags + * @param {T} flag The flag to check if included + * @returns {boolean} + */ + has(flag: T): boolean; + /** + * Returns the bits of the flags this instance contains + * @type {number} + */ + get bits(): number; + /** + * Creates a new instance of {@link Flags} based on given flags + * @param {T[]} flags An array of all flags the {@link Flags} instance should contain + * @returns {Flags} + */ + static from(...flags: T[]): Flags; +} diff --git a/lib/structures/flags/Flags.js b/lib/structures/flags/Flags.js new file mode 100644 index 000000000..17ae97fac --- /dev/null +++ b/lib/structures/flags/Flags.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Flags = void 0; +/** + * Manager class responsible for retrieving data off of bit-wise flags + * @template T + */ +class Flags { + constructor(flags) { + this.flags = flags; + } + /** + * Whether a specific flag is included in this instance's flags + * @param {T} flag The flag to check if included + * @returns {boolean} + */ + has(flag) { + return (this.flags & flag) === flag; + } + /** + * Returns the bits of the flags this instance contains + * @type {number} + */ + get bits() { + return this.flags; + } + /** + * Creates a new instance of {@link Flags} based on given flags + * @param {T[]} flags An array of all flags the {@link Flags} instance should contain + * @returns {Flags} + */ + static from(...flags) { + const bits = flags.reduce((totalBits, bit) => totalBits | bit, 0); + return new Flags(bits); + } +} +exports.Flags = Flags; diff --git a/lib/structures/flags/GuildSystemChannelFlags.d.ts b/lib/structures/flags/GuildSystemChannelFlags.d.ts new file mode 100644 index 000000000..77d1c3ed4 --- /dev/null +++ b/lib/structures/flags/GuildSystemChannelFlags.d.ts @@ -0,0 +1,17 @@ +import { Flags } from './Flags'; +/** + * All guild system channel flags + * https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags + */ +export declare enum SystemChannelFlag { + /** + * Suppress member join notifications + */ + SuppressJoinNotifications = 1, + /** + * Suppress server boost notifications + */ + SuppressBoosts = 2 +} +export declare class GuildSystemChannelFlags extends Flags { +} diff --git a/lib/structures/flags/GuildSystemChannelFlags.js b/lib/structures/flags/GuildSystemChannelFlags.js new file mode 100644 index 000000000..384c7c59c --- /dev/null +++ b/lib/structures/flags/GuildSystemChannelFlags.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildSystemChannelFlags = exports.SystemChannelFlag = void 0; +const Flags_1 = require("./Flags"); +/** + * All guild system channel flags + * https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags + */ +var SystemChannelFlag; +(function (SystemChannelFlag) { + /** + * Suppress member join notifications + */ + SystemChannelFlag[SystemChannelFlag["SuppressJoinNotifications"] = 1] = "SuppressJoinNotifications"; + /** + * Suppress server boost notifications + */ + SystemChannelFlag[SystemChannelFlag["SuppressBoosts"] = 2] = "SuppressBoosts"; +})(SystemChannelFlag = exports.SystemChannelFlag || (exports.SystemChannelFlag = {})); +class GuildSystemChannelFlags extends Flags_1.Flags { +} +exports.GuildSystemChannelFlags = GuildSystemChannelFlags; diff --git a/lib/structures/flags/MessageFlags.d.ts b/lib/structures/flags/MessageFlags.d.ts new file mode 100644 index 000000000..9409d83e4 --- /dev/null +++ b/lib/structures/flags/MessageFlags.d.ts @@ -0,0 +1,29 @@ +import { Flags } from './Flags'; +/** + * All message flags + * https://discord.com/developers/docs/resources/channel#message-object-message-flags + */ +export declare enum MessageFlag { + /** + * This message has been published to subscribed channels (via Channel Following) + */ + Crossposted = 1, + /** + * This message originated from a message in another channel (via Channel Following) + */ + IsCrosspost = 2, + /** + * Do not include any embeds when serializing this message + */ + SuppressEmbeds = 4, + /** + * The source message for this crosspost has been deleted (via Channel Following) + */ + SourceMessageDeleted = 8, + /** + * This message came from the urgent message system + */ + Urgent = 16 +} +export declare class MessageFlags extends Flags { +} diff --git a/lib/structures/flags/MessageFlags.js b/lib/structures/flags/MessageFlags.js new file mode 100644 index 000000000..988265b2f --- /dev/null +++ b/lib/structures/flags/MessageFlags.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageFlags = exports.MessageFlag = void 0; +const Flags_1 = require("./Flags"); +/** + * All message flags + * https://discord.com/developers/docs/resources/channel#message-object-message-flags + */ +var MessageFlag; +(function (MessageFlag) { + /** + * This message has been published to subscribed channels (via Channel Following) + */ + MessageFlag[MessageFlag["Crossposted"] = 1] = "Crossposted"; + /** + * This message originated from a message in another channel (via Channel Following) + */ + MessageFlag[MessageFlag["IsCrosspost"] = 2] = "IsCrosspost"; + /** + * Do not include any embeds when serializing this message + */ + MessageFlag[MessageFlag["SuppressEmbeds"] = 4] = "SuppressEmbeds"; + /** + * The source message for this crosspost has been deleted (via Channel Following) + */ + MessageFlag[MessageFlag["SourceMessageDeleted"] = 8] = "SourceMessageDeleted"; + /** + * This message came from the urgent message system + */ + MessageFlag[MessageFlag["Urgent"] = 16] = "Urgent"; +})(MessageFlag = exports.MessageFlag || (exports.MessageFlag = {})); +class MessageFlags extends Flags_1.Flags { +} +exports.MessageFlags = MessageFlags; diff --git a/lib/structures/flags/PermissionFlags.d.ts b/lib/structures/flags/PermissionFlags.d.ts new file mode 100644 index 000000000..99f334c08 --- /dev/null +++ b/lib/structures/flags/PermissionFlags.d.ts @@ -0,0 +1,82 @@ +import { Flags } from './Flags'; +import { Snowflake } from '../../types'; +/** + * All Discord permission flags + * https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags + */ +export declare enum Permission { + CreateInstantInvite = 1, + KickMembers = 2, + BanMembers = 4, + Administrator = 8, + ManageChannels = 16, + ManageGuild = 32, + AddReactions = 64, + ViewAuditLog = 128, + PrioritySpeaker = 256, + Stream = 512, + ViewChannel = 1024, + SendMessages = 2048, + SendTTSMessages = 4096, + ManageMessages = 8192, + EmbedLinks = 16384, + AttachFiles = 32768, + ReadMessageHistory = 65536, + MentionEveryone = 131072, + UseExternalEmojis = 262144, + ViewGuildInsights = 524288, + Connect = 1048576, + Speak = 2097152, + MuteMembers = 4194304, + DeafenMembers = 8388608, + MoveMembers = 16777216, + UseVAD = 33554432, + ChangeNickname = 67108864, + ManageNicknames = 134217728, + ManageRoles = 268435456, + ManageWebhooks = 536870912, + ManageEmojis = 1073741824 +} +/** + * The type of the permission overwrite + */ +export declare enum PermissibleType { + /** + * The permission overwrite is for a member + */ + Member = "member", + /** + * The permission overwrite is for a role + */ + Role = "role" +} +/** + * Data about the member or role which will be modified in the guild channel's permission + */ +export interface Permissible { + /** + * The ID of the member or role + */ + id: Snowflake; + /** + * Whether this is a 'member' or a 'role' + */ + type: PermissibleType; +} +/** + * Used to overwrite permissions for a guild channel. + * Contains the allowed and denied permission flags for a specific member or a role + */ +export interface PermissionOverwriteFlags { + /** + * The allowed permission flags for the member or role in a guild channel + */ + allow?: PermissionFlags; + /** + * The denied permission flags for the member or role in a guild channel + */ + deny?: PermissionFlags; +} +export declare class PermissionFlags extends Flags { + constructor(flags: string); +} diff --git a/lib/structures/flags/PermissionFlags.js b/lib/structures/flags/PermissionFlags.js new file mode 100644 index 000000000..ce1649397 --- /dev/null +++ b/lib/structures/flags/PermissionFlags.js @@ -0,0 +1,63 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionFlags = exports.PermissibleType = exports.Permission = void 0; +const Flags_1 = require("./Flags"); +/** + * All Discord permission flags + * https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags + */ +var Permission; +(function (Permission) { + Permission[Permission["CreateInstantInvite"] = 1] = "CreateInstantInvite"; + Permission[Permission["KickMembers"] = 2] = "KickMembers"; + Permission[Permission["BanMembers"] = 4] = "BanMembers"; + Permission[Permission["Administrator"] = 8] = "Administrator"; + Permission[Permission["ManageChannels"] = 16] = "ManageChannels"; + Permission[Permission["ManageGuild"] = 32] = "ManageGuild"; + Permission[Permission["AddReactions"] = 64] = "AddReactions"; + Permission[Permission["ViewAuditLog"] = 128] = "ViewAuditLog"; + Permission[Permission["PrioritySpeaker"] = 256] = "PrioritySpeaker"; + Permission[Permission["Stream"] = 512] = "Stream"; + Permission[Permission["ViewChannel"] = 1024] = "ViewChannel"; + Permission[Permission["SendMessages"] = 2048] = "SendMessages"; + Permission[Permission["SendTTSMessages"] = 4096] = "SendTTSMessages"; + Permission[Permission["ManageMessages"] = 8192] = "ManageMessages"; + Permission[Permission["EmbedLinks"] = 16384] = "EmbedLinks"; + Permission[Permission["AttachFiles"] = 32768] = "AttachFiles"; + Permission[Permission["ReadMessageHistory"] = 65536] = "ReadMessageHistory"; + Permission[Permission["MentionEveryone"] = 131072] = "MentionEveryone"; + Permission[Permission["UseExternalEmojis"] = 262144] = "UseExternalEmojis"; + Permission[Permission["ViewGuildInsights"] = 524288] = "ViewGuildInsights"; + Permission[Permission["Connect"] = 1048576] = "Connect"; + Permission[Permission["Speak"] = 2097152] = "Speak"; + Permission[Permission["MuteMembers"] = 4194304] = "MuteMembers"; + Permission[Permission["DeafenMembers"] = 8388608] = "DeafenMembers"; + Permission[Permission["MoveMembers"] = 16777216] = "MoveMembers"; + Permission[Permission["UseVAD"] = 33554432] = "UseVAD"; + Permission[Permission["ChangeNickname"] = 67108864] = "ChangeNickname"; + Permission[Permission["ManageNicknames"] = 134217728] = "ManageNicknames"; + Permission[Permission["ManageRoles"] = 268435456] = "ManageRoles"; + Permission[Permission["ManageWebhooks"] = 536870912] = "ManageWebhooks"; + Permission[Permission["ManageEmojis"] = 1073741824] = "ManageEmojis"; +})(Permission = exports.Permission || (exports.Permission = {})); +/** + * The type of the permission overwrite + */ +var PermissibleType; +(function (PermissibleType) { + /** + * The permission overwrite is for a member + */ + PermissibleType["Member"] = "member"; + /** + * The permission overwrite is for a role + */ + PermissibleType["Role"] = "role"; +})(PermissibleType = exports.PermissibleType || (exports.PermissibleType = {})); +class PermissionFlags extends Flags_1.Flags { + // Permission flags in Discord are now received in serialized strings + constructor(flags) { + super(parseInt(flags)); + } +} +exports.PermissionFlags = PermissionFlags; diff --git a/lib/structures/flags/PresenceActivityFlags.d.ts b/lib/structures/flags/PresenceActivityFlags.d.ts new file mode 100644 index 000000000..372136581 --- /dev/null +++ b/lib/structures/flags/PresenceActivityFlags.d.ts @@ -0,0 +1,15 @@ +import { Flags } from './Flags'; +/** + * All Discord presence activity flags + * https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags + */ +export declare enum PresenceActivity { + Instance = 1, + Join = 2, + Spectate = 4, + JoinRequest = 8, + Sync = 16, + Play = 32 +} +export declare class PresenceActivityFlags extends Flags { +} diff --git a/lib/structures/flags/PresenceActivityFlags.js b/lib/structures/flags/PresenceActivityFlags.js new file mode 100644 index 000000000..d546c4eac --- /dev/null +++ b/lib/structures/flags/PresenceActivityFlags.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PresenceActivityFlags = exports.PresenceActivity = void 0; +const Flags_1 = require("./Flags"); +/** + * All Discord presence activity flags + * https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags + */ +var PresenceActivity; +(function (PresenceActivity) { + PresenceActivity[PresenceActivity["Instance"] = 1] = "Instance"; + PresenceActivity[PresenceActivity["Join"] = 2] = "Join"; + PresenceActivity[PresenceActivity["Spectate"] = 4] = "Spectate"; + PresenceActivity[PresenceActivity["JoinRequest"] = 8] = "JoinRequest"; + PresenceActivity[PresenceActivity["Sync"] = 16] = "Sync"; + PresenceActivity[PresenceActivity["Play"] = 32] = "Play"; +})(PresenceActivity = exports.PresenceActivity || (exports.PresenceActivity = {})); +class PresenceActivityFlags extends Flags_1.Flags { +} +exports.PresenceActivityFlags = PresenceActivityFlags; diff --git a/lib/structures/flags/UserFlags.d.ts b/lib/structures/flags/UserFlags.d.ts new file mode 100644 index 000000000..d92b858a9 --- /dev/null +++ b/lib/structures/flags/UserFlags.d.ts @@ -0,0 +1,23 @@ +import { Flags } from './Flags'; +/** + * All Discord user flags (profile badges) + * https://discord.com/developers/docs/resources/user#user-object-user-flags + */ +export declare enum UserFlag { + None = 0, + DiscordEmployee = 1, + DiscordPartner = 2, + HypeSquadEvents = 4, + BugHunterLevel1 = 8, + HouseBravery = 64, + HouseBrilliance = 128, + HouseBalance = 256, + EarlySupporter = 512, + TeamUser = 1024, + System = 4096, + BugHunterLevel2 = 16384, + VerifiedBot = 65536, + VerifiedBotDeveloper = 131072 +} +export declare class UserFlags extends Flags { +} diff --git a/lib/structures/flags/UserFlags.js b/lib/structures/flags/UserFlags.js new file mode 100644 index 000000000..df56de021 --- /dev/null +++ b/lib/structures/flags/UserFlags.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserFlags = exports.UserFlag = void 0; +const Flags_1 = require("./Flags"); +/** + * All Discord user flags (profile badges) + * https://discord.com/developers/docs/resources/user#user-object-user-flags + */ +var UserFlag; +(function (UserFlag) { + UserFlag[UserFlag["None"] = 0] = "None"; + UserFlag[UserFlag["DiscordEmployee"] = 1] = "DiscordEmployee"; + UserFlag[UserFlag["DiscordPartner"] = 2] = "DiscordPartner"; + UserFlag[UserFlag["HypeSquadEvents"] = 4] = "HypeSquadEvents"; + UserFlag[UserFlag["BugHunterLevel1"] = 8] = "BugHunterLevel1"; + UserFlag[UserFlag["HouseBravery"] = 64] = "HouseBravery"; + UserFlag[UserFlag["HouseBrilliance"] = 128] = "HouseBrilliance"; + UserFlag[UserFlag["HouseBalance"] = 256] = "HouseBalance"; + UserFlag[UserFlag["EarlySupporter"] = 512] = "EarlySupporter"; + UserFlag[UserFlag["TeamUser"] = 1024] = "TeamUser"; + UserFlag[UserFlag["System"] = 4096] = "System"; + UserFlag[UserFlag["BugHunterLevel2"] = 16384] = "BugHunterLevel2"; + UserFlag[UserFlag["VerifiedBot"] = 65536] = "VerifiedBot"; + UserFlag[UserFlag["VerifiedBotDeveloper"] = 131072] = "VerifiedBotDeveloper"; +})(UserFlag = exports.UserFlag || (exports.UserFlag = {})); +class UserFlags extends Flags_1.Flags { +} +exports.UserFlags = UserFlags; diff --git a/lib/structures/flags/index.d.ts b/lib/structures/flags/index.d.ts new file mode 100644 index 000000000..efa8d4433 --- /dev/null +++ b/lib/structures/flags/index.d.ts @@ -0,0 +1,6 @@ +export * from './Flags'; +export * from './GuildSystemChannelFlags'; +export * from './MessageFlags'; +export * from './PermissionFlags'; +export * from './PresenceActivityFlags'; +export * from './UserFlags'; diff --git a/lib/structures/flags/index.js b/lib/structures/flags/index.js new file mode 100644 index 000000000..21ebd7b58 --- /dev/null +++ b/lib/structures/flags/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Flags"), exports); +__exportStar(require("./GuildSystemChannelFlags"), exports); +__exportStar(require("./MessageFlags"), exports); +__exportStar(require("./PermissionFlags"), exports); +__exportStar(require("./PresenceActivityFlags"), exports); +__exportStar(require("./UserFlags"), exports); diff --git a/lib/structures/guild/Guild.d.ts b/lib/structures/guild/Guild.d.ts new file mode 100644 index 000000000..dc11abc1d --- /dev/null +++ b/lib/structures/guild/Guild.d.ts @@ -0,0 +1,446 @@ +import { GuildPreview } from './GuildPreview'; +import { GuildUnavailable } from './GuildUnavailable'; +import { GuildWidget } from './GuildWidget'; +import Collection from '../../Collection'; +import { Bot } from '../../bot'; +import { GuildChannelsController, GuildEmojisController, GuildInvitesController, GuildMembersController } from '../../controllers/guild'; +import { GuildBansController, GuildIntegrationsController, GuildRolesController } from '../../controllers/guild'; +import { GuildWebhooksController } from '../../controllers/guild/GuildWebhooksController'; +import { BotSocketShard } from '../../socket'; +import { ShardId, Snowflake } from '../../types'; +import { GuildBannerFormat } from '../Avatar'; +import { ImageURI } from '../ImageURI'; +import { GatewayStruct } from '../base'; +import { GuildChannel, GuildTextChannel } from '../channels'; +import { GuildSystemChannelFlags, PermissionFlags } from '../flags'; +import { Member, MemberPresence } from '../member'; +import GuildVoice from '../voice/GuildVoice'; +import VoiceState from '../voice/VoiceState'; +/** + * Guild verification levels + */ +export declare enum VerificationLevel { + None = 0, + Low = 1, + Medium = 2, + High = 3, + VeryHigh = 4 +} +/** + * Guild default notification levels + */ +export declare enum NotificationLevels { + AllMessages = 0, + OnlyMentions = 1 +} +/** + * Guild explicit content filtering levels + */ +export declare enum ExplicitContentLevels { + Disabled = 0, + MembersWithoutRoles = 1, + AllMembers = 2 +} +/** + * MFA levels required for the Guild + */ +export declare enum MFALevel { + None = 0, + Elevated = 1 +} +/** + * Guild premium (boost) tiers + */ +export declare enum BoostTiers { + None = 0, + Tier1 = 1, + Tier2 = 2, + Tier3 = 3 +} +/** + * Information about the Guild's AFK channel and timeout + */ +export interface GuildAFK { + /** + * The AFK channel + */ + channel?: GuildChannel; + /** + * AFK timeout in seconds + */ + timeout: number; +} +/** + * Information about guild option levels + */ +export interface GuildLevels { + /** + * Verification level required for the guild + */ + verification: VerificationLevel; + /** + * Default notifications level + */ + notifications: NotificationLevels; + /** + * Explicit content filter level + */ + explicitContent: ExplicitContentLevels; + /** + * Required MFA level for the guild + */ + mfa: MFALevel; +} +/** + * Information about the guild's system channel + */ +export interface GuildSystemChannel { + /** + * System channel flags + */ + flags: GuildSystemChannelFlags; + /** + * The channel where guild notices such as welcome messages and boost events are posted. + * Possibly undefined if such channel does not exist + */ + channel: GuildTextChannel | undefined; +} +export interface GuildBoosts { + /** + * Guild boost level + */ + tier: BoostTiers; + /** + * Number of boosts this server currently has + */ + boostsCount: number | undefined; +} +/** + * Information for approximated data about a guild + */ +export interface GuildApproximates { + /** + * Approximate number of members in a guild + */ + memberCount?: number; + /** + * Approximate number of non-offline members in a guild + */ + presenceCount?: number; +} +/** + * The new data of the guild after modifying it + */ +export interface ModifyGuildOptions { + /** + * The new name of the guild + */ + name?: string; + /** + * The new guild's voice region ID + */ + region?: string; + /** + * New level data for the guild. Includes verification level, message notification level and explicit content filter level + */ + levels?: Partial>; + /** + * Data for the guild's AFK channel and timeout + */ + afk?: Partial; + /** + * The new icon of the guild + */ + icon?: ImageURI | null; + /** + * The user ID to transfer guild ownership to (bot must be the owner of the guild) + */ + ownerId?: Snowflake; + /** + * The new splash image of the guild + */ + splash?: ImageURI | null; + /** + * The new banner image of the guild + */ + banner?: ImageURI | null; + /** + * The ID of the channel where guid notices such as welcome messages and boost events are posted + */ + systemChannelId?: Snowflake; + /** + * The ID of the channel where public guilds display rules and/or guidelines + */ + rulesChannelId?: Snowflake; + /** + * The ID of the channel where admins and moderators of public guilds receive notices from Discord + */ + updatesChannelId?: Snowflake; + /** + * The preferred locale of a public guild used in server discovery and notices from Discord; defaults to "en-US" + */ + locale?: string; +} +/** + * Options used for a guild prune count + */ +export interface PruneCountOptions { + /** + * The number of days to count prune for (1 or more) + * @default 7 + */ + days?: number; + /** + * By default, prune will not remove users with roles. + * You can optionally include specific roles in your prune by providing this field with an array of role IDs to include in the prune. + * @default [] + */ + includeRoles?: Snowflake[]; +} +/** + * Options for a guild prune operation + */ +export interface PruneOptions extends PruneCountOptions { + /** + * Whether the prune operation will return the number of members pruned. Discouraged for large guilds + * @default true + */ + computePruneCount?: number; +} +/** + * Represents a guild's vanity URL invite object + */ +export interface GuildVanityInvite { + /** + * The vanity URL code. Possibly null if a vanity URL for the guild is not set + */ + code: string | null; + /** + * The number of uses this invite has + */ + uses: number; +} +/** + * Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI. + * @extends BaseStruct + */ +export declare class Guild extends GuildPreview { + /** + * The guild's channels controller + */ + channels: GuildChannelsController; + /** + * The guild's roles controller + */ + roles: GuildRolesController; + /** + * The guild's members controller + */ + members: GuildMembersController; + /** + * Guild owner {@link Member}. + * Possibly undefined if the bot is yet to cache that member + */ + owner: Member | undefined; + /** + * Guild owner ID + */ + ownerId: Snowflake; + /** + * Total permissions for the Bot in the guild (excludes overrides) + */ + permissions: PermissionFlags | undefined; + /** + * Guild voice region + */ + region: string; + /** + * Information about the Guild AFK options + */ + afk: GuildAFK; + /** + * {@link GuildLevels} object containing information about all guild level data + */ + levels: GuildLevels; + /** + * This guild's emojis controller + */ + emojis: GuildEmojisController; + /** + * Application ID of the guild creator if it is bot-created + */ + applicationId: Snowflake | null; + /** + * The guild widget data + */ + widget: GuildWidget; + /** + * {@link GuildSystemChannel} object containing information about the guild system channel + */ + systemChannel: GuildSystemChannel; + /** + * The channel where public guilds display rules and/or guidelines + */ + rulesChannel: GuildTextChannel | undefined; + /** + * Timestamp for when the guild was created + */ + createdAt: string | undefined; + /** + * Whether this guild is considered a large guild + */ + large: boolean | undefined; + /** + * Whether this guild is unavailable + */ + unavailable: boolean | undefined; + /** + * Total number of members in this guild + */ + memberCount: number | undefined; + voiceStates: Collection; + /** + * Presences of the members in the guild. + * Will only include non-offline members if the size is greater than {@link identify.large_threshold} + */ + presences: Collection; + /** + * The maximum number of presence for the guild (the default value, currently 25000, is in effect when `null` is returned) + */ + maxPresences: number | null | undefined; + /** + * The maximum number of members for the guild + */ + maxMembers: number | undefined; + /** + * The vanity URL code for the guild. Possibly null if guild does not have a vanity URL + */ + vanityURLCode: string | null; + /** + * The description for the guild. Possibly null if guild does not have a description + */ + description: string | null; + /** + * Guild banner image hash. Possibly null if guild does not have a banner image + */ + bannerHash: string | null; + /** + * {@link GuildBoosts} object containing guild boosts data + */ + boosts: GuildBoosts; + /** + * The preferred locale of a public guild used in server discovery and notices from Discord + * @default "en-US" + */ + locale: string; + /** + * The channel where admins and moderators of public guilds receive notice from Discord + */ + updatesChannel: GuildTextChannel | undefined; + /** + * The guild's invites controller + */ + invites: GuildInvitesController; + /** + * The guild's bans controller + */ + bans: GuildBansController; + /** + * The guild's integrations controller + */ + integrations: GuildIntegrationsController; + /** + * The guild's webhooks controller + */ + webhooks: GuildWebhooksController; + /** + * The id of the shard which belongs to this guild + */ + shardId?: ShardId; + voice: GuildVoice; + constructor(bot: Bot, guild: GatewayStruct, shardId?: ShardId); + /** + * @ignore + * @param {GatewayStruct} guild The guild data + * @returns {this} + */ + init(guild: GatewayStruct): this; + /** + * Returns the URL of the guild's banner image. + * Possibly returns null if the guild does not have a banner image + * @param {GuildBannerFormat} format The format of the returned guild banner image + * @param {number} size The size of the returned guild banner image + * @returns {string | null} + */ + bannerURL(format?: GuildBannerFormat, size?: number): string | null; + /** + * Modifies this guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modify(options: ModifyGuildOptions): Promise; + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + pruneCount(options?: PruneCountOptions): Promise; + /** + * Begins a prune operation on this guild. + * Requires the {@link Permission.KickMembers} permission + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + prune(options?: PruneOptions): Promise; + /** + * Fetches this guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchWidget(): Promise; + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchVanityURL(): Promise; + /** + * Leaves this guild + * @returns {Promise} + */ + leave(): Promise; + get shard(): BotSocketShard; + /** + * Creates a {@link Guild} or {@link GuildUnavailable} + * @param {Bot} bot The bot instance + * @param {GatewayStruct} guild The guild data + * @param {number} [shardId] The shard id that belongs to that guild + * @returns {Guild | GuildUnavailable} + * @ignore + */ + static create(bot: Bot, guild: GatewayStruct, shardId: number): Guild | GuildUnavailable; + /** + * Finds a {@link Guild} or {@link GuildUnavailable} from the correct cache + * @param {Bot} bot The bot instance + * @param {Snowflake} guildId The ID of the guild + * @returns {Guild | GuildUnavailable | undefined} + * @ignore + */ + static find(bot: Bot, guildId: Snowflake): Guild | GuildUnavailable | undefined; + /** + * Caches a guild in the correct collection + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The guild you wish to cache + * @ignore + */ + static cache(bot: Bot, guild: Guild | GuildUnavailable): void; + /** + * Deletes a guild from the correct cache + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The available / unavailable guild + * @ignore + */ + static delete(bot: Bot, guild: Guild | GuildUnavailable): void; +} diff --git a/lib/structures/guild/Guild.js b/lib/structures/guild/Guild.js new file mode 100644 index 000000000..2cbcd1104 --- /dev/null +++ b/lib/structures/guild/Guild.js @@ -0,0 +1,283 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Guild = exports.BoostTiers = exports.MFALevel = exports.ExplicitContentLevels = exports.NotificationLevels = exports.VerificationLevel = void 0; +const GuildEmoji_1 = require("./GuildEmoji"); +const GuildPreview_1 = require("./GuildPreview"); +const GuildUnavailable_1 = require("./GuildUnavailable"); +const GuildWidget_1 = require("./GuildWidget"); +const Collection_1 = __importDefault(require("../../Collection")); +const guild_1 = require("../../controllers/guild"); +const guild_2 = require("../../controllers/guild"); +const GuildWebhooksController_1 = require("../../controllers/guild/GuildWebhooksController"); +const Avatar_1 = require("../Avatar"); +const Role_1 = require("../Role"); +const utils_1 = require("../channels/utils"); +const flags_1 = require("../flags"); +const member_1 = require("../member"); +const GuildVoice_1 = __importDefault(require("../voice/GuildVoice")); +const VoiceState_1 = __importDefault(require("../voice/VoiceState")); +/** + * Guild verification levels + */ +var VerificationLevel; +(function (VerificationLevel) { + VerificationLevel[VerificationLevel["None"] = 0] = "None"; + VerificationLevel[VerificationLevel["Low"] = 1] = "Low"; + VerificationLevel[VerificationLevel["Medium"] = 2] = "Medium"; + VerificationLevel[VerificationLevel["High"] = 3] = "High"; + VerificationLevel[VerificationLevel["VeryHigh"] = 4] = "VeryHigh"; +})(VerificationLevel = exports.VerificationLevel || (exports.VerificationLevel = {})); +/** + * Guild default notification levels + */ +var NotificationLevels; +(function (NotificationLevels) { + NotificationLevels[NotificationLevels["AllMessages"] = 0] = "AllMessages"; + NotificationLevels[NotificationLevels["OnlyMentions"] = 1] = "OnlyMentions"; +})(NotificationLevels = exports.NotificationLevels || (exports.NotificationLevels = {})); +/** + * Guild explicit content filtering levels + */ +var ExplicitContentLevels; +(function (ExplicitContentLevels) { + ExplicitContentLevels[ExplicitContentLevels["Disabled"] = 0] = "Disabled"; + ExplicitContentLevels[ExplicitContentLevels["MembersWithoutRoles"] = 1] = "MembersWithoutRoles"; + ExplicitContentLevels[ExplicitContentLevels["AllMembers"] = 2] = "AllMembers"; +})(ExplicitContentLevels = exports.ExplicitContentLevels || (exports.ExplicitContentLevels = {})); +/** + * MFA levels required for the Guild + */ +var MFALevel; +(function (MFALevel) { + MFALevel[MFALevel["None"] = 0] = "None"; + MFALevel[MFALevel["Elevated"] = 1] = "Elevated"; +})(MFALevel = exports.MFALevel || (exports.MFALevel = {})); +/** + * Guild premium (boost) tiers + */ +var BoostTiers; +(function (BoostTiers) { + BoostTiers[BoostTiers["None"] = 0] = "None"; + BoostTiers[BoostTiers["Tier1"] = 1] = "Tier1"; + BoostTiers[BoostTiers["Tier2"] = 2] = "Tier2"; + BoostTiers[BoostTiers["Tier3"] = 3] = "Tier3"; +})(BoostTiers = exports.BoostTiers || (exports.BoostTiers = {})); +/** + * Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI. + * @extends BaseStruct + */ +class Guild extends GuildPreview_1.GuildPreview { + constructor(bot, guild, shardId) { + super(bot, guild); + this.shardId = shardId; + } + /** + * @ignore + * @param {GatewayStruct} guild The guild data + * @returns {this} + */ + init(guild) { + super.init(guild); + this.presences = new Collection_1.default(); + this.roles = new guild_2.GuildRolesController(this); + this.members = new guild_1.GuildMembersController(this); + this.emojis = new guild_1.GuildEmojisController(this); + this.channels = new guild_1.GuildChannelsController(this); + this.invites = new guild_1.GuildInvitesController(this); + this.bans = new guild_2.GuildBansController(this); + this.integrations = new guild_2.GuildIntegrationsController(this); + this.webhooks = new GuildWebhooksController_1.GuildWebhooksController(this); + this.voice = new GuildVoice_1.default(this); + this.voiceStates = new Collection_1.default(); + if (guild.channels) { + this.channels.cache.addMany(guild.channels.map((channel) => utils_1.ChannelUtils.createGuildChannel(this.bot, channel, this))); + } + // Add all of this guild's cached channels to the Bot's cached channels + this.bot.channels.cache.merge(this.channels.cache); + this.roles.cache.addMany(guild.roles.map((role) => new Role_1.Role(this.bot, role, this))); + if (guild.members) { + this.members.cache.addMany(guild.members.map((member) => { + var _a; + return new member_1.Member(this.bot, member, this, (_a = guild.presences) === null || _a === void 0 ? void 0 : _a.find((presence) => presence.user.id === member.user.id)); + })); + } + if (guild.voice_states) { + for (const voicestate of guild.voice_states) { + this.voiceStates.set(voicestate.user_id, new VoiceState_1.default(this.bot, this.members.cache.get(voicestate.user_id), voicestate)); + } + } + this.owner = this.members.cache.get(guild.owner_id); + this.ownerId = guild.owner_id; + if (guild.permissions) { + this.permissions = new flags_1.PermissionFlags(guild.permissions.toString()); + } + this.region = guild.region; + this.afk = { + channel: guild.afk_channel_id && this.channels.cache.get(guild.afk_channel_id), + timeout: guild.afk_timeout, + }; + this.levels = { + verification: guild.verification_level, + notifications: guild.default_message_notifications, + explicitContent: guild.explicit_content_filter, + mfa: guild.mfa_level, + }; + this.emojis.cache.addMany(guild.emojis.map((emoji) => new GuildEmoji_1.GuildEmoji(this.bot, emoji, this))); + // Add all of this guild's cached emojis to the Bot's cached emojis + this.bot.emojis.merge(this.emojis.cache); + this.applicationId = guild.application_id; + this.widget = new GuildWidget_1.GuildWidget(this.bot, + // Serializes the guild widget data + { enabled: guild.widget_enabled, channel_id: guild.widget_channel_id }, this); + this.systemChannel = { + channel: this.channels.cache.get(guild.system_channel_id), + flags: new flags_1.GuildSystemChannelFlags(guild.system_channel_flags), + }; + this.rulesChannel = this.channels.cache.get(guild.rules_channel_id); + this.createdAt = guild.joined_at; + this.large = guild.large; + this.unavailable = guild.unavailable; + this.memberCount = guild.member_count; + this.maxPresences = guild.max_presences; + this.maxMembers = guild.max_members; + this.vanityURLCode = guild.vanity_url_code; + this.description = guild.description; + this.bannerHash = guild.banner; + this.boosts = { + tier: guild.premium_tier, + boostsCount: guild.premium_subscription_count, + }; + this.locale = guild.locale; + if (guild.public_updates_channel_id) { + this.updatesChannel = this.channels.cache.get(guild.public_updates_channel_id); + } + return this; + } + /** + * Returns the URL of the guild's banner image. + * Possibly returns null if the guild does not have a banner image + * @param {GuildBannerFormat} format The format of the returned guild banner image + * @param {number} size The size of the returned guild banner image + * @returns {string | null} + */ + bannerURL(format = Avatar_1.GuildBannerFormat.PNG, size) { + return this.bannerHash && Avatar_1.Avatar.bannerURL(this.bannerHash, this.id, format, size); + } + /** + * Modifies this guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuild(this.id, options); + } + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + pruneCount(options) { + return this.bot.api.guildPruneCount(this.id, options); + } + /** + * Begins a prune operation on this guild. + * Requires the {@link Permission.KickMembers} permission + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + prune(options) { + return this.bot.api.guildPrune(this.id, options); + } + /** + * Fetches this guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchWidget() { + return this.bot.api.fetchGuildWidget(this.id); + } + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchVanityURL() { + return this.bot.api.fetchGuildVanityURL(this.id); + } + /** + * Leaves this guild + * @returns {Promise} + */ + leave() { + return this.bot.api.leaveGuild(this.id); + } + // Returns undefined if the guilds fetched via REST and not received by gateway + get shard() { + return this.bot.connection.shards.get(this.shardId); + } + /** + * Creates a {@link Guild} or {@link GuildUnavailable} + * @param {Bot} bot The bot instance + * @param {GatewayStruct} guild The guild data + * @param {number} [shardId] The shard id that belongs to that guild + * @returns {Guild | GuildUnavailable} + * @ignore + */ + static create(bot, guild, shardId) { + return guild.unavailable + ? new GuildUnavailable_1.GuildUnavailable(bot, guild, shardId) + : new Guild(bot, guild, shardId); + } + /** + * Finds a {@link Guild} or {@link GuildUnavailable} from the correct cache + * @param {Bot} bot The bot instance + * @param {Snowflake} guildId The ID of the guild + * @returns {Guild | GuildUnavailable | undefined} + * @ignore + */ + static find(bot, guildId) { + if (bot.unavailableGuilds.has(guildId)) { + // Guild is part of the unavailable guilds collection + return bot.unavailableGuilds.get(guildId); + } + else { + // Guild is part of the guilds collection + return bot.guilds.cache.get(guildId); + } + } + /** + * Caches a guild in the correct collection + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The guild you wish to cache + * @ignore + */ + static cache(bot, guild) { + if (guild instanceof Guild) { + bot.guilds.cache.add(guild); + } + else { + bot.unavailableGuilds.set(guild.id, guild); + } + } + /** + * Deletes a guild from the correct cache + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The available / unavailable guild + * @ignore + */ + static delete(bot, guild) { + if (guild instanceof Guild) { + // The bot left the guild or it has become unavailable. + bot.guilds.cache.delete(guild.id); + } + else { + bot.unavailableGuilds.set(guild.id, guild); + } + } +} +exports.Guild = Guild; diff --git a/lib/structures/guild/GuildBan.d.ts b/lib/structures/guild/GuildBan.d.ts new file mode 100644 index 000000000..bae9769fd --- /dev/null +++ b/lib/structures/guild/GuildBan.d.ts @@ -0,0 +1,32 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { User } from '../User'; +import { BaseGuildStruct, GatewayStruct } from '../base'; +/** + * Represents a user ban in a guild + */ +export declare class GuildBan extends BaseGuildStruct { + /** + * The reason for the guild. + * Possibly undefined if no reason was specified + */ + reason: string | undefined; + /** + * The user banned from the guild + */ + user: User; + constructor(bot: Bot, ban: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} ban The ban data + * @returns {this} + */ + init(ban: GatewayStruct): this; + /** + * The ID of the user banned from the guild. + * Serves as an identifier for this ban + * @type {Snowflake} + */ + get id(): Snowflake; +} diff --git a/lib/structures/guild/GuildBan.js b/lib/structures/guild/GuildBan.js new file mode 100644 index 000000000..a7f7eb620 --- /dev/null +++ b/lib/structures/guild/GuildBan.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildBan = void 0; +const User_1 = require("../User"); +const base_1 = require("../base"); +/** + * Represents a user ban in a guild + */ +class GuildBan extends base_1.BaseGuildStruct { + constructor(bot, ban, guild) { + super(bot, guild, ban); + this.init(ban); + } + /** + * @ignore + * @param {GatewayStruct} ban The ban data + * @returns {this} + */ + init(ban) { + this.reason = ban.reason; + this.user = this.bot.users.cache.get(ban.user.id) || new User_1.User(this.bot, ban.user); + return this; + } + /** + * The ID of the user banned from the guild. + * Serves as an identifier for this ban + * @type {Snowflake} + */ + get id() { + return this.user.id; + } +} +exports.GuildBan = GuildBan; diff --git a/lib/structures/guild/GuildEmoji.d.ts b/lib/structures/guild/GuildEmoji.d.ts new file mode 100644 index 000000000..71bdeee8e --- /dev/null +++ b/lib/structures/guild/GuildEmoji.d.ts @@ -0,0 +1,81 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { GuildEmojiFormat } from '../Avatar'; +import { Emoji } from '../Emoji'; +import { ImageURI } from '../ImageURI'; +import { Role } from '../Role'; +import { GatewayStruct } from '../base'; +/** + * Options for when creating new guild emojis + */ +export interface CreateEmojiOptions { + /** + * The name of the emoji + */ + name: string; + /** + * The 128x128 emoji image + */ + image: ImageURI; + /** + * Roles for which this emoji will be whitelisted + */ + roles?: (Snowflake | Role)[]; +} +/** + * Options for when modifying guild emojis + */ +export interface ModifyEmojiOptions { + /** + * The name of the emoji + */ + name?: string; + /** + * Roles for which this emoji will be whitelisted + */ + roles?: (Snowflake | Role)[]; +} +/** + * Structure for Emojis that were created in a Guild + */ +export declare class GuildEmoji extends Emoji { + /** + * Every guild emoji is associated to a guild + */ + guild: Guild; + /** + * Every guild emoji has a name + */ + name: string; + constructor(bot: Bot, emoji: GatewayStruct, guild: Guild); + /** + * Every guild emoji has an identifier + */ + get id(): string; + /** + * Returns the guild emoji's URL + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + URL(format?: GuildEmojiFormat, size?: number): string; + /** + * Modifies this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} + */ + modify(options: ModifyEmojiOptions): Promise; + /** + * Deletes this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @returns {Promise} + */ + delete(): Promise; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/guild/GuildEmoji.js b/lib/structures/guild/GuildEmoji.js new file mode 100644 index 000000000..5aa422551 --- /dev/null +++ b/lib/structures/guild/GuildEmoji.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildEmoji = void 0; +const Avatar_1 = require("../Avatar"); +const Emoji_1 = require("../Emoji"); +/** + * Structure for Emojis that were created in a Guild + */ +class GuildEmoji extends Emoji_1.Emoji { + constructor(bot, emoji, guild) { + super(bot, emoji, guild); + } + /** + * Every guild emoji has an identifier + */ + get id() { + return this.emojiId; + } + /** + * Returns the guild emoji's URL + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + URL(format = Avatar_1.GuildEmojiFormat.PNG, size) { + return Avatar_1.Avatar.emojiURL(this.id, format, size); + } + /** + * Modifies this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuildEmoji(this.guild.id, this.id, options); + } + /** + * Deletes this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @returns {Promise} + */ + delete() { + return this.bot.api.deleteGuildEmoji(this.guild.id, this.id); + } + /** + * @ignore + * @returns {string} + */ + toString() { + return this.animated ? `` : `<:${this.name}:${this.id}>`; + } +} +exports.GuildEmoji = GuildEmoji; diff --git a/lib/structures/guild/GuildIntegration.d.ts b/lib/structures/guild/GuildIntegration.d.ts new file mode 100644 index 000000000..eaf016f30 --- /dev/null +++ b/lib/structures/guild/GuildIntegration.d.ts @@ -0,0 +1,137 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { Role } from '../Role'; +import { Timestamp } from '../Timestamp'; +import { User } from '../User'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +/** + * The behavior of expiring subscribers of an integration + */ +export declare enum IntegrationExpireBehavior { + RemoveRole = 0, + Kick = 1 +} +/** + * Expire data for an integration + */ +export interface IntegrationExpire { + /** + * The behavior of expiring subscribers + */ + behavior: IntegrationExpireBehavior; + /** + * The grace period (in days) before expiring subscribers + */ + gracePeriod: number; +} +/** + * Account data for an integration + */ +export interface IntegrationAccount { + /** + * The ID of the account + */ + id: string; + /** + * The name of the account + */ + name: string; +} +/** + * Options for when creating new guild integrations + */ +export interface CreateIntegrationOptions { + /** + * The integration type + */ + type: string; + /** + * The ID of the integration + */ + id: Snowflake; +} +/** + * Options for when modifying guild integrations + */ +export interface ModifyIntegrationOptions { + /** + * The new expire options for the modified integration + */ + expire?: Partial; + /** + * Whether emoticons should be synced for this integration (twitch only currently) + */ + enableEmoticons?: boolean; +} +/** + * Guild integration object + */ +export declare class GuildIntegration extends BaseGuildStruct { + /** + * The ID of the integration + */ + id: Snowflake; + /** + * The name of the integration + */ + name: string; + /** + * The type of the integration + * @example twitch + * @example youtube + */ + type: string; + /** + * Whether this integration is enabled + */ + enabled: boolean; + /** + * Whether this integration is syncing + */ + syncing: boolean; + /** + * The role that this integration uses for "subscribers" + */ + role: Role | undefined; + /** + * Whether emoticons should be synced for this integration (twitch only currently) + */ + enableEmoticons: boolean; + /** + * The expire data for this integration + */ + expire: IntegrationExpire; + /** + * The user for this integration + */ + user: User; + /** + * The integration account information + */ + account: IntegrationAccount; + /** + * Timestamp of when this integration was last synced + */ + syncedAt: Timestamp; + constructor(bot: Bot, integration: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} integration The integration data + * @returns {this} + */ + init(integration: GatewayStruct): this; + /** + * Modifies the behavior and settings of this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modify(options: ModifyIntegrationOptions): Promise; + /** + * Syncs this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + sync(): Promise; +} diff --git a/lib/structures/guild/GuildIntegration.js b/lib/structures/guild/GuildIntegration.js new file mode 100644 index 000000000..629b2f881 --- /dev/null +++ b/lib/structures/guild/GuildIntegration.js @@ -0,0 +1,64 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildIntegration = exports.IntegrationExpireBehavior = void 0; +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +const base_1 = require("../base"); +/** + * The behavior of expiring subscribers of an integration + */ +var IntegrationExpireBehavior; +(function (IntegrationExpireBehavior) { + IntegrationExpireBehavior[IntegrationExpireBehavior["RemoveRole"] = 0] = "RemoveRole"; + IntegrationExpireBehavior[IntegrationExpireBehavior["Kick"] = 1] = "Kick"; +})(IntegrationExpireBehavior = exports.IntegrationExpireBehavior || (exports.IntegrationExpireBehavior = {})); +/** + * Guild integration object + */ +class GuildIntegration extends base_1.BaseGuildStruct { + constructor(bot, integration, guild) { + super(bot, guild, integration); + this.init(integration); + } + /** + * @ignore + * @param {GatewayStruct} integration The integration data + * @returns {this} + */ + init(integration) { + this.id = integration.id; + this.name = integration.name; + this.type = integration.type; + this.enabled = integration.enabled; + this.syncing = integration.syncing; + this.role = this.guild.roles.cache.get(integration.role_id); + this.enableEmoticons = integration.enable_emoticons; + this.expire = { + behavior: integration.expire_behavior, + gracePeriod: integration.expire_grace_period, + }; + this.user = + this.bot.users.cache.get(integration.user.id) || new User_1.User(this.bot, integration.user); + this.account = integration.account; + this.syncedAt = new Timestamp_1.Timestamp(integration.synced_at); + return this; + } + /** + * Modifies the behavior and settings of this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuildIntegration(this.guild.id, this.id, options); + } + /** + * Syncs this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + sync() { + return this.bot.api.syncGuildIntegration(this.guild.id, this.id); + } +} +exports.GuildIntegration = GuildIntegration; diff --git a/lib/structures/guild/GuildPreview.d.ts b/lib/structures/guild/GuildPreview.d.ts new file mode 100644 index 000000000..4f00bc72a --- /dev/null +++ b/lib/structures/guild/GuildPreview.d.ts @@ -0,0 +1,128 @@ +import { GuildApproximates } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { GuildDiscoverySplashFormat, GuildIconFormat, GuildSplashFormat } from '../Avatar'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * All guild features + * https://discord.com/developers/docs/resources/guild#guild-object-guild-features + */ +export declare enum GuildFeature { + /** + * Guild has access to set an invite splash background + */ + InviteSplash = "INVITE_SPLASH", + /** + * Guild has access to set 384kbps bitrate in voice (previously VIP voice servers) + */ + VIPRegions = "VIP_REGIONS", + /** + * Guild has access to set a vanity URL + */ + VanityURL = "VANITY_URL", + /** + * Guild is verified + */ + Verified = "VERIFIED", + /** + * Guild is partnered + */ + Partnered = "PARTNERED", + /** + * Guild is public + */ + Public = "PUBLIC", + /** + * Guild has access to use commerce features (i.e. create store channels) + */ + Commerce = "COMMERCE", + /** + * Guild has access to create news channels + */ + News = "NEWS", + /** + * Guild is able to be discovered in the directory + */ + Discoverable = "DISCOVERABLE", + /** + * Guild is able to be featured in the directory + */ + Featurable = "FEATURABLE", + /** + * Guild has access to set an animated guild icon + */ + AnimatedIcon = "ANIMATED_ICON", + /** + * Guild has access to set a guild banner image + */ + Banner = "BANNER", + /** + * Guild cannot be public + */ + PublicDisabled = "PUBLIC_DISABLED", + /** + * Guild has enabled the welcome screen + */ + WelcomeScreenEnabled = "WELCOME_SCREEN_ENABLED" +} +export declare class GuildPreview extends BaseStruct { + /** + * Guild ID + */ + id: Snowflake; + /** + * Guild name + */ + name: string; + /** + * Guild icon hash. Possibly null if guild does not have an icon + */ + iconHash: string | null; + /** + * Guild splash image hash. Possibly null if guild does not have a splash image + */ + splashHash: string | null; + /** + * Guild discovery splash image hash. Possibly null if guild does not have a discovery splash image + */ + discoverySplashHash: string | null; + /** + * Enabled guild features + */ + features: GuildFeature[]; + /** + * Information about approximated data for this guild + */ + approximates: GuildApproximates; + constructor(bot: Bot, preview: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} preview The guild preview data + * @returns {this} + */ + init(preview: GatewayStruct): this; + /** + * Returns the URL of the guild's icon image. + * Possibly returns null if the guild does not have an icon + * @param {GuildIconFormat} format The format of the returned guild icon image + * @param {number} size The size of the returned guild icon image + * @returns {string | null} + */ + iconURL(format?: GuildIconFormat, size?: number): string | null; + /** + * Returns the URL of the guild's splash image. + * Possibly returns null if the guild does not have a splash image + * @param {GuildSplashFormat} format The format of the returned guild splash image + * @param {number} size The size of the returned guild splash image + * @returns {string | null} + */ + splashURL(format?: GuildSplashFormat, size?: number): string | null; + /** + * Returns the URL of the guild's discovery splash image. + * Possibly returns null if the guild does not have a discovery splash image + * @param {GuildDiscoverySplashFormat} format The format of the returned guild discovery splash image + * @param {number} size The size of the returned guild discovery splash image + * @returns {string | null} + */ + discoverySplashURL(format?: GuildDiscoverySplashFormat, size?: number): string | null; +} diff --git a/lib/structures/guild/GuildPreview.js b/lib/structures/guild/GuildPreview.js new file mode 100644 index 000000000..2cca22aee --- /dev/null +++ b/lib/structures/guild/GuildPreview.js @@ -0,0 +1,124 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildPreview = exports.GuildFeature = void 0; +const Avatar_1 = require("../Avatar"); +const base_1 = require("../base"); +/** + * All guild features + * https://discord.com/developers/docs/resources/guild#guild-object-guild-features + */ +var GuildFeature; +(function (GuildFeature) { + /** + * Guild has access to set an invite splash background + */ + GuildFeature["InviteSplash"] = "INVITE_SPLASH"; + /** + * Guild has access to set 384kbps bitrate in voice (previously VIP voice servers) + */ + GuildFeature["VIPRegions"] = "VIP_REGIONS"; + /** + * Guild has access to set a vanity URL + */ + GuildFeature["VanityURL"] = "VANITY_URL"; + /** + * Guild is verified + */ + GuildFeature["Verified"] = "VERIFIED"; + /** + * Guild is partnered + */ + GuildFeature["Partnered"] = "PARTNERED"; + /** + * Guild is public + */ + GuildFeature["Public"] = "PUBLIC"; + /** + * Guild has access to use commerce features (i.e. create store channels) + */ + GuildFeature["Commerce"] = "COMMERCE"; + /** + * Guild has access to create news channels + */ + GuildFeature["News"] = "NEWS"; + /** + * Guild is able to be discovered in the directory + */ + GuildFeature["Discoverable"] = "DISCOVERABLE"; + /** + * Guild is able to be featured in the directory + */ + GuildFeature["Featurable"] = "FEATURABLE"; + /** + * Guild has access to set an animated guild icon + */ + GuildFeature["AnimatedIcon"] = "ANIMATED_ICON"; + /** + * Guild has access to set a guild banner image + */ + GuildFeature["Banner"] = "BANNER"; + /** + * Guild cannot be public + */ + GuildFeature["PublicDisabled"] = "PUBLIC_DISABLED"; + /** + * Guild has enabled the welcome screen + */ + GuildFeature["WelcomeScreenEnabled"] = "WELCOME_SCREEN_ENABLED"; +})(GuildFeature = exports.GuildFeature || (exports.GuildFeature = {})); +class GuildPreview extends base_1.BaseStruct { + constructor(bot, preview) { + super(bot, preview); + this.init(preview); + } + /** + * @ignore + * @param {GatewayStruct} preview The guild preview data + * @returns {this} + */ + init(preview) { + this.id = preview.id; + this.name = preview.name; + this.iconHash = preview.icon; + this.splashHash = preview.splash; + this.discoverySplashHash = preview.discovery_splash; + this.features = preview.features; + this.approximates = { + memberCount: preview.approximate_member_count, + presenceCount: preview.approximate_presence_count, + }; + return this; + } + /** + * Returns the URL of the guild's icon image. + * Possibly returns null if the guild does not have an icon + * @param {GuildIconFormat} format The format of the returned guild icon image + * @param {number} size The size of the returned guild icon image + * @returns {string | null} + */ + iconURL(format = Avatar_1.GuildIconFormat.PNG, size) { + return this.iconHash && Avatar_1.Avatar.guildURL(this.iconHash, this.id, format, size); + } + /** + * Returns the URL of the guild's splash image. + * Possibly returns null if the guild does not have a splash image + * @param {GuildSplashFormat} format The format of the returned guild splash image + * @param {number} size The size of the returned guild splash image + * @returns {string | null} + */ + splashURL(format = Avatar_1.GuildSplashFormat.PNG, size) { + return this.splashHash && Avatar_1.Avatar.splashURL(this.splashHash, this.id, format, size); + } + /** + * Returns the URL of the guild's discovery splash image. + * Possibly returns null if the guild does not have a discovery splash image + * @param {GuildDiscoverySplashFormat} format The format of the returned guild discovery splash image + * @param {number} size The size of the returned guild discovery splash image + * @returns {string | null} + */ + discoverySplashURL(format = Avatar_1.GuildDiscoverySplashFormat.PNG, size) { + return (this.discoverySplashHash && + Avatar_1.Avatar.discoverySplashURL(this.discoverySplashHash, this.id, format, size)); + } +} +exports.GuildPreview = GuildPreview; diff --git a/lib/structures/guild/GuildUnavailable.d.ts b/lib/structures/guild/GuildUnavailable.d.ts new file mode 100644 index 000000000..8e298c217 --- /dev/null +++ b/lib/structures/guild/GuildUnavailable.d.ts @@ -0,0 +1,30 @@ +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * Used instead of {@link Guild} when the guild is unavailable + * Includes just the ID of the guild, which should be fetched in order to obtain the full guild class + + * @extends BaseStruct + */ +export declare class GuildUnavailable extends BaseStruct { + /** + * Guild ID + */ + id: Snowflake; + /** + * Whether this guild is unavailable + */ + unavailable: boolean | undefined; + /** + * The id of the shard which belongs to this guild + */ + shardId: number | undefined; + constructor(bot: Bot, guild: GatewayStruct, shardId?: number); + /** + * @ignore + * @param {GatewayStruct} guild The unavailable guild data + * @returns {this} + */ + init(guild: GatewayStruct): this; +} diff --git a/lib/structures/guild/GuildUnavailable.js b/lib/structures/guild/GuildUnavailable.js new file mode 100644 index 000000000..d706a0f5a --- /dev/null +++ b/lib/structures/guild/GuildUnavailable.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildUnavailable = void 0; +const base_1 = require("../base"); +/** + * Used instead of {@link Guild} when the guild is unavailable + * Includes just the ID of the guild, which should be fetched in order to obtain the full guild class + + * @extends BaseStruct + */ +class GuildUnavailable extends base_1.BaseStruct { + constructor(bot, guild, shardId) { + super(bot, guild); + this.shardId = shardId; + this.init(guild); + } + /** + * @ignore + * @param {GatewayStruct} guild The unavailable guild data + * @returns {this} + */ + init(guild) { + this.id = guild.id; + this.unavailable = guild.unavailable; + return this; + } +} +exports.GuildUnavailable = GuildUnavailable; diff --git a/lib/structures/guild/GuildWidget.d.ts b/lib/structures/guild/GuildWidget.d.ts new file mode 100644 index 000000000..ff64264bd --- /dev/null +++ b/lib/structures/guild/GuildWidget.d.ts @@ -0,0 +1,41 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +import { GuildChannel } from '../channels'; +/** + * Options for when modifying guild widgets + */ +export interface ModifyWidgetOptions { + /** + * Whether the guild widget should be enabled + */ + enabled?: boolean; + /** + * The updated widget channel ID + */ + channelId?: Snowflake; +} +/** + * Guild widget object + */ +export declare class GuildWidget extends BaseGuildStruct { + /** + * Whether or not the guild widget is enabled + */ + enabled: boolean | undefined; + /** + * The channel for the guild widget. Possibly null if widget is not enabled or widget + * channel has not been selected + */ + channel: GuildChannel | null | undefined; + constructor(bot: Bot, widget: GatewayStruct, guild: Guild); + init(widget: GatewayStruct): this; + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modify(options: ModifyWidgetOptions): Promise; +} diff --git a/lib/structures/guild/GuildWidget.js b/lib/structures/guild/GuildWidget.js new file mode 100644 index 000000000..1b69efe7d --- /dev/null +++ b/lib/structures/guild/GuildWidget.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildWidget = void 0; +const base_1 = require("../base"); +/** + * Guild widget object + */ +class GuildWidget extends base_1.BaseGuildStruct { + constructor(bot, widget, guild) { + super(bot, guild, widget); + this.init(widget); + } + init(widget) { + this.enabled = widget.enabled; + this.channel = this.guild.channels.cache.get(widget.channel_id); + return this; + } + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modify(options) { + return this.bot.api.modifyGuildWidget(this.guild.id, options); + } +} +exports.GuildWidget = GuildWidget; diff --git a/lib/structures/guild/index.d.ts b/lib/structures/guild/index.d.ts new file mode 100644 index 000000000..544a9686d --- /dev/null +++ b/lib/structures/guild/index.d.ts @@ -0,0 +1,7 @@ +export * from './Guild'; +export * from './GuildBan'; +export * from './GuildEmoji'; +export * from './GuildIntegration'; +export * from './GuildPreview'; +export * from './GuildUnavailable'; +export * from './GuildWidget'; diff --git a/lib/structures/guild/index.js b/lib/structures/guild/index.js new file mode 100644 index 000000000..36aa13d8d --- /dev/null +++ b/lib/structures/guild/index.js @@ -0,0 +1,19 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Guild"), exports); +__exportStar(require("./GuildBan"), exports); +__exportStar(require("./GuildEmoji"), exports); +__exportStar(require("./GuildIntegration"), exports); +__exportStar(require("./GuildPreview"), exports); +__exportStar(require("./GuildUnavailable"), exports); +__exportStar(require("./GuildWidget"), exports); diff --git a/lib/structures/index.d.ts b/lib/structures/index.d.ts new file mode 100644 index 000000000..a36f7e0e6 --- /dev/null +++ b/lib/structures/index.d.ts @@ -0,0 +1,16 @@ +export * from './base'; +export * from './channels'; +export * from './flags'; +export * from './guild'; +export * from './member'; +export * from './message'; +export * from './Avatar'; +export * from './BotUser'; +export * from './Emoji'; +export * from './ImageURI'; +export * from './Invite'; +export * from './PermissionOverwrite'; +export * from './Role'; +export * from './Timestamp'; +export * from './User'; +export * from './Webhook'; diff --git a/lib/structures/index.js b/lib/structures/index.js new file mode 100644 index 000000000..089c1e03b --- /dev/null +++ b/lib/structures/index.js @@ -0,0 +1,28 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./base"), exports); +__exportStar(require("./channels"), exports); +__exportStar(require("./flags"), exports); +__exportStar(require("./guild"), exports); +__exportStar(require("./member"), exports); +__exportStar(require("./message"), exports); +__exportStar(require("./Avatar"), exports); +__exportStar(require("./BotUser"), exports); +__exportStar(require("./Emoji"), exports); +__exportStar(require("./ImageURI"), exports); +__exportStar(require("./Invite"), exports); +__exportStar(require("./PermissionOverwrite"), exports); +__exportStar(require("./Role"), exports); +__exportStar(require("./Timestamp"), exports); +__exportStar(require("./User"), exports); +__exportStar(require("./Webhook"), exports); diff --git a/lib/structures/member/Member.d.ts b/lib/structures/member/Member.d.ts new file mode 100644 index 000000000..6e0885b92 --- /dev/null +++ b/lib/structures/member/Member.d.ts @@ -0,0 +1,127 @@ +import { MemberPresence } from './MemberPresence'; +import { Bot } from '../../bot'; +import { MemberRolesController } from '../../controllers/member'; +import { Snowflake } from '../../types'; +import { Role } from '../Role'; +import { Timestamp } from '../Timestamp'; +import { User } from '../User'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +import { Guild } from '../guild'; +import VoiceState from '../voice/VoiceState'; +/** + * Options used when modifying a guild member + */ +export interface ModifyMemberOptions { + /** + * The value to set the member's nickname to. + * Requires the {@link Permission.ManageNicknames} permission + */ + nick?: string; + /** + * Array of roles / role IDs to assign to the member. + * Requires the {@link Permission.ManageRoles} permission + */ + roles?: (Snowflake | Role)[]; + /** + * Whether the member should be muted in voice channels. + * Requires the {@link Permission.MuteMembers} permission + */ + mute?: boolean; + /** + * Whether the member should be deafened in voice channel. + * Requires the {@link Permission.DeafenMembers} permission + */ + deaf?: boolean; + /** + * The ID of the voice channel to move the member to (if they are connected to another voice channel already). + * Requires the {@link Permission.MoveMembers} permission + */ + channelId?: Snowflake; +} +/** + * Options used when banning a member + */ +export interface MemberBanOptions { + /** + * Reason for the ban + */ + reason?: string; + /** + * Number of days to delete messages for (0-7) + */ + deleteMessageDays: number; +} +/** + * Representation of a Discord {@link User} in a guild + * @extends BaseGuildStruct + */ +export declare class Member extends BaseGuildStruct { + /** + * The member's user ID + */ + id: Snowflake; + /** + * The user this guild member represents + */ + user: User | undefined; + /** + * The user's guild nickname + */ + nick: string | null; + /** + * {@link Collection} of all {@link Role}s associated to this member + */ + roles: MemberRolesController; + /** + * Timestamp of when the member joined the guild + */ + joinedAt: Timestamp; + /** + * Timestamp of when the member start boosting the guild. + * Possibly null if the user has never boosted this server + */ + boostingSince: Timestamp | null; + /** + * The member's user presence data + */ + presence: MemberPresence | undefined; + constructor(bot: Bot, member: GatewayStruct, guild: Guild, presence?: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} member The member data + * @param {GatewayStruct} presence The member presence data + * @returns {this} + */ + init(member: GatewayStruct, presence?: GatewayStruct): this; + /** + * Modifies attributes of this member + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modify(options: ModifyMemberOptions): Promise; + /** + * Modifies the nickname of this member + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyNickname(nick: string): Promise; + /** + * Removes this member from the guild + * @returns {Promise} + */ + remove(): Promise; + /** + * Bans this member from the guild, and optionally deletes its previous messages. + * Requires the {@link Permission.BanMembers} permission + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + ban(options: MemberBanOptions): Promise; + /** + * @ignore + * @returns {string | undefined} + */ + toString(): string | undefined; + get voice(): VoiceState; + set voice(val: VoiceState); +} diff --git a/lib/structures/member/Member.js b/lib/structures/member/Member.js new file mode 100644 index 000000000..f96b64798 --- /dev/null +++ b/lib/structures/member/Member.js @@ -0,0 +1,97 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Member = void 0; +const MemberPresence_1 = require("./MemberPresence"); +const member_1 = require("../../controllers/member"); +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +const base_1 = require("../base"); +/** + * Representation of a Discord {@link User} in a guild + * @extends BaseGuildStruct + */ +class Member extends base_1.BaseGuildStruct { + constructor(bot, member, guild, presence) { + super(bot, guild, member); + this.roles = new member_1.MemberRolesController(this); + this.init(member, presence); + } + /** + * @ignore + * @param {GatewayStruct} member The member data + * @param {GatewayStruct} presence The member presence data + * @returns {this} + */ + init(member, presence) { + var _a; + this.id = (_a = member.user) === null || _a === void 0 ? void 0 : _a.id; + this.nick = member.nick; + if (presence) { + this.presence = new MemberPresence_1.MemberPresence(this.bot, presence, this); + this.guild.presences.set(this.id, this.presence); + } + if (member.user) { + if (this.bot.users.cache.has(this.id)) { + // Update the cached user to this member's user + // Store the cached user in this user field + this.user = this.bot.users.cache.get(this.id).init(member.user); + } + else { + // Create a new user instance and cache it + this.user = new User_1.User(this.bot, member.user); + this.bot.users.cache.add(this.user); + } + } + this.roles.cache.merge(this.guild.roles.cache.filter((_role, id) => { var _a; return (_a = member.roles) === null || _a === void 0 ? void 0 : _a.includes(id); })); + this.joinedAt = new Timestamp_1.Timestamp(member.joined_at); + this.boostingSince = member.premium_since ? new Timestamp_1.Timestamp(member.premium_since) : null; + return this; + } + /** + * Modifies attributes of this member + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyMember(this.guild.id, this.id, options); + } + /** + * Modifies the nickname of this member + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyNickname(nick) { + return this.bot.api.modifyMemberNickname(this.guild.id, this.id, nick); + } + /** + * Removes this member from the guild + * @returns {Promise} + */ + remove() { + return this.bot.api.removeMember(this.guild.id, this.id); + } + /** + * Bans this member from the guild, and optionally deletes its previous messages. + * Requires the {@link Permission.BanMembers} permission + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + ban(options) { + return this.bot.api.banMember(this.guild.id, this.id, options); + } + /** + * @ignore + * @returns {string | undefined} + */ + toString() { + var _a; + return (_a = this.user) === null || _a === void 0 ? void 0 : _a.toString(); + } + get voice() { + return this.guild.voiceStates.get(this.id); + } + set voice(val) { + this.guild.voiceStates.set(this.id, val); + } +} +exports.Member = Member; diff --git a/lib/structures/member/MemberPresence.d.ts b/lib/structures/member/MemberPresence.d.ts new file mode 100644 index 000000000..afabe947d --- /dev/null +++ b/lib/structures/member/MemberPresence.d.ts @@ -0,0 +1,229 @@ +import { Member } from './Member'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { Timestamp } from '../Timestamp'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +import { PresenceActivityFlags } from '../flags'; +/** + * The type of the presence activity + */ +export declare enum PresenceActivityType { + Game = 0, + Streaming = 1, + Listening = 2, + Custom = 3 +} +/** + * The timestamps of the presence activity + */ +export interface PresenceActivityTimestamps { + /** + * Unix time in (milliseconds) of when the activity started + */ + start?: number; + /** + * Unix time (in milliseconds) of when the activity ends + */ + end?: number; +} +/** + * The information about the presence emoji for a custom status + */ +export interface PresenceActivityEmoji { + /** + * The name of the emoji + */ + name: string; + /** + * The ID of the emoji + */ + id?: Snowflake; + /** + * Whether this emoji is animated + */ + animated?: boolean; +} +/** + * Information for party showed in a member's presence activity + */ +export interface PresenceActivityParty { + /** + * The ID of the party + */ + id?: string; + /** + * Used to show the party's current and maximum size + * ```typescript + * [current_size, max_size] + * ``` + */ + size?: [number, number]; +} +/** + * Information for the presence's images and their hover texts + */ +export interface PresenceActivityAssets { + /** + * The ID for a large asset of the activity, usually a snowflake + */ + largeImage?: string; + /** + * Text displayed when hovering over the large image of the activity + */ + largeText?: string; + /** + * The ID for a small asset of the activity, usually a snowflake + */ + smallImage?: string; + /** + * Text displayed when hovering over the small image of the activity + */ + smallText?: string; +} +/** + * Presence secrets for Rich Presence joining and spectating + */ +export interface PresenceActivitySecrets { + /** + * The secret for joining a party + */ + join?: string; + /** + * The secret for spectating a game + */ + spectate?: string; + /** + * The secret for a specific instanced match + */ + match?: string; +} +/** + * Object presenting the member's current activity + */ +export interface PresenceGame { + /** + * The activity's name + */ + name: string; + /** + * The activity's type + */ + type: PresenceActivityType; + /** + * Stream URL, is validated when type is {@link PresenceActivityType.Streaming} + */ + url?: string | null; + /** + * Unix timestamp of when the activity was added to the member's session + */ + createdAt: number; + /** + * Unix timestamps for start and/or end of the game + */ + timestamps?: PresenceActivityTimestamps; + /** + * Application ID for the game + */ + applicationId?: Snowflake; + /** + * What the player is currently doing + */ + details?: string | null; + /** + * The member's current party status + */ + state?: string | null; + /** + * The emoji used for a custom status + */ + emoji?: PresenceActivityEmoji; + /** + * Information for the current party of the player + */ + party?: PresenceActivityParty; + /** + * Images for the presence and their hover texts + */ + assets?: PresenceActivityAssets; + /** + * Secrets for Rich Presence joining and spectating + */ + secrets?: PresenceActivitySecrets; + /** + * Whether or not the activity an instanced game session + */ + instance?: boolean; + /** + * Describes what the payload includes + */ + flags?: PresenceActivityFlags; +} +/** + * The presence status of a member + */ +export declare enum PresenceStatus { + Idle = "idle", + DND = "dnd", + Online = "online", + Invisible = "invisible", + Offline = "offline" +} +export declare enum PresenceClientStatusIndicator { + Online = "online", + Idle = "idle", + DND = "dnd" +} +/** + * Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is offline or invisible, the corresponding field is not present. + */ +export interface PresenceClientStatus { + desktop?: PresenceClientStatusIndicator; + mobile?: PresenceClientStatusIndicator; + web?: PresenceClientStatusIndicator; +} +/** + * A member's presence is their current state on a guild + */ +export declare class MemberPresence extends BaseGuildStruct { + /** + * The member associated to this presence + */ + readonly member: Member; + /** + * The member's current activity + */ + game: PresenceGame | undefined; + /** + * The member's current status + */ + status: PresenceStatus; + /** + * The member's current activities + */ + activities: PresenceGame[] | undefined; + /** + * The member's platform-dependent status + */ + clientStatus: PresenceClientStatus; + /** + * When the member started boosting the guild + */ + boostingSince: Timestamp | undefined; + /** + * The member's guild nickname (if one is set) + */ + nick: string | undefined | null; + constructor(bot: Bot, presence: GatewayStruct, member: Member); + /** + * @ignore + * @param {GatewayStruct} presence The presence data + * @returns {this} + */ + init(presence: GatewayStruct): this; + /** + * Builds a {@link PresenceGame} object from a received gateway activity object + * @param {GatewayStruct} activity The gateway activity object + * @returns {PresenceGame} + */ + private static parseActivity; +} diff --git a/lib/structures/member/MemberPresence.js b/lib/structures/member/MemberPresence.js new file mode 100644 index 000000000..94ff46189 --- /dev/null +++ b/lib/structures/member/MemberPresence.js @@ -0,0 +1,92 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MemberPresence = exports.PresenceClientStatusIndicator = exports.PresenceStatus = exports.PresenceActivityType = void 0; +const Timestamp_1 = require("../Timestamp"); +const base_1 = require("../base"); +const flags_1 = require("../flags"); +/** + * The type of the presence activity + */ +var PresenceActivityType; +(function (PresenceActivityType) { + PresenceActivityType[PresenceActivityType["Game"] = 0] = "Game"; + PresenceActivityType[PresenceActivityType["Streaming"] = 1] = "Streaming"; + PresenceActivityType[PresenceActivityType["Listening"] = 2] = "Listening"; + PresenceActivityType[PresenceActivityType["Custom"] = 3] = "Custom"; +})(PresenceActivityType = exports.PresenceActivityType || (exports.PresenceActivityType = {})); +/** + * The presence status of a member + */ +var PresenceStatus; +(function (PresenceStatus) { + PresenceStatus["Idle"] = "idle"; + PresenceStatus["DND"] = "dnd"; + PresenceStatus["Online"] = "online"; + PresenceStatus["Invisible"] = "invisible"; + PresenceStatus["Offline"] = "offline"; +})(PresenceStatus = exports.PresenceStatus || (exports.PresenceStatus = {})); +var PresenceClientStatusIndicator; +(function (PresenceClientStatusIndicator) { + PresenceClientStatusIndicator["Online"] = "online"; + PresenceClientStatusIndicator["Idle"] = "idle"; + PresenceClientStatusIndicator["DND"] = "dnd"; +})(PresenceClientStatusIndicator = exports.PresenceClientStatusIndicator || (exports.PresenceClientStatusIndicator = {})); +/** + * A member's presence is their current state on a guild + */ +class MemberPresence extends base_1.BaseGuildStruct { + constructor(bot, presence, member) { + super(bot, member.guild, presence); + this.member = member; + this.init(presence); + } + /** + * @ignore + * @param {GatewayStruct} presence The presence data + * @returns {this} + */ + init(presence) { + if (presence.game) { + this.game = MemberPresence.parseActivity(presence.game); + } + this.status = presence.status; + if (presence.activities) { + this.activities = presence.activities.map((activity) => MemberPresence.parseActivity(activity)); + } + this.clientStatus = presence.client_status; + if (presence.premium_since) { + this.boostingSince = new Timestamp_1.Timestamp(presence.premium_since); + } + this.nick = presence.nick; + return this; + } + /** + * Builds a {@link PresenceGame} object from a received gateway activity object + * @param {GatewayStruct} activity The gateway activity object + * @returns {PresenceGame} + */ + static parseActivity(activity) { + return { + name: activity.name, + type: activity.type, + url: activity.url, + createdAt: activity.created_at, + timestamps: activity.timestamps, + applicationId: activity.application_id, + details: activity.details, + state: activity.state, + emoji: activity.emoji, + party: activity.party, + assets: activity.assets && { + largeImage: activity.assets.large_image, + largeText: activity.assets.large_text, + smallImage: activity.assets.small_image, + smallText: activity.assets.small_text, + }, + secrets: activity.secrets, + instance: activity.instance, + flags: activity.flags && new flags_1.PresenceActivityFlags(activity.flags), + }; + } +} +exports.MemberPresence = MemberPresence; diff --git a/lib/structures/member/index.d.ts b/lib/structures/member/index.d.ts new file mode 100644 index 000000000..2287e2d58 --- /dev/null +++ b/lib/structures/member/index.d.ts @@ -0,0 +1,2 @@ +export * from './Member'; +export * from './MemberPresence'; diff --git a/lib/structures/member/index.js b/lib/structures/member/index.js new file mode 100644 index 000000000..277d30694 --- /dev/null +++ b/lib/structures/member/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Member"), exports); +__exportStar(require("./MemberPresence"), exports); diff --git a/lib/structures/message/Message.d.ts b/lib/structures/message/Message.d.ts new file mode 100644 index 000000000..0624262b6 --- /dev/null +++ b/lib/structures/message/Message.d.ts @@ -0,0 +1,295 @@ +import { MessageAttachment } from './MessageAttachment'; +import { MessageEmbed, MessageEmbedOptions } from './MessageEmbed'; +import { MessageMentions } from './MessageMentions'; +import Collection from '../../Collection'; +import { RequestFile } from '../../api/rateLimit'; +import { Bot } from '../../bot'; +import { MessageReactionsController } from '../../controllers/message'; +import { Snowflake } from '../../types'; +import { EmojiResolvable } from '../Emoji'; +import { Timestamp } from '../Timestamp'; +import { User } from '../User'; +import { BaseStruct, GatewayStruct } from '../base'; +import { TextBasedChannel } from '../channels'; +import { MessageFlags } from '../flags'; +import { Guild } from '../guild'; +import { Member } from '../member/Member'; +/** + * The type of a message + */ +declare enum MessageType { + Default = 0, + RecipientAdd = 1, + RecipientRemove = 2, + Call = 3, + ChannelNameChange = 4, + ChannelIconChange = 5, + ChannelPinnedMessage = 6, + GuildMemberJoin = 7, + UserPremiumGuildSubscription = 8, + UserPremiumGuildSubscriptionTier1 = 9, + UserPremiumGuildSubscriptionTier2 = 10, + UserPremiumGuildSubscriptionTier3 = 11, + ChannelFollowAdd = 12, + GuildDiscoveryDisqualified = 13, + GuildDiscoveryRequalified = 14 +} +export interface PartialMessage { + id: Snowflake; + guild?: Guild; + channel: TextBasedChannel; +} +/** + * Message activity types + */ +export declare enum MessageActivityType { + Join = 1, + Spectate = 2, + Listen = 3, + JoinRequest = 4 +} +/** + * Information about the message activity + */ +export interface MessageActivity { + /** + * The type of the message activity + */ + type: MessageActivityType; + /** + * The party_id from a Rich Presence event + * https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields + */ + partyId?: string; +} +/** + * Information about the message application + */ +export interface MessageApplication { + /** + * The ID of the application + */ + id: Snowflake; + /** + * The ID of the embed's image asset + */ + coverImage?: string; + /** + * The application's description + */ + description: string; + /** + * The ID of the application's icon + */ + icon: string | null; + /** + * The name of the application + */ + name: string; +} +/** + * Information about the message reference for crossposted messages + */ +export interface MessageReference { + /** + * ID of the originating message + */ + messageId?: Snowflake; + /** + * ID of the originating message's channel + */ + channelId: Snowflake; + /** + * ID of the originating message's guild + */ + guildId?: Snowflake; +} +/** + * The message data for when sending new messages + */ +export interface MessageData { + /** + * The message's raw content + */ + content?: string; + /** + * The message's embed data + */ + embed?: Omit | MessageEmbed; + /** + * The path to a file to send as an attachment + */ + files?: RequestFile[]; +} +/** + * The message options for when sending a new message + */ +export interface MessageOptions { + /** + * A nonce that can be used for identifying the sent message + */ + nonce?: number | string; + /** + * Whether this is a Text To Speech (TTS) message + */ + tts?: boolean; +} +/** + * The message data for when editing a message + */ +export interface MessageEditData extends MessageData { + /** + * The new flags of the message + */ + flags?: MessageFlags; +} +/** + * Represents a message sent in a {@link TextChannel} within Discord + */ +export declare class Message extends BaseStruct { + /** + * The message's ID + */ + id: Snowflake; + /** + * The guild the message was sent in. Possibly null if message was sent over a DM + */ + guild: Guild | undefined; + /** + * The channel the message was sent in + */ + channel: TextBasedChannel; + /** + * The author of this message. + * Might not be a valid {@link User} object if message was generated by a webhook + */ + author: User | undefined; + /** + * The member properties for this message's author. + * Might not exist if message was sent over a DM + */ + member: Member | undefined; + /** + * The content of the message + */ + content: string; + /** + * Timestamp of when this message was sent + */ + sentAt: Timestamp; + /** + * Timestamp of when this message was edited. + * Possibly null if message has not been edited + */ + editedAt: Timestamp | null; + /** + * Whether this was a TTS message + */ + tts: boolean; + /** + * Whether this message mentions everyone + */ + mentionsEveryone: boolean; + /** + * All types of mentionable instances mentioned in this message + */ + mentions: MessageMentions; + /** + * {@link Collection} of all {@link MessageAttachment}s attached to this message + */ + attachments: Collection; + /** + * All embedded content associated to this message + */ + embeds: MessageEmbed[]; + /** + * The message's reactions controller + */ + reactions: MessageReactionsController; + /** + * Used for validating a message was sent + */ + nonce: number | string | undefined; + /** + * Whether this message is pinned + */ + pinned: boolean; + /** + * The Webhook ID in case this message was generated by a Webhook + */ + webhookId: Snowflake | undefined; + /** + * The type of the message + */ + type: MessageType; + /** + * Sent with Rich Presence-related chat embeds + */ + activity: MessageActivity | undefined; + /** + * Sent with Rich Presence-related chat embeds + */ + application: MessageApplication | undefined; + /** + * Reference data sent with crossposted messages + */ + messageReference: MessageReference | undefined; + /** + * Describes extra features of the message + */ + flags: MessageFlags | undefined; + /** + * Whether this message is deleted from its channel + */ + deleted: boolean; + constructor(bot: Bot, message: GatewayStruct, channel: TextBasedChannel); + /** + * @ignore + * @param {GatewayStruct} message The message data + * @returns {this} + */ + init(message: GatewayStruct): this; + /** + * Creates a reaction for this message. + * Requires the {@link Permission.ReadMessageHistory} permission. + * Additionally, if nobody else has reacted to the message using this emoji, this requires the {@link Permission.AddReactions} permission + * @param {EmojiResolvable} emoji The emoji to react to this message with + * @returns {Promise} + */ + react(emoji: EmojiResolvable): Promise; + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + edit(data: string | MessageEditData): Promise; + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete(): Promise; + /** + * Pins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + pin(): Promise; + /** + * Unpins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + unpin(): Promise; +} +export {}; diff --git a/lib/structures/message/Message.js b/lib/structures/message/Message.js new file mode 100644 index 000000000..caa1cfc20 --- /dev/null +++ b/lib/structures/message/Message.js @@ -0,0 +1,174 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Message = exports.MessageActivityType = void 0; +const MessageAttachment_1 = require("./MessageAttachment"); +const MessageEmbed_1 = require("./MessageEmbed"); +const MessageMentions_1 = require("./MessageMentions"); +const MessageReaction_1 = require("./MessageReaction"); +const Collection_1 = __importDefault(require("../../Collection")); +const message_1 = require("../../controllers/message"); +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +const base_1 = require("../base"); +const flags_1 = require("../flags"); +/** + * The type of a message + */ +var MessageType; +(function (MessageType) { + MessageType[MessageType["Default"] = 0] = "Default"; + MessageType[MessageType["RecipientAdd"] = 1] = "RecipientAdd"; + MessageType[MessageType["RecipientRemove"] = 2] = "RecipientRemove"; + MessageType[MessageType["Call"] = 3] = "Call"; + MessageType[MessageType["ChannelNameChange"] = 4] = "ChannelNameChange"; + MessageType[MessageType["ChannelIconChange"] = 5] = "ChannelIconChange"; + MessageType[MessageType["ChannelPinnedMessage"] = 6] = "ChannelPinnedMessage"; + MessageType[MessageType["GuildMemberJoin"] = 7] = "GuildMemberJoin"; + MessageType[MessageType["UserPremiumGuildSubscription"] = 8] = "UserPremiumGuildSubscription"; + MessageType[MessageType["UserPremiumGuildSubscriptionTier1"] = 9] = "UserPremiumGuildSubscriptionTier1"; + MessageType[MessageType["UserPremiumGuildSubscriptionTier2"] = 10] = "UserPremiumGuildSubscriptionTier2"; + MessageType[MessageType["UserPremiumGuildSubscriptionTier3"] = 11] = "UserPremiumGuildSubscriptionTier3"; + MessageType[MessageType["ChannelFollowAdd"] = 12] = "ChannelFollowAdd"; + MessageType[MessageType["GuildDiscoveryDisqualified"] = 13] = "GuildDiscoveryDisqualified"; + MessageType[MessageType["GuildDiscoveryRequalified"] = 14] = "GuildDiscoveryRequalified"; +})(MessageType || (MessageType = {})); +/** + * Message activity types + */ +var MessageActivityType; +(function (MessageActivityType) { + MessageActivityType[MessageActivityType["Join"] = 1] = "Join"; + MessageActivityType[MessageActivityType["Spectate"] = 2] = "Spectate"; + MessageActivityType[MessageActivityType["Listen"] = 3] = "Listen"; + MessageActivityType[MessageActivityType["JoinRequest"] = 4] = "JoinRequest"; +})(MessageActivityType = exports.MessageActivityType || (exports.MessageActivityType = {})); +/** + * Represents a message sent in a {@link TextChannel} within Discord + */ +class Message extends base_1.BaseStruct { + constructor(bot, message, channel) { + super(bot, message); + this.channel = channel; + this.reactions = new message_1.MessageReactionsController(this); + this.deleted = false; + this.init(message); + } + /** + * @ignore + * @param {GatewayStruct} message The message data + * @returns {this} + */ + init(message) { + if (message.guild_id) { + this.guild = this.bot.guilds.cache.get(message.guild_id); + } + this.id = message.id; + this.webhookId = message.webhook_id; + if (!this.webhookId) { + this.author = new User_1.User(this.bot, message.author); + } + this.content = message.content; + this.sentAt = new Timestamp_1.Timestamp(message.timestamp); + this.editedAt = message.edited_timestamp ? new Timestamp_1.Timestamp(message.edited_timestamp) : null; + this.tts = message.tts; + this.mentionsEveryone = message.mention_everyone; + this.mentions = new MessageMentions_1.MessageMentions(this, { + users: message.mentions, + roles: message.mention_roles, + crosspostedChannels: message.mention_channels, + }); + this.attachments = new Collection_1.default(message.attachments.map((attachment) => [ + attachment.id, + new MessageAttachment_1.MessageAttachment(this, attachment), + ])); + this.embeds = message.embeds.map((embed) => new MessageEmbed_1.MessageEmbed(embed)); + if (message.reactions) { + this.reactions.cache.addMany(message.reactions.map((reaction) => new MessageReaction_1.MessageReaction(this, reaction))); + } + this.nonce = message.nonce; + this.pinned = message.pinned; + this.type = message.type; + if (message.activity) { + this.activity = { + type: message.activity.type, + partyId: message.activity.party_id, + }; + } + if (message.application) { + this.application = { + id: message.application.id, + coverImage: message.application.cover_image, + description: message.application.description, + icon: message.application.icon, + name: message.application.name, + }; + } + if (message.message_reference) { + this.messageReference = { + messageId: message.message_reference.message_id, + channelId: message.message_reference.channel_id, + guildId: message.message_reference.guild_id, + }; + } + if (message.flags) { + this.flags = new flags_1.MessageFlags(message.flags); + } + return this; + } + /** + * Creates a reaction for this message. + * Requires the {@link Permission.ReadMessageHistory} permission. + * Additionally, if nobody else has reacted to the message using this emoji, this requires the {@link Permission.AddReactions} permission + * @param {EmojiResolvable} emoji The emoji to react to this message with + * @returns {Promise} + */ + react(emoji) { + return this.bot.api.addMessageReaction(this.channel.id, this.id, emoji); + } + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + edit(data) { + return this.bot.api.editMessage(this.channel.id, this.id, data); + } + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete() { + return this.bot.api.deleteMessage(this.channel.id, this.id); + } + /** + * Pins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + pin() { + return this.bot.api.pinMessage(this.channel.id, this.id); + } + /** + * Unpins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + unpin() { + return this.bot.api.unpinMessage(this.channel.id, this.id); + } +} +exports.Message = Message; diff --git a/lib/structures/message/MessageAttachment.d.ts b/lib/structures/message/MessageAttachment.d.ts new file mode 100644 index 000000000..76d0917c8 --- /dev/null +++ b/lib/structures/message/MessageAttachment.d.ts @@ -0,0 +1,46 @@ +import { Message } from './Message'; +import { Snowflake, Dimensions, Nullable } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * Represents an attachment added to a {@link Message} + + * @extends BaseStruct + */ +export declare class MessageAttachment extends BaseStruct { + /** + * The {@link Message} associated to this attachment + */ + message: Message; + /** + * The attachment's ID + */ + id: Snowflake; + /** + * Name of the attached file + */ + filename: string; + /** + * Size of the attached file in bytes + */ + size: number; + /** + * Source URL of the attached file + */ + url: string; + /** + * A proxies URL of the attached file + */ + proxyURL: string; + /** + * The width and height of the file. + * Possibly null if the attached file is not an image + */ + dimensions: Nullable; + constructor(message: Message, attachment: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} attachment The attachment data + * @returns {this} + */ + init(attachment: GatewayStruct): this; +} diff --git a/lib/structures/message/MessageAttachment.js b/lib/structures/message/MessageAttachment.js new file mode 100644 index 000000000..2d9b84be2 --- /dev/null +++ b/lib/structures/message/MessageAttachment.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageAttachment = void 0; +const base_1 = require("../base"); +/** + * Represents an attachment added to a {@link Message} + + * @extends BaseStruct + */ +class MessageAttachment extends base_1.BaseStruct { + constructor(message, attachment) { + super(message.bot, attachment); + this.message = message; + this.init(attachment); + } + /** + * @ignore + * @param {GatewayStruct} attachment The attachment data + * @returns {this} + */ + init(attachment) { + this.id = attachment.id; + this.filename = attachment.filename; + this.size = attachment.size; + this.url = attachment.url; + this.proxyURL = attachment.proxy_url; + this.dimensions = { + height: attachment.height, + width: attachment.width, + }; + return this; + } +} +exports.MessageAttachment = MessageAttachment; diff --git a/lib/structures/message/MessageEmbed.d.ts b/lib/structures/message/MessageEmbed.d.ts new file mode 100644 index 000000000..dc23ef7f9 --- /dev/null +++ b/lib/structures/message/MessageEmbed.d.ts @@ -0,0 +1,234 @@ +import { Dimensions } from '../../types'; +import { Timestamp } from '../Timestamp'; +import { GatewayStruct } from '../base'; +/** + * Embed types are "loosely defined" and, for the most part, are not used by our clients for rendering. Embed attributes power what is rendered. Embed types should be considered deprecated and might be removed in a future API version. + */ +export declare enum MessageEmbedType { + Rich = "rich", + Image = "image", + Video = "video", + Gifv = "gifv", + Article = "article", + Link = "link" +} +/** + * Message embed footer data + */ +export interface MessageEmbedFooter { + /** + * Footer text + */ + text: string; + /** + * URL of footer icon (only supports http(s) and attachments) + */ + iconURL?: string; + /** + * A proxied URL of footer icon + */ + proxyIconURL?: string; +} +/** + * Message embed image data + */ +export interface MessageEmbedImage { + /** + * Source URL of image (only supports http(s) and attachments) + */ + url?: string; + /** + * A proxied URL of the image + */ + proxyURL?: string; + /** + * {@link Dimensions} object containing the dimensions of the image + */ + dimensions: Partial; +} +/** + * Message embed thumbnail data + */ +export interface MessageEmbedThumbnail { + /** + * Source URL of thumbnail (only supports http(s) and attachments) + */ + url?: string; + /** + * A proxied URL of the thumbnail + */ + proxyURL?: string; + /** + * {@link Dimensions} object containing the dimensions of the thumbnail image + */ + dimensions: Partial; +} +/** + * Message embed video data + */ +export interface MessageEmbedVideo { + /** + * Source URL of the video + */ + url?: string; + /** + * {@link Dimensions} object containing the dimensions of the video + */ + dimensions: Partial; +} +/** + * Message embed provider data + */ +export interface MessageEmbedProvider { + /** + * Name of the provider + */ + name?: string; + /** + * URL of the provider + */ + url?: string; +} +/** + * Message embed author data + */ +export interface MessageEmbedAuthor { + /** + * Name of the author + */ + name?: string; + /** + * URL of the author + */ + url?: string; + /** + * URL of the author's icon (only supports http(s) and attachments) + */ + iconURL?: string; + /** + * A proxied URL of the author's icon + */ + proxyIconURL?: string; +} +/** + * Message embed field + */ +export interface MessageEmbedField { + /** + * Name of the embed field + */ + name: string; + /** + * The text content of the embed field + */ + content: string; + /** + * Whether or not this field should be displayed inline + */ + inline?: boolean; +} +/** + * Options for when creating message embeds + */ +export interface MessageEmbedOptions { + title?: string; + type?: MessageEmbedType; + description?: string; + url?: string; + timestamp?: number | Timestamp; + color?: number; + footer?: MessageEmbedFooter; + image?: MessageEmbedImage; + thumbnail?: MessageEmbedThumbnail; + video?: MessageEmbedVideo; + provider?: MessageEmbedProvider; + author?: MessageEmbedAuthor; + fields?: MessageEmbedField[]; +} +/** + * Represents an embed contained in a {@link Message} + */ +export declare class MessageEmbed implements MessageEmbedOptions { + /** + * The structure used to initialize this MessageEmbed object + * @ignore + */ + structure: GatewayStruct; + /** + * Title of this embed + */ + title: string | undefined; + /** + * Type of this embed (always "rich" for webhook embeds) + */ + type: MessageEmbedType | undefined; + /** + * Description of this embed + */ + description: string | undefined; + /** + * URL of this embed + */ + url: string | undefined; + /** + * Timestamp of this embed's content + */ + timestamp: Timestamp | undefined; + /** + * Color code of the embed + */ + color: number | undefined; + /** + * {@link MessageEmbedFooter} object containing this embed's footer data + */ + footer: MessageEmbedFooter | undefined; + /** + * {@link MessageEmbedImage} object containing this embed's image data + */ + image: MessageEmbedImage | undefined; + /** + * {@link MessageEmbedThumbnail} object containing this embed's thumbnail data + */ + thumbnail: MessageEmbedThumbnail | undefined; + /** + * {@link MessageEmbedVideo} object containing this embed's video data + */ + video: MessageEmbedVideo | undefined; + /** + * {@link MessageEmbedProvider} object containing this embed's provider data + */ + provider: MessageEmbedProvider | undefined; + /** + * {@link MessageEmbedFooter} object containing this embed's author data + */ + author: MessageEmbedAuthor | undefined; + /** + * {@link MessageEmbedField} array containing this embed's fields + */ + fields: MessageEmbedField[] | undefined; + constructor(embed: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} embed The embed data + * @returns {this} + */ + init(embed: GatewayStruct): this; + /** + * Returns the gateway structure of a given embed data + * @param {MessageEmbedOptions} embed The embed data + * @returns {GatewayStruct} + */ + static dataToStructure(embed: MessageEmbedOptions): GatewayStruct; + /** + * Returns the dimensions from a gateway structure + * @param {{height: number, width: number}} struct The gateway structure including the dimensions information + * @returns {Partial} + */ + private static getDimensions; + /** + * Creates a new MessageEmbed object from the given message embed options + * @param {MessageEmbedOptions} options The message embed + * @returns {MessageEmbed} + */ + static from(options: MessageEmbedOptions): MessageEmbed; +} diff --git a/lib/structures/message/MessageEmbed.js b/lib/structures/message/MessageEmbed.js new file mode 100644 index 000000000..ea799d22b --- /dev/null +++ b/lib/structures/message/MessageEmbed.js @@ -0,0 +1,151 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageEmbed = exports.MessageEmbedType = void 0; +const Timestamp_1 = require("../Timestamp"); +/** + * Embed types are "loosely defined" and, for the most part, are not used by our clients for rendering. Embed attributes power what is rendered. Embed types should be considered deprecated and might be removed in a future API version. + */ +var MessageEmbedType; +(function (MessageEmbedType) { + MessageEmbedType["Rich"] = "rich"; + MessageEmbedType["Image"] = "image"; + MessageEmbedType["Video"] = "video"; + MessageEmbedType["Gifv"] = "gifv"; + MessageEmbedType["Article"] = "article"; + MessageEmbedType["Link"] = "link"; +})(MessageEmbedType = exports.MessageEmbedType || (exports.MessageEmbedType = {})); +// TODO: Link this description to a guide page about Discord message embeds +/** + * Represents an embed contained in a {@link Message} + */ +class MessageEmbed { + constructor(embed) { + this.init(embed); + } + /** + * @ignore + * @param {GatewayStruct} embed The embed data + * @returns {this} + */ + init(embed) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; + this.structure = embed; + this.title = embed.title; + this.type = embed.type; + this.description = embed.description; + this.url = embed.url; + this.timestamp = new Timestamp_1.Timestamp(embed.timestamp); + this.color = embed.color; + this.footer = { + text: (_a = embed.footer) === null || _a === void 0 ? void 0 : _a.text, + iconURL: (_b = embed.footer) === null || _b === void 0 ? void 0 : _b.icon_url, + proxyIconURL: (_c = embed.footer) === null || _c === void 0 ? void 0 : _c.proxy_icon_url, + }; + this.image = { + url: (_d = embed.image) === null || _d === void 0 ? void 0 : _d.url, + proxyURL: (_e = embed.image) === null || _e === void 0 ? void 0 : _e.proxy_url, + dimensions: MessageEmbed.getDimensions(embed.image), + }; + this.thumbnail = { + url: (_f = embed.thumbnail) === null || _f === void 0 ? void 0 : _f.url, + proxyURL: (_g = embed.thumbnail) === null || _g === void 0 ? void 0 : _g.proxy_url, + dimensions: MessageEmbed.getDimensions(embed.thumbnail), + }; + this.video = { + url: (_h = embed.video) === null || _h === void 0 ? void 0 : _h.url, + dimensions: MessageEmbed.getDimensions(embed.video), + }; + this.provider = { + name: (_j = embed.video) === null || _j === void 0 ? void 0 : _j.name, + url: (_k = embed.video) === null || _k === void 0 ? void 0 : _k.url, + }; + this.author = { + name: (_l = embed.author) === null || _l === void 0 ? void 0 : _l.name, + url: (_m = embed.author) === null || _m === void 0 ? void 0 : _m.url, + iconURL: (_o = embed.author) === null || _o === void 0 ? void 0 : _o.icon_url, + proxyIconURL: (_p = embed.author) === null || _p === void 0 ? void 0 : _p.proxy_icon_url, + }; + this.fields = (_q = embed.fields) === null || _q === void 0 ? void 0 : _q.map((field) => ({ + name: field.name, + content: field.value, + inline: field.inline, + })); + return this; + } + /** + * Returns the gateway structure of a given embed data + * @param {MessageEmbedOptions} embed The embed data + * @returns {GatewayStruct} + */ + static dataToStructure(embed) { + return { + title: embed.title, + type: embed.type, + description: embed.description, + url: embed.url, + timestamp: embed.timestamp && + (embed.timestamp instanceof Timestamp_1.Timestamp + ? embed.timestamp.date + : new Date(embed.timestamp).toISOString()), + color: embed.color, + footer: embed.footer && { + text: embed.footer.text, + icon_url: embed.footer.iconURL, + proxy_icon_url: embed.footer.proxyIconURL, + }, + image: embed.image && { + url: embed.image.url, + proxy_url: embed.image.proxyURL, + height: embed.image.dimensions.height, + width: embed.image.dimensions.width, + }, + thumbnail: embed.thumbnail && { + url: embed.thumbnail.url, + proxy_url: embed.thumbnail.proxyURL, + height: embed.thumbnail.dimensions.height, + width: embed.thumbnail.dimensions.width, + }, + video: embed.video && { + url: embed.video.url, + height: embed.video.dimensions.height, + width: embed.video.dimensions.width, + }, + provider: embed.provider && { + name: embed.provider.name, + url: embed.provider.url, + }, + author: embed.author && { + name: embed.author.name, + url: embed.author.url, + icon_url: embed.author.iconURL, + proxy_icon_url: embed.author.proxyIconURL, + }, + fields: embed.fields && + embed.fields.map((field) => ({ + name: field.name, + value: field.content, + inline: field.inline, + })), + }; + } + /** + * Returns the dimensions from a gateway structure + * @param {{height: number, width: number}} struct The gateway structure including the dimensions information + * @returns {Partial} + */ + static getDimensions(struct) { + return { + height: struct === null || struct === void 0 ? void 0 : struct.height, + width: struct === null || struct === void 0 ? void 0 : struct.width, + }; + } + /** + * Creates a new MessageEmbed object from the given message embed options + * @param {MessageEmbedOptions} options The message embed + * @returns {MessageEmbed} + */ + static from(options) { + return new MessageEmbed(MessageEmbed.dataToStructure(options)); + } +} +exports.MessageEmbed = MessageEmbed; diff --git a/lib/structures/message/MessageMentions.d.ts b/lib/structures/message/MessageMentions.d.ts new file mode 100644 index 000000000..e46d25476 --- /dev/null +++ b/lib/structures/message/MessageMentions.d.ts @@ -0,0 +1,54 @@ +import { Message } from './Message'; +import Collection from '../../Collection'; +import { Snowflake } from '../../types'; +import { Role } from '../Role'; +import { User } from '../User'; +import { BaseStruct, GatewayStruct } from '../base'; +import { GuildChannel } from '../channels'; +import { Member } from '../member/Member'; +interface MentionTypes { + users: GatewayStruct[]; + roles: Snowflake[]; + crosspostedChannels: GatewayStruct[]; +} +/** + * Represents all mentions (members, roles, channels) that appear in a {@link Message} + + * @extends BaseStruct + */ +export declare class MessageMentions extends BaseStruct { + /** + * The {@link Message} associated to these mentions + */ + message: Message; + /** + * {@link Collection} of all {@link User}s mentioned in this message + */ + users: Collection; + /** + * {@link Collection} of all {@link Member}s mentioned in this message + */ + members: Collection; + /** + * {@link Collection} of all {@link Role}s mentioned in this message + */ + roles: Collection; + crosspostedChannels: Collection; + /** + * Cache for all channel mentions found in the message + */ + private _channels; + constructor(message: Message, mentions: Partial); + /** + * @ignore + * @param {GatewayStruct} mentions The message mentions data + * @returns {this} + */ + init(mentions: Partial): this; + /** + * Fetches or retrieves from cache all channels mentioned in the message + * @type {Collection | undefined} + */ + get channels(): Collection | undefined; +} +export {}; diff --git a/lib/structures/message/MessageMentions.js b/lib/structures/message/MessageMentions.js new file mode 100644 index 000000000..a6aa42dc6 --- /dev/null +++ b/lib/structures/message/MessageMentions.js @@ -0,0 +1,75 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageMentions = void 0; +const Collection_1 = __importDefault(require("../../Collection")); +const User_1 = require("../User"); +const base_1 = require("../base"); +const Member_1 = require("../member/Member"); +const mentionsRegexp = { + channels: new RegExp(/<#(\d{17,19})>/g), +}; +/** + * Represents all mentions (members, roles, channels) that appear in a {@link Message} + + * @extends BaseStruct + */ +class MessageMentions extends base_1.BaseStruct { + constructor(message, mentions) { + super(message.bot, mentions); + this.message = message; + this.users = new Collection_1.default(); + this.members = new Collection_1.default(); + this.roles = new Collection_1.default(); + this.crosspostedChannels = new Collection_1.default(); + this.init(mentions); + } + /** + * @ignore + * @param {GatewayStruct} mentions The message mentions data + * @returns {this} + */ + init(mentions) { + if (mentions.users) { + this.users.merge(mentions.users.map(user => [user.id, new User_1.User(this.bot, user)])); + } + // We have to use mentions.users to obtain the members + if (mentions.users && this.message.guild) { + this.members.merge(mentions.users + .filter(user => user.member) + .map(user => [ + user.id, + new Member_1.Member(this.bot, Object.assign(Object.assign({}, user.member), { user }), this.message.guild), + ])); + } + if (mentions.roles && this.message.guild) { + this.roles.merge(this.message.guild.roles.cache.filter(role => mentions.roles.includes(role.id))); + } + if (mentions.crosspostedChannels && this.message.guild) { + this.crosspostedChannels.merge(this.message.guild.channels.cache.filter(channel => mentions.crosspostedChannels.some(mention => mention.guild_id === channel.guild.id && mention.id === channel.id))); + } + return this; + } + /** + * Fetches or retrieves from cache all channels mentioned in the message + * @type {Collection | undefined} + */ + get channels() { + if (this._channels) + return this._channels; + if (this.message.guild) { + this._channels = new Collection_1.default(); + let matches; + while ((matches = mentionsRegexp.channels.exec(this.message.content))) { + const channel = this.message.guild.channels.cache.get(matches[1]); + if (channel) { + this._channels.set(channel.id, channel); + } + } + return this._channels; + } + } +} +exports.MessageMentions = MessageMentions; diff --git a/lib/structures/message/MessageReaction.d.ts b/lib/structures/message/MessageReaction.d.ts new file mode 100644 index 000000000..da4733f42 --- /dev/null +++ b/lib/structures/message/MessageReaction.d.ts @@ -0,0 +1,55 @@ +import { Message } from './Message'; +import Collection from '../../Collection'; +import { ReactionUsersController } from '../../controllers/reaction'; +import { Snowflake } from '../../types'; +import { Emoji } from '../Emoji'; +import { BaseStruct, GatewayStruct } from '../base'; +import { Member } from '../member/Member'; +/** + * Holds all users that reacted to a {@link Message} with a specific {@link Emoji} + */ +export declare class MessageReaction extends BaseStruct { + /** + * The message this reaction is attached to + */ + message: Message; + /** + * The times this emoji has been used to react + */ + count: number; + /** + * Whether the bot reacted using this emoji + */ + botReacted: boolean | undefined; + /** + * The reaction's users controller + */ + users: ReactionUsersController; + /** + * The members that added this reaction + */ + members: Collection; + /** + * The emoji this reaction used. This emoji is partial + */ + emoji: Emoji; + constructor(message: Message, reaction: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} reaction The reaction data + * @returns {this} + */ + init(reaction: GatewayStruct): this; + /** + * Deletes all reactions for this emoji. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete(): Promise; + /** + * The ID of the emoji this reaction stores + * Serves as an identifier for this reaction + * @type {string} + */ + get id(): string; +} diff --git a/lib/structures/message/MessageReaction.js b/lib/structures/message/MessageReaction.js new file mode 100644 index 000000000..72410ac58 --- /dev/null +++ b/lib/structures/message/MessageReaction.js @@ -0,0 +1,51 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageReaction = void 0; +const Collection_1 = __importDefault(require("../../Collection")); +const reaction_1 = require("../../controllers/reaction"); +const Emoji_1 = require("../Emoji"); +const base_1 = require("../base"); +/** + * Holds all users that reacted to a {@link Message} with a specific {@link Emoji} + */ +class MessageReaction extends base_1.BaseStruct { + constructor(message, reaction) { + super(message.bot, reaction); + this.message = message; + this.users = new reaction_1.ReactionUsersController(this); + this.members = new Collection_1.default(); + this.emoji = new Emoji_1.Emoji(this.bot, reaction.emoji); + this.init(reaction); + } + /** + * @ignore + * @param {GatewayStruct} reaction The reaction data + * @returns {this} + */ + init(reaction) { + this.count = reaction.count || 0; + this.botReacted = reaction.me; + this.emoji = new Emoji_1.Emoji(this.bot, reaction.emoji); + return this; + } + /** + * Deletes all reactions for this emoji. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete() { + return this.bot.api.removeMessageReactionsEmoji(this.message.channel.id, this.message.id, this.emoji); + } + /** + * The ID of the emoji this reaction stores + * Serves as an identifier for this reaction + * @type {string} + */ + get id() { + return this.emoji.id; + } +} +exports.MessageReaction = MessageReaction; diff --git a/lib/structures/message/index.d.ts b/lib/structures/message/index.d.ts new file mode 100644 index 000000000..a33bee20a --- /dev/null +++ b/lib/structures/message/index.d.ts @@ -0,0 +1,5 @@ +export * from './Message'; +export * from './MessageAttachment'; +export * from './MessageEmbed'; +export * from './MessageMentions'; +export * from './MessageReaction'; diff --git a/lib/structures/message/index.js b/lib/structures/message/index.js new file mode 100644 index 000000000..42b822a21 --- /dev/null +++ b/lib/structures/message/index.js @@ -0,0 +1,17 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Message"), exports); +__exportStar(require("./MessageAttachment"), exports); +__exportStar(require("./MessageEmbed"), exports); +__exportStar(require("./MessageMentions"), exports); +__exportStar(require("./MessageReaction"), exports); diff --git a/lib/structures/voice/Connection.d.ts b/lib/structures/voice/Connection.d.ts new file mode 100644 index 000000000..d5edde41d --- /dev/null +++ b/lib/structures/voice/Connection.d.ts @@ -0,0 +1,20 @@ +import GuildVoice from './GuildVoice'; +import UDPSocket from './UDPSocket'; +import VoiceWebSocket from './VoiceWebSocket'; +export default class Connection { + sockets: { + ws: VoiceWebSocket; + udp: UDPSocket; + }; + token: string; + active: boolean; + private _endpoint; + /** + * The endpoint voice websocket is going to connect + * @type {string} + */ + set endpoint(val: string); + get endpoint(): string; + voice: GuildVoice; + constructor(voice: GuildVoice); +} diff --git a/lib/structures/voice/Connection.js b/lib/structures/voice/Connection.js new file mode 100644 index 000000000..10e0e1c7c --- /dev/null +++ b/lib/structures/voice/Connection.js @@ -0,0 +1,41 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const UDPSocket_1 = __importDefault(require("./UDPSocket")); +const VoiceWebSocket_1 = __importDefault(require("./VoiceWebSocket")); +class Connection { + constructor(voice) { + this.active = false; + this._endpoint = ''; + this.voice = voice; + } + /** + * The endpoint voice websocket is going to connect + * @type {string} + */ + set endpoint(val) { + if (this._endpoint === val) + return; + else { + if (this.sockets.ws) { + this.sockets.ws.close(); + this.sockets.ws.removeAllListeners(); + } + if (this.sockets.udp) { + this.sockets.udp.close(); + this.sockets.udp.removeAllListeners(); + } + this.active = true; + this.sockets = { + ws: new VoiceWebSocket_1.default(this), + udp: new UDPSocket_1.default(this), + }; + } + } + get endpoint() { + return this._endpoint; + } +} +exports.default = Connection; diff --git a/lib/structures/voice/GuildVoice.d.ts b/lib/structures/voice/GuildVoice.d.ts new file mode 100644 index 000000000..b30d992b0 --- /dev/null +++ b/lib/structures/voice/GuildVoice.d.ts @@ -0,0 +1,24 @@ +import Connection from './Connection'; +import { Guild } from '..'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +/** + * Represents a voice connection of a guild + */ +export default class GuildVoice { + /** + * The {@link Bot} operating this structure + */ + bot: Bot; + /** + * The {@link Guild} that this voice connection belongs + * @param guild Guild + */ + guild: Guild; + connection: Connection; + constructor(guild: Guild); + join(channelId: Snowflake, options: { + mute?: boolean; + deaf?: boolean; + }): Promise; +} diff --git a/lib/structures/voice/GuildVoice.js b/lib/structures/voice/GuildVoice.js new file mode 100644 index 000000000..32226f2b0 --- /dev/null +++ b/lib/structures/voice/GuildVoice.js @@ -0,0 +1,40 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Connection_1 = __importDefault(require("./Connection")); +const socket_1 = require("../../socket"); +/** + * Represents a voice connection of a guild + */ +class GuildVoice { + constructor(guild) { + this.connection = new Connection_1.default(this); + this.guild = guild; + this.bot = guild.bot; + } + join(channelId, options) { + this.guild.shard.send({ + op: 2, + d: { + guild_id: this.guild.id, + channel_id: channelId, + self_mute: !!options.mute, + self_deaf: !!options.deaf, + }, + }); + return new Promise(resolve => { + const listener = ((guild, voiceServer) => { + if (guild.id === this.guild.id) { + this.connection.endpoint = voiceServer.endpoint; + this.connection.token = voiceServer.token; + this.bot.events.removeListener(socket_1.BotEvent.VoiceServerUpdate, listener); + resolve(this.connection); + } + }).bind(this); + this.bot.events.on(socket_1.BotEvent.VoiceServerUpdate, listener); + }); + } +} +exports.default = GuildVoice; diff --git a/lib/structures/voice/UDPSocket.d.ts b/lib/structures/voice/UDPSocket.d.ts new file mode 100644 index 000000000..31a56d97c --- /dev/null +++ b/lib/structures/voice/UDPSocket.d.ts @@ -0,0 +1,34 @@ +/// +import { Socket } from 'dgram'; +import { EventEmitter } from 'events'; +import { Readable } from 'stream'; +import Connection from './Connection'; +export default class UDPSocket extends EventEmitter { + connection: Connection; + socket: Socket; + private auth?; + private nonce; + secretKeys: Buffer; + /** + * PCM Raw + */ + PCMOut: Readable; + /** + * Opus Encoded + */ + OpusOut: Readable; + private OpusEncoder; + constructor(connection: Connection); + discoverIP(server: { + ip: string; + port: number; + ssrc: number; + }): Promise<{ + ip: string; + port: number; + }>; + start(): void; + stop(): void; + decryptPackage(buffer: Buffer): Buffer | Error; + close(): void; +} diff --git a/lib/structures/voice/UDPSocket.js b/lib/structures/voice/UDPSocket.js new file mode 100644 index 000000000..88069ef26 --- /dev/null +++ b/lib/structures/voice/UDPSocket.js @@ -0,0 +1,109 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const dgram_1 = require("dgram"); +const events_1 = require("events"); +const stream_1 = require("stream"); +const socket_1 = require("../../socket"); +let LibSodium; +let OpusScript; +try { + LibSodium = require("sodium-native"); //eslint-disable-line +} +catch (err) { } //eslint-disable-line +try { + OpusScript = require("opusscript").OpusScript; //eslint-disable-line +} +catch (err) { } //eslint-disable-line +class UDPSocket extends events_1.EventEmitter { + constructor(connection) { + super(); + this.nonce = Buffer.alloc(24); + /** + * PCM Raw + */ + this.PCMOut = new stream_1.Readable(); + /** + * Opus Encoded + */ + this.OpusOut = new stream_1.Readable(); + this.connection = connection; + this.socket = dgram_1.createSocket('udp4'); + } + // This method should be called before the udp connection started! + discoverIP(server) { + return __awaiter(this, void 0, void 0, function* () { + this.auth = server; + return new Promise(resolve => { + const message = Buffer.alloc(70); + message.writeUIntBE(this.auth.ssrc, 0, 4); + this.socket.send(message, 0, message.length, this.auth.port, this.auth.ip); + this.socket.once('message', message => { + const local = { ip: '', port: 0 }; + for (let i = 4; i < message.indexOf(0, i); i++) { + local.ip += String.fromCharCode(message[i]); + } + local.port = parseInt(message.readUIntBE(message.length - 2, 2).toString(10)); + resolve(local); + }); + }); + }); + } + start() { + if (!OpusScript) + throw new Error('OpusScript not found!'); + if (!LibSodium) + throw new Error('LibSodium not found!'); + if (!this.OpusEncoder) { + this.OpusEncoder = new OpusScript(OpusScript.VALID_SAMPLING_RATES[4], 2, OpusScript.Application.AUDIO); + this.OpusEncoder.encoderCTL(4002, 64000); + } + this.socket.on('message', message => { + const data = this.decryptPackage(message); + if (data instanceof Error) + return this.connection.voice.bot.events.emit(socket_1.BotEvent.Debug, data); + this.OpusOut.push(data); + this.PCMOut.push(this.OpusEncoder.decode(data)); + }); + } + stop() { + this.socket.removeAllListeners(); + } + decryptPackage(buffer) { + buffer.copy(this.nonce, 0, 0, 12); + const nonce = Buffer.alloc(24); + buffer.copy(nonce, 0, 0, 12); + let data; + data = Buffer.allocUnsafe(buffer.length - 12 - LibSodium.crypto_secretbox_MACBYTES); + try { + LibSodium.crypto_secretbox_open_easy(data, buffer.slice(12), this.nonce, Buffer.from(this.secretKeys)); + } + catch (err) { + return err; + } + if ((buffer[0] & 0b1111) > 0) { + data = data.slice(buffer[0] & (0b1111 * 4)); + } + if (buffer[0] & 0b10000) { + const l = (data[2] << 8) | data[3]; + let index = 4 + l * 4; + while (data[index] == 0) { + ++index; + } + data = data.slice(index); + } + return data; + } + close() { + this.socket.close(); + } +} +exports.default = UDPSocket; diff --git a/lib/structures/voice/VoiceHeartbeats.d.ts b/lib/structures/voice/VoiceHeartbeats.d.ts new file mode 100644 index 000000000..ca4478c16 --- /dev/null +++ b/lib/structures/voice/VoiceHeartbeats.d.ts @@ -0,0 +1,38 @@ +/// +import VoiceWebSocket from './VoiceWebSocket'; +interface HeartbeatInterval { + timeout: number; + executor?: NodeJS.Timeout; +} +/** + * Handles the sending and receiving of Discord heartbeats + */ +export default class VoiceHeartbeats { + private readonly ws; + private readonly sequence; + private acked; + interval: HeartbeatInterval; + constructor(ws: VoiceWebSocket); + /** + * Starts the heartbeat interval + */ + start(): void; + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat(): void; + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat(): void; + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + private get heartbeatData(); + /** + * Called when a heartbeat is acked + */ + receivedAck(): void; +} +export {}; diff --git a/lib/structures/voice/VoiceHeartbeats.js b/lib/structures/voice/VoiceHeartbeats.js new file mode 100644 index 000000000..77e92ba22 --- /dev/null +++ b/lib/structures/voice/VoiceHeartbeats.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Handles the sending and receiving of Discord heartbeats + */ +class VoiceHeartbeats { + constructor(ws) { + this.ws = ws; + this.sequence = ws.sequnce; + this.acked = true; + this.interval = { + timeout: 0, + }; + } + /** + * Starts the heartbeat interval + */ + start() { + this.interval.executor = setInterval(this.sendHeartbeat.bind(this), this.interval.timeout); + } + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat() { + if (!this.acked) + return; // Instead of reconnecting to the voice ws it just skips this heartbeat. + this.acked = false; + this.ws.send(this.heartbeatData); + } + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat() { + this.interval.timeout = -1; + if (this.interval.executor) { + clearInterval(this.interval.executor); + } + } + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + get heartbeatData() { + return { op: 3, d: this.interval.timeout }; + } + /** + * Called when a heartbeat is acked + */ + receivedAck() { + this.acked = true; + } +} +exports.default = VoiceHeartbeats; diff --git a/lib/structures/voice/VoiceState.d.ts b/lib/structures/voice/VoiceState.d.ts new file mode 100644 index 000000000..28d8cc770 --- /dev/null +++ b/lib/structures/voice/VoiceState.d.ts @@ -0,0 +1,35 @@ +import { Bot } from '../../bot'; +import { Nullable, Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +import GuildVoiceChannel from '../channels/GuildVoiceChannel'; +import { Member } from '../member'; +interface VoiceStateData { + guild_id: Snowflake; + channel_id: Nullable; + user_id: Snowflake; + session_id: string; + deaf: boolean; + mute: boolean; + self_deaf: boolean; + self_mute: boolean; + self_stream: boolean; + self_video: boolean; + suppress: boolean; +} +declare enum MUTE_STATE { + SELF = 0, + FORCE = 1, + NONE = 2 +} +export default class VoiceState extends BaseStruct { + private channelId; + sessionId: string; + deafen: MUTE_STATE; + muted: MUTE_STATE; + member: Member; + static MUTE_STATE: typeof MUTE_STATE; + constructor(bot: Bot, member: Member, voiceState: GatewayStruct); + init(voiceState: VoiceStateData): this; + get currentChannel(): Nullable; +} +export {}; diff --git a/lib/structures/voice/VoiceState.js b/lib/structures/voice/VoiceState.js new file mode 100644 index 000000000..7410d92a2 --- /dev/null +++ b/lib/structures/voice/VoiceState.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const base_1 = require("../base"); +var MUTE_STATE; +(function (MUTE_STATE) { + MUTE_STATE[MUTE_STATE["SELF"] = 0] = "SELF"; + MUTE_STATE[MUTE_STATE["FORCE"] = 1] = "FORCE"; + MUTE_STATE[MUTE_STATE["NONE"] = 2] = "NONE"; +})(MUTE_STATE || (MUTE_STATE = {})); +class VoiceState extends base_1.BaseStruct { + constructor(bot, member, voiceState) { + super(bot, voiceState); + this.member = member; + this.init(voiceState); + } + init(voiceState) { + if (voiceState.self_mute) + this.muted = MUTE_STATE.SELF; + else if (voiceState.mute) + this.muted = MUTE_STATE.FORCE; + else + this.muted = MUTE_STATE.NONE; + if (voiceState.self_deaf) + this.deafen = MUTE_STATE.SELF; + else if (voiceState.deaf) + this.deafen = MUTE_STATE.FORCE; + else + this.deafen = MUTE_STATE.NONE; + this.channelId = voiceState.channel_id; + return this; + } + get currentChannel() { + const channel = this.bot.channels.cache.get(this.channelId); + return channel ? channel : null; + } +} +exports.default = VoiceState; +VoiceState.MUTE_STATE = MUTE_STATE; diff --git a/lib/structures/voice/VoiceWebSocket.d.ts b/lib/structures/voice/VoiceWebSocket.d.ts new file mode 100644 index 000000000..3aa89ca0e --- /dev/null +++ b/lib/structures/voice/VoiceWebSocket.d.ts @@ -0,0 +1,41 @@ +/// +import { EventEmitter } from 'events'; +import Connection from './Connection'; +import { PayloadData } from '../../socket'; +export declare enum VOICE_OPCODES { + IDENTIFY = 0, + SELECT_PROTOCOL = 1, + READY = 2, + HEARTBEAT = 3, + SESSION_DESCRIPTION = 4, + SPEAKING = 5, + HEARTBEAT_ACK = 6, + RESUME = 7, + HELLO = 8, + RESUMED = 9, + DISCONNECT = 13 +} +/** + * A payload thats gonna be sent to the Discord Voice Server + */ +export interface VoiceCommand { + op: VOICE_OPCODES; + d: PayloadData; +} +/** + * A payload received from the Discord Voice Server gateway + */ +export interface VoicePayload extends VoiceCommand { + s: number; +} +export default class VoiceWebSocket extends EventEmitter { + connection: Connection; + private ws?; + private hearbeat?; + sequnce: number; + constructor(connection: Connection); + open(): void; + private onMessage; + send(data: VoiceCommand): void; + close(): void; +} diff --git a/lib/structures/voice/VoiceWebSocket.js b/lib/structures/voice/VoiceWebSocket.js new file mode 100644 index 000000000..8644342b7 --- /dev/null +++ b/lib/structures/voice/VoiceWebSocket.js @@ -0,0 +1,99 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VOICE_OPCODES = void 0; +const events_1 = require("events"); +const ws_1 = __importDefault(require("ws")); +const VoiceHeartbeats_1 = __importDefault(require("./VoiceHeartbeats")); +var VOICE_OPCODES; +(function (VOICE_OPCODES) { + VOICE_OPCODES[VOICE_OPCODES["IDENTIFY"] = 0] = "IDENTIFY"; + VOICE_OPCODES[VOICE_OPCODES["SELECT_PROTOCOL"] = 1] = "SELECT_PROTOCOL"; + VOICE_OPCODES[VOICE_OPCODES["READY"] = 2] = "READY"; + VOICE_OPCODES[VOICE_OPCODES["HEARTBEAT"] = 3] = "HEARTBEAT"; + VOICE_OPCODES[VOICE_OPCODES["SESSION_DESCRIPTION"] = 4] = "SESSION_DESCRIPTION"; + VOICE_OPCODES[VOICE_OPCODES["SPEAKING"] = 5] = "SPEAKING"; + VOICE_OPCODES[VOICE_OPCODES["HEARTBEAT_ACK"] = 6] = "HEARTBEAT_ACK"; + VOICE_OPCODES[VOICE_OPCODES["RESUME"] = 7] = "RESUME"; + VOICE_OPCODES[VOICE_OPCODES["HELLO"] = 8] = "HELLO"; + VOICE_OPCODES[VOICE_OPCODES["RESUMED"] = 9] = "RESUMED"; + VOICE_OPCODES[VOICE_OPCODES["DISCONNECT"] = 13] = "DISCONNECT"; +})(VOICE_OPCODES = exports.VOICE_OPCODES || (exports.VOICE_OPCODES = {})); +class VoiceWebSocket extends events_1.EventEmitter { + constructor(connection) { + super(); + this.connection = connection; + } + open() { + if (!this.connection.endpoint) + throw new Error('A voice gateway connection was tried to be established before endpoint declaration.'); + this.ws = new ws_1.default(`wss://${this.connection.endpoint.split(':')[0]}/?v=4`); + this.ws.on('message', this.onMessage.bind(this)); + this.hearbeat = new VoiceHeartbeats_1.default(this); + this.send({ + op: VOICE_OPCODES.IDENTIFY, + d: { + server_id: this.connection.voice.guild.id, + user_id: this.connection.voice.bot.user.id, + session_id: this.connection.voice.guild.shard.sessionId, + token: this.connection.token, + }, + }); + } + onMessage(message) { + return __awaiter(this, void 0, void 0, function* () { + this.sequnce = message.s; + switch (message.op) { + case VOICE_OPCODES.HELLO: { + this.hearbeat.interval.timeout = message.d.heartbeat_interval; + this.hearbeat.start(); + break; + } + case VOICE_OPCODES.READY: { + const ip = yield this.connection.sockets.udp.discoverIP({ + ip: message.d.ip, + port: message.d.port, + ssrc: message.d.ssrc, + }); + this.send({ + op: VOICE_OPCODES.SELECT_PROTOCOL, + d: { + protocol: 'udp', + data: { + address: ip.ip, + port: ip.port, + mode: 'xsalsa20_poly1305', + }, + }, + }); + break; + } + case VOICE_OPCODES.SESSION_DESCRIPTION: { + this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys); + break; + } + } + }); + } + send(data) { + if (this.ws) + this.ws.send(JSON.stringify(data)); + } + close() { + var _a, _b; + (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close(); + (_b = this.hearbeat) === null || _b === void 0 ? void 0 : _b.stopHeartbeat(); + } +} +exports.default = VoiceWebSocket; diff --git a/lib/structures/voice/index.d.ts b/lib/structures/voice/index.d.ts new file mode 100644 index 000000000..3babae3d0 --- /dev/null +++ b/lib/structures/voice/index.d.ts @@ -0,0 +1,6 @@ +export * from './Connection'; +export * from './GuildVoice'; +export * from './UDPSocket'; +export * from './VoiceHeartbeats'; +export * from './VoiceState'; +export * from './VoiceWebSocket'; diff --git a/lib/structures/voice/index.js b/lib/structures/voice/index.js new file mode 100644 index 000000000..bbb6c6e29 --- /dev/null +++ b/lib/structures/voice/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Connection"), exports); +__exportStar(require("./GuildVoice"), exports); +__exportStar(require("./UDPSocket"), exports); +__exportStar(require("./VoiceHeartbeats"), exports); +__exportStar(require("./VoiceState"), exports); +__exportStar(require("./VoiceWebSocket"), exports); diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts new file mode 100644 index 000000000..fcb073fef --- /dev/null +++ b/lib/types/index.d.ts @@ -0,0 +1 @@ +export * from './types'; diff --git a/lib/types/index.js b/lib/types/index.js new file mode 100644 index 000000000..a022a97e2 --- /dev/null +++ b/lib/types/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./types"), exports); diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts new file mode 100644 index 000000000..84e086bb4 --- /dev/null +++ b/lib/types/types.d.ts @@ -0,0 +1,19 @@ +/** + * Discord Snowflake. + * More information can be found here {@link https://discordapp.com/developers/docs/reference#snowflakes} + */ +export declare type Snowflake = string; +export declare type ShardId = number; +/** + * Turns all fields of a given type to be nullable + */ +export declare type Nullable = { + [P in keyof T]: T[P] | null; +} | null; +/** + * Width and height dimensions. Mostly used for images and/or videos + */ +export interface Dimensions { + height: number; + width: number; +} diff --git a/lib/types/types.js b/lib/types/types.js new file mode 100644 index 000000000..c8ad2e549 --- /dev/null +++ b/lib/types/types.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); From 47ab91504cf7f14c19ac15cffd9e062db0ed7d7e Mon Sep 17 00:00:00 2001 From: Sardonyx Date: Tue, 1 Sep 2020 03:15:28 +0300 Subject: [PATCH 13/48] build(Npm): removed prepare script since its already compiled it wont need to be recompiled there. --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index f4afd8d1c..3583d96ad 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "lint": "eslint src --ext .ts --ext .js", "lint:fix": "eslint --fix src --ext .ts --ext .js", "docs:generate": "typedoc --options typedoc.json", - "prepare": "npm run build", "prepublishOnly": "npm test && npm run lint", "preversion": "npm run lint", "version": "npm run lint:fix && git add -A src", From 57a1ae94a3ff35586ec1c0b1ed971e1aff309012 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 03:34:30 +0300 Subject: [PATCH 14/48] fix(Voice): GuildVoiceChannels are now stored in the channels. --- src/controllers/guild/GuildChannelsController.ts | 16 ++++++++++++++++ src/structures/channels/GuildVoiceChannel.ts | 7 ++++--- src/structures/channels/utils/ChannelUtils.ts | 3 +++ src/structures/guild/Guild.ts | 4 ++-- src/structures/voice/Connection.ts | 8 ++++---- src/structures/voice/GuildVoice.ts | 4 ++-- src/structures/voice/UDPSocket.ts | 4 ++-- src/structures/voice/VoiceHeartbeats.ts | 4 ++-- src/structures/voice/VoiceState.ts | 4 ++-- src/structures/voice/VoiceWebSocket.ts | 6 +++--- 10 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/controllers/guild/GuildChannelsController.ts b/src/controllers/guild/GuildChannelsController.ts index 6033f3ca1..584f7c1b1 100644 --- a/src/controllers/guild/GuildChannelsController.ts +++ b/src/controllers/guild/GuildChannelsController.ts @@ -4,6 +4,7 @@ import { GuildChannel, CreateGuildChannelOptions, GuildTextChannel, + GuildVoiceChannel, } from '../../structures/channels'; import { Guild } from '../../structures/guild'; import { Snowflake } from '../../types'; @@ -49,6 +50,21 @@ export class GuildChannelsController extends BaseFetchController return channel; } + /** + * Gets or fetches a guild voice channel by its ID + * @param {Snowflake} id The ID of the guild voice channel + * @returns {Promise} + */ + public async getVoice(id: Snowflake): Promise { + const channel = await this.get(id); + + if (!(channel instanceof GuildVoiceChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + + return channel; + } + /** * Creates a new guild channel in the guild associated to this controller. * Requires the {@link Permission.ManageChannels} diff --git a/src/structures/channels/GuildVoiceChannel.ts b/src/structures/channels/GuildVoiceChannel.ts index 162ab2885..95d57eae4 100644 --- a/src/structures/channels/GuildVoiceChannel.ts +++ b/src/structures/channels/GuildVoiceChannel.ts @@ -1,12 +1,13 @@ -import { Guild, GuildChannel } from '..'; +import { GuildChannel } from './GuildChannel'; import { Bot } from '../../bot'; import { GatewayStruct } from '../base'; -import Connection from '../voice/Connection'; +import { Guild } from '../guild/Guild'; +import { Connection } from '../voice/Connection'; /** * Represents a Voice channel */ -export default class GuildVoiceChannel extends GuildChannel { +export class GuildVoiceChannel extends GuildChannel { constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild) { super(bot, guildChannel, guild); } diff --git a/src/structures/channels/utils/ChannelUtils.ts b/src/structures/channels/utils/ChannelUtils.ts index 6bee231ec..0a78dc660 100644 --- a/src/structures/channels/utils/ChannelUtils.ts +++ b/src/structures/channels/utils/ChannelUtils.ts @@ -6,6 +6,7 @@ import { DMChannel } from '../DMChannel'; import { GuildCategoryChannel } from '../GuildCategoryChannel'; import { GuildChannel } from '../GuildChannel'; import { GuildTextChannel } from '../GuildTextChannel'; +import { GuildVoiceChannel } from '../GuildVoiceChannel'; /** * Handles channel-related util methods @@ -46,6 +47,8 @@ export class ChannelUtils { channel = new GuildCategoryChannel(bot, data, guild); break; case ChannelType.GuildVoice: + channel = new GuildVoiceChannel(bot, data, guild); + break; case ChannelType.GuildNews: case ChannelType.GuildStore: channel = new GuildChannel(bot, data, guild); diff --git a/src/structures/guild/Guild.ts b/src/structures/guild/Guild.ts index 4c272cb98..c9c647c73 100644 --- a/src/structures/guild/Guild.ts +++ b/src/structures/guild/Guild.ts @@ -26,8 +26,8 @@ import { GuildChannel, GuildTextChannel } from '../channels'; import { ChannelUtils } from '../channels/utils'; import { GuildSystemChannelFlags, PermissionFlags } from '../flags'; import { Member, MemberPresence } from '../member'; -import GuildVoice from '../voice/GuildVoice'; -import VoiceState from '../voice/VoiceState'; +import { GuildVoice } from '../voice/GuildVoice'; +import { VoiceState } from '../voice/VoiceState'; /** * Guild verification levels diff --git a/src/structures/voice/Connection.ts b/src/structures/voice/Connection.ts index f9d211fd0..9eda1e665 100644 --- a/src/structures/voice/Connection.ts +++ b/src/structures/voice/Connection.ts @@ -1,8 +1,8 @@ -import GuildVoice from './GuildVoice'; -import UDPSocket from './UDPSocket'; -import VoiceWebSocket from './VoiceWebSocket'; +import { GuildVoice } from './GuildVoice'; +import { UDPSocket } from './UDPSocket'; +import { VoiceWebSocket } from './VoiceWebSocket'; -export default class Connection { +export class Connection { public sockets!: { ws: VoiceWebSocket; udp: UDPSocket; diff --git a/src/structures/voice/GuildVoice.ts b/src/structures/voice/GuildVoice.ts index 46b9205ac..10a1ae8cf 100644 --- a/src/structures/voice/GuildVoice.ts +++ b/src/structures/voice/GuildVoice.ts @@ -1,4 +1,4 @@ -import Connection from './Connection'; +import { Connection } from './Connection'; import { Guild } from '..'; import { Bot } from '../../bot'; import { BotEvent } from '../../socket'; @@ -7,7 +7,7 @@ import { Snowflake } from '../../types'; /** * Represents a voice connection of a guild */ -export default class GuildVoice { +export class GuildVoice { /** * The {@link Bot} operating this structure */ diff --git a/src/structures/voice/UDPSocket.ts b/src/structures/voice/UDPSocket.ts index 7be8ad1a7..cee4bd2f9 100644 --- a/src/structures/voice/UDPSocket.ts +++ b/src/structures/voice/UDPSocket.ts @@ -1,7 +1,7 @@ import { createSocket, Socket } from 'dgram'; import { EventEmitter } from 'events'; import { Readable } from 'stream'; -import Connection from './Connection'; +import { Connection } from './Connection'; import { BotEvent } from '../../socket'; let LibSodium: typeof import('sodium-native'); @@ -15,7 +15,7 @@ try { OpusScript = require("opusscript").OpusScript //eslint-disable-line } catch (err) {} //eslint-disable-line -export default class UDPSocket extends EventEmitter { +export class UDPSocket extends EventEmitter { public connection: Connection; public socket: Socket; diff --git a/src/structures/voice/VoiceHeartbeats.ts b/src/structures/voice/VoiceHeartbeats.ts index ff3a83729..3b65e84d7 100644 --- a/src/structures/voice/VoiceHeartbeats.ts +++ b/src/structures/voice/VoiceHeartbeats.ts @@ -1,4 +1,4 @@ -import VoiceWebSocket from './VoiceWebSocket'; +import { VoiceWebSocket } from './VoiceWebSocket'; interface HeartbeatData { op: 3; @@ -13,7 +13,7 @@ interface HeartbeatInterval { /** * Handles the sending and receiving of Discord heartbeats */ -export default class VoiceHeartbeats { +export class VoiceHeartbeats { private readonly ws: VoiceWebSocket; private readonly sequence: number | null; private acked: boolean; diff --git a/src/structures/voice/VoiceState.ts b/src/structures/voice/VoiceState.ts index 0b889b6cf..e9977f44e 100644 --- a/src/structures/voice/VoiceState.ts +++ b/src/structures/voice/VoiceState.ts @@ -1,7 +1,7 @@ import { Bot } from '../../bot'; import { Nullable, Snowflake } from '../../types'; import { BaseStruct, GatewayStruct } from '../base'; -import GuildVoiceChannel from '../channels/GuildVoiceChannel'; +import { GuildVoiceChannel } from '../channels/GuildVoiceChannel'; import { Member } from '../member'; interface VoiceStateData { @@ -24,7 +24,7 @@ enum MUTE_STATE { NONE, } -export default class VoiceState extends BaseStruct { +export class VoiceState extends BaseStruct { private channelId!: Nullable; public sessionId!: string; public deafen!: MUTE_STATE; diff --git a/src/structures/voice/VoiceWebSocket.ts b/src/structures/voice/VoiceWebSocket.ts index 945d5b896..c13bbdfeb 100644 --- a/src/structures/voice/VoiceWebSocket.ts +++ b/src/structures/voice/VoiceWebSocket.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import WebSocket from 'ws'; -import Connection from './Connection'; -import VoiceHeartbeats from './VoiceHeartbeats'; +import { Connection } from './Connection'; +import { VoiceHeartbeats } from './VoiceHeartbeats'; import { PayloadData } from '../../socket'; export enum VOICE_OPCODES { @@ -33,7 +33,7 @@ export interface VoicePayload extends VoiceCommand { s: number; } -export default class VoiceWebSocket extends EventEmitter { +export class VoiceWebSocket extends EventEmitter { public connection: Connection; private ws?: WebSocket; From d82b9b86cbfe32d5a3f9baeea57a3c18db81f775 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 03:39:17 +0300 Subject: [PATCH 15/48] fix(Exports): imported correctly --- src/bot/handlers/events/events.ts | 2 +- src/socket/handlers/voiceStateUpdate.ts | 2 +- src/structures/member/Member.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bot/handlers/events/events.ts b/src/bot/handlers/events/events.ts index c46b67f22..1c0f4ad63 100644 --- a/src/bot/handlers/events/events.ts +++ b/src/bot/handlers/events/events.ts @@ -14,7 +14,7 @@ import { Channel, GuildChannel } from '../../../structures/channels'; import { Guild, GuildUnavailable, GuildBan } from '../../../structures/guild'; import { Member, MemberPresence } from '../../../structures/member'; import { Message, PartialMessage, MessageReaction } from '../../../structures/message'; -import VoiceState from '../../../structures/voice/VoiceState'; +import { VoiceState } from '../../../structures/voice/VoiceState'; import { Snowflake } from '../../../types'; /* eslint-disable @typescript-eslint/no-explicit-any */ diff --git a/src/socket/handlers/voiceStateUpdate.ts b/src/socket/handlers/voiceStateUpdate.ts index 89078420f..4ff5525a3 100644 --- a/src/socket/handlers/voiceStateUpdate.ts +++ b/src/socket/handlers/voiceStateUpdate.ts @@ -1,6 +1,6 @@ import { Payload, BotEvent } from '..'; import { Bot } from '../..'; -import VoiceState from '../../structures/voice/VoiceState'; +import { VoiceState } from '../../structures/voice/VoiceState'; export default async ({ d }: Payload, bot: Bot): Promise => { const oldVoiceState = bot.guilds.cache.get(d.guild_id)!.voiceStates.get(d.user_id)!; diff --git a/src/structures/member/Member.ts b/src/structures/member/Member.ts index 85decfc5f..5f4aabb5f 100644 --- a/src/structures/member/Member.ts +++ b/src/structures/member/Member.ts @@ -7,7 +7,7 @@ import { Timestamp } from '../Timestamp'; import { User } from '../User'; import { GatewayStruct, BaseGuildStruct } from '../base'; import { Guild } from '../guild'; -import VoiceState from '../voice/VoiceState'; +import { VoiceState } from '../voice/VoiceState'; /** * Options used when modifying a guild member From aa704bd50a8e46b3e26914e029722798c7f1fae8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 00:40:21 +0000 Subject: [PATCH 16/48] ci(Compile): compiled latest push --- lib/Collection.d.ts | 135 ++ lib/Collection.js | 253 ++++ lib/api/APIFile.d.ts | 16 + lib/api/APIFile.js | 58 + lib/api/APIRequest.d.ts | 52 + lib/api/APIRequest.js | 137 ++ lib/api/APISerializer.d.ts | 185 +++ lib/api/APISerializer.js | 385 ++++++ lib/api/BotAPI.d.ts | 644 ++++++++++ lib/api/BotAPI.js | 1118 +++++++++++++++++ lib/api/constants.d.ts | 18 + lib/api/constants.js | 22 + lib/api/endpoints.d.ts | 93 ++ lib/api/endpoints.js | 141 +++ lib/api/index.d.ts | 5 + lib/api/index.js | 17 + lib/api/rateLimit/RateLimitBucket.d.ts | 67 + lib/api/rateLimit/RateLimitBucket.js | 132 ++ lib/api/rateLimit/RateLimitQueue.d.ts | 48 + lib/api/rateLimit/RateLimitQueue.js | 61 + lib/api/rateLimit/Requests.d.ts | 72 ++ lib/api/rateLimit/Requests.js | 59 + lib/api/rateLimit/index.d.ts | 3 + lib/api/rateLimit/index.js | 15 + lib/bot/Bot.d.ts | 137 ++ lib/bot/Bot.js | 85 ++ lib/bot/BotConnection.d.ts | 37 + lib/bot/BotConnection.js | 63 + lib/bot/handlers/Handler.d.ts | 30 + lib/bot/handlers/Handler.js | 28 + lib/bot/handlers/HandlerItem.d.ts | 56 + lib/bot/handlers/HandlerItem.js | 65 + lib/bot/handlers/command/Command.d.ts | 55 + lib/bot/handlers/command/Command.js | 57 + lib/bot/handlers/command/CommandsHandler.d.ts | 25 + lib/bot/handlers/command/CommandsHandler.js | 50 + lib/bot/handlers/command/index.d.ts | 2 + lib/bot/handlers/command/index.js | 14 + lib/bot/handlers/events/Event.d.ts | 54 + lib/bot/handlers/events/Event.js | 57 + lib/bot/handlers/events/EventsHandler.d.ts | 29 + lib/bot/handlers/events/EventsHandler.js | 51 + lib/bot/handlers/events/events.d.ts | 351 ++++++ lib/bot/handlers/events/events.js | 3 + lib/bot/handlers/events/index.d.ts | 2 + lib/bot/handlers/events/index.js | 14 + lib/bot/handlers/index.d.ts | 4 + lib/bot/handlers/index.js | 16 + lib/bot/index.d.ts | 3 + lib/bot/index.js | 15 + lib/controllers/ControllerCache.d.ts | 19 + lib/controllers/ControllerCache.js | 30 + lib/controllers/base/BaseController.d.ts | 18 + lib/controllers/base/BaseController.js | 21 + .../base/BaseCreateController.d.ts | 14 + lib/controllers/base/BaseCreateController.js | 12 + .../base/BaseDeleteController.d.ts | 15 + lib/controllers/base/BaseDeleteController.js | 11 + .../base/BaseFetchAllController.d.ts | 15 + .../base/BaseFetchAllController.js | 11 + lib/controllers/base/BaseFetchController.d.ts | 21 + lib/controllers/base/BaseFetchController.js | 30 + .../base/BaseFetchSomeController.d.ts | 15 + .../base/BaseFetchSomeController.js | 11 + lib/controllers/base/index.d.ts | 6 + lib/controllers/base/index.js | 18 + .../bot/BotChannelsController.d.ts | 35 + lib/controllers/bot/BotChannelsController.js | 81 ++ lib/controllers/bot/BotGuildsController.d.ts | 31 + lib/controllers/bot/BotGuildsController.js | 41 + lib/controllers/bot/BotUsersController.d.ts | 22 + lib/controllers/bot/BotUsersController.js | 52 + lib/controllers/bot/index.d.ts | 3 + lib/controllers/bot/index.js | 15 + .../channel/ChannelMessagesController.d.ts | 55 + .../channel/ChannelMessagesController.js | 56 + .../channel/ChannelPermissionsController.d.ts | 31 + .../channel/ChannelPermissionsController.js | 34 + .../channel/ChannelPinsController.d.ts | 39 + .../channel/ChannelPinsController.js | 62 + lib/controllers/channel/index.d.ts | 3 + lib/controllers/channel/index.js | 15 + .../guild/GuildBansController.d.ts | 33 + lib/controllers/guild/GuildBansController.js | 56 + .../guild/GuildChannelInvitesController.d.ts | 28 + .../guild/GuildChannelInvitesController.js | 45 + .../guild/GuildChannelWebhooksController.d.ts | 41 + .../guild/GuildChannelWebhooksController.js | 73 ++ .../guild/GuildChannelsController.d.ts | 67 + .../guild/GuildChannelsController.js | 117 ++ .../guild/GuildEmojisController.d.ts | 32 + .../guild/GuildEmojisController.js | 55 + .../guild/GuildIntegrationsController.d.ts | 35 + .../guild/GuildIntegrationsController.js | 54 + .../guild/GuildInvitesController.d.ts | 44 + .../guild/GuildInvitesController.js | 57 + .../guild/GuildMembersController.d.ts | 52 + .../guild/GuildMembersController.js | 65 + .../guild/GuildRolesController.d.ts | 43 + lib/controllers/guild/GuildRolesController.js | 62 + .../guild/GuildWebhooksController.d.ts | 20 + .../guild/GuildWebhooksController.js | 35 + lib/controllers/guild/index.d.ts | 10 + lib/controllers/guild/index.js | 22 + lib/controllers/index.d.ts | 8 + lib/controllers/index.js | 20 + .../member/MemberRolesController.d.ts | 31 + .../member/MemberRolesController.js | 32 + lib/controllers/member/index.d.ts | 1 + lib/controllers/member/index.js | 13 + .../message/MessageReactionsController.d.ts | 36 + .../message/MessageReactionsController.js | 42 + lib/controllers/message/index.d.ts | 1 + lib/controllers/message/index.js | 13 + .../reaction/ReactionUsersController.d.ts | 43 + .../reaction/ReactionUsersController.js | 48 + lib/controllers/reaction/index.d.ts | 1 + lib/controllers/reaction/index.js | 13 + lib/index.d.ts | 8 + lib/index.js | 32 + lib/sharding/BotCommunication.d.ts | 253 ++++ lib/sharding/BotCommunication.js | 138 ++ lib/sharding/BotShard.d.ts | 53 + lib/sharding/BotShard.js | 158 +++ lib/sharding/BotShardManager.d.ts | 53 + lib/sharding/BotShardManager.js | 96 ++ lib/sharding/index.d.ts | 3 + lib/sharding/index.js | 15 + lib/socket/BotHeartbeats.d.ts | 43 + lib/socket/BotHeartbeats.js | 66 + lib/socket/BotSocket.d.ts | 69 + lib/socket/BotSocket.js | 140 +++ lib/socket/BotSocketShard.d.ts | 186 +++ lib/socket/BotSocketShard.js | 339 +++++ lib/socket/constants.d.ts | 129 ++ lib/socket/constants.js | 65 + lib/socket/handlers/channelCreate.d.ts | 4 + lib/socket/handlers/channelCreate.js | 18 + lib/socket/handlers/channelDelete.d.ts | 4 + lib/socket/handlers/channelDelete.js | 18 + lib/socket/handlers/channelPinsUpdate.d.ts | 4 + lib/socket/handlers/channelPinsUpdate.js | 20 + lib/socket/handlers/channelUpdate.d.ts | 4 + lib/socket/handlers/channelUpdate.js | 18 + lib/socket/handlers/guildBanAdd.d.ts | 4 + lib/socket/handlers/guildBanAdd.js | 21 + lib/socket/handlers/guildBanRemove.d.ts | 4 + lib/socket/handlers/guildBanRemove.js | 21 + lib/socket/handlers/guildCreate.d.ts | 4 + lib/socket/handlers/guildCreate.js | 31 + lib/socket/handlers/guildDelete.d.ts | 4 + lib/socket/handlers/guildDelete.js | 19 + lib/socket/handlers/guildEmojisUpdate.d.ts | 4 + lib/socket/handlers/guildEmojisUpdate.js | 23 + .../handlers/guildIntegrationsUpdate.d.ts | 4 + .../handlers/guildIntegrationsUpdate.js | 17 + lib/socket/handlers/guildMemberAdd.d.ts | 4 + lib/socket/handlers/guildMemberAdd.js | 25 + lib/socket/handlers/guildMemberRemove.d.ts | 4 + lib/socket/handlers/guildMemberRemove.js | 21 + lib/socket/handlers/guildMemberUpdate.d.ts | 4 + lib/socket/handlers/guildMemberUpdate.js | 19 + lib/socket/handlers/guildMembersChunk.d.ts | 17 + lib/socket/handlers/guildMembersChunk.js | 47 + lib/socket/handlers/guildRoleCreate.d.ts | 4 + lib/socket/handlers/guildRoleCreate.js | 21 + lib/socket/handlers/guildRoleDelete.d.ts | 4 + lib/socket/handlers/guildRoleDelete.js | 22 + lib/socket/handlers/guildRoleUpdate.d.ts | 4 + lib/socket/handlers/guildRoleUpdate.js | 21 + lib/socket/handlers/guildUpdate.d.ts | 4 + lib/socket/handlers/guildUpdate.js | 21 + lib/socket/handlers/index.d.ts | 35 + lib/socket/handlers/index.js | 72 ++ lib/socket/handlers/inviteCreate.d.ts | 4 + lib/socket/handlers/inviteCreate.js | 13 + lib/socket/handlers/inviteDelete.d.ts | 4 + lib/socket/handlers/inviteDelete.js | 30 + lib/socket/handlers/messageCreate.d.ts | 4 + lib/socket/handlers/messageCreate.js | 23 + lib/socket/handlers/messageDelete.d.ts | 4 + lib/socket/handlers/messageDelete.js | 30 + lib/socket/handlers/messageDeleteBulk.d.ts | 4 + lib/socket/handlers/messageDeleteBulk.js | 26 + lib/socket/handlers/messageReactionAdd.d.ts | 4 + lib/socket/handlers/messageReactionAdd.js | 44 + .../handlers/messageReactionRemove.d.ts | 4 + lib/socket/handlers/messageReactionRemove.js | 46 + .../handlers/messageReactionRemoveAll.d.ts | 4 + .../handlers/messageReactionRemoveAll.js | 19 + .../handlers/messageReactionRemoveEmoji.d.ts | 4 + .../handlers/messageReactionRemoveEmoji.js | 22 + lib/socket/handlers/messageUpdate.d.ts | 4 + lib/socket/handlers/messageUpdate.js | 19 + lib/socket/handlers/presenceUpdate.d.ts | 4 + lib/socket/handlers/presenceUpdate.js | 23 + lib/socket/handlers/ready.d.ts | 4 + lib/socket/handlers/ready.js | 21 + lib/socket/handlers/typingStart.d.ts | 4 + lib/socket/handlers/typingStart.js | 23 + lib/socket/handlers/userUpdate.d.ts | 4 + lib/socket/handlers/userUpdate.js | 20 + lib/socket/handlers/voiceServerUpdate.d.ts | 4 + lib/socket/handlers/voiceServerUpdate.js | 15 + lib/socket/handlers/voiceStateUpdate.d.ts | 4 + lib/socket/handlers/voiceStateUpdate.js | 19 + lib/socket/handlers/webhooksUpdate.d.ts | 4 + lib/socket/handlers/webhooksUpdate.js | 18 + lib/socket/index.d.ts | 6 + lib/socket/index.js | 18 + lib/socket/properties.d.ts | 34 + lib/socket/properties.js | 19 + lib/socket/utils/HandlersUtils.d.ts | 16 + lib/socket/utils/HandlersUtils.js | 13 + lib/socket/utils/ReactionHandlersUtils.d.ts | 17 + lib/socket/utils/ReactionHandlersUtils.js | 38 + lib/socket/utils/index.d.ts | 2 + lib/socket/utils/index.js | 14 + lib/structures/Avatar.d.ts | 133 ++ lib/structures/Avatar.js | 164 +++ lib/structures/BotPresence.d.ts | 28 + lib/structures/BotPresence.js | 57 + lib/structures/BotUser.d.ts | 112 ++ lib/structures/BotUser.js | 39 + lib/structures/Emoji.d.ts | 70 ++ lib/structures/Emoji.js | 57 + lib/structures/ImageURI.d.ts | 32 + lib/structures/ImageURI.js | 62 + lib/structures/Invite.d.ts | 100 ++ lib/structures/Invite.js | 50 + lib/structures/PermissionOverwrite.d.ts | 35 + lib/structures/PermissionOverwrite.js | 41 + lib/structures/Role.d.ts | 89 ++ lib/structures/Role.js | 47 + lib/structures/Timestamp.d.ts | 20 + lib/structures/Timestamp.js | 34 + lib/structures/User.d.ts | 113 ++ lib/structures/User.js | 104 ++ lib/structures/Webhook.d.ts | 89 ++ lib/structures/Webhook.js | 59 + lib/structures/base/BaseGuildStruct.d.ts | 14 + lib/structures/base/BaseGuildStruct.js | 15 + lib/structures/base/BaseStruct.d.ts | 50 + lib/structures/base/BaseStruct.js | 39 + lib/structures/base/index.d.ts | 2 + lib/structures/base/index.js | 14 + lib/structures/channels/Channel.d.ts | 45 + lib/structures/channels/Channel.js | 51 + lib/structures/channels/DMChannel.d.ts | 34 + lib/structures/channels/DMChannel.js | 38 + .../channels/GuildCategoryChannel.d.ts | 13 + .../channels/GuildCategoryChannel.js | 17 + lib/structures/channels/GuildChannel.d.ts | 123 ++ lib/structures/channels/GuildChannel.js | 53 + lib/structures/channels/GuildTextChannel.d.ts | 41 + lib/structures/channels/GuildTextChannel.js | 48 + .../channels/GuildVoiceChannel.d.ts | 12 + lib/structures/channels/GuildVoiceChannel.js | 16 + lib/structures/channels/TextChannel.d.ts | 54 + lib/structures/channels/TextChannel.js | 2 + lib/structures/channels/index.d.ts | 8 + lib/structures/channels/index.js | 20 + .../channels/utils/ChannelUtils.d.ts | 54 + lib/structures/channels/utils/ChannelUtils.js | 126 ++ lib/structures/channels/utils/index.d.ts | 1 + lib/structures/channels/utils/index.js | 13 + lib/structures/flags/Flags.d.ts | 28 + lib/structures/flags/Flags.js | 37 + .../flags/GuildSystemChannelFlags.d.ts | 17 + .../flags/GuildSystemChannelFlags.js | 22 + lib/structures/flags/MessageFlags.d.ts | 29 + lib/structures/flags/MessageFlags.js | 34 + lib/structures/flags/PermissionFlags.d.ts | 82 ++ lib/structures/flags/PermissionFlags.js | 63 + .../flags/PresenceActivityFlags.d.ts | 15 + lib/structures/flags/PresenceActivityFlags.js | 20 + lib/structures/flags/UserFlags.d.ts | 23 + lib/structures/flags/UserFlags.js | 28 + lib/structures/flags/index.d.ts | 6 + lib/structures/flags/index.js | 18 + lib/structures/guild/Guild.d.ts | 446 +++++++ lib/structures/guild/Guild.js | 283 +++++ lib/structures/guild/GuildBan.d.ts | 32 + lib/structures/guild/GuildBan.js | 33 + lib/structures/guild/GuildEmoji.d.ts | 81 ++ lib/structures/guild/GuildEmoji.js | 53 + lib/structures/guild/GuildIntegration.d.ts | 137 ++ lib/structures/guild/GuildIntegration.js | 64 + lib/structures/guild/GuildPreview.d.ts | 128 ++ lib/structures/guild/GuildPreview.js | 124 ++ lib/structures/guild/GuildUnavailable.d.ts | 30 + lib/structures/guild/GuildUnavailable.js | 28 + lib/structures/guild/GuildWidget.d.ts | 41 + lib/structures/guild/GuildWidget.js | 28 + lib/structures/guild/index.d.ts | 7 + lib/structures/guild/index.js | 19 + lib/structures/index.d.ts | 16 + lib/structures/index.js | 28 + lib/structures/member/Member.d.ts | 127 ++ lib/structures/member/Member.js | 97 ++ lib/structures/member/MemberPresence.d.ts | 229 ++++ lib/structures/member/MemberPresence.js | 92 ++ lib/structures/member/index.d.ts | 2 + lib/structures/member/index.js | 14 + lib/structures/message/Message.d.ts | 295 +++++ lib/structures/message/Message.js | 174 +++ lib/structures/message/MessageAttachment.d.ts | 46 + lib/structures/message/MessageAttachment.js | 34 + lib/structures/message/MessageEmbed.d.ts | 234 ++++ lib/structures/message/MessageEmbed.js | 151 +++ lib/structures/message/MessageMentions.d.ts | 54 + lib/structures/message/MessageMentions.js | 75 ++ lib/structures/message/MessageReaction.d.ts | 55 + lib/structures/message/MessageReaction.js | 51 + lib/structures/message/index.d.ts | 5 + lib/structures/message/index.js | 17 + lib/structures/voice/Connection.d.ts | 20 + lib/structures/voice/Connection.js | 39 + lib/structures/voice/GuildVoice.d.ts | 24 + lib/structures/voice/GuildVoice.js | 38 + lib/structures/voice/UDPSocket.d.ts | 34 + lib/structures/voice/UDPSocket.js | 110 ++ lib/structures/voice/VoiceHeartbeats.d.ts | 38 + lib/structures/voice/VoiceHeartbeats.js | 54 + lib/structures/voice/VoiceState.d.ts | 35 + lib/structures/voice/VoiceState.js | 39 + lib/structures/voice/VoiceWebSocket.d.ts | 41 + lib/structures/voice/VoiceWebSocket.js | 99 ++ lib/structures/voice/index.d.ts | 6 + lib/structures/voice/index.js | 18 + lib/types/index.d.ts | 1 + lib/types/index.js | 13 + lib/types/types.d.ts | 19 + lib/types/types.js | 2 + 334 files changed, 17146 insertions(+) create mode 100644 lib/Collection.d.ts create mode 100644 lib/Collection.js create mode 100644 lib/api/APIFile.d.ts create mode 100644 lib/api/APIFile.js create mode 100644 lib/api/APIRequest.d.ts create mode 100644 lib/api/APIRequest.js create mode 100644 lib/api/APISerializer.d.ts create mode 100644 lib/api/APISerializer.js create mode 100644 lib/api/BotAPI.d.ts create mode 100644 lib/api/BotAPI.js create mode 100644 lib/api/constants.d.ts create mode 100644 lib/api/constants.js create mode 100644 lib/api/endpoints.d.ts create mode 100644 lib/api/endpoints.js create mode 100644 lib/api/index.d.ts create mode 100644 lib/api/index.js create mode 100644 lib/api/rateLimit/RateLimitBucket.d.ts create mode 100644 lib/api/rateLimit/RateLimitBucket.js create mode 100644 lib/api/rateLimit/RateLimitQueue.d.ts create mode 100644 lib/api/rateLimit/RateLimitQueue.js create mode 100644 lib/api/rateLimit/Requests.d.ts create mode 100644 lib/api/rateLimit/Requests.js create mode 100644 lib/api/rateLimit/index.d.ts create mode 100644 lib/api/rateLimit/index.js create mode 100644 lib/bot/Bot.d.ts create mode 100644 lib/bot/Bot.js create mode 100644 lib/bot/BotConnection.d.ts create mode 100644 lib/bot/BotConnection.js create mode 100644 lib/bot/handlers/Handler.d.ts create mode 100644 lib/bot/handlers/Handler.js create mode 100644 lib/bot/handlers/HandlerItem.d.ts create mode 100644 lib/bot/handlers/HandlerItem.js create mode 100644 lib/bot/handlers/command/Command.d.ts create mode 100644 lib/bot/handlers/command/Command.js create mode 100644 lib/bot/handlers/command/CommandsHandler.d.ts create mode 100644 lib/bot/handlers/command/CommandsHandler.js create mode 100644 lib/bot/handlers/command/index.d.ts create mode 100644 lib/bot/handlers/command/index.js create mode 100644 lib/bot/handlers/events/Event.d.ts create mode 100644 lib/bot/handlers/events/Event.js create mode 100644 lib/bot/handlers/events/EventsHandler.d.ts create mode 100644 lib/bot/handlers/events/EventsHandler.js create mode 100644 lib/bot/handlers/events/events.d.ts create mode 100644 lib/bot/handlers/events/events.js create mode 100644 lib/bot/handlers/events/index.d.ts create mode 100644 lib/bot/handlers/events/index.js create mode 100644 lib/bot/handlers/index.d.ts create mode 100644 lib/bot/handlers/index.js create mode 100644 lib/bot/index.d.ts create mode 100644 lib/bot/index.js create mode 100644 lib/controllers/ControllerCache.d.ts create mode 100644 lib/controllers/ControllerCache.js create mode 100644 lib/controllers/base/BaseController.d.ts create mode 100644 lib/controllers/base/BaseController.js create mode 100644 lib/controllers/base/BaseCreateController.d.ts create mode 100644 lib/controllers/base/BaseCreateController.js create mode 100644 lib/controllers/base/BaseDeleteController.d.ts create mode 100644 lib/controllers/base/BaseDeleteController.js create mode 100644 lib/controllers/base/BaseFetchAllController.d.ts create mode 100644 lib/controllers/base/BaseFetchAllController.js create mode 100644 lib/controllers/base/BaseFetchController.d.ts create mode 100644 lib/controllers/base/BaseFetchController.js create mode 100644 lib/controllers/base/BaseFetchSomeController.d.ts create mode 100644 lib/controllers/base/BaseFetchSomeController.js create mode 100644 lib/controllers/base/index.d.ts create mode 100644 lib/controllers/base/index.js create mode 100644 lib/controllers/bot/BotChannelsController.d.ts create mode 100644 lib/controllers/bot/BotChannelsController.js create mode 100644 lib/controllers/bot/BotGuildsController.d.ts create mode 100644 lib/controllers/bot/BotGuildsController.js create mode 100644 lib/controllers/bot/BotUsersController.d.ts create mode 100644 lib/controllers/bot/BotUsersController.js create mode 100644 lib/controllers/bot/index.d.ts create mode 100644 lib/controllers/bot/index.js create mode 100644 lib/controllers/channel/ChannelMessagesController.d.ts create mode 100644 lib/controllers/channel/ChannelMessagesController.js create mode 100644 lib/controllers/channel/ChannelPermissionsController.d.ts create mode 100644 lib/controllers/channel/ChannelPermissionsController.js create mode 100644 lib/controllers/channel/ChannelPinsController.d.ts create mode 100644 lib/controllers/channel/ChannelPinsController.js create mode 100644 lib/controllers/channel/index.d.ts create mode 100644 lib/controllers/channel/index.js create mode 100644 lib/controllers/guild/GuildBansController.d.ts create mode 100644 lib/controllers/guild/GuildBansController.js create mode 100644 lib/controllers/guild/GuildChannelInvitesController.d.ts create mode 100644 lib/controllers/guild/GuildChannelInvitesController.js create mode 100644 lib/controllers/guild/GuildChannelWebhooksController.d.ts create mode 100644 lib/controllers/guild/GuildChannelWebhooksController.js create mode 100644 lib/controllers/guild/GuildChannelsController.d.ts create mode 100644 lib/controllers/guild/GuildChannelsController.js create mode 100644 lib/controllers/guild/GuildEmojisController.d.ts create mode 100644 lib/controllers/guild/GuildEmojisController.js create mode 100644 lib/controllers/guild/GuildIntegrationsController.d.ts create mode 100644 lib/controllers/guild/GuildIntegrationsController.js create mode 100644 lib/controllers/guild/GuildInvitesController.d.ts create mode 100644 lib/controllers/guild/GuildInvitesController.js create mode 100644 lib/controllers/guild/GuildMembersController.d.ts create mode 100644 lib/controllers/guild/GuildMembersController.js create mode 100644 lib/controllers/guild/GuildRolesController.d.ts create mode 100644 lib/controllers/guild/GuildRolesController.js create mode 100644 lib/controllers/guild/GuildWebhooksController.d.ts create mode 100644 lib/controllers/guild/GuildWebhooksController.js create mode 100644 lib/controllers/guild/index.d.ts create mode 100644 lib/controllers/guild/index.js create mode 100644 lib/controllers/index.d.ts create mode 100644 lib/controllers/index.js create mode 100644 lib/controllers/member/MemberRolesController.d.ts create mode 100644 lib/controllers/member/MemberRolesController.js create mode 100644 lib/controllers/member/index.d.ts create mode 100644 lib/controllers/member/index.js create mode 100644 lib/controllers/message/MessageReactionsController.d.ts create mode 100644 lib/controllers/message/MessageReactionsController.js create mode 100644 lib/controllers/message/index.d.ts create mode 100644 lib/controllers/message/index.js create mode 100644 lib/controllers/reaction/ReactionUsersController.d.ts create mode 100644 lib/controllers/reaction/ReactionUsersController.js create mode 100644 lib/controllers/reaction/index.d.ts create mode 100644 lib/controllers/reaction/index.js create mode 100644 lib/index.d.ts create mode 100644 lib/index.js create mode 100644 lib/sharding/BotCommunication.d.ts create mode 100644 lib/sharding/BotCommunication.js create mode 100644 lib/sharding/BotShard.d.ts create mode 100644 lib/sharding/BotShard.js create mode 100644 lib/sharding/BotShardManager.d.ts create mode 100644 lib/sharding/BotShardManager.js create mode 100644 lib/sharding/index.d.ts create mode 100644 lib/sharding/index.js create mode 100644 lib/socket/BotHeartbeats.d.ts create mode 100644 lib/socket/BotHeartbeats.js create mode 100644 lib/socket/BotSocket.d.ts create mode 100644 lib/socket/BotSocket.js create mode 100644 lib/socket/BotSocketShard.d.ts create mode 100644 lib/socket/BotSocketShard.js create mode 100644 lib/socket/constants.d.ts create mode 100644 lib/socket/constants.js create mode 100644 lib/socket/handlers/channelCreate.d.ts create mode 100644 lib/socket/handlers/channelCreate.js create mode 100644 lib/socket/handlers/channelDelete.d.ts create mode 100644 lib/socket/handlers/channelDelete.js create mode 100644 lib/socket/handlers/channelPinsUpdate.d.ts create mode 100644 lib/socket/handlers/channelPinsUpdate.js create mode 100644 lib/socket/handlers/channelUpdate.d.ts create mode 100644 lib/socket/handlers/channelUpdate.js create mode 100644 lib/socket/handlers/guildBanAdd.d.ts create mode 100644 lib/socket/handlers/guildBanAdd.js create mode 100644 lib/socket/handlers/guildBanRemove.d.ts create mode 100644 lib/socket/handlers/guildBanRemove.js create mode 100644 lib/socket/handlers/guildCreate.d.ts create mode 100644 lib/socket/handlers/guildCreate.js create mode 100644 lib/socket/handlers/guildDelete.d.ts create mode 100644 lib/socket/handlers/guildDelete.js create mode 100644 lib/socket/handlers/guildEmojisUpdate.d.ts create mode 100644 lib/socket/handlers/guildEmojisUpdate.js create mode 100644 lib/socket/handlers/guildIntegrationsUpdate.d.ts create mode 100644 lib/socket/handlers/guildIntegrationsUpdate.js create mode 100644 lib/socket/handlers/guildMemberAdd.d.ts create mode 100644 lib/socket/handlers/guildMemberAdd.js create mode 100644 lib/socket/handlers/guildMemberRemove.d.ts create mode 100644 lib/socket/handlers/guildMemberRemove.js create mode 100644 lib/socket/handlers/guildMemberUpdate.d.ts create mode 100644 lib/socket/handlers/guildMemberUpdate.js create mode 100644 lib/socket/handlers/guildMembersChunk.d.ts create mode 100644 lib/socket/handlers/guildMembersChunk.js create mode 100644 lib/socket/handlers/guildRoleCreate.d.ts create mode 100644 lib/socket/handlers/guildRoleCreate.js create mode 100644 lib/socket/handlers/guildRoleDelete.d.ts create mode 100644 lib/socket/handlers/guildRoleDelete.js create mode 100644 lib/socket/handlers/guildRoleUpdate.d.ts create mode 100644 lib/socket/handlers/guildRoleUpdate.js create mode 100644 lib/socket/handlers/guildUpdate.d.ts create mode 100644 lib/socket/handlers/guildUpdate.js create mode 100644 lib/socket/handlers/index.d.ts create mode 100644 lib/socket/handlers/index.js create mode 100644 lib/socket/handlers/inviteCreate.d.ts create mode 100644 lib/socket/handlers/inviteCreate.js create mode 100644 lib/socket/handlers/inviteDelete.d.ts create mode 100644 lib/socket/handlers/inviteDelete.js create mode 100644 lib/socket/handlers/messageCreate.d.ts create mode 100644 lib/socket/handlers/messageCreate.js create mode 100644 lib/socket/handlers/messageDelete.d.ts create mode 100644 lib/socket/handlers/messageDelete.js create mode 100644 lib/socket/handlers/messageDeleteBulk.d.ts create mode 100644 lib/socket/handlers/messageDeleteBulk.js create mode 100644 lib/socket/handlers/messageReactionAdd.d.ts create mode 100644 lib/socket/handlers/messageReactionAdd.js create mode 100644 lib/socket/handlers/messageReactionRemove.d.ts create mode 100644 lib/socket/handlers/messageReactionRemove.js create mode 100644 lib/socket/handlers/messageReactionRemoveAll.d.ts create mode 100644 lib/socket/handlers/messageReactionRemoveAll.js create mode 100644 lib/socket/handlers/messageReactionRemoveEmoji.d.ts create mode 100644 lib/socket/handlers/messageReactionRemoveEmoji.js create mode 100644 lib/socket/handlers/messageUpdate.d.ts create mode 100644 lib/socket/handlers/messageUpdate.js create mode 100644 lib/socket/handlers/presenceUpdate.d.ts create mode 100644 lib/socket/handlers/presenceUpdate.js create mode 100644 lib/socket/handlers/ready.d.ts create mode 100644 lib/socket/handlers/ready.js create mode 100644 lib/socket/handlers/typingStart.d.ts create mode 100644 lib/socket/handlers/typingStart.js create mode 100644 lib/socket/handlers/userUpdate.d.ts create mode 100644 lib/socket/handlers/userUpdate.js create mode 100644 lib/socket/handlers/voiceServerUpdate.d.ts create mode 100644 lib/socket/handlers/voiceServerUpdate.js create mode 100644 lib/socket/handlers/voiceStateUpdate.d.ts create mode 100644 lib/socket/handlers/voiceStateUpdate.js create mode 100644 lib/socket/handlers/webhooksUpdate.d.ts create mode 100644 lib/socket/handlers/webhooksUpdate.js create mode 100644 lib/socket/index.d.ts create mode 100644 lib/socket/index.js create mode 100644 lib/socket/properties.d.ts create mode 100644 lib/socket/properties.js create mode 100644 lib/socket/utils/HandlersUtils.d.ts create mode 100644 lib/socket/utils/HandlersUtils.js create mode 100644 lib/socket/utils/ReactionHandlersUtils.d.ts create mode 100644 lib/socket/utils/ReactionHandlersUtils.js create mode 100644 lib/socket/utils/index.d.ts create mode 100644 lib/socket/utils/index.js create mode 100644 lib/structures/Avatar.d.ts create mode 100644 lib/structures/Avatar.js create mode 100644 lib/structures/BotPresence.d.ts create mode 100644 lib/structures/BotPresence.js create mode 100644 lib/structures/BotUser.d.ts create mode 100644 lib/structures/BotUser.js create mode 100644 lib/structures/Emoji.d.ts create mode 100644 lib/structures/Emoji.js create mode 100644 lib/structures/ImageURI.d.ts create mode 100644 lib/structures/ImageURI.js create mode 100644 lib/structures/Invite.d.ts create mode 100644 lib/structures/Invite.js create mode 100644 lib/structures/PermissionOverwrite.d.ts create mode 100644 lib/structures/PermissionOverwrite.js create mode 100644 lib/structures/Role.d.ts create mode 100644 lib/structures/Role.js create mode 100644 lib/structures/Timestamp.d.ts create mode 100644 lib/structures/Timestamp.js create mode 100644 lib/structures/User.d.ts create mode 100644 lib/structures/User.js create mode 100644 lib/structures/Webhook.d.ts create mode 100644 lib/structures/Webhook.js create mode 100644 lib/structures/base/BaseGuildStruct.d.ts create mode 100644 lib/structures/base/BaseGuildStruct.js create mode 100644 lib/structures/base/BaseStruct.d.ts create mode 100644 lib/structures/base/BaseStruct.js create mode 100644 lib/structures/base/index.d.ts create mode 100644 lib/structures/base/index.js create mode 100644 lib/structures/channels/Channel.d.ts create mode 100644 lib/structures/channels/Channel.js create mode 100644 lib/structures/channels/DMChannel.d.ts create mode 100644 lib/structures/channels/DMChannel.js create mode 100644 lib/structures/channels/GuildCategoryChannel.d.ts create mode 100644 lib/structures/channels/GuildCategoryChannel.js create mode 100644 lib/structures/channels/GuildChannel.d.ts create mode 100644 lib/structures/channels/GuildChannel.js create mode 100644 lib/structures/channels/GuildTextChannel.d.ts create mode 100644 lib/structures/channels/GuildTextChannel.js create mode 100644 lib/structures/channels/GuildVoiceChannel.d.ts create mode 100644 lib/structures/channels/GuildVoiceChannel.js create mode 100644 lib/structures/channels/TextChannel.d.ts create mode 100644 lib/structures/channels/TextChannel.js create mode 100644 lib/structures/channels/index.d.ts create mode 100644 lib/structures/channels/index.js create mode 100644 lib/structures/channels/utils/ChannelUtils.d.ts create mode 100644 lib/structures/channels/utils/ChannelUtils.js create mode 100644 lib/structures/channels/utils/index.d.ts create mode 100644 lib/structures/channels/utils/index.js create mode 100644 lib/structures/flags/Flags.d.ts create mode 100644 lib/structures/flags/Flags.js create mode 100644 lib/structures/flags/GuildSystemChannelFlags.d.ts create mode 100644 lib/structures/flags/GuildSystemChannelFlags.js create mode 100644 lib/structures/flags/MessageFlags.d.ts create mode 100644 lib/structures/flags/MessageFlags.js create mode 100644 lib/structures/flags/PermissionFlags.d.ts create mode 100644 lib/structures/flags/PermissionFlags.js create mode 100644 lib/structures/flags/PresenceActivityFlags.d.ts create mode 100644 lib/structures/flags/PresenceActivityFlags.js create mode 100644 lib/structures/flags/UserFlags.d.ts create mode 100644 lib/structures/flags/UserFlags.js create mode 100644 lib/structures/flags/index.d.ts create mode 100644 lib/structures/flags/index.js create mode 100644 lib/structures/guild/Guild.d.ts create mode 100644 lib/structures/guild/Guild.js create mode 100644 lib/structures/guild/GuildBan.d.ts create mode 100644 lib/structures/guild/GuildBan.js create mode 100644 lib/structures/guild/GuildEmoji.d.ts create mode 100644 lib/structures/guild/GuildEmoji.js create mode 100644 lib/structures/guild/GuildIntegration.d.ts create mode 100644 lib/structures/guild/GuildIntegration.js create mode 100644 lib/structures/guild/GuildPreview.d.ts create mode 100644 lib/structures/guild/GuildPreview.js create mode 100644 lib/structures/guild/GuildUnavailable.d.ts create mode 100644 lib/structures/guild/GuildUnavailable.js create mode 100644 lib/structures/guild/GuildWidget.d.ts create mode 100644 lib/structures/guild/GuildWidget.js create mode 100644 lib/structures/guild/index.d.ts create mode 100644 lib/structures/guild/index.js create mode 100644 lib/structures/index.d.ts create mode 100644 lib/structures/index.js create mode 100644 lib/structures/member/Member.d.ts create mode 100644 lib/structures/member/Member.js create mode 100644 lib/structures/member/MemberPresence.d.ts create mode 100644 lib/structures/member/MemberPresence.js create mode 100644 lib/structures/member/index.d.ts create mode 100644 lib/structures/member/index.js create mode 100644 lib/structures/message/Message.d.ts create mode 100644 lib/structures/message/Message.js create mode 100644 lib/structures/message/MessageAttachment.d.ts create mode 100644 lib/structures/message/MessageAttachment.js create mode 100644 lib/structures/message/MessageEmbed.d.ts create mode 100644 lib/structures/message/MessageEmbed.js create mode 100644 lib/structures/message/MessageMentions.d.ts create mode 100644 lib/structures/message/MessageMentions.js create mode 100644 lib/structures/message/MessageReaction.d.ts create mode 100644 lib/structures/message/MessageReaction.js create mode 100644 lib/structures/message/index.d.ts create mode 100644 lib/structures/message/index.js create mode 100644 lib/structures/voice/Connection.d.ts create mode 100644 lib/structures/voice/Connection.js create mode 100644 lib/structures/voice/GuildVoice.d.ts create mode 100644 lib/structures/voice/GuildVoice.js create mode 100644 lib/structures/voice/UDPSocket.d.ts create mode 100644 lib/structures/voice/UDPSocket.js create mode 100644 lib/structures/voice/VoiceHeartbeats.d.ts create mode 100644 lib/structures/voice/VoiceHeartbeats.js create mode 100644 lib/structures/voice/VoiceState.d.ts create mode 100644 lib/structures/voice/VoiceState.js create mode 100644 lib/structures/voice/VoiceWebSocket.d.ts create mode 100644 lib/structures/voice/VoiceWebSocket.js create mode 100644 lib/structures/voice/index.d.ts create mode 100644 lib/structures/voice/index.js create mode 100644 lib/types/index.d.ts create mode 100644 lib/types/index.js create mode 100644 lib/types/types.d.ts create mode 100644 lib/types/types.js diff --git a/lib/Collection.d.ts b/lib/Collection.d.ts new file mode 100644 index 000000000..f3aeda0e9 --- /dev/null +++ b/lib/Collection.d.ts @@ -0,0 +1,135 @@ +/** + * Collections serve as data holders throughout the library. + * They are a combination of JavaScript Maps and Arrays with the + * ability to hold large amount of data. + * @template K, V + */ +declare class Collection extends Map { + /** + * The maximum number of items allowed in this Collection + */ + readonly limit: number | null | undefined; + constructor(entries?: Iterable | null, limit?: number); + /** + * Whether the given argument is a Collection + * @param {unknown} collection Data to check if collection + * @returns {boolean} + */ + static isCollection(collection: unknown): collection is Collection; + /** + * Maps the {@link Collection} values into an array + * @type {V[]} + */ + get toArray(): V[]; + /** + * Maps the {@link Collection} keys into an array + * @type {K[]} + */ + get toArrayKeys(): K[]; + /** + * Maps the {@link Collection} entries an array + * @type {[K, V][]} + */ + get toArrayEntries(): [K, V][]; + /** + * Creates a new Collection with all elements that pass the test implemented by the provided callback. + * Identical to {@link Array.prototype.filter} + * @param {function(value: V, key: K, collection: this): boolean} cb Callback function. Return true to keep the element, false otherwise + * @returns {Collection} + */ + filter(cb: (value: V, key?: K, collection?: this) => boolean): Collection; + /** + * Get the first value in the {@link Collection} + * @type {V | undefined} + */ + get first(): V | undefined; + /** + * Get he first key in the {@link Collection} + * @returns {K} + */ + get firstKey(): K | undefined; + /** + * Get the last value in the {@link Collection} + * @type {V} + */ + get last(): V | undefined; + /** + * Get he last key in the {@link Collection} + * @returns {K} + */ + get lastKey(): K | undefined; + /** + * Returns the item if exists or creates a new one and caches it. + * @param {K} key The key of the item + * @param {V} value The value of the item + * @returns {V} + */ + getOrSet(key: K, value: V): V; + /** + * Ö›Merges collection(s) on top of this one. Replaces existing keys by newer Collections + * @param {...(Collection | [K, V][])[]} collections The collection(s) to be merged on top of this one + */ + merge(...collections: (Collection | [K, V][])[]): void; + /** + * Create a new Collection populated with the results of calling a provided callback on every element in the calling Collection. + * Identical to {@link Array.prototype.map} + * @param {function(value: V, key: K, collection: this): R} cb Callback function. The returned value is added to the new Collection + * @returns {Collection} + * @template K, V + * @template R - is the type of the values the new Collection will contain + */ + map(cb: (value: V, key: K, collection: this) => R): Collection; + /** + * Checks if the Collection has reached its limit and sets the item using {@link Map.prototype.set} + * @param {K} key The key to set + * @param {V} value The value to set + * @param {boolean} force Whether to add the item to the Collection if its limit was reached + * @returns {this} + */ + set(key: K, value: V, force?: boolean): this; + /** + * Returns the matching values for the given keys inside the Collection. + * @param {K[]} keys Array of all keys to look for + * @returns {V[]} + */ + getMany(keys: K[]): (V | undefined)[]; + /** + * Removes the last item from the Collection and returns that item + * Equivalent to Array#pop() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop + * @returns {[K, V] | undefined} + */ + pop(): [K, V] | undefined; + /** + * Removes the first item from a Collection and returns that removed item + * Equivalent to Array#shift() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + * @returns {[K, V] | undefined} + */ + shift(): [K, V] | undefined; + /** + * Tests whether all items in the Collection pass the test implemented by the provided function + * Equivalent to Array#every() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + every(cb: (value: V, key: K, collection: this) => boolean): boolean; + /** + * Tests whether at least one element in the array passes the test implemented by the provided function + * Equivalent to Array#some() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + some(cb: (value: V, key: K, collection: this) => boolean): boolean; + /** + * Returns the value of the item in this collection that satisfies the provided testing callback function + * Equivalent to Array#find() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {V | undefined} + */ + find(cb: (value: V, key: K, collection: this) => boolean): V | undefined; + /** + * @ignore + * @returns {Serializable} + */ + toJSON(): V[]; +} +export default Collection; diff --git a/lib/Collection.js b/lib/Collection.js new file mode 100644 index 000000000..0976331f7 --- /dev/null +++ b/lib/Collection.js @@ -0,0 +1,253 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Collections serve as data holders throughout the library. + * They are a combination of JavaScript Maps and Arrays with the + * ability to hold large amount of data. + * @template K, V + */ +class Collection extends Map { + constructor(entries, limit) { + if (entries) { + super(entries); + } + else { + super(); + } + if (limit) { + this.limit = limit > 0 ? limit : null; + } + } + /** + * Whether the given argument is a Collection + * @param {unknown} collection Data to check if collection + * @returns {boolean} + */ + static isCollection(collection) { + return collection instanceof Collection; + } + /** + * Maps the {@link Collection} values into an array + * @type {V[]} + */ + get toArray() { + return [...this.values()]; + } + /** + * Maps the {@link Collection} keys into an array + * @type {K[]} + */ + get toArrayKeys() { + return [...this.keys()]; + } + /** + * Maps the {@link Collection} entries an array + * @type {[K, V][]} + */ + get toArrayEntries() { + return [...this.entries()]; + } + /** + * Creates a new Collection with all elements that pass the test implemented by the provided callback. + * Identical to {@link Array.prototype.filter} + * @param {function(value: V, key: K, collection: this): boolean} cb Callback function. Return true to keep the element, false otherwise + * @returns {Collection} + */ + filter(cb) { + const collection = new Collection(); + for (const [key, value] of this) { + if (cb(value, key, this)) { + collection.set(key, value); + } + } + return collection; + } + /** + * Get the first value in the {@link Collection} + * @type {V | undefined} + */ + get first() { + if (!this.size) + return undefined; + return this.values().next().value; + } + /** + * Get he first key in the {@link Collection} + * @returns {K} + */ + get firstKey() { + if (!this.size) + return undefined; + return this.keys().next().value; + } + /** + * Get the last value in the {@link Collection} + * @type {V} + */ + get last() { + if (!this.size) + return undefined; + return this.toArray[this.size - 1]; + } + /** + * Get he last key in the {@link Collection} + * @returns {K} + */ + get lastKey() { + if (!this.size) + return undefined; + return this.toArrayKeys[this.size - 1]; + } + /** + * Returns the item if exists or creates a new one and caches it. + * @param {K} key The key of the item + * @param {V} value The value of the item + * @returns {V} + */ + getOrSet(key, value) { + if (this.has(key)) { + return this.get(key); + } + else { + this.set(key, value); + return value; + } + } + /** + * Ö›Merges collection(s) on top of this one. Replaces existing keys by newer Collections + * @param {...(Collection | [K, V][])[]} collections The collection(s) to be merged on top of this one + */ + merge(...collections) { + for (const collection of collections) { + for (const [key, value] of collection) { + this.set(key, value); + } + } + } + /** + * Create a new Collection populated with the results of calling a provided callback on every element in the calling Collection. + * Identical to {@link Array.prototype.map} + * @param {function(value: V, key: K, collection: this): R} cb Callback function. The returned value is added to the new Collection + * @returns {Collection} + * @template K, V + * @template R - is the type of the values the new Collection will contain + */ + map(cb) { + const collection = new Collection(); + for (const [key, value] of this) { + collection.set(key, cb(value, key, this)); + } + return collection; + } + /** + * Checks if the Collection has reached its limit and sets the item using {@link Map.prototype.set} + * @param {K} key The key to set + * @param {V} value The value to set + * @param {boolean} force Whether to add the item to the Collection if its limit was reached + * @returns {this} + */ + set(key, value, force) { + // If the key already exists, a new item won't be added, thus keeping the size at the limit + if (!force && this.limit && this.limit <= this.size && !this.has(key)) { + if (this.firstKey) { + this.delete(this.firstKey); + } + } + return super.set(key, value); + } + /** + * Returns the matching values for the given keys inside the Collection. + * @param {K[]} keys Array of all keys to look for + * @returns {V[]} + */ + getMany(keys) { + const values = []; + for (const key of keys) { + values.push(this.get(key)); + } + return values; + } + /** + * Removes the last item from the Collection and returns that item + * Equivalent to Array#pop() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop + * @returns {[K, V] | undefined} + */ + pop() { + if (!this.lastKey || !this.last) + return; + const popped = [this.lastKey, this.last]; + this.delete(this.lastKey); + return popped; + } + /** + * Removes the first item from a Collection and returns that removed item + * Equivalent to Array#shift() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + * @returns {[K, V] | undefined} + */ + shift() { + if (!this.first || !this.firstKey) + return; + const shifted = [this.firstKey, this.first]; + this.delete(this.firstKey); + return shifted; + } + /** + * Tests whether all items in the Collection pass the test implemented by the provided function + * Equivalent to Array#every() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + every(cb) { + let flag = true; + for (const [key, value] of this) { + const result = cb(value, key, this); + if (!result) { + flag = false; + break; + } + } + return flag; + } + /** + * Tests whether at least one element in the array passes the test implemented by the provided function + * Equivalent to Array#some() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {boolean} + */ + some(cb) { + let flag = false; + for (const [key, value] of this) { + const result = cb(value, key, this); + if (result) { + flag = true; + break; + } + } + return flag; + } + /** + * Returns the value of the item in this collection that satisfies the provided testing callback function + * Equivalent to Array#find() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find + * @param {function(value: V, key: K, collection: this): boolean} cb A function to test for each element + * @returns {V | undefined} + */ + find(cb) { + let item; + for (const [key, value] of this) { + const result = cb(value, key, this); + if (result) { + item = value; + break; + } + } + return item; + } + /** + * @ignore + * @returns {Serializable} + */ + toJSON() { + return this.toArray; + } +} +exports.default = Collection; diff --git a/lib/api/APIFile.d.ts b/lib/api/APIFile.d.ts new file mode 100644 index 000000000..364f19096 --- /dev/null +++ b/lib/api/APIFile.d.ts @@ -0,0 +1,16 @@ +/// +import { RequestFile } from './rateLimit'; +/** + * Represents a file sent directly to the API + */ +export declare class APIFile { + private readonly type; + readonly path: string; + readonly name: string; + constructor(file: RequestFile); + /** + * Reads the content of this file as a readable stream + * @returns {Promise} + */ + read(): Promise; +} diff --git a/lib/api/APIFile.js b/lib/api/APIFile.js new file mode 100644 index 000000000..5dbaec6f2 --- /dev/null +++ b/lib/api/APIFile.js @@ -0,0 +1,58 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.APIFile = void 0; +const fs_1 = __importDefault(require("fs")); +const node_fetch_1 = __importDefault(require("node-fetch")); +/** + * The type of the API file + */ +var APIFileType; +(function (APIFileType) { + APIFileType[APIFileType["File"] = 0] = "File"; + APIFileType[APIFileType["URL"] = 1] = "URL"; +})(APIFileType || (APIFileType = {})); +/** + * Represents a file sent directly to the API + */ +class APIFile { + constructor(file) { + if (typeof file === 'string') { + this.type = APIFileType.URL; + this.path = file; + this.name = file.substring(file.lastIndexOf('/') + 1); + } + else { + this.type = APIFileType.File; + this.path = file.path; + this.name = file.name; + } + } + /** + * Reads the content of this file as a readable stream + * @returns {Promise} + */ + read() { + return __awaiter(this, void 0, void 0, function* () { + if (this.type === APIFileType.File) { + return fs_1.default.createReadStream(this.path); + } + else { + const { body } = yield node_fetch_1.default(this.path); + return body; + } + }); + } +} +exports.APIFile = APIFile; diff --git a/lib/api/APIRequest.d.ts b/lib/api/APIRequest.d.ts new file mode 100644 index 000000000..b4b54c743 --- /dev/null +++ b/lib/api/APIRequest.d.ts @@ -0,0 +1,52 @@ +import { Response } from 'node-fetch'; +import { APIFile } from './APIFile'; +import { HttpMethod } from './constants'; +import { Params, RequestFile } from './rateLimit'; +/** + * Enum containing common request headers + */ +export declare enum Header { + ContentType = "content-type" +} +/** + * Creates and sends an HTTP request to Discord's API + */ +export declare class APIRequest { + private readonly token; + private readonly endpoint; + private readonly params; + private readonly method; + private readonly files; + constructor(token: string, endpoint: string, params: Params, method: HttpMethod, files?: APIFile[]); + /** + * Sends the API request + * @returns {Promise} + */ + send(): Promise; + /** + * Returns the full URL after adding the parameters if required + * @type {string} + */ + private get url(); + /** + * Returns the body of the request + * @returns {string | Promise | undefined} + */ + private body; + /** + * Returns a form-data body if files need to be sent + * @returns {Promise} + */ + private bodyFiles; + /** + * Returns the headers required for the request + * @type {Record} + */ + private get headers(); + /** + * Parses the given files into {@link APIFile} objects + * @param {RequestFile[]} files The files + * @returns {APIFile[] | undefined} + */ + static parseFiles(files?: RequestFile[]): APIFile[] | undefined; +} diff --git a/lib/api/APIRequest.js b/lib/api/APIRequest.js new file mode 100644 index 000000000..0dc99f460 --- /dev/null +++ b/lib/api/APIRequest.js @@ -0,0 +1,137 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.APIRequest = exports.Header = void 0; +const querystring_1 = __importDefault(require("querystring")); +const form_data_1 = __importDefault(require("form-data")); +const node_fetch_1 = __importDefault(require("node-fetch")); +const APIFile_1 = require("./APIFile"); +const constants_1 = require("./constants"); +const paramsMethods = [constants_1.HttpMethod.Get]; +const bodyMethods = [constants_1.HttpMethod.Post, constants_1.HttpMethod.Patch, constants_1.HttpMethod.Put]; +/** + * Enum containing common request headers + */ +var Header; +(function (Header) { + Header["ContentType"] = "content-type"; +})(Header = exports.Header || (exports.Header = {})); +/** + * Creates and sends an HTTP request to Discord's API + */ +class APIRequest { + constructor(token, endpoint, params, method, files) { + this.token = token; + this.endpoint = endpoint; + this.method = method; + this.files = files; + // Clear all nullish params + if (params) { + if (Array.isArray(params)) { + this.params = params.filter(param => Object.entries(param).every(([, value]) => value !== undefined && value !== null)); + } + else { + this.params = Object.entries(params) + .filter(([, value]) => value !== undefined) + .reduce((params, [key, value]) => (Object.assign(Object.assign({}, params), { [key]: value })), {}); + } + } + } + /** + * Sends the API request + * @returns {Promise} + */ + send() { + return __awaiter(this, void 0, void 0, function* () { + const { url, headers } = this; + const body = yield this.body(); + // Adds the headers from the form-data body + if (body && body instanceof form_data_1.default) { + Object.assign(headers, body.getHeaders()); + } + return node_fetch_1.default(url, { + method: this.method, + body, + headers, + }); + }); + } + /** + * Returns the full URL after adding the parameters if required + * @type {string} + */ + get url() { + let url = `${constants_1.baseURL}${this.endpoint}`; + // Add the parameters to the URL if the HTTP method is included in 'paramsMethods' + if (this.params && paramsMethods.includes(this.method) && Object.keys(this.params).length) { + url += `?${querystring_1.default.encode(this.params)}`; + } + return url; + } + /** + * Returns the body of the request + * @returns {string | Promise | undefined} + */ + body() { + // Only return a body if the HTTP method is included in 'bodyMethods' + if (bodyMethods.includes(this.method) && this.params) { + return this.files ? this.bodyFiles() : JSON.stringify(this.params); + } + } + /** + * Returns a form-data body if files need to be sent + * @returns {Promise} + */ + bodyFiles() { + return __awaiter(this, void 0, void 0, function* () { + const { files, params } = this; + if (!files) + throw new Error('No files found!'); + const body = new form_data_1.default(); + // Adds all the files to the form-data + for (const file of files) { + body.append(file.name, yield file.read(), file.name); + } + // Adds additional params to the 'payload_json' field + if (params) { + body.append('payload_json', JSON.stringify(params)); + } + return body; + }); + } + /** + * Returns the headers required for the request + * @type {Record} + */ + get headers() { + // Default headers required for every request + const headers = { + 'X-RateLimit-Precision': 'millisecond', + Authorization: `Bot ${this.token}`, + }; + // Adds the Content-Type header if the request contains a body + if (this.body() && !this.files) + headers[Header.ContentType] = 'application/json'; + return headers; + } + /** + * Parses the given files into {@link APIFile} objects + * @param {RequestFile[]} files The files + * @returns {APIFile[] | undefined} + */ + static parseFiles(files) { + return files === null || files === void 0 ? void 0 : files.map(file => new APIFile_1.APIFile(file)); + } +} +exports.APIRequest = APIRequest; diff --git a/lib/api/APISerializer.d.ts b/lib/api/APISerializer.d.ts new file mode 100644 index 000000000..a374c2f65 --- /dev/null +++ b/lib/api/APISerializer.d.ts @@ -0,0 +1,185 @@ +import { Positions } from './BotAPI'; +import { Params } from './rateLimit'; +import { FetchGuildOptions, FetchInviteOptions, FetchReactionUsersOptions, FetchSomeMembersOptions, FetchSomeMessagesOptions } from '../controllers'; +import { InviteOptions, FetchGuildsOptions, ModifyBotUserOptions, RoleOptions, CreateWebhookOptions, ModifyWebhookOptions } from '../structures'; +import { CreateGuildChannelOptions, GuildChannelOptions } from '../structures/channels'; +import { Permissible, PermissionOverwriteFlags } from '../structures/flags'; +import { ModifyGuildOptions, PruneCountOptions, PruneOptions, CreateEmojiOptions, ModifyEmojiOptions } from '../structures/guild'; +import { CreateIntegrationOptions, ModifyIntegrationOptions, ModifyWidgetOptions } from '../structures/guild'; +import { MemberBanOptions, ModifyMemberOptions } from '../structures/member'; +import { MessageData } from '../structures/message'; +import { Snowflake } from '../types'; +/** + * Serializes API options and data into the API format + */ +export declare class APISerializer { + /** + * Serializes an array of role IDs and role instances into an array of role IDs + * @param {(Role | Snowflake)[]} roles The roles array + * @returns {Snowflake[]} + */ + private static roleIds; + /** + * Returns the serialized guild channel options for when modifying a guild channel + * @param {GuildChannelOptions} options The guild channel options + * @returns {Params} + */ + static guildChannelOptions(options: GuildChannelOptions): Params; + /** + * Returns the serialized fetch some messages options for when fetching some messages in a text channel + * @param {FetchSomeMessagesOptions} options The fetch some messages options + * @returns {Params} + */ + static fetchSomeMessagesOptions(options?: FetchSomeMessagesOptions): Params; + /** + * Returns the serialized message data for when sending or editing messages + * @param {MessageData} data The message data + * @returns {Params} + */ + static messageData(data: MessageData): Params; + /** + * Returns the serialized fetch reactions options for when fetching all users that reacted with a reaction + * @param {FetchReactionUsersOptions} options The fetch reaction users options + * @returns {Params} + */ + static fetchReactionUsersOptions(options?: FetchReactionUsersOptions): Params; + /** + * Returns the serialized guild channel permissions for when modifying a guild channel's permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The modified permissions + * @returns {Params} + */ + static guildChannelPermissions(permissible: Permissible, flags: PermissionOverwriteFlags): Params; + /** + * Returns the serialized create guild channel options for creating new guild channels + * @param {CreateGuildChannelOptions} options The create guild channel options + * @returns {Params} + */ + static createGuildChannelOptions(options: CreateGuildChannelOptions): Params; + /** + * Returns the serialized positions for when modifying lists positions + * @param {Positions} positions The new positions + * @returns {Params} + */ + static positions(positions: Positions): Params; + /** + * Returns the serialized invite options for when creating a guild channel invite + * @param {InviteOptions} options The invite options + * @returns {Params} + */ + static inviteOptions(options?: InviteOptions): Params; + /** + * Returns the serialized modify emoji options for modifying emojis + * @param {ModifyEmojiOptions} options The modify emoji options + * @returns {Params} + */ + static modifyEmojiOptions(options: ModifyEmojiOptions): Params; + /** + * Returns the serialized create emoji options for when creating emojis + * @param {CreateEmojiOptions} options The create emoji options + * @returns {Promise} + */ + static createEmojiOptions(options: CreateEmojiOptions): Promise; + /** + * Returns the serialized fetch guild options for when fetching a guild + * @param {FetchGuildOptions} options The fetch guild options + * @returns {Params} + */ + static fetchGuildOptions(options?: FetchGuildOptions): Params; + /** + * Returns the serialized modify guild options for when modifying a guild + * @param {ModifyGuildOptions} options The modify guild options + * @returns {Promise} + */ + static modifyGuildOptions(options: ModifyGuildOptions): Promise; + /** + * Returns the serialized fetch all members options for when fetching all members in a guild + * @param {ModifyGuildOptions} options The fetch all members options + * @returns {Params} + */ + static fetchSomeMembersOptions(options?: FetchSomeMembersOptions): Params; + /** + * Returns the serialized modify member options for when modifying guild members + * @param {ModifyMemberOptions} options The modify member options + * @returns {Params} + */ + static modifyMemberOptions(options: ModifyMemberOptions): Params; + /** + * Returns the serialized ban member options for when banning guild members + * @param {MemberBanOptions} options The ban member options + * @returns {Params} + */ + static banMemberOptions(options: MemberBanOptions): Params; + /** + * Returns the serialized role options for when creating or modifying roles + * @param {RoleOptions} options The role options + * @returns {Params} + */ + static roleOptions(options?: RoleOptions): Params; + /** + * Returns the serialized prune count options for when getting a guild prune count + * @param {PruneCountOptions} options The prune count options + * @returns {Params} + */ + static pruneCountOptions(options?: PruneCountOptions): Params; + /** + * Returns the serialized prune options for when beginning a guild prune operation + * @param {PruneOptions} options The prune options + * @returns {Params} + */ + static pruneOptions(options?: PruneOptions): Params; + /** + * Returns the serialized create integration options for when creating new guild integrations + * @param {CreateIntegrationOptions} options The create integration options + * @returns {Params} + */ + static createIntegrationOptions(options: CreateIntegrationOptions): Params; + /** + * Returns the serialized modify integration options for when modifying guild integrations + * @param {ModifyIntegrationOptions} options The modify integration options + * @returns {Params} + */ + static modifyIntegrationOptions(options: ModifyIntegrationOptions): Params; + /** + * Returns the serialized modify widget options for when modifying a guild widget + * @param {ModifyWidgetOptions} options The modify widget options + * @returns {Params} + */ + static modifyWidgetOptions(options: ModifyWidgetOptions): Params; + /** + * Returns the serialized modify bot user options for when modifying this bot's user + * @param {ModifyBotUserOptions} options The modify bot user options + * @returns {Promise} + */ + static modifyBotUserOptions(options: ModifyBotUserOptions): Promise; + /** + * Returns the serialized fetch guilds options for when fetching the guilds the bot's user is in + * @param {FetchGuildsOptions} options The fetch guilds options + * @returns {Params} + */ + static fetchGuildsOptions(options?: FetchGuildsOptions): Params; + /** + * Returns the serialized parameters for when creating a new DM channel + * @param {Snowflake} userId The ID of the DM channel recipient user + * @returns {Params} + */ + static createDM(userId: Snowflake): Params; + /** + * Returns the serialized fetch invite options for when fetching an invite + * @param {FetchInviteOptions} options The fetch invite options + * @returns {Params} + */ + static fetchInviteOptions(options?: FetchInviteOptions): Params; + /** + * Returns the serialized create webhook options for when creating webhooks + * @param {CreateWebhookOptions} options The create webhook options + * @returns {Promise} + */ + static createWebhookOptions(options: CreateWebhookOptions): Promise; + /** + * Returns the serialized modify webhook options for when modifying webhooks + * @param {ModifyWebhookOptions} options The modify webhook options + * @returns {Promise} + */ + static modifyWebhookOptions(options: ModifyWebhookOptions): Promise; +} diff --git a/lib/api/APISerializer.js b/lib/api/APISerializer.js new file mode 100644 index 000000000..92caaff87 --- /dev/null +++ b/lib/api/APISerializer.js @@ -0,0 +1,385 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.APISerializer = void 0; +const structures_1 = require("../structures"); +const message_1 = require("../structures/message"); +/** + * Serializes API options and data into the API format + */ +class APISerializer { + /** + * Serializes an array of role IDs and role instances into an array of role IDs + * @param {(Role | Snowflake)[]} roles The roles array + * @returns {Snowflake[]} + */ + static roleIds(roles) { + return roles.map((role) => (role instanceof structures_1.Role ? role.id : role)); + } + /** + * Returns the serialized guild channel options for when modifying a guild channel + * @param {GuildChannelOptions} options The guild channel options + * @returns {Params} + */ + static guildChannelOptions(options) { + return { + name: options.name, + type: options.type, + topic: options.topic, + nsfw: options.nsfw, + rate_limit_per_user: options.slowModeTimeout, + bitrate: options.bitrate, + user_limit: options.userLimit, + }; + } + /** + * Returns the serialized fetch some messages options for when fetching some messages in a text channel + * @param {FetchSomeMessagesOptions} options The fetch some messages options + * @returns {Params} + */ + static fetchSomeMessagesOptions(options) { + return (options && { + around: options.around, + before: options.before, + after: options.after, + limit: options.limit, + }); + } + /** + * Returns the serialized message data for when sending or editing messages + * @param {MessageData} data The message data + * @returns {Params} + */ + static messageData(data) { + const { content, embed } = data; + return { + content, + embed: embed && + (embed instanceof message_1.MessageEmbed ? embed.structure : message_1.MessageEmbed.dataToStructure(embed)), + }; + } + /** + * Returns the serialized fetch reactions options for when fetching all users that reacted with a reaction + * @param {FetchReactionUsersOptions} options The fetch reaction users options + * @returns {Params} + */ + static fetchReactionUsersOptions(options) { + return options && options; + } + /** + * Returns the serialized guild channel permissions for when modifying a guild channel's permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The modified permissions + * @returns {Params} + */ + static guildChannelPermissions(permissible, flags) { + var _a, _b; + return { + type: permissible.type, + allow: (_a = flags.allow) === null || _a === void 0 ? void 0 : _a.bits, + deny: (_b = flags.deny) === null || _b === void 0 ? void 0 : _b.bits, + }; + } + /** + * Returns the serialized create guild channel options for creating new guild channels + * @param {CreateGuildChannelOptions} options The create guild channel options + * @returns {Params} + */ + static createGuildChannelOptions(options) { + return { + name: options.name, + type: options.type, + topic: options.topic, + bitrate: options.bitrate, + user_limit: options.userLimit, + rate_limit_per_user: options.slowModeTimeout, + position: options.position, + permission_overwrites: options.permissions && + Object.entries(options.permissions).map(([id, overwrite]) => { + var _a, _b; + return ({ + id, + type: overwrite.type, + allow: (_a = overwrite.allow) === null || _a === void 0 ? void 0 : _a.bits, + deny: (_b = overwrite.deny) === null || _b === void 0 ? void 0 : _b.bits, + }); + }), + parent_id: options.parentId, + nsfw: options.nsfw, + }; + } + /** + * Returns the serialized positions for when modifying lists positions + * @param {Positions} positions The new positions + * @returns {Params} + */ + static positions(positions) { + return Object.entries(positions).map(([id, position]) => ({ id, position })); + } + /** + * Returns the serialized invite options for when creating a guild channel invite + * @param {InviteOptions} options The invite options + * @returns {Params} + */ + static inviteOptions(options) { + var _a, _b; + return options + ? { + max_age: (_a = options.max) === null || _a === void 0 ? void 0 : _a.age, + max_uses: (_b = options.max) === null || _b === void 0 ? void 0 : _b.uses, + temporary: options.temporary, + unique: options.unique, + } + : {}; + } + /** + * Returns the serialized modify emoji options for modifying emojis + * @param {ModifyEmojiOptions} options The modify emoji options + * @returns {Params} + */ + static modifyEmojiOptions(options) { + return { + name: options.name, + // Serialize the role IDs + roles: options.roles && APISerializer.roleIds(options.roles), + }; + } + /** + * Returns the serialized create emoji options for when creating emojis + * @param {CreateEmojiOptions} options The create emoji options + * @returns {Promise} + */ + static createEmojiOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + image: yield options.image.stringify(), + roles: options.roles, + }; + }); + } + /** + * Returns the serialized fetch guild options for when fetching a guild + * @param {FetchGuildOptions} options The fetch guild options + * @returns {Params} + */ + static fetchGuildOptions(options) { + return (options && { + with_counts: options.withCounts, + }); + } + /** + * Returns the serialized modify guild options for when modifying a guild + * @param {ModifyGuildOptions} options The modify guild options + * @returns {Promise} + */ + static modifyGuildOptions(options) { + var _a, _b, _c, _d, _e, _f; + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + region: options.region, + verification_level: (_a = options.levels) === null || _a === void 0 ? void 0 : _a.verification, + default_message_notifications: (_b = options.levels) === null || _b === void 0 ? void 0 : _b.notifications, + explicit_content_filter: (_c = options.levels) === null || _c === void 0 ? void 0 : _c.explicitContent, + afk_channel_id: (_e = (_d = options.afk) === null || _d === void 0 ? void 0 : _d.channel) === null || _e === void 0 ? void 0 : _e.id, + afk_timeout: (_f = options.afk) === null || _f === void 0 ? void 0 : _f.timeout, + icon: options.icon && (yield options.icon.stringify()), + owner_id: options.ownerId, + splash: options.splash && (yield options.splash.stringify()), + banner: options.banner && (yield options.banner.stringify()), + system_channel_id: options.systemChannelId, + rules_channel_id: options.rulesChannelId, + public_updates_channel_id: options.updatesChannelId, + preferred_locale: options.locale, + }; + }); + } + /** + * Returns the serialized fetch all members options for when fetching all members in a guild + * @param {ModifyGuildOptions} options The fetch all members options + * @returns {Params} + */ + static fetchSomeMembersOptions(options) { + return (options && { + limit: options.limit, + after: options.after, + }); + } + /** + * Returns the serialized modify member options for when modifying guild members + * @param {ModifyMemberOptions} options The modify member options + * @returns {Params} + */ + static modifyMemberOptions(options) { + return { + nick: options.nick, + // Serialize the role IDs + roles: options.roles && APISerializer.roleIds(options.roles), + mute: options.mute, + deaf: options.deaf, + channel_id: options.channelId, + }; + } + /** + * Returns the serialized ban member options for when banning guild members + * @param {MemberBanOptions} options The ban member options + * @returns {Params} + */ + static banMemberOptions(options) { + return { + reason: options.reason, + delete_message_days: options.deleteMessageDays, + }; + } + /** + * Returns the serialized role options for when creating or modifying roles + * @param {RoleOptions} options The role options + * @returns {Params} + */ + static roleOptions(options) { + var _a; + return (options && { + name: options.name, + permissions: (_a = options.permissions) === null || _a === void 0 ? void 0 : _a.bits, + color: options.color, + hoist: options.listedSeparately, + mentionable: options.mentionable, + }); + } + /** + * Returns the serialized prune count options for when getting a guild prune count + * @param {PruneCountOptions} options The prune count options + * @returns {Params} + */ + static pruneCountOptions(options) { + return (options && { + days: options.days, + include_roles: options.includeRoles, + }); + } + /** + * Returns the serialized prune options for when beginning a guild prune operation + * @param {PruneOptions} options The prune options + * @returns {Params} + */ + static pruneOptions(options) { + return (options && Object.assign(Object.assign({}, APISerializer.pruneCountOptions(options)), { compute_prune_count: options.computePruneCount })); + } + /** + * Returns the serialized create integration options for when creating new guild integrations + * @param {CreateIntegrationOptions} options The create integration options + * @returns {Params} + */ + static createIntegrationOptions(options) { + return { + type: options.type, + id: options.id, + }; + } + /** + * Returns the serialized modify integration options for when modifying guild integrations + * @param {ModifyIntegrationOptions} options The modify integration options + * @returns {Params} + */ + static modifyIntegrationOptions(options) { + var _a, _b; + return { + expire_behavior: (_a = options.expire) === null || _a === void 0 ? void 0 : _a.behavior, + expire_grace_period: (_b = options.expire) === null || _b === void 0 ? void 0 : _b.gracePeriod, + enable_emoticons: options.enableEmoticons, + }; + } + /** + * Returns the serialized modify widget options for when modifying a guild widget + * @param {ModifyWidgetOptions} options The modify widget options + * @returns {Params} + */ + static modifyWidgetOptions(options) { + return { + enabled: options.enabled, + channel_id: options.channelId, + }; + } + /** + * Returns the serialized modify bot user options for when modifying this bot's user + * @param {ModifyBotUserOptions} options The modify bot user options + * @returns {Promise} + */ + static modifyBotUserOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + username: options.username, + avatar: options.avatar && (yield options.avatar.stringify()), + }; + }); + } + /** + * Returns the serialized fetch guilds options for when fetching the guilds the bot's user is in + * @param {FetchGuildsOptions} options The fetch guilds options + * @returns {Params} + */ + static fetchGuildsOptions(options) { + return (options && { + before: options.before, + after: options.after, + limit: options.limit, + }); + } + /** + * Returns the serialized parameters for when creating a new DM channel + * @param {Snowflake} userId The ID of the DM channel recipient user + * @returns {Params} + */ + static createDM(userId) { + return { + recipient_id: userId, + }; + } + /** + * Returns the serialized fetch invite options for when fetching an invite + * @param {FetchInviteOptions} options The fetch invite options + * @returns {Params} + */ + static fetchInviteOptions(options) { + return (options && { + with_counts: options.withCounts, + }); + } + /** + * Returns the serialized create webhook options for when creating webhooks + * @param {CreateWebhookOptions} options The create webhook options + * @returns {Promise} + */ + static createWebhookOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + avatar: options.avatar && (yield options.avatar.stringify()), + }; + }); + } + /** + * Returns the serialized modify webhook options for when modifying webhooks + * @param {ModifyWebhookOptions} options The modify webhook options + * @returns {Promise} + */ + static modifyWebhookOptions(options) { + return __awaiter(this, void 0, void 0, function* () { + return { + name: options.name, + avatar: options.avatar && (yield options.avatar.stringify()), + channel_id: options.channelId, + }; + }); + } +} +exports.APISerializer = APISerializer; diff --git a/lib/api/BotAPI.d.ts b/lib/api/BotAPI.d.ts new file mode 100644 index 000000000..082e99f79 --- /dev/null +++ b/lib/api/BotAPI.d.ts @@ -0,0 +1,644 @@ +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { FetchGuildOptions, FetchInviteOptions, FetchReactionUsersOptions, FetchSomeMembersOptions, FetchSomeMessagesOptions } from '../controllers'; +import { EmojiResolvable, BotUser, FetchGuildsOptions, ModifyBotUserOptions, PartialGuild, Invite, InviteOptions, PermissionOverwrite, Role, RoleOptions, User, CreateWebhookOptions, Webhook, ModifyWebhookOptions } from '../structures'; +import { Channel, CreateGuildChannelOptions, DMChannel, GuildChannel, GuildChannelOptions } from '../structures/channels'; +import { Permissible, PermissionOverwriteFlags } from '../structures/flags'; +import { CreateEmojiOptions, Guild, GuildEmoji, GuildPreview, GuildVanityInvite, ModifyEmojiOptions, ModifyGuildOptions, PruneCountOptions, PruneOptions } from '../structures/guild'; +import { GuildBan, CreateIntegrationOptions, GuildIntegration, ModifyIntegrationOptions, GuildWidget, ModifyWidgetOptions } from '../structures/guild'; +import { Member, MemberBanOptions, ModifyMemberOptions } from '../structures/member'; +import { Message, MessageData, MessageEditData, MessageEmbed, MessageOptions } from '../structures/message'; +import { Snowflake } from '../types'; +/** + * New positions for a orderly listed values on Discord, such as guild channels or roles + * The key is the item's ID. + * The value is the item's new position. + * + * The positions are in a descending order ending at 0 + * @example + * // Guild channels positions + * { '702476896008405005': 1, '702476896008405005': 2 } + * @example + * // Roles positions + * { '706861476752785461': 2 } + */ +export declare type Positions = Record; +/** + * Creates all outgoing API requests + */ +export declare class BotAPI { + /** + * The bot instance + */ + private readonly bot; + /** + * The bot's token + */ + private readonly token; + /** + * Manages all outgoing API requests + */ + private readonly requests; + constructor(bot: Bot, token: string); + /** + * Fetches a channel by its ID + * @param {Snowflake} channelId The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetchChannel(channelId: Snowflake): Promise; + /** + * Fetches a guild channel by its ID + * @param {Snowflake} channelId The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetchGuildChannel(channelId: Snowflake): Promise; + /** + * Fetches a DM channel by its ID + * @param {Snowflake} channelId The ID of the DM channel you wish to fetch + * @returns {Promise} + */ + fetchDMChannel(channelId: Snowflake): Promise; + /** + * Updates a {@link GuildChannel}'s settings. Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the modified channel + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modifyGuildChannel(channelId: Snowflake, options: GuildChannelOptions): Promise; + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise} + */ + deleteChannel(channelId: Snowflake): Promise; + /** + * Deletes a {@link GuildChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the guild channel you wish to delete + * @returns {Promise} + */ + deleteGuildChannel(channelId: Snowflake): Promise; + /** + * Fetches some messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMessages(channelId: Snowflake, options?: FetchSomeMessagesOptions): Promise>; + /** + * Fetches a message in a text channel by their IDs + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message you wish to fetch + * @returns {Promise} + */ + fetchMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. + * If operating on a {@link GuildTextChannel}, this requires the {@link Permission.SendMessages} permission. + * If the {@link MessageOptions.tts} field is set to true, the {@link Permission.SendTTSMessages} permission is required + * @param {Snowflake} channelId The ID of the channel to send the message in + * @param {string | MessageData | MessageEmbed} data The message data. + * Can be: + * 1. Raw content to be sent as a message + * @example ```typescript + * channel.sendMessage('Hello World!'); + * ``` + * 2. A {@link MessageData} object, containing content and/or embed + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', embed: { title: 'My Embed!' } }); + * ``` + * 3. A {@link MessageEmbed} instance + * @param {MessageOptions} options The message's options + * @returns {Promise} + */ + sendMessage(channelId: Snowflake, data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** + * Creates a reaction for a message. This method requires the {@link Permission.ReadMessageHistory} permission to be present on the Bot. Additionally, if nobody else has reacted to the message using this emoji, this method requires the {@link Permission.AddReactions} permission to be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {string} emoji The emoji to react with to the message + * @returns {Promise} + */ + addMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: EmojiResolvable): Promise; + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {EmojiResolvable} emoji The emoji to delete from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + removeMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: EmojiResolvable, userId?: Snowflake): Promise; + /** + * Fetches a list of users that reacted with a particular emoji on a message + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message + * @param {string} emoji The emoji the users reacted with + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise>} + */ + fetchReactionUsers(channelId: Snowflake, messageId: Snowflake, emoji: string, options?: FetchReactionUsersOptions): Promise>; + /** + * Removes all reactions on a message. This method requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions + * @returns {Promise} + */ + removeMessageReactions(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Deletes all reactions for an emoji. This method requires the {@link Permission.ManageMessages} permission ot be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions for a given emoji + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + removeMessageReactionsEmoji(channelId: Snowflake, messageId: Snowflake, emoji: EmojiResolvable): Promise; + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to edit + * @param {Snowflake} messageId The ID of the message you wish to edit + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + editMessage(channelId: Snowflake, messageId: Snowflake, data: string | MessageEditData): Promise; + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to delete + * @param {Snowflake} messageId The ID of the message you wish to delete + * @returns {Promise} + */ + deleteMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The channel ID that contains the messages you wish to delete + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(channelId: Snowflake, messages: Snowflake[]): Promise; + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel for which to overwrite the permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions you wish to modify + * @returns {Promise} + */ + modifyGuildChannelPermissions(channelId: Snowflake, permissible: Permissible, flags: PermissionOverwriteFlags): Promise; + /** + * Fetches a list of invites for a channel. + * Requires the {@link Permission.ManageChannels} permission + * @param {Snowflake} channelId The ID of the channel to fetch invites in + * @returns {Promise>} + */ + fetchChannelInvites(channelId: Snowflake): Promise>; + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {Snowflake} channelId The ID of the channel to create the invite for + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + createChannelInvite(channelId: Snowflake, options?: InviteOptions): Promise; + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel that contains the permission overwrite you wish to delete + * @param {Snowflake} permissible The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + deleteGuildChannelPermission(channelId: Snowflake, permissible: Snowflake): Promise; + /** + * Posts a typing indicator for a specified text channel. + * Useful when the bot is responding to a command and expects the computation to take a few seconds. + * This method may be called to let the user know that the bot is processing their message. + * @param {Snowflake} channelId The ID of the text channel to trigger typing in + * @returns {Promise} + */ + triggerTextChannelTyping(channelId: Snowflake): Promise; + /** + * Fetches all pinned messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise>} + */ + fetchChannelPins(channelId: Snowflake): Promise>; + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to pin + * @param {Snowflake} messageId The ID of the message you wish to pin + * @returns {Promise} + */ + pinMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to unpin + * @param {Snowflake} messageId The ID of the message you wish to unpin + * @returns {Promise} + */ + unpinMessage(channelId: Snowflake, messageId: Snowflake): Promise; + /** + * Fetches all emojis in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildEmojis(guildId: Snowflake): Promise>; + /** + * Fetches an emoji in a given guild + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @returns {Promise} + */ + fetchGuildEmoji(guildId: Snowflake, emojiId: Snowflake): Promise; + /** + * Creates a new emoji for a guild. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateEmojiOptions} options The options for the new emoji + * @returns {Promise} + */ + createGuildEmoji(guildId: Snowflake, options: CreateEmojiOptions): Promise; + /** + * Modifies a given guild emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} The updated emoji + */ + modifyGuildEmoji(guildId: Snowflake, emojiId: Snowflake, options: ModifyEmojiOptions): Promise; + /** + * Deletes a given guild emoji + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji to delete + * @returns {Promise} + */ + deleteGuildEmoji(guildId: Snowflake, emojiId: Snowflake): Promise; + /** + * Fetches a guild by its ID and additional options + * @param {Snowflake} guildId The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetchGuild(guildId: Snowflake, options?: FetchGuildOptions): Promise; + /** + * Fetches a guild preview by its guild ID. + * This is only available for public guilds + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildPreview(guildId: Snowflake): Promise; + /** + * Modifies a guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modifyGuild(guildId: Snowflake, options: ModifyGuildOptions): Promise; + /** + * Fetches all guild channels in a given guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildChannels(guildId: Snowflake): Promise>; + /** + * Creates a new guild channel in a guild. + * Requires the {@link Permission.ManageChannels} + * @param {Snowflake} guildId The ID of the guild to create the channel in + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + createGuildChannel(guildId: Snowflake, options: CreateGuildChannelOptions): Promise; + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyGuildChannelsPositions(guildId: Snowflake, positions: Positions): Promise; + /** + * Fetches a guild member by its user ID + * @param {Snowflake} guildId The ID of the guild this member is in + * @param {Snowflake} userId The ID of the member user + * @returns {Promise} + */ + fetchMember(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Fetches all members in a guild + * @param {Snowflake} guildId The ID of the guild + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMembers(guildId: Snowflake, options?: FetchSomeMembersOptions): Promise>; + /** + * Modifies attributes of a guild member + * @param {Snowflake} guildId The ID of the guild that contains this member + * @param {Snowflake} userId The ID of the member user + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modifyMember(guildId: Snowflake, userId: Snowflake, options: ModifyMemberOptions): Promise; + /** + * Modify a guild member's nickname. + * Returns the modified nickname when changing this bot's nickname or void when changing another member's nickname + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyMemberNickname(guildId: Snowflake, userId: Snowflake, nick?: string): Promise; + /** + * Modifies the nickname of the bot user in a guild. + * Returns the modified nickname + * @param {Snowflake} guildId The ID of the guild + * @param {string} nick The new nickname for the bot + * @returns {Promise} + */ + modifyBotNickname(guildId: Snowflake, nick?: string): Promise; + /** + * Adds a role to a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberAddRole(guildId: Snowflake, userId: Snowflake, roleId: Snowflake): Promise; + /** + * Removes a role from a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberRemoveRole(guildId: Snowflake, userId: Snowflake, roleId: Snowflake): Promise; + /** + * Removes a member from a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to remove + * @returns {Promise} + */ + removeMember(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Fetches all bans in a guild by a guild ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildBans(guildId: Snowflake): Promise>; + /** + * Fetches a ban in a guild by a user ID + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + fetchGuildBan(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Bans a member from a guild, and optionally deletes the previous messages sent by the banner member. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + banMember(guildId: Snowflake, userId: Snowflake, options: MemberBanOptions): Promise; + /** + * Unbans a member from a guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to unban + * @returns {Promise} + */ + unbanMember(guildId: Snowflake, userId: Snowflake): Promise; + /** + * Fetches all roles in a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchRoles(guildId: Snowflake): Promise>; + /** + * Creates a new role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {RoleOptions} options The options for the created role + * @returns {Promise} + */ + createRole(guildId: Snowflake, options?: RoleOptions): Promise; + /** + * Modifies the positions of a set of roles for a guild. + * Requires the {@link Permission.ManageRoles} + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyRolesPositions(guildId: Snowflake, positions: Positions): Promise>; + /** + * Modifies a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @param {RoleOptions} options The options for the updated role + * @returns {Promise} The updated role + */ + modifyRole(guildId: Snowflake, roleId: Snowflake, options: RoleOptions): Promise; + /** + * Deletes a role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + deleteRole(guildId: Snowflake, roleId: Snowflake): Promise; + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {Snowflake} guildId The Id of the guild + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + guildPruneCount(guildId: Snowflake, options?: PruneCountOptions): Promise; + /** + * Begins a prune operation on a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + guildPrune(guildId: Snowflake, options?: PruneOptions): Promise; + /** + * Fetches all invites (with metadata) in a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildInvites(guildId: Snowflake): Promise>; + /** + * Fetches all guild integrations in a guild + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildIntegrations(guildId: Snowflake): Promise>; + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + createGuildIntegration(guildId: Snowflake, options: CreateIntegrationOptions): Promise; + /** + * Modifies the behavior and settings of a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the integration + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modifyGuildIntegration(guildId: Snowflake, integrationId: Snowflake, options: ModifyIntegrationOptions): Promise; + /** + * Deletes the attached integration for a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + deleteGuildIntegration(guildId: Snowflake, integrationId: Snowflake): Promise; + /** + * Syncs a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + syncGuildIntegration(guildId: Snowflake, integrationId: Snowflake): Promise; + /** + * Fetches a guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildWidget(guildId: Snowflake): Promise; + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modifyGuildWidget(guildId: Snowflake, options: ModifyWidgetOptions): Promise; + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildVanityURL(guildId: Snowflake): Promise; + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBotUser(): Promise; + /** + * Fetches a user by its ID + * @param {Snowflake} userId The user ID + * @returns {Promise} + */ + fetchUser(userId: Snowflake): Promise; + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modifyBotUser(options: ModifyBotUserOptions): Promise; + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchBotGuilds(options?: FetchGuildsOptions): Promise>; + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId: Snowflake): Promise; + /** + * Creates a new DM channel between a user and the bot user + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + createDM(userId: Snowflake): Promise; + /** + * Fetches an invite by its invite code + * @param {string} inviteCode The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetchInvite(inviteCode: string, options?: FetchInviteOptions): Promise; + /** + * Deletes an invite by its invite code. + * Requires the {@link Permission.ManageChannels} permission on the channel this invite belongs to, or {@link Permission.ManageGuild} to remove any invite across the guild + * @param {string} inviteCode The invite code + * @returns {Promise} + */ + deleteInvite(inviteCode: string): Promise; + /** + * Creates a new webhook for a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + createWebhook(channelId: Snowflake, options: CreateWebhookOptions): Promise; + /** + * Fetches all webhooks in a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @returns {Promise>} + */ + fetchWebhooks(channelId: Snowflake): Promise>; + /** + * Fetches all webhooks in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildWebhooks(guildId: Snowflake): Promise>; + /** + * Fetches a webhook by its ID + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + fetchWebhook(webhookId: Snowflake): Promise; + /** + * Modifies a webhook by its ID + * @param {Snowflake} webhookId The webhook ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modifyWebhook(webhookId: Snowflake, options: ModifyWebhookOptions): Promise; + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + deleteWebhook(webhookId: Snowflake): Promise; +} diff --git a/lib/api/BotAPI.js b/lib/api/BotAPI.js new file mode 100644 index 000000000..57a82d3ed --- /dev/null +++ b/lib/api/BotAPI.js @@ -0,0 +1,1118 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotAPI = void 0; +const APISerializer_1 = require("./APISerializer"); +const constants_1 = require("./constants"); +const endpoints_1 = require("./endpoints"); +const rateLimit_1 = require("./rateLimit"); +const Collection_1 = __importDefault(require("../Collection")); +const structures_1 = require("../structures"); +const channels_1 = require("../structures/channels"); +const utils_1 = require("../structures/channels/utils"); +const flags_1 = require("../structures/flags"); +const guild_1 = require("../structures/guild"); +const guild_2 = require("../structures/guild"); +const member_1 = require("../structures/member"); +const message_1 = require("../structures/message"); +/** + * Creates all outgoing API requests + */ +class BotAPI { + constructor(bot, token) { + this.bot = bot; + this.token = token; + this.requests = new rateLimit_1.Requests(this.bot, this.token); + } + /** + * Fetches a channel by its ID + * @param {Snowflake} channelId The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetchChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Get); + return new channels_1.Channel(this.bot, channel); + }); + } + /** + * Fetches a guild channel by its ID + * @param {Snowflake} channelId The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetchGuildChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.fetchChannel(channelId); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channel.structure, yield utils_1.ChannelUtils.getChannelGuild(this.bot, channel)); + }); + } + /** + * Fetches a DM channel by its ID + * @param {Snowflake} channelId The ID of the DM channel you wish to fetch + * @returns {Promise} + */ + fetchDMChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.fetchChannel(channelId); + return utils_1.ChannelUtils.createDMChannel(this.bot, channel.structure); + }); + } + /** + * Updates a {@link GuildChannel}'s settings. Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the modified channel + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modifyGuildChannel(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const channelData = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.guildChannelOptions(options)); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channelData, yield utils_1.ChannelUtils.getChannelGuild(this.bot, channelData)); + }); + } + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise} + */ + deleteChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channelData = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Delete); + return utils_1.ChannelUtils.create(this.bot, channelData); + }); + } + /** + * Deletes a {@link GuildChannel}. + * Requires the {@link Permission.ManageChannels} permission for the guild + * @param {Snowflake} channelId The ID of the guild channel you wish to delete + * @returns {Promise} + */ + deleteGuildChannel(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.deleteChannel(channelId); + if (!(channel instanceof channels_1.GuildChannel)) { + throw new TypeError('The deleted channel is not a guild channel'); + } + return channel; + }); + } + /** + * Fetches some messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMessages(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const messages = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMessagesOptions(options))); + const channel = yield this.bot.channels.getText(channelId); + return new Collection_1.default(messages.map(message => [message.id, new message_1.Message(this.bot, message, channel)])); + }); + } + /** + * Fetches a message in a text channel by their IDs + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message you wish to fetch + * @returns {Promise} + */ + fetchMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + const message = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Get); + const channel = yield this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, message, channel); + }); + } + /** + * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. + * If operating on a {@link GuildTextChannel}, this requires the {@link Permission.SendMessages} permission. + * If the {@link MessageOptions.tts} field is set to true, the {@link Permission.SendTTSMessages} permission is required + * @param {Snowflake} channelId The ID of the channel to send the message in + * @param {string | MessageData | MessageEmbed} data The message data. + * Can be: + * 1. Raw content to be sent as a message + * @example ```typescript + * channel.sendMessage('Hello World!'); + * ``` + * 2. A {@link MessageData} object, containing content and/or embed + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', embed: { title: 'My Embed!' } }); + * ``` + * 3. A {@link MessageEmbed} instance + * @param {MessageOptions} options The message's options + * @returns {Promise} + */ + sendMessage(channelId, data, options) { + return __awaiter(this, void 0, void 0, function* () { + // Default params to be sent in the request + let params = Object.assign({}, options); + let files; + if (typeof data === 'string') { + // The params should only include the raw content + params['content'] = data; + } + else if (data instanceof message_1.MessageEmbed) { + // The params should only include the given embed structure + params['embed'] = data.structure; + } + else { + // The params should include all given data fields + params = yield APISerializer_1.APISerializer.messageData(data); + files = data.files; + } + const messageData = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Post, params, files); + const channel = yield this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, messageData, channel); + }); + } + /** + * Creates a reaction for a message. This method requires the {@link Permission.ReadMessageHistory} permission to be present on the Bot. Additionally, if nobody else has reacted to the message using this emoji, this method requires the {@link Permission.AddReactions} permission to be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {string} emoji The emoji to react with to the message + * @returns {Promise} + */ + addMessageReaction(channelId, messageId, emoji) { + return __awaiter(this, void 0, void 0, function* () { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for addMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); + } + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { channelId, messageId, emoji: encodeURI(identifier) }, constants_1.HttpMethod.Put); + }); + } + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message to react to + * @param {EmojiResolvable} emoji The emoji to delete from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + removeMessageReaction(channelId, messageId, emoji, userId = '@me') { + return __awaiter(this, void 0, void 0, function* () { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for removeMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji}) user ${userId}`); + } + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { + channelId, + messageId, + emoji: encodeURI(identifier), + userId, + }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches a list of users that reacted with a particular emoji on a message + * @param {Snowflake} channelId The ID of the channel that contains the message + * @param {Snowflake} messageId The ID of the message + * @param {string} emoji The emoji the users reacted with + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise>} + */ + fetchReactionUsers(channelId, messageId, emoji, options) { + return __awaiter(this, void 0, void 0, function* () { + const users = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { + channelId, + messageId, + emoji, + }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchReactionUsersOptions(options))); + return new Collection_1.default(users.map(user => [user.id, new structures_1.User(this.bot, user)])); + }); + } + /** + * Removes all reactions on a message. This method requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions + * @returns {Promise} + */ + removeMessageReactions(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactions, { + channelId, + messageId, + }, constants_1.HttpMethod.Delete); + }); + } + /** + * Deletes all reactions for an emoji. This method requires the {@link Permission.ManageMessages} permission ot be present on the Bot. + * @param {Snowflake} channelId The ID of the channel containing the message + * @param {Snowflake} messageId The ID of the message of which to remove all reactions for a given emoji + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + removeMessageReactionsEmoji(channelId, messageId, emoji) { + return __awaiter(this, void 0, void 0, function* () { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for removeMessageReactionsEmoji request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); + } + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { + channelId, + messageId, + emoji: encodeURI(identifier), + }, constants_1.HttpMethod.Delete); + }); + } + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to edit + * @param {Snowflake} messageId The ID of the message you wish to edit + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + editMessage(channelId, messageId, data) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const params = {}; + if (typeof data === 'string') { + // The given data is the new message content + params['content'] = data; + } + else { + // The given data should be passed to the endpoint + Object.assign(params, Object.assign(Object.assign({}, APISerializer_1.APISerializer.messageData(data)), { flags: (_a = data.flags) === null || _a === void 0 ? void 0 : _a.bits })); + } + const messageData = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Patch, params); + const channel = yield this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, messageData, channel); + }); + } + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to delete + * @param {Snowflake} messageId The ID of the message you wish to delete + * @returns {Promise} + */ + deleteMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The channel ID that contains the messages you wish to delete + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(channelId, messages) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesBulkDelete, { channelId }, constants_1.HttpMethod.Post, { + messages, + }); + }); + } + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel for which to overwrite the permissions + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions you wish to modify + * @returns {Promise} + */ + modifyGuildChannelPermissions(channelId, permissible, flags) { + return __awaiter(this, void 0, void 0, function* () { + const params = APISerializer_1.APISerializer.guildChannelPermissions(permissible, flags); + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible.id }, constants_1.HttpMethod.Put, params); + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.PermissionOverwrite(this.bot, Object.assign(Object.assign({}, params), { id: permissible.id }), channel); + }); + } + /** + * Fetches a list of invites for a channel. + * Requires the {@link Permission.ManageChannels} permission + * @param {Snowflake} channelId The ID of the channel to fetch invites in + * @returns {Promise>} + */ + fetchChannelInvites(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const invites = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Get)); + return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite)])); + }); + } + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {Snowflake} channelId The ID of the channel to create the invite for + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + createChannelInvite(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.inviteOptions(options)); + return new structures_1.Invite(this.bot, invite); + }); + } + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} channelId The ID of the channel that contains the permission overwrite you wish to delete + * @param {Snowflake} permissible The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + deleteGuildChannelPermission(channelId, permissible) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible }, constants_1.HttpMethod.Delete); + }); + } + /** + * Posts a typing indicator for a specified text channel. + * Useful when the bot is responding to a command and expects the computation to take a few seconds. + * This method may be called to let the user know that the bot is processing their message. + * @param {Snowflake} channelId The ID of the text channel to trigger typing in + * @returns {Promise} + */ + triggerTextChannelTyping(channelId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelTyping, { channelId }, constants_1.HttpMethod.Post); + }); + } + /** + * Fetches all pinned messages in a text channel + * @param {Snowflake} channelId The ID of the channel + * @returns {Promise>} + */ + fetchChannelPins(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const pins = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelPins, { channelId }, constants_1.HttpMethod.Get)); + const channel = yield this.bot.channels.getText(channelId); + return new Collection_1.default(pins.map(pin => [pin.id, new message_1.Message(this.bot, pin, channel)])); + }); + } + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to pin + * @param {Snowflake} messageId The ID of the message you wish to pin + * @returns {Promise} + */ + pinMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Put); + }); + } + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} channelId The ID of the channel that contains the message you wish to unpin + * @param {Snowflake} messageId The ID of the message you wish to unpin + * @returns {Promise} + */ + unpinMessage(channelId, messageId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches all emojis in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildEmojis(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const emojis = (yield this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Get)); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(emojis.map(emoji => [emoji.id, new guild_1.GuildEmoji(this.bot, emoji, guild)])); + }); + } + /** + * Fetches an emoji in a given guild + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @returns {Promise} + */ + fetchGuildEmoji(guildId, emojiId) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); + }); + } + /** + * Creates a new emoji for a guild. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateEmojiOptions} options The options for the new emoji + * @returns {Promise} + */ + createGuildEmoji(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Post, yield APISerializer_1.APISerializer.createEmojiOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); + }); + } + /** + * Modifies a given guild emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} The updated emoji + */ + modifyGuildEmoji(guildId, emojiId, options) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyEmojiOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); + }); + } + /** + * Deletes a given guild emoji + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} emojiId The ID of the emoji to delete + * @returns {Promise} + */ + deleteGuildEmoji(guildId, emojiId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches a guild by its ID and additional options + * @param {Snowflake} guildId The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetchGuild(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const guild = yield this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildOptions(options)); + return new guild_1.Guild(this.bot, guild); + }); + } + /** + * Fetches a guild preview by its guild ID. + * This is only available for public guilds + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildPreview(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const preview = yield this.requests.send(endpoints_1.EndpointRoute.GuildPreview, { guildId }, constants_1.HttpMethod.Get); + return new guild_1.GuildPreview(this.bot, preview); + }); + } + /** + * Modifies a guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modifyGuild(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const guild = yield this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyGuildOptions(options)); + return new guild_1.Guild(this.bot, guild); + }); + } + /** + * Fetches all guild channels in a given guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildChannels(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const channels = (yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Get)); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(channels.map(channel => [ + channel.id, + utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild), + ])); + }); + } + /** + * Creates a new guild channel in a guild. + * Requires the {@link Permission.ManageChannels} + * @param {Snowflake} guildId The ID of the guild to create the channel in + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + createGuildChannel(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createGuildChannelOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild); + }); + } + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyGuildChannelsPositions(guildId, positions) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); + }); + } + /** + * Fetches a guild member by its user ID + * @param {Snowflake} guildId The ID of the guild this member is in + * @param {Snowflake} userId The ID of the member user + * @returns {Promise} + */ + fetchMember(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const member = yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new member_1.Member(this.bot, member, guild); + }); + } + /** + * Fetches all members in a guild + * @param {Snowflake} guildId The ID of the guild + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSomeMembers(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const members = (yield this.requests.send(endpoints_1.EndpointRoute.GuildMembers, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMembersOptions(options))); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(members.map(member => [member.user.id, new member_1.Member(this.bot, member, guild)])); + }); + } + /** + * Modifies attributes of a guild member + * @param {Snowflake} guildId The ID of the guild that contains this member + * @param {Snowflake} userId The ID of the member user + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modifyMember(guildId, userId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyMemberOptions(options)); + }); + } + /** + * Modify a guild member's nickname. + * Returns the modified nickname when changing this bot's nickname or void when changing another member's nickname + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyMemberNickname(guildId, userId, nick) { + var _a; + if (userId === ((_a = this.bot.user) === null || _a === void 0 ? void 0 : _a.id)) { + return this.modifyBotNickname(guildId, nick); + } + else { + return this.modifyMember(guildId, userId, { nick }); + } + } + /** + * Modifies the nickname of the bot user in a guild. + * Returns the modified nickname + * @param {Snowflake} guildId The ID of the guild + * @param {string} nick The new nickname for the bot + * @returns {Promise} + */ + modifyBotNickname(guildId, nick) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberBotNick, { guildId }, constants_1.HttpMethod.Patch, { + nick, + }); + }); + } + /** + * Adds a role to a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberAddRole(guildId, userId, roleId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Put); + }); + } + /** + * Removes a role from a guild member. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the member user + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + memberRemoveRole(guildId, userId, roleId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Removes a member from a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to remove + * @returns {Promise} + */ + removeMember(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches all bans in a guild by a guild ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildBans(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const bans = yield this.requests.send(endpoints_1.EndpointRoute.GuildBans, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(bans.map(ban => [ban.user.id, new guild_2.GuildBan(this.bot, ban, guild)])); + }); + } + /** + * Fetches a ban in a guild by a user ID + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + fetchGuildBan(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const ban = yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new guild_2.GuildBan(this.bot, ban, guild); + }); + } + /** + * Bans a member from a guild, and optionally deletes the previous messages sent by the banner member. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + banMember(guildId, userId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Put, APISerializer_1.APISerializer.banMemberOptions(options)); + }); + } + /** + * Unbans a member from a guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} userId The ID of the user to unban + * @returns {Promise} + */ + unbanMember(guildId, userId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Fetches all roles in a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchRoles(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const roles = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); + }); + } + /** + * Creates a new role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {RoleOptions} options The options for the created role + * @returns {Promise} + */ + createRole(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const role = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.roleOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new structures_1.Role(this.bot, role, guild); + }); + } + /** + * Modifies the positions of a set of roles for a guild. + * Requires the {@link Permission.ManageRoles} + * @param {Snowflake} guildId The ID of the guild + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyRolesPositions(guildId, positions) { + return __awaiter(this, void 0, void 0, function* () { + const roles = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); + }); + } + /** + * Modifies a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @param {RoleOptions} options The options for the updated role + * @returns {Promise} The updated role + */ + modifyRole(guildId, roleId, options) { + return __awaiter(this, void 0, void 0, function* () { + const role = yield this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.roleOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new structures_1.Role(this.bot, role, guild); + }); + } + /** + * Deletes a role in a guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + deleteRole(guildId, roleId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {Snowflake} guildId The Id of the guild + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + guildPruneCount(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const { pruned } = yield this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.pruneCountOptions(options)); + return pruned; + }); + } + /** + * Begins a prune operation on a guild. + * Requires the {@link Permission.KickMembers} permission + * @param {Snowflake} guildId The ID of the guild + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + guildPrune(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const { pruned } = yield this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.pruneOptions(options)); + return pruned; + }); + } + /** + * Fetches all invites (with metadata) in a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildInvites(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const invites = yield this.requests.send(endpoints_1.EndpointRoute.GuildInvites, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite, guild)])); + }); + } + /** + * Fetches all guild integrations in a guild + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildIntegrations(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const integrations = yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegrations, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new Collection_1.default(integrations.map(integration => [ + integration.id, + new guild_2.GuildIntegration(this.bot, integration, guild), + ])); + }); + } + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + createGuildIntegration(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createIntegrationOptions(options)); + }); + } + /** + * Modifies the behavior and settings of a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the integration + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modifyGuildIntegration(guildId, integrationId, options) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyIntegrationOptions(options)); + }); + } + /** + * Deletes the attached integration for a guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + deleteGuildIntegration(guildId, integrationId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Syncs a guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {Snowflake} integrationId The ID of the guild integration + * @returns {Promise} + */ + syncGuildIntegration(guildId, integrationId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegrationSync, { guildId, integrationId }, constants_1.HttpMethod.Post); + }); + } + /** + * Fetches a guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildWidget(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const widget = yield this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Get); + const guild = yield this.bot.guilds.get(guildId); + return new guild_2.GuildWidget(this.bot, widget, guild); + }); + } + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modifyGuildWidget(guildId, options) { + return __awaiter(this, void 0, void 0, function* () { + const widget = yield this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyWidgetOptions(options)); + const guild = yield this.bot.guilds.get(guildId); + return new guild_2.GuildWidget(this.bot, widget, guild); + }); + } + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + fetchGuildVanityURL(guildId) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.requests.send(endpoints_1.EndpointRoute.GuildVanityURL, { guildId }, constants_1.HttpMethod.Get); + }); + } + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBotUser() { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Get); + return new structures_1.BotUser(this.bot, user); + }); + } + /** + * Fetches a user by its ID + * @param {Snowflake} userId The user ID + * @returns {Promise} + */ + fetchUser(userId) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.requests.send(endpoints_1.EndpointRoute.User, { userId }, constants_1.HttpMethod.Get); + return new structures_1.User(this.bot, user); + }); + } + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modifyBotUser(options) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyBotUserOptions(options)); + return new structures_1.BotUser(this.bot, user); + }); + } + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchBotGuilds(options) { + return __awaiter(this, void 0, void 0, function* () { + const guilds = yield this.requests.send(endpoints_1.EndpointRoute.UserBotGuilds, {}, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildsOptions(options)); + return new Collection_1.default(guilds.map(guild => [ + guild.id, + { + id: guild.id, + name: guild.name, + icon: guild.icon, + owner: guild.owner, + permissions: new flags_1.PermissionFlags(guild.permissions_new), + }, + ])); + }); + } + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.UserBotGuild, { guildId }, constants_1.HttpMethod.Delete); + }); + } + /** + * Creates a new DM channel between a user and the bot user + * @param {Snowflake} userId The ID of the user + * @returns {Promise} + */ + createDM(userId) { + return __awaiter(this, void 0, void 0, function* () { + const dmChannel = yield this.requests.send(endpoints_1.EndpointRoute.UserBotChannels, {}, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createDM(userId)); + return new channels_1.DMChannel(this.bot, dmChannel); + }); + } + /** + * Fetches an invite by its invite code + * @param {string} inviteCode The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetchInvite(inviteCode, options) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchInviteOptions(options)); + return new structures_1.Invite(this.bot, invite); + }); + } + /** + * Deletes an invite by its invite code. + * Requires the {@link Permission.ManageChannels} permission on the channel this invite belongs to, or {@link Permission.ManageGuild} to remove any invite across the guild + * @param {string} inviteCode The invite code + * @returns {Promise} + */ + deleteInvite(inviteCode) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Delete); + return new structures_1.Invite(this.bot, invite); + }); + } + /** + * Creates a new webhook for a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + createWebhook(channelId, options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Post, yield APISerializer_1.APISerializer.createWebhookOptions(options)); + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); + }); + } + /** + * Fetches all webhooks in a guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} channelId The ID of the guild channel + * @returns {Promise>} + */ + fetchWebhooks(channelId) { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Get); + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new Collection_1.default(webhooks.map(webhook => [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)])); + }); + } + /** + * Fetches all webhooks in a guild + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise>} + */ + fetchGuildWebhooks(guildId) { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.requests.send(endpoints_1.EndpointRoute.GuildWebhooks, { guildId }, constants_1.HttpMethod.Get); + return new Collection_1.default(yield Promise.all(webhooks.map((webhook) => __awaiter(this, void 0, void 0, function* () { + const { channel_id: channelId } = webhook; + // Fetch the guild channel associated to this webhook + const channel = yield this.bot.channels.getGuildChannel(channelId); + return [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)]; + })))); + }); + } + /** + * Fetches a webhook by its ID + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + fetchWebhook(webhookId) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Get); + const { channel_id: channelId } = webhook; + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); + }); + } + /** + * Modifies a webhook by its ID + * @param {Snowflake} webhookId The webhook ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modifyWebhook(webhookId, options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyWebhookOptions(options)); + const { channel_id: channelId } = webhook; + const channel = yield this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); + }); + } + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} webhookId The ID of the webhook + * @returns {Promise} + */ + deleteWebhook(webhookId) { + return __awaiter(this, void 0, void 0, function* () { + yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Delete); + }); + } +} +exports.BotAPI = BotAPI; diff --git a/lib/api/constants.d.ts b/lib/api/constants.d.ts new file mode 100644 index 000000000..97607bc4a --- /dev/null +++ b/lib/api/constants.d.ts @@ -0,0 +1,18 @@ +/** + * The latest version of the Discord API + */ +export declare const version = 6; +/** + * The base URL for the Discord API + */ +export declare const baseURL: string; +/** + * All HTTP methods the API supports + */ +export declare enum HttpMethod { + Get = "GET", + Post = "POST", + Put = "PUT", + Patch = "PATCH", + Delete = "DELETE" +} diff --git a/lib/api/constants.js b/lib/api/constants.js new file mode 100644 index 000000000..e58fc5e7c --- /dev/null +++ b/lib/api/constants.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HttpMethod = exports.baseURL = exports.version = void 0; +/** + * The latest version of the Discord API + */ +exports.version = 6; +/** + * The base URL for the Discord API + */ +exports.baseURL = `https://discord.com/api/v${exports.version}`; +/** + * All HTTP methods the API supports + */ +var HttpMethod; +(function (HttpMethod) { + HttpMethod["Get"] = "GET"; + HttpMethod["Post"] = "POST"; + HttpMethod["Put"] = "PUT"; + HttpMethod["Patch"] = "PATCH"; + HttpMethod["Delete"] = "DELETE"; +})(HttpMethod = exports.HttpMethod || (exports.HttpMethod = {})); diff --git a/lib/api/endpoints.d.ts b/lib/api/endpoints.d.ts new file mode 100644 index 000000000..3ce099640 --- /dev/null +++ b/lib/api/endpoints.d.ts @@ -0,0 +1,93 @@ +/** + * All Endpoint routes for the Discord API. + * Every route is appropriate for all HTTP methods that the API supports + */ +export declare enum EndpointRoute { + Channel = "/channels/{channel.id}", + ChannelMessage = "/channels/{channel.id}/messages/{message.id}", + ChannelMessages = "/channels/{channel.id}/messages", + ChannelMessagesReactions = "/channels/{channel.id}/messages/{message.id}/reactions", + ChannelMessagesReactionsEmoji = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}", + ChannelMessagesReactionsEmojiUser = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id}", + ChannelMessagesBulkDelete = "/channels/{channel.id}/messages/bulk-delete", + ChannelPermissionsOverwrite = "/channels/{channel.id}/permissions/{overwrite.id}", + ChannelInvites = "/channels/{channel.id}/invites", + ChannelTyping = "/channels/{channel.id}/typing", + ChannelPins = "/channels/{channel.id}/pins", + ChannelPinsMessage = "/channels/{channel.id}/pins/{message.id}", + GuildEmojis = "/guilds/{guild.id}/emojis", + GuildEmoji = "/guilds/{guild.id}/emojis/{emoji.id}", + Guild = "/guilds/{guild.id}", + GuildPreview = "/guilds/{guild.id}/preview", + GuildChannels = "/guilds/{guild.id}/channels", + GuildMembers = "/guilds/{guild.id}/members", + GuildMember = "/guilds/{guild.id}/members/{user.id}", + GuildMemberBotNick = "/guilds/{guild.id}/members/@me/nick", + GuildMemberRole = "/guilds/{guild.id}/members/{user.id}/roles/{role.id}", + GuildBans = "/guilds/{guild.id}/bans", + GuildBan = "/guilds/{guild.id}/bans/{user.id}", + GuildRoles = "/guilds/{guild.id}/roles", + GuildRole = "/guilds/{guild.id}/roles/{role.id}", + GuildPrune = "/guilds/{guild.id}/prune", + GuildInvites = "/guilds/{guild.id}/invites", + GuildIntegrations = "/guilds/{guild.id}/integrations", + GuildIntegration = "/guilds/{guild.id}/integrations/{integration.id}", + GuildIntegrationSync = "/guilds/{guild.id}/integrations/{integration.id}/sync", + GuildWidget = "/guilds/{guild.id}/widget", + GuildVanityURL = "/guilds/{guild.id}/vanity-url", + UserBot = "/users/@me", + User = "/users/{user.id}", + UserBotGuilds = "/users/@me/guilds", + UserBotGuild = "/users/@me/guild/{guild.id}", + UserBotChannels = "/users/@me/channels", + Invite = "/invites/{invite.code}", + ChannelWebhooks = "/channels/{channel.id}/webhooks", + GuildWebhooks = "/guilds/{guild.id}/webhooks", + Webhook = "/webhooks/{webhook.id}" +} +/** + * All endpoints mapped by their route name + * @type {Record string>} + */ +export declare const Endpoints: Record string>; +/** + * All headers used to identifier the rate limit information of the request + */ +export declare enum RateLimitHeaders { + Global = "x-ratelimit-global", + Limit = "x-ratelimit-limit", + Remaining = "x-ratelimit-remaining", + Reset = "x-ratelimit-reset", + ResetAfter = "x-ratelimit-reset-after", + Bucket = "x-ratelimit-bucket" +} +/** + * All status codes that might be returned in response to an API request + */ +export declare enum StatusCode { + /** + * The request was successful + */ + OK = 200, + /** + * The request was successful and led to the creation of a resource + */ + Created = 201, + /** + * The request succeeded with no content as response + * @type {number} + */ + NoContent = 204, + /** + * The token is no longer valid + */ + UnAuthorized = 401, + /** + * The bot has insufficient permissions to send this request + */ + Forbidden = 403, + /** + * The rate limit has been reached + */ + TooManyRequests = 429 +} diff --git a/lib/api/endpoints.js b/lib/api/endpoints.js new file mode 100644 index 000000000..8a06fb9c5 --- /dev/null +++ b/lib/api/endpoints.js @@ -0,0 +1,141 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StatusCode = exports.RateLimitHeaders = exports.Endpoints = exports.EndpointRoute = void 0; +/** + * All Endpoint routes for the Discord API. + * Every route is appropriate for all HTTP methods that the API supports + */ +var EndpointRoute; +(function (EndpointRoute) { + EndpointRoute["Channel"] = "/channels/{channel.id}"; + EndpointRoute["ChannelMessage"] = "/channels/{channel.id}/messages/{message.id}"; + EndpointRoute["ChannelMessages"] = "/channels/{channel.id}/messages"; + EndpointRoute["ChannelMessagesReactions"] = "/channels/{channel.id}/messages/{message.id}/reactions"; + EndpointRoute["ChannelMessagesReactionsEmoji"] = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}"; + EndpointRoute["ChannelMessagesReactionsEmojiUser"] = "/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id}"; + EndpointRoute["ChannelMessagesBulkDelete"] = "/channels/{channel.id}/messages/bulk-delete"; + EndpointRoute["ChannelPermissionsOverwrite"] = "/channels/{channel.id}/permissions/{overwrite.id}"; + EndpointRoute["ChannelInvites"] = "/channels/{channel.id}/invites"; + EndpointRoute["ChannelTyping"] = "/channels/{channel.id}/typing"; + EndpointRoute["ChannelPins"] = "/channels/{channel.id}/pins"; + EndpointRoute["ChannelPinsMessage"] = "/channels/{channel.id}/pins/{message.id}"; + EndpointRoute["GuildEmojis"] = "/guilds/{guild.id}/emojis"; + EndpointRoute["GuildEmoji"] = "/guilds/{guild.id}/emojis/{emoji.id}"; + EndpointRoute["Guild"] = "/guilds/{guild.id}"; + EndpointRoute["GuildPreview"] = "/guilds/{guild.id}/preview"; + EndpointRoute["GuildChannels"] = "/guilds/{guild.id}/channels"; + EndpointRoute["GuildMembers"] = "/guilds/{guild.id}/members"; + EndpointRoute["GuildMember"] = "/guilds/{guild.id}/members/{user.id}"; + EndpointRoute["GuildMemberBotNick"] = "/guilds/{guild.id}/members/@me/nick"; + EndpointRoute["GuildMemberRole"] = "/guilds/{guild.id}/members/{user.id}/roles/{role.id}"; + EndpointRoute["GuildBans"] = "/guilds/{guild.id}/bans"; + EndpointRoute["GuildBan"] = "/guilds/{guild.id}/bans/{user.id}"; + EndpointRoute["GuildRoles"] = "/guilds/{guild.id}/roles"; + EndpointRoute["GuildRole"] = "/guilds/{guild.id}/roles/{role.id}"; + EndpointRoute["GuildPrune"] = "/guilds/{guild.id}/prune"; + EndpointRoute["GuildInvites"] = "/guilds/{guild.id}/invites"; + EndpointRoute["GuildIntegrations"] = "/guilds/{guild.id}/integrations"; + EndpointRoute["GuildIntegration"] = "/guilds/{guild.id}/integrations/{integration.id}"; + EndpointRoute["GuildIntegrationSync"] = "/guilds/{guild.id}/integrations/{integration.id}/sync"; + EndpointRoute["GuildWidget"] = "/guilds/{guild.id}/widget"; + EndpointRoute["GuildVanityURL"] = "/guilds/{guild.id}/vanity-url"; + EndpointRoute["UserBot"] = "/users/@me"; + EndpointRoute["User"] = "/users/{user.id}"; + EndpointRoute["UserBotGuilds"] = "/users/@me/guilds"; + EndpointRoute["UserBotGuild"] = "/users/@me/guild/{guild.id}"; + EndpointRoute["UserBotChannels"] = "/users/@me/channels"; + EndpointRoute["Invite"] = "/invites/{invite.code}"; + EndpointRoute["ChannelWebhooks"] = "/channels/{channel.id}/webhooks"; + EndpointRoute["GuildWebhooks"] = "/guilds/{guild.id}/webhooks"; + EndpointRoute["Webhook"] = "/webhooks/{webhook.id}"; +})(EndpointRoute = exports.EndpointRoute || (exports.EndpointRoute = {})); +/** + * All endpoints mapped by their route name + * @type {Record string>} + */ +exports.Endpoints = { + [EndpointRoute.Channel]: (channelId) => `/channels/${channelId}`, + [EndpointRoute.ChannelMessage]: (channelId, messageId) => `/channels/${channelId}/messages/${messageId}`, + [EndpointRoute.ChannelMessages]: (channelId) => `/channels/${channelId}/messages`, + [EndpointRoute.ChannelMessagesReactions]: (channelId, messageId) => `/channels/${channelId}/messages/${messageId}/reactions`, + [EndpointRoute.ChannelMessagesReactionsEmoji]: (channelId, messageId, emoji) => `/channels/${channelId}/messages/${messageId}/reactions/${emoji}`, + [EndpointRoute.ChannelMessagesReactionsEmojiUser]: (channelId, messageId, emoji, userId = '@me') => `/channels/${channelId}/messages/${messageId}/reactions/${emoji}/${userId}`, + [EndpointRoute.ChannelMessagesBulkDelete]: (channelId) => `/channels/${channelId}/messages/bulk-delete`, + [EndpointRoute.ChannelPermissionsOverwrite]: (channelId, overwriteId) => `/channels/${channelId}/permissions/${overwriteId}`, + [EndpointRoute.ChannelInvites]: (channelId) => `/channels/${channelId}/invites`, + [EndpointRoute.ChannelTyping]: (channelId) => `/channels/${channelId}/typing`, + [EndpointRoute.ChannelPins]: (channelId) => `/channels/${channelId}/pins`, + [EndpointRoute.ChannelPinsMessage]: (channelId, messageId) => `/channels/${channelId}/pins/${messageId}`, + [EndpointRoute.GuildEmojis]: (guildId) => `/guilds/${guildId}/emojis`, + [EndpointRoute.GuildEmoji]: (guildId, emojiId) => `/guilds/${guildId}/emojis/${emojiId}`, + [EndpointRoute.Guild]: (guildId) => `/guilds/${guildId}`, + [EndpointRoute.GuildPreview]: (guildId) => `/guilds/${guildId}/preview`, + [EndpointRoute.GuildChannels]: (guildId) => `/guilds/${guildId}/channels`, + [EndpointRoute.GuildMembers]: (guildId) => `/guilds/${guildId}/members`, + [EndpointRoute.GuildMember]: (guildId, userId) => `/guilds/${guildId}/members/${userId}`, + [EndpointRoute.GuildMemberBotNick]: (guildId) => `/guilds/${guildId}/members/@me/nick`, + [EndpointRoute.GuildMemberRole]: (guildId, userId, roleId) => `/guilds/${guildId}/members/${userId}/roles/${roleId}`, + [EndpointRoute.GuildBans]: (guildId) => `/guilds/${guildId}/bans`, + [EndpointRoute.GuildBan]: (guildId, userId) => `/guilds/${guildId}/bans/${userId}`, + [EndpointRoute.GuildRoles]: (guildId) => `/guilds/${guildId}/roles`, + [EndpointRoute.GuildRole]: (guildId, roleId) => `/guilds/${guildId}/roles/${roleId}`, + [EndpointRoute.GuildPrune]: (guildId) => `/guilds/${guildId}/prune`, + [EndpointRoute.GuildInvites]: (guildId) => `/guilds/${guildId}/invites`, + [EndpointRoute.GuildIntegrations]: (guildId) => `/guilds/${guildId}/integrations`, + [EndpointRoute.GuildIntegration]: (guildId, integrationId) => `/guilds/${guildId}/integrations/${integrationId}`, + [EndpointRoute.GuildIntegrationSync]: (guildId, integrationId) => `/guilds/${guildId}/integrations/${integrationId}/sync`, + [EndpointRoute.GuildWidget]: (guildId) => `/guilds/${guildId}/widget`, + [EndpointRoute.GuildVanityURL]: (guildId) => `/guilds/${guildId}/vanity-url`, + [EndpointRoute.UserBot]: () => `/users/@me`, + [EndpointRoute.User]: (userId) => `/users/${userId}`, + [EndpointRoute.UserBotGuilds]: () => `/users/@me/guilds`, + [EndpointRoute.UserBotGuild]: (guildId) => `/users/@me/guilds/${guildId}`, + [EndpointRoute.UserBotChannels]: () => `/users/@me/channels`, + [EndpointRoute.Invite]: (inviteCode) => `/invites/${inviteCode}`, + [EndpointRoute.ChannelWebhooks]: (channelId) => `/channels/${channelId}/webhooks`, + [EndpointRoute.GuildWebhooks]: (guildId) => `/guilds/${guildId}/webhooks`, + [EndpointRoute.Webhook]: (webhookId) => `/webhooks/${webhookId}`, +}; +/** + * All headers used to identifier the rate limit information of the request + */ +var RateLimitHeaders; +(function (RateLimitHeaders) { + RateLimitHeaders["Global"] = "x-ratelimit-global"; + RateLimitHeaders["Limit"] = "x-ratelimit-limit"; + RateLimitHeaders["Remaining"] = "x-ratelimit-remaining"; + RateLimitHeaders["Reset"] = "x-ratelimit-reset"; + RateLimitHeaders["ResetAfter"] = "x-ratelimit-reset-after"; + RateLimitHeaders["Bucket"] = "x-ratelimit-bucket"; +})(RateLimitHeaders = exports.RateLimitHeaders || (exports.RateLimitHeaders = {})); +/** + * All status codes that might be returned in response to an API request + */ +var StatusCode; +(function (StatusCode) { + /** + * The request was successful + */ + StatusCode[StatusCode["OK"] = 200] = "OK"; + /** + * The request was successful and led to the creation of a resource + */ + StatusCode[StatusCode["Created"] = 201] = "Created"; + /** + * The request succeeded with no content as response + * @type {number} + */ + StatusCode[StatusCode["NoContent"] = 204] = "NoContent"; + /** + * The token is no longer valid + */ + StatusCode[StatusCode["UnAuthorized"] = 401] = "UnAuthorized"; + /** + * The bot has insufficient permissions to send this request + */ + StatusCode[StatusCode["Forbidden"] = 403] = "Forbidden"; + /** + * The rate limit has been reached + */ + StatusCode[StatusCode["TooManyRequests"] = 429] = "TooManyRequests"; +})(StatusCode = exports.StatusCode || (exports.StatusCode = {})); diff --git a/lib/api/index.d.ts b/lib/api/index.d.ts new file mode 100644 index 000000000..9adf92c90 --- /dev/null +++ b/lib/api/index.d.ts @@ -0,0 +1,5 @@ +export * from './rateLimit'; +export * from './constants'; +export * from './endpoints'; +export * from './APISerializer'; +export * from './BotAPI'; diff --git a/lib/api/index.js b/lib/api/index.js new file mode 100644 index 000000000..1d3e5cbcc --- /dev/null +++ b/lib/api/index.js @@ -0,0 +1,17 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./rateLimit"), exports); +__exportStar(require("./constants"), exports); +__exportStar(require("./endpoints"), exports); +__exportStar(require("./APISerializer"), exports); +__exportStar(require("./BotAPI"), exports); diff --git a/lib/api/rateLimit/RateLimitBucket.d.ts b/lib/api/rateLimit/RateLimitBucket.d.ts new file mode 100644 index 000000000..64ed0ae79 --- /dev/null +++ b/lib/api/rateLimit/RateLimitBucket.d.ts @@ -0,0 +1,67 @@ +import { Params, RequestFile, ReturnedData } from './Requests'; +import { Bot } from '../../bot'; +import { HttpMethod } from '../constants'; +import { StatusCode, EndpointRoute } from '../endpoints'; +export declare const ValidCodes: StatusCode[]; +export declare class RateLimitBucket { + /** + * The bot instance + */ + private readonly bot; + /** + * The bot's token + */ + private readonly token; + /** + * The queue associated to this bucket + */ + private readonly queue; + /** + * The route this bucket references + */ + private readonly route; + /** + * The HTTP method this bucket references + */ + private readonly method; + /** + * The number of remaining requests this bucket has until the next reset {@link reset} + */ + remaining: number | undefined; + /** + * The number of total requests that can be made + */ + private limit; + /** + * The Unix timestamp of the next refill for this bucket + */ + private reset; + /** + * The current setTimeout that will run once the next refill is reached + */ + private timeout; + constructor(bot: Bot, token: string, route: EndpointRoute, method: HttpMethod); + /** + * Creates a new API request and sends it if the bucket is capable of doing so + * @param {string} endpoint The request endpoint + * @param {Params} params The request params / body + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(endpoint: string, params: Params, files?: RequestFile[]): Promise; + /** + * Refill this bucket + */ + private refillBucket; + /** + * Validates the response. Throws an error in case it is not valid + * @param {Response} response The response received from sending an API request + * @param {ReturnedData} json The parsed response in JSON + */ + private validateResponse; + /** + * Sets the rate limit information given from the response's headers + * @param {Headers} headers The response's headers + */ + private setLimits; +} diff --git a/lib/api/rateLimit/RateLimitBucket.js b/lib/api/rateLimit/RateLimitBucket.js new file mode 100644 index 000000000..ec1726f7f --- /dev/null +++ b/lib/api/rateLimit/RateLimitBucket.js @@ -0,0 +1,132 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RateLimitBucket = exports.ValidCodes = void 0; +const util_1 = __importDefault(require("util")); +const RateLimitQueue_1 = require("./RateLimitQueue"); +const APIRequest_1 = require("../APIRequest"); +const endpoints_1 = require("../endpoints"); +exports.ValidCodes = [endpoints_1.StatusCode.OK, endpoints_1.StatusCode.Created, endpoints_1.StatusCode.NoContent]; +class RateLimitBucket { + constructor(bot, token, route, method) { + this.bot = bot; + this.token = token; + this.queue = new RateLimitQueue_1.RateLimitQueue(this); + this.route = route; + this.method = method; + /* + Initialize the number of remaining requests to 1 + in order to determine the 'limit' and 'remaining' values after sending the first request. + */ + this.remaining = 1; + } + /** + * Creates a new API request and sends it if the bucket is capable of doing so + * @param {string} endpoint The request endpoint + * @param {Params} params The request params / body + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(endpoint, params, files) { + return __awaiter(this, void 0, void 0, function* () { + // The rate limit is reached. Add request to the queue + if (this.remaining !== undefined && this.remaining <= 0) { + this.bot.debug(`You reached the rate limit for ${endpoint}. Your request will still go through, but make sure you don't do this again!`); + return this.queue.add(endpoint, params, files); + } + // Decrements the remaining number of requests (to later be updated by the fixed value) + if (this.remaining) { + this.remaining--; + } + // Creates a new API request + const apiRequest = new APIRequest_1.APIRequest(this.token, endpoint, params, this.method, APIRequest_1.APIRequest.parseFiles(files)); + const response = yield apiRequest.send(); + // Sets the rate limit information given from the response's headers + this.setLimits(response.headers); + const json = response.status !== endpoints_1.StatusCode.NoContent + ? (yield response.json()) + : undefined; + if (json) { + // Validates the response before proceeding + this.validateResponse(response, json); + } + return json; + }); + } + /** + * Refill this bucket + */ + refillBucket() { + return __awaiter(this, void 0, void 0, function* () { + // Set the remaining number of requests in this bucket back to its limit + this.remaining = this.limit; + // Delete the stored setTimeout function + this.timeout = undefined; + // Empties the queue + yield this.queue.free(); + }); + } + /** + * Validates the response. Throws an error in case it is not valid + * @param {Response} response The response received from sending an API request + * @param {ReturnedData} json The parsed response in JSON + */ + validateResponse(response, json) { + switch (response.status) { + case endpoints_1.StatusCode.UnAuthorized: + this.bot.connection.disconnectAll(4004 /* AuthenticationFailed */); + throw new Error('Your Bot token is invalid! This connection has been terminated'); + case endpoints_1.StatusCode.Forbidden: + throw new Error(`The request to ${response.url} was rejected to to insufficient bot permissions`); + case endpoints_1.StatusCode.TooManyRequests: + throw new Error("You have reached Discord's API rate limit. Please slow down"); + } + if (!exports.ValidCodes.includes(response.status)) { + // Debug the json response + this.bot.debug('Error!', json); + if (Array.isArray(json)) { + throw new TypeError(`${response.url} - an error has occurred with an array response type - ${util_1.default.inspect(json)}`); + } + else { + throw new Error(`${response.url} (${response.status} code) - ${json.message || json.content || util_1.default.inspect(json)}`); + } + } + } + // TODO: Global rate limit + /** + * Sets the rate limit information given from the response's headers + * @param {Headers} headers The response's headers + */ + setLimits(headers) { + // Retrieve all the rate limit information from the request headers + const remaining = headers.get(endpoints_1.RateLimitHeaders.Remaining); + const limit = headers.get(endpoints_1.RateLimitHeaders.Limit); + const reset = headers.get(endpoints_1.RateLimitHeaders.Reset); + const resetAfter = headers.get(endpoints_1.RateLimitHeaders.ResetAfter); + // Set the parsed value of the rate limit information + // Use the cached remaining number if this is not the first request for async requests + this.remaining = this.limit ? this.remaining : remaining ? parseInt(remaining) : undefined; + this.limit = limit ? parseInt(limit) : undefined; + this.reset = reset ? parseFloat(reset) : undefined; + if (!this.reset && !resetAfter) + return; + // Initialize the timeout for the bucket's refill if one hasn't been initialized before + if (!this.timeout) { + // The timeout until the bucket refills + const refillTimeout = ((resetAfter && parseFloat(resetAfter)) || Date.now() - this.reset) * 1000; + this.timeout = setTimeout(this.refillBucket.bind(this), refillTimeout); + } + } +} +exports.RateLimitBucket = RateLimitBucket; diff --git a/lib/api/rateLimit/RateLimitQueue.d.ts b/lib/api/rateLimit/RateLimitQueue.d.ts new file mode 100644 index 000000000..be5936161 --- /dev/null +++ b/lib/api/rateLimit/RateLimitQueue.d.ts @@ -0,0 +1,48 @@ +import { RateLimitBucket } from './RateLimitBucket'; +import { Params, RequestFile, ReturnedData } from './Requests'; +/** + * The items that the rate limit queue stores + */ +export interface RateLimitQueueItem { + /** + * The endpoint for this API request + */ + endpoint: string; + /** + * The params / body for this API request + */ + params: Params; + /** + * The files sent in the API request + */ + files?: RequestFile[]; + /** + * Callback function to be called whenever the request is made + * @param {never} data + */ + callback: (data: never) => void; +} +/** + * The rate limit queue + * Receives all overlapping API requests and ultimately sends them after the the bucket refills + */ +export declare class RateLimitQueue extends Array { + /** + * The bucket instance that initialized this queue + */ + private readonly bucket; + constructor(bucket: RateLimitBucket); + /** + * Adds a new API request to the queue and waits until it executes + * @param {string} endpoint The endpoint for the added API request + * @param {Params} params The params / body for the added API request + * @param {RequestFile[]} files The files sent in the API request + * @returns {Promise} + */ + add(endpoint: string, params: Params, files?: RequestFile[]): Promise; + /** + * Frees the queue for the remaining capacity of the bucket + * @returns {Promise} + */ + free(): Promise; +} diff --git a/lib/api/rateLimit/RateLimitQueue.js b/lib/api/rateLimit/RateLimitQueue.js new file mode 100644 index 000000000..712c2341f --- /dev/null +++ b/lib/api/rateLimit/RateLimitQueue.js @@ -0,0 +1,61 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RateLimitQueue = void 0; +/** + * The rate limit queue + * Receives all overlapping API requests and ultimately sends them after the the bucket refills + */ +class RateLimitQueue extends Array { + constructor(bucket) { + super(); + this.bucket = bucket; + } + /** + * Adds a new API request to the queue and waits until it executes + * @param {string} endpoint The endpoint for the added API request + * @param {Params} params The params / body for the added API request + * @param {RequestFile[]} files The files sent in the API request + * @returns {Promise} + */ + add(endpoint, params, files) { + return new Promise(resolve => { + this.push({ + endpoint, + params, + files, + callback: (data) => { + resolve(data); + }, + }); + }); + } + /** + * Frees the queue for the remaining capacity of the bucket + * @returns {Promise} + */ + free() { + return __awaiter(this, void 0, void 0, function* () { + // Runs until either the queue's or the bucket's capacity empties + while (this.length && this.bucket.remaining && this.bucket.remaining > 0) { + const nextRequest = this.shift(); + if (!nextRequest) + break; + const { endpoint, params, files, callback } = nextRequest; + // Sends the request + const data = yield this.bucket.send(endpoint, params, files); + // Executes the callback function + callback(data); + } + }); + } +} +exports.RateLimitQueue = RateLimitQueue; diff --git a/lib/api/rateLimit/Requests.d.ts b/lib/api/rateLimit/Requests.d.ts new file mode 100644 index 000000000..4f9a7c125 --- /dev/null +++ b/lib/api/rateLimit/Requests.d.ts @@ -0,0 +1,72 @@ +/// +import { Serializable } from 'child_process'; +import { Bot } from '../../bot'; +import { HttpMethod } from '../constants'; +import { Endpoints } from '../endpoints'; +/** + * Rate limit buckets are indexed by the Endpoint Route, Http Method, and all Major Parameters + * @example + * [EndpointRoute.Channel, HttpMethod.Get, [channelId]].join(' '); + */ +export declare type BucketIndex = string; +/** + * Dictionary type for request Params and Body + */ +export declare type Data = Record; +/** + * The type of data returned from the API + */ +export declare type ReturnedData = Data | Array; +/** + * The request's Body type + */ +export declare type Body = Data | Data[]; +/** + * The request's Params type + */ +export declare type Params = Data | Data[] | undefined; +/** + * The route's arguments type (as a parameter) + */ +export declare type RouteArgs = Record; +/** + * Represents a file attachment sent to the API + */ +export declare type RequestFile = { + path: string; + name: string; +} | string; +/** + * Manager for sending requests according to the Discord API rate limit + */ +export declare class Requests { + /** + * The bot instance + */ + private readonly bot; + /** + * The Bot's token + */ + private readonly token; + /** + * Rate limit buckets are unique identifiers for every API route + */ + private readonly buckets; + constructor(bot: Bot, token: string); + /** + * Tells the bucket that is responsible for this request to send this request + * @param {EndpointRoute} route The route this request should go to + * @param {RouteArgs} routeArgs The arguments this route requires + * @param {HttpMethod} method The http method for this request + * @param {Params} params The params / body for this request + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(route: T, routeArgs: RouteArgs, method: HttpMethod, params?: Params, files?: RequestFile[]): Promise; + /** + * Retrieves the major params from the route arguments + * @param {RouteArgs} routeArgs The route arguments + * @returns {string[]} + */ + private getMajorParams; +} diff --git a/lib/api/rateLimit/Requests.js b/lib/api/rateLimit/Requests.js new file mode 100644 index 000000000..1388a59fc --- /dev/null +++ b/lib/api/rateLimit/Requests.js @@ -0,0 +1,59 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Requests = void 0; +const RateLimitBucket_1 = require("./RateLimitBucket"); +const Collection_1 = __importDefault(require("../../Collection")); +const endpoints_1 = require("../endpoints"); +/** + * All major param keys + * https://discord.com/developers/docs/topics/rate-limits#rate-limits + */ +const majorKeys = ['channelId', 'guildId', 'webhookId']; +/** + * Manager for sending requests according to the Discord API rate limit + */ +class Requests { + constructor(bot, token) { + this.bot = bot; + this.token = token; + this.buckets = new Collection_1.default(); + } + /** + * Tells the bucket that is responsible for this request to send this request + * @param {EndpointRoute} route The route this request should go to + * @param {RouteArgs} routeArgs The arguments this route requires + * @param {HttpMethod} method The http method for this request + * @param {Params} params The params / body for this request + * @param {RequestFile[]} files The files sent in this request + * @returns {Promise} + */ + send(route, routeArgs, method, params, files) { + // Retrieve the major params of this request + const majorParams = this.getMajorParams(routeArgs); + // Set this bucket's identifier + const identifier = [route, method, majorParams].join(' '); + if (!this.buckets.has(identifier)) { + // Creates a new bucket if one is not cached + this.buckets.set(identifier, new RateLimitBucket_1.RateLimitBucket(this.bot, this.token, route, method)); + } + const bucket = this.buckets.get(identifier); + // Retrieves this request's endpoint + const endpoint = endpoints_1.Endpoints[route](...Object.values(routeArgs)); + // Tells the bucket to send this request + return bucket.send(endpoint, params, files); + } + /** + * Retrieves the major params from the route arguments + * @param {RouteArgs} routeArgs The route arguments + * @returns {string[]} + */ + getMajorParams(routeArgs) { + return Object.keys(routeArgs).reduce((keys, key) => { + return majorKeys.includes(key) ? [...keys, key] : keys; + }, []); + } +} +exports.Requests = Requests; diff --git a/lib/api/rateLimit/index.d.ts b/lib/api/rateLimit/index.d.ts new file mode 100644 index 000000000..86414c3ca --- /dev/null +++ b/lib/api/rateLimit/index.d.ts @@ -0,0 +1,3 @@ +export * from './RateLimitBucket'; +export * from './RateLimitQueue'; +export * from './Requests'; diff --git a/lib/api/rateLimit/index.js b/lib/api/rateLimit/index.js new file mode 100644 index 000000000..7f6c24cf4 --- /dev/null +++ b/lib/api/rateLimit/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./RateLimitBucket"), exports); +__exportStar(require("./RateLimitQueue"), exports); +__exportStar(require("./Requests"), exports); diff --git a/lib/bot/Bot.d.ts b/lib/bot/Bot.d.ts new file mode 100644 index 000000000..987e66306 --- /dev/null +++ b/lib/bot/Bot.d.ts @@ -0,0 +1,137 @@ +/// +import { Serializable } from 'child_process'; +import { BotConnection } from './BotConnection'; +import { CommandsHandler } from './handlers/command'; +import { EventsHandler } from './handlers/events'; +import Collection from '../Collection'; +import { BotAPI } from '../api'; +import { BotChannelsController, BotGuildsController, BotUsersController } from '../controllers/bot'; +import { BotCommunication } from '../sharding'; +import { WebsocketOptions } from '../socket'; +import { BotUser } from '../structures'; +import { GuildEmoji, GuildUnavailable } from '../structures/guild'; +import { ShardId, Snowflake } from '../types'; +/** + * The options given to every Bot shard + */ +export interface ShardOptions { + /** + * Shard ID + */ + id: ShardId; + /** + * Number of shards this instance of the bot uses + */ + amount?: number; +} +/** + * Bot cache options + */ +export interface CacheOptions { + /** + * The limit of messages cached in every channel. Set to `0` for no limit + */ + messagesLimit: number; +} +/** + * The default options used to initialize a Bot instance + * @type {BotOptions} + */ +export declare const botOptions: BotOptions; +/** + * The options used to initialize the Bot + */ +export interface BotOptions { + /** + * Websocket connection options + */ + websocket: Partial; + /** + * Bot cache options + */ + cache: CacheOptions; +} +/** + * The bot is the main operator of the API. + * It handles the events, and properties for all structures. + */ +export declare class Bot { + /** + * Bot token + */ + private readonly token; + /** + * Options used to determine how the Bot operates + */ + readonly options: BotOptions; + /** + * {@link ShardOptions} object containing sharding information + */ + readonly shardOptions: ShardOptions; + /** + * Creates all outgoing API requests + */ + readonly api: BotAPI; + /** + * Responsible for handling all of the Bot's commands + */ + commands: CommandsHandler; + /** + * Responsible for handling all of the Bot's events + */ + events: EventsHandler; + /** + * Responsible for managing the bot connection to the Discord gateway + */ + connection: BotConnection; + /** + * Responsible for the communication between shards created by {@link BotShardManager} + */ + communication: BotCommunication; + /** + * This bot's Discord user + * Initializes right before the Bot READY event + */ + user: BotUser | undefined; + /** + * The bot's guilds controller + */ + guilds: BotGuildsController; + /** + * {@link Collection} of all {@link GuildUnavailable}s associated to the Bot + */ + unavailableGuilds: Collection; + /** + * The bot's channels controller + */ + channels: BotChannelsController; + /** + * The bot's users controller + */ + users: BotUsersController; + /** + * {@link Collection} of all {@link GuildEmoji}s found in all guilds the Bot is part of + */ + emojis: Collection; + constructor(token: string, options?: Partial); + /** + * Connects the bot to the Discord gateway + * @returns {Promise} + */ + connect(): Promise; + /** + * Sends debug messages to the {@link BotEvent.Debug} event + * @example ```typescript + * bot.on(BotEvents.Debug, console.log); + * + * bot.debug('Hello World!'); // 'Hello World' + * ``` + * @param {...unknown[]} messages The debug messages + */ + debug(...messages: unknown[]): void; + /** + * @ignore + * @returns {Serializable} + */ + toJSON(): Serializable; +} diff --git a/lib/bot/Bot.js b/lib/bot/Bot.js new file mode 100644 index 000000000..62df8eebb --- /dev/null +++ b/lib/bot/Bot.js @@ -0,0 +1,85 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Bot = exports.botOptions = void 0; +const BotConnection_1 = require("./BotConnection"); +const command_1 = require("./handlers/command"); +const events_1 = require("./handlers/events"); +const Collection_1 = __importDefault(require("../Collection")); +const api_1 = require("../api"); +const bot_1 = require("../controllers/bot"); +const sharding_1 = require("../sharding"); +const socket_1 = require("../socket"); +/** + * The default options used to initialize a Bot instance + * @type {BotOptions} + */ +exports.botOptions = { + cache: { + messagesLimit: 100, + }, + websocket: { + v: api_1.version, + }, +}; +/** + * The bot is the main operator of the API. + * It handles the events, and properties for all structures. + */ +class Bot { + constructor(token, options) { + this.token = token; + this.options = Object.assign(Object.assign({}, exports.botOptions), options); + this.api = new api_1.BotAPI(this, this.token); + // Sets bot sharding data + const shardId = parseInt(process.env.SHARD_ID); + const shardAmount = parseInt(process.env.SHARDS_AMOUNT); + this.shardOptions = { + id: shardId, + amount: shardAmount, + }; + this.commands = new command_1.CommandsHandler(); + this.events = new events_1.EventsHandler(); + this.connection = new BotConnection_1.BotConnection(this, token); + this.communication = new sharding_1.BotCommunication(this); + this.guilds = new bot_1.BotGuildsController(this); + this.unavailableGuilds = new Collection_1.default(); + this.channels = new bot_1.BotChannelsController(this); + this.users = new bot_1.BotUsersController(this); + this.emojis = new Collection_1.default(); + } + /** + * Connects the bot to the Discord gateway + * @returns {Promise} + */ + connect() { + return this.connection.connect(); + } + /** + * Sends debug messages to the {@link BotEvent.Debug} event + * @example ```typescript + * bot.on(BotEvents.Debug, console.log); + * + * bot.debug('Hello World!'); // 'Hello World' + * ``` + * @param {...unknown[]} messages The debug messages + */ + debug(...messages) { + this.events.emit(socket_1.BotEvent.Debug, ...messages); + } + /** + * @ignore + * @returns {Serializable} + */ + toJSON() { + return { + token: this.token, + shardOptions: this.shardOptions, + commands: this.commands, + events: this.events, + }; + } +} +exports.Bot = Bot; diff --git a/lib/bot/BotConnection.d.ts b/lib/bot/BotConnection.d.ts new file mode 100644 index 000000000..ee5a586d2 --- /dev/null +++ b/lib/bot/BotConnection.d.ts @@ -0,0 +1,37 @@ +import { Bot } from './Bot'; +import Collection from '../Collection'; +import { BotSocketShard, GatewayCloseCode } from '../socket'; +import { GatewayStruct } from '../structures'; +import { ShardId } from '../types'; +/** + * Responsible for the creation and closure of the WebSocket connection to the Discord API gateway + */ +export declare class BotConnection { + /** + * Bot socket connection (may split into shards) + */ + private readonly socket; + constructor(bot: Bot, token: string); + /** + * Creates a new bot connection + * @returns {Promise} + */ + connect(): Promise; + /** + * Closes the currently running connection + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnect(code?: GatewayCloseCode): void; + /** + * Closes all currently running connections for all shards + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnectAll(code?: GatewayCloseCode): void; + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @returns {void} + */ + modifyPresence(presence: GatewayStruct): void; + get shards(): Collection; +} diff --git a/lib/bot/BotConnection.js b/lib/bot/BotConnection.js new file mode 100644 index 000000000..309b60dec --- /dev/null +++ b/lib/bot/BotConnection.js @@ -0,0 +1,63 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotConnection = void 0; +const socket_1 = require("../socket"); +/** + * Responsible for the creation and closure of the WebSocket connection to the Discord API gateway + */ +class BotConnection { + constructor(bot, token) { + this.socket = new socket_1.BotSocket(bot, token); + } + /** + * Creates a new bot connection + * @returns {Promise} + */ + connect() { + return __awaiter(this, void 0, void 0, function* () { + yield this.socket.startShards(); + }); + } + /** + * Closes the currently running connection + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnect(code = 3000 /* ManualClosure */) { + this.socket.stopShards(code); + } + /** + * Closes all currently running connections for all shards + * @param {GatewayCloseCode} code WebSocket close code + */ + disconnectAll(code = 3000 /* ManualClosure */) { + if (process.send) { + const request = { + action: "disconnectAll" /* DisconnectAll */, + payload: code, + identifier: Date.now(), + }; + process.send(request); + } + } + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @returns {void} + */ + modifyPresence(presence) { + this.socket.modifyPresence(presence); + } + get shards() { + return this.socket.shards; + } +} +exports.BotConnection = BotConnection; diff --git a/lib/bot/handlers/Handler.d.ts b/lib/bot/handlers/Handler.d.ts new file mode 100644 index 000000000..4599526ea --- /dev/null +++ b/lib/bot/handlers/Handler.d.ts @@ -0,0 +1,30 @@ +/** + * All possible events for a handler to listen to. + * You can register a listener method for any of these events by using the {@link RegisterCommandHandler} or {@link RegisterEventHandler} decorators or by using the {@link Handler.registerHandler} method + */ +export declare enum HandlerEvent { + /** + * This will fire right before the handler is executed + */ + Before = "before", + /** + * This will fire when the handler is executed + */ + Execute = "execute", + /** + * This will fire right after the handler is executed + */ + After = "after" +} +/** + * @template T + */ +export declare abstract class Handler { + /** + * Registers an item to this handler + * @param {T} item The item to register + */ + abstract register(item: T): void; +} diff --git a/lib/bot/handlers/Handler.js b/lib/bot/handlers/Handler.js new file mode 100644 index 000000000..05d0a75d5 --- /dev/null +++ b/lib/bot/handlers/Handler.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Handler = exports.HandlerEvent = void 0; +/** + * All possible events for a handler to listen to. + * You can register a listener method for any of these events by using the {@link RegisterCommandHandler} or {@link RegisterEventHandler} decorators or by using the {@link Handler.registerHandler} method + */ +var HandlerEvent; +(function (HandlerEvent) { + /** + * This will fire right before the handler is executed + */ + HandlerEvent["Before"] = "before"; + /** + * This will fire when the handler is executed + */ + HandlerEvent["Execute"] = "execute"; + /** + * This will fire right after the handler is executed + */ + HandlerEvent["After"] = "after"; +})(HandlerEvent = exports.HandlerEvent || (exports.HandlerEvent = {})); +/** + * @template T + */ +class Handler { +} +exports.Handler = Handler; diff --git a/lib/bot/handlers/HandlerItem.d.ts b/lib/bot/handlers/HandlerItem.d.ts new file mode 100644 index 000000000..ebfd429ea --- /dev/null +++ b/lib/bot/handlers/HandlerItem.d.ts @@ -0,0 +1,56 @@ +import { HandlerEvent } from './Handler'; +import { Bot } from '../Bot'; +/** + * Default handler function + */ +export declare type HandlerFunction = (...args: any[]) => void; +/** + * Default handler attributes + */ +export declare type HandlerAttributes = { + name: T; +}; +/** + * Represents a handler for commands and events. + * Contains inner handlers for every {@link HandlerEvent} event + */ +export declare class HandlerItem, TEvents extends Record> { + /** + * The bot instance + */ + protected bot: Bot; + /** + * The attributes for the handler item + */ + attributes: TAttrs; + /** + * The event handlers applied by the {@link RegisterCommandHandler} and {@link RegisterEventHandler} decorators. + * The handlers will be asynchronously executed in order when the handler item is called + * @ignore + */ + handlers: Record; + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot: Bot, attributes?: TAttrs); + /** + * Runs all handlers for a specific event + * @param {T} event The event + * @param {any} args The arguments for this event's listener function + * @returns {Promise} + * @ignore + */ + runAll(event: T, ...args: Parameters): Promise; + /** + * Registers an event handler in the {@link EventEmitter} associated to this Command + * @param {T} name The name of the event + * @param {Function} callback The listener for this callback + */ + protected registerHandler(name: T, callback: TEvents[T]): void; + /** + * Returns this command's name + * @type {string} + */ + get name(): TName; +} diff --git a/lib/bot/handlers/HandlerItem.js b/lib/bot/handlers/HandlerItem.js new file mode 100644 index 000000000..8ef116cf7 --- /dev/null +++ b/lib/bot/handlers/HandlerItem.js @@ -0,0 +1,65 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HandlerItem = void 0; +/** + * Represents a handler for commands and events. + * Contains inner handlers for every {@link HandlerEvent} event + */ +class HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot, attributes) { + this.bot = bot; + // Applies the attributes given by the constructor if the class register decorator wasn't used + if (attributes) { + this.attributes = attributes; + } + if (!this.attributes) { + throw new Error('No attributes were provided for this command'); + } + if (!this.handlers) { + this.handlers = { before: [], after: [], execute: [] }; + } + } + /** + * Runs all handlers for a specific event + * @param {T} event The event + * @param {any} args The arguments for this event's listener function + * @returns {Promise} + * @ignore + */ + runAll(event, ...args) { + return __awaiter(this, void 0, void 0, function* () { + for (const handler of this.handlers[event]) { + yield handler.bind(this)(...args); + } + }); + } + /** + * Registers an event handler in the {@link EventEmitter} associated to this Command + * @param {T} name The name of the event + * @param {Function} callback The listener for this callback + */ + registerHandler(name, callback) { + this.handlers[name].push(callback); + } + /** + * Returns this command's name + * @type {string} + */ + get name() { + return this.attributes.name; + } +} +exports.HandlerItem = HandlerItem; diff --git a/lib/bot/handlers/command/Command.d.ts b/lib/bot/handlers/command/Command.d.ts new file mode 100644 index 000000000..ffdc00544 --- /dev/null +++ b/lib/bot/handlers/command/Command.d.ts @@ -0,0 +1,55 @@ +import { Message } from '../../../structures/message'; +import { Bot } from '../../Bot'; +import { HandlerEvent } from '../Handler'; +import { HandlerItem, HandlerFunction } from '../HandlerItem'; +export declare type CommandExecuteFunction = (message: Message, ...args: any[]) => T; +/** + * The {@link HandlerEvent}s listeners signatures for commands + */ +export interface CommandEvents extends Record { + [HandlerEvent.Execute]: CommandExecuteFunction; + [HandlerEvent.Before]: () => T; + [HandlerEvent.After]: () => T; +} +/** + * The attributes for a command + */ +export interface CommandAttributes { + /** + * The name of the command. + * This is used for mapping the commands in the Bot's commands Collection {@link CommandsHandler.commands} + */ + name: string; +} +/** + * Represents an abstract bot command for commands to extend from + */ +export declare abstract class Command extends HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot: Bot, attributes?: CommandAttributes); +} +/** + * Decorator for registering a command + * @example ```typescript + * @RegisterCommand({ name: 'ping' }) + * export class Ping extends Command { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Command): void} + */ +export declare function RegisterCommand(attributes: CommandAttributes): (constructor: typeof Command) => void; +/** + * Decorator for registering an event handler for a command + * @example ```typescript + * @RegisterCommandHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log('Pong!', message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +export declare function RegisterCommandHandler(name: THandlerName): (target: T, propertyKey: keyof T, descriptor: TypedPropertyDescriptor[THandlerName]>) => void; diff --git a/lib/bot/handlers/command/Command.js b/lib/bot/handlers/command/Command.js new file mode 100644 index 000000000..95b3c32ae --- /dev/null +++ b/lib/bot/handlers/command/Command.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RegisterCommandHandler = exports.RegisterCommand = exports.Command = void 0; +const Handler_1 = require("../Handler"); +const HandlerItem_1 = require("../HandlerItem"); +/** + * Represents an abstract bot command for commands to extend from + */ +class Command extends HandlerItem_1.HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {CommandAttributes} attributes The attributes for this command if the RegisterCommand decorator wasn't used + */ + constructor(bot, attributes) { + super(bot, attributes); + // Register this command + bot.commands.register(this); + } +} +exports.Command = Command; +/** + * Decorator for registering a command + * @example ```typescript + * @RegisterCommand({ name: 'ping' }) + * export class Ping extends Command { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Command): void} + */ +function RegisterCommand(attributes) { + return function (constructor) { + constructor.prototype.attributes = attributes; + }; +} +exports.RegisterCommand = RegisterCommand; +/** + * Decorator for registering an event handler for a command + * @example ```typescript + * @RegisterCommandHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log('Pong!', message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +function RegisterCommandHandler(name) { + return function (target, propertyKey, descriptor) { + if (!descriptor.value) + return; + if (!target.handlers) { + target.handlers = { before: [], after: [], execute: [] }; + } + target.handlers[name].push(descriptor.value); + }; +} +exports.RegisterCommandHandler = RegisterCommandHandler; diff --git a/lib/bot/handlers/command/CommandsHandler.d.ts b/lib/bot/handlers/command/CommandsHandler.d.ts new file mode 100644 index 000000000..05f6f6412 --- /dev/null +++ b/lib/bot/handlers/command/CommandsHandler.d.ts @@ -0,0 +1,25 @@ +import { Command, CommandEvents } from './Command'; +import Collection from '../../../Collection'; +import { Handler, HandlerEvent } from '../Handler'; +/** + * Handles the commands associated to a bot + */ +export declare class CommandsHandler implements Handler { + /** + * The commands associated to this handler mapped by their names + */ + readonly commands: Collection; + constructor(); + /** + * Executes a command and runs its event handlers + * @param {string} name The name of the command + * @param {any} args The arguments to give the command's {@link HandlerEvent.Execute} event handler + * @returns {boolean} + */ + execute(name: string, ...args: Parameters): Promise; + /** + * Registers a command to this handler + * @param {Command} command The command to register + */ + register(command: Command): void; +} diff --git a/lib/bot/handlers/command/CommandsHandler.js b/lib/bot/handlers/command/CommandsHandler.js new file mode 100644 index 000000000..c0cec94ea --- /dev/null +++ b/lib/bot/handlers/command/CommandsHandler.js @@ -0,0 +1,50 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CommandsHandler = void 0; +const Collection_1 = __importDefault(require("../../../Collection")); +const Handler_1 = require("../Handler"); +/** + * Handles the commands associated to a bot + */ +class CommandsHandler { + constructor() { + this.commands = new Collection_1.default(); + } + /** + * Executes a command and runs its event handlers + * @param {string} name The name of the command + * @param {any} args The arguments to give the command's {@link HandlerEvent.Execute} event handler + * @returns {boolean} + */ + execute(name, ...args) { + return __awaiter(this, void 0, void 0, function* () { + if (!this.commands.has(name)) + return false; + const command = this.commands.get(name); + yield command.runAll(Handler_1.HandlerEvent.Before); + yield command.runAll(Handler_1.HandlerEvent.Execute, ...args); + yield command.runAll(Handler_1.HandlerEvent.After); + return true; + }); + } + /** + * Registers a command to this handler + * @param {Command} command The command to register + */ + register(command) { + this.commands.set(command.name, command); + } +} +exports.CommandsHandler = CommandsHandler; diff --git a/lib/bot/handlers/command/index.d.ts b/lib/bot/handlers/command/index.d.ts new file mode 100644 index 000000000..f13e079a8 --- /dev/null +++ b/lib/bot/handlers/command/index.d.ts @@ -0,0 +1,2 @@ +export * from './Command'; +export * from './CommandsHandler'; diff --git a/lib/bot/handlers/command/index.js b/lib/bot/handlers/command/index.js new file mode 100644 index 000000000..ee87b6888 --- /dev/null +++ b/lib/bot/handlers/command/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Command"), exports); +__exportStar(require("./CommandsHandler"), exports); diff --git a/lib/bot/handlers/events/Event.d.ts b/lib/bot/handlers/events/Event.d.ts new file mode 100644 index 000000000..6daa7894c --- /dev/null +++ b/lib/bot/handlers/events/Event.d.ts @@ -0,0 +1,54 @@ +import { Events } from './events'; +import { BotEvent } from '../../../socket'; +import { Bot } from '../../Bot'; +import { HandlerEvent } from '../Handler'; +import { HandlerFunction, HandlerItem } from '../HandlerItem'; +/** + * The {@link HandlerEvent}s listeners signatures for events + */ +export interface EventEvents extends Record { + [HandlerEvent.Execute]: Events[T]; + [HandlerEvent.Before]: () => TReturn; + [HandlerEvent.After]: () => TReturn; +} +/** + * The attributes for a event + */ +export interface EventAttributes { + /** + * The name of the event + */ + name: T; +} +/** + * Represents an abstract bot event for events to extend from + */ +export declare class Event extends HandlerItem, EventEvents> { + /** + * @param {Bot} bot The bot instance + * @param {EventAttributes} attributes The attributes for this event if the RegisterEvent decorator wasn't used + */ + constructor(bot: Bot, attributes?: EventAttributes); +} +/** + * Decorator for registering an event + * @example ```typescript + * @RegisterCommand({ name: BotEvent.Ready }) + * export class Ready extends Event { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Event): void} + */ +export declare function RegisterEvent(attributes: EventAttributes): (constructor: Function) => void; +/** + * Decorator for registering an event handler for an event + * @example ```typescript + * @RegisterEventHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log(message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +export declare function RegisterEventHandler(name: THandlerName): , TName extends T extends Event ? N : never, TReturn>(target: T, propertyKey: keyof T, descriptor: TypedPropertyDescriptor[THandlerName]>) => void; diff --git a/lib/bot/handlers/events/Event.js b/lib/bot/handlers/events/Event.js new file mode 100644 index 000000000..ba14358e2 --- /dev/null +++ b/lib/bot/handlers/events/Event.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RegisterEventHandler = exports.RegisterEvent = exports.Event = void 0; +const Handler_1 = require("../Handler"); +const HandlerItem_1 = require("../HandlerItem"); +/** + * Represents an abstract bot event for events to extend from + */ +class Event extends HandlerItem_1.HandlerItem { + /** + * @param {Bot} bot The bot instance + * @param {EventAttributes} attributes The attributes for this event if the RegisterEvent decorator wasn't used + */ + constructor(bot, attributes) { + super(bot, attributes); + // Register this command + bot.events.register(this); + } +} +exports.Event = Event; +/** + * Decorator for registering an event + * @example ```typescript + * @RegisterCommand({ name: BotEvent.Ready }) + * export class Ready extends Event { ... } + * ``` + * @param {CommandAttributes} attributes The attributes for the command + * @returns {function(constructor: Event): void} + */ +function RegisterEvent(attributes) { + return function (constructor) { + constructor.prototype.attributes = attributes; + }; +} +exports.RegisterEvent = RegisterEvent; +/** + * Decorator for registering an event handler for an event + * @example ```typescript + * @RegisterEventHandler(HandlerEvent.Execute) + * public main(message: Message): void { + * console.log(message.content); + * } + * ``` + * @param {THandlerName} name The name of the handler event + * @returns {Function} + */ +function RegisterEventHandler(name) { + return function (target, propertyKey, descriptor) { + if (!descriptor.value) + return; + if (!target.handlers) { + target.handlers = { before: [], after: [], execute: [] }; + } + target.handlers[name].push(descriptor.value); + }; +} +exports.RegisterEventHandler = RegisterEventHandler; diff --git a/lib/bot/handlers/events/EventsHandler.d.ts b/lib/bot/handlers/events/EventsHandler.d.ts new file mode 100644 index 000000000..2afd8c9b1 --- /dev/null +++ b/lib/bot/handlers/events/EventsHandler.d.ts @@ -0,0 +1,29 @@ +import TypedEventEmitter from 'typed-emitter'; +import { Event } from './Event'; +import { Events } from './events'; +import { BotEvent } from '../../../socket'; +import { Handler } from '../Handler'; +declare const EventsHandler_base: new () => TypedEventEmitter; +/** + * Responsible for handling all of the Bot's events + */ +export declare class EventsHandler extends EventsHandler_base implements Handler> { + /** + * Registers an event to be fired when the event's name is dispatched by the API + * @param {Event} event The event to be fired + */ + register(event: Event): void; + /** + * Asynchronously waits until an event is executed, and returns its arguments in an array + * @example ```typescript + * // Waits until the event is called + * const [ message ] = await bot.events.wait(BotEvents.MessageCreate); + * // You can now use 'message'... + * ``` + * @param {T} name The name of the event + * @returns {Promise} The arguments of the event in an array + * @template E - the event name + */ + wait>(name: T): Promise; +} +export {}; diff --git a/lib/bot/handlers/events/EventsHandler.js b/lib/bot/handlers/events/EventsHandler.js new file mode 100644 index 000000000..c032815e6 --- /dev/null +++ b/lib/bot/handlers/events/EventsHandler.js @@ -0,0 +1,51 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventsHandler = void 0; +const events_1 = require("events"); +const Handler_1 = require("../Handler"); +/** + * Responsible for handling all of the Bot's events + */ +class EventsHandler extends events_1.EventEmitter { + /** + * Registers an event to be fired when the event's name is dispatched by the API + * @param {Event} event The event to be fired + */ + register(event) { + this.on(event.name, ((...args) => __awaiter(this, void 0, void 0, function* () { + yield event.runAll(Handler_1.HandlerEvent.Before); + yield event.runAll(Handler_1.HandlerEvent.Execute, ...args); + yield event.runAll(Handler_1.HandlerEvent.After); + }))); + } + /** + * Asynchronously waits until an event is executed, and returns its arguments in an array + * @example ```typescript + * // Waits until the event is called + * const [ message ] = await bot.events.wait(BotEvents.MessageCreate); + * // You can now use 'message'... + * ``` + * @param {T} name The name of the event + * @returns {Promise} The arguments of the event in an array + * @template E - the event name + */ + wait(name) { + return new Promise(resolve => { + const listener = ((...args) => { + resolve(args); + this.removeListener(name, listener); + }); + this.on(name, listener); + }); + } +} +exports.EventsHandler = EventsHandler; diff --git a/lib/bot/handlers/events/events.d.ts b/lib/bot/handlers/events/events.d.ts new file mode 100644 index 000000000..988325da0 --- /dev/null +++ b/lib/bot/handlers/events/events.d.ts @@ -0,0 +1,351 @@ +import Collection from '../../../Collection'; +import { BotSocketShard, BotEvent } from '../../../socket'; +import { GuildMembersChunk } from '../../../socket/handlers/guildMembersChunk'; +import { Emoji, Invite, PartialInvite, Role, Timestamp, User, TextBasedChannel } from '../../../structures'; +import { Channel, GuildChannel } from '../../../structures/channels'; +import { Guild, GuildUnavailable, GuildBan } from '../../../structures/guild'; +import { Member, MemberPresence } from '../../../structures/member'; +import { Message, PartialMessage, MessageReaction } from '../../../structures/message'; +import { VoiceState } from '../../../structures/voice/VoiceState'; +import { Snowflake } from '../../../types'; +/** + * Sent when all shards become ready + * @asMemberOf EventsHandler + * @event BotEventsHandler#READY + */ +declare function READY(): any; +/** + * Sent when a shard becomes ready + * @param {BotSocketShard} shard + * @asMemberOf EventsHandler + * @event BotEventsHandler#SHARD_READY + */ +declare function SHARD_READY(shard: BotSocketShard): any; +/** + * Sent when a shard closes (disconnects) + * @param {BotSocketShard} shard + * @asMemberOf EventsHandler + * @event BotEventsHandler#SHARD_CLOSE + */ +declare function SHARD_CLOSE(shard: BotSocketShard): any; +/** + * Sent when a new channel is created + * @param {Channel} channel The new channel + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_CREATE + */ +declare function CHANNEL_CREATE(channel: Channel): any; +/** + * Sent when a channel is updated. + * This is not sent when the field {@link GuildTextChannel.lastMessageId} is altered. + * To keep track of the lastMessageId changes, you must listen for {@link MESSAGE_CREATE} events + * @param {Channel} before The channel before being updated + * @param {Channel} after The channel after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_UPDATE + */ +declare function CHANNEL_UPDATE(before: Channel, after: Channel): any; +/** + * Sent when a channel is deleted + * @param {Channel} channel The deleted channel + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_DELETE + */ +declare function CHANNEL_DELETE(channel: Channel): any; +/** + * Sent when a message is pinned or unpinned in a text channel. + * This is not sent when a pinned message is deleted + * @param {TextBasedChannel} channel The channel which pins were updated + * @param {number | undefined} oldPinTimestamp The previous last pin timestamp + * @asMemberOf EventsHandler + * @event BotEventsHandler#CHANNEL_PINS_UPDATE + */ +declare function CHANNEL_PINS_UPDATE(channel: TextBasedChannel, oldPinTimestamp: Timestamp | undefined): any; +/** + * Sent when the Bot joins a guild, or a guild becomes available to the Bot + * @param {Guild | GuildUnavailable} guild The guild that was created + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_CREATE + */ +declare function GUILD_CREATE(guild: Guild | GuildUnavailable): any; +/** + * Sent when a guild is updated + * @param {Guild | GuildUnavailable} before The guild before being updated + * @param {Guild | GuildUnavailable} after The guild after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_UPDATE + */ +declare function GUILD_UPDATE(before: Guild | GuildUnavailable, after: Guild | GuildUnavailable): any; +/** + * Sent when a guild becomes unavailable during a guild outage, or when the user leaves or is removed from a guild + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_DELETE + */ +declare function GUILD_DELETE(guild: Guild | GuildUnavailable): any; +/** + * Sent when a user is banned from a guild + * @param {GuildBan} ban The guild ban object + * @asMemberOf EventsHandler + * @event BotEventHandler#GUILD_BAN_ADD + */ +declare function GUILD_BAN_ADD(ban: GuildBan): any; +/** + * Sent when a user is unbanned from a guild. + * @param {GuildBan} ban The guild ban object. Possibly undefined if the ban is yet to be cached + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_BAN_ADD + */ +declare function GUILD_BAN_REMOVE(ban: GuildBan | undefined): any; +/** + * Sent when a guild's emojis have been updated. + * @param {Collection} before {@link Collection} of {@link Emoji}s before the update + * @param {Collection} after {@link Collection} of {@link Emoji}s after the update + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_EMOJIS_UPDATE + */ +declare function GUILD_EMOJIS_UPDATE(before: Collection, after: Collection): any; +/** + * Sent when a guild integration is updated. + * @param {Guild} guild The guild whose integrations were updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_INTEGRATIONS_UPDATE + */ +declare function GUILD_INTEGRATIONS_UPDATE(guild: Guild): any; +/** + * Sent when a new user joins a guild + * @param {Member} member The member that joined the guild + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBER_ADD + */ +declare function GUILD_MEMBER_ADD(member: Member): any; +/** + * Sent when a user is removed from a guild (leave/kick/ban). + * @param {Member | User} member The member that left the guild + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBER_REMOVE + */ +declare function GUILD_MEMBER_REMOVE(member: Member | User): any; +/** + * Sent when a guild member is updated. This will also fire when the user object of a guild member changes. + * @param {Member} before The member before being updated + * @param {Member} after The member after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBER_UPDATE + */ +declare function GUILD_MEMBER_UPDATE(before: Member, after: Member): any; +/** + * Sent in response to a Guild Members request + * @param {Guild} guild The guild whose members were requested + * @param {string} nonce The nonce used in the Guild Members Request + * @param {GuildMembersChunk} chunk The information for the chunk that activated this event + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBERS_CHUNK + */ +declare function GUILD_MEMBERS_CHUNK(guild: Guild, nonce: string | undefined, chunk: GuildMembersChunk): any; +/** + * Sent when all Guild Member Chunks for a request are collected + * @param {Guild} guild The guild whose members were requested + * @param {string | undefined} nonce The nonce used in the Guild Members Request + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_MEMBERS_CHUNK_FINISH + */ +declare function GUILD_MEMBERS_CHUNK_FINISH(guild: Guild, nonce: string | undefined): any; +/** + * Sent when a guild role is created + * @param {Role} role The newly created role + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_ROLE_CREATE + */ +declare function GUILD_ROLE_CREATE(role: Role): any; +/** + * Sent when a guild role is updated + * @param {Role} before The role before being updated + * @param {Role} after The role after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_ROLE_UPDATE + */ +declare function GUILD_ROLE_UPDATE(before: Role, after: Role): any; +/** + * Sent when a guild role is deleted + * @param {Role} role The role that has been deleted + * @asMemberOf EventsHandler + * @event BotEventsHandler#GUILD_ROLE_DELETE + */ +declare function GUILD_ROLE_DELETE(role: Role): any; +/** + * Sent when a new invite to a channel is created. + * @param {Invite} invite The invite that has been created + * @asMemberOf EventsHandler + * @event BotEventsHandler#INVITE_CREATE + */ +declare function INVITE_CREATE(invite: Invite): any; +/** + * Sent when an invite is deleted. + * @param {Invite | PartialInvite} invite The {@link Invite} that has been deleted. Possibly {@link PartialInvite} if the invite has not been cached. + * @asMemberOf EventsHandler + * @event BotEventsHandler#INVITE_DELETE + */ +declare function INVITE_DELETE(invite: Invite | PartialInvite): any; +/** + * Sent when a message is created + * @param {Message} message The message that has been created + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_CREATE + */ +declare function MESSAGE_CREATE(message: Message): any; +/** + * Sent when a message is updated + * @param {Message | undefined} before The message before being updated + * @param {Message} after The message after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_UPDATE + */ +declare function MESSAGE_UPDATE(before: Message | undefined, after: Message): any; +/** + * Sent when a message is deleted + * @param {Message | PartialMessage} message The deleted message + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_DELETE + */ +declare function MESSAGE_DELETE(message: Message | PartialMessage): any; +/** + * Sent when multiple messages are deleted at once. + * @param {TextBasedChannel} channel The channel the messages were deleted in + * @param {(Message | Snowflake)[]} messages Array of the deleted messages. + * Cached messages will show as {@link Message}s while the rest will show as {@link Snowflake}s + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_DELETE_BULK + */ +declare function MESSAGE_DELETE_BULK(channel: TextBasedChannel, messages: (Message | Snowflake)[]): any; +/** + * Sent when a user adds a reaction to a message. + * @param {MessageReaction} reaction The reaction the user has added to the message + * @param {Member | User} user The user that added the reaction + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_ADD + */ +declare function MESSAGE_REACTION_ADD(reaction: MessageReaction, user: Member | User | undefined): any; +/** + * Sent when a user removes a reaction from a message. + * @param {MessageReaction} reaction The reaction the user has removed from the message + * @param {Member | User | undefined} user The user that removed the reaction + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_REMOVE + */ +declare function MESSAGE_REACTION_REMOVE(reaction: MessageReaction, user: Member | User | undefined): any; +/** + * Sent when a user explicitly removes all reactions from a message. + * @param {Message} message The message of which reactions were removed + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_REMOVE_ALL + */ +declare function MESSAGE_REACTION_REMOVE_ALL(message: Message): any; +/** + * Sent when a bot removes all instances of a given emoji from the reactions of a message. + * @param {MessageReaction} reaction The reaction associated to the emoji which was removed in its original state. + * @asMemberOf EventsHandler + * @event BotEventsHandler#MESSAGE_REACTION_REMOVE_EMOJI + */ +declare function MESSAGE_REACTION_REMOVE_EMOJI(reaction: MessageReaction | undefined): any; +/** + * Sent when a member's presence or info, such as name or avatar, is updated. + * @param {MemberPresence | undefined} before The member's presence before being updated + * @param {MemberPresence} after The member's presence after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#PRESENCE_UPDATE + */ +declare function PRESENCE_UPDATE(before: MemberPresence | undefined, after: MemberPresence): any; +/** + * Sent when a user starts typing in a channel. + * @param {TextBasedChannel} channel The channel the user started typing in + * @param {Member | User} user The user that started typing + * @param {number} startedAt The unix time (in seconds) of when the user started typing + * @asMemberOf EventsHandler + * @event BotEventsHandler#TYPING_START + */ +declare function TYPING_START(channel: TextBasedChannel | undefined, user: Member | User | undefined, startedAt: number): any; +/** + * Sent when properties about the Bot's user change + * @param {User} before The user before being updated + * @param {User} after The user after being updated + * @asMemberOf EventsHandler + * @event BotEventsHandler#USER_UPDATE + */ +declare function USER_UPDATE(before: User, after: User): any; +/** + * Sent when a guild channel's webhook is created, updated, or deleted. + * @param {GuildChannel} channel The guild channel the updated webhook is associated to + * @asMemberOf EventsHandler + * @event BotEventsHandler#WEBHOOKS_UPDATE + */ +declare function WEBHOOKS_UPDATE(channel: GuildChannel): any; +/** + * Sent whenever a new voice server update comes + * @param {Guild} guild The guild that happens in it + * @param {{ token: string; endpoint: string }} voiceServerStats The new voice server information + * @asMemberOf EventsHandler + * @event BotEventsHandler#VOICE_SERVER_UPDATE + */ +declare function VOICE_SERVER_UPDATE(guild: Guild, voiceServer: { + token: string; + endpoint: string; +}): any; +/** + * Sent whenever a voice state changes + * @param {VoiceState} old The old one + * @param {VoiceState} new The new one + * @asMemberOf EventHandler + * @event BotEventsHandler#VOICE_STATE_UPDATE + */ +declare function VOICE_STATE_UPDATE(oldState: VoiceState, newState: VoiceState): any; +/** + * Events that are called when all Bot shards change their state. + * These events take no arguments + */ +export declare type BotStateEvents = BotEvent.Ready | BotEvent.Close; +/** + * All possible events for the bot and their callback function + */ +export interface Events { + [BotEvent.Debug]: typeof console.log; + [BotEvent.Ready]: typeof READY; + [BotEvent.Close]: () => any; + [BotEvent.ChannelCreate]: typeof CHANNEL_CREATE; + [BotEvent.ChannelUpdate]: typeof CHANNEL_UPDATE; + [BotEvent.ChannelDelete]: typeof CHANNEL_DELETE; + [BotEvent.ChannelPinsUpdate]: typeof CHANNEL_PINS_UPDATE; + [BotEvent.GuildCreate]: typeof GUILD_CREATE; + [BotEvent.GuildUpdate]: typeof GUILD_UPDATE; + [BotEvent.GuildDelete]: typeof GUILD_DELETE; + [BotEvent.GuildBanAdd]: typeof GUILD_BAN_ADD; + [BotEvent.GuildBanRemove]: typeof GUILD_BAN_REMOVE; + [BotEvent.GuildEmojisUpdate]: typeof GUILD_EMOJIS_UPDATE; + [BotEvent.GuildIntegrationsUpdate]: typeof GUILD_INTEGRATIONS_UPDATE; + [BotEvent.GuildMemberAdd]: typeof GUILD_MEMBER_ADD; + [BotEvent.GuildMemberRemove]: typeof GUILD_MEMBER_REMOVE; + [BotEvent.GuildMemberUpdate]: typeof GUILD_MEMBER_UPDATE; + [BotEvent.GuildMembersChunk]: typeof GUILD_MEMBERS_CHUNK; + [BotEvent.GuildMembersChunkFinish]: typeof GUILD_MEMBERS_CHUNK_FINISH; + [BotEvent.GuildRoleCreate]: typeof GUILD_ROLE_CREATE; + [BotEvent.GuildRoleUpdate]: typeof GUILD_ROLE_UPDATE; + [BotEvent.GuildRoleDelete]: typeof GUILD_ROLE_DELETE; + [BotEvent.InviteCreate]: typeof INVITE_CREATE; + [BotEvent.InviteDelete]: typeof INVITE_DELETE; + [BotEvent.MessageCreate]: typeof MESSAGE_CREATE; + [BotEvent.MessageUpdate]: typeof MESSAGE_UPDATE; + [BotEvent.MessageDelete]: typeof MESSAGE_DELETE; + [BotEvent.MessageDeleteBulk]: typeof MESSAGE_DELETE_BULK; + [BotEvent.MessageReactionAdd]: typeof MESSAGE_REACTION_ADD; + [BotEvent.MessageReactionRemove]: typeof MESSAGE_REACTION_REMOVE; + [BotEvent.MessageReactionRemoveAll]: typeof MESSAGE_REACTION_REMOVE_ALL; + [BotEvent.MessageReactionRemoveEmoji]: typeof MESSAGE_REACTION_REMOVE_EMOJI; + [BotEvent.PresenceUpdate]: typeof PRESENCE_UPDATE; + [BotEvent.ShardReady]: typeof SHARD_READY; + [BotEvent.ShardClose]: typeof SHARD_CLOSE; + [BotEvent.TypingStart]: typeof TYPING_START; + [BotEvent.UserUpdate]: typeof USER_UPDATE; + [BotEvent.WebhooksUpdate]: typeof WEBHOOKS_UPDATE; + [BotEvent.VoiceServerUpdate]: typeof VOICE_SERVER_UPDATE; + [BotEvent.VoiceStateUpdate]: typeof VOICE_STATE_UPDATE; +} +export {}; diff --git a/lib/bot/handlers/events/events.js b/lib/bot/handlers/events/events.js new file mode 100644 index 000000000..a93d2f4ee --- /dev/null +++ b/lib/bot/handlers/events/events.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const socket_1 = require("../../../socket"); diff --git a/lib/bot/handlers/events/index.d.ts b/lib/bot/handlers/events/index.d.ts new file mode 100644 index 000000000..b8cfc73fc --- /dev/null +++ b/lib/bot/handlers/events/index.d.ts @@ -0,0 +1,2 @@ +export * from './Event'; +export * from './EventsHandler'; diff --git a/lib/bot/handlers/events/index.js b/lib/bot/handlers/events/index.js new file mode 100644 index 000000000..f19d9d0b4 --- /dev/null +++ b/lib/bot/handlers/events/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Event"), exports); +__exportStar(require("./EventsHandler"), exports); diff --git a/lib/bot/handlers/index.d.ts b/lib/bot/handlers/index.d.ts new file mode 100644 index 000000000..d3808239f --- /dev/null +++ b/lib/bot/handlers/index.d.ts @@ -0,0 +1,4 @@ +export * from './command'; +export * from './events'; +export * from './Handler'; +export * from './HandlerItem'; diff --git a/lib/bot/handlers/index.js b/lib/bot/handlers/index.js new file mode 100644 index 000000000..6136a0649 --- /dev/null +++ b/lib/bot/handlers/index.js @@ -0,0 +1,16 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./command"), exports); +__exportStar(require("./events"), exports); +__exportStar(require("./Handler"), exports); +__exportStar(require("./HandlerItem"), exports); diff --git a/lib/bot/index.d.ts b/lib/bot/index.d.ts new file mode 100644 index 000000000..94836eec7 --- /dev/null +++ b/lib/bot/index.d.ts @@ -0,0 +1,3 @@ +export * from './handlers'; +export * from './Bot'; +export * from './BotConnection'; diff --git a/lib/bot/index.js b/lib/bot/index.js new file mode 100644 index 000000000..700537422 --- /dev/null +++ b/lib/bot/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./handlers"), exports); +__exportStar(require("./Bot"), exports); +__exportStar(require("./BotConnection"), exports); diff --git a/lib/controllers/ControllerCache.d.ts b/lib/controllers/ControllerCache.d.ts new file mode 100644 index 000000000..95666b6ad --- /dev/null +++ b/lib/controllers/ControllerCache.d.ts @@ -0,0 +1,19 @@ +import Collection from '../Collection'; +import { BaseStructWithId } from '../structures/base'; +/** + * Cache holder for controllers. + * @template T + */ +export declare class ControllerCache extends Collection { + /** + * Adds an item to the cache mapped by its ID + * @param {T} item The item you wish to add + * @returns {T} + */ + add(item: T): T; + /** + * Adds multiple items to the cache + * @param {T[]} items The items you wish to add + */ + addMany(items: T[]): void; +} diff --git a/lib/controllers/ControllerCache.js b/lib/controllers/ControllerCache.js new file mode 100644 index 000000000..94a5cb55d --- /dev/null +++ b/lib/controllers/ControllerCache.js @@ -0,0 +1,30 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ControllerCache = void 0; +const Collection_1 = __importDefault(require("../Collection")); +/** + * Cache holder for controllers. + * @template T + */ +class ControllerCache extends Collection_1.default { + /** + * Adds an item to the cache mapped by its ID + * @param {T} item The item you wish to add + * @returns {T} + */ + add(item) { + this.set(item.id, item); + return item; + } + /** + * Adds multiple items to the cache + * @param {T[]} items The items you wish to add + */ + addMany(items) { + this.merge(items.map(i => [i.id, i])); + } +} +exports.ControllerCache = ControllerCache; diff --git a/lib/controllers/base/BaseController.d.ts b/lib/controllers/base/BaseController.d.ts new file mode 100644 index 000000000..559c87172 --- /dev/null +++ b/lib/controllers/base/BaseController.d.ts @@ -0,0 +1,18 @@ +import { Bot } from '../../bot'; +import { BaseStruct, BaseStructWithId } from '../../structures/base'; +import { ControllerCache } from '../ControllerCache'; +/** + * Provides a base interface for the bot's cached data + * @template T + */ +export declare abstract class BaseController { + /** + * The bot instance + */ + protected readonly bot: Bot; + /** + * The cached data this controller contains + */ + cache: ControllerCache; + constructor(struct: BaseStruct | Bot, limit?: number); +} diff --git a/lib/controllers/base/BaseController.js b/lib/controllers/base/BaseController.js new file mode 100644 index 000000000..9d24a89d2 --- /dev/null +++ b/lib/controllers/base/BaseController.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseController = void 0; +const base_1 = require("../../structures/base"); +const ControllerCache_1 = require("../ControllerCache"); +/** + * Provides a base interface for the bot's cached data + * @template T + */ +class BaseController { + constructor(struct, limit) { + if (struct instanceof base_1.BaseStruct) { + this.bot = struct.bot; + } + else { + this.bot = struct; + } + this.cache = new ControllerCache_1.ControllerCache(null, limit); + } +} +exports.BaseController = BaseController; diff --git a/lib/controllers/base/BaseCreateController.d.ts b/lib/controllers/base/BaseCreateController.d.ts new file mode 100644 index 000000000..44710efcd --- /dev/null +++ b/lib/controllers/base/BaseCreateController.d.ts @@ -0,0 +1,14 @@ +import { BaseController } from './BaseController'; +import { BaseStructWithId } from '../../structures/base'; +/** + * Base controller with create capabilities + * @template TStruct + * @template TOptions + */ +export declare abstract class BaseCreateController extends BaseController { + /** + * Creates a new item and caches it + * @returns {Promise} + */ + abstract create(options?: TOptions): Promise; +} diff --git a/lib/controllers/base/BaseCreateController.js b/lib/controllers/base/BaseCreateController.js new file mode 100644 index 000000000..b5e6ef30c --- /dev/null +++ b/lib/controllers/base/BaseCreateController.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseCreateController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with create capabilities + * @template TStruct + * @template TOptions + */ +class BaseCreateController extends BaseController_1.BaseController { +} +exports.BaseCreateController = BaseCreateController; diff --git a/lib/controllers/base/BaseDeleteController.d.ts b/lib/controllers/base/BaseDeleteController.d.ts new file mode 100644 index 000000000..f4ca20a69 --- /dev/null +++ b/lib/controllers/base/BaseDeleteController.d.ts @@ -0,0 +1,15 @@ +import { BaseController } from './BaseController'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with delete capabilities + * @template T + */ +export declare abstract class BaseDeleteController extends BaseController { + /** + * Deletes a cached item + * @param {Snowflake} id The ID of the item you wish to delete + * @returns {Promise} + */ + abstract delete(id: Snowflake | string): Promise; +} diff --git a/lib/controllers/base/BaseDeleteController.js b/lib/controllers/base/BaseDeleteController.js new file mode 100644 index 000000000..277fec95c --- /dev/null +++ b/lib/controllers/base/BaseDeleteController.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseDeleteController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with delete capabilities + * @template T + */ +class BaseDeleteController extends BaseController_1.BaseController { +} +exports.BaseDeleteController = BaseDeleteController; diff --git a/lib/controllers/base/BaseFetchAllController.d.ts b/lib/controllers/base/BaseFetchAllController.d.ts new file mode 100644 index 000000000..a43107fde --- /dev/null +++ b/lib/controllers/base/BaseFetchAllController.d.ts @@ -0,0 +1,15 @@ +import { BaseController } from './BaseController'; +import Collection from '../../Collection'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with fetch all capabilities + * @template T + */ +export declare abstract class BaseFetchAllController extends BaseController { + /** + * Fetches all items associated to the controller and caches them + * @returns {Promise} + */ + abstract fetchAll(): Promise>; +} diff --git a/lib/controllers/base/BaseFetchAllController.js b/lib/controllers/base/BaseFetchAllController.js new file mode 100644 index 000000000..0eda90751 --- /dev/null +++ b/lib/controllers/base/BaseFetchAllController.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseFetchAllController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with fetch all capabilities + * @template T + */ +class BaseFetchAllController extends BaseController_1.BaseController { +} +exports.BaseFetchAllController = BaseFetchAllController; diff --git a/lib/controllers/base/BaseFetchController.d.ts b/lib/controllers/base/BaseFetchController.d.ts new file mode 100644 index 000000000..2abe03765 --- /dev/null +++ b/lib/controllers/base/BaseFetchController.d.ts @@ -0,0 +1,21 @@ +import { BaseController } from './BaseController'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with fetch capabilities + * @template T + */ +export declare abstract class BaseFetchController extends BaseController { + /** + * Fetches a new item and caches it + * @param {Snowflake | string} id The ID of the item you wish to fetch + * @returns {Promise} + */ + abstract fetch(id: Snowflake | string): Promise; + /** + * Returns an already cached item or fetches it + * @param {Snowflake | string} id The ID of the item you wish to get or fetch + * @returns {Promise} + */ + get(id: Snowflake | string): Promise; +} diff --git a/lib/controllers/base/BaseFetchController.js b/lib/controllers/base/BaseFetchController.js new file mode 100644 index 000000000..be9f73dfd --- /dev/null +++ b/lib/controllers/base/BaseFetchController.js @@ -0,0 +1,30 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseFetchController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with fetch capabilities + * @template T + */ +class BaseFetchController extends BaseController_1.BaseController { + /** + * Returns an already cached item or fetches it + * @param {Snowflake | string} id The ID of the item you wish to get or fetch + * @returns {Promise} + */ + get(id) { + return __awaiter(this, void 0, void 0, function* () { + return this.cache.get(id) || this.fetch(id); + }); + } +} +exports.BaseFetchController = BaseFetchController; diff --git a/lib/controllers/base/BaseFetchSomeController.d.ts b/lib/controllers/base/BaseFetchSomeController.d.ts new file mode 100644 index 000000000..340f53ee0 --- /dev/null +++ b/lib/controllers/base/BaseFetchSomeController.d.ts @@ -0,0 +1,15 @@ +import { BaseController } from './BaseController'; +import Collection from '../../Collection'; +import { BaseStructWithId } from '../../structures/base'; +import { Snowflake } from '../../types'; +/** + * Base controller with fetch some capabilities + * @template T + */ +export declare abstract class BaseFetchSomeController extends BaseController { + /** + * Fetches some items associated to the controller and caches them + * @returns {Promise} + */ + abstract fetchSome(): Promise>; +} diff --git a/lib/controllers/base/BaseFetchSomeController.js b/lib/controllers/base/BaseFetchSomeController.js new file mode 100644 index 000000000..384ab5c9b --- /dev/null +++ b/lib/controllers/base/BaseFetchSomeController.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseFetchSomeController = void 0; +const BaseController_1 = require("./BaseController"); +/** + * Base controller with fetch some capabilities + * @template T + */ +class BaseFetchSomeController extends BaseController_1.BaseController { +} +exports.BaseFetchSomeController = BaseFetchSomeController; diff --git a/lib/controllers/base/index.d.ts b/lib/controllers/base/index.d.ts new file mode 100644 index 000000000..548726bab --- /dev/null +++ b/lib/controllers/base/index.d.ts @@ -0,0 +1,6 @@ +export * from './BaseController'; +export * from './BaseCreateController'; +export * from './BaseDeleteController'; +export * from './BaseFetchAllController'; +export * from './BaseFetchSomeController'; +export * from './BaseFetchController'; diff --git a/lib/controllers/base/index.js b/lib/controllers/base/index.js new file mode 100644 index 000000000..2c306d371 --- /dev/null +++ b/lib/controllers/base/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BaseController"), exports); +__exportStar(require("./BaseCreateController"), exports); +__exportStar(require("./BaseDeleteController"), exports); +__exportStar(require("./BaseFetchAllController"), exports); +__exportStar(require("./BaseFetchSomeController"), exports); +__exportStar(require("./BaseFetchController"), exports); diff --git a/lib/controllers/bot/BotChannelsController.d.ts b/lib/controllers/bot/BotChannelsController.d.ts new file mode 100644 index 000000000..387d23edc --- /dev/null +++ b/lib/controllers/bot/BotChannelsController.d.ts @@ -0,0 +1,35 @@ +import { Channel, GuildChannel, TextBasedChannel } from '../../structures/channels'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchController } from '../base'; +/** + * Provides an interface for the bot's channels cache. + * The channels are mapped by their IDs + */ +export declare class BotChannelsController extends BaseFetchController implements BaseDeleteController { + /** + * Deletes a channel + * @param {Snowflake} id The ID of the channel you wish to delete + * @returns {Promise} + */ + delete(id: Snowflake): Promise; + /** + * Fetches a channel + * @param {Snowflake} id The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** @inheritDoc */ + get(id: Snowflake): Promise; + /** + * Gets or fetches a text channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getText(id: Snowflake): Promise; + /** + * Gets or fetches a guild channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getGuildChannel(id: Snowflake): Promise; +} diff --git a/lib/controllers/bot/BotChannelsController.js b/lib/controllers/bot/BotChannelsController.js new file mode 100644 index 000000000..dc7514d86 --- /dev/null +++ b/lib/controllers/bot/BotChannelsController.js @@ -0,0 +1,81 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotChannelsController = void 0; +const channels_1 = require("../../structures/channels"); +const base_1 = require("../base"); +/** + * Provides an interface for the bot's channels cache. + * The channels are mapped by their IDs + */ +class BotChannelsController extends base_1.BaseFetchController { + /** + * Deletes a channel + * @param {Snowflake} id The ID of the channel you wish to delete + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteChannel(id); + } + /** + * Fetches a channel + * @param {Snowflake} id The ID of the channel you wish to fetch + * @returns {Promise} + */ + fetch(id) { + return this.bot.api.fetchChannel(id); + } + /** @inheritDoc */ + get(id) { + const _super = Object.create(null, { + get: { get: () => super.get } + }); + return __awaiter(this, void 0, void 0, function* () { + const channel = yield _super.get.call(this, id); + if (channel instanceof channels_1.GuildChannel) { + channel.guild.channels.cache.add(channel); + } + else if (channel instanceof channels_1.DMChannel) { + channel.recipient.dm = channel; + } + return channel; + }); + } + /** + * Gets or fetches a text channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getText(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildTextChannel || channel instanceof channels_1.DMChannel)) { + throw new TypeError('The channel is not a valid text channel'); + } + return channel; + }); + } + /** + * Gets or fetches a guild channel by its ID + * @param {Snowflake} id The ID of the text channel + * @returns {Promise} + */ + getGuildChannel(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildChannel)) { + throw new TypeError('The channel is not a valid guild channel'); + } + return channel; + }); + } +} +exports.BotChannelsController = BotChannelsController; diff --git a/lib/controllers/bot/BotGuildsController.d.ts b/lib/controllers/bot/BotGuildsController.d.ts new file mode 100644 index 000000000..d1510f081 --- /dev/null +++ b/lib/controllers/bot/BotGuildsController.d.ts @@ -0,0 +1,31 @@ +import { Guild, GuildPreview } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseFetchController } from '../base'; +/** + * Options for when fetching guilds + */ +export interface FetchGuildOptions { + /** + * When `true`, the fetched guild will contain a full {@link GuildApproximates} object + */ + withCounts?: boolean; +} +/** + * Interface for the bot's guilds cache. + * The guilds are mapped by their IDs + */ +export declare class BotGuildsController extends BaseFetchController { + /** + * Fetches a guild by its ID and caches it + * @param {Snowflake} id The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetch(id: Snowflake, options?: FetchGuildOptions): Promise; + /** + * Fetches a guild preview by its guild ID + * @param {Snowflake} id The ID of the guild + * @returns {Promise} + */ + fetchPreview(id: Snowflake): Promise; +} diff --git a/lib/controllers/bot/BotGuildsController.js b/lib/controllers/bot/BotGuildsController.js new file mode 100644 index 000000000..4daac7b1a --- /dev/null +++ b/lib/controllers/bot/BotGuildsController.js @@ -0,0 +1,41 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotGuildsController = void 0; +const base_1 = require("../base"); +/** + * Interface for the bot's guilds cache. + * The guilds are mapped by their IDs + */ +class BotGuildsController extends base_1.BaseFetchController { + /** + * Fetches a guild by its ID and caches it + * @param {Snowflake} id The ID of the guild + * @param {FetchGuildOptions} options The additional options for the fetch operation + * @returns {Promise} + */ + fetch(id, options) { + return __awaiter(this, void 0, void 0, function* () { + const guild = yield this.bot.api.fetchGuild(id, options); + this.cache.add(guild); + return guild; + }); + } + /** + * Fetches a guild preview by its guild ID + * @param {Snowflake} id The ID of the guild + * @returns {Promise} + */ + fetchPreview(id) { + return this.bot.api.fetchGuildPreview(id); + } +} +exports.BotGuildsController = BotGuildsController; diff --git a/lib/controllers/bot/BotUsersController.d.ts b/lib/controllers/bot/BotUsersController.d.ts new file mode 100644 index 000000000..cc18ed5fd --- /dev/null +++ b/lib/controllers/bot/BotUsersController.d.ts @@ -0,0 +1,22 @@ +import { Bot } from '../../bot'; +import { BotUser, User } from '../../structures'; +import { Snowflake } from '../../types'; +import { BaseFetchController } from '../base'; +/** + * Provides an interface for the bot's users cache. + * The users are mapped by their IDs + */ +export declare class BotUsersController extends BaseFetchController { + constructor(bot: Bot); + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBot(): Promise; + /** + * Fetches a user by its ID + * @param {Snowflake} id The user ID + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; +} diff --git a/lib/controllers/bot/BotUsersController.js b/lib/controllers/bot/BotUsersController.js new file mode 100644 index 000000000..e11665e51 --- /dev/null +++ b/lib/controllers/bot/BotUsersController.js @@ -0,0 +1,52 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotUsersController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for the bot's users cache. + * The users are mapped by their IDs + */ +class BotUsersController extends base_1.BaseFetchController { + constructor(bot) { + super(bot); + } + /** + * Fetches the bot user + * @returns {Promise} + */ + fetchBot() { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.bot.api.fetchBotUser(); + this.cache.add(user); + if (this.bot.user) { + this.bot.user.update(user.structure); + } + else { + this.bot.user = user; + } + return user; + }); + } + /** + * Fetches a user by its ID + * @param {Snowflake} id The user ID + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield this.bot.api.fetchUser(id); + this.cache.add(user); + return user; + }); + } +} +exports.BotUsersController = BotUsersController; diff --git a/lib/controllers/bot/index.d.ts b/lib/controllers/bot/index.d.ts new file mode 100644 index 000000000..f8f21ddee --- /dev/null +++ b/lib/controllers/bot/index.d.ts @@ -0,0 +1,3 @@ +export * from './BotChannelsController'; +export * from './BotGuildsController'; +export * from './BotUsersController'; diff --git a/lib/controllers/bot/index.js b/lib/controllers/bot/index.js new file mode 100644 index 000000000..9f2364ef0 --- /dev/null +++ b/lib/controllers/bot/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BotChannelsController"), exports); +__exportStar(require("./BotGuildsController"), exports); +__exportStar(require("./BotUsersController"), exports); diff --git a/lib/controllers/channel/ChannelMessagesController.d.ts b/lib/controllers/channel/ChannelMessagesController.d.ts new file mode 100644 index 000000000..c0b864495 --- /dev/null +++ b/lib/controllers/channel/ChannelMessagesController.d.ts @@ -0,0 +1,55 @@ +import Collection from '../../Collection'; +import { TextBasedChannel } from '../../structures/channels'; +import { Message } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchController, BaseFetchSomeController } from '../base'; +/** + * Options for when fetching some messages in a text channel + */ +export interface FetchSomeMessagesOptions { + /** + * Get messages around this message ID + */ + around?: Snowflake; + /** + * Get messages before this message ID + */ + before?: Snowflake; + /** + * Get messages after this message ID + */ + after?: Snowflake; + /** + * The max number of messages to return (1-100) + */ + limit?: number; +} +/** + * Provides an interface for a text channel's messages cache. + * The messages are mapped by their IDs + */ +export declare class ChannelMessagesController extends BaseFetchController implements BaseDeleteController, BaseFetchSomeController { + /** + * The guild this controller is associated to + */ + readonly channel: TextBasedChannel; + constructor(channel: TextBasedChannel); + /** + * Deletes a message in the channel + * @param {Snowflake} id The ID of the message you wish to delete + * @returns {Promise} + */ + delete(id: Snowflake): Promise; + /** + * Fetches and caches a message in the channel + * @param {Snowflake} id The ID of the message you wish to fetch + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches and caches some messages in the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options?: FetchSomeMessagesOptions): Promise>; +} diff --git a/lib/controllers/channel/ChannelMessagesController.js b/lib/controllers/channel/ChannelMessagesController.js new file mode 100644 index 000000000..ee6175334 --- /dev/null +++ b/lib/controllers/channel/ChannelMessagesController.js @@ -0,0 +1,56 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelMessagesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a text channel's messages cache. + * The messages are mapped by their IDs + */ +class ChannelMessagesController extends base_1.BaseFetchController { + constructor(channel) { + super(channel, channel.bot.options.cache.messagesLimit); + this.channel = channel; + } + /** + * Deletes a message in the channel + * @param {Snowflake} id The ID of the message you wish to delete + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteMessage(this.channel.id, id); + } + /** + * Fetches and caches a message in the channel + * @param {Snowflake} id The ID of the message you wish to fetch + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const message = yield this.bot.api.fetchMessage(this.channel.id, id); + this.cache.add(message); + return message; + }); + } + /** + * Fetches and caches some messages in the channel + * @param {FetchSomeMessagesOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options) { + return __awaiter(this, void 0, void 0, function* () { + const messages = yield this.bot.api.fetchSomeMessages(this.channel.id, options); + this.cache.merge(messages); + return messages; + }); + } +} +exports.ChannelMessagesController = ChannelMessagesController; diff --git a/lib/controllers/channel/ChannelPermissionsController.d.ts b/lib/controllers/channel/ChannelPermissionsController.d.ts new file mode 100644 index 000000000..aadc90589 --- /dev/null +++ b/lib/controllers/channel/ChannelPermissionsController.d.ts @@ -0,0 +1,31 @@ +import { PermissionOverwrite } from '../../structures'; +import { GuildChannel } from '../../structures/channels'; +import { Permissible, PermissionOverwriteFlags } from '../../structures/flags'; +import { Snowflake } from '../../types'; +import { BaseDeleteController } from '../base'; +/** + * Interface for a guild channel's permission overwrites cache. + * The permission overwrites are mapped by their Permissible's ID + */ +export declare class ChannelPermissionsController extends BaseDeleteController { + /** + * The guild channel this controller is associated to + */ + channel: GuildChannel; + constructor(channel: GuildChannel); + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions flags you wish to modify + * @returns {Promise} + */ + modify(permissible: Permissible, flags: PermissionOverwriteFlags): Promise; + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/channel/ChannelPermissionsController.js b/lib/controllers/channel/ChannelPermissionsController.js new file mode 100644 index 000000000..609016e8e --- /dev/null +++ b/lib/controllers/channel/ChannelPermissionsController.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelPermissionsController = void 0; +const base_1 = require("../base"); +/** + * Interface for a guild channel's permission overwrites cache. + * The permission overwrites are mapped by their Permissible's ID + */ +class ChannelPermissionsController extends base_1.BaseDeleteController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Modifies the channel permission overwrites for a member or a role. + * Requires the {@link Permission.ManageRoles} permission + * @param {Permissible} permissible Data for the member or role + * @param {PermissionOverwriteFlags} flags The permissions flags you wish to modify + * @returns {Promise} + */ + modify(permissible, flags) { + return this.bot.api.modifyGuildChannelPermissions(this.channel.id, permissible, flags); + } + /** + * Deletes a channel permission overwrite for a user or role in a guild channel. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the user or role you wish to delete from the channel's permission overwrites + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteGuildChannelPermission(this.channel.id, id); + } +} +exports.ChannelPermissionsController = ChannelPermissionsController; diff --git a/lib/controllers/channel/ChannelPinsController.d.ts b/lib/controllers/channel/ChannelPinsController.d.ts new file mode 100644 index 000000000..43530fa29 --- /dev/null +++ b/lib/controllers/channel/ChannelPinsController.d.ts @@ -0,0 +1,39 @@ +import Collection from '../../Collection'; +import { Timestamp, TextBasedChannel } from '../../structures'; +import { Message } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseFetchAllController } from '../base'; +/** + * Interface for a text channel's pinned messages cache. + * The pinned messages are mapped by their IDs + */ +export declare class ChannelPinsController extends BaseFetchAllController { + /** + * The channel associated to this controller + */ + channel: TextBasedChannel; + /** + * Timestamp of when the last pinned message was pinned + */ + lastPinTimestamp: Timestamp | undefined; + constructor(channel: TextBasedChannel); + /** + * Fetches all messages in the text channel and caches them + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + pin(id: Snowflake): Promise; + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + unpin(id: Snowflake): Promise; +} diff --git a/lib/controllers/channel/ChannelPinsController.js b/lib/controllers/channel/ChannelPinsController.js new file mode 100644 index 000000000..aece5d1d2 --- /dev/null +++ b/lib/controllers/channel/ChannelPinsController.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelPinsController = void 0; +const base_1 = require("../base"); +/** + * Interface for a text channel's pinned messages cache. + * The pinned messages are mapped by their IDs + */ +class ChannelPinsController extends base_1.BaseFetchAllController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Fetches all messages in the text channel and caches them + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const pins = yield this.bot.api.fetchChannelPins(this.channel.id); + this.cache.merge(pins); + return pins; + }); + } + /** + * Pins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + pin(id) { + return __awaiter(this, void 0, void 0, function* () { + yield this.bot.api.pinMessage(this.channel.id, id); + // Cache the pinned message + const message = yield this.channel.messages.get(id); + this.cache.add(message); + }); + } + /** + * Unpins a message in a text channel. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake} id The ID of the message + * @returns {Promise} + */ + unpin(id) { + return __awaiter(this, void 0, void 0, function* () { + yield this.bot.api.unpinMessage(this.channel.id, id); + // Remove the unpinned message from the cache + this.cache.delete(id); + }); + } +} +exports.ChannelPinsController = ChannelPinsController; diff --git a/lib/controllers/channel/index.d.ts b/lib/controllers/channel/index.d.ts new file mode 100644 index 000000000..10411a20e --- /dev/null +++ b/lib/controllers/channel/index.d.ts @@ -0,0 +1,3 @@ +export * from './ChannelMessagesController'; +export * from './ChannelPermissionsController'; +export * from './ChannelPinsController'; diff --git a/lib/controllers/channel/index.js b/lib/controllers/channel/index.js new file mode 100644 index 000000000..a8d567df7 --- /dev/null +++ b/lib/controllers/channel/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./ChannelMessagesController"), exports); +__exportStar(require("./ChannelPermissionsController"), exports); +__exportStar(require("./ChannelPinsController"), exports); diff --git a/lib/controllers/guild/GuildBansController.d.ts b/lib/controllers/guild/GuildBansController.d.ts new file mode 100644 index 000000000..f2e0c340f --- /dev/null +++ b/lib/controllers/guild/GuildBansController.d.ts @@ -0,0 +1,33 @@ +import Collection from '../../Collection'; +import { Guild, GuildBan } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild's bans cache. + * The bans are mapped by their banned user IDs + */ +export declare class GuildBansController extends BaseFetchController implements BaseFetchAllController, BaseDeleteController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches all bans in the guilds + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Fetches a guild ban by a user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Unbans a member from the guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} id The ID of the member + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildBansController.js b/lib/controllers/guild/GuildBansController.js new file mode 100644 index 000000000..e81ff71b5 --- /dev/null +++ b/lib/controllers/guild/GuildBansController.js @@ -0,0 +1,56 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildBansController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's bans cache. + * The bans are mapped by their banned user IDs + */ +class GuildBansController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all bans in the guilds + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const bans = yield this.bot.api.fetchGuildBans(this.guild.id); + this.cache.merge(bans); + return bans; + }); + } + /** + * Fetches a guild ban by a user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const ban = yield this.bot.api.fetchGuildBan(this.guild.id, id); + this.cache.add(ban); + return ban; + }); + } + /** + * Unbans a member from the guild. + * Requires the {@link Permission.BanMembers} permission + * @param {Snowflake} id The ID of the member + * @returns {Promise} + */ + delete(id) { + return this.bot.api.unbanMember(this.guild.id, id); + } +} +exports.GuildBansController = GuildBansController; diff --git a/lib/controllers/guild/GuildChannelInvitesController.d.ts b/lib/controllers/guild/GuildChannelInvitesController.d.ts new file mode 100644 index 000000000..f5480126c --- /dev/null +++ b/lib/controllers/guild/GuildChannelInvitesController.d.ts @@ -0,0 +1,28 @@ +import Collection from '../../Collection'; +import { Invite, InviteOptions } from '../../structures'; +import { GuildChannel } from '../../structures/channels'; +import { BaseCreateController, BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild channel's invites cache. + * The invites are mapped by their invite codes + */ +export declare class GuildChannelInvitesController extends BaseFetchAllController implements BaseCreateController { + /** + * The channel this controller is associated to + */ + readonly channel: GuildChannel; + constructor(channel: GuildChannel); + /** + * Fetches all invites for this channel. + * Requires the {@link Permission.ManageChannels} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + create(options?: InviteOptions): Promise; +} diff --git a/lib/controllers/guild/GuildChannelInvitesController.js b/lib/controllers/guild/GuildChannelInvitesController.js new file mode 100644 index 000000000..6ad0c7a25 --- /dev/null +++ b/lib/controllers/guild/GuildChannelInvitesController.js @@ -0,0 +1,45 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannelInvitesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild channel's invites cache. + * The invites are mapped by their invite codes + */ +class GuildChannelInvitesController extends base_1.BaseFetchAllController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Fetches all invites for this channel. + * Requires the {@link Permission.ManageChannels} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const invites = yield this.bot.api.fetchChannelInvites(this.channel.id); + this.cache.merge(invites); + return invites; + }); + } + /** + * Creates a new invite for a guild channel. + * Requires the {@link Permission.CreateInstantInvite} permission + * @param {InviteOptions} options The new invite options + * @returns {Promise} + */ + create(options) { + return this.bot.api.createChannelInvite(this.channel.id, options); + } +} +exports.GuildChannelInvitesController = GuildChannelInvitesController; diff --git a/lib/controllers/guild/GuildChannelWebhooksController.d.ts b/lib/controllers/guild/GuildChannelWebhooksController.d.ts new file mode 100644 index 000000000..d73eba01e --- /dev/null +++ b/lib/controllers/guild/GuildChannelWebhooksController.d.ts @@ -0,0 +1,41 @@ +import Collection from '../../Collection'; +import { Webhook, CreateWebhookOptions, GuildChannel } from '../../structures'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild channel's webhooks cache. + * The webhooks are mapped by their IDs + */ +export declare class GuildChannelWebhooksController extends BaseFetchController implements BaseCreateController, BaseFetchAllController, BaseDeleteController { + /** + * The guild channel associated to this controller + */ + readonly channel: GuildChannel; + constructor(channel: GuildChannel); + /** + * Creates a new webhook for this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + create(options: CreateWebhookOptions): Promise; + /** + * Fetches all webhooks in this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Fetches a webhook by its ID + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildChannelWebhooksController.js b/lib/controllers/guild/GuildChannelWebhooksController.js new file mode 100644 index 000000000..4f3b2837e --- /dev/null +++ b/lib/controllers/guild/GuildChannelWebhooksController.js @@ -0,0 +1,73 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannelWebhooksController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild channel's webhooks cache. + * The webhooks are mapped by their IDs + */ +class GuildChannelWebhooksController extends base_1.BaseFetchController { + constructor(channel) { + super(channel); + this.channel = channel; + } + /** + * Creates a new webhook for this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {CreateWebhookOptions} options The options for the new webhook + * @returns {Promise} + */ + create(options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.bot.api.createWebhook(this.channel.id, options); + this.cache.add(webhook); + return webhook; + }); + } + /** + * Fetches all webhooks in this guild channel. + * Requires the {@link Permission.ManageWebhooks} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.bot.api.fetchWebhooks(this.channel.id); + this.cache.merge(webhooks); + return webhooks; + }); + } + /** + * Fetches a webhook by its ID + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.bot.api.fetchWebhook(id); + this.cache.add(webhook); + return webhook; + }); + } + /** + * Deletes a webhook permanently. + * Requires the {@link Permission.ManageWebhooks} permission + * @param {Snowflake} id The ID of the webhook + * @returns {Promise} + */ + delete(id) { + return __awaiter(this, void 0, void 0, function* () { + yield this.bot.api.deleteWebhook(id); + this.cache.delete(id); + }); + } +} +exports.GuildChannelWebhooksController = GuildChannelWebhooksController; diff --git a/lib/controllers/guild/GuildChannelsController.d.ts b/lib/controllers/guild/GuildChannelsController.d.ts new file mode 100644 index 000000000..578f432cc --- /dev/null +++ b/lib/controllers/guild/GuildChannelsController.d.ts @@ -0,0 +1,67 @@ +import Collection from '../../Collection'; +import { Positions } from '../../api'; +import { GuildChannel, CreateGuildChannelOptions, GuildTextChannel, GuildVoiceChannel } from '../../structures/channels'; +import { Guild } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild's channels cache. + * The guild channels a mapped by their IDs + */ +export declare class GuildChannelsController extends BaseFetchController implements BaseCreateController, BaseDeleteController, BaseFetchAllController { + /** + * The guild this controller is associated to + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Gets or fetches a guild text channel by its ID + * @param {Snowflake} id The ID of the guild text channel + * @returns {Promise} + */ + getText(id: Snowflake): Promise; + /** + * Gets or fetches a guild voice channel by its ID + * @param {Snowflake} id The ID of the guild voice channel + * @returns {Promise} + */ + getVoice(id: Snowflake): Promise; + /** + * Creates a new guild channel in the guild associated to this controller. + * Requires the {@link Permission.ManageChannels} + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + create(options: CreateGuildChannelOptions): Promise; + /** + * Deletes a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to delete + * @returns {Promise} + */ + delete(id: Snowflake): Promise; + /** + * Fetches a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches and caches all channels the guild associated to this controller + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Swaps the positions of 2 guild channels with one another + * @param {GuildChannel} channel1 The first guild channel + * @param {GuildChannel} channel2 The second guild channel + * @returns {Promise} + */ + swap(channel1: GuildChannel, channel2: GuildChannel): Promise; + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyPositions(positions: Positions): Promise; +} diff --git a/lib/controllers/guild/GuildChannelsController.js b/lib/controllers/guild/GuildChannelsController.js new file mode 100644 index 000000000..69c60f230 --- /dev/null +++ b/lib/controllers/guild/GuildChannelsController.js @@ -0,0 +1,117 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannelsController = void 0; +const channels_1 = require("../../structures/channels"); +const base_1 = require("../base"); +/** + * Provides an interface for a guild's channels cache. + * The guild channels a mapped by their IDs + */ +class GuildChannelsController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Gets or fetches a guild text channel by its ID + * @param {Snowflake} id The ID of the guild text channel + * @returns {Promise} + */ + getText(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildTextChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + return channel; + }); + } + /** + * Gets or fetches a guild voice channel by its ID + * @param {Snowflake} id The ID of the guild voice channel + * @returns {Promise} + */ + getVoice(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildVoiceChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + return channel; + }); + } + /** + * Creates a new guild channel in the guild associated to this controller. + * Requires the {@link Permission.ManageChannels} + * @param {CreateGuildChannelOptions} options The options for the new guild channel + * @returns {Promise} + */ + create(options) { + return this.bot.api.createGuildChannel(this.guild.id, options); + } + /** + * Deletes a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to delete + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteGuildChannel(id); + } + /** + * Fetches a guild channel + * @param {Snowflake} id The ID of the guild channel you wish to fetch + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.bot.api.fetchGuildChannel(id); + this.cache.add(channel); + this.bot.channels.cache.add(channel); + return channel; + }); + } + /** + * Fetches and caches all channels the guild associated to this controller + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const channels = yield this.bot.api.fetchGuildChannels(this.guild.id); + this.cache.merge(channels); + return channels; + }); + } + /** + * Swaps the positions of 2 guild channels with one another + * @param {GuildChannel} channel1 The first guild channel + * @param {GuildChannel} channel2 The second guild channel + * @returns {Promise} + */ + swap(channel1, channel2) { + return __awaiter(this, void 0, void 0, function* () { + const positions = { [channel1.id]: channel2.position, [channel2.id]: channel1.position }; + return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); + }); + } + /** + * Modifies the positions of a set of channels for the guild. + * Requires the {@Link Permission.ManageChannels} permission + * @param {Positions} positions The new positions for the guild channels + * @returns {Promise} + */ + modifyPositions(positions) { + return __awaiter(this, void 0, void 0, function* () { + return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); + }); + } +} +exports.GuildChannelsController = GuildChannelsController; diff --git a/lib/controllers/guild/GuildEmojisController.d.ts b/lib/controllers/guild/GuildEmojisController.d.ts new file mode 100644 index 000000000..65ce58384 --- /dev/null +++ b/lib/controllers/guild/GuildEmojisController.d.ts @@ -0,0 +1,32 @@ +import Collection from '../../Collection'; +import { Guild, GuildEmoji, CreateEmojiOptions } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Provides an interface for a guild's emojis cache. + * The emojis are mapped by their IDs + */ +export declare class GuildEmojisController extends BaseFetchController implements BaseFetchAllController, BaseCreateController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Creates a new guild emoji + * @param {CreateEmojiOptions} options The options for the new guild emoji + * @returns {Promise} + */ + create(options: CreateEmojiOptions): Promise; + /** + * Fetches a guild emoji by its ID + * @param {Snowflake} id The ID of the guild emoji + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches all emojis in a guild + * @returns {Promise>} + */ + fetchAll(): Promise>; +} diff --git a/lib/controllers/guild/GuildEmojisController.js b/lib/controllers/guild/GuildEmojisController.js new file mode 100644 index 000000000..d20459b82 --- /dev/null +++ b/lib/controllers/guild/GuildEmojisController.js @@ -0,0 +1,55 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildEmojisController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's emojis cache. + * The emojis are mapped by their IDs + */ +class GuildEmojisController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Creates a new guild emoji + * @param {CreateEmojiOptions} options The options for the new guild emoji + * @returns {Promise} + */ + create(options) { + return this.bot.api.createGuildEmoji(this.guild.id, options); + } + /** + * Fetches a guild emoji by its ID + * @param {Snowflake} id The ID of the guild emoji + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const emoji = yield this.bot.api.fetchGuildEmoji(this.guild.id, id); + this.cache.add(emoji); + return emoji; + }); + } + /** + * Fetches all emojis in a guild + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const emojis = yield this.bot.api.fetchGuildEmojis(this.guild.id); + this.cache.merge(emojis); + return emojis; + }); + } +} +exports.GuildEmojisController = GuildEmojisController; diff --git a/lib/controllers/guild/GuildIntegrationsController.d.ts b/lib/controllers/guild/GuildIntegrationsController.d.ts new file mode 100644 index 000000000..4bb2deed5 --- /dev/null +++ b/lib/controllers/guild/GuildIntegrationsController.d.ts @@ -0,0 +1,35 @@ +import Collection from '../../Collection'; +import { Guild, GuildIntegration, CreateIntegrationOptions } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild's integrations cache. + * The integrations are mapped by their IDs + */ +export declare class GuildIntegrationsController extends BaseFetchAllController implements BaseDeleteController { + /** + * The guild associated to this controller + */ + guild: Guild; + constructor(guild: Guild); + /** + * Fetches all guild integrations in this guild + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + create(options: CreateIntegrationOptions): Promise; + /** + * Deletes the attached integration for this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} id The ID of the guild integration + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildIntegrationsController.js b/lib/controllers/guild/GuildIntegrationsController.js new file mode 100644 index 000000000..0f27ba138 --- /dev/null +++ b/lib/controllers/guild/GuildIntegrationsController.js @@ -0,0 +1,54 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildIntegrationsController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's integrations cache. + * The integrations are mapped by their IDs + */ +class GuildIntegrationsController extends base_1.BaseFetchAllController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all guild integrations in this guild + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const integrations = yield this.bot.api.fetchGuildIntegrations(this.guild.id); + this.cache.merge(integrations); + return integrations; + }); + } + /** + * Attaches an integration from the Bot to this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {CreateIntegrationOptions} options The options for the new integration + * @returns {Promise} + */ + create(options) { + return this.bot.api.createGuildIntegration(this.guild.id, options); + } + /** + * Deletes the attached integration for this guild. + * Requires the {@link Permission.ManageGuild} permission + * @param {Snowflake} id The ID of the guild integration + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteGuildIntegration(this.guild.id, id); + } +} +exports.GuildIntegrationsController = GuildIntegrationsController; diff --git a/lib/controllers/guild/GuildInvitesController.d.ts b/lib/controllers/guild/GuildInvitesController.d.ts new file mode 100644 index 000000000..61131a510 --- /dev/null +++ b/lib/controllers/guild/GuildInvitesController.d.ts @@ -0,0 +1,44 @@ +import Collection from '../../Collection'; +import { Invite } from '../../structures'; +import { Guild } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; +/** + * Options for when fetching an invite + */ +export interface FetchInviteOptions { + /** + * Whether the invite should contain approximate member counts + */ + withCounts?: boolean; +} +/** + * Provides an interface for a guild's invites cache + * The invites are mapped by their invite codes + */ +export declare class GuildInvitesController extends BaseFetchController implements BaseDeleteController, BaseFetchAllController { + /** + * The guild this controller is associated to + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Delete an invite by its invite code + * @param {Snowflake} code The invite code + * @returns {Promise} + */ + delete(code: Snowflake): Promise; + /** + * Fetches an invite by its invite code + * @param {string} code The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetch(code: string, options?: FetchInviteOptions): Promise; + /** + * Fetches all invites (with metadata) in this guild. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll(): Promise>; +} diff --git a/lib/controllers/guild/GuildInvitesController.js b/lib/controllers/guild/GuildInvitesController.js new file mode 100644 index 000000000..f6834301a --- /dev/null +++ b/lib/controllers/guild/GuildInvitesController.js @@ -0,0 +1,57 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildInvitesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's invites cache + * The invites are mapped by their invite codes + */ +class GuildInvitesController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Delete an invite by its invite code + * @param {Snowflake} code The invite code + * @returns {Promise} + */ + delete(code) { + return this.bot.api.deleteInvite(code); + } + /** + * Fetches an invite by its invite code + * @param {string} code The invite code + * @param {FetchInviteOptions} options An additional set of options for the invite + * @returns {Promise} + */ + fetch(code, options) { + return __awaiter(this, void 0, void 0, function* () { + const invite = yield this.bot.api.fetchInvite(code, options); + this.cache.add(invite); + return invite; + }); + } + /** + * Fetches all invites (with metadata) in this guild. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const invites = yield this.bot.api.fetchGuildInvites(this.guild.id); + this.cache.merge(invites); + return invites; + }); + } +} +exports.GuildInvitesController = GuildInvitesController; diff --git a/lib/controllers/guild/GuildMembersController.d.ts b/lib/controllers/guild/GuildMembersController.d.ts new file mode 100644 index 000000000..4a4923481 --- /dev/null +++ b/lib/controllers/guild/GuildMembersController.d.ts @@ -0,0 +1,52 @@ +import Collection from '../../Collection'; +import { Guild } from '../../structures/guild'; +import { Member } from '../../structures/member/Member'; +import { Snowflake } from '../../types'; +import { BaseFetchSomeController, BaseFetchController } from '../base'; +/** + * Options for when fetching some guild members in a guild + */ +export interface FetchSomeMembersOptions { + /** + * The max number of members to return (1-1000) + */ + limit?: number; + /** + * The highest user ID in the previous page + */ + after?: Snowflake; +} +/** + * Provides an interface for a guild's members cache. + * The members are mapped by their IDs + */ +export declare class GuildMembersController extends BaseFetchController implements BaseFetchSomeController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches a guild member and caches it + * @param {Snowflake} id The ID of the guild member + * @returns {Promise} + */ + fetch(id: Snowflake): Promise; + /** + * Fetches some guild members in this guild and caches them + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options?: FetchSomeMembersOptions): Promise>; + /** + * Removes a user from this guild by its user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + remove(id: Snowflake): Promise; + /** + * Returns the bot member in the guild + * @type {Member | undefined} + */ + get me(): Member | undefined; +} diff --git a/lib/controllers/guild/GuildMembersController.js b/lib/controllers/guild/GuildMembersController.js new file mode 100644 index 000000000..beb8241ab --- /dev/null +++ b/lib/controllers/guild/GuildMembersController.js @@ -0,0 +1,65 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildMembersController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's members cache. + * The members are mapped by their IDs + */ +class GuildMembersController extends base_1.BaseFetchController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches a guild member and caches it + * @param {Snowflake} id The ID of the guild member + * @returns {Promise} + */ + fetch(id) { + return __awaiter(this, void 0, void 0, function* () { + const member = yield this.bot.api.fetchMember(this.guild.id, id); + this.cache.add(member); + return member; + }); + } + /** + * Fetches some guild members in this guild and caches them + * @param {FetchSomeMembersOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchSome(options) { + return __awaiter(this, void 0, void 0, function* () { + const members = yield this.bot.api.fetchSomeMembers(this.guild.id, options); + this.cache.merge(members); + return members; + }); + } + /** + * Removes a user from this guild by its user ID + * @param {Snowflake} id The ID of the user + * @returns {Promise} + */ + remove(id) { + return __awaiter(this, void 0, void 0, function* () { + return this.bot.api.removeMember(this.guild.id, id); + }); + } + /** + * Returns the bot member in the guild + * @type {Member | undefined} + */ + get me() { + return this.bot.user && this.cache.get(this.bot.user.id); + } +} +exports.GuildMembersController = GuildMembersController; diff --git a/lib/controllers/guild/GuildRolesController.d.ts b/lib/controllers/guild/GuildRolesController.d.ts new file mode 100644 index 000000000..f38121d1f --- /dev/null +++ b/lib/controllers/guild/GuildRolesController.d.ts @@ -0,0 +1,43 @@ +import Collection from '../../Collection'; +import { Positions } from '../../api'; +import { Role, RoleOptions } from '../../structures'; +import { Guild } from '../../structures/guild'; +import { Snowflake } from '../../types'; +import { BaseCreateController, BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild's roles cache. + * The roles are mapped by their IDs + */ +export declare class GuildRolesController extends BaseFetchAllController implements BaseCreateController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches all roles in this guild + * @returns {Promise>} + */ + fetchAll(): Promise>; + /** + * Creates a new role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions | undefined} options The options for the created role + * @returns {Promise} + */ + create(options?: RoleOptions): Promise; + /** + * Modifies the positions of a set of roles for this guild. + * Requires the {@link Permission.ManageRoles} + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyPositions(positions: Positions): Promise>; + /** + * Deletes a role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the role + * @returns {Promise} + */ + delete(id: Snowflake): Promise; +} diff --git a/lib/controllers/guild/GuildRolesController.js b/lib/controllers/guild/GuildRolesController.js new file mode 100644 index 000000000..ad86f30e8 --- /dev/null +++ b/lib/controllers/guild/GuildRolesController.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildRolesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's roles cache. + * The roles are mapped by their IDs + */ +class GuildRolesController extends base_1.BaseFetchAllController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all roles in this guild + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const roles = yield this.bot.api.fetchRoles(this.guild.id); + this.cache.merge(roles); + return roles; + }); + } + /** + * Creates a new role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions | undefined} options The options for the created role + * @returns {Promise} + */ + create(options) { + return this.bot.api.createRole(this.guild.id, options); + } + /** + * Modifies the positions of a set of roles for this guild. + * Requires the {@link Permission.ManageRoles} + * @param {Positions} positions The new roles positions + * @returns {Promise>} A collection of all the guild's roles + */ + modifyPositions(positions) { + return this.bot.api.modifyRolesPositions(this.guild.id, positions); + } + /** + * Deletes a role in this guild. + * Requires the {@link Permission.ManageRoles} permission + * @param {Snowflake} id The ID of the role + * @returns {Promise} + */ + delete(id) { + return this.bot.api.deleteRole(this.guild.id, id); + } +} +exports.GuildRolesController = GuildRolesController; diff --git a/lib/controllers/guild/GuildWebhooksController.d.ts b/lib/controllers/guild/GuildWebhooksController.d.ts new file mode 100644 index 000000000..da24bcb50 --- /dev/null +++ b/lib/controllers/guild/GuildWebhooksController.d.ts @@ -0,0 +1,20 @@ +import Collection from '../../Collection'; +import { Guild, Webhook } from '../../structures'; +import { Snowflake } from '../../types'; +import { BaseFetchAllController } from '../base'; +/** + * Provides an interface for a guild's webhooks cache. + * The webhooks are mapped by their IDs + */ +export declare class GuildWebhooksController extends BaseFetchAllController { + /** + * The guild associated to this controller + */ + readonly guild: Guild; + constructor(guild: Guild); + /** + * Fetches all webhooks in this guild + * @returns {Promise>} + */ + fetchAll(): Promise>; +} diff --git a/lib/controllers/guild/GuildWebhooksController.js b/lib/controllers/guild/GuildWebhooksController.js new file mode 100644 index 000000000..1fbf70de3 --- /dev/null +++ b/lib/controllers/guild/GuildWebhooksController.js @@ -0,0 +1,35 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildWebhooksController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a guild's webhooks cache. + * The webhooks are mapped by their IDs + */ +class GuildWebhooksController extends base_1.BaseFetchAllController { + constructor(guild) { + super(guild); + this.guild = guild; + } + /** + * Fetches all webhooks in this guild + * @returns {Promise>} + */ + fetchAll() { + return __awaiter(this, void 0, void 0, function* () { + const webhooks = yield this.bot.api.fetchGuildWebhooks(this.guild.id); + this.cache.merge(webhooks); + return webhooks; + }); + } +} +exports.GuildWebhooksController = GuildWebhooksController; diff --git a/lib/controllers/guild/index.d.ts b/lib/controllers/guild/index.d.ts new file mode 100644 index 000000000..b9b3734d6 --- /dev/null +++ b/lib/controllers/guild/index.d.ts @@ -0,0 +1,10 @@ +export * from './GuildBansController'; +export * from './GuildChannelInvitesController'; +export * from './GuildChannelsController'; +export * from './GuildChannelWebhooksController'; +export * from './GuildEmojisController'; +export * from './GuildIntegrationsController'; +export * from './GuildInvitesController'; +export * from './GuildMembersController'; +export * from './GuildRolesController'; +export * from './GuildWebhooksController'; diff --git a/lib/controllers/guild/index.js b/lib/controllers/guild/index.js new file mode 100644 index 000000000..82436c7e2 --- /dev/null +++ b/lib/controllers/guild/index.js @@ -0,0 +1,22 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./GuildBansController"), exports); +__exportStar(require("./GuildChannelInvitesController"), exports); +__exportStar(require("./GuildChannelsController"), exports); +__exportStar(require("./GuildChannelWebhooksController"), exports); +__exportStar(require("./GuildEmojisController"), exports); +__exportStar(require("./GuildIntegrationsController"), exports); +__exportStar(require("./GuildInvitesController"), exports); +__exportStar(require("./GuildMembersController"), exports); +__exportStar(require("./GuildRolesController"), exports); +__exportStar(require("./GuildWebhooksController"), exports); diff --git a/lib/controllers/index.d.ts b/lib/controllers/index.d.ts new file mode 100644 index 000000000..208155bb9 --- /dev/null +++ b/lib/controllers/index.d.ts @@ -0,0 +1,8 @@ +export * from './base'; +export * from './bot'; +export * from './channel'; +export * from './guild'; +export * from './member'; +export * from './message'; +export * from './reaction'; +export * from './ControllerCache'; diff --git a/lib/controllers/index.js b/lib/controllers/index.js new file mode 100644 index 000000000..622ad3492 --- /dev/null +++ b/lib/controllers/index.js @@ -0,0 +1,20 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./base"), exports); +__exportStar(require("./bot"), exports); +__exportStar(require("./channel"), exports); +__exportStar(require("./guild"), exports); +__exportStar(require("./member"), exports); +__exportStar(require("./message"), exports); +__exportStar(require("./reaction"), exports); +__exportStar(require("./ControllerCache"), exports); diff --git a/lib/controllers/member/MemberRolesController.d.ts b/lib/controllers/member/MemberRolesController.d.ts new file mode 100644 index 000000000..2bb620f3f --- /dev/null +++ b/lib/controllers/member/MemberRolesController.d.ts @@ -0,0 +1,31 @@ +import { Role } from '../../structures'; +import { Member } from '../../structures/member/Member'; +import { Snowflake } from '../../types'; +import { BaseController } from '../base'; +/** + * Provides an interface for a member's roles cache. + * The roles are mapped by their IDs + */ +export declare class MemberRolesController extends BaseController { + /** + * The member associated to this controller + */ + readonly member: Member; + /** + * The guild this member is in + */ + private readonly guild; + constructor(member: Member); + /** + * Adds a role to this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + add(roleId: Snowflake): Promise; + /** + * Removes a role from this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + remove(roleId: Snowflake): Promise; +} diff --git a/lib/controllers/member/MemberRolesController.js b/lib/controllers/member/MemberRolesController.js new file mode 100644 index 000000000..f6c25bb2e --- /dev/null +++ b/lib/controllers/member/MemberRolesController.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MemberRolesController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a member's roles cache. + * The roles are mapped by their IDs + */ +class MemberRolesController extends base_1.BaseController { + constructor(member) { + super(member); + this.member = member; + this.guild = member.guild; + } + /** + * Adds a role to this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + add(roleId) { + return this.bot.api.memberAddRole(this.guild.id, this.member.id, roleId); + } + /** + * Removes a role from this member by the role's ID + * @param {Snowflake} roleId The ID of the role + * @returns {Promise} + */ + remove(roleId) { + return this.bot.api.memberRemoveRole(this.guild.id, this.member.id, roleId); + } +} +exports.MemberRolesController = MemberRolesController; diff --git a/lib/controllers/member/index.d.ts b/lib/controllers/member/index.d.ts new file mode 100644 index 000000000..905339dc9 --- /dev/null +++ b/lib/controllers/member/index.d.ts @@ -0,0 +1 @@ +export * from './MemberRolesController'; diff --git a/lib/controllers/member/index.js b/lib/controllers/member/index.js new file mode 100644 index 000000000..ef97e9693 --- /dev/null +++ b/lib/controllers/member/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./MemberRolesController"), exports); diff --git a/lib/controllers/message/MessageReactionsController.d.ts b/lib/controllers/message/MessageReactionsController.d.ts new file mode 100644 index 000000000..fdf782839 --- /dev/null +++ b/lib/controllers/message/MessageReactionsController.d.ts @@ -0,0 +1,36 @@ +import { EmojiResolvable } from '../../structures'; +import { Message, MessageReaction } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseDeleteController } from '../base'; +/** + * Provides an interface for a message's reactions cache. + * The reactions are mapped by the emoji name or emoji ID + */ +export declare class MessageReactionsController extends BaseDeleteController { + /** + * The message this controller is associated to + */ + readonly message: Message; + constructor(message: Message); + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {EmojiResolvable} emoji The emoji to remove from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + delete(emoji: EmojiResolvable, userId?: Snowflake): Promise; + /** + * Deletes all reactions for an emoji. + * Requires the {@link Permission.ManageMessages} permission + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + deleteEmoji(emoji: EmojiResolvable): Promise; + /** + * Removes all reactions on the message associated to this controller. + * Requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @returns {Promise} + */ + deleteAll(): Promise; +} diff --git a/lib/controllers/message/MessageReactionsController.js b/lib/controllers/message/MessageReactionsController.js new file mode 100644 index 000000000..878dafe4d --- /dev/null +++ b/lib/controllers/message/MessageReactionsController.js @@ -0,0 +1,42 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageReactionsController = void 0; +const base_1 = require("../base"); +/** + * Provides an interface for a message's reactions cache. + * The reactions are mapped by the emoji name or emoji ID + */ +class MessageReactionsController extends base_1.BaseDeleteController { + constructor(message) { + super(message); + this.message = message; + } + /** + * Deletes a reaction a user reacted with. + * If no `userId` argument was provided, the Bot will remove its own reaction. + * @param {EmojiResolvable} emoji The emoji to remove from the message + * @param {Snowflake} userId The ID of the user of which reaction should be removed + * @returns {Promise} + */ + delete(emoji, userId = '@me') { + return this.bot.api.removeMessageReaction(this.message.channel.id, this.message.id, emoji, userId); + } + /** + * Deletes all reactions for an emoji. + * Requires the {@link Permission.ManageMessages} permission + * @param {EmojiResolvable} emoji The reaction emoji you wish to delete + * @returns {Promise} + */ + deleteEmoji(emoji) { + return this.bot.api.removeMessageReactionsEmoji(this.message.channel.id, this.message.id, emoji); + } + /** + * Removes all reactions on the message associated to this controller. + * Requires the {@link Permission.ManageMessages} permission to be present on the Bot + * @returns {Promise} + */ + deleteAll() { + return this.bot.api.removeMessageReactions(this.message.channel.id, this.message.id); + } +} +exports.MessageReactionsController = MessageReactionsController; diff --git a/lib/controllers/message/index.d.ts b/lib/controllers/message/index.d.ts new file mode 100644 index 000000000..7541f63b7 --- /dev/null +++ b/lib/controllers/message/index.d.ts @@ -0,0 +1 @@ +export * from './MessageReactionsController'; diff --git a/lib/controllers/message/index.js b/lib/controllers/message/index.js new file mode 100644 index 000000000..022a3f75e --- /dev/null +++ b/lib/controllers/message/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./MessageReactionsController"), exports); diff --git a/lib/controllers/reaction/ReactionUsersController.d.ts b/lib/controllers/reaction/ReactionUsersController.d.ts new file mode 100644 index 000000000..ae929d3f9 --- /dev/null +++ b/lib/controllers/reaction/ReactionUsersController.d.ts @@ -0,0 +1,43 @@ +import Collection from '../../Collection'; +import { User } from '../../structures'; +import { MessageReaction } from '../../structures/message'; +import { Snowflake } from '../../types'; +import { BaseFetchAllController } from '../base'; +/** + * Options for when fetching the users that reacted with a particular emoji + */ +export interface FetchReactionUsersOptions { + /** + * Get users before this user ID + */ + before?: Snowflake; + /** + * Get users after this user ID + */ + after?: Snowflake; + /** + * Max number of users to return (1-100) + */ + limit?: number; +} +/** + * Interface for the users that added a reaction identified by its emoji. + * The users are mapped by their IDs + */ +export declare class ReactionUsersController extends BaseFetchAllController { + /** + * The reaction this controller is associated to + */ + readonly reaction: MessageReaction; + /** + * The message associated to the reaction + */ + private readonly message; + constructor(reaction: MessageReaction); + /** + * Fetches all users that reacted with the reaction emoji associated to this controller + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise} + */ + fetchAll(options?: FetchReactionUsersOptions): Promise>; +} diff --git a/lib/controllers/reaction/ReactionUsersController.js b/lib/controllers/reaction/ReactionUsersController.js new file mode 100644 index 000000000..3ee5eac42 --- /dev/null +++ b/lib/controllers/reaction/ReactionUsersController.js @@ -0,0 +1,48 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ReactionUsersController = void 0; +const base_1 = require("../base"); +/** + * Interface for the users that added a reaction identified by its emoji. + * The users are mapped by their IDs + */ +class ReactionUsersController extends base_1.BaseFetchAllController { + constructor(reaction) { + super(reaction); + this.reaction = reaction; + this.message = reaction.message; + } + /** + * Fetches all users that reacted with the reaction emoji associated to this controller + * @param {FetchReactionUsersOptions} options A set of options for this operation + * @returns {Promise} + */ + fetchAll(options) { + return __awaiter(this, void 0, void 0, function* () { + const users = yield this.bot.api.fetchReactionUsers(this.message.channel.id, this.message.id, this.reaction.id, options); + this.cache.merge(users); + if (this.bot.user && users.has(this.bot.user.id)) { + // The bot reacted to this reaction + this.reaction.botReacted = true; + } + if (this.message.guild) { + // The message was sent in a guild + // All users are also members in that guild + this.reaction.members.merge(users + .filter(user => this.message.guild.members.cache.has(user.id)) + .map(user => this.message.guild.members.cache.get(user.id))); + } + return users; + }); + } +} +exports.ReactionUsersController = ReactionUsersController; diff --git a/lib/controllers/reaction/index.d.ts b/lib/controllers/reaction/index.d.ts new file mode 100644 index 000000000..f0b59f3fa --- /dev/null +++ b/lib/controllers/reaction/index.d.ts @@ -0,0 +1 @@ +export * from './ReactionUsersController'; diff --git a/lib/controllers/reaction/index.js b/lib/controllers/reaction/index.js new file mode 100644 index 000000000..61264c8be --- /dev/null +++ b/lib/controllers/reaction/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./ReactionUsersController"), exports); diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 000000000..d45c805f0 --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,8 @@ +export * from './api'; +export * from './bot'; +export * from './controllers'; +export * from './sharding'; +export * from './socket'; +export * from './structures'; +export * from './types'; +export * as Collection from './Collection'; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 000000000..f6471494d --- /dev/null +++ b/lib/index.js @@ -0,0 +1,32 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./api"), exports); +__exportStar(require("./bot"), exports); +__exportStar(require("./controllers"), exports); +__exportStar(require("./sharding"), exports); +__exportStar(require("./socket"), exports); +__exportStar(require("./structures"), exports); +__exportStar(require("./types"), exports); +exports.Collection = __importStar(require("./Collection")); diff --git a/lib/sharding/BotCommunication.d.ts b/lib/sharding/BotCommunication.d.ts new file mode 100644 index 000000000..d62d9018d --- /dev/null +++ b/lib/sharding/BotCommunication.d.ts @@ -0,0 +1,253 @@ +/// +import { Serializable } from 'child_process'; +import { EventEmitter } from 'events'; +import { Arguments } from 'typed-emitter'; +import { BotShardState } from './BotShard'; +import { Bot } from '../bot'; +import { Events, BotStateEvents } from '../bot/handlers/events/events'; +import { GatewayCloseCode } from '../socket'; +import { ShardId } from '../types'; +/** + * Abstract typing for all shard requests + */ +export interface ShardRequest { + /** + * The action to dispatch + */ + action: string; + /** + * The matching payload for the action + */ + payload: Serializable; + /** + * The Unix date of when this request was sent. + * Used to identify the response of this request + */ + identifier: number; +} +/** + * Abstract typing for all shard responses + */ +export interface ShardResponse { + /** + * Data to be sent in response to a request + */ + payload: Serializable; + /** + * The Unix date of when this request was sent. + * Used to identify the request that led to this response + */ + identifier: number; +} +/** + * Actions that can be sent to the shard manager to be evaluated in specific / all shards + */ +export declare const enum ShardCommunicationAction { + /** + * Updates the presence status of all shards + */ + UpdatePresence = "updatePresence", + /** + * Emits an event on all shards + */ + Broadcast = "broadcast", + /** + * Emits an event on a specific shard + */ + Send = "send", + /** + * A shard has changed its state + */ + ShardChangedState = "shardChangedState", + /** + * A shard requested to disconnect all currently active shards + */ + DisconnectAll = "disconnectAll" +} +/** + * The type of result sent in response to an action ({@link ShardCommunicationAction}) from the shard manager + */ +export declare const enum ShardCommunicationActionResponses { + /** + * Responses sent in response to a {@link ShardCommunicationAction.Broadcast} request + */ + BroadcastResponses = "broadcastResponses", + /** + * Response sent in response to a {@link ShardCommunicationAction.Send} request + */ + SendResponse = "sendResponse" +} +/** + * Actions to emit communication / bot events on specific / all shards + */ +export declare const enum ShardCommunicationEmitEvents { + /** + * Emit a specific communication event + */ + EmitCommunicationEvent = "emitCommunicationEvent", + /** + * The response for {@link ShardCommunicationEmitEvents.EmitCommunicationEvent} + */ + EmitCommunicationEventResponse = "emitCommunicationEventResponse", + /** + * Emit a specific Bot event (registered under {@link EventsHandler}) + */ + EmitBotEvent = "emitBotEvent", + /** + * Tells the shard to disconnect from their gateway connection + */ + EmitDisconnect = "emitDisconnect" +} +/** + * Format for the request to emit an event on all shards + * {@link ShardCommunicationAction.Broadcast} + */ +export interface ShardBroadcastRequest extends ShardRequest { + action: ShardCommunicationAction.Broadcast; + /** + * The name of the event to emit + */ + payload: string; +} +/** + * Format for the request to emit an event to a specific shard + * {@link ShardCommunicationAction.Send} + */ +export interface ShardSendRequest extends ShardRequest { + action: ShardCommunicationAction.Send; + payload: { + /** + * The name of the event to emit + */ + event: string; + /** + * The shard ID to emit this event on + */ + shardId: ShardId; + }; +} +/** + * Request sent to the shard manager when a shard has changed its state + */ +export interface ShardChangedStateRequest extends ShardRequest { + action: ShardCommunicationAction.ShardChangedState; + payload: { + /** + * The new shard state + */ + state: BotShardState; + /** + * The Bot event ({@link EventsHandler}) to emit if all remaining shards share the same state + */ + botEvent: BotStateEvents; + }; +} +export interface ShardDisconnectAllRequest extends ShardRequest { + action: ShardCommunicationAction.DisconnectAll; + /** + * The close code for all shards + */ + payload: GatewayCloseCode; +} +/** + * Request to emit a Bot event on a shard. + * Sent if all shards share the same state {@link ShardChangedStateRequest} + */ +export interface ShardEmitBotEventRequest extends ShardRequest { + action: ShardCommunicationEmitEvents.EmitBotEvent; + payload: { + /** + * The Bot Event to emit + */ + event: E; + /** + * The arguments this event requires + */ + args: Arguments; + }; +} +/** + * Request a communication event to be emitted on a specific / all shards + */ +export interface ShardEmitCommunicationEventRequest extends ShardRequest { + action: ShardCommunicationEmitEvents.EmitCommunicationEvent; + payload: { + /** + * The event name + */ + event: string; + }; +} +export interface ShardEmitDisconnectRequest extends ShardRequest { + action: ShardCommunicationEmitEvents.EmitDisconnect; + /** + * The close code for the shard + */ + payload: GatewayCloseCode; +} +/** + * Response for a {@link ShardBroadcastRequest} / {@link ShardSendRequest} request + */ +export interface ShardCommunicationActionResponse extends ShardResponse { + payload: { + event: string; + data?: Serializable | Serializable[]; + }; +} +/** + * Response for a {@link ShardEmitCommunicationEventRequest} + */ +export interface ShardEmitCommunicationEventResponse extends ShardResponse { + payload: { + /** + * Data returned from the event + */ + data: Serializable | Serializable[]; + }; +} +export declare class BotCommunication extends EventEmitter { + /** + * The Bot instance + */ + private readonly bot; + constructor(bot: Bot); + /** + * Listener function for new messages coming from the parent process + * @param {ShardEmitCommunicationEventRequest | ShardEmitBotEventRequest} message The message received from the parent process + * @returns {Promise} + */ + private onMessage; + /** + * {@link EventEmitter} 'on' override to resolve with the listener's returned value + * @param {string | symbol} event Event name + * @param {function(bot: Bot)} listener Callback to be executed when this event is emitted + * @returns {this} + */ + on(event: string | symbol, listener: (bot: Bot) => void): this; + /** + * {@link EventEmitter} 'emit' override to return a {@link Promise} with the return value of the registered listener + * @param {string | symbol} event + * @param args + * @returns {Promise} + */ + emit(event: string | symbol, ...args: unknown[]): boolean; + emit(event: string | symbol, ...args: unknown[]): Promise; + /** + * Send and receive the data returned from the registered event for each shard + * @param {string} event The registered event to be emitted + * @returns {Promise} + */ + broadcast(event: string): Promise; + /** + * Send and receive the data returned from the registered event for the shard matching the supplied ID + * @param {string} event The registered event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise} + */ + send(event: string, shardId: ShardId): Promise; + /** + * Generates a random identifier to provide to cross-shard requests + * @type {number} + */ + static get identifier(): number; +} diff --git a/lib/sharding/BotCommunication.js b/lib/sharding/BotCommunication.js new file mode 100644 index 000000000..0d7a34728 --- /dev/null +++ b/lib/sharding/BotCommunication.js @@ -0,0 +1,138 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotCommunication = void 0; +const events_1 = require("events"); +class BotCommunication extends events_1.EventEmitter { + constructor(bot) { + super(); + this.bot = bot; + process.on('message', this.onMessage.bind(this)); + } + /** + * Listener function for new messages coming from the parent process + * @param {ShardEmitCommunicationEventRequest | ShardEmitBotEventRequest} message The message received from the parent process + * @returns {Promise} + */ + onMessage(message) { + return __awaiter(this, void 0, void 0, function* () { + switch (message.action) { + // Tells the Bot to dispatch an event and return its result + case "emitCommunicationEvent" /* EmitCommunicationEvent */: + if (process.send) { + const data = yield this.emit(message.payload.event); + const reply = { + payload: { + data, + }, + identifier: message.identifier, + }; + process.send(reply); + } + break; + // Tells the Bot to emit an event to BotEvents + case "emitBotEvent" /* EmitBotEvent */: + this.bot.events.emit(message.payload.event, ...message.payload.args); + break; + // Tells the Bot to disconnect from its current connection + case "emitDisconnect" /* EmitDisconnect */: + this.bot.connection.disconnect(message.payload); + break; + } + }); + } + /** + * {@link EventEmitter} 'on' override to resolve with the listener's returned value + * @param {string | symbol} event Event name + * @param {function(bot: Bot)} listener Callback to be executed when this event is emitted + * @returns {this} + */ + on(event, listener) { + super.on(event, ([resolve, reject]) => { + try { + resolve(listener.bind(this)(this.bot)); + } + catch (err) { + reject(err); + } + }); + return this; + } + emit(event, ...args) { + return new Promise((resolve, reject) => { + super.emit(event, [resolve, reject], ...args); + }); + } + /** + * Send and receive the data returned from the registered event for each shard + * @param {string} event The registered event to be emitted + * @returns {Promise} + */ + broadcast(event) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => { + const { identifier } = BotCommunication; + const listener = ({ payload: { event: event, data }, identifier: responseIdentifier, }) => { + if (event === "broadcastResponses" /* BroadcastResponses */ && + identifier === responseIdentifier && + Array.isArray(data)) { + resolve(data); + } + }; + process.on('message', listener); + const request = { + action: "broadcast" /* Broadcast */, + payload: event, + identifier, + }; + if (process.send) { + process.send(request); + } + }); + }); + } + /** + * Send and receive the data returned from the registered event for the shard matching the supplied ID + * @param {string} event The registered event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise} + */ + send(event, shardId) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => { + const { identifier } = BotCommunication; + const listener = ({ payload: { event, data }, identifier: responseIdentifier, }) => { + if (event === "sendResponse" /* SendResponse */ && + identifier === responseIdentifier) { + resolve(data); + } + }; + process.on('message', listener); + const message = { + action: "send" /* Send */, + payload: { event, shardId }, + identifier, + }; + if (process.send) { + process.send(message); + } + }); + }); + } + /** + * Generates a random identifier to provide to cross-shard requests + * @type {number} + */ + static get identifier() { + return Math.random(); + } +} +exports.BotCommunication = BotCommunication; diff --git a/lib/sharding/BotShard.d.ts b/lib/sharding/BotShard.d.ts new file mode 100644 index 000000000..63bd9c6a2 --- /dev/null +++ b/lib/sharding/BotShard.d.ts @@ -0,0 +1,53 @@ +/// +import { Serializable } from 'child_process'; +import { Arguments } from 'typed-emitter'; +import { ShardBroadcastRequest, ShardChangedStateRequest, ShardDisconnectAllRequest, ShardSendRequest } from './BotCommunication'; +import { BotShardManager } from './BotShardManager'; +import { Events } from '../bot/handlers/events/events'; +import { GatewayCloseCode } from '../socket'; +import { ShardId } from '../types'; +/** + * The shard state + */ +export declare enum BotShardState { + Ready = 0, + Closed = 1 +} +/** + * Creates and handles the communication of a shard + */ +export declare class BotShard { + private readonly manager; + private process; + readonly id: ShardId; + state: BotShardState; + constructor(manager: BotShardManager, id: ShardId); + /** + * Spawns a new child according to the given file path. + * Sets the environmental variables for the process with the sharding information + */ + spawn(): void; + /** + * Listener for child process messages + * @param {ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest} request The request received from the child process + * @returns {Promise} + */ + onMessage(request: ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest | ShardDisconnectAllRequest): Promise; + /** + * Sends a message to the child process in order to emit a registered given event + * @param {string} event The event to be emitted in the child process + * @returns {Promise} + */ + communicate(event: string): Promise; + /** + * Sends the child process a message to emit the given event to {@link EventsHandler} + * @param {E} event The event to be emitted + * @param {Array} args The arguments of the events + */ + emitEvent(event: E, args: Arguments): void; + /** + * Disconnects this shard + * @param {GatewayCloseCode} code The shard close code + */ + disconnect(code: GatewayCloseCode): void; +} diff --git a/lib/sharding/BotShard.js b/lib/sharding/BotShard.js new file mode 100644 index 000000000..79235d41e --- /dev/null +++ b/lib/sharding/BotShard.js @@ -0,0 +1,158 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotShard = exports.BotShardState = void 0; +const child_process_1 = require("child_process"); +const path_1 = __importDefault(require("path")); +const BotCommunication_1 = require("./BotCommunication"); +/** + * The shard state + */ +var BotShardState; +(function (BotShardState) { + BotShardState[BotShardState["Ready"] = 0] = "Ready"; + BotShardState[BotShardState["Closed"] = 1] = "Closed"; +})(BotShardState = exports.BotShardState || (exports.BotShardState = {})); +/** + * Creates and handles the communication of a shard + */ +class BotShard { + constructor(manager, id) { + this.manager = manager; + this.id = id; + this.state = BotShardState.Closed; + } + /** + * Spawns a new child according to the given file path. + * Sets the environmental variables for the process with the sharding information + */ + spawn() { + this.process = child_process_1.fork(path_1.default.resolve(this.manager.file), [], { + env: { + SHARD_ID: this.id.toString(), + SHARDS_AMOUNT: this.manager.shardsAmount.toString(), + }, + }); + this.process.on('message', this.onMessage.bind(this)); + } + /** + * Listener for child process messages + * @param {ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest} request The request received from the child process + * @returns {Promise} + */ + onMessage(request) { + return __awaiter(this, void 0, void 0, function* () { + const { identifier } = request; + switch (request.action) { + // Broadcast requested by the shard + case "broadcast" /* Broadcast */: { + const results = yield this.manager.broadcast(request.payload); + const response = { + payload: { + event: "broadcastResponses" /* BroadcastResponses */, + data: results, + }, + identifier, + }; + this.process.send(response); + break; + } + // Single shard send requested by the shard + case "send" /* Send */: { + const { event, shardId } = request.payload; + const result = yield this.manager.send(event, shardId); + const response = { + payload: { + event: "sendResponse" /* SendResponse */, + data: result, + }, + identifier, + }; + this.process.send(response); + break; + } + // The shard changed its state + case "shardChangedState" /* ShardChangedState */: { + this.state = request.payload.state; + if (this.manager.checkShardsState(request.payload.state)) { + this.manager.emitEvent(request.payload.botEvent, []); + } + break; + } + // The shard requests all connected shards to disconnect + case "disconnectAll" /* DisconnectAll */: { + yield this.manager.disconnectAll(request.payload); + break; + } + } + }); + } + /** + * Sends a message to the child process in order to emit a registered given event + * @param {string} event The event to be emitted in the child process + * @returns {Promise} + */ + communicate(event) { + return new Promise((resolve, reject) => { + const { identifier } = BotCommunication_1.BotCommunication; + const listener = ({ payload: { data }, identifier: responseIdentifier, }) => { + // Check if the received message is indeed identified by our identifier + if (identifier === responseIdentifier) { + // Resolve with the message data + resolve(data); + this.process.removeListener('message', listener); + } + }; + this.process.on('message', listener); + const request = { + action: "emitCommunicationEvent" /* EmitCommunicationEvent */, + payload: { event }, + identifier, + }; + this.process.send(request, err => { + if (err) + reject(err); + }); + }); + } + /** + * Sends the child process a message to emit the given event to {@link EventsHandler} + * @param {E} event The event to be emitted + * @param {Array} args The arguments of the events + */ + emitEvent(event, args) { + const request = { + action: "emitBotEvent" /* EmitBotEvent */, + payload: { + event, + args, + }, + identifier: Date.now(), + }; + this.process.send(request); + } + /** + * Disconnects this shard + * @param {GatewayCloseCode} code The shard close code + */ + disconnect(code) { + const request = { + action: "emitDisconnect" /* EmitDisconnect */, + payload: code, + identifier: Date.now(), + }; + this.process.send(request); + } +} +exports.BotShard = BotShard; diff --git a/lib/sharding/BotShardManager.d.ts b/lib/sharding/BotShardManager.d.ts new file mode 100644 index 000000000..119c4a0f1 --- /dev/null +++ b/lib/sharding/BotShardManager.d.ts @@ -0,0 +1,53 @@ +/// +import { Serializable } from 'child_process'; +import { Arguments } from 'typed-emitter'; +import { BotShardState } from './BotShard'; +import { Events } from '../bot/handlers/events/events'; +import { GatewayCloseCode } from '../socket'; +import { ShardId } from '../types'; +/** + * Creates and manages all bot shards + */ +export declare class BotShardManager { + private readonly token; + private readonly shards; + readonly file: string; + readonly shardsAmount: number; + constructor(file: string, token: string, shardsAmount: number); + /** + * Starts the shards and stores them inside a {@link Collection} + * @returns {Promise} + */ + start(): Promise; + /** + * Emits an event on all shards initiated with this manager + * @param {string} event The event to be emitted + * @returns {Promise} + */ + broadcast(event: string): Promise; + /** + * Emits a given event on all shards under this manager + * @param {E} event The event to emit + * @param {Array} args The arguments of the event + */ + emitEvent(event: E, args: Arguments): void; + /** + * Emits an event on a specific shard. + * Returns undefined if no shard matching the given ID was found + * @param {string} event The event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise | null} + */ + send(event: string, shardId: ShardId): Promise | undefined; + /** + * Checks if all shards under this manager match the given state + * @param {BotShardState} state The state to check if all shards match + * @returns {boolean} Whether all shards match that state + */ + checkShardsState(state: BotShardState): boolean; + /** + * Disconnects all active shards under this manager + * @param {GatewayCloseCode} code The shards' close code + */ + disconnectAll(code: GatewayCloseCode): void; +} diff --git a/lib/sharding/BotShardManager.js b/lib/sharding/BotShardManager.js new file mode 100644 index 000000000..0f4ca8db6 --- /dev/null +++ b/lib/sharding/BotShardManager.js @@ -0,0 +1,96 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotShardManager = void 0; +const BotShard_1 = require("./BotShard"); +const Collection_1 = __importDefault(require("../Collection")); +/** + * Creates and manages all bot shards + */ +class BotShardManager { + constructor(file, token, shardsAmount) { + this.file = file; + this.token = token; + this.shardsAmount = shardsAmount; + this.shards = new Collection_1.default(); + } + /** + * Starts the shards and stores them inside a {@link Collection} + * @returns {Promise} + */ + start() { + return __awaiter(this, void 0, void 0, function* () { + for (let i = 0; i < this.shardsAmount; i++) { + const shard = new BotShard_1.BotShard(this, i); + this.shards.set(shard.id, shard); + } + for (const [, shard] of this.shards) { + shard.spawn(); + } + }); + } + /** + * Emits an event on all shards initiated with this manager + * @param {string} event The event to be emitted + * @returns {Promise} + */ + broadcast(event) { + const results = []; + for (const [, shard] of this.shards) { + results.push(shard.communicate(event)); + } + return Promise.all(results); + } + /** + * Emits a given event on all shards under this manager + * @param {E} event The event to emit + * @param {Array} args The arguments of the event + */ + emitEvent(event, args) { + for (const [, shard] of this.shards) { + shard.emitEvent(event, args); + } + } + /** + * Emits an event on a specific shard. + * Returns undefined if no shard matching the given ID was found + * @param {string} event The event to be emitted + * @param {ShardId} shardId The ID of the shard where this event should be emitted + * @returns {Promise | null} + */ + send(event, shardId) { + const shard = this.shards.get(shardId); + if (!shard) + return undefined; + return shard.communicate(event); + } + /** + * Checks if all shards under this manager match the given state + * @param {BotShardState} state The state to check if all shards match + * @returns {boolean} Whether all shards match that state + */ + checkShardsState(state) { + return this.shards.toArray.every(value => value.state === state); + } + /** + * Disconnects all active shards under this manager + * @param {GatewayCloseCode} code The shards' close code + */ + disconnectAll(code) { + for (const [, shard] of this.shards) { + shard.disconnect(code); + } + } +} +exports.BotShardManager = BotShardManager; diff --git a/lib/sharding/index.d.ts b/lib/sharding/index.d.ts new file mode 100644 index 000000000..82e7f7e7d --- /dev/null +++ b/lib/sharding/index.d.ts @@ -0,0 +1,3 @@ +export * from './BotCommunication'; +export * from './BotShard'; +export * from './BotShardManager'; diff --git a/lib/sharding/index.js b/lib/sharding/index.js new file mode 100644 index 000000000..04885ac38 --- /dev/null +++ b/lib/sharding/index.js @@ -0,0 +1,15 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BotCommunication"), exports); +__exportStar(require("./BotShard"), exports); +__exportStar(require("./BotShardManager"), exports); diff --git a/lib/socket/BotHeartbeats.d.ts b/lib/socket/BotHeartbeats.d.ts new file mode 100644 index 000000000..4d02c57b1 --- /dev/null +++ b/lib/socket/BotHeartbeats.d.ts @@ -0,0 +1,43 @@ +/// +import { BotSocketShard } from './BotSocketShard'; +interface HeartbeatInterval { + timeout: number; + executor?: NodeJS.Timeout; +} +/** + * Handles the sending and receiving of Discord heartbeats + */ +export declare class BotHeartbeats { + private botSocketShard; + private readonly ws; + private readonly sequence; + private acked; + interval: HeartbeatInterval; + constructor(botSocket: BotSocketShard); + /** + * Starts the heartbeat interval + */ + start(): void; + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat(): void; + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat(): void; + /** + * Called when acking failed. Closes the socket and tries to reconnect + */ + private ackFailed; + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + private get heartbeatData(); + /** + * Called when a heartbeat is acked + */ + receivedAck(): void; +} +export {}; diff --git a/lib/socket/BotHeartbeats.js b/lib/socket/BotHeartbeats.js new file mode 100644 index 000000000..f7156024b --- /dev/null +++ b/lib/socket/BotHeartbeats.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotHeartbeats = void 0; +/** + * Handles the sending and receiving of Discord heartbeats + */ +class BotHeartbeats { + constructor(botSocket) { + this.botSocketShard = botSocket; + this.ws = botSocket.ws; + this.sequence = botSocket.sequence; + this.acked = true; + this.interval = { + timeout: 0, + }; + } + /** + * Starts the heartbeat interval + */ + start() { + this.interval.executor = setInterval(this.sendHeartbeat.bind(this), this.interval.timeout); + } + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat() { + if (!this.acked) { + this.ackFailed(); + return; + } + this.acked = false; + this.ws.send(this.botSocketShard.pack(this.heartbeatData), err => { + if (err) + throw err; + }); + } + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat() { + this.interval.timeout = -1; + if (this.interval.executor) { + clearInterval(this.interval.executor); + } + } + /** + * Called when acking failed. Closes the socket and tries to reconnect + */ + ackFailed() { + this.botSocketShard.close(); + } + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + get heartbeatData() { + return { op: 1 /* Heartbeat */, d: this.sequence || -1 }; + } + /** + * Called when a heartbeat is acked + */ + receivedAck() { + this.acked = true; + } +} +exports.BotHeartbeats = BotHeartbeats; diff --git a/lib/socket/BotSocket.d.ts b/lib/socket/BotSocket.d.ts new file mode 100644 index 000000000..2ce1a2f9f --- /dev/null +++ b/lib/socket/BotSocket.d.ts @@ -0,0 +1,69 @@ +import { BotSocketShard, BotSocketShardState } from './BotSocketShard'; +import { GatewayCloseCode } from './constants'; +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { BotStateEvents } from '../bot/handlers/events/events'; +import { BotShardState } from '../sharding'; +import { GatewayStruct } from '../structures'; +import { ShardId } from '../types'; +export interface SessionStartLimit { + total: number; + remaining: number; + reset_after: number; +} +/** + * Creates and manages socket shards + */ +export declare class BotSocket { + private readonly token; + readonly shards: Collection; + readonly bot: Bot; + gatewayURL: string; + sessionStartLimit: SessionStartLimit; + constructor(bot: Bot, token: string); + /** + * Start and connect every bot shard + * @param {number} [timeout=5500] Time in milliseconds to wait before establishing a new shard + * @returns {Promise} + */ + startShards(timeout?: number): Promise; + /** + * Stops and disconnects all active shards started by this process + * @param {GatewayCloseCode} code Gateway closure code + */ + stopShards(code: GatewayCloseCode): void; + /** + * Checks if all shards under this socket match a given state + * @param {BotSocketShardState} state The state to be checked for + * @returns {boolean} + */ + checkShardsState(state: BotSocketShardState): boolean; + /** + * Called when a shard under this socket changes its state. + * If all shards under this socket now have the same state, a message will be sent to the {@link BotShardManager} + * telling it to emit an event for all shards + * @param {BotSocketShardState} state The state to be checked for + * @param {BotShardState} shardState The state {@link BotShard} should be at after sending the message + * @param {BotStateEvents} botEvent The event that should be emitted to all shards + * @example ```typescript + * this.botSocket.shardChangedState( + * BotSocketShardState.Ready, + * BotShardState.Ready, + * BotEvents.Ready, + * ); + * ``` + */ + shardChangedState(state: BotSocketShardState, shardState: BotShardState, botEvent: BotStateEvents): void; + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @param {number} [shardId] The shard id thats gonna be affected + * @returns {void} + */ + modifyPresence(presence: GatewayStruct, shardId?: number): void; + /** + * Sends a request to the gateway in order to receive the connection information + * @returns {Promise} + */ + private get gateway(); +} diff --git a/lib/socket/BotSocket.js b/lib/socket/BotSocket.js new file mode 100644 index 000000000..01e2bbba8 --- /dev/null +++ b/lib/socket/BotSocket.js @@ -0,0 +1,140 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotSocket = void 0; +const node_fetch_1 = __importDefault(require("node-fetch")); +const BotSocketShard_1 = require("./BotSocketShard"); +const constants_1 = require("./constants"); +const Collection_1 = __importDefault(require("../Collection")); +const api_1 = require("../api"); +/** + * Creates and manages socket shards + */ +class BotSocket { + constructor(bot, token) { + this.bot = bot; + this.token = token; + this.shards = new Collection_1.default(); + } + /** + * Start and connect every bot shard + * @param {number} [timeout=5500] Time in milliseconds to wait before establishing a new shard + * @returns {Promise} + */ + startShards(timeout = constants_1.recommendedShardTimeout) { + return __awaiter(this, void 0, void 0, function* () { + const { url: gatewayURL, shards: suggestedShards, session_start_limit: sessionStartLimit, } = yield this.gateway; + this.gatewayURL = gatewayURL; + this.sessionStartLimit = sessionStartLimit; + const { id, amount = suggestedShards } = this.bot.shardOptions; + const shards = id !== undefined ? [id] : Array.from({ length: amount }).map((_, i) => i); + for (const shardId of shards) { + const botShard = new BotSocketShard_1.BotSocketShard(this, this.token, { + amount, + id: shardId, + }); + botShard.configure(); + this.shards.set(shardId, botShard); + botShard.connect(); + // eslint-disable-next-line no-await-in-loop + yield new Promise(resolve => setTimeout(resolve, timeout)); + } + }); + } + /** + * Stops and disconnects all active shards started by this process + * @param {GatewayCloseCode} code Gateway closure code + */ + stopShards(code) { + for (const [, shard] of this.shards) { + shard.close(code); + } + } + /** + * Checks if all shards under this socket match a given state + * @param {BotSocketShardState} state The state to be checked for + * @returns {boolean} + */ + checkShardsState(state) { + return this.shards.toArray.every(value => value.state === state); + } + /** + * Called when a shard under this socket changes its state. + * If all shards under this socket now have the same state, a message will be sent to the {@link BotShardManager} + * telling it to emit an event for all shards + * @param {BotSocketShardState} state The state to be checked for + * @param {BotShardState} shardState The state {@link BotShard} should be at after sending the message + * @param {BotStateEvents} botEvent The event that should be emitted to all shards + * @example ```typescript + * this.botSocket.shardChangedState( + * BotSocketShardState.Ready, + * BotShardState.Ready, + * BotEvents.Ready, + * ); + * ``` + */ + shardChangedState(state, shardState, botEvent) { + if (!this.checkShardsState(state)) + return; + if (process.send) { + const request = { + action: "shardChangedState" /* ShardChangedState */, + payload: { + state: shardState, + botEvent, + }, + identifier: Date.now(), + }; + process.send(request); + } + else { + this.bot.events.emit(botEvent); + } + } + /** + * Modifies the presence of the bot + * @param {GatewayStruct} presence The new presence for the bot + * @param {number} [shardId] The shard id thats gonna be affected + * @returns {void} + */ + modifyPresence(presence, shardId) { + if (shardId) { + const shard = this.shards.get(shardId); + if (!shard) + return; + shard.send({ + op: 3 /* PresenceUpdate */, + d: presence, + }); + } + else { + for (const [, shard] of this.shards) { + shard.send({ + op: 3 /* PresenceUpdate */, + d: presence, + }); + } + } + } + /** + * Sends a request to the gateway in order to receive the connection information + * @returns {Promise} + */ + get gateway() { + return node_fetch_1.default(`${api_1.baseURL}/gateway/bot`, { + headers: { Authorization: `Bot ${this.token}` }, + }).then(res => res.json()); + } +} +exports.BotSocket = BotSocket; diff --git a/lib/socket/BotSocketShard.d.ts b/lib/socket/BotSocketShard.d.ts new file mode 100644 index 000000000..f7eab1cf5 --- /dev/null +++ b/lib/socket/BotSocketShard.d.ts @@ -0,0 +1,186 @@ +/// +import WebSocket from 'ws'; +import { BotSocket } from './BotSocket'; +import { GatewayCloseCode, GatewayEvent, OPCode } from './constants'; +import { Bot, ShardOptions } from '../bot'; +import { Snowflake } from '../types'; +/** + * The state of the socket shard + */ +export declare const enum BotSocketShardState { + Connecting = 0, + Processing = 1, + Ready = 2, + Closed = 3, + Terminated = 4 +} +export declare type PayloadData = any; +/** + * A payload thats gonna be sent to the Discord API + */ +export interface GatewayCommand { + op: OPCode; + d: PayloadData; +} +/** + * A payload received from the Discord API gateway + */ +export interface Payload extends GatewayCommand { + s: number; + t: GatewayEvent; +} +export declare type EventHandlers = Record void | Promise>; +/** + * Connects every bot shard to a {@link WebSocket} with the Discord Gateway. + * Handles gateway events and messages + */ +export declare class BotSocketShard { + /** + * The bot socket connection that initialized this shard + */ + private readonly botSocket; + /** + * The bot instance associated to this shard + */ + private readonly bot; + /** + * The token of the bot associated to this shard + */ + private readonly token; + /** + * Responsible for sending heartbeat payloads representing this shard + */ + private heartbeats; + /** + * Holds shard details + */ + readonly shard: ShardOptions; + /** + * Timeout before retrying to create a new connection after disconnection + * Resets on successful connection + */ + private retryTimeout; + /** + * All guilds pending to be cached from the gateway READY event + */ + pendingGuilds: Set; + /** + * This shard's state + */ + state: BotSocketShardState; + /** + * The WebSocket connection associated to this shard + */ + ws: WebSocket; + /** + * The session ID of this shard + */ + sessionId: string; + /** + * The sequence number of this shard + */ + sequence: number | null; + /** + * The number of the last sequence of this shard. Used to resume the connection from the last sequence in case of connection loss + */ + private lastSequence; + /** + * Holds options for the websocket connection + */ + private options; + /** + * The Inflate context used to compress incoming payloads + */ + private zlib; + constructor(botSocket: BotSocket, token: string, shard: ShardOptions); + /** + * Loads optional libraries and sets the options for the gateway websocket initialization + * @returns {void} + */ + configure(): void; + /** + * Connects to the Discord Gateway and resumes a previous connection if needed + * @param {boolean} resume Whether to resume a previous connection + * @returns {Promise} + */ + connect(resume?: boolean): Promise; + /** + * Decompresses data from the WebSocket if the compress option is sent to the gateway + * @param {Buffer} data The message received from the gateway + * @returns {Buffer | undefined} + */ + private decompressData; + /** + * Uses the right decoding and decompression to retrieve the payload object from the gateway message + * @param {WebSocket.Data} messageData The data of the message received from the gateway + * @returns {Payload | undefined} + */ + private retrievePayload; + /** + * Called when a new message is received from the gateway + * @param {Data} message WebSocket message event + * @returns {void} + */ + private onMessage; + /** + * Sends a new identify request to the gateway. + * Will use shards if needed + */ + private identify; + /** + * Calls the matching dispatch event from {@link events} + * @param {Payload} payload Dispatch payload + */ + private handleDispatch; + /** + * Close the connection between the bot and the gateway + * @param {GatewayCloseCode} code Socket close code https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + */ + close(code?: GatewayCloseCode): void; + /** + * Called when the bot is fully ready to proceed, after collecting + * all guilds from GUILD_CREATE events + */ + ready(): void; + /** + * Called when the close event from the {@link WebSocket} is emitted + * @param {number} code WebSocket closure code + * @param {string} reason The reason for the WebSocket closure + */ + private onClose; + /** + * Sends a request to the gateway in order to resume from the last connection + */ + private resume; + /** + * Waits if no further connections can be made at the time + * @param {SessionStartLimit} sessionLimit Session start limit object received + * from connecting to the gateway + * @returns {Promise} + */ + private handleSessionLimit; + /** + * Packs and sends the data to the WebSocket + * @param {unknown} data The data + * @returns {void} + */ + send(data: GatewayCommand): void; + /** + * Transfers the received data from the gateway into a {@link Payload} object + * @param {string} data Data received from the gateway + * @returns {Payload} + */ + private static parse; + /** + * Transfers the data into format which can be sent across the gateway + * @param {any} data Data to be transferred and sent to the gateway + * @returns {string} + */ + pack(data: GatewayCommand): Buffer | string | undefined; + /** + * Get the fully modified Socket URL to use when connecting to the gateway + * @param {string} url Socket URL + * @returns {string} + */ + private socketURL; +} diff --git a/lib/socket/BotSocketShard.js b/lib/socket/BotSocketShard.js new file mode 100644 index 000000000..965817291 --- /dev/null +++ b/lib/socket/BotSocketShard.js @@ -0,0 +1,339 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotSocketShard = void 0; +const querystring_1 = __importDefault(require("querystring")); +const ws_1 = __importDefault(require("ws")); +const BotHeartbeats_1 = require("./BotHeartbeats"); +const constants_1 = require("./constants"); +const events = __importStar(require("./handlers")); +const properties_1 = require("./properties"); +const api_1 = require("../api"); +const sharding_1 = require("../sharding"); +// Initializes variables for optional libraries +let erlpack; +let zlib; +/** + * Connects every bot shard to a {@link WebSocket} with the Discord Gateway. + * Handles gateway events and messages + */ +class BotSocketShard { + constructor(botSocket, token, shard) { + this.botSocket = botSocket; + this.bot = botSocket.bot; + this.token = token; + this.shard = shard; + this.retryTimeout = 0; + this.pendingGuilds = new Set(); + this.state = 3 /* Closed */; + this.sequence = null; + this.lastSequence = null; + } + /** + * Loads optional libraries and sets the options for the gateway websocket initialization + * @returns {void} + */ + configure() { + try { + erlpack = require('erlpack'); + } + catch (err) { + // Use json encoding + } + try { + zlib = require('zlib-sync'); + // Create new Inflate context + this.zlib = new zlib.Inflate({ + chunkSize: 128 * 1024, + windowBits: 32, + }); + } + catch (err) { + // Do not use data compressing + } + this.options = Object.assign({ v: api_1.version, encoding: erlpack ? 'etf' : 'json', compress: zlib && 'zlib-stream' }, this.bot.options.websocket); + this.bot.debug(this.options, 'options'); + } + /** + * Connects to the Discord Gateway and resumes a previous connection if needed + * @param {boolean} resume Whether to resume a previous connection + * @returns {Promise} + */ + connect(resume = false) { + return __awaiter(this, void 0, void 0, function* () { + this.bot.debug('Connecting...'); + if (this.state === 0 /* Connecting */) + return; + this.state = 0 /* Connecting */; + const { gatewayURL, sessionStartLimit } = this.botSocket; + const socketURL = this.socketURL(gatewayURL); + yield this.handleSessionLimit(sessionStartLimit); + this.ws = new ws_1.default(socketURL); + this.ws.on('message', this.onMessage.bind(this)); + this.ws.on('close', this.onClose.bind(this)); + this.heartbeats = new BotHeartbeats_1.BotHeartbeats(this); + this.botSocket.sessionStartLimit.remaining--; + if (resume && this.lastSequence) { + this.ws.on('open', this.resume.bind(this)); + } + }); + } + /** + * Decompresses data from the WebSocket if the compress option is sent to the gateway + * @param {Buffer} data The message received from the gateway + * @returns {Buffer | undefined} + */ + decompressData(data) { + if (!this.zlib || !zlib) + return; + if (data.length < 4 || data.readUInt32BE(data.length - 4) !== 0xffff) { + this.zlib.push(data, false); + return; + } + this.zlib.push(data, zlib.Z_SYNC_FLUSH); + if (this.zlib.err) { + this.bot.debug(`Zlib error: ${this.zlib.err} - ${this.zlib.msg}`); + return; + } + return Buffer.from(this.zlib.result); + } + /** + * Uses the right decoding and decompression to retrieve the payload object from the gateway message + * @param {WebSocket.Data} messageData The data of the message received from the gateway + * @returns {Payload | undefined} + */ + retrievePayload(messageData) { + let data; + if (messageData instanceof ArrayBuffer) { + // eslint-disable-next-line no-param-reassign + messageData = new Uint8Array(messageData); + } + if (messageData instanceof Buffer) { + /* + Payloads are served inside Buffer when: + 1. The ETF encoding is used + 2. Compression is used + + Decompress the message, and store it in the right format + */ + const decompressed = this.options.compress === 'zlib-stream' ? this.decompressData(messageData) : messageData; + if (!decompressed) + return; + data = this.options.encoding === 'etf' ? decompressed : decompressed.toString(); + } + else if (typeof messageData === 'string') { + // Payloads are served inside a string when the JSON encoding is used without compression + data = messageData; + } + if (!data) + return; + return BotSocketShard.parse(data); + } + /** + * Called when a new message is received from the gateway + * @param {Data} message WebSocket message event + * @returns {void} + */ + onMessage(message) { + const payload = this.retrievePayload(message); + if (!payload) + return; + const { op, t, d, s } = payload; + this.bot.debug(op, t, 'op - t'); + switch (op) { + case 0 /* Dispatch */: + this.sequence = s; + this.handleDispatch(payload); + break; + case 7 /* Reconnect */: + this.close(4000 /* UnknownError */); + break; + case 9 /* InvalidSession */: + // Wait 5 seconds and re-identify + setTimeout(this.identify.bind(this), 5000); + break; + case 10 /* Hello */: + this.heartbeats.interval.timeout = d.heartbeat_interval; + this.heartbeats.start(); + this.identify(); + this.state = 1 /* Processing */; + break; + case 11 /* HeartbeatACK */: + this.heartbeats.receivedAck(); + break; + } + } + /** + * Sends a new identify request to the gateway. + * Will use shards if needed + */ + identify() { + const { id, amount } = this.shard; + this.send({ + op: 2 /* Identify */, + d: Object.assign(Object.assign({}, properties_1.identify), { token: this.token, shard: [id, amount] }), + }); + } + /** + * Calls the matching dispatch event from {@link events} + * @param {Payload} payload Dispatch payload + */ + handleDispatch(payload) { + const { t } = payload; + // Set session ID in case of a Ready event + if (t === "READY" /* Ready */) { + this.sessionId = payload.d.session_id; + } + // Find the matching event and run it + const event = events[t]; + if (event) { + event(payload, this.bot, this); + } + } + /** + * Close the connection between the bot and the gateway + * @param {GatewayCloseCode} code Socket close code https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + */ + close(code = 1000 /* NormalClosure */) { + this.bot.debug('Closing connection!'); + this.state = 4 /* Terminated */; + // Stop sending heartbeats + this.heartbeats.stopHeartbeat(); + this.lastSequence = this.sequence; + // Close socket + this.ws.close(code); + } + /** + * Called when the bot is fully ready to proceed, after collecting + * all guilds from GUILD_CREATE events + */ + ready() { + this.state = 2 /* Ready */; + this.retryTimeout = 0; + this.bot.debug('Ready!', this.bot.guilds.cache.map(i => i.name)); + this.bot.events.emit(constants_1.BotEvent.ShardReady, this); + this.botSocket.shardChangedState(2 /* Ready */, sharding_1.BotShardState.Ready, constants_1.BotEvent.Ready); + } + /** + * Called when the close event from the {@link WebSocket} is emitted + * @param {number} code WebSocket closure code + * @param {string} reason The reason for the WebSocket closure + */ + onClose(code, reason) { + return __awaiter(this, void 0, void 0, function* () { + this.bot.debug('Close', code, reason); + this.state = 3 /* Closed */; + // Emit the 'ShardClose' event to the Bot + this.bot.events.emit(constants_1.BotEvent.ShardClose, this); + // Tell the BotSocket that the shard has been closed + this.botSocket.shardChangedState(3 /* Closed */, sharding_1.BotShardState.Closed, constants_1.BotEvent.Close); + this.heartbeats.stopHeartbeat(); + if (!constants_1.UnreconnectableGatewayCloseCodes.includes(code)) { + if (this.retryTimeout) { + yield new Promise(resolve => setTimeout(resolve, this.retryTimeout)); + } + this.retryTimeout += 1000; + yield this.connect(!constants_1.UnresumeableGatewayCloseCodes.includes(code)); + } + }); + } + /** + * Sends a request to the gateway in order to resume from the last connection + */ + resume() { + this.send({ + op: 6 /* Resume */, + d: { + token: this.token, + session_id: this.sessionId, + seq: this.lastSequence || this.sequence, + }, + }); + } + /** + * Waits if no further connections can be made at the time + * @param {SessionStartLimit} sessionLimit Session start limit object received + * from connecting to the gateway + * @returns {Promise} + */ + handleSessionLimit(sessionLimit) { + return __awaiter(this, void 0, void 0, function* () { + const { remaining, reset_after: resetAfter } = sessionLimit; + this.bot.debug(remaining, resetAfter, 'Handle session limit'); + if (remaining === 0) { + console.error(`Maximum number of daily Discord API connections exceeded! You will have to wait ${resetAfter}ms before attempting a new connection`); + yield new Promise(resolve => setTimeout(resolve, resetAfter)); + } + }); + } + /** + * Packs and sends the data to the WebSocket + * @param {unknown} data The data + * @returns {void} + */ + send(data) { + return this.ws.send(this.pack(data)); + } + /** + * Transfers the received data from the gateway into a {@link Payload} object + * @param {string} data Data received from the gateway + * @returns {Payload} + */ + static parse(data) { + if (data instanceof Buffer) { + if (!erlpack) + return; + return erlpack.unpack(data); + } + else { + return JSON.parse(data); + } + } + /** + * Transfers the data into format which can be sent across the gateway + * @param {any} data Data to be transferred and sent to the gateway + * @returns {string} + */ + pack(data) { + return this.options.encoding === 'etf' ? erlpack === null || erlpack === void 0 ? void 0 : erlpack.pack(data) : JSON.stringify(data); + } + /** + * Get the fully modified Socket URL to use when connecting to the gateway + * @param {string} url Socket URL + * @returns {string} + */ + socketURL(url) { + return `${url}/?${querystring_1.default.stringify(this.options)}`; + } +} +exports.BotSocketShard = BotSocketShard; diff --git a/lib/socket/constants.d.ts b/lib/socket/constants.d.ts new file mode 100644 index 000000000..de3c7590c --- /dev/null +++ b/lib/socket/constants.d.ts @@ -0,0 +1,129 @@ +/** + * https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes + */ +export declare const enum OPCode { + Dispatch = 0, + Heartbeat = 1, + Identify = 2, + PresenceUpdate = 3, + VoiceStateUpdate = 4, + Resume = 6, + Reconnect = 7, + RequestGuildMembers = 8, + InvalidSession = 9, + Hello = 10, + HeartbeatACK = 11 +} +/** + * https://discordapp.com/developers/docs/topics/gateway#commands-and-events-gateway-events + */ +export declare const enum GatewayEvent { + Ready = "READY", + Close = "CLOSE", + Resumed = "RESUMED", + Reconnect = "RECONNECT", + InvalidSession = "INVALID_SESSION", + ChannelCreate = "CHANNEL_CREATE", + ChannelUpdate = "CHANNEL_UPDATE", + ChannelDelete = "CHANNEL_DELETE", + ChannelPinsUpdate = "CHANNEL_PINS_UPDATE", + GuildCreate = "GUILD_CREATE", + GuildUpdate = "GUILD_UPDATE", + GuildDelete = "GUILD_DELETE", + GuildBanAdd = "GUILD_BAN_ADD", + GuildBanRemove = "GUILD_BAN_REMOVE", + GuildEmojisUpdate = "GUILD_EMOJIS_UPDATE", + GuildIntegrationsUpdate = "GUILD_INTEGRATIONS_UPDATE", + GuildMemberAdd = "GUILD_MEMBER_ADD", + GuildMemberRemove = "GUILD_MEMBER_REMOVE", + GuildMemberUpdate = "GUILD_MEMBER_UPDATE", + GuildMembersChunk = "GUILD_MEMBERS_CHUNK", + GuildRoleCreate = "GUILD_ROLE_CREATE", + GuildRoleUpdate = "GUILD_ROLE_UPDATE", + GuildRoleDelete = "GUILD_ROLE_DELETE", + InviteCreate = "INVITE_CREATE", + InviteDelete = "INVITE_DELETE", + MessageCreate = "MESSAGE_CREATE", + MessageUpdate = "MESSAGE_UPDATE", + MessageDelete = "MESSAGE_DELETE", + MessageDeleteBulk = "MESSAGE_DELETE_BULK", + MessageReactionAdd = "MESSAGE_REACTION_ADD", + MessageReactionRemove = "MESSAGE_REACTION_REMOVE", + MessageReactionRemoveAll = "MESSAGE_REACTION_REMOVE_ALL", + MessageReactionRemoveEmoji = "MESSAGE_REACTION_REMOVE_EMOJI", + PresenceUpdate = "PRESENCE_UPDATE", + TypingStart = "TYPING_START", + UserUpdate = "USER_UPDATE", + VoiceStateUpdate = "VOICE_STATE_UPDATE", + VoiceServerUpdate = "VOICE_SERVER_UPDATE", + WebhooksUpdate = "WEBHOOKS_UPDATE" +} +/** + * All Bot events + */ +export declare enum BotEvent { + Ready = "READY", + Close = "CLOSE", + Debug = "DEBUG", + ChannelCreate = "CHANNEL_CREATE", + ChannelUpdate = "CHANNEL_UPDATE", + ChannelDelete = "CHANNEL_DELETE", + ChannelPinsUpdate = "CHANNEL_PINS_UPDATE", + GuildCreate = "GUILD_CREATE", + GuildUpdate = "GUILD_UPDATE", + GuildDelete = "GUILD_DELETE", + GuildBanAdd = "GUILD_BAN_ADD", + GuildBanRemove = "GUILD_BAN_REMOVE", + GuildEmojisUpdate = "GUILD_EMOJIS_UPDATE", + GuildIntegrationsUpdate = "GUILD_INTEGRATIONS_UPDATE", + GuildMemberAdd = "GUILD_MEMBER_ADD", + GuildMemberRemove = "GUILD_MEMBER_REMOVE", + GuildMemberUpdate = "GUILD_MEMBER_UPDATE", + GuildMembersChunk = "GUILD_MEMBERS_CHUNK", + GuildMembersChunkFinish = "GUILD_MEMBERS_CHUNK_FINISH", + GuildRoleCreate = "GUILD_ROLE_CREATE", + GuildRoleUpdate = "GUILD_ROLE_UPDATE", + GuildRoleDelete = "GUILD_ROLE_DELETE", + InviteCreate = "INVITE_CREATE", + InviteDelete = "INVITE_DELETE", + MessageCreate = "MESSAGE_CREATE", + MessageUpdate = "MESSAGE_UPDATE", + MessageDelete = "MESSAGE_DELETE", + MessageDeleteBulk = "MESSAGE_DELETE_BULK", + MessageReactionAdd = "MESSAGE_REACTION_ADD", + MessageReactionRemove = "MESSAGE_REACTION_REMOVE", + MessageReactionRemoveAll = "MESSAGE_REACTION_REMOVE_ALL", + MessageReactionRemoveEmoji = "MESSAGE_REACTION_REMOVE_EMOJI", + PresenceUpdate = "PRESENCE_UPDATE", + ShardReady = "SHARD_READY", + ShardClose = "SHARD_CLOSE", + TypingStart = "TYPING_START", + UserUpdate = "USER_UPDATE", + VoiceStateUpdate = "VOICE_STATE_UPDATE", + VoiceServerUpdate = "VOICE_SERVER_UPDATE", + WebhooksUpdate = "WEBHOOKS_UPDATE" +} +/** + * https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + */ +export declare const enum GatewayCloseCode { + NormalClosure = 1000, + ManualClosure = 3000, + UnknownError = 4000, + UnknownOpcode = 4001, + DecodeError = 4002, + NotAuthenticated = 4003, + AuthenticationFailed = 4004, + AlreadyAuthenticated = 4005, + InvalidSeq = 4007, + RateLimited = 4008, + SessionTimedOut = 4009, + InvalidShard = 4010, + ShardingRequired = 4011, + InvalidAPIVersion = 4012, + InvalidIntent = 4013, + DisallowedIntent = 4014 +} +export declare const UnreconnectableGatewayCloseCodes: GatewayCloseCode[]; +export declare const UnresumeableGatewayCloseCodes: GatewayCloseCode[]; +export declare const recommendedShardTimeout = 5500; diff --git a/lib/socket/constants.js b/lib/socket/constants.js new file mode 100644 index 000000000..4b57435d4 --- /dev/null +++ b/lib/socket/constants.js @@ -0,0 +1,65 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.recommendedShardTimeout = exports.UnresumeableGatewayCloseCodes = exports.UnreconnectableGatewayCloseCodes = exports.BotEvent = void 0; +/** + * All Bot events + */ +var BotEvent; +(function (BotEvent) { + BotEvent["Ready"] = "READY"; + BotEvent["Close"] = "CLOSE"; + BotEvent["Debug"] = "DEBUG"; + /*Resumed = 'RESUMED', + Reconnect = 'RECONNECT', + InvalidSession = 'INVALID_SESSION',*/ + BotEvent["ChannelCreate"] = "CHANNEL_CREATE"; + BotEvent["ChannelUpdate"] = "CHANNEL_UPDATE"; + BotEvent["ChannelDelete"] = "CHANNEL_DELETE"; + BotEvent["ChannelPinsUpdate"] = "CHANNEL_PINS_UPDATE"; + BotEvent["GuildCreate"] = "GUILD_CREATE"; + BotEvent["GuildUpdate"] = "GUILD_UPDATE"; + BotEvent["GuildDelete"] = "GUILD_DELETE"; + BotEvent["GuildBanAdd"] = "GUILD_BAN_ADD"; + BotEvent["GuildBanRemove"] = "GUILD_BAN_REMOVE"; + BotEvent["GuildEmojisUpdate"] = "GUILD_EMOJIS_UPDATE"; + BotEvent["GuildIntegrationsUpdate"] = "GUILD_INTEGRATIONS_UPDATE"; + BotEvent["GuildMemberAdd"] = "GUILD_MEMBER_ADD"; + BotEvent["GuildMemberRemove"] = "GUILD_MEMBER_REMOVE"; + BotEvent["GuildMemberUpdate"] = "GUILD_MEMBER_UPDATE"; + BotEvent["GuildMembersChunk"] = "GUILD_MEMBERS_CHUNK"; + BotEvent["GuildMembersChunkFinish"] = "GUILD_MEMBERS_CHUNK_FINISH"; + BotEvent["GuildRoleCreate"] = "GUILD_ROLE_CREATE"; + BotEvent["GuildRoleUpdate"] = "GUILD_ROLE_UPDATE"; + BotEvent["GuildRoleDelete"] = "GUILD_ROLE_DELETE"; + BotEvent["InviteCreate"] = "INVITE_CREATE"; + BotEvent["InviteDelete"] = "INVITE_DELETE"; + BotEvent["MessageCreate"] = "MESSAGE_CREATE"; + BotEvent["MessageUpdate"] = "MESSAGE_UPDATE"; + BotEvent["MessageDelete"] = "MESSAGE_DELETE"; + BotEvent["MessageDeleteBulk"] = "MESSAGE_DELETE_BULK"; + BotEvent["MessageReactionAdd"] = "MESSAGE_REACTION_ADD"; + BotEvent["MessageReactionRemove"] = "MESSAGE_REACTION_REMOVE"; + BotEvent["MessageReactionRemoveAll"] = "MESSAGE_REACTION_REMOVE_ALL"; + BotEvent["MessageReactionRemoveEmoji"] = "MESSAGE_REACTION_REMOVE_EMOJI"; + BotEvent["PresenceUpdate"] = "PRESENCE_UPDATE"; + BotEvent["ShardReady"] = "SHARD_READY"; + BotEvent["ShardClose"] = "SHARD_CLOSE"; + BotEvent["TypingStart"] = "TYPING_START"; + BotEvent["UserUpdate"] = "USER_UPDATE"; + BotEvent["VoiceStateUpdate"] = "VOICE_STATE_UPDATE"; + BotEvent["VoiceServerUpdate"] = "VOICE_SERVER_UPDATE"; + BotEvent["WebhooksUpdate"] = "WEBHOOKS_UPDATE"; +})(BotEvent = exports.BotEvent || (exports.BotEvent = {})); +exports.UnreconnectableGatewayCloseCodes = [ + 3000 /* ManualClosure */, + 4004 /* AuthenticationFailed */, + 4010 /* InvalidShard */, + 4011 /* ShardingRequired */, + 4013 /* InvalidIntent */, + 4014 /* DisallowedIntent */, +]; +exports.UnresumeableGatewayCloseCodes = [ + 1000 /* NormalClosure */, + 4007 /* InvalidSeq */, +]; +exports.recommendedShardTimeout = 5500; diff --git a/lib/socket/handlers/channelCreate.d.ts b/lib/socket/handlers/channelCreate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelCreate.js b/lib/socket/handlers/channelCreate.js new file mode 100644 index 000000000..e4493b39a --- /dev/null +++ b/lib/socket/handlers/channelCreate.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils_1 = require("../../structures/channels/utils"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const channel = yield utils_1.ChannelUtils.create(bot, d); + utils_1.ChannelUtils.cache(bot, channel); + bot.events.emit(constants_1.BotEvent.ChannelCreate, channel); +}); diff --git a/lib/socket/handlers/channelDelete.d.ts b/lib/socket/handlers/channelDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelDelete.js b/lib/socket/handlers/channelDelete.js new file mode 100644 index 000000000..cc2fb6aeb --- /dev/null +++ b/lib/socket/handlers/channelDelete.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils_1 = require("../../structures/channels/utils"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const channel = yield utils_1.ChannelUtils.create(bot, d); + utils_1.ChannelUtils.delete(bot, channel); + bot.events.emit(constants_1.BotEvent.ChannelDelete, channel); +}); diff --git a/lib/socket/handlers/channelPinsUpdate.d.ts b/lib/socket/handlers/channelPinsUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelPinsUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelPinsUpdate.js b/lib/socket/handlers/channelPinsUpdate.js new file mode 100644 index 000000000..fab2c7278 --- /dev/null +++ b/lib/socket/handlers/channelPinsUpdate.js @@ -0,0 +1,20 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId, last_pin_timestamp: lastPinTimestamp } = d; + const channel = yield bot.channels.getText(channelId); + const oldPinTimestamp = channel.pins.lastPinTimestamp; + channel.pins.lastPinTimestamp = new structures_1.Timestamp(lastPinTimestamp); + bot.events.emit(constants_1.BotEvent.ChannelPinsUpdate, channel, oldPinTimestamp); +}); diff --git a/lib/socket/handlers/channelUpdate.d.ts b/lib/socket/handlers/channelUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/channelUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/channelUpdate.js b/lib/socket/handlers/channelUpdate.js new file mode 100644 index 000000000..94dbaf0cf --- /dev/null +++ b/lib/socket/handlers/channelUpdate.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id } = d; + const channel = yield bot.channels.get(id); + const { before, after } = channel.update(d); + bot.events.emit(constants_1.BotEvent.ChannelUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildBanAdd.d.ts b/lib/socket/handlers/guildBanAdd.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildBanAdd.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildBanAdd.js b/lib/socket/handlers/guildBanAdd.js new file mode 100644 index 000000000..9f4184701 --- /dev/null +++ b/lib/socket/handlers/guildBanAdd.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + const ban = new guild_1.GuildBan(bot, { user }, guild); + // Add the member to the guild's bans controller + guild.bans.cache.add(ban); + bot.events.emit(constants_1.BotEvent.GuildBanAdd, ban); +}); diff --git a/lib/socket/handlers/guildBanRemove.d.ts b/lib/socket/handlers/guildBanRemove.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildBanRemove.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildBanRemove.js b/lib/socket/handlers/guildBanRemove.js new file mode 100644 index 000000000..08633a264 --- /dev/null +++ b/lib/socket/handlers/guildBanRemove.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + // Retrieve the ban if cached + const ban = guild.bans.cache.has(user.id) ? guild.bans.cache.get(user.id) : undefined; + // Remove the member from the Guild's bans Collection + guild.bans.cache.delete(user.id); + bot.events.emit(constants_1.BotEvent.GuildBanRemove, ban); +}); diff --git a/lib/socket/handlers/guildCreate.d.ts b/lib/socket/handlers/guildCreate.d.ts new file mode 100644 index 000000000..bdb96e9fd --- /dev/null +++ b/lib/socket/handlers/guildCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { BotSocketShard, Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot, socket: BotSocketShard) => void; +export default _default; diff --git a/lib/socket/handlers/guildCreate.js b/lib/socket/handlers/guildCreate.js new file mode 100644 index 000000000..f66dd5db3 --- /dev/null +++ b/lib/socket/handlers/guildCreate.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot, socket) => { + const { session_id: sessionId } = d; + socket.sessionId = sessionId; + const guild = guild_1.Guild.create(bot, d, socket.shard.id); + if (guild instanceof guild_1.Guild) { + // Delete the guild from the unavailable guilds collection if exists + if (bot.unavailableGuilds.has(guild.id)) { + bot.unavailableGuilds.delete(guild.id); + } + bot.guilds.cache.add(guild); + } + else { + bot.unavailableGuilds.set(guild.id, guild); + } + // The socket is still processing incoming GuildCreate events + if (socket.state === 1 /* Processing */) { + // Remove this guild from the pending guilds cache + socket.pendingGuilds.delete(guild.id); + if (!socket.pendingGuilds.size) { + // Fire the ready event if no guilds are pending + socket.ready(); + } + } + else { + bot.events.emit(constants_1.BotEvent.GuildCreate, guild); + } +}; diff --git a/lib/socket/handlers/guildDelete.d.ts b/lib/socket/handlers/guildDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildDelete.js b/lib/socket/handlers/guildDelete.js new file mode 100644 index 000000000..ee58a7569 --- /dev/null +++ b/lib/socket/handlers/guildDelete.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { unavailable, id } = d; + const guild = unavailable ? new guild_1.GuildUnavailable(bot, d) : yield bot.guilds.get(id); + guild_1.Guild.delete(bot, guild); + bot.events.emit(constants_1.BotEvent.GuildDelete, guild); +}); diff --git a/lib/socket/handlers/guildEmojisUpdate.d.ts b/lib/socket/handlers/guildEmojisUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildEmojisUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildEmojisUpdate.js b/lib/socket/handlers/guildEmojisUpdate.js new file mode 100644 index 000000000..050cc1ec4 --- /dev/null +++ b/lib/socket/handlers/guildEmojisUpdate.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const controllers_1 = require("../../controllers"); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, emojis } = d; + const guild = yield bot.guilds.get(guildId); + const before = guild.emojis.cache; + const after = new controllers_1.ControllerCache(emojis.map((emoji) => [emoji.id, new guild_1.GuildEmoji(bot, emoji, guild)])); + guild.emojis.cache = after; + bot.emojis.merge(guild.emojis.cache); + bot.events.emit(constants_1.BotEvent.GuildEmojisUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildIntegrationsUpdate.d.ts b/lib/socket/handlers/guildIntegrationsUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildIntegrationsUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildIntegrationsUpdate.js b/lib/socket/handlers/guildIntegrationsUpdate.js new file mode 100644 index 000000000..738f656e2 --- /dev/null +++ b/lib/socket/handlers/guildIntegrationsUpdate.js @@ -0,0 +1,17 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + bot.events.emit(constants_1.BotEvent.GuildIntegrationsUpdate, guild); +}); diff --git a/lib/socket/handlers/guildMemberAdd.d.ts b/lib/socket/handlers/guildMemberAdd.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildMemberAdd.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMemberAdd.js b/lib/socket/handlers/guildMemberAdd.js new file mode 100644 index 000000000..8bb3a8a6e --- /dev/null +++ b/lib/socket/handlers/guildMemberAdd.js @@ -0,0 +1,25 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Member_1 = require("../../structures/member/Member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + const member = new Member_1.Member(bot, d, guild); + // Cache the member in the guild's members cache + guild.members.cache.add(member); + if (member.user) { + // Cache the user in the Bot's users cache + bot.users.cache.add(member.user); + } + bot.events.emit(constants_1.BotEvent.GuildMemberAdd, member); +}); diff --git a/lib/socket/handlers/guildMemberRemove.d.ts b/lib/socket/handlers/guildMemberRemove.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildMemberRemove.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMemberRemove.js b/lib/socket/handlers/guildMemberRemove.js new file mode 100644 index 000000000..01a85efa2 --- /dev/null +++ b/lib/socket/handlers/guildMemberRemove.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + const member = guild.members.cache.get(user.id) || new structures_1.User(bot, user); + // Remove the member from the guild's members cache + guild.members.cache.delete(member.id); + bot.events.emit(constants_1.BotEvent.GuildMemberRemove, member); +}); diff --git a/lib/socket/handlers/guildMemberUpdate.d.ts b/lib/socket/handlers/guildMemberUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildMemberUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMemberUpdate.js b/lib/socket/handlers/guildMemberUpdate.js new file mode 100644 index 000000000..95d7d3ac1 --- /dev/null +++ b/lib/socket/handlers/guildMemberUpdate.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, user } = d; + const guild = yield bot.guilds.get(guildId); + const member = yield guild.members.get(user.id); + const { before, after } = member.update(Object.assign({ nick: member.nick, joined_at: member.joinedAt.date }, d)); + bot.events.emit(constants_1.BotEvent.GuildMemberUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildMembersChunk.d.ts b/lib/socket/handlers/guildMembersChunk.d.ts new file mode 100644 index 000000000..73c42dbdf --- /dev/null +++ b/lib/socket/handlers/guildMembersChunk.d.ts @@ -0,0 +1,17 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +/** + * Contains information about the current Guild Members Chunk + */ +export interface GuildMembersChunk { + /** + * The chunk index in the expected chunks for this response + */ + index: number; + /** + * The total number of expected chunks for this response + */ + count: number; +} +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildMembersChunk.js b/lib/socket/handlers/guildMembersChunk.js new file mode 100644 index 000000000..6b6918205 --- /dev/null +++ b/lib/socket/handlers/guildMembersChunk.js @@ -0,0 +1,47 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const member_1 = require("../../structures/member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { not_found: notFound, guild_id: guildId, members, presences, nonce, chunk_index: chunkIndex, chunk_count: chunkCount, } = d; + if (notFound) { + throw new Error('An invalid ID was passed to the Guild Members request'); + } + const guild = yield bot.guilds.get(guildId); + // Add the new members to the guild's members cache + guild.members.cache.addMany(members.map((member) => new member_1.Member(bot, member, guild))); + // Assign the presence returned from the event + if (presences) { + for (const presence of presences) { + const { id } = presence.user; + const member = yield guild.members.get(id); + if (member.presence) { + // Re-initialize the member presence class with the updated presence + member.presence.init(presence); + } + else { + // Initialize the member presence class if not cached + member.presence = new member_1.MemberPresence(bot, presence, member); + } + } + } + // This chunk's information + const chunk = { + index: chunkIndex, + count: chunkCount, + }; + bot.events.emit(constants_1.BotEvent.GuildMembersChunk, guild, nonce, chunk); + if (chunk.index === chunk.count - 1) { + // This is the last chunk of the request, activate the GuildMembersChunkFinish event + bot.events.emit(constants_1.BotEvent.GuildMembersChunkFinish, guild, nonce); + } +}); diff --git a/lib/socket/handlers/guildRoleCreate.d.ts b/lib/socket/handlers/guildRoleCreate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildRoleCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildRoleCreate.js b/lib/socket/handlers/guildRoleCreate.js new file mode 100644 index 000000000..d4e5c33fd --- /dev/null +++ b/lib/socket/handlers/guildRoleCreate.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + const role = new structures_1.Role(bot, d.role, guild); + // Add role to the guild's roles cache + guild.roles.cache.add(role); + bot.events.emit(constants_1.BotEvent.GuildRoleCreate, role); +}); diff --git a/lib/socket/handlers/guildRoleDelete.d.ts b/lib/socket/handlers/guildRoleDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildRoleDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildRoleDelete.js b/lib/socket/handlers/guildRoleDelete.js new file mode 100644 index 000000000..2679d61d1 --- /dev/null +++ b/lib/socket/handlers/guildRoleDelete.js @@ -0,0 +1,22 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, role_id: roleId } = d; + const guild = yield bot.guilds.get(guildId); + const role = guild.roles.cache.get(roleId); + if (!role) + return; + // Remove the role from the guild's roles cache + guild.roles.cache.delete(role.id); + bot.events.emit(constants_1.BotEvent.GuildRoleDelete, role); +}); diff --git a/lib/socket/handlers/guildRoleUpdate.d.ts b/lib/socket/handlers/guildRoleUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildRoleUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildRoleUpdate.js b/lib/socket/handlers/guildRoleUpdate.js new file mode 100644 index 000000000..b6481b1e3 --- /dev/null +++ b/lib/socket/handlers/guildRoleUpdate.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId } = d; + const guild = yield bot.guilds.get(guildId); + const role = guild.roles.cache.get(d.role.id); + if (!role) + return; + const { before, after } = role.update(d.role); + bot.events.emit(constants_1.BotEvent.GuildRoleUpdate, before, after); +}); diff --git a/lib/socket/handlers/guildUpdate.d.ts b/lib/socket/handlers/guildUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/guildUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/guildUpdate.js b/lib/socket/handlers/guildUpdate.js new file mode 100644 index 000000000..8d6bcf7cc --- /dev/null +++ b/lib/socket/handlers/guildUpdate.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const guild_1 = require("../../structures/guild"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id } = d; + const guild = guild_1.Guild.find(bot, id); + if (!guild) + return; + const { before, after } = guild.update(d); + bot.events.emit(constants_1.BotEvent.GuildUpdate, before, after); +}); diff --git a/lib/socket/handlers/index.d.ts b/lib/socket/handlers/index.d.ts new file mode 100644 index 000000000..d4842fe06 --- /dev/null +++ b/lib/socket/handlers/index.d.ts @@ -0,0 +1,35 @@ +export { default as CHANNEL_CREATE } from './channelCreate'; +export { default as CHANNEL_DELETE } from './channelDelete'; +export { default as CHANNEL_PINS_UPDATE } from './channelPinsUpdate'; +export { default as CHANNEL_UPDATE } from './channelUpdate'; +export { default as GUILD_BAN_ADD } from './guildBanAdd'; +export { default as GUILD_BAN_REMOVE } from './guildBanRemove'; +export { default as GUILD_CREATE } from './guildCreate'; +export { default as GUILD_DELETE } from './guildDelete'; +export { default as GUILD_EMOJIS_UPDATE } from './guildEmojisUpdate'; +export { default as GUILD_INTEGRATIONS_UPDATE } from './guildIntegrationsUpdate'; +export { default as GUILD_MEMBER_ADD } from './guildMemberAdd'; +export { default as GUILD_MEMBER_REMOVE } from './guildMemberRemove'; +export { default as GUILD_MEMBERS_CHUNK } from './guildMembersChunk'; +export { default as GUILD_MEMBER_UPDATE } from './guildMemberUpdate'; +export { default as GUILD_ROLE_CREATE } from './guildRoleCreate'; +export { default as GUILD_ROLE_DELETE } from './guildRoleDelete'; +export { default as GUILD_ROLE_UPDATE } from './guildRoleUpdate'; +export { default as GUILD_UPDATE } from './guildUpdate'; +export { default as INVITE_CREATE } from './inviteCreate'; +export { default as INVITE_DELETE } from './inviteDelete'; +export { default as MESSAGE_CREATE } from './messageCreate'; +export { default as MESSAGE_DELETE } from './messageDelete'; +export { default as MESSAGE_DELETE_BULK } from './messageDeleteBulk'; +export { default as MESSAGE_REACTION_ADD } from './messageReactionAdd'; +export { default as MESSAGE_REACTION_REMOVE } from './messageReactionRemove'; +export { default as MESSAGE_REACTION_REMOVE_ALL } from './messageReactionRemoveAll'; +export { default as MESSAGE_REACTION_REMOVE_EMOJI } from './messageReactionRemoveEmoji'; +export { default as MESSAGE_UPDATE } from './messageUpdate'; +export { default as READY } from './ready'; +export { default as PRESENCE_UPDATE } from './presenceUpdate'; +export { default as TYPING_START } from './typingStart'; +export { default as USER_UPDATE } from './userUpdate'; +export { default as WEBHOOKS_UPDATE } from './webhooksUpdate'; +export { default as VOICE_SERVER_UPDATE } from './voiceServerUpdate'; +export { default as VOICE_STATE_UPDATE } from './voiceStateUpdate'; diff --git a/lib/socket/handlers/index.js b/lib/socket/handlers/index.js new file mode 100644 index 000000000..874b84ae4 --- /dev/null +++ b/lib/socket/handlers/index.js @@ -0,0 +1,72 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var channelCreate_1 = require("./channelCreate"); +Object.defineProperty(exports, "CHANNEL_CREATE", { enumerable: true, get: function () { return channelCreate_1.default; } }); +var channelDelete_1 = require("./channelDelete"); +Object.defineProperty(exports, "CHANNEL_DELETE", { enumerable: true, get: function () { return channelDelete_1.default; } }); +var channelPinsUpdate_1 = require("./channelPinsUpdate"); +Object.defineProperty(exports, "CHANNEL_PINS_UPDATE", { enumerable: true, get: function () { return channelPinsUpdate_1.default; } }); +var channelUpdate_1 = require("./channelUpdate"); +Object.defineProperty(exports, "CHANNEL_UPDATE", { enumerable: true, get: function () { return channelUpdate_1.default; } }); +var guildBanAdd_1 = require("./guildBanAdd"); +Object.defineProperty(exports, "GUILD_BAN_ADD", { enumerable: true, get: function () { return guildBanAdd_1.default; } }); +var guildBanRemove_1 = require("./guildBanRemove"); +Object.defineProperty(exports, "GUILD_BAN_REMOVE", { enumerable: true, get: function () { return guildBanRemove_1.default; } }); +var guildCreate_1 = require("./guildCreate"); +Object.defineProperty(exports, "GUILD_CREATE", { enumerable: true, get: function () { return guildCreate_1.default; } }); +var guildDelete_1 = require("./guildDelete"); +Object.defineProperty(exports, "GUILD_DELETE", { enumerable: true, get: function () { return guildDelete_1.default; } }); +var guildEmojisUpdate_1 = require("./guildEmojisUpdate"); +Object.defineProperty(exports, "GUILD_EMOJIS_UPDATE", { enumerable: true, get: function () { return guildEmojisUpdate_1.default; } }); +var guildIntegrationsUpdate_1 = require("./guildIntegrationsUpdate"); +Object.defineProperty(exports, "GUILD_INTEGRATIONS_UPDATE", { enumerable: true, get: function () { return guildIntegrationsUpdate_1.default; } }); +var guildMemberAdd_1 = require("./guildMemberAdd"); +Object.defineProperty(exports, "GUILD_MEMBER_ADD", { enumerable: true, get: function () { return guildMemberAdd_1.default; } }); +var guildMemberRemove_1 = require("./guildMemberRemove"); +Object.defineProperty(exports, "GUILD_MEMBER_REMOVE", { enumerable: true, get: function () { return guildMemberRemove_1.default; } }); +var guildMembersChunk_1 = require("./guildMembersChunk"); +Object.defineProperty(exports, "GUILD_MEMBERS_CHUNK", { enumerable: true, get: function () { return guildMembersChunk_1.default; } }); +var guildMemberUpdate_1 = require("./guildMemberUpdate"); +Object.defineProperty(exports, "GUILD_MEMBER_UPDATE", { enumerable: true, get: function () { return guildMemberUpdate_1.default; } }); +var guildRoleCreate_1 = require("./guildRoleCreate"); +Object.defineProperty(exports, "GUILD_ROLE_CREATE", { enumerable: true, get: function () { return guildRoleCreate_1.default; } }); +var guildRoleDelete_1 = require("./guildRoleDelete"); +Object.defineProperty(exports, "GUILD_ROLE_DELETE", { enumerable: true, get: function () { return guildRoleDelete_1.default; } }); +var guildRoleUpdate_1 = require("./guildRoleUpdate"); +Object.defineProperty(exports, "GUILD_ROLE_UPDATE", { enumerable: true, get: function () { return guildRoleUpdate_1.default; } }); +var guildUpdate_1 = require("./guildUpdate"); +Object.defineProperty(exports, "GUILD_UPDATE", { enumerable: true, get: function () { return guildUpdate_1.default; } }); +var inviteCreate_1 = require("./inviteCreate"); +Object.defineProperty(exports, "INVITE_CREATE", { enumerable: true, get: function () { return inviteCreate_1.default; } }); +var inviteDelete_1 = require("./inviteDelete"); +Object.defineProperty(exports, "INVITE_DELETE", { enumerable: true, get: function () { return inviteDelete_1.default; } }); +var messageCreate_1 = require("./messageCreate"); +Object.defineProperty(exports, "MESSAGE_CREATE", { enumerable: true, get: function () { return messageCreate_1.default; } }); +var messageDelete_1 = require("./messageDelete"); +Object.defineProperty(exports, "MESSAGE_DELETE", { enumerable: true, get: function () { return messageDelete_1.default; } }); +var messageDeleteBulk_1 = require("./messageDeleteBulk"); +Object.defineProperty(exports, "MESSAGE_DELETE_BULK", { enumerable: true, get: function () { return messageDeleteBulk_1.default; } }); +var messageReactionAdd_1 = require("./messageReactionAdd"); +Object.defineProperty(exports, "MESSAGE_REACTION_ADD", { enumerable: true, get: function () { return messageReactionAdd_1.default; } }); +var messageReactionRemove_1 = require("./messageReactionRemove"); +Object.defineProperty(exports, "MESSAGE_REACTION_REMOVE", { enumerable: true, get: function () { return messageReactionRemove_1.default; } }); +var messageReactionRemoveAll_1 = require("./messageReactionRemoveAll"); +Object.defineProperty(exports, "MESSAGE_REACTION_REMOVE_ALL", { enumerable: true, get: function () { return messageReactionRemoveAll_1.default; } }); +var messageReactionRemoveEmoji_1 = require("./messageReactionRemoveEmoji"); +Object.defineProperty(exports, "MESSAGE_REACTION_REMOVE_EMOJI", { enumerable: true, get: function () { return messageReactionRemoveEmoji_1.default; } }); +var messageUpdate_1 = require("./messageUpdate"); +Object.defineProperty(exports, "MESSAGE_UPDATE", { enumerable: true, get: function () { return messageUpdate_1.default; } }); +var ready_1 = require("./ready"); +Object.defineProperty(exports, "READY", { enumerable: true, get: function () { return ready_1.default; } }); +var presenceUpdate_1 = require("./presenceUpdate"); +Object.defineProperty(exports, "PRESENCE_UPDATE", { enumerable: true, get: function () { return presenceUpdate_1.default; } }); +var typingStart_1 = require("./typingStart"); +Object.defineProperty(exports, "TYPING_START", { enumerable: true, get: function () { return typingStart_1.default; } }); +var userUpdate_1 = require("./userUpdate"); +Object.defineProperty(exports, "USER_UPDATE", { enumerable: true, get: function () { return userUpdate_1.default; } }); +var webhooksUpdate_1 = require("./webhooksUpdate"); +Object.defineProperty(exports, "WEBHOOKS_UPDATE", { enumerable: true, get: function () { return webhooksUpdate_1.default; } }); +var voiceServerUpdate_1 = require("./voiceServerUpdate"); +Object.defineProperty(exports, "VOICE_SERVER_UPDATE", { enumerable: true, get: function () { return voiceServerUpdate_1.default; } }); +var voiceStateUpdate_1 = require("./voiceStateUpdate"); +Object.defineProperty(exports, "VOICE_STATE_UPDATE", { enumerable: true, get: function () { return voiceStateUpdate_1.default; } }); diff --git a/lib/socket/handlers/inviteCreate.d.ts b/lib/socket/handlers/inviteCreate.d.ts new file mode 100644 index 000000000..13344373b --- /dev/null +++ b/lib/socket/handlers/inviteCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => void; +export default _default; diff --git a/lib/socket/handlers/inviteCreate.js b/lib/socket/handlers/inviteCreate.js new file mode 100644 index 000000000..53130e0f6 --- /dev/null +++ b/lib/socket/handlers/inviteCreate.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => { + var _a, _b; + const invite = new structures_1.Invite(bot, d); + // Add the invite to the guild's invites cache + (_a = invite.guild) === null || _a === void 0 ? void 0 : _a.invites.cache.add(invite); + // Add the invite to the guild channel's invites cache + (_b = invite.channel) === null || _b === void 0 ? void 0 : _b.invites.cache.add(invite); + bot.events.emit(constants_1.BotEvent.InviteCreate, invite); +}; diff --git a/lib/socket/handlers/inviteDelete.d.ts b/lib/socket/handlers/inviteDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/inviteDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/inviteDelete.js b/lib/socket/handlers/inviteDelete.js new file mode 100644 index 000000000..664beac9b --- /dev/null +++ b/lib/socket/handlers/inviteDelete.js @@ -0,0 +1,30 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const channels_1 = require("../../structures/channels"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, channel_id: channelId, code } = d; + const guild = yield bot.guilds.get(guildId); + const channel = yield bot.channels.get(channelId); + const invite = (guild === null || guild === void 0 ? void 0 : guild.invites.cache.get(code)) || { + channelId: channelId, + guild, + code, + }; + // Delete the invite from the guild's invites cache + guild === null || guild === void 0 ? void 0 : guild.invites.cache.delete(code); + if (channel instanceof channels_1.GuildChannel) { + // Delete the invite from the guild channel's invites cache + channel.invites.cache.delete(invite.code); + } + bot.events.emit(constants_1.BotEvent.InviteDelete, invite); +}); diff --git a/lib/socket/handlers/messageCreate.d.ts b/lib/socket/handlers/messageCreate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageCreate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageCreate.js b/lib/socket/handlers/messageCreate.js new file mode 100644 index 000000000..92cda18bf --- /dev/null +++ b/lib/socket/handlers/messageCreate.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId } = d; + const channel = yield bot.channels.getText(channelId); + const message = new message_1.Message(bot, d, channel); + // Add the message to the cache + channel.messages.cache.add(message); + // Set the last message ID of the channel to that message + channel.lastMessageId = message.id; + bot.events.emit(constants_1.BotEvent.MessageCreate, message); +}); diff --git a/lib/socket/handlers/messageDelete.d.ts b/lib/socket/handlers/messageDelete.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageDelete.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageDelete.js b/lib/socket/handlers/messageDelete.js new file mode 100644 index 000000000..7421447eb --- /dev/null +++ b/lib/socket/handlers/messageDelete.js @@ -0,0 +1,30 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const channels_1 = require("../../structures/channels"); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id, channel_id: channelId } = d; + const channel = yield bot.channels.getText(channelId); + const guild = channel instanceof channels_1.GuildTextChannel ? channel.guild : undefined; + const message = channel.messages.cache.get(id) || { + id, + guild, + channel, + }; + if (message instanceof message_1.Message) { + // Mark the message as deleted + message.deleted = true; + } + channel.messages.cache.delete(message.id); + bot.events.emit(constants_1.BotEvent.MessageDelete, message); +}); diff --git a/lib/socket/handlers/messageDeleteBulk.d.ts b/lib/socket/handlers/messageDeleteBulk.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageDeleteBulk.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageDeleteBulk.js b/lib/socket/handlers/messageDeleteBulk.js new file mode 100644 index 000000000..795d0fda9 --- /dev/null +++ b/lib/socket/handlers/messageDeleteBulk.js @@ -0,0 +1,26 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { ids, channel_id: channelId } = d; + const channel = yield bot.channels.getText(channelId); + // Use the Message class if the message is cached, otherwise use the message ID + const messages = ids.map((id) => channel.messages.cache.get(id) || id); + for (const message of messages) { + if (message instanceof message_1.Message) { + // Mark message as deleted + message.deleted = true; + } + } + bot.events.emit(constants_1.BotEvent.MessageDeleteBulk, channel, messages); +}); diff --git a/lib/socket/handlers/messageReactionAdd.d.ts b/lib/socket/handlers/messageReactionAdd.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionAdd.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionAdd.js b/lib/socket/handlers/messageReactionAdd.js new file mode 100644 index 000000000..9573c1a8f --- /dev/null +++ b/lib/socket/handlers/messageReactionAdd.js @@ -0,0 +1,44 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Member_1 = require("../../structures/member/Member"); +const message_1 = require("../../structures/message"); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + var _a, _b; + const { user_id: userId } = d; + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const { emoji } = handlersUtils; + const message = yield handlersUtils.getMessage(); + const { guild } = message; + const { id } = emoji; + // Set the reaction object for this Emoji if one hasn't been set before + const reaction = message.reactions.cache.getOrSet(id, new message_1.MessageReaction(message, { + emoji, + botReacted: userId === ((_a = bot.user) === null || _a === void 0 ? void 0 : _a.id), + })); + // Change the reaction's botReacted state + reaction.botReacted = userId === ((_b = bot.user) === null || _b === void 0 ? void 0 : _b.id); + // Increment the count of the reaction + reaction.count++; + const user = yield bot.users.get(userId); + const member = d.member && guild ? new Member_1.Member(bot, d.member, guild) : undefined; + // Add the user to the Collection of users who reacted with this reaction + if (user) { + reaction.users.cache.add(user); + } + // Add the member to the Collection of members who reacted with this reaction + if (member) { + reaction.members.set(member.id, member); + } + bot.events.emit(constants_1.BotEvent.MessageReactionAdd, reaction, member || user); +}); diff --git a/lib/socket/handlers/messageReactionRemove.d.ts b/lib/socket/handlers/messageReactionRemove.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemove.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionRemove.js b/lib/socket/handlers/messageReactionRemove.js new file mode 100644 index 000000000..2be170147 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemove.js @@ -0,0 +1,46 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + var _a; + const { user_id: userId } = d; + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const { emoji } = handlersUtils; + const message = yield handlersUtils.getMessage(); + const { id } = emoji; + // Validates that the reaction is cached + if (!message.reactions.cache.has(id)) + return; + const reaction = message.reactions.cache.get(id); + const user = reaction.users.cache.get(userId); + const member = reaction.members.get(userId); + // Change the reaction's botReacted state + reaction.botReacted = userId === ((_a = bot.user) === null || _a === void 0 ? void 0 : _a.id); + // Decrements the count of the reaction + reaction.count--; + if (reaction.count > 0) { + // Removes the user from the Collection of users who reacted with this reaction + if (user) { + reaction.users.cache.delete(user.id); + } + // Removes the member from the Collection of members who reacted with this reaction + if (member) { + reaction.members.delete(member.id); + } + } + else { + // Terminate the reaction completely from the message cached reactions + message.reactions.cache.delete(id); + } + bot.events.emit(constants_1.BotEvent.MessageReactionRemove, reaction, member || user); +}); diff --git a/lib/socket/handlers/messageReactionRemoveAll.d.ts b/lib/socket/handlers/messageReactionRemoveAll.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveAll.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionRemoveAll.js b/lib/socket/handlers/messageReactionRemoveAll.js new file mode 100644 index 000000000..efc8bfefb --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveAll.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const message = yield handlersUtils.getMessage(); + message.reactions.cache.clear(); + bot.events.emit(constants_1.BotEvent.MessageReactionRemoveAll, message); +}); diff --git a/lib/socket/handlers/messageReactionRemoveEmoji.d.ts b/lib/socket/handlers/messageReactionRemoveEmoji.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveEmoji.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageReactionRemoveEmoji.js b/lib/socket/handlers/messageReactionRemoveEmoji.js new file mode 100644 index 000000000..eb7d3e618 --- /dev/null +++ b/lib/socket/handlers/messageReactionRemoveEmoji.js @@ -0,0 +1,22 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +const utils_1 = require("../utils"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); + const { emoji } = handlersUtils; + const message = yield handlersUtils.getMessage(); + const { id } = emoji; + const reaction = message.reactions.cache.get(id); + message.reactions.cache.delete(id); + bot.events.emit(constants_1.BotEvent.MessageReactionRemoveEmoji, reaction); +}); diff --git a/lib/socket/handlers/messageUpdate.d.ts b/lib/socket/handlers/messageUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/messageUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/messageUpdate.js b/lib/socket/handlers/messageUpdate.js new file mode 100644 index 000000000..fc3c304f3 --- /dev/null +++ b/lib/socket/handlers/messageUpdate.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId, id } = d; + const channel = yield bot.channels.getText(channelId); + const message = yield channel.messages.get(id); + const { before, after } = message.update(d); + bot.events.emit(constants_1.BotEvent.MessageUpdate, before, after); +}); diff --git a/lib/socket/handlers/presenceUpdate.d.ts b/lib/socket/handlers/presenceUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/presenceUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/presenceUpdate.js b/lib/socket/handlers/presenceUpdate.js new file mode 100644 index 000000000..644601677 --- /dev/null +++ b/lib/socket/handlers/presenceUpdate.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const member_1 = require("../../structures/member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { user: { id: memberId }, guild_id: guildId, } = d; + const guild = yield bot.guilds.get(guildId); + const member = yield guild.members.get(memberId); + const { presence } = member; + // Manual update due to presence's possibility of being undefined + const before = presence === null || presence === void 0 ? void 0 : presence.clone(); + const after = (presence === null || presence === void 0 ? void 0 : presence.init(d)) || (member.presence = new member_1.MemberPresence(bot, d, member)); + bot.events.emit(constants_1.BotEvent.PresenceUpdate, before, after); +}); diff --git a/lib/socket/handlers/ready.d.ts b/lib/socket/handlers/ready.d.ts new file mode 100644 index 000000000..bdb96e9fd --- /dev/null +++ b/lib/socket/handlers/ready.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { BotSocketShard, Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot, socket: BotSocketShard) => void; +export default _default; diff --git a/lib/socket/handlers/ready.js b/lib/socket/handlers/ready.js new file mode 100644 index 000000000..1fc22e559 --- /dev/null +++ b/lib/socket/handlers/ready.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const structures_1 = require("../../structures"); +exports.default = ({ d }, bot, socket) => { + const { session_id: sessionId, user, guilds } = d; + socket.sessionId = sessionId; + bot.user = new structures_1.BotUser(bot, user); + bot.users.cache.add(bot.user); + if (guilds.length) { + // Store all pending guilds to later be retrieved from incoming gateway GUILD_CREATE events + for (const guild of guilds) { + bot.unavailableGuilds.set(guild.id, guild); + socket.pendingGuilds.add(guild.id); + } + } + else { + // No pending guilds - the bot is ready! + socket.ready(); + } + bot.debug(bot.guilds, 'bot guilds'); +}; diff --git a/lib/socket/handlers/typingStart.d.ts b/lib/socket/handlers/typingStart.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/typingStart.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/typingStart.js b/lib/socket/handlers/typingStart.js new file mode 100644 index 000000000..9f28464ec --- /dev/null +++ b/lib/socket/handlers/typingStart.js @@ -0,0 +1,23 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const channels_1 = require("../../structures/channels"); +const member_1 = require("../../structures/member"); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { channel_id: channelId, timestamp, member } = d; + const channel = yield bot.channels.getText(channelId); + // Retrieve user / member from the channel + const user = channel instanceof channels_1.GuildTextChannel + ? new member_1.Member(bot, member, channel.guild) + : channel.recipient; + bot.events.emit(constants_1.BotEvent.TypingStart, channel, user, timestamp); +}); diff --git a/lib/socket/handlers/userUpdate.d.ts b/lib/socket/handlers/userUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/userUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/userUpdate.js b/lib/socket/handlers/userUpdate.js new file mode 100644 index 000000000..28c715f92 --- /dev/null +++ b/lib/socket/handlers/userUpdate.js @@ -0,0 +1,20 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { id } = d; + const user = bot.users.cache.get(id); + if (!user) + return; + const { before, after } = user.update(d); + bot.events.emit(constants_1.BotEvent.UserUpdate, before, after); +}); diff --git a/lib/socket/handlers/voiceServerUpdate.d.ts b/lib/socket/handlers/voiceServerUpdate.d.ts new file mode 100644 index 000000000..e5b7c949a --- /dev/null +++ b/lib/socket/handlers/voiceServerUpdate.d.ts @@ -0,0 +1,4 @@ +import { Payload } from '..'; +import { Bot } from '../..'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/voiceServerUpdate.js b/lib/socket/handlers/voiceServerUpdate.js new file mode 100644 index 000000000..9a8636bf5 --- /dev/null +++ b/lib/socket/handlers/voiceServerUpdate.js @@ -0,0 +1,15 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const __1 = require(".."); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + bot.events.emit(__1.BotEvent.VoiceServerUpdate, bot.guilds.cache.get(d.guild_id), d); +}); diff --git a/lib/socket/handlers/voiceStateUpdate.d.ts b/lib/socket/handlers/voiceStateUpdate.d.ts new file mode 100644 index 000000000..e5b7c949a --- /dev/null +++ b/lib/socket/handlers/voiceStateUpdate.d.ts @@ -0,0 +1,4 @@ +import { Payload } from '..'; +import { Bot } from '../..'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/voiceStateUpdate.js b/lib/socket/handlers/voiceStateUpdate.js new file mode 100644 index 000000000..9fd4243dd --- /dev/null +++ b/lib/socket/handlers/voiceStateUpdate.js @@ -0,0 +1,19 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const __1 = require(".."); +const VoiceState_1 = require("../../structures/voice/VoiceState"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const oldVoiceState = bot.guilds.cache.get(d.guild_id).voiceStates.get(d.user_id); + const newVoiceState = new VoiceState_1.VoiceState(bot, oldVoiceState.member, d); + bot.events.emit(__1.BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); + bot.guilds.cache.get(d.guild_id).voiceStates.set(d.user_id, newVoiceState); +}); diff --git a/lib/socket/handlers/webhooksUpdate.d.ts b/lib/socket/handlers/webhooksUpdate.d.ts new file mode 100644 index 000000000..6f5d6e794 --- /dev/null +++ b/lib/socket/handlers/webhooksUpdate.d.ts @@ -0,0 +1,4 @@ +import { Bot } from '../../bot'; +import { Payload } from '../BotSocketShard'; +declare const _default: ({ d }: Payload, bot: Bot) => Promise; +export default _default; diff --git a/lib/socket/handlers/webhooksUpdate.js b/lib/socket/handlers/webhooksUpdate.js new file mode 100644 index 000000000..70addf334 --- /dev/null +++ b/lib/socket/handlers/webhooksUpdate.js @@ -0,0 +1,18 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../constants"); +exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { + const { guild_id: guildId, channel_id: channelId } = d; + const guild = yield bot.guilds.get(guildId); + const channel = yield guild.channels.get(channelId); + bot.events.emit(constants_1.BotEvent.WebhooksUpdate, channel); +}); diff --git a/lib/socket/index.d.ts b/lib/socket/index.d.ts new file mode 100644 index 000000000..7dd03c537 --- /dev/null +++ b/lib/socket/index.d.ts @@ -0,0 +1,6 @@ +export * from './utils'; +export * from './BotHeartbeats'; +export * from './BotSocket'; +export * from './BotSocketShard'; +export * from './constants'; +export * from './properties'; diff --git a/lib/socket/index.js b/lib/socket/index.js new file mode 100644 index 000000000..b2fb56971 --- /dev/null +++ b/lib/socket/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./utils"), exports); +__exportStar(require("./BotHeartbeats"), exports); +__exportStar(require("./BotSocket"), exports); +__exportStar(require("./BotSocketShard"), exports); +__exportStar(require("./constants"), exports); +__exportStar(require("./properties"), exports); diff --git a/lib/socket/properties.d.ts b/lib/socket/properties.d.ts new file mode 100644 index 000000000..fc9946a5d --- /dev/null +++ b/lib/socket/properties.d.ts @@ -0,0 +1,34 @@ +/// +/** + * Websocket connection options + */ +export interface WebsocketOptions { + /** + * API Version + */ + v: number; + /** + * Gateway payload encoding type + */ + encoding: 'json' | 'etf'; + /** + * Gateway payload compression type + */ + compress?: 'zlib-stream'; + [key: string]: number | string | undefined; +} +/** + * All details that are sent in the Bot's 'IDENTIFY' request + */ +export declare const identify: { + properties: { + $os: NodeJS.Platform; + $browser: string; + $device: string; + }; + presence: { + status: string; + }; + large_threshold: number; + version: number; +}; diff --git a/lib/socket/properties.js b/lib/socket/properties.js new file mode 100644 index 000000000..68438a5e0 --- /dev/null +++ b/lib/socket/properties.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.identify = void 0; +const api_1 = require("../api"); +/** + * All details that are sent in the Bot's 'IDENTIFY' request + */ +exports.identify = { + properties: { + $os: process.platform, + $browser: 'node-discord', + $device: 'node-discord', + }, + presence: { + status: 'online', + }, + large_threshold: 250, + version: api_1.version, +}; diff --git a/lib/socket/utils/HandlersUtils.d.ts b/lib/socket/utils/HandlersUtils.d.ts new file mode 100644 index 000000000..8675021ec --- /dev/null +++ b/lib/socket/utils/HandlersUtils.d.ts @@ -0,0 +1,16 @@ +import { Bot } from '../../bot'; +import { PayloadData } from '../BotSocketShard'; +/** + * Main class for all util methods for socket event handlers. + */ +export declare class HandlersUtils { + /** + * The bot associated to this socket event + */ + protected readonly bot: Bot; + /** + * The data received from the socket event + */ + protected readonly data: PayloadData; + constructor(bot: Bot, data: PayloadData); +} diff --git a/lib/socket/utils/HandlersUtils.js b/lib/socket/utils/HandlersUtils.js new file mode 100644 index 000000000..fe4b1d225 --- /dev/null +++ b/lib/socket/utils/HandlersUtils.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HandlersUtils = void 0; +/** + * Main class for all util methods for socket event handlers. + */ +class HandlersUtils { + constructor(bot, data) { + this.bot = bot; + this.data = data; + } +} +exports.HandlersUtils = HandlersUtils; diff --git a/lib/socket/utils/ReactionHandlersUtils.d.ts b/lib/socket/utils/ReactionHandlersUtils.d.ts new file mode 100644 index 000000000..7c4f9739a --- /dev/null +++ b/lib/socket/utils/ReactionHandlersUtils.d.ts @@ -0,0 +1,17 @@ +import { HandlersUtils } from './HandlersUtils'; +import { Emoji, Message } from '../../structures'; +/** + * Provides util methods for all reactions-related handlers + */ +export declare class ReactionHandlersUtils extends HandlersUtils { + /** + * Returns the {@link Emoji} received from the event data + * @type {Emoji} + */ + get emoji(): Emoji; + /** + * Returns the message extracted from the event data + * @type {Message | undefined} + */ + getMessage(): Promise; +} diff --git a/lib/socket/utils/ReactionHandlersUtils.js b/lib/socket/utils/ReactionHandlersUtils.js new file mode 100644 index 000000000..25fc4deda --- /dev/null +++ b/lib/socket/utils/ReactionHandlersUtils.js @@ -0,0 +1,38 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ReactionHandlersUtils = void 0; +const HandlersUtils_1 = require("./HandlersUtils"); +const structures_1 = require("../../structures"); +/** + * Provides util methods for all reactions-related handlers + */ +class ReactionHandlersUtils extends HandlersUtils_1.HandlersUtils { + /** + * Returns the {@link Emoji} received from the event data + * @type {Emoji} + */ + get emoji() { + return new structures_1.Emoji(this.bot, this.data.emoji); + } + /** + * Returns the message extracted from the event data + * @type {Message | undefined} + */ + getMessage() { + return __awaiter(this, void 0, void 0, function* () { + const { channel_id: channelId, message_id: messageId } = this.data; + const channel = yield this.bot.channels.getText(channelId); + return channel.messages.get(messageId); + }); + } +} +exports.ReactionHandlersUtils = ReactionHandlersUtils; diff --git a/lib/socket/utils/index.d.ts b/lib/socket/utils/index.d.ts new file mode 100644 index 000000000..822a90546 --- /dev/null +++ b/lib/socket/utils/index.d.ts @@ -0,0 +1,2 @@ +export * from './HandlersUtils'; +export * from './ReactionHandlersUtils'; diff --git a/lib/socket/utils/index.js b/lib/socket/utils/index.js new file mode 100644 index 000000000..56f70b4bd --- /dev/null +++ b/lib/socket/utils/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./HandlersUtils"), exports); +__exportStar(require("./ReactionHandlersUtils"), exports); diff --git a/lib/structures/Avatar.d.ts b/lib/structures/Avatar.d.ts new file mode 100644 index 000000000..1a137b32d --- /dev/null +++ b/lib/structures/Avatar.d.ts @@ -0,0 +1,133 @@ +import { Snowflake } from '../types'; +/** + * The base URL for Discord API images + * https://discord.com/developers/docs/reference#image-formatting-image-base-url + * @type {string} + */ +export declare const cdnBaseURL = "https://cdn.discordapp.com"; +/** + * The allowed formats for guild emojis (better known as custom emojis) + */ +export declare enum GuildEmojiFormat { + PNG = "png", + GIF = "gif" +} +/** + * The allowed formats for guild icons + */ +export declare enum GuildIconFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp", + GIF = "gif" +} +/** + * The allowed formats for a guild's splash image + */ +export declare enum GuildSplashFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp" +} +/** + * The allowed formats for a guild's discovery splash image + */ +export declare enum GuildDiscoverySplashFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp" +} +/** + * The allowed formats for a guild's banner image + */ +export declare enum GuildBannerFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp" +} +/** + * The allowed formats for a user's avatar + */ +export declare enum UserAvatarFormat { + JPEG = "jpeg", + JPG = "jpg", + PNG = "png", + WebP = "webp", + GIF = "gif" +} +/** + * All avatar format enums + */ +export declare type AvatarFormat = GuildEmojiFormat | GuildIconFormat | GuildSplashFormat | GuildDiscoverySplashFormat | GuildBannerFormat | UserAvatarFormat; +/** + * Generates and modifies avatar URLs according to the given arguments + */ +export declare class Avatar { + /** + * Adds the given modifiers to an avatar URL + * @param {string} url The original avatar URL + * @param {AvatarFormat} format The required avatar image format + * @param {number} size The required avatar image size + * @returns {string} + */ + private static enhanceURL; + /** + * Returns a guild emoji's image + * @param {Snowflake} id The ID of the emoji + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + static emojiURL(id: Snowflake, format?: GuildEmojiFormat, size?: number): string; + /** + * Returns a guild's icon + * @param {string} hash The guild's icon hash + * @param {Snowflake} id The ID of the guild + * @param {GuildIconFormat} format The format of the returned icon image + * @param {number} size The size of the returned icon image + * @returns {string} + */ + static guildURL(hash: string, id: Snowflake, format?: GuildIconFormat, size?: number): string; + /** + * Returns a guild's splash image URL + * @param {string} hash The splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildSplashFormat} format The format of the returned splash image + * @param {number} size The size of the returned splash image + * @returns {string} + */ + static splashURL(hash: string, id: Snowflake, format?: GuildSplashFormat, size?: number): string; + /** + * Returns a guild's discovery splash image URL + * @param {string} hash The discovery splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildDiscoverySplashFormat} format The format of the returned discovery splash image + * @param {number} size The size of the returned discovery splash image + * @returns {string} + */ + static discoverySplashURL(hash: string, id: Snowflake, format?: GuildDiscoverySplashFormat, size?: number): string; + /** + * Returns a guild's banner image URL + * @param {string} hash The banner hash + * @param {Snowflake} id The ID of the guild + * @param {GuildBannerFormat} format The format of the returned banner image + * @param {number} size The size of the returned banner image + * @returns {string} + */ + static bannerURL(hash: string, id: Snowflake, format?: GuildBannerFormat, size?: number): string; + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link Format.PNG}* + * @param {string | null} hash The user's avatar hash + * @param {Snowflake} id The user's ID + * @param {number} hashtag the user's hashtag + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + static userAvatarURL(hash: string | null, id: Snowflake, hashtag: string, format?: UserAvatarFormat, size?: number): string; +} diff --git a/lib/structures/Avatar.js b/lib/structures/Avatar.js new file mode 100644 index 000000000..181cca677 --- /dev/null +++ b/lib/structures/Avatar.js @@ -0,0 +1,164 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Avatar = exports.UserAvatarFormat = exports.GuildBannerFormat = exports.GuildDiscoverySplashFormat = exports.GuildSplashFormat = exports.GuildIconFormat = exports.GuildEmojiFormat = exports.cdnBaseURL = void 0; +/** + * The base URL for Discord API images + * https://discord.com/developers/docs/reference#image-formatting-image-base-url + * @type {string} + */ +exports.cdnBaseURL = 'https://cdn.discordapp.com'; +/** + * The allowed formats for guild emojis (better known as custom emojis) + */ +var GuildEmojiFormat; +(function (GuildEmojiFormat) { + GuildEmojiFormat["PNG"] = "png"; + GuildEmojiFormat["GIF"] = "gif"; +})(GuildEmojiFormat = exports.GuildEmojiFormat || (exports.GuildEmojiFormat = {})); +/** + * The allowed formats for guild icons + */ +var GuildIconFormat; +(function (GuildIconFormat) { + GuildIconFormat["JPEG"] = "jpeg"; + GuildIconFormat["JPG"] = "jpg"; + GuildIconFormat["PNG"] = "png"; + GuildIconFormat["WebP"] = "webp"; + GuildIconFormat["GIF"] = "gif"; +})(GuildIconFormat = exports.GuildIconFormat || (exports.GuildIconFormat = {})); +/** + * The allowed formats for a guild's splash image + */ +var GuildSplashFormat; +(function (GuildSplashFormat) { + GuildSplashFormat["JPEG"] = "jpeg"; + GuildSplashFormat["JPG"] = "jpg"; + GuildSplashFormat["PNG"] = "png"; + GuildSplashFormat["WebP"] = "webp"; +})(GuildSplashFormat = exports.GuildSplashFormat || (exports.GuildSplashFormat = {})); +/** + * The allowed formats for a guild's discovery splash image + */ +var GuildDiscoverySplashFormat; +(function (GuildDiscoverySplashFormat) { + GuildDiscoverySplashFormat["JPEG"] = "jpeg"; + GuildDiscoverySplashFormat["JPG"] = "jpg"; + GuildDiscoverySplashFormat["PNG"] = "png"; + GuildDiscoverySplashFormat["WebP"] = "webp"; +})(GuildDiscoverySplashFormat = exports.GuildDiscoverySplashFormat || (exports.GuildDiscoverySplashFormat = {})); +/** + * The allowed formats for a guild's banner image + */ +var GuildBannerFormat; +(function (GuildBannerFormat) { + GuildBannerFormat["JPEG"] = "jpeg"; + GuildBannerFormat["JPG"] = "jpg"; + GuildBannerFormat["PNG"] = "png"; + GuildBannerFormat["WebP"] = "webp"; +})(GuildBannerFormat = exports.GuildBannerFormat || (exports.GuildBannerFormat = {})); +/** + * The allowed formats for a user's avatar + */ +var UserAvatarFormat; +(function (UserAvatarFormat) { + UserAvatarFormat["JPEG"] = "jpeg"; + UserAvatarFormat["JPG"] = "jpg"; + UserAvatarFormat["PNG"] = "png"; + UserAvatarFormat["WebP"] = "webp"; + UserAvatarFormat["GIF"] = "gif"; +})(UserAvatarFormat = exports.UserAvatarFormat || (exports.UserAvatarFormat = {})); +/** + * Generates and modifies avatar URLs according to the given arguments + */ +class Avatar { + /** + * Adds the given modifiers to an avatar URL + * @param {string} url The original avatar URL + * @param {AvatarFormat} format The required avatar image format + * @param {number} size The required avatar image size + * @returns {string} + */ + static enhanceURL(url, format, size) { + if (size && (size < 16 || size > 4096 || (size & (size - 1)) !== 0)) { + throw new Error('You provided an invalid image size! The size must be any power of two between 16 and 4096'); + } + const avatarURL = `${url}.${format}`; + return size ? `${avatarURL}?size=${size}` : avatarURL; + } + /** + * Returns a guild emoji's image + * @param {Snowflake} id The ID of the emoji + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + static emojiURL(id, format = GuildEmojiFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/emojis/${id}`, format, size); + } + /** + * Returns a guild's icon + * @param {string} hash The guild's icon hash + * @param {Snowflake} id The ID of the guild + * @param {GuildIconFormat} format The format of the returned icon image + * @param {number} size The size of the returned icon image + * @returns {string} + */ + static guildURL(hash, id, format = GuildIconFormat.PNG, size) { + if (format === GuildIconFormat.GIF && !hash.startsWith('a_')) { + throw new Error(`The icon of guild ${id} is not a GIF as you requested! Choose a different image format`); + } + return Avatar.enhanceURL(`${exports.cdnBaseURL}/icons/${id}/${hash}`, format, size); + } + /** + * Returns a guild's splash image URL + * @param {string} hash The splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildSplashFormat} format The format of the returned splash image + * @param {number} size The size of the returned splash image + * @returns {string} + */ + static splashURL(hash, id, format = GuildSplashFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/splashes/${id}/${hash}`, format, size); + } + /** + * Returns a guild's discovery splash image URL + * @param {string} hash The discovery splash hash + * @param {Snowflake} id The ID of the guild + * @param {GuildDiscoverySplashFormat} format The format of the returned discovery splash image + * @param {number} size The size of the returned discovery splash image + * @returns {string} + */ + static discoverySplashURL(hash, id, format = GuildDiscoverySplashFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/discovery-splashes/${id}/${hash}`, format, size); + } + /** + * Returns a guild's banner image URL + * @param {string} hash The banner hash + * @param {Snowflake} id The ID of the guild + * @param {GuildBannerFormat} format The format of the returned banner image + * @param {number} size The size of the returned banner image + * @returns {string} + */ + static bannerURL(hash, id, format = GuildBannerFormat.PNG, size) { + return Avatar.enhanceURL(`${exports.cdnBaseURL}/banners/${id}/${hash}`, format, size); + } + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link Format.PNG}* + * @param {string | null} hash The user's avatar hash + * @param {Snowflake} id The user's ID + * @param {number} hashtag the user's hashtag + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + static userAvatarURL(hash, id, hashtag, format = UserAvatarFormat.PNG, size) { + if (format === UserAvatarFormat.GIF && hash && !hash.startsWith('a_')) { + throw new Error(`The avatar of user ${id} is not a GIF as you requested! Choose a different image format`); + } + return hash + ? Avatar.enhanceURL(`${exports.cdnBaseURL}/avatars/${id}/${hash}`, format, size) + : Avatar.enhanceURL(`${exports.cdnBaseURL}/embed/avatars/${parseInt(hashtag) % 5}`, format, size); + } +} +exports.Avatar = Avatar; diff --git a/lib/structures/BotPresence.d.ts b/lib/structures/BotPresence.d.ts new file mode 100644 index 000000000..bf5a8e873 --- /dev/null +++ b/lib/structures/BotPresence.d.ts @@ -0,0 +1,28 @@ +import { UpdateStatus } from './BotUser'; +import { Bot } from '../bot'; +/** + * Represents the bot's presence + */ +export declare class BotPresence { + /** + * The bot instance + */ + readonly bot: Bot; + /** + * The current bot presence + */ + presence: UpdateStatus | undefined; + constructor(bot: Bot); + /** + * Modifies the presence of the bot + * @param {UpdateStatus} presence The new bot presence + * @returns {void} + */ + modify(presence: UpdateStatus): void; + /** + * Serializes the bot presence into a gateway structure + * @param {UpdateStatus} presence The bot presence + * @returns {GatewayStruct} + */ + private static serialize; +} diff --git a/lib/structures/BotPresence.js b/lib/structures/BotPresence.js new file mode 100644 index 000000000..4591187a6 --- /dev/null +++ b/lib/structures/BotPresence.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotPresence = void 0; +/** + * Represents the bot's presence + */ +class BotPresence { + constructor(bot) { + this.bot = bot; + } + /** + * Modifies the presence of the bot + * @param {UpdateStatus} presence The new bot presence + * @returns {void} + */ + modify(presence) { + this.presence = presence; + return this.bot.connection.modifyPresence(BotPresence.serialize(presence)); + } + /** + * Serializes the bot presence into a gateway structure + * @param {UpdateStatus} presence The bot presence + * @returns {GatewayStruct} + */ + static serialize(presence) { + var _a; + return { + status: presence.status, + afk: presence.afk || false, + since: presence.since || null, + game: presence.game + ? { + name: presence.game.name, + type: presence.game.type, + url: presence.game.url, + created_at: presence.game.createdAt, + timestamps: presence.game.timestamps, + application_id: presence.game.applicationId, + details: presence.game.details, + state: presence.game.state, + emoji: presence.game.emoji, + party: presence.game.party, + assets: presence.game.assets && { + large_image: presence.game.assets.largeImage, + large_text: presence.game.assets.largeText, + small_image: presence.game.assets.smallImage, + small_text: presence.game.assets.smallText, + }, + secrets: presence.game.secrets, + instance: presence.game.instance, + flags: (_a = presence.game.flags) === null || _a === void 0 ? void 0 : _a.bits, + } + : null, + }; + } +} +exports.BotPresence = BotPresence; diff --git a/lib/structures/BotUser.d.ts b/lib/structures/BotUser.d.ts new file mode 100644 index 000000000..61cd37783 --- /dev/null +++ b/lib/structures/BotUser.d.ts @@ -0,0 +1,112 @@ +import { BotPresence } from './BotPresence'; +import { ImageURI } from './ImageURI'; +import { User } from './User'; +import { GatewayStruct } from './base'; +import { PermissionFlags } from './flags'; +import { PresenceGame, PresenceStatus } from './member'; +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * Options for when modifying the bot user + */ +export interface ModifyBotUserOptions { + /** + * The modified bot user's username. If changed may cause the user's discriminator to be randomized + */ + username?: string; + /** + * If passed, modifies the user's avatar. + * Path to the new user's avatar image + */ + avatar?: ImageURI | null; +} +/** + * Options for when fetching guilds using the bot's user + */ +export interface FetchGuildsOptions { + /** + * Fetch guilds before this guild ID + */ + before?: Snowflake; + /** + * Fetch guilds after this guild ID + */ + after?: Snowflake; + /** + * Max number of guilds to return (1-100) + * @default 100 + */ + limit?: number; +} +/** + * Partial guild object + */ +export interface PartialGuild { + /** + * The ID of the guild + */ + id: Snowflake; + /** + * The name of the guild + */ + name: string; + /** + * The icon hash of the guild + */ + icon: string; + /** + * Whether the bot user is the owner of the guild + */ + owner: boolean; + /** + * The permissions for the bot in this guild + */ + permissions: PermissionFlags; +} +/** + * Sent by the bot to indicate a presence or status update + */ +export interface UpdateStatus { + /** + * UNIX time (in milliseconds) of when the client went idle, or null if the client is not idle + */ + since?: number; + /** + * Null, or the user's new activity + */ + game?: Partial; + /** + * Whether or not the client is AFK + */ + afk?: boolean; + /** + * The user's new status + */ + status: PresenceStatus; +} +/** + * Represents the bot's user account + */ +export declare class BotUser extends User { + presence: BotPresence; + constructor(bot: Bot, user: GatewayStruct); + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modify(options: ModifyBotUserOptions): Promise; + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchGuilds(options?: FetchGuildsOptions): Promise>; + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId: Snowflake): Promise; +} diff --git a/lib/structures/BotUser.js b/lib/structures/BotUser.js new file mode 100644 index 000000000..e7380b8f8 --- /dev/null +++ b/lib/structures/BotUser.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BotUser = void 0; +const BotPresence_1 = require("./BotPresence"); +const User_1 = require("./User"); +/** + * Represents the bot's user account + */ +class BotUser extends User_1.User { + constructor(bot, user) { + super(bot, user); + this.presence = new BotPresence_1.BotPresence(this.bot); + } + /** + * Modifies this bot's user account settings + * @param {ModifyBotUserOptions} options The options for the modified bot user + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyBotUser(options); + } + /** + * Fetches the guilds the bot user is a member of + * @param {FetchGuildsOptions} options The options for the fetch operation + * @returns {Promise>} + */ + fetchGuilds(options) { + return this.bot.api.fetchBotGuilds(options); + } + /** + * Leaves a guild by its ID + * @param {Snowflake} guildId The ID of the guild + * @returns {Promise} + */ + leaveGuild(guildId) { + return this.bot.api.leaveGuild(guildId); + } +} +exports.BotUser = BotUser; diff --git a/lib/structures/Emoji.d.ts b/lib/structures/Emoji.d.ts new file mode 100644 index 000000000..d2f480253 --- /dev/null +++ b/lib/structures/Emoji.d.ts @@ -0,0 +1,70 @@ +import { Role } from './Role'; +import { User } from './User'; +import { BaseStruct, GatewayStruct } from './base'; +import { Guild } from './guild'; +import Collection from '../Collection'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * Any kind of emoji. Could be its unicode name, ID or {@link Emoji} object + */ +export declare type EmojiResolvable = string | Snowflake | Emoji; +export declare class Emoji extends BaseStruct { + /** + * The ID of the emoji. Possibly null if the emoji class was generated from a standard emoji + */ + emojiId: Snowflake | null; + /** + * The guild this emoji was created. Possibly undefined if this is a standard emoji + */ + guild: Guild | undefined; + /** + * The name of the emoji. Possibly null in reaction emoji objects + */ + name: string | null; + /** + * {@link Collection} of {@link Role}s this emoji is whitelisted to + */ + roles: Collection | undefined; + /** + * The user that created this emoji + */ + user: User | undefined; + /** + * Whether this emoji must be wrapped in colons + */ + requiresColons: boolean | undefined; + /** + * Whether this emoji is managed + */ + managed: boolean | undefined; + /** + * Whether this emoji is animated + */ + animated: boolean | undefined; + /** + * Whether this emoji can be used, may be false due to loss of Server Boosts + */ + available: boolean | undefined; + constructor(bot: Bot, emoji: GatewayStruct, guild?: Guild); + /** + * @ignore + * @param {GatewayStruct} emoji The emoji data + * @returns {this} + */ + init(emoji: GatewayStruct): this; + /** + * Returns this emoji's identifier. + * An emoji identifier could be its name for built-in emojis, or a combination of its name and ID if it's a guild emoji. + * @returns {string} + */ + get id(): string; + /** + * Finds the identifier of the given emoji. + * The emoji can be a Guild emoji, meaning we would have to search for it in the Bot's cached emojis Collection + * @param {Collection} emojis An emojis cache to search for the emoji in + * @param {EmojiResolvable} emoji The emoji to resolve + * @returns {string | null} + */ + static resolve(emojis: Collection, emoji: EmojiResolvable): string | null; +} diff --git a/lib/structures/Emoji.js b/lib/structures/Emoji.js new file mode 100644 index 000000000..47b139037 --- /dev/null +++ b/lib/structures/Emoji.js @@ -0,0 +1,57 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Emoji = void 0; +const User_1 = require("./User"); +const base_1 = require("./base"); +const Collection_1 = __importDefault(require("../Collection")); +class Emoji extends base_1.BaseStruct { + constructor(bot, emoji, guild) { + super(bot, emoji); + this.guild = guild; + this.init(emoji); + } + /** + * @ignore + * @param {GatewayStruct} emoji The emoji data + * @returns {this} + */ + init(emoji) { + var _a; + this.emojiId = emoji.id; + this.name = emoji.name; + this.roles = new Collection_1.default((_a = this.guild) === null || _a === void 0 ? void 0 : _a.roles.cache.filter((_r, id) => emoji.roles.includes(id))); + if (emoji.user) { + this.user = new User_1.User(this.bot, emoji.user); + } + this.requiresColons = emoji.require_colons; + this.managed = emoji.managed; + this.animated = emoji.animated; + this.available = emoji.available; + return this; + } + /** + * Returns this emoji's identifier. + * An emoji identifier could be its name for built-in emojis, or a combination of its name and ID if it's a guild emoji. + * @returns {string} + */ + get id() { + if (this.emojiId) + return `${this.animated ? 'a:' : ''}${this.name}:${this.emojiId}`; + return this.name; + } + /** + * Finds the identifier of the given emoji. + * The emoji can be a Guild emoji, meaning we would have to search for it in the Bot's cached emojis Collection + * @param {Collection} emojis An emojis cache to search for the emoji in + * @param {EmojiResolvable} emoji The emoji to resolve + * @returns {string | null} + */ + static resolve(emojis, emoji) { + var _a; + return emoji instanceof Emoji ? emoji.id : ((_a = emojis.get(emoji)) === null || _a === void 0 ? void 0 : _a.id) || emoji; + } +} +exports.Emoji = Emoji; diff --git a/lib/structures/ImageURI.d.ts b/lib/structures/ImageURI.d.ts new file mode 100644 index 000000000..45027cd54 --- /dev/null +++ b/lib/structures/ImageURI.d.ts @@ -0,0 +1,32 @@ +/** + * Represents an image being sent to the Discord API as Image Data + * https://discord.com/developers/docs/reference#image-data + */ +export declare class ImageURI { + /** + * The path of the image + */ + private readonly path; + /** + * @param {string} path The image path + * @example ```typescript + * const image = new ImageURI('./image.png'); + * ``` + */ + constructor(path: string); + /** + * Returns the image mime and base64 data as a formatted string + * @returns {string} + */ + stringify(): Promise; + /** + * Returns the image as base64 + * @type {string} + */ + private get image(); + /** + * Returns the mime type of the image + * @type {string | boolean} + */ + private get mime(); +} diff --git a/lib/structures/ImageURI.js b/lib/structures/ImageURI.js new file mode 100644 index 000000000..09747726f --- /dev/null +++ b/lib/structures/ImageURI.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImageURI = void 0; +const fs_1 = __importDefault(require("fs")); +const util_1 = __importDefault(require("util")); +const mime_types_1 = __importDefault(require("mime-types")); +const readFile = util_1.default.promisify(fs_1.default.readFile); +/** + * Represents an image being sent to the Discord API as Image Data + * https://discord.com/developers/docs/reference#image-data + */ +class ImageURI { + /** + * @param {string} path The image path + * @example ```typescript + * const image = new ImageURI('./image.png'); + * ``` + */ + constructor(path) { + this.path = path; + } + /** + * Returns the image mime and base64 data as a formatted string + * @returns {string} + */ + stringify() { + return __awaiter(this, void 0, void 0, function* () { + const { image, mime } = this; + if (!mime) { + throw new Error(`Invalid mime type for image ${this.path}`); + } + return `data:${mime};base64,${yield image}`; + }); + } + /** + * Returns the image as base64 + * @type {string} + */ + get image() { + return readFile(this.path, { encoding: 'base64' }); + } + /** + * Returns the mime type of the image + * @type {string | boolean} + */ + get mime() { + return mime_types_1.default.lookup(this.path); + } +} +exports.ImageURI = ImageURI; diff --git a/lib/structures/Invite.d.ts b/lib/structures/Invite.d.ts new file mode 100644 index 000000000..04b4d16b3 --- /dev/null +++ b/lib/structures/Invite.d.ts @@ -0,0 +1,100 @@ +import { Timestamp } from './Timestamp'; +import { User } from './User'; +import { BaseStruct, GatewayStruct } from './base'; +import { GuildChannel } from './channels'; +import { Guild } from './guild'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +export declare type InviteCode = string; +/** + * Returned from the {@link INVITE_DELETE} event when the invite has not been cached + */ +export interface PartialInvite { + /** + * The ID of the channel this invite is for + */ + channelId: Snowflake; + /** + * The guild this invite is for + */ + guild: Guild | undefined; + /** + * The invite code (unique ID) + */ + code: InviteCode; +} +export interface InviteMax { + /** + * Duration (in seconds) after which the invite expires + */ + age: number; + /** + * Maximum number of times this invite can be used + */ + uses: number; +} +/** + * Options used when creating new invites for channels + */ +export interface InviteOptions { + /** + * The maximum data for the new invite + */ + max?: Partial; + /** + * Whether this invite only grants temporary membership + */ + temporary?: boolean; + /** + * If true, don't try to reuse a similar invite (useful for creating many unique one time use invites) + */ + unique?: boolean; +} +export declare class Invite extends BaseStruct { + /** + * The channel this invite is for + */ + channel: GuildChannel | undefined; + /** + * The invite code (unique ID) + */ + code: InviteCode; + /** + * The timestamp of when the invite was created + */ + createdAt: Timestamp; + /** + * The guild this invite is for + */ + guild: Guild | undefined; + /** + * The user who created the invite + */ + inviter: User | undefined; + /** + * {@link InviteMax} object containing the invite's maximum age and maximum uses + */ + max: InviteMax; + /** + * Whether this invite grants temporary membership + */ + temporary: boolean; + /** + * Number of times this invite has been used + */ + uses: number; + constructor(bot: Bot, invite: GatewayStruct, guild?: Guild); + /** + * @ignore + * @param {GatewayStruct} invite The invite data + * @param {Guild} guild The guild this invite is for + * @returns {this} + */ + init(invite: GatewayStruct, guild?: Guild): this; + /** + * The code this invite stores. + * Servers as an identifier for this invite + * @type {string} + */ + get id(): string; +} diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js new file mode 100644 index 000000000..9a61f086b --- /dev/null +++ b/lib/structures/Invite.js @@ -0,0 +1,50 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Invite = void 0; +const Timestamp_1 = require("./Timestamp"); +const User_1 = require("./User"); +const base_1 = require("./base"); +class Invite extends base_1.BaseStruct { + constructor(bot, invite, guild) { + super(bot, invite); + this.init(invite, guild); + } + /** + * @ignore + * @param {GatewayStruct} invite The invite data + * @param {Guild} guild The guild this invite is for + * @returns {this} + */ + init(invite, guild) { + this.code = invite.code; + this.createdAt = new Timestamp_1.Timestamp(invite.created_at); + if (guild) { + this.guild = guild; + } + else if (invite.guild) { + this.guild = this.bot.guilds.cache.get(invite.guild.id); + } + if (this.guild && invite.channel) { + this.channel = this.guild.channels.cache.get(invite.channel.id); + } + if (invite.inviter) { + this.inviter = new User_1.User(this.bot, invite.inviter); + } + this.max = { + age: invite.max_age, + uses: invite.max_uses, + }; + this.temporary = invite.temporary; + this.uses = invite.uses; + return this; + } + /** + * The code this invite stores. + * Servers as an identifier for this invite + * @type {string} + */ + get id() { + return this.code; + } +} +exports.Invite = Invite; diff --git a/lib/structures/PermissionOverwrite.d.ts b/lib/structures/PermissionOverwrite.d.ts new file mode 100644 index 000000000..28d550549 --- /dev/null +++ b/lib/structures/PermissionOverwrite.d.ts @@ -0,0 +1,35 @@ +import { BaseStruct, GatewayStruct } from './base'; +import { GuildChannel } from './channels'; +import { Permissible, PermissionOverwriteFlags } from './flags'; +import { Bot } from '../bot'; +/** + * A full permission overwrite entry. + * Contains full data about a guild channel's permission overwrite for a user or a role + */ +export declare class PermissionOverwrite extends BaseStruct { + /** + * The channel this overwrite is associated to + */ + readonly channel: GuildChannel; + /** + * The permission flags of this permission overwrite + */ + flags: Required; + /** + * The user or role this permission overwrite is for + */ + permissible: Permissible; + constructor(bot: Bot, permission: GatewayStruct, channel: GuildChannel); + /** + * @ignore + * @param {GatewayStruct} permission The permission data + * @returns {this} + */ + init(permission: GatewayStruct): this; + /** + * The permissible's ID. + * Servers as an identifier for this permission overwrite + * @type {string} + */ + get id(): string; +} diff --git a/lib/structures/PermissionOverwrite.js b/lib/structures/PermissionOverwrite.js new file mode 100644 index 000000000..5cdc930f2 --- /dev/null +++ b/lib/structures/PermissionOverwrite.js @@ -0,0 +1,41 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionOverwrite = void 0; +const base_1 = require("./base"); +const flags_1 = require("./flags"); +/** + * A full permission overwrite entry. + * Contains full data about a guild channel's permission overwrite for a user or a role + */ +class PermissionOverwrite extends base_1.BaseStruct { + constructor(bot, permission, channel) { + super(bot, permission); + this.channel = channel; + this.init(permission); + } + /** + * @ignore + * @param {GatewayStruct} permission The permission data + * @returns {this} + */ + init(permission) { + this.flags = { + allow: new flags_1.PermissionFlags(permission.allow_new), + deny: new flags_1.PermissionFlags(permission.deny_new), + }; + this.permissible = { + id: permission.id, + type: permission.type, + }; + return this; + } + /** + * The permissible's ID. + * Servers as an identifier for this permission overwrite + * @type {string} + */ + get id() { + return this.permissible.id; + } +} +exports.PermissionOverwrite = PermissionOverwrite; diff --git a/lib/structures/Role.d.ts b/lib/structures/Role.d.ts new file mode 100644 index 000000000..d33fa136b --- /dev/null +++ b/lib/structures/Role.d.ts @@ -0,0 +1,89 @@ +import { GatewayStruct, BaseGuildStruct } from './base'; +import { PermissionFlags } from './flags'; +import { Guild } from './guild'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +export interface RoleOptions { + /** + * The name of the created role + * @default "new role" + */ + name?: string; + /** + * The enabled permission flags for the created role + * @default Permissions for the @everyone role + */ + permissions?: PermissionFlags; + /** + * The hex color for the created role + * @default 0 + * @example 0x406fff // creates a blue role + */ + color?: number; + /** + * Whether the created role should be displayed separately in the sidebar + * @default false + */ + listedSeparately?: boolean; + /** + * Whether the created role should be mentionable + * @default false + */ + mentionable?: boolean; +} +/** + * Represents a role in a guild + */ +export declare class Role extends BaseGuildStruct { + /** + * The role's ID + */ + id: Snowflake; + /** + * The role name + */ + name: string; + /** + * Number representation of hexadecimal color code + */ + color: number; + /** + * Whether this role is listed separately on the guild members list + */ + listedSeparately: boolean; + /** + * The position of this role + */ + position: number; + /** + * The permissions of the role + */ + permissions: PermissionFlags; + /** + * Whether this role is managed by an integration + */ + managed: boolean; + /** + * Whether this role is mentionable + */ + mentionable: boolean; + constructor(bot: Bot, role: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} role The role data + * @returns {this} + */ + init(role: GatewayStruct): this; + /** + * Modifies this role. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions} options The options for the modified role + * @returns {Promise} The updated role + */ + modify(options: RoleOptions): Promise; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/Role.js b/lib/structures/Role.js new file mode 100644 index 000000000..fb0176045 --- /dev/null +++ b/lib/structures/Role.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Role = void 0; +const base_1 = require("./base"); +const flags_1 = require("./flags"); +/** + * Represents a role in a guild + */ +class Role extends base_1.BaseGuildStruct { + constructor(bot, role, guild) { + super(bot, guild, role); + this.init(role); + } + /** + * @ignore + * @param {GatewayStruct} role The role data + * @returns {this} + */ + init(role) { + this.id = role.id; + this.name = role.name; + this.color = role.color; + this.listedSeparately = role.hoist; + this.position = role.position; + this.permissions = new flags_1.PermissionFlags(role.permissions_new); + this.managed = role.managed; + this.mentionable = role.mentionable; + return this; + } + /** + * Modifies this role. + * Requires the {@link Permission.ManageRoles} permission + * @param {RoleOptions} options The options for the modified role + * @returns {Promise} The updated role + */ + modify(options) { + return this.bot.api.modifyRole(this.guild.id, this.id, options); + } + /** + * @ignore + * @returns {string} + */ + toString() { + return `<@&${this.id}>`; + } +} +exports.Role = Role; diff --git a/lib/structures/Timestamp.d.ts b/lib/structures/Timestamp.d.ts new file mode 100644 index 000000000..cdc22d6e0 --- /dev/null +++ b/lib/structures/Timestamp.d.ts @@ -0,0 +1,20 @@ +/** + * Handles the conversion of timestamps received from the Discord API into UNIX timestamps + */ +export declare class Timestamp { + /** + * The ISO date of this timestamp + */ + date: string | undefined; + constructor(date: string | number | Date | undefined); + /** + * Returns the UNIX timestamp of this timestamp + * @returns {number | undefined} + */ + unix(): number | undefined; + /** + * Returns the ISO date of this timestamp + * @returns {string | undefined} + */ + get iso(): string | undefined; +} diff --git a/lib/structures/Timestamp.js b/lib/structures/Timestamp.js new file mode 100644 index 000000000..d10827d66 --- /dev/null +++ b/lib/structures/Timestamp.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Timestamp = void 0; +/** + * Handles the conversion of timestamps received from the Discord API into UNIX timestamps + */ +class Timestamp { + constructor(date) { + if (typeof date === 'number') { + this.date = new Date(date).toISOString(); + } + else if (date instanceof Date) { + this.date = date.toISOString(); + } + else { + this.date = date; + } + } + /** + * Returns the UNIX timestamp of this timestamp + * @returns {number | undefined} + */ + unix() { + return this.date ? Date.parse(this.date) : undefined; + } + /** + * Returns the ISO date of this timestamp + * @returns {string | undefined} + */ + get iso() { + return this.date ? new Date(this.date).toISOString() : undefined; + } +} +exports.Timestamp = Timestamp; diff --git a/lib/structures/User.d.ts b/lib/structures/User.d.ts new file mode 100644 index 000000000..a9e56b098 --- /dev/null +++ b/lib/structures/User.d.ts @@ -0,0 +1,113 @@ +import { UserAvatarFormat } from './Avatar'; +import { BaseStruct, GatewayStruct } from './base'; +import { DMChannel, TextChannel } from './channels'; +import { UserFlags } from './flags'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * User nitro types + */ +export declare enum NitroType { + None = 0, + NitroClassic = 1, + Nitro = 2 +} +/** + * Represents a user in the Discord platform + * @extends BaseStruct + */ +export declare class User extends BaseStruct { + /** + * The user's ID + */ + id: Snowflake; + /** + * User's username, not unique across the platform + */ + username: string; + /** + * User's 4-digit discord-tag + */ + hashtag: string; + /** + * User's avatar image. Possibly null if user does not have an avatar image + */ + avatarHash: string | null; + /** + * Whether the user is a bot + */ + isBot: boolean | undefined; + /** + * Whether the user is an Official Discord System user (part of urgent message system) + */ + system: boolean | undefined; + /** + * Whether the user has two factor enabled on their account + */ + mfaEnabled: boolean | undefined; + /** + * The user's chosen language option + */ + locale: string; + /** + * Whether the email on this user has been verified + */ + verified: boolean | undefined; + /** + * The user's email + */ + email: string | null | undefined; + /** + * {@link NitroType} object containing the type of nitro subscription on a user's account + */ + nitroType: NitroType | undefined; + /** + * The flags on a user's account + */ + flags: UserFlags | undefined; + /** + * The public flags on a user's account + */ + publicFlags: UserFlags | undefined; + /** + * This user's DM channel with the bot, if cached + */ + dm: DMChannel | undefined; + constructor(bot: Bot, user: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} user The user data + * @returns {this} + */ + init(user: GatewayStruct): this; + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link UserAvatarFormat.PNG}* + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + avatarURL(format?: UserAvatarFormat, size?: number): string; + /** + * Creates a new DM channel between this user and the bot user + * @returns {Promise} + */ + createDM(): Promise; + /** + * Sends a DM message to the user from the bot user + * @param {any} args Identical to the arguments of {@link TextChannel.sendMessage} + * @returns {any} Identical to the return type of {@link TextChannel.sendMessage} + */ + sendMessage(...args: Parameters): ReturnType; + /** + * Combines a user's username and hashtag and generates a full name + * @type {string} + * @example Day#0001 + */ + get fullName(): string; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/User.js b/lib/structures/User.js new file mode 100644 index 000000000..08f15aff0 --- /dev/null +++ b/lib/structures/User.js @@ -0,0 +1,104 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.User = exports.NitroType = void 0; +const Avatar_1 = require("./Avatar"); +const base_1 = require("./base"); +const flags_1 = require("./flags"); +/** + * User nitro types + */ +var NitroType; +(function (NitroType) { + NitroType[NitroType["None"] = 0] = "None"; + NitroType[NitroType["NitroClassic"] = 1] = "NitroClassic"; + NitroType[NitroType["Nitro"] = 2] = "Nitro"; +})(NitroType = exports.NitroType || (exports.NitroType = {})); +// TODO: replace "new User()" with cache get or create +/** + * Represents a user in the Discord platform + * @extends BaseStruct + */ +class User extends base_1.BaseStruct { + constructor(bot, user) { + super(bot, user); + this.init(user); + } + /** + * @ignore + * @param {GatewayStruct} user The user data + * @returns {this} + */ + init(user) { + this.id = user.id; + this.username = user.username; + this.hashtag = user.discriminator; + this.avatarHash = user.avatar; + this.isBot = user.bot; + this.system = user.system; + this.mfaEnabled = user.mfa_enabled; + this.locale = user.locale; + this.verified = user.verified; + this.email = user.email; + this.nitroType = user.premium_type; + if (user.flags) { + this.flags = new flags_1.UserFlags(user.flags); + } + if (user.public_flags) { + this.flags = new flags_1.UserFlags(user.public_flags); + } + return this; + } + /** + * Returns a user's avatar URL. Returns the default user avatar if the user does not have an avatar. + * *Note: the default user avatar only supports type {@link UserAvatarFormat.PNG}* + * @param {UserAvatarFormat} format The avatar image format + * @param {number} size The avatar image size + * @returns {string} + */ + avatarURL(format = Avatar_1.UserAvatarFormat.PNG, size) { + return Avatar_1.Avatar.userAvatarURL(this.avatarHash, this.id, this.hashtag, format, size); + } + /** + * Creates a new DM channel between this user and the bot user + * @returns {Promise} + */ + createDM() { + return this.bot.api.createDM(this.id); + } + /** + * Sends a DM message to the user from the bot user + * @param {any} args Identical to the arguments of {@link TextChannel.sendMessage} + * @returns {any} Identical to the return type of {@link TextChannel.sendMessage} + */ + sendMessage(...args) { + return __awaiter(this, void 0, void 0, function* () { + const dm = this.dm || (yield this.createDM()); + return dm.sendMessage(...args); + }); + } + /** + * Combines a user's username and hashtag and generates a full name + * @type {string} + * @example Day#0001 + */ + get fullName() { + return `${this.username}#${this.hashtag}`; + } + /** + * @ignore + * @returns {string} + */ + toString() { + return `<@${this.id}>`; + } +} +exports.User = User; diff --git a/lib/structures/Webhook.d.ts b/lib/structures/Webhook.d.ts new file mode 100644 index 000000000..82dbd8cef --- /dev/null +++ b/lib/structures/Webhook.d.ts @@ -0,0 +1,89 @@ +import { ImageURI } from './ImageURI'; +import { User } from './User'; +import { BaseGuildStruct, GatewayStruct } from './base'; +import { GuildChannel } from './channels'; +import { Bot } from '../bot'; +import { Snowflake } from '../types'; +/** + * Options used for creating webhooks + */ +export interface CreateWebhookOptions { + /** + * The name of the webhook (1-80 characters) + */ + name: string; + /** + * The image for the default webhook avatar + */ + avatar?: ImageURI; +} +/** + * Options used for modifying webhooks + */ +export interface ModifyWebhookOptions { + /** + * The modified default name of the webhook + */ + name?: string; + /** + * The modified image for the default webhook avatar + */ + avatar?: ImageURI | null; + /** + * The modified channel ID this webhook should be moved to + */ + channelId?: Snowflake; +} +/** + * The type of a webhook + */ +export declare enum WebhookType { + Incoming = 1, + ChannelFollower = 2 +} +/** + * Webhooks are a low-effort way to post messages to channels in Discord + */ +export declare class Webhook extends BaseGuildStruct { + /** + * The ID of the webhook + */ + id: Snowflake; + /** + * The type of the webhook + */ + type: WebhookType; + /** + * The channel this webhook is for + */ + readonly channel: GuildChannel; + /** + * The user this webhook was created by + */ + user: User; + /** + * The default name of the webhook + */ + name: string | null; + /** + * The default avatar hash of the webhook + */ + avatarHash: string | null; + /** + * The secure token of the webhook. Returned for incoming webhooks + */ + token: string | undefined; + constructor(bot: Bot, webhook: GatewayStruct, channel: GuildChannel); + /** + * @param {GatewayStruct} webhook The webhook object + * @returns {this} + * @ignore + */ + init(webhook: GatewayStruct): this; + /** + * Modifies a webhook by its ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modify(options: ModifyWebhookOptions): Promise; +} diff --git a/lib/structures/Webhook.js b/lib/structures/Webhook.js new file mode 100644 index 000000000..7c94bebf7 --- /dev/null +++ b/lib/structures/Webhook.js @@ -0,0 +1,59 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Webhook = exports.WebhookType = void 0; +const User_1 = require("./User"); +const base_1 = require("./base"); +/** + * The type of a webhook + */ +var WebhookType; +(function (WebhookType) { + WebhookType[WebhookType["Incoming"] = 1] = "Incoming"; + WebhookType[WebhookType["ChannelFollower"] = 2] = "ChannelFollower"; +})(WebhookType = exports.WebhookType || (exports.WebhookType = {})); +/** + * Webhooks are a low-effort way to post messages to channels in Discord + */ +class Webhook extends base_1.BaseGuildStruct { + constructor(bot, webhook, channel) { + super(bot, channel.guild, webhook); + this.channel = channel; + this.init(webhook); + } + /** + * @param {GatewayStruct} webhook The webhook object + * @returns {this} + * @ignore + */ + init(webhook) { + this.id = webhook.id; + this.type = webhook.type; + this.user = new User_1.User(this.bot, webhook.user); + this.name = webhook.name; + this.avatarHash = webhook.avatar; + this.token = webhook.token; + return this; + } + /** + * Modifies a webhook by its ID + * @param {ModifyWebhookOptions} options The options for the modified webhook + * @returns {Promise} + */ + modify(options) { + return __awaiter(this, void 0, void 0, function* () { + const webhook = yield this.bot.api.modifyWebhook(this.id, options); + this.update(webhook); + return webhook; + }); + } +} +exports.Webhook = Webhook; diff --git a/lib/structures/base/BaseGuildStruct.d.ts b/lib/structures/base/BaseGuildStruct.d.ts new file mode 100644 index 000000000..f17c9ff45 --- /dev/null +++ b/lib/structures/base/BaseGuildStruct.d.ts @@ -0,0 +1,14 @@ +import { BaseStruct, GatewayStruct } from './BaseStruct'; +import { Bot } from '../../bot'; +import { Guild } from '../guild'; +/** + * Basic structure every guild-related structure extends + * Handles the creation of a guild property and guild-related methods + */ +export declare class BaseGuildStruct extends BaseStruct { + /** + * The {@link Guild} associated to this structure + */ + guild: Guild; + constructor(bot: Bot, guild: Guild, structure: GatewayStruct); +} diff --git a/lib/structures/base/BaseGuildStruct.js b/lib/structures/base/BaseGuildStruct.js new file mode 100644 index 000000000..ff514e020 --- /dev/null +++ b/lib/structures/base/BaseGuildStruct.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseGuildStruct = void 0; +const BaseStruct_1 = require("./BaseStruct"); +/** + * Basic structure every guild-related structure extends + * Handles the creation of a guild property and guild-related methods + */ +class BaseGuildStruct extends BaseStruct_1.BaseStruct { + constructor(bot, guild, structure) { + super(bot, structure); + this.guild = guild; + } +} +exports.BaseGuildStruct = BaseGuildStruct; diff --git a/lib/structures/base/BaseStruct.d.ts b/lib/structures/base/BaseStruct.d.ts new file mode 100644 index 000000000..5020302ce --- /dev/null +++ b/lib/structures/base/BaseStruct.d.ts @@ -0,0 +1,50 @@ +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +/** + * Payload data received from the Discord gateway + */ +export declare type GatewayStruct = Record; +interface UpdateReturn { + before: T; + after: T; +} +/** + * A base structure with the ID field + */ +export declare type BaseStructWithId = BaseStruct & { + id: Snowflake | string; +}; +/** + * Basic structure that all other API-related structures extend + * Includes the bot property which every structure must have + */ +export declare class BaseStruct { + /** + * The {@link Bot} operating this structure + */ + bot: Bot; + /** + * The gateway structure that initialized this instance + * @ignore + */ + readonly structure: GatewayStruct; + constructor(bot: Bot, structure: GatewayStruct); + /** + * Virtual init method + * @param {GatewayStruct} _struct The structure to initialize from + * @returns {this} + */ + init(_struct: GatewayStruct): this; + /** + * Clone a structure + * @returns {this} + */ + clone(): this; + /** + * Update a structure and return its before and after versions + * @param {GatewayStruct} data The updated data + * @returns {UpdateReturn} + */ + update(data: GatewayStruct): UpdateReturn; +} +export {}; diff --git a/lib/structures/base/BaseStruct.js b/lib/structures/base/BaseStruct.js new file mode 100644 index 000000000..4f5151bf6 --- /dev/null +++ b/lib/structures/base/BaseStruct.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseStruct = void 0; +/** + * Basic structure that all other API-related structures extend + * Includes the bot property which every structure must have + */ +class BaseStruct { + constructor(bot, structure) { + this.bot = bot; + this.structure = structure; + } + /** + * Virtual init method + * @param {GatewayStruct} _struct The structure to initialize from + * @returns {this} + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + init(_struct) { + return this; + } + /** + * Clone a structure + * @returns {this} + */ + clone() { + return Object.assign(Object.create(this), this); + } + /** + * Update a structure and return its before and after versions + * @param {GatewayStruct} data The updated data + * @returns {UpdateReturn} + */ + update(data) { + const clone = this.clone(); + return { before: clone, after: this.init(Object.assign(Object.assign({}, this.structure), data)) }; + } +} +exports.BaseStruct = BaseStruct; diff --git a/lib/structures/base/index.d.ts b/lib/structures/base/index.d.ts new file mode 100644 index 000000000..2a38e4863 --- /dev/null +++ b/lib/structures/base/index.d.ts @@ -0,0 +1,2 @@ +export * from './BaseStruct'; +export * from './BaseGuildStruct'; diff --git a/lib/structures/base/index.js b/lib/structures/base/index.js new file mode 100644 index 000000000..bd6a0fae8 --- /dev/null +++ b/lib/structures/base/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./BaseStruct"), exports); +__exportStar(require("./BaseGuildStruct"), exports); diff --git a/lib/structures/channels/Channel.d.ts b/lib/structures/channels/Channel.d.ts new file mode 100644 index 000000000..772e0a910 --- /dev/null +++ b/lib/structures/channels/Channel.d.ts @@ -0,0 +1,45 @@ +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * The type of a channel + */ +export declare enum ChannelType { + GuildText = 0, + DM = 1, + GuildVoice = 2, + GroupDM = 3, + GuildCategory = 4, + GuildNews = 5, + GuildStore = 6 +} +/** + * Represents a guild or DM channel within Discord. + */ +export declare class Channel extends BaseStruct { + /** + * The ID of this channel + */ + id: Snowflake; + /** + * The type of this channel + */ + type: ChannelType; + constructor(bot: Bot, channel: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} channel The channel data + * @returns {this} + */ + init(channel: GatewayStruct): this; + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. Requires the {@link Permission.ManageChannels} permission for the guild + * @returns {Promise} + */ + delete(): Promise; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/channels/Channel.js b/lib/structures/channels/Channel.js new file mode 100644 index 000000000..9e34a9958 --- /dev/null +++ b/lib/structures/channels/Channel.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Channel = exports.ChannelType = void 0; +const base_1 = require("../base"); +/** + * The type of a channel + */ +var ChannelType; +(function (ChannelType) { + ChannelType[ChannelType["GuildText"] = 0] = "GuildText"; + ChannelType[ChannelType["DM"] = 1] = "DM"; + ChannelType[ChannelType["GuildVoice"] = 2] = "GuildVoice"; + ChannelType[ChannelType["GroupDM"] = 3] = "GroupDM"; + ChannelType[ChannelType["GuildCategory"] = 4] = "GuildCategory"; + ChannelType[ChannelType["GuildNews"] = 5] = "GuildNews"; + ChannelType[ChannelType["GuildStore"] = 6] = "GuildStore"; +})(ChannelType = exports.ChannelType || (exports.ChannelType = {})); +/** + * Represents a guild or DM channel within Discord. + */ +class Channel extends base_1.BaseStruct { + constructor(bot, channel) { + super(bot, channel); + this.init(channel); + } + /** + * @ignore + * @param {GatewayStruct} channel The channel data + * @returns {this} + */ + init(channel) { + this.id = channel.id; + this.type = channel.type; + return this; + } + /** + * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. Requires the {@link Permission.ManageChannels} permission for the guild + * @returns {Promise} + */ + delete() { + return this.bot.api.deleteChannel(this.id); + } + /** + * @ignore + * @returns {string} + */ + toString() { + return `<#${this.id}>`; + } +} +exports.Channel = Channel; diff --git a/lib/structures/channels/DMChannel.d.ts b/lib/structures/channels/DMChannel.d.ts new file mode 100644 index 000000000..39a28caa5 --- /dev/null +++ b/lib/structures/channels/DMChannel.d.ts @@ -0,0 +1,34 @@ +import { Channel } from './Channel'; +import { TextChannel } from './TextChannel'; +import { Bot } from '../../bot'; +import { ChannelMessagesController, ChannelPinsController } from '../../controllers/channel'; +import { Snowflake } from '../../types'; +import { User } from '../User'; +import { GatewayStruct } from '../base'; +import { Message, MessageData, MessageOptions, MessageEmbed } from '../message'; +/** + * Represents a private channel between the Bot and a User + */ +export declare class DMChannel extends Channel implements TextChannel { + /** @inheritDoc */ + lastMessageId: Snowflake | null | undefined; + /** @inheritDoc */ + messages: ChannelMessagesController; + /** @inheritDoc */ + pins: ChannelPinsController; + /** + * The recipient of the DM + */ + recipient: User; + constructor(bot: Bot, dmChannel: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} dmChannel The DM channel data + * @returns {this} + */ + init(dmChannel: GatewayStruct): this; + /** @inheritDoc */ + sendMessage(data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** @inheritDoc */ + triggerTyping(): Promise; +} diff --git a/lib/structures/channels/DMChannel.js b/lib/structures/channels/DMChannel.js new file mode 100644 index 000000000..4c3da06b5 --- /dev/null +++ b/lib/structures/channels/DMChannel.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DMChannel = void 0; +const Channel_1 = require("./Channel"); +const channel_1 = require("../../controllers/channel"); +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +/** + * Represents a private channel between the Bot and a User + */ +class DMChannel extends Channel_1.Channel { + constructor(bot, dmChannel) { + super(bot, dmChannel); + this.messages = new channel_1.ChannelMessagesController(this); + } + /** + * @ignore + * @param {GatewayStruct} dmChannel The DM channel data + * @returns {this} + */ + init(dmChannel) { + super.init(dmChannel); + this.pins = new channel_1.ChannelPinsController(this); + this.lastMessageId = dmChannel.last_message_id; + this.pins.lastPinTimestamp = new Timestamp_1.Timestamp(dmChannel.last_pin_timestamp); + this.recipient = new User_1.User(this.bot, dmChannel.recipients[0]); + return this; + } + /** @inheritDoc */ + sendMessage(data, options) { + return this.bot.api.sendMessage(this.id, data, options); + } + /** @inheritDoc */ + triggerTyping() { + return this.bot.api.triggerTextChannelTyping(this.id); + } +} +exports.DMChannel = DMChannel; diff --git a/lib/structures/channels/GuildCategoryChannel.d.ts b/lib/structures/channels/GuildCategoryChannel.d.ts new file mode 100644 index 000000000..9259dcdfd --- /dev/null +++ b/lib/structures/channels/GuildCategoryChannel.d.ts @@ -0,0 +1,13 @@ +import { GuildChannel } from './GuildChannel'; +import Collection from '../../Collection'; +import { Snowflake } from '../../types'; +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildCategory} + */ +export declare class GuildCategoryChannel extends GuildChannel { + /** + * Returns all {@link GuildChannel}s under this category channel + * @type {Collection} + */ + get children(): Collection; +} diff --git a/lib/structures/channels/GuildCategoryChannel.js b/lib/structures/channels/GuildCategoryChannel.js new file mode 100644 index 000000000..724cae2b0 --- /dev/null +++ b/lib/structures/channels/GuildCategoryChannel.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildCategoryChannel = void 0; +const GuildChannel_1 = require("./GuildChannel"); +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildCategory} + */ +class GuildCategoryChannel extends GuildChannel_1.GuildChannel { + /** + * Returns all {@link GuildChannel}s under this category channel + * @type {Collection} + */ + get children() { + return this.guild.channels.cache.filter(c => { var _a; return ((_a = c.parent) === null || _a === void 0 ? void 0 : _a.id) === this.id; }); + } +} +exports.GuildCategoryChannel = GuildCategoryChannel; diff --git a/lib/structures/channels/GuildChannel.d.ts b/lib/structures/channels/GuildChannel.d.ts new file mode 100644 index 000000000..0a6542b6e --- /dev/null +++ b/lib/structures/channels/GuildChannel.d.ts @@ -0,0 +1,123 @@ +import { Channel, ChannelType } from './Channel'; +import { GuildCategoryChannel } from './GuildCategoryChannel'; +import { Bot } from '../../bot'; +import { ChannelPermissionsController } from '../../controllers/channel'; +import { GuildChannelInvitesController, GuildChannelWebhooksController } from '../../controllers/guild'; +import { Snowflake } from '../../types'; +import { GatewayStruct } from '../base'; +import { PermissibleType, PermissionOverwriteFlags } from '../flags'; +import { Guild } from '../guild'; +/** + * Options used when modifying a {@link GuildChannel} + */ +export interface GuildChannelOptions { + /** + * The new guild channel's name + */ + name?: string; + /** + * The new type of the guild channel. + * Can only change between {@link ChannelType.GuildText} and {@link ChannelType.GuildNews} + */ + type?: ChannelType.GuildText | ChannelType.GuildNews; + /** + * The new topic of the guild channel + */ + topic?: string | null; + /** + * Whether the guild channel should be marked as nsfw + */ + nsfw?: boolean | null; + /** + * The guild channel's slow mode timeout (for {@link GuildTextChannel}) + */ + slowModeTimeout?: number | null; + /** + * The guild channel's audio bit rate (for {@link GuildVoiceChannel}) + */ + bitrate?: number | null; + /** + * The guild channel's user limit (for {@link GuildVoiceChannel}) + */ + userLimit?: number | null; +} +/** + * Options for when creating new guild channels + */ +export interface CreateGuildChannelOptions extends GuildChannelOptions { + /** + * The name of the new channel + */ + name: string; + /** + * The position of the new channel + */ + position?: number; + /** + * The permissions of the new channel. + * Object of user / roles IDs and their permissions for the new channel + */ + permissions?: Record; + /** + * The ID of the parent category for the channel + */ + parentId?: Snowflake; +} +/** + * Represents a channel found in a guild of any type + */ +export declare class GuildChannel extends Channel { + /** + * The guild this channel is associated to + */ + guild: Guild; + /** + * Sorting position of the channel + */ + position: number; + /** + * This guild channel's permission overwrites controller + */ + permissions: ChannelPermissionsController; + /** + * The name of the channel + */ + name: string; + /** + * The topic of the channel. + * Possibly null if channel does not have a topic + */ + topic: string | null; + /** + * The guild channel's invites controller + */ + invites: GuildChannelInvitesController; + /** + * The ID of this channel's parent category + */ + parentId: Snowflake | undefined | null; + /** + * The guild channel's webhooks controller + */ + webhooks: GuildChannelWebhooksController; + constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} guildChannel The guild channel data + * @returns {this} + */ + init(guildChannel: GatewayStruct): this; + /** + * Parent {@link GuildCategoryChannel} of this channel. + * Possibly null if this channel does not have a parent category channel, or the category is not cached + */ + get parent(): GuildCategoryChannel | null; + /** + * Update a channel's settings. Requires the {@link Permission.ManageChannels} permission for the guild. + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modify(options: GuildChannelOptions): Promise; +} diff --git a/lib/structures/channels/GuildChannel.js b/lib/structures/channels/GuildChannel.js new file mode 100644 index 000000000..07740524c --- /dev/null +++ b/lib/structures/channels/GuildChannel.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildChannel = void 0; +const Channel_1 = require("./Channel"); +const channel_1 = require("../../controllers/channel"); +const guild_1 = require("../../controllers/guild"); +const PermissionOverwrite_1 = require("../PermissionOverwrite"); +/** + * Represents a channel found in a guild of any type + */ +class GuildChannel extends Channel_1.Channel { + constructor(bot, guildChannel, guild) { + super(bot, guildChannel); + this.guild = guild; + this.invites = new guild_1.GuildChannelInvitesController(this); + this.webhooks = new guild_1.GuildChannelWebhooksController(this); + } + /** + * @ignore + * @param {GatewayStruct} guildChannel The guild channel data + * @returns {this} + */ + init(guildChannel) { + super.init(guildChannel); + this.position = guildChannel.position; + this.permissions = new channel_1.ChannelPermissionsController(this); + if (guildChannel.permission_overwrites) { + this.permissions.cache.addMany(guildChannel.permission_overwrites.map((permission) => new PermissionOverwrite_1.PermissionOverwrite(this.bot, permission, this))); + } + this.name = guildChannel.name; + this.topic = guildChannel.topic; + this.parentId = guildChannel.parent_id; + return this; + } + /** + * Parent {@link GuildCategoryChannel} of this channel. + * Possibly null if this channel does not have a parent category channel, or the category is not cached + */ + get parent() { + if (!this.parentId) + return null; + return this.guild.channels.cache.get(this.parentId) || null; + } + /** + * Update a channel's settings. Requires the {@link Permission.ManageChannels} permission for the guild. + * @param {GuildChannelOptions} options The modified channel's settings + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuildChannel(this.id, options); + } +} +exports.GuildChannel = GuildChannel; diff --git a/lib/structures/channels/GuildTextChannel.d.ts b/lib/structures/channels/GuildTextChannel.d.ts new file mode 100644 index 000000000..11d1ac624 --- /dev/null +++ b/lib/structures/channels/GuildTextChannel.d.ts @@ -0,0 +1,41 @@ +import { GuildChannel } from './GuildChannel'; +import { TextChannel } from './TextChannel'; +import { Bot } from '../../bot'; +import { ChannelMessagesController, ChannelPinsController } from '../../controllers/channel'; +import { Snowflake } from '../../types'; +import { GatewayStruct } from '../base'; +import { Guild } from '../guild'; +import { Message, MessageOptions, MessageData, MessageEmbed } from '../message'; +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildText} + */ +export declare class GuildTextChannel extends GuildChannel implements TextChannel { + /** @inheritDoc */ + nsfw: boolean | undefined; + /** @inheritDoc */ + lastMessageId: Snowflake | null | undefined; + /** @inheritDoc */ + slowModeTimeout: number; + /** @inheritDoc */ + messages: ChannelMessagesController; + /** @inheritDoc */ + pins: ChannelPinsController; + constructor(bot: Bot, textChannel: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} textChannel The text channel data + * @returns {this} + */ + init(textChannel: GatewayStruct): this; + /** @inheritDoc */ + sendMessage(data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(messages: Snowflake[]): Promise; + /** @inheritDoc */ + triggerTyping(): Promise; +} diff --git a/lib/structures/channels/GuildTextChannel.js b/lib/structures/channels/GuildTextChannel.js new file mode 100644 index 000000000..11ada62a3 --- /dev/null +++ b/lib/structures/channels/GuildTextChannel.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildTextChannel = void 0; +const GuildChannel_1 = require("./GuildChannel"); +const channel_1 = require("../../controllers/channel"); +const Timestamp_1 = require("../Timestamp"); +/** + * Represents a channel found in a guild of type {@link ChannelType.GuildText} + */ +class GuildTextChannel extends GuildChannel_1.GuildChannel { + // Guild parameter used when creating the channel from the Guild constructor + constructor(bot, textChannel, guild) { + super(bot, textChannel, guild); + this.messages = new channel_1.ChannelMessagesController(this); + } + /** + * @ignore + * @param {GatewayStruct} textChannel The text channel data + * @returns {this} + */ + init(textChannel) { + super.init(textChannel); + this.pins = new channel_1.ChannelPinsController(this); + this.nsfw = textChannel.nsfw; + this.lastMessageId = textChannel.last_message_id; + this.slowModeTimeout = textChannel.rate_limit_per_user; + this.pins.lastPinTimestamp = new Timestamp_1.Timestamp(textChannel.last_pin_timestamp); + return this; + } + /** @inheritDoc */ + sendMessage(data, options) { + return this.bot.api.sendMessage(this.id, data, options); + } + /** + * Deletes multiple messages in a single request. + * Requires the {@link Permission.ManageMessages} permission + * @param {Snowflake[]} messages An array of the messages IDs you wish to delete + * @returns {Promise} + */ + bulkDeleteMessages(messages) { + return this.bot.api.bulkDeleteMessages(this.id, messages); + } + /** @inheritDoc */ + triggerTyping() { + return this.bot.api.triggerTextChannelTyping(this.id); + } +} +exports.GuildTextChannel = GuildTextChannel; diff --git a/lib/structures/channels/GuildVoiceChannel.d.ts b/lib/structures/channels/GuildVoiceChannel.d.ts new file mode 100644 index 000000000..f9d26bd83 --- /dev/null +++ b/lib/structures/channels/GuildVoiceChannel.d.ts @@ -0,0 +1,12 @@ +import { GuildChannel } from './GuildChannel'; +import { Bot } from '../../bot'; +import { GatewayStruct } from '../base'; +import { Guild } from '../guild/Guild'; +import { Connection } from '../voice/Connection'; +/** + * Represents a Voice channel + */ +export declare class GuildVoiceChannel extends GuildChannel { + constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild); + join(mute?: boolean, deaf?: boolean): Promise; +} diff --git a/lib/structures/channels/GuildVoiceChannel.js b/lib/structures/channels/GuildVoiceChannel.js new file mode 100644 index 000000000..60c8c9a53 --- /dev/null +++ b/lib/structures/channels/GuildVoiceChannel.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildVoiceChannel = void 0; +const GuildChannel_1 = require("./GuildChannel"); +/** + * Represents a Voice channel + */ +class GuildVoiceChannel extends GuildChannel_1.GuildChannel { + constructor(bot, guildChannel, guild) { + super(bot, guildChannel, guild); + } + join(mute, deaf) { + return this.guild.voice.join(this.id, { mute, deaf }); + } +} +exports.GuildVoiceChannel = GuildVoiceChannel; diff --git a/lib/structures/channels/TextChannel.d.ts b/lib/structures/channels/TextChannel.d.ts new file mode 100644 index 000000000..268530f63 --- /dev/null +++ b/lib/structures/channels/TextChannel.d.ts @@ -0,0 +1,54 @@ +import { DMChannel } from './DMChannel'; +import { GuildTextChannel } from './GuildTextChannel'; +import { ChannelMessagesController, ChannelPinsController } from '../../controllers/channel'; +import { Snowflake } from '../../types'; +import { Message, MessageData, MessageOptions, MessageEmbed } from '../message'; +/** + * Text based channels of Discord + */ +export declare type TextBasedChannel = GuildTextChannel | DMChannel; +/** + * Abstract class that all text-based channels implement + */ +export interface TextChannel { + /** + * The ID of the last message sent in this channel. + * May not point to an existing or valid message + */ + lastMessageId: Snowflake | null | undefined; + /** + * The text channel's messages controller + */ + messages: ChannelMessagesController; + /** + * The text channel's pinned messages controller + */ + pins: ChannelPinsController; + /** + * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. If operating on a {@link GuildTextChannel}, this endpoint requires the {@link Permission.SendMessages} permission to be present on the current user. If the {@link MessageOptions.tts} field is set to true, the {@link Permission.SendTTSMessages} permission is required for the message to be spoken + * @param {string | MessageData | MessageEmbed} data The message data. + * Can be: + * 1. Raw content to be sent as a message + * @example ```typescript + * channel.sendMessage('Hello World!'); + * ``` + * 2. A {@link MessageData} object, containing content and/or embed + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', embed: { title: 'My Embed!' } }); + * ``` + * @example ```typescript + * channel.sendMessage({ content: 'Hello World!', files: [{ path: './my_image.png', name: 'image.png' }] }); + * ``` + * 3. A {@link MessageEmbed} instance + * @param {MessageOptions} options + * @returns {Promise} + */ + sendMessage(data: string | MessageData | MessageEmbed, options?: MessageOptions): Promise; + /** + * Posts a typing indicator for a specified text channel. + * Useful when the bot is responding to a command and expects the computation to take a few seconds. + * This method may be called to let the user know that the bot is processing their message. + * @returns {Promise} + */ + triggerTyping(): Promise; +} diff --git a/lib/structures/channels/TextChannel.js b/lib/structures/channels/TextChannel.js new file mode 100644 index 000000000..c8ad2e549 --- /dev/null +++ b/lib/structures/channels/TextChannel.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/structures/channels/index.d.ts b/lib/structures/channels/index.d.ts new file mode 100644 index 000000000..b64eb6a85 --- /dev/null +++ b/lib/structures/channels/index.d.ts @@ -0,0 +1,8 @@ +export * from './Channel'; +export * from './DMChannel'; +export * from './GuildCategoryChannel'; +export * from './GuildChannel'; +export * from './GuildTextChannel'; +export * from './TextChannel'; +export * from './GuildVoiceChannel'; +export * from './utils'; diff --git a/lib/structures/channels/index.js b/lib/structures/channels/index.js new file mode 100644 index 000000000..89c7e28f8 --- /dev/null +++ b/lib/structures/channels/index.js @@ -0,0 +1,20 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Channel"), exports); +__exportStar(require("./DMChannel"), exports); +__exportStar(require("./GuildCategoryChannel"), exports); +__exportStar(require("./GuildChannel"), exports); +__exportStar(require("./GuildTextChannel"), exports); +__exportStar(require("./TextChannel"), exports); +__exportStar(require("./GuildVoiceChannel"), exports); +__exportStar(require("./utils"), exports); diff --git a/lib/structures/channels/utils/ChannelUtils.d.ts b/lib/structures/channels/utils/ChannelUtils.d.ts new file mode 100644 index 000000000..b9b654956 --- /dev/null +++ b/lib/structures/channels/utils/ChannelUtils.d.ts @@ -0,0 +1,54 @@ +import { Bot } from '../../../bot'; +import { GatewayStruct } from '../../base'; +import { Guild } from '../../guild'; +import { Channel } from '../Channel'; +import { DMChannel } from '../DMChannel'; +import { GuildChannel } from '../GuildChannel'; +/** + * Handles channel-related util methods + */ +export declare class ChannelUtils { + /** + * Creates a new {@link Channel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild | undefined} guild_ The guild associated to the channel + * @returns {Promise} + */ + static create(bot: Bot, data: GatewayStruct, guild_?: Guild): Promise; + /** + * Creates a new {@link GuildChannel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild} guild The guild associated to the channel + * @returns {Promise} + */ + static createGuildChannel(bot: Bot, data: GatewayStruct, guild: Guild): GuildChannel; + /** + * Creates a new {@link DMChannel} instance + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @returns {Promise} + */ + static createDMChannel(bot: Bot, data: GatewayStruct): DMChannel; + /** + * Retrieves the guild hidden in a channel instance's structure + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel instance + * @returns {Promise} + */ + static getChannelGuild(bot: Bot, channel: Channel | GatewayStruct): Promise; + /** + * Caches a channel in the correct Collection + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to cache + * @param {boolean} force Whether or not to force cache DM channels if already cached + */ + static cache(bot: Bot, channel: Channel, force?: boolean): void; + /** + * Deletes a channel from the cache + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to delete + */ + static delete(bot: Bot, channel: Channel): void; +} diff --git a/lib/structures/channels/utils/ChannelUtils.js b/lib/structures/channels/utils/ChannelUtils.js new file mode 100644 index 000000000..af67111cf --- /dev/null +++ b/lib/structures/channels/utils/ChannelUtils.js @@ -0,0 +1,126 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChannelUtils = void 0; +const Channel_1 = require("../Channel"); +const DMChannel_1 = require("../DMChannel"); +const GuildCategoryChannel_1 = require("../GuildCategoryChannel"); +const GuildChannel_1 = require("../GuildChannel"); +const GuildTextChannel_1 = require("../GuildTextChannel"); +const GuildVoiceChannel_1 = require("../GuildVoiceChannel"); +/** + * Handles channel-related util methods + */ +class ChannelUtils { + /** + * Creates a new {@link Channel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild | undefined} guild_ The guild associated to the channel + * @returns {Promise} + */ + static create(bot, data, guild_) { + return __awaiter(this, void 0, void 0, function* () { + const { guild_id: guildId } = data; + const guild = guild_ || (guildId && (yield bot.guilds.get(guildId))); + return guild + ? ChannelUtils.createGuildChannel(bot, data, guild) + : ChannelUtils.createDMChannel(bot, data); + }); + } + /** + * Creates a new {@link GuildChannel} instance, initialized relatively to its type + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @param {Guild} guild The guild associated to the channel + * @returns {Promise} + */ + static createGuildChannel(bot, data, guild) { + let channel; + switch (data.type) { + case Channel_1.ChannelType.GuildText: + channel = new GuildTextChannel_1.GuildTextChannel(bot, data, guild); + break; + case Channel_1.ChannelType.GuildCategory: + channel = new GuildCategoryChannel_1.GuildCategoryChannel(bot, data, guild); + break; + case Channel_1.ChannelType.GuildVoice: + channel = new GuildVoiceChannel_1.GuildVoiceChannel(bot, data, guild); + break; + case Channel_1.ChannelType.GuildNews: + case Channel_1.ChannelType.GuildStore: + channel = new GuildChannel_1.GuildChannel(bot, data, guild); + } + if (!channel) { + throw new TypeError('Invalid guild channel type!'); + } + return channel; + } + /** + * Creates a new {@link DMChannel} instance + * @param {Bot} bot The bot instance + * @param {GatewayStruct} data The channel data received from the gateway + * @returns {Promise} + */ + static createDMChannel(bot, data) { + const { guild_id: guildId } = data; + if (guildId) { + throw new TypeError('DM channels cannot have a guild ID!'); + } + return new DMChannel_1.DMChannel(bot, data); + } + /** + * Retrieves the guild hidden in a channel instance's structure + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel instance + * @returns {Promise} + */ + static getChannelGuild(bot, channel) { + const { guild_id: guildId } = channel instanceof Channel_1.Channel ? channel.structure : channel; + if (!guildId) { + throw new TypeError('No guild ID specified for channel!'); + } + return bot.guilds.get(guildId); + } + /** + * Caches a channel in the correct Collection + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to cache + * @param {boolean} force Whether or not to force cache DM channels if already cached + */ + static cache(bot, channel, force = false) { + if (channel instanceof GuildChannel_1.GuildChannel) { + channel.guild.channels.cache.add(channel); + } + if (channel instanceof GuildChannel_1.GuildChannel || + (channel instanceof DMChannel_1.DMChannel && (force || !bot.channels.cache.has(channel.id)))) { + bot.channels.cache.add(channel); + } + if (channel instanceof DMChannel_1.DMChannel) { + const recipient = bot.users.cache.get(channel.recipient.id); + if (!recipient) + return; + recipient.dm = channel; + } + } + /** + * Deletes a channel from the cache + * @param {Bot} bot The bot instance + * @param {Channel} channel The channel you wish to delete + */ + static delete(bot, channel) { + if (channel instanceof GuildChannel_1.GuildChannel) { + channel.guild.channels.cache.delete(channel.id); + } + bot.channels.cache.delete(channel.id); + } +} +exports.ChannelUtils = ChannelUtils; diff --git a/lib/structures/channels/utils/index.d.ts b/lib/structures/channels/utils/index.d.ts new file mode 100644 index 000000000..8ecfb6ea6 --- /dev/null +++ b/lib/structures/channels/utils/index.d.ts @@ -0,0 +1 @@ +export * from './ChannelUtils'; diff --git a/lib/structures/channels/utils/index.js b/lib/structures/channels/utils/index.js new file mode 100644 index 000000000..2863faa30 --- /dev/null +++ b/lib/structures/channels/utils/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./ChannelUtils"), exports); diff --git a/lib/structures/flags/Flags.d.ts b/lib/structures/flags/Flags.d.ts new file mode 100644 index 000000000..964fadcd6 --- /dev/null +++ b/lib/structures/flags/Flags.d.ts @@ -0,0 +1,28 @@ +/** + * Manager class responsible for retrieving data off of bit-wise flags + * @template T + */ +export declare class Flags { + /** + * Integer of the flags + */ + protected readonly flags: number; + constructor(flags: number); + /** + * Whether a specific flag is included in this instance's flags + * @param {T} flag The flag to check if included + * @returns {boolean} + */ + has(flag: T): boolean; + /** + * Returns the bits of the flags this instance contains + * @type {number} + */ + get bits(): number; + /** + * Creates a new instance of {@link Flags} based on given flags + * @param {T[]} flags An array of all flags the {@link Flags} instance should contain + * @returns {Flags} + */ + static from(...flags: T[]): Flags; +} diff --git a/lib/structures/flags/Flags.js b/lib/structures/flags/Flags.js new file mode 100644 index 000000000..17ae97fac --- /dev/null +++ b/lib/structures/flags/Flags.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Flags = void 0; +/** + * Manager class responsible for retrieving data off of bit-wise flags + * @template T + */ +class Flags { + constructor(flags) { + this.flags = flags; + } + /** + * Whether a specific flag is included in this instance's flags + * @param {T} flag The flag to check if included + * @returns {boolean} + */ + has(flag) { + return (this.flags & flag) === flag; + } + /** + * Returns the bits of the flags this instance contains + * @type {number} + */ + get bits() { + return this.flags; + } + /** + * Creates a new instance of {@link Flags} based on given flags + * @param {T[]} flags An array of all flags the {@link Flags} instance should contain + * @returns {Flags} + */ + static from(...flags) { + const bits = flags.reduce((totalBits, bit) => totalBits | bit, 0); + return new Flags(bits); + } +} +exports.Flags = Flags; diff --git a/lib/structures/flags/GuildSystemChannelFlags.d.ts b/lib/structures/flags/GuildSystemChannelFlags.d.ts new file mode 100644 index 000000000..77d1c3ed4 --- /dev/null +++ b/lib/structures/flags/GuildSystemChannelFlags.d.ts @@ -0,0 +1,17 @@ +import { Flags } from './Flags'; +/** + * All guild system channel flags + * https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags + */ +export declare enum SystemChannelFlag { + /** + * Suppress member join notifications + */ + SuppressJoinNotifications = 1, + /** + * Suppress server boost notifications + */ + SuppressBoosts = 2 +} +export declare class GuildSystemChannelFlags extends Flags { +} diff --git a/lib/structures/flags/GuildSystemChannelFlags.js b/lib/structures/flags/GuildSystemChannelFlags.js new file mode 100644 index 000000000..384c7c59c --- /dev/null +++ b/lib/structures/flags/GuildSystemChannelFlags.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildSystemChannelFlags = exports.SystemChannelFlag = void 0; +const Flags_1 = require("./Flags"); +/** + * All guild system channel flags + * https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags + */ +var SystemChannelFlag; +(function (SystemChannelFlag) { + /** + * Suppress member join notifications + */ + SystemChannelFlag[SystemChannelFlag["SuppressJoinNotifications"] = 1] = "SuppressJoinNotifications"; + /** + * Suppress server boost notifications + */ + SystemChannelFlag[SystemChannelFlag["SuppressBoosts"] = 2] = "SuppressBoosts"; +})(SystemChannelFlag = exports.SystemChannelFlag || (exports.SystemChannelFlag = {})); +class GuildSystemChannelFlags extends Flags_1.Flags { +} +exports.GuildSystemChannelFlags = GuildSystemChannelFlags; diff --git a/lib/structures/flags/MessageFlags.d.ts b/lib/structures/flags/MessageFlags.d.ts new file mode 100644 index 000000000..9409d83e4 --- /dev/null +++ b/lib/structures/flags/MessageFlags.d.ts @@ -0,0 +1,29 @@ +import { Flags } from './Flags'; +/** + * All message flags + * https://discord.com/developers/docs/resources/channel#message-object-message-flags + */ +export declare enum MessageFlag { + /** + * This message has been published to subscribed channels (via Channel Following) + */ + Crossposted = 1, + /** + * This message originated from a message in another channel (via Channel Following) + */ + IsCrosspost = 2, + /** + * Do not include any embeds when serializing this message + */ + SuppressEmbeds = 4, + /** + * The source message for this crosspost has been deleted (via Channel Following) + */ + SourceMessageDeleted = 8, + /** + * This message came from the urgent message system + */ + Urgent = 16 +} +export declare class MessageFlags extends Flags { +} diff --git a/lib/structures/flags/MessageFlags.js b/lib/structures/flags/MessageFlags.js new file mode 100644 index 000000000..988265b2f --- /dev/null +++ b/lib/structures/flags/MessageFlags.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageFlags = exports.MessageFlag = void 0; +const Flags_1 = require("./Flags"); +/** + * All message flags + * https://discord.com/developers/docs/resources/channel#message-object-message-flags + */ +var MessageFlag; +(function (MessageFlag) { + /** + * This message has been published to subscribed channels (via Channel Following) + */ + MessageFlag[MessageFlag["Crossposted"] = 1] = "Crossposted"; + /** + * This message originated from a message in another channel (via Channel Following) + */ + MessageFlag[MessageFlag["IsCrosspost"] = 2] = "IsCrosspost"; + /** + * Do not include any embeds when serializing this message + */ + MessageFlag[MessageFlag["SuppressEmbeds"] = 4] = "SuppressEmbeds"; + /** + * The source message for this crosspost has been deleted (via Channel Following) + */ + MessageFlag[MessageFlag["SourceMessageDeleted"] = 8] = "SourceMessageDeleted"; + /** + * This message came from the urgent message system + */ + MessageFlag[MessageFlag["Urgent"] = 16] = "Urgent"; +})(MessageFlag = exports.MessageFlag || (exports.MessageFlag = {})); +class MessageFlags extends Flags_1.Flags { +} +exports.MessageFlags = MessageFlags; diff --git a/lib/structures/flags/PermissionFlags.d.ts b/lib/structures/flags/PermissionFlags.d.ts new file mode 100644 index 000000000..99f334c08 --- /dev/null +++ b/lib/structures/flags/PermissionFlags.d.ts @@ -0,0 +1,82 @@ +import { Flags } from './Flags'; +import { Snowflake } from '../../types'; +/** + * All Discord permission flags + * https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags + */ +export declare enum Permission { + CreateInstantInvite = 1, + KickMembers = 2, + BanMembers = 4, + Administrator = 8, + ManageChannels = 16, + ManageGuild = 32, + AddReactions = 64, + ViewAuditLog = 128, + PrioritySpeaker = 256, + Stream = 512, + ViewChannel = 1024, + SendMessages = 2048, + SendTTSMessages = 4096, + ManageMessages = 8192, + EmbedLinks = 16384, + AttachFiles = 32768, + ReadMessageHistory = 65536, + MentionEveryone = 131072, + UseExternalEmojis = 262144, + ViewGuildInsights = 524288, + Connect = 1048576, + Speak = 2097152, + MuteMembers = 4194304, + DeafenMembers = 8388608, + MoveMembers = 16777216, + UseVAD = 33554432, + ChangeNickname = 67108864, + ManageNicknames = 134217728, + ManageRoles = 268435456, + ManageWebhooks = 536870912, + ManageEmojis = 1073741824 +} +/** + * The type of the permission overwrite + */ +export declare enum PermissibleType { + /** + * The permission overwrite is for a member + */ + Member = "member", + /** + * The permission overwrite is for a role + */ + Role = "role" +} +/** + * Data about the member or role which will be modified in the guild channel's permission + */ +export interface Permissible { + /** + * The ID of the member or role + */ + id: Snowflake; + /** + * Whether this is a 'member' or a 'role' + */ + type: PermissibleType; +} +/** + * Used to overwrite permissions for a guild channel. + * Contains the allowed and denied permission flags for a specific member or a role + */ +export interface PermissionOverwriteFlags { + /** + * The allowed permission flags for the member or role in a guild channel + */ + allow?: PermissionFlags; + /** + * The denied permission flags for the member or role in a guild channel + */ + deny?: PermissionFlags; +} +export declare class PermissionFlags extends Flags { + constructor(flags: string); +} diff --git a/lib/structures/flags/PermissionFlags.js b/lib/structures/flags/PermissionFlags.js new file mode 100644 index 000000000..ce1649397 --- /dev/null +++ b/lib/structures/flags/PermissionFlags.js @@ -0,0 +1,63 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionFlags = exports.PermissibleType = exports.Permission = void 0; +const Flags_1 = require("./Flags"); +/** + * All Discord permission flags + * https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags + */ +var Permission; +(function (Permission) { + Permission[Permission["CreateInstantInvite"] = 1] = "CreateInstantInvite"; + Permission[Permission["KickMembers"] = 2] = "KickMembers"; + Permission[Permission["BanMembers"] = 4] = "BanMembers"; + Permission[Permission["Administrator"] = 8] = "Administrator"; + Permission[Permission["ManageChannels"] = 16] = "ManageChannels"; + Permission[Permission["ManageGuild"] = 32] = "ManageGuild"; + Permission[Permission["AddReactions"] = 64] = "AddReactions"; + Permission[Permission["ViewAuditLog"] = 128] = "ViewAuditLog"; + Permission[Permission["PrioritySpeaker"] = 256] = "PrioritySpeaker"; + Permission[Permission["Stream"] = 512] = "Stream"; + Permission[Permission["ViewChannel"] = 1024] = "ViewChannel"; + Permission[Permission["SendMessages"] = 2048] = "SendMessages"; + Permission[Permission["SendTTSMessages"] = 4096] = "SendTTSMessages"; + Permission[Permission["ManageMessages"] = 8192] = "ManageMessages"; + Permission[Permission["EmbedLinks"] = 16384] = "EmbedLinks"; + Permission[Permission["AttachFiles"] = 32768] = "AttachFiles"; + Permission[Permission["ReadMessageHistory"] = 65536] = "ReadMessageHistory"; + Permission[Permission["MentionEveryone"] = 131072] = "MentionEveryone"; + Permission[Permission["UseExternalEmojis"] = 262144] = "UseExternalEmojis"; + Permission[Permission["ViewGuildInsights"] = 524288] = "ViewGuildInsights"; + Permission[Permission["Connect"] = 1048576] = "Connect"; + Permission[Permission["Speak"] = 2097152] = "Speak"; + Permission[Permission["MuteMembers"] = 4194304] = "MuteMembers"; + Permission[Permission["DeafenMembers"] = 8388608] = "DeafenMembers"; + Permission[Permission["MoveMembers"] = 16777216] = "MoveMembers"; + Permission[Permission["UseVAD"] = 33554432] = "UseVAD"; + Permission[Permission["ChangeNickname"] = 67108864] = "ChangeNickname"; + Permission[Permission["ManageNicknames"] = 134217728] = "ManageNicknames"; + Permission[Permission["ManageRoles"] = 268435456] = "ManageRoles"; + Permission[Permission["ManageWebhooks"] = 536870912] = "ManageWebhooks"; + Permission[Permission["ManageEmojis"] = 1073741824] = "ManageEmojis"; +})(Permission = exports.Permission || (exports.Permission = {})); +/** + * The type of the permission overwrite + */ +var PermissibleType; +(function (PermissibleType) { + /** + * The permission overwrite is for a member + */ + PermissibleType["Member"] = "member"; + /** + * The permission overwrite is for a role + */ + PermissibleType["Role"] = "role"; +})(PermissibleType = exports.PermissibleType || (exports.PermissibleType = {})); +class PermissionFlags extends Flags_1.Flags { + // Permission flags in Discord are now received in serialized strings + constructor(flags) { + super(parseInt(flags)); + } +} +exports.PermissionFlags = PermissionFlags; diff --git a/lib/structures/flags/PresenceActivityFlags.d.ts b/lib/structures/flags/PresenceActivityFlags.d.ts new file mode 100644 index 000000000..372136581 --- /dev/null +++ b/lib/structures/flags/PresenceActivityFlags.d.ts @@ -0,0 +1,15 @@ +import { Flags } from './Flags'; +/** + * All Discord presence activity flags + * https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags + */ +export declare enum PresenceActivity { + Instance = 1, + Join = 2, + Spectate = 4, + JoinRequest = 8, + Sync = 16, + Play = 32 +} +export declare class PresenceActivityFlags extends Flags { +} diff --git a/lib/structures/flags/PresenceActivityFlags.js b/lib/structures/flags/PresenceActivityFlags.js new file mode 100644 index 000000000..d546c4eac --- /dev/null +++ b/lib/structures/flags/PresenceActivityFlags.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PresenceActivityFlags = exports.PresenceActivity = void 0; +const Flags_1 = require("./Flags"); +/** + * All Discord presence activity flags + * https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags + */ +var PresenceActivity; +(function (PresenceActivity) { + PresenceActivity[PresenceActivity["Instance"] = 1] = "Instance"; + PresenceActivity[PresenceActivity["Join"] = 2] = "Join"; + PresenceActivity[PresenceActivity["Spectate"] = 4] = "Spectate"; + PresenceActivity[PresenceActivity["JoinRequest"] = 8] = "JoinRequest"; + PresenceActivity[PresenceActivity["Sync"] = 16] = "Sync"; + PresenceActivity[PresenceActivity["Play"] = 32] = "Play"; +})(PresenceActivity = exports.PresenceActivity || (exports.PresenceActivity = {})); +class PresenceActivityFlags extends Flags_1.Flags { +} +exports.PresenceActivityFlags = PresenceActivityFlags; diff --git a/lib/structures/flags/UserFlags.d.ts b/lib/structures/flags/UserFlags.d.ts new file mode 100644 index 000000000..d92b858a9 --- /dev/null +++ b/lib/structures/flags/UserFlags.d.ts @@ -0,0 +1,23 @@ +import { Flags } from './Flags'; +/** + * All Discord user flags (profile badges) + * https://discord.com/developers/docs/resources/user#user-object-user-flags + */ +export declare enum UserFlag { + None = 0, + DiscordEmployee = 1, + DiscordPartner = 2, + HypeSquadEvents = 4, + BugHunterLevel1 = 8, + HouseBravery = 64, + HouseBrilliance = 128, + HouseBalance = 256, + EarlySupporter = 512, + TeamUser = 1024, + System = 4096, + BugHunterLevel2 = 16384, + VerifiedBot = 65536, + VerifiedBotDeveloper = 131072 +} +export declare class UserFlags extends Flags { +} diff --git a/lib/structures/flags/UserFlags.js b/lib/structures/flags/UserFlags.js new file mode 100644 index 000000000..df56de021 --- /dev/null +++ b/lib/structures/flags/UserFlags.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserFlags = exports.UserFlag = void 0; +const Flags_1 = require("./Flags"); +/** + * All Discord user flags (profile badges) + * https://discord.com/developers/docs/resources/user#user-object-user-flags + */ +var UserFlag; +(function (UserFlag) { + UserFlag[UserFlag["None"] = 0] = "None"; + UserFlag[UserFlag["DiscordEmployee"] = 1] = "DiscordEmployee"; + UserFlag[UserFlag["DiscordPartner"] = 2] = "DiscordPartner"; + UserFlag[UserFlag["HypeSquadEvents"] = 4] = "HypeSquadEvents"; + UserFlag[UserFlag["BugHunterLevel1"] = 8] = "BugHunterLevel1"; + UserFlag[UserFlag["HouseBravery"] = 64] = "HouseBravery"; + UserFlag[UserFlag["HouseBrilliance"] = 128] = "HouseBrilliance"; + UserFlag[UserFlag["HouseBalance"] = 256] = "HouseBalance"; + UserFlag[UserFlag["EarlySupporter"] = 512] = "EarlySupporter"; + UserFlag[UserFlag["TeamUser"] = 1024] = "TeamUser"; + UserFlag[UserFlag["System"] = 4096] = "System"; + UserFlag[UserFlag["BugHunterLevel2"] = 16384] = "BugHunterLevel2"; + UserFlag[UserFlag["VerifiedBot"] = 65536] = "VerifiedBot"; + UserFlag[UserFlag["VerifiedBotDeveloper"] = 131072] = "VerifiedBotDeveloper"; +})(UserFlag = exports.UserFlag || (exports.UserFlag = {})); +class UserFlags extends Flags_1.Flags { +} +exports.UserFlags = UserFlags; diff --git a/lib/structures/flags/index.d.ts b/lib/structures/flags/index.d.ts new file mode 100644 index 000000000..efa8d4433 --- /dev/null +++ b/lib/structures/flags/index.d.ts @@ -0,0 +1,6 @@ +export * from './Flags'; +export * from './GuildSystemChannelFlags'; +export * from './MessageFlags'; +export * from './PermissionFlags'; +export * from './PresenceActivityFlags'; +export * from './UserFlags'; diff --git a/lib/structures/flags/index.js b/lib/structures/flags/index.js new file mode 100644 index 000000000..21ebd7b58 --- /dev/null +++ b/lib/structures/flags/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Flags"), exports); +__exportStar(require("./GuildSystemChannelFlags"), exports); +__exportStar(require("./MessageFlags"), exports); +__exportStar(require("./PermissionFlags"), exports); +__exportStar(require("./PresenceActivityFlags"), exports); +__exportStar(require("./UserFlags"), exports); diff --git a/lib/structures/guild/Guild.d.ts b/lib/structures/guild/Guild.d.ts new file mode 100644 index 000000000..1896a3fad --- /dev/null +++ b/lib/structures/guild/Guild.d.ts @@ -0,0 +1,446 @@ +import { GuildPreview } from './GuildPreview'; +import { GuildUnavailable } from './GuildUnavailable'; +import { GuildWidget } from './GuildWidget'; +import Collection from '../../Collection'; +import { Bot } from '../../bot'; +import { GuildChannelsController, GuildEmojisController, GuildInvitesController, GuildMembersController } from '../../controllers/guild'; +import { GuildBansController, GuildIntegrationsController, GuildRolesController } from '../../controllers/guild'; +import { GuildWebhooksController } from '../../controllers/guild/GuildWebhooksController'; +import { BotSocketShard } from '../../socket'; +import { ShardId, Snowflake } from '../../types'; +import { GuildBannerFormat } from '../Avatar'; +import { ImageURI } from '../ImageURI'; +import { GatewayStruct } from '../base'; +import { GuildChannel, GuildTextChannel } from '../channels'; +import { GuildSystemChannelFlags, PermissionFlags } from '../flags'; +import { Member, MemberPresence } from '../member'; +import { GuildVoice } from '../voice/GuildVoice'; +import { VoiceState } from '../voice/VoiceState'; +/** + * Guild verification levels + */ +export declare enum VerificationLevel { + None = 0, + Low = 1, + Medium = 2, + High = 3, + VeryHigh = 4 +} +/** + * Guild default notification levels + */ +export declare enum NotificationLevels { + AllMessages = 0, + OnlyMentions = 1 +} +/** + * Guild explicit content filtering levels + */ +export declare enum ExplicitContentLevels { + Disabled = 0, + MembersWithoutRoles = 1, + AllMembers = 2 +} +/** + * MFA levels required for the Guild + */ +export declare enum MFALevel { + None = 0, + Elevated = 1 +} +/** + * Guild premium (boost) tiers + */ +export declare enum BoostTiers { + None = 0, + Tier1 = 1, + Tier2 = 2, + Tier3 = 3 +} +/** + * Information about the Guild's AFK channel and timeout + */ +export interface GuildAFK { + /** + * The AFK channel + */ + channel?: GuildChannel; + /** + * AFK timeout in seconds + */ + timeout: number; +} +/** + * Information about guild option levels + */ +export interface GuildLevels { + /** + * Verification level required for the guild + */ + verification: VerificationLevel; + /** + * Default notifications level + */ + notifications: NotificationLevels; + /** + * Explicit content filter level + */ + explicitContent: ExplicitContentLevels; + /** + * Required MFA level for the guild + */ + mfa: MFALevel; +} +/** + * Information about the guild's system channel + */ +export interface GuildSystemChannel { + /** + * System channel flags + */ + flags: GuildSystemChannelFlags; + /** + * The channel where guild notices such as welcome messages and boost events are posted. + * Possibly undefined if such channel does not exist + */ + channel: GuildTextChannel | undefined; +} +export interface GuildBoosts { + /** + * Guild boost level + */ + tier: BoostTiers; + /** + * Number of boosts this server currently has + */ + boostsCount: number | undefined; +} +/** + * Information for approximated data about a guild + */ +export interface GuildApproximates { + /** + * Approximate number of members in a guild + */ + memberCount?: number; + /** + * Approximate number of non-offline members in a guild + */ + presenceCount?: number; +} +/** + * The new data of the guild after modifying it + */ +export interface ModifyGuildOptions { + /** + * The new name of the guild + */ + name?: string; + /** + * The new guild's voice region ID + */ + region?: string; + /** + * New level data for the guild. Includes verification level, message notification level and explicit content filter level + */ + levels?: Partial>; + /** + * Data for the guild's AFK channel and timeout + */ + afk?: Partial; + /** + * The new icon of the guild + */ + icon?: ImageURI | null; + /** + * The user ID to transfer guild ownership to (bot must be the owner of the guild) + */ + ownerId?: Snowflake; + /** + * The new splash image of the guild + */ + splash?: ImageURI | null; + /** + * The new banner image of the guild + */ + banner?: ImageURI | null; + /** + * The ID of the channel where guid notices such as welcome messages and boost events are posted + */ + systemChannelId?: Snowflake; + /** + * The ID of the channel where public guilds display rules and/or guidelines + */ + rulesChannelId?: Snowflake; + /** + * The ID of the channel where admins and moderators of public guilds receive notices from Discord + */ + updatesChannelId?: Snowflake; + /** + * The preferred locale of a public guild used in server discovery and notices from Discord; defaults to "en-US" + */ + locale?: string; +} +/** + * Options used for a guild prune count + */ +export interface PruneCountOptions { + /** + * The number of days to count prune for (1 or more) + * @default 7 + */ + days?: number; + /** + * By default, prune will not remove users with roles. + * You can optionally include specific roles in your prune by providing this field with an array of role IDs to include in the prune. + * @default [] + */ + includeRoles?: Snowflake[]; +} +/** + * Options for a guild prune operation + */ +export interface PruneOptions extends PruneCountOptions { + /** + * Whether the prune operation will return the number of members pruned. Discouraged for large guilds + * @default true + */ + computePruneCount?: number; +} +/** + * Represents a guild's vanity URL invite object + */ +export interface GuildVanityInvite { + /** + * The vanity URL code. Possibly null if a vanity URL for the guild is not set + */ + code: string | null; + /** + * The number of uses this invite has + */ + uses: number; +} +/** + * Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI. + * @extends BaseStruct + */ +export declare class Guild extends GuildPreview { + /** + * The guild's channels controller + */ + channels: GuildChannelsController; + /** + * The guild's roles controller + */ + roles: GuildRolesController; + /** + * The guild's members controller + */ + members: GuildMembersController; + /** + * Guild owner {@link Member}. + * Possibly undefined if the bot is yet to cache that member + */ + owner: Member | undefined; + /** + * Guild owner ID + */ + ownerId: Snowflake; + /** + * Total permissions for the Bot in the guild (excludes overrides) + */ + permissions: PermissionFlags | undefined; + /** + * Guild voice region + */ + region: string; + /** + * Information about the Guild AFK options + */ + afk: GuildAFK; + /** + * {@link GuildLevels} object containing information about all guild level data + */ + levels: GuildLevels; + /** + * This guild's emojis controller + */ + emojis: GuildEmojisController; + /** + * Application ID of the guild creator if it is bot-created + */ + applicationId: Snowflake | null; + /** + * The guild widget data + */ + widget: GuildWidget; + /** + * {@link GuildSystemChannel} object containing information about the guild system channel + */ + systemChannel: GuildSystemChannel; + /** + * The channel where public guilds display rules and/or guidelines + */ + rulesChannel: GuildTextChannel | undefined; + /** + * Timestamp for when the guild was created + */ + createdAt: string | undefined; + /** + * Whether this guild is considered a large guild + */ + large: boolean | undefined; + /** + * Whether this guild is unavailable + */ + unavailable: boolean | undefined; + /** + * Total number of members in this guild + */ + memberCount: number | undefined; + voiceStates: Collection; + /** + * Presences of the members in the guild. + * Will only include non-offline members if the size is greater than {@link identify.large_threshold} + */ + presences: Collection; + /** + * The maximum number of presence for the guild (the default value, currently 25000, is in effect when `null` is returned) + */ + maxPresences: number | null | undefined; + /** + * The maximum number of members for the guild + */ + maxMembers: number | undefined; + /** + * The vanity URL code for the guild. Possibly null if guild does not have a vanity URL + */ + vanityURLCode: string | null; + /** + * The description for the guild. Possibly null if guild does not have a description + */ + description: string | null; + /** + * Guild banner image hash. Possibly null if guild does not have a banner image + */ + bannerHash: string | null; + /** + * {@link GuildBoosts} object containing guild boosts data + */ + boosts: GuildBoosts; + /** + * The preferred locale of a public guild used in server discovery and notices from Discord + * @default "en-US" + */ + locale: string; + /** + * The channel where admins and moderators of public guilds receive notice from Discord + */ + updatesChannel: GuildTextChannel | undefined; + /** + * The guild's invites controller + */ + invites: GuildInvitesController; + /** + * The guild's bans controller + */ + bans: GuildBansController; + /** + * The guild's integrations controller + */ + integrations: GuildIntegrationsController; + /** + * The guild's webhooks controller + */ + webhooks: GuildWebhooksController; + /** + * The id of the shard which belongs to this guild + */ + shardId?: ShardId; + voice: GuildVoice; + constructor(bot: Bot, guild: GatewayStruct, shardId?: ShardId); + /** + * @ignore + * @param {GatewayStruct} guild The guild data + * @returns {this} + */ + init(guild: GatewayStruct): this; + /** + * Returns the URL of the guild's banner image. + * Possibly returns null if the guild does not have a banner image + * @param {GuildBannerFormat} format The format of the returned guild banner image + * @param {number} size The size of the returned guild banner image + * @returns {string | null} + */ + bannerURL(format?: GuildBannerFormat, size?: number): string | null; + /** + * Modifies this guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modify(options: ModifyGuildOptions): Promise; + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + pruneCount(options?: PruneCountOptions): Promise; + /** + * Begins a prune operation on this guild. + * Requires the {@link Permission.KickMembers} permission + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + prune(options?: PruneOptions): Promise; + /** + * Fetches this guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchWidget(): Promise; + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchVanityURL(): Promise; + /** + * Leaves this guild + * @returns {Promise} + */ + leave(): Promise; + get shard(): BotSocketShard; + /** + * Creates a {@link Guild} or {@link GuildUnavailable} + * @param {Bot} bot The bot instance + * @param {GatewayStruct} guild The guild data + * @param {number} [shardId] The shard id that belongs to that guild + * @returns {Guild | GuildUnavailable} + * @ignore + */ + static create(bot: Bot, guild: GatewayStruct, shardId: number): Guild | GuildUnavailable; + /** + * Finds a {@link Guild} or {@link GuildUnavailable} from the correct cache + * @param {Bot} bot The bot instance + * @param {Snowflake} guildId The ID of the guild + * @returns {Guild | GuildUnavailable | undefined} + * @ignore + */ + static find(bot: Bot, guildId: Snowflake): Guild | GuildUnavailable | undefined; + /** + * Caches a guild in the correct collection + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The guild you wish to cache + * @ignore + */ + static cache(bot: Bot, guild: Guild | GuildUnavailable): void; + /** + * Deletes a guild from the correct cache + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The available / unavailable guild + * @ignore + */ + static delete(bot: Bot, guild: Guild | GuildUnavailable): void; +} diff --git a/lib/structures/guild/Guild.js b/lib/structures/guild/Guild.js new file mode 100644 index 000000000..fa85d337e --- /dev/null +++ b/lib/structures/guild/Guild.js @@ -0,0 +1,283 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Guild = exports.BoostTiers = exports.MFALevel = exports.ExplicitContentLevels = exports.NotificationLevels = exports.VerificationLevel = void 0; +const GuildEmoji_1 = require("./GuildEmoji"); +const GuildPreview_1 = require("./GuildPreview"); +const GuildUnavailable_1 = require("./GuildUnavailable"); +const GuildWidget_1 = require("./GuildWidget"); +const Collection_1 = __importDefault(require("../../Collection")); +const guild_1 = require("../../controllers/guild"); +const guild_2 = require("../../controllers/guild"); +const GuildWebhooksController_1 = require("../../controllers/guild/GuildWebhooksController"); +const Avatar_1 = require("../Avatar"); +const Role_1 = require("../Role"); +const utils_1 = require("../channels/utils"); +const flags_1 = require("../flags"); +const member_1 = require("../member"); +const GuildVoice_1 = require("../voice/GuildVoice"); +const VoiceState_1 = require("../voice/VoiceState"); +/** + * Guild verification levels + */ +var VerificationLevel; +(function (VerificationLevel) { + VerificationLevel[VerificationLevel["None"] = 0] = "None"; + VerificationLevel[VerificationLevel["Low"] = 1] = "Low"; + VerificationLevel[VerificationLevel["Medium"] = 2] = "Medium"; + VerificationLevel[VerificationLevel["High"] = 3] = "High"; + VerificationLevel[VerificationLevel["VeryHigh"] = 4] = "VeryHigh"; +})(VerificationLevel = exports.VerificationLevel || (exports.VerificationLevel = {})); +/** + * Guild default notification levels + */ +var NotificationLevels; +(function (NotificationLevels) { + NotificationLevels[NotificationLevels["AllMessages"] = 0] = "AllMessages"; + NotificationLevels[NotificationLevels["OnlyMentions"] = 1] = "OnlyMentions"; +})(NotificationLevels = exports.NotificationLevels || (exports.NotificationLevels = {})); +/** + * Guild explicit content filtering levels + */ +var ExplicitContentLevels; +(function (ExplicitContentLevels) { + ExplicitContentLevels[ExplicitContentLevels["Disabled"] = 0] = "Disabled"; + ExplicitContentLevels[ExplicitContentLevels["MembersWithoutRoles"] = 1] = "MembersWithoutRoles"; + ExplicitContentLevels[ExplicitContentLevels["AllMembers"] = 2] = "AllMembers"; +})(ExplicitContentLevels = exports.ExplicitContentLevels || (exports.ExplicitContentLevels = {})); +/** + * MFA levels required for the Guild + */ +var MFALevel; +(function (MFALevel) { + MFALevel[MFALevel["None"] = 0] = "None"; + MFALevel[MFALevel["Elevated"] = 1] = "Elevated"; +})(MFALevel = exports.MFALevel || (exports.MFALevel = {})); +/** + * Guild premium (boost) tiers + */ +var BoostTiers; +(function (BoostTiers) { + BoostTiers[BoostTiers["None"] = 0] = "None"; + BoostTiers[BoostTiers["Tier1"] = 1] = "Tier1"; + BoostTiers[BoostTiers["Tier2"] = 2] = "Tier2"; + BoostTiers[BoostTiers["Tier3"] = 3] = "Tier3"; +})(BoostTiers = exports.BoostTiers || (exports.BoostTiers = {})); +/** + * Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI. + * @extends BaseStruct + */ +class Guild extends GuildPreview_1.GuildPreview { + constructor(bot, guild, shardId) { + super(bot, guild); + this.shardId = shardId; + } + /** + * @ignore + * @param {GatewayStruct} guild The guild data + * @returns {this} + */ + init(guild) { + super.init(guild); + this.presences = new Collection_1.default(); + this.roles = new guild_2.GuildRolesController(this); + this.members = new guild_1.GuildMembersController(this); + this.emojis = new guild_1.GuildEmojisController(this); + this.channels = new guild_1.GuildChannelsController(this); + this.invites = new guild_1.GuildInvitesController(this); + this.bans = new guild_2.GuildBansController(this); + this.integrations = new guild_2.GuildIntegrationsController(this); + this.webhooks = new GuildWebhooksController_1.GuildWebhooksController(this); + this.voice = new GuildVoice_1.GuildVoice(this); + this.voiceStates = new Collection_1.default(); + if (guild.channels) { + this.channels.cache.addMany(guild.channels.map((channel) => utils_1.ChannelUtils.createGuildChannel(this.bot, channel, this))); + } + // Add all of this guild's cached channels to the Bot's cached channels + this.bot.channels.cache.merge(this.channels.cache); + this.roles.cache.addMany(guild.roles.map((role) => new Role_1.Role(this.bot, role, this))); + if (guild.members) { + this.members.cache.addMany(guild.members.map((member) => { + var _a; + return new member_1.Member(this.bot, member, this, (_a = guild.presences) === null || _a === void 0 ? void 0 : _a.find((presence) => presence.user.id === member.user.id)); + })); + } + if (guild.voice_states) { + for (const voicestate of guild.voice_states) { + this.voiceStates.set(voicestate.user_id, new VoiceState_1.VoiceState(this.bot, this.members.cache.get(voicestate.user_id), voicestate)); + } + } + this.owner = this.members.cache.get(guild.owner_id); + this.ownerId = guild.owner_id; + if (guild.permissions) { + this.permissions = new flags_1.PermissionFlags(guild.permissions.toString()); + } + this.region = guild.region; + this.afk = { + channel: guild.afk_channel_id && this.channels.cache.get(guild.afk_channel_id), + timeout: guild.afk_timeout, + }; + this.levels = { + verification: guild.verification_level, + notifications: guild.default_message_notifications, + explicitContent: guild.explicit_content_filter, + mfa: guild.mfa_level, + }; + this.emojis.cache.addMany(guild.emojis.map((emoji) => new GuildEmoji_1.GuildEmoji(this.bot, emoji, this))); + // Add all of this guild's cached emojis to the Bot's cached emojis + this.bot.emojis.merge(this.emojis.cache); + this.applicationId = guild.application_id; + this.widget = new GuildWidget_1.GuildWidget(this.bot, + // Serializes the guild widget data + { enabled: guild.widget_enabled, channel_id: guild.widget_channel_id }, this); + this.systemChannel = { + channel: this.channels.cache.get(guild.system_channel_id), + flags: new flags_1.GuildSystemChannelFlags(guild.system_channel_flags), + }; + this.rulesChannel = this.channels.cache.get(guild.rules_channel_id); + this.createdAt = guild.joined_at; + this.large = guild.large; + this.unavailable = guild.unavailable; + this.memberCount = guild.member_count; + this.maxPresences = guild.max_presences; + this.maxMembers = guild.max_members; + this.vanityURLCode = guild.vanity_url_code; + this.description = guild.description; + this.bannerHash = guild.banner; + this.boosts = { + tier: guild.premium_tier, + boostsCount: guild.premium_subscription_count, + }; + this.locale = guild.locale; + if (guild.public_updates_channel_id) { + this.updatesChannel = this.channels.cache.get(guild.public_updates_channel_id); + } + return this; + } + /** + * Returns the URL of the guild's banner image. + * Possibly returns null if the guild does not have a banner image + * @param {GuildBannerFormat} format The format of the returned guild banner image + * @param {number} size The size of the returned guild banner image + * @returns {string | null} + */ + bannerURL(format = Avatar_1.GuildBannerFormat.PNG, size) { + return this.bannerHash && Avatar_1.Avatar.bannerURL(this.bannerHash, this.id, format, size); + } + /** + * Modifies this guild's settings. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyGuildOptions} options The new options for the updated guild + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuild(this.id, options); + } + /** + * Returns the number of members that would be removed in a prune operation. + * Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + * @param {PruneCountOptions} options Options for the prune + * @returns {Promise} + */ + pruneCount(options) { + return this.bot.api.guildPruneCount(this.id, options); + } + /** + * Begins a prune operation on this guild. + * Requires the {@link Permission.KickMembers} permission + * @param {PruneOptions} options The options for the prune operation + * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false + */ + prune(options) { + return this.bot.api.guildPrune(this.id, options); + } + /** + * Fetches this guild's widget object. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchWidget() { + return this.bot.api.fetchGuildWidget(this.id); + } + /** + * Fetches this guild's vanity URL. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + fetchVanityURL() { + return this.bot.api.fetchGuildVanityURL(this.id); + } + /** + * Leaves this guild + * @returns {Promise} + */ + leave() { + return this.bot.api.leaveGuild(this.id); + } + // Returns undefined if the guilds fetched via REST and not received by gateway + get shard() { + return this.bot.connection.shards.get(this.shardId); + } + /** + * Creates a {@link Guild} or {@link GuildUnavailable} + * @param {Bot} bot The bot instance + * @param {GatewayStruct} guild The guild data + * @param {number} [shardId] The shard id that belongs to that guild + * @returns {Guild | GuildUnavailable} + * @ignore + */ + static create(bot, guild, shardId) { + return guild.unavailable + ? new GuildUnavailable_1.GuildUnavailable(bot, guild, shardId) + : new Guild(bot, guild, shardId); + } + /** + * Finds a {@link Guild} or {@link GuildUnavailable} from the correct cache + * @param {Bot} bot The bot instance + * @param {Snowflake} guildId The ID of the guild + * @returns {Guild | GuildUnavailable | undefined} + * @ignore + */ + static find(bot, guildId) { + if (bot.unavailableGuilds.has(guildId)) { + // Guild is part of the unavailable guilds collection + return bot.unavailableGuilds.get(guildId); + } + else { + // Guild is part of the guilds collection + return bot.guilds.cache.get(guildId); + } + } + /** + * Caches a guild in the correct collection + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The guild you wish to cache + * @ignore + */ + static cache(bot, guild) { + if (guild instanceof Guild) { + bot.guilds.cache.add(guild); + } + else { + bot.unavailableGuilds.set(guild.id, guild); + } + } + /** + * Deletes a guild from the correct cache + * @param {Bot} bot The bot instance + * @param {Guild | GuildUnavailable} guild The available / unavailable guild + * @ignore + */ + static delete(bot, guild) { + if (guild instanceof Guild) { + // The bot left the guild or it has become unavailable. + bot.guilds.cache.delete(guild.id); + } + else { + bot.unavailableGuilds.set(guild.id, guild); + } + } +} +exports.Guild = Guild; diff --git a/lib/structures/guild/GuildBan.d.ts b/lib/structures/guild/GuildBan.d.ts new file mode 100644 index 000000000..bae9769fd --- /dev/null +++ b/lib/structures/guild/GuildBan.d.ts @@ -0,0 +1,32 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { User } from '../User'; +import { BaseGuildStruct, GatewayStruct } from '../base'; +/** + * Represents a user ban in a guild + */ +export declare class GuildBan extends BaseGuildStruct { + /** + * The reason for the guild. + * Possibly undefined if no reason was specified + */ + reason: string | undefined; + /** + * The user banned from the guild + */ + user: User; + constructor(bot: Bot, ban: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} ban The ban data + * @returns {this} + */ + init(ban: GatewayStruct): this; + /** + * The ID of the user banned from the guild. + * Serves as an identifier for this ban + * @type {Snowflake} + */ + get id(): Snowflake; +} diff --git a/lib/structures/guild/GuildBan.js b/lib/structures/guild/GuildBan.js new file mode 100644 index 000000000..a7f7eb620 --- /dev/null +++ b/lib/structures/guild/GuildBan.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildBan = void 0; +const User_1 = require("../User"); +const base_1 = require("../base"); +/** + * Represents a user ban in a guild + */ +class GuildBan extends base_1.BaseGuildStruct { + constructor(bot, ban, guild) { + super(bot, guild, ban); + this.init(ban); + } + /** + * @ignore + * @param {GatewayStruct} ban The ban data + * @returns {this} + */ + init(ban) { + this.reason = ban.reason; + this.user = this.bot.users.cache.get(ban.user.id) || new User_1.User(this.bot, ban.user); + return this; + } + /** + * The ID of the user banned from the guild. + * Serves as an identifier for this ban + * @type {Snowflake} + */ + get id() { + return this.user.id; + } +} +exports.GuildBan = GuildBan; diff --git a/lib/structures/guild/GuildEmoji.d.ts b/lib/structures/guild/GuildEmoji.d.ts new file mode 100644 index 000000000..71bdeee8e --- /dev/null +++ b/lib/structures/guild/GuildEmoji.d.ts @@ -0,0 +1,81 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { GuildEmojiFormat } from '../Avatar'; +import { Emoji } from '../Emoji'; +import { ImageURI } from '../ImageURI'; +import { Role } from '../Role'; +import { GatewayStruct } from '../base'; +/** + * Options for when creating new guild emojis + */ +export interface CreateEmojiOptions { + /** + * The name of the emoji + */ + name: string; + /** + * The 128x128 emoji image + */ + image: ImageURI; + /** + * Roles for which this emoji will be whitelisted + */ + roles?: (Snowflake | Role)[]; +} +/** + * Options for when modifying guild emojis + */ +export interface ModifyEmojiOptions { + /** + * The name of the emoji + */ + name?: string; + /** + * Roles for which this emoji will be whitelisted + */ + roles?: (Snowflake | Role)[]; +} +/** + * Structure for Emojis that were created in a Guild + */ +export declare class GuildEmoji extends Emoji { + /** + * Every guild emoji is associated to a guild + */ + guild: Guild; + /** + * Every guild emoji has a name + */ + name: string; + constructor(bot: Bot, emoji: GatewayStruct, guild: Guild); + /** + * Every guild emoji has an identifier + */ + get id(): string; + /** + * Returns the guild emoji's URL + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + URL(format?: GuildEmojiFormat, size?: number): string; + /** + * Modifies this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} + */ + modify(options: ModifyEmojiOptions): Promise; + /** + * Deletes this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @returns {Promise} + */ + delete(): Promise; + /** + * @ignore + * @returns {string} + */ + toString(): string; +} diff --git a/lib/structures/guild/GuildEmoji.js b/lib/structures/guild/GuildEmoji.js new file mode 100644 index 000000000..5aa422551 --- /dev/null +++ b/lib/structures/guild/GuildEmoji.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildEmoji = void 0; +const Avatar_1 = require("../Avatar"); +const Emoji_1 = require("../Emoji"); +/** + * Structure for Emojis that were created in a Guild + */ +class GuildEmoji extends Emoji_1.Emoji { + constructor(bot, emoji, guild) { + super(bot, emoji, guild); + } + /** + * Every guild emoji has an identifier + */ + get id() { + return this.emojiId; + } + /** + * Returns the guild emoji's URL + * @param {GuildEmojiFormat} format The format of the returned emoji image + * @param {number} size The size of the returned emoji image + * @returns {string} + */ + URL(format = Avatar_1.GuildEmojiFormat.PNG, size) { + return Avatar_1.Avatar.emojiURL(this.id, format, size); + } + /** + * Modifies this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @param {ModifyEmojiOptions} options The options for the updated emoji + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuildEmoji(this.guild.id, this.id, options); + } + /** + * Deletes this emoji. + * Requires the {@link Permission.ManageEmojis} permission + * @returns {Promise} + */ + delete() { + return this.bot.api.deleteGuildEmoji(this.guild.id, this.id); + } + /** + * @ignore + * @returns {string} + */ + toString() { + return this.animated ? `` : `<:${this.name}:${this.id}>`; + } +} +exports.GuildEmoji = GuildEmoji; diff --git a/lib/structures/guild/GuildIntegration.d.ts b/lib/structures/guild/GuildIntegration.d.ts new file mode 100644 index 000000000..eaf016f30 --- /dev/null +++ b/lib/structures/guild/GuildIntegration.d.ts @@ -0,0 +1,137 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { Role } from '../Role'; +import { Timestamp } from '../Timestamp'; +import { User } from '../User'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +/** + * The behavior of expiring subscribers of an integration + */ +export declare enum IntegrationExpireBehavior { + RemoveRole = 0, + Kick = 1 +} +/** + * Expire data for an integration + */ +export interface IntegrationExpire { + /** + * The behavior of expiring subscribers + */ + behavior: IntegrationExpireBehavior; + /** + * The grace period (in days) before expiring subscribers + */ + gracePeriod: number; +} +/** + * Account data for an integration + */ +export interface IntegrationAccount { + /** + * The ID of the account + */ + id: string; + /** + * The name of the account + */ + name: string; +} +/** + * Options for when creating new guild integrations + */ +export interface CreateIntegrationOptions { + /** + * The integration type + */ + type: string; + /** + * The ID of the integration + */ + id: Snowflake; +} +/** + * Options for when modifying guild integrations + */ +export interface ModifyIntegrationOptions { + /** + * The new expire options for the modified integration + */ + expire?: Partial; + /** + * Whether emoticons should be synced for this integration (twitch only currently) + */ + enableEmoticons?: boolean; +} +/** + * Guild integration object + */ +export declare class GuildIntegration extends BaseGuildStruct { + /** + * The ID of the integration + */ + id: Snowflake; + /** + * The name of the integration + */ + name: string; + /** + * The type of the integration + * @example twitch + * @example youtube + */ + type: string; + /** + * Whether this integration is enabled + */ + enabled: boolean; + /** + * Whether this integration is syncing + */ + syncing: boolean; + /** + * The role that this integration uses for "subscribers" + */ + role: Role | undefined; + /** + * Whether emoticons should be synced for this integration (twitch only currently) + */ + enableEmoticons: boolean; + /** + * The expire data for this integration + */ + expire: IntegrationExpire; + /** + * The user for this integration + */ + user: User; + /** + * The integration account information + */ + account: IntegrationAccount; + /** + * Timestamp of when this integration was last synced + */ + syncedAt: Timestamp; + constructor(bot: Bot, integration: GatewayStruct, guild: Guild); + /** + * @ignore + * @param {GatewayStruct} integration The integration data + * @returns {this} + */ + init(integration: GatewayStruct): this; + /** + * Modifies the behavior and settings of this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modify(options: ModifyIntegrationOptions): Promise; + /** + * Syncs this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + sync(): Promise; +} diff --git a/lib/structures/guild/GuildIntegration.js b/lib/structures/guild/GuildIntegration.js new file mode 100644 index 000000000..629b2f881 --- /dev/null +++ b/lib/structures/guild/GuildIntegration.js @@ -0,0 +1,64 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildIntegration = exports.IntegrationExpireBehavior = void 0; +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +const base_1 = require("../base"); +/** + * The behavior of expiring subscribers of an integration + */ +var IntegrationExpireBehavior; +(function (IntegrationExpireBehavior) { + IntegrationExpireBehavior[IntegrationExpireBehavior["RemoveRole"] = 0] = "RemoveRole"; + IntegrationExpireBehavior[IntegrationExpireBehavior["Kick"] = 1] = "Kick"; +})(IntegrationExpireBehavior = exports.IntegrationExpireBehavior || (exports.IntegrationExpireBehavior = {})); +/** + * Guild integration object + */ +class GuildIntegration extends base_1.BaseGuildStruct { + constructor(bot, integration, guild) { + super(bot, guild, integration); + this.init(integration); + } + /** + * @ignore + * @param {GatewayStruct} integration The integration data + * @returns {this} + */ + init(integration) { + this.id = integration.id; + this.name = integration.name; + this.type = integration.type; + this.enabled = integration.enabled; + this.syncing = integration.syncing; + this.role = this.guild.roles.cache.get(integration.role_id); + this.enableEmoticons = integration.enable_emoticons; + this.expire = { + behavior: integration.expire_behavior, + gracePeriod: integration.expire_grace_period, + }; + this.user = + this.bot.users.cache.get(integration.user.id) || new User_1.User(this.bot, integration.user); + this.account = integration.account; + this.syncedAt = new Timestamp_1.Timestamp(integration.synced_at); + return this; + } + /** + * Modifies the behavior and settings of this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyIntegrationOptions} options The options for the modified guild integration + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyGuildIntegration(this.guild.id, this.id, options); + } + /** + * Syncs this guild integration. + * Requires the {@link Permission.ManageGuild} permission + * @returns {Promise} + */ + sync() { + return this.bot.api.syncGuildIntegration(this.guild.id, this.id); + } +} +exports.GuildIntegration = GuildIntegration; diff --git a/lib/structures/guild/GuildPreview.d.ts b/lib/structures/guild/GuildPreview.d.ts new file mode 100644 index 000000000..4f00bc72a --- /dev/null +++ b/lib/structures/guild/GuildPreview.d.ts @@ -0,0 +1,128 @@ +import { GuildApproximates } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { GuildDiscoverySplashFormat, GuildIconFormat, GuildSplashFormat } from '../Avatar'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * All guild features + * https://discord.com/developers/docs/resources/guild#guild-object-guild-features + */ +export declare enum GuildFeature { + /** + * Guild has access to set an invite splash background + */ + InviteSplash = "INVITE_SPLASH", + /** + * Guild has access to set 384kbps bitrate in voice (previously VIP voice servers) + */ + VIPRegions = "VIP_REGIONS", + /** + * Guild has access to set a vanity URL + */ + VanityURL = "VANITY_URL", + /** + * Guild is verified + */ + Verified = "VERIFIED", + /** + * Guild is partnered + */ + Partnered = "PARTNERED", + /** + * Guild is public + */ + Public = "PUBLIC", + /** + * Guild has access to use commerce features (i.e. create store channels) + */ + Commerce = "COMMERCE", + /** + * Guild has access to create news channels + */ + News = "NEWS", + /** + * Guild is able to be discovered in the directory + */ + Discoverable = "DISCOVERABLE", + /** + * Guild is able to be featured in the directory + */ + Featurable = "FEATURABLE", + /** + * Guild has access to set an animated guild icon + */ + AnimatedIcon = "ANIMATED_ICON", + /** + * Guild has access to set a guild banner image + */ + Banner = "BANNER", + /** + * Guild cannot be public + */ + PublicDisabled = "PUBLIC_DISABLED", + /** + * Guild has enabled the welcome screen + */ + WelcomeScreenEnabled = "WELCOME_SCREEN_ENABLED" +} +export declare class GuildPreview extends BaseStruct { + /** + * Guild ID + */ + id: Snowflake; + /** + * Guild name + */ + name: string; + /** + * Guild icon hash. Possibly null if guild does not have an icon + */ + iconHash: string | null; + /** + * Guild splash image hash. Possibly null if guild does not have a splash image + */ + splashHash: string | null; + /** + * Guild discovery splash image hash. Possibly null if guild does not have a discovery splash image + */ + discoverySplashHash: string | null; + /** + * Enabled guild features + */ + features: GuildFeature[]; + /** + * Information about approximated data for this guild + */ + approximates: GuildApproximates; + constructor(bot: Bot, preview: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} preview The guild preview data + * @returns {this} + */ + init(preview: GatewayStruct): this; + /** + * Returns the URL of the guild's icon image. + * Possibly returns null if the guild does not have an icon + * @param {GuildIconFormat} format The format of the returned guild icon image + * @param {number} size The size of the returned guild icon image + * @returns {string | null} + */ + iconURL(format?: GuildIconFormat, size?: number): string | null; + /** + * Returns the URL of the guild's splash image. + * Possibly returns null if the guild does not have a splash image + * @param {GuildSplashFormat} format The format of the returned guild splash image + * @param {number} size The size of the returned guild splash image + * @returns {string | null} + */ + splashURL(format?: GuildSplashFormat, size?: number): string | null; + /** + * Returns the URL of the guild's discovery splash image. + * Possibly returns null if the guild does not have a discovery splash image + * @param {GuildDiscoverySplashFormat} format The format of the returned guild discovery splash image + * @param {number} size The size of the returned guild discovery splash image + * @returns {string | null} + */ + discoverySplashURL(format?: GuildDiscoverySplashFormat, size?: number): string | null; +} diff --git a/lib/structures/guild/GuildPreview.js b/lib/structures/guild/GuildPreview.js new file mode 100644 index 000000000..2cca22aee --- /dev/null +++ b/lib/structures/guild/GuildPreview.js @@ -0,0 +1,124 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildPreview = exports.GuildFeature = void 0; +const Avatar_1 = require("../Avatar"); +const base_1 = require("../base"); +/** + * All guild features + * https://discord.com/developers/docs/resources/guild#guild-object-guild-features + */ +var GuildFeature; +(function (GuildFeature) { + /** + * Guild has access to set an invite splash background + */ + GuildFeature["InviteSplash"] = "INVITE_SPLASH"; + /** + * Guild has access to set 384kbps bitrate in voice (previously VIP voice servers) + */ + GuildFeature["VIPRegions"] = "VIP_REGIONS"; + /** + * Guild has access to set a vanity URL + */ + GuildFeature["VanityURL"] = "VANITY_URL"; + /** + * Guild is verified + */ + GuildFeature["Verified"] = "VERIFIED"; + /** + * Guild is partnered + */ + GuildFeature["Partnered"] = "PARTNERED"; + /** + * Guild is public + */ + GuildFeature["Public"] = "PUBLIC"; + /** + * Guild has access to use commerce features (i.e. create store channels) + */ + GuildFeature["Commerce"] = "COMMERCE"; + /** + * Guild has access to create news channels + */ + GuildFeature["News"] = "NEWS"; + /** + * Guild is able to be discovered in the directory + */ + GuildFeature["Discoverable"] = "DISCOVERABLE"; + /** + * Guild is able to be featured in the directory + */ + GuildFeature["Featurable"] = "FEATURABLE"; + /** + * Guild has access to set an animated guild icon + */ + GuildFeature["AnimatedIcon"] = "ANIMATED_ICON"; + /** + * Guild has access to set a guild banner image + */ + GuildFeature["Banner"] = "BANNER"; + /** + * Guild cannot be public + */ + GuildFeature["PublicDisabled"] = "PUBLIC_DISABLED"; + /** + * Guild has enabled the welcome screen + */ + GuildFeature["WelcomeScreenEnabled"] = "WELCOME_SCREEN_ENABLED"; +})(GuildFeature = exports.GuildFeature || (exports.GuildFeature = {})); +class GuildPreview extends base_1.BaseStruct { + constructor(bot, preview) { + super(bot, preview); + this.init(preview); + } + /** + * @ignore + * @param {GatewayStruct} preview The guild preview data + * @returns {this} + */ + init(preview) { + this.id = preview.id; + this.name = preview.name; + this.iconHash = preview.icon; + this.splashHash = preview.splash; + this.discoverySplashHash = preview.discovery_splash; + this.features = preview.features; + this.approximates = { + memberCount: preview.approximate_member_count, + presenceCount: preview.approximate_presence_count, + }; + return this; + } + /** + * Returns the URL of the guild's icon image. + * Possibly returns null if the guild does not have an icon + * @param {GuildIconFormat} format The format of the returned guild icon image + * @param {number} size The size of the returned guild icon image + * @returns {string | null} + */ + iconURL(format = Avatar_1.GuildIconFormat.PNG, size) { + return this.iconHash && Avatar_1.Avatar.guildURL(this.iconHash, this.id, format, size); + } + /** + * Returns the URL of the guild's splash image. + * Possibly returns null if the guild does not have a splash image + * @param {GuildSplashFormat} format The format of the returned guild splash image + * @param {number} size The size of the returned guild splash image + * @returns {string | null} + */ + splashURL(format = Avatar_1.GuildSplashFormat.PNG, size) { + return this.splashHash && Avatar_1.Avatar.splashURL(this.splashHash, this.id, format, size); + } + /** + * Returns the URL of the guild's discovery splash image. + * Possibly returns null if the guild does not have a discovery splash image + * @param {GuildDiscoverySplashFormat} format The format of the returned guild discovery splash image + * @param {number} size The size of the returned guild discovery splash image + * @returns {string | null} + */ + discoverySplashURL(format = Avatar_1.GuildDiscoverySplashFormat.PNG, size) { + return (this.discoverySplashHash && + Avatar_1.Avatar.discoverySplashURL(this.discoverySplashHash, this.id, format, size)); + } +} +exports.GuildPreview = GuildPreview; diff --git a/lib/structures/guild/GuildUnavailable.d.ts b/lib/structures/guild/GuildUnavailable.d.ts new file mode 100644 index 000000000..8e298c217 --- /dev/null +++ b/lib/structures/guild/GuildUnavailable.d.ts @@ -0,0 +1,30 @@ +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * Used instead of {@link Guild} when the guild is unavailable + * Includes just the ID of the guild, which should be fetched in order to obtain the full guild class + + * @extends BaseStruct + */ +export declare class GuildUnavailable extends BaseStruct { + /** + * Guild ID + */ + id: Snowflake; + /** + * Whether this guild is unavailable + */ + unavailable: boolean | undefined; + /** + * The id of the shard which belongs to this guild + */ + shardId: number | undefined; + constructor(bot: Bot, guild: GatewayStruct, shardId?: number); + /** + * @ignore + * @param {GatewayStruct} guild The unavailable guild data + * @returns {this} + */ + init(guild: GatewayStruct): this; +} diff --git a/lib/structures/guild/GuildUnavailable.js b/lib/structures/guild/GuildUnavailable.js new file mode 100644 index 000000000..d706a0f5a --- /dev/null +++ b/lib/structures/guild/GuildUnavailable.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildUnavailable = void 0; +const base_1 = require("../base"); +/** + * Used instead of {@link Guild} when the guild is unavailable + * Includes just the ID of the guild, which should be fetched in order to obtain the full guild class + + * @extends BaseStruct + */ +class GuildUnavailable extends base_1.BaseStruct { + constructor(bot, guild, shardId) { + super(bot, guild); + this.shardId = shardId; + this.init(guild); + } + /** + * @ignore + * @param {GatewayStruct} guild The unavailable guild data + * @returns {this} + */ + init(guild) { + this.id = guild.id; + this.unavailable = guild.unavailable; + return this; + } +} +exports.GuildUnavailable = GuildUnavailable; diff --git a/lib/structures/guild/GuildWidget.d.ts b/lib/structures/guild/GuildWidget.d.ts new file mode 100644 index 000000000..ff64264bd --- /dev/null +++ b/lib/structures/guild/GuildWidget.d.ts @@ -0,0 +1,41 @@ +import { Guild } from './Guild'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +import { GuildChannel } from '../channels'; +/** + * Options for when modifying guild widgets + */ +export interface ModifyWidgetOptions { + /** + * Whether the guild widget should be enabled + */ + enabled?: boolean; + /** + * The updated widget channel ID + */ + channelId?: Snowflake; +} +/** + * Guild widget object + */ +export declare class GuildWidget extends BaseGuildStruct { + /** + * Whether or not the guild widget is enabled + */ + enabled: boolean | undefined; + /** + * The channel for the guild widget. Possibly null if widget is not enabled or widget + * channel has not been selected + */ + channel: GuildChannel | null | undefined; + constructor(bot: Bot, widget: GatewayStruct, guild: Guild); + init(widget: GatewayStruct): this; + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modify(options: ModifyWidgetOptions): Promise; +} diff --git a/lib/structures/guild/GuildWidget.js b/lib/structures/guild/GuildWidget.js new file mode 100644 index 000000000..1b69efe7d --- /dev/null +++ b/lib/structures/guild/GuildWidget.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildWidget = void 0; +const base_1 = require("../base"); +/** + * Guild widget object + */ +class GuildWidget extends base_1.BaseGuildStruct { + constructor(bot, widget, guild) { + super(bot, guild, widget); + this.init(widget); + } + init(widget) { + this.enabled = widget.enabled; + this.channel = this.guild.channels.cache.get(widget.channel_id); + return this; + } + /** + * Modifies this guild widget. + * Requires the {@link Permission.ManageGuild} permission + * @param {ModifyWidgetOptions} options The options for the updated guild widget + * @returns {Promise} The updated guild widget + */ + modify(options) { + return this.bot.api.modifyGuildWidget(this.guild.id, options); + } +} +exports.GuildWidget = GuildWidget; diff --git a/lib/structures/guild/index.d.ts b/lib/structures/guild/index.d.ts new file mode 100644 index 000000000..544a9686d --- /dev/null +++ b/lib/structures/guild/index.d.ts @@ -0,0 +1,7 @@ +export * from './Guild'; +export * from './GuildBan'; +export * from './GuildEmoji'; +export * from './GuildIntegration'; +export * from './GuildPreview'; +export * from './GuildUnavailable'; +export * from './GuildWidget'; diff --git a/lib/structures/guild/index.js b/lib/structures/guild/index.js new file mode 100644 index 000000000..36aa13d8d --- /dev/null +++ b/lib/structures/guild/index.js @@ -0,0 +1,19 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Guild"), exports); +__exportStar(require("./GuildBan"), exports); +__exportStar(require("./GuildEmoji"), exports); +__exportStar(require("./GuildIntegration"), exports); +__exportStar(require("./GuildPreview"), exports); +__exportStar(require("./GuildUnavailable"), exports); +__exportStar(require("./GuildWidget"), exports); diff --git a/lib/structures/index.d.ts b/lib/structures/index.d.ts new file mode 100644 index 000000000..a36f7e0e6 --- /dev/null +++ b/lib/structures/index.d.ts @@ -0,0 +1,16 @@ +export * from './base'; +export * from './channels'; +export * from './flags'; +export * from './guild'; +export * from './member'; +export * from './message'; +export * from './Avatar'; +export * from './BotUser'; +export * from './Emoji'; +export * from './ImageURI'; +export * from './Invite'; +export * from './PermissionOverwrite'; +export * from './Role'; +export * from './Timestamp'; +export * from './User'; +export * from './Webhook'; diff --git a/lib/structures/index.js b/lib/structures/index.js new file mode 100644 index 000000000..089c1e03b --- /dev/null +++ b/lib/structures/index.js @@ -0,0 +1,28 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./base"), exports); +__exportStar(require("./channels"), exports); +__exportStar(require("./flags"), exports); +__exportStar(require("./guild"), exports); +__exportStar(require("./member"), exports); +__exportStar(require("./message"), exports); +__exportStar(require("./Avatar"), exports); +__exportStar(require("./BotUser"), exports); +__exportStar(require("./Emoji"), exports); +__exportStar(require("./ImageURI"), exports); +__exportStar(require("./Invite"), exports); +__exportStar(require("./PermissionOverwrite"), exports); +__exportStar(require("./Role"), exports); +__exportStar(require("./Timestamp"), exports); +__exportStar(require("./User"), exports); +__exportStar(require("./Webhook"), exports); diff --git a/lib/structures/member/Member.d.ts b/lib/structures/member/Member.d.ts new file mode 100644 index 000000000..9c2a94495 --- /dev/null +++ b/lib/structures/member/Member.d.ts @@ -0,0 +1,127 @@ +import { MemberPresence } from './MemberPresence'; +import { Bot } from '../../bot'; +import { MemberRolesController } from '../../controllers/member'; +import { Snowflake } from '../../types'; +import { Role } from '../Role'; +import { Timestamp } from '../Timestamp'; +import { User } from '../User'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +import { Guild } from '../guild'; +import { VoiceState } from '../voice/VoiceState'; +/** + * Options used when modifying a guild member + */ +export interface ModifyMemberOptions { + /** + * The value to set the member's nickname to. + * Requires the {@link Permission.ManageNicknames} permission + */ + nick?: string; + /** + * Array of roles / role IDs to assign to the member. + * Requires the {@link Permission.ManageRoles} permission + */ + roles?: (Snowflake | Role)[]; + /** + * Whether the member should be muted in voice channels. + * Requires the {@link Permission.MuteMembers} permission + */ + mute?: boolean; + /** + * Whether the member should be deafened in voice channel. + * Requires the {@link Permission.DeafenMembers} permission + */ + deaf?: boolean; + /** + * The ID of the voice channel to move the member to (if they are connected to another voice channel already). + * Requires the {@link Permission.MoveMembers} permission + */ + channelId?: Snowflake; +} +/** + * Options used when banning a member + */ +export interface MemberBanOptions { + /** + * Reason for the ban + */ + reason?: string; + /** + * Number of days to delete messages for (0-7) + */ + deleteMessageDays: number; +} +/** + * Representation of a Discord {@link User} in a guild + * @extends BaseGuildStruct + */ +export declare class Member extends BaseGuildStruct { + /** + * The member's user ID + */ + id: Snowflake; + /** + * The user this guild member represents + */ + user: User | undefined; + /** + * The user's guild nickname + */ + nick: string | null; + /** + * {@link Collection} of all {@link Role}s associated to this member + */ + roles: MemberRolesController; + /** + * Timestamp of when the member joined the guild + */ + joinedAt: Timestamp; + /** + * Timestamp of when the member start boosting the guild. + * Possibly null if the user has never boosted this server + */ + boostingSince: Timestamp | null; + /** + * The member's user presence data + */ + presence: MemberPresence | undefined; + constructor(bot: Bot, member: GatewayStruct, guild: Guild, presence?: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} member The member data + * @param {GatewayStruct} presence The member presence data + * @returns {this} + */ + init(member: GatewayStruct, presence?: GatewayStruct): this; + /** + * Modifies attributes of this member + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modify(options: ModifyMemberOptions): Promise; + /** + * Modifies the nickname of this member + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyNickname(nick: string): Promise; + /** + * Removes this member from the guild + * @returns {Promise} + */ + remove(): Promise; + /** + * Bans this member from the guild, and optionally deletes its previous messages. + * Requires the {@link Permission.BanMembers} permission + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + ban(options: MemberBanOptions): Promise; + /** + * @ignore + * @returns {string | undefined} + */ + toString(): string | undefined; + get voice(): VoiceState; + set voice(val: VoiceState); +} diff --git a/lib/structures/member/Member.js b/lib/structures/member/Member.js new file mode 100644 index 000000000..f96b64798 --- /dev/null +++ b/lib/structures/member/Member.js @@ -0,0 +1,97 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Member = void 0; +const MemberPresence_1 = require("./MemberPresence"); +const member_1 = require("../../controllers/member"); +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +const base_1 = require("../base"); +/** + * Representation of a Discord {@link User} in a guild + * @extends BaseGuildStruct + */ +class Member extends base_1.BaseGuildStruct { + constructor(bot, member, guild, presence) { + super(bot, guild, member); + this.roles = new member_1.MemberRolesController(this); + this.init(member, presence); + } + /** + * @ignore + * @param {GatewayStruct} member The member data + * @param {GatewayStruct} presence The member presence data + * @returns {this} + */ + init(member, presence) { + var _a; + this.id = (_a = member.user) === null || _a === void 0 ? void 0 : _a.id; + this.nick = member.nick; + if (presence) { + this.presence = new MemberPresence_1.MemberPresence(this.bot, presence, this); + this.guild.presences.set(this.id, this.presence); + } + if (member.user) { + if (this.bot.users.cache.has(this.id)) { + // Update the cached user to this member's user + // Store the cached user in this user field + this.user = this.bot.users.cache.get(this.id).init(member.user); + } + else { + // Create a new user instance and cache it + this.user = new User_1.User(this.bot, member.user); + this.bot.users.cache.add(this.user); + } + } + this.roles.cache.merge(this.guild.roles.cache.filter((_role, id) => { var _a; return (_a = member.roles) === null || _a === void 0 ? void 0 : _a.includes(id); })); + this.joinedAt = new Timestamp_1.Timestamp(member.joined_at); + this.boostingSince = member.premium_since ? new Timestamp_1.Timestamp(member.premium_since) : null; + return this; + } + /** + * Modifies attributes of this member + * @param {ModifyMemberOptions} options The options to modify for the member + * @returns {Promise} + */ + modify(options) { + return this.bot.api.modifyMember(this.guild.id, this.id, options); + } + /** + * Modifies the nickname of this member + * @param {string} nick The new nickname + * @returns {Promise} + */ + modifyNickname(nick) { + return this.bot.api.modifyMemberNickname(this.guild.id, this.id, nick); + } + /** + * Removes this member from the guild + * @returns {Promise} + */ + remove() { + return this.bot.api.removeMember(this.guild.id, this.id); + } + /** + * Bans this member from the guild, and optionally deletes its previous messages. + * Requires the {@link Permission.BanMembers} permission + * @param {MemberBanOptions} options The options for the ban + * @returns {Promise} + */ + ban(options) { + return this.bot.api.banMember(this.guild.id, this.id, options); + } + /** + * @ignore + * @returns {string | undefined} + */ + toString() { + var _a; + return (_a = this.user) === null || _a === void 0 ? void 0 : _a.toString(); + } + get voice() { + return this.guild.voiceStates.get(this.id); + } + set voice(val) { + this.guild.voiceStates.set(this.id, val); + } +} +exports.Member = Member; diff --git a/lib/structures/member/MemberPresence.d.ts b/lib/structures/member/MemberPresence.d.ts new file mode 100644 index 000000000..afabe947d --- /dev/null +++ b/lib/structures/member/MemberPresence.d.ts @@ -0,0 +1,229 @@ +import { Member } from './Member'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +import { Timestamp } from '../Timestamp'; +import { GatewayStruct, BaseGuildStruct } from '../base'; +import { PresenceActivityFlags } from '../flags'; +/** + * The type of the presence activity + */ +export declare enum PresenceActivityType { + Game = 0, + Streaming = 1, + Listening = 2, + Custom = 3 +} +/** + * The timestamps of the presence activity + */ +export interface PresenceActivityTimestamps { + /** + * Unix time in (milliseconds) of when the activity started + */ + start?: number; + /** + * Unix time (in milliseconds) of when the activity ends + */ + end?: number; +} +/** + * The information about the presence emoji for a custom status + */ +export interface PresenceActivityEmoji { + /** + * The name of the emoji + */ + name: string; + /** + * The ID of the emoji + */ + id?: Snowflake; + /** + * Whether this emoji is animated + */ + animated?: boolean; +} +/** + * Information for party showed in a member's presence activity + */ +export interface PresenceActivityParty { + /** + * The ID of the party + */ + id?: string; + /** + * Used to show the party's current and maximum size + * ```typescript + * [current_size, max_size] + * ``` + */ + size?: [number, number]; +} +/** + * Information for the presence's images and their hover texts + */ +export interface PresenceActivityAssets { + /** + * The ID for a large asset of the activity, usually a snowflake + */ + largeImage?: string; + /** + * Text displayed when hovering over the large image of the activity + */ + largeText?: string; + /** + * The ID for a small asset of the activity, usually a snowflake + */ + smallImage?: string; + /** + * Text displayed when hovering over the small image of the activity + */ + smallText?: string; +} +/** + * Presence secrets for Rich Presence joining and spectating + */ +export interface PresenceActivitySecrets { + /** + * The secret for joining a party + */ + join?: string; + /** + * The secret for spectating a game + */ + spectate?: string; + /** + * The secret for a specific instanced match + */ + match?: string; +} +/** + * Object presenting the member's current activity + */ +export interface PresenceGame { + /** + * The activity's name + */ + name: string; + /** + * The activity's type + */ + type: PresenceActivityType; + /** + * Stream URL, is validated when type is {@link PresenceActivityType.Streaming} + */ + url?: string | null; + /** + * Unix timestamp of when the activity was added to the member's session + */ + createdAt: number; + /** + * Unix timestamps for start and/or end of the game + */ + timestamps?: PresenceActivityTimestamps; + /** + * Application ID for the game + */ + applicationId?: Snowflake; + /** + * What the player is currently doing + */ + details?: string | null; + /** + * The member's current party status + */ + state?: string | null; + /** + * The emoji used for a custom status + */ + emoji?: PresenceActivityEmoji; + /** + * Information for the current party of the player + */ + party?: PresenceActivityParty; + /** + * Images for the presence and their hover texts + */ + assets?: PresenceActivityAssets; + /** + * Secrets for Rich Presence joining and spectating + */ + secrets?: PresenceActivitySecrets; + /** + * Whether or not the activity an instanced game session + */ + instance?: boolean; + /** + * Describes what the payload includes + */ + flags?: PresenceActivityFlags; +} +/** + * The presence status of a member + */ +export declare enum PresenceStatus { + Idle = "idle", + DND = "dnd", + Online = "online", + Invisible = "invisible", + Offline = "offline" +} +export declare enum PresenceClientStatusIndicator { + Online = "online", + Idle = "idle", + DND = "dnd" +} +/** + * Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is offline or invisible, the corresponding field is not present. + */ +export interface PresenceClientStatus { + desktop?: PresenceClientStatusIndicator; + mobile?: PresenceClientStatusIndicator; + web?: PresenceClientStatusIndicator; +} +/** + * A member's presence is their current state on a guild + */ +export declare class MemberPresence extends BaseGuildStruct { + /** + * The member associated to this presence + */ + readonly member: Member; + /** + * The member's current activity + */ + game: PresenceGame | undefined; + /** + * The member's current status + */ + status: PresenceStatus; + /** + * The member's current activities + */ + activities: PresenceGame[] | undefined; + /** + * The member's platform-dependent status + */ + clientStatus: PresenceClientStatus; + /** + * When the member started boosting the guild + */ + boostingSince: Timestamp | undefined; + /** + * The member's guild nickname (if one is set) + */ + nick: string | undefined | null; + constructor(bot: Bot, presence: GatewayStruct, member: Member); + /** + * @ignore + * @param {GatewayStruct} presence The presence data + * @returns {this} + */ + init(presence: GatewayStruct): this; + /** + * Builds a {@link PresenceGame} object from a received gateway activity object + * @param {GatewayStruct} activity The gateway activity object + * @returns {PresenceGame} + */ + private static parseActivity; +} diff --git a/lib/structures/member/MemberPresence.js b/lib/structures/member/MemberPresence.js new file mode 100644 index 000000000..94ff46189 --- /dev/null +++ b/lib/structures/member/MemberPresence.js @@ -0,0 +1,92 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MemberPresence = exports.PresenceClientStatusIndicator = exports.PresenceStatus = exports.PresenceActivityType = void 0; +const Timestamp_1 = require("../Timestamp"); +const base_1 = require("../base"); +const flags_1 = require("../flags"); +/** + * The type of the presence activity + */ +var PresenceActivityType; +(function (PresenceActivityType) { + PresenceActivityType[PresenceActivityType["Game"] = 0] = "Game"; + PresenceActivityType[PresenceActivityType["Streaming"] = 1] = "Streaming"; + PresenceActivityType[PresenceActivityType["Listening"] = 2] = "Listening"; + PresenceActivityType[PresenceActivityType["Custom"] = 3] = "Custom"; +})(PresenceActivityType = exports.PresenceActivityType || (exports.PresenceActivityType = {})); +/** + * The presence status of a member + */ +var PresenceStatus; +(function (PresenceStatus) { + PresenceStatus["Idle"] = "idle"; + PresenceStatus["DND"] = "dnd"; + PresenceStatus["Online"] = "online"; + PresenceStatus["Invisible"] = "invisible"; + PresenceStatus["Offline"] = "offline"; +})(PresenceStatus = exports.PresenceStatus || (exports.PresenceStatus = {})); +var PresenceClientStatusIndicator; +(function (PresenceClientStatusIndicator) { + PresenceClientStatusIndicator["Online"] = "online"; + PresenceClientStatusIndicator["Idle"] = "idle"; + PresenceClientStatusIndicator["DND"] = "dnd"; +})(PresenceClientStatusIndicator = exports.PresenceClientStatusIndicator || (exports.PresenceClientStatusIndicator = {})); +/** + * A member's presence is their current state on a guild + */ +class MemberPresence extends base_1.BaseGuildStruct { + constructor(bot, presence, member) { + super(bot, member.guild, presence); + this.member = member; + this.init(presence); + } + /** + * @ignore + * @param {GatewayStruct} presence The presence data + * @returns {this} + */ + init(presence) { + if (presence.game) { + this.game = MemberPresence.parseActivity(presence.game); + } + this.status = presence.status; + if (presence.activities) { + this.activities = presence.activities.map((activity) => MemberPresence.parseActivity(activity)); + } + this.clientStatus = presence.client_status; + if (presence.premium_since) { + this.boostingSince = new Timestamp_1.Timestamp(presence.premium_since); + } + this.nick = presence.nick; + return this; + } + /** + * Builds a {@link PresenceGame} object from a received gateway activity object + * @param {GatewayStruct} activity The gateway activity object + * @returns {PresenceGame} + */ + static parseActivity(activity) { + return { + name: activity.name, + type: activity.type, + url: activity.url, + createdAt: activity.created_at, + timestamps: activity.timestamps, + applicationId: activity.application_id, + details: activity.details, + state: activity.state, + emoji: activity.emoji, + party: activity.party, + assets: activity.assets && { + largeImage: activity.assets.large_image, + largeText: activity.assets.large_text, + smallImage: activity.assets.small_image, + smallText: activity.assets.small_text, + }, + secrets: activity.secrets, + instance: activity.instance, + flags: activity.flags && new flags_1.PresenceActivityFlags(activity.flags), + }; + } +} +exports.MemberPresence = MemberPresence; diff --git a/lib/structures/member/index.d.ts b/lib/structures/member/index.d.ts new file mode 100644 index 000000000..2287e2d58 --- /dev/null +++ b/lib/structures/member/index.d.ts @@ -0,0 +1,2 @@ +export * from './Member'; +export * from './MemberPresence'; diff --git a/lib/structures/member/index.js b/lib/structures/member/index.js new file mode 100644 index 000000000..277d30694 --- /dev/null +++ b/lib/structures/member/index.js @@ -0,0 +1,14 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Member"), exports); +__exportStar(require("./MemberPresence"), exports); diff --git a/lib/structures/message/Message.d.ts b/lib/structures/message/Message.d.ts new file mode 100644 index 000000000..0624262b6 --- /dev/null +++ b/lib/structures/message/Message.d.ts @@ -0,0 +1,295 @@ +import { MessageAttachment } from './MessageAttachment'; +import { MessageEmbed, MessageEmbedOptions } from './MessageEmbed'; +import { MessageMentions } from './MessageMentions'; +import Collection from '../../Collection'; +import { RequestFile } from '../../api/rateLimit'; +import { Bot } from '../../bot'; +import { MessageReactionsController } from '../../controllers/message'; +import { Snowflake } from '../../types'; +import { EmojiResolvable } from '../Emoji'; +import { Timestamp } from '../Timestamp'; +import { User } from '../User'; +import { BaseStruct, GatewayStruct } from '../base'; +import { TextBasedChannel } from '../channels'; +import { MessageFlags } from '../flags'; +import { Guild } from '../guild'; +import { Member } from '../member/Member'; +/** + * The type of a message + */ +declare enum MessageType { + Default = 0, + RecipientAdd = 1, + RecipientRemove = 2, + Call = 3, + ChannelNameChange = 4, + ChannelIconChange = 5, + ChannelPinnedMessage = 6, + GuildMemberJoin = 7, + UserPremiumGuildSubscription = 8, + UserPremiumGuildSubscriptionTier1 = 9, + UserPremiumGuildSubscriptionTier2 = 10, + UserPremiumGuildSubscriptionTier3 = 11, + ChannelFollowAdd = 12, + GuildDiscoveryDisqualified = 13, + GuildDiscoveryRequalified = 14 +} +export interface PartialMessage { + id: Snowflake; + guild?: Guild; + channel: TextBasedChannel; +} +/** + * Message activity types + */ +export declare enum MessageActivityType { + Join = 1, + Spectate = 2, + Listen = 3, + JoinRequest = 4 +} +/** + * Information about the message activity + */ +export interface MessageActivity { + /** + * The type of the message activity + */ + type: MessageActivityType; + /** + * The party_id from a Rich Presence event + * https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields + */ + partyId?: string; +} +/** + * Information about the message application + */ +export interface MessageApplication { + /** + * The ID of the application + */ + id: Snowflake; + /** + * The ID of the embed's image asset + */ + coverImage?: string; + /** + * The application's description + */ + description: string; + /** + * The ID of the application's icon + */ + icon: string | null; + /** + * The name of the application + */ + name: string; +} +/** + * Information about the message reference for crossposted messages + */ +export interface MessageReference { + /** + * ID of the originating message + */ + messageId?: Snowflake; + /** + * ID of the originating message's channel + */ + channelId: Snowflake; + /** + * ID of the originating message's guild + */ + guildId?: Snowflake; +} +/** + * The message data for when sending new messages + */ +export interface MessageData { + /** + * The message's raw content + */ + content?: string; + /** + * The message's embed data + */ + embed?: Omit | MessageEmbed; + /** + * The path to a file to send as an attachment + */ + files?: RequestFile[]; +} +/** + * The message options for when sending a new message + */ +export interface MessageOptions { + /** + * A nonce that can be used for identifying the sent message + */ + nonce?: number | string; + /** + * Whether this is a Text To Speech (TTS) message + */ + tts?: boolean; +} +/** + * The message data for when editing a message + */ +export interface MessageEditData extends MessageData { + /** + * The new flags of the message + */ + flags?: MessageFlags; +} +/** + * Represents a message sent in a {@link TextChannel} within Discord + */ +export declare class Message extends BaseStruct { + /** + * The message's ID + */ + id: Snowflake; + /** + * The guild the message was sent in. Possibly null if message was sent over a DM + */ + guild: Guild | undefined; + /** + * The channel the message was sent in + */ + channel: TextBasedChannel; + /** + * The author of this message. + * Might not be a valid {@link User} object if message was generated by a webhook + */ + author: User | undefined; + /** + * The member properties for this message's author. + * Might not exist if message was sent over a DM + */ + member: Member | undefined; + /** + * The content of the message + */ + content: string; + /** + * Timestamp of when this message was sent + */ + sentAt: Timestamp; + /** + * Timestamp of when this message was edited. + * Possibly null if message has not been edited + */ + editedAt: Timestamp | null; + /** + * Whether this was a TTS message + */ + tts: boolean; + /** + * Whether this message mentions everyone + */ + mentionsEveryone: boolean; + /** + * All types of mentionable instances mentioned in this message + */ + mentions: MessageMentions; + /** + * {@link Collection} of all {@link MessageAttachment}s attached to this message + */ + attachments: Collection; + /** + * All embedded content associated to this message + */ + embeds: MessageEmbed[]; + /** + * The message's reactions controller + */ + reactions: MessageReactionsController; + /** + * Used for validating a message was sent + */ + nonce: number | string | undefined; + /** + * Whether this message is pinned + */ + pinned: boolean; + /** + * The Webhook ID in case this message was generated by a Webhook + */ + webhookId: Snowflake | undefined; + /** + * The type of the message + */ + type: MessageType; + /** + * Sent with Rich Presence-related chat embeds + */ + activity: MessageActivity | undefined; + /** + * Sent with Rich Presence-related chat embeds + */ + application: MessageApplication | undefined; + /** + * Reference data sent with crossposted messages + */ + messageReference: MessageReference | undefined; + /** + * Describes extra features of the message + */ + flags: MessageFlags | undefined; + /** + * Whether this message is deleted from its channel + */ + deleted: boolean; + constructor(bot: Bot, message: GatewayStruct, channel: TextBasedChannel); + /** + * @ignore + * @param {GatewayStruct} message The message data + * @returns {this} + */ + init(message: GatewayStruct): this; + /** + * Creates a reaction for this message. + * Requires the {@link Permission.ReadMessageHistory} permission. + * Additionally, if nobody else has reacted to the message using this emoji, this requires the {@link Permission.AddReactions} permission + * @param {EmojiResolvable} emoji The emoji to react to this message with + * @returns {Promise} + */ + react(emoji: EmojiResolvable): Promise; + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + edit(data: string | MessageEditData): Promise; + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete(): Promise; + /** + * Pins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + pin(): Promise; + /** + * Unpins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + unpin(): Promise; +} +export {}; diff --git a/lib/structures/message/Message.js b/lib/structures/message/Message.js new file mode 100644 index 000000000..caa1cfc20 --- /dev/null +++ b/lib/structures/message/Message.js @@ -0,0 +1,174 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Message = exports.MessageActivityType = void 0; +const MessageAttachment_1 = require("./MessageAttachment"); +const MessageEmbed_1 = require("./MessageEmbed"); +const MessageMentions_1 = require("./MessageMentions"); +const MessageReaction_1 = require("./MessageReaction"); +const Collection_1 = __importDefault(require("../../Collection")); +const message_1 = require("../../controllers/message"); +const Timestamp_1 = require("../Timestamp"); +const User_1 = require("../User"); +const base_1 = require("../base"); +const flags_1 = require("../flags"); +/** + * The type of a message + */ +var MessageType; +(function (MessageType) { + MessageType[MessageType["Default"] = 0] = "Default"; + MessageType[MessageType["RecipientAdd"] = 1] = "RecipientAdd"; + MessageType[MessageType["RecipientRemove"] = 2] = "RecipientRemove"; + MessageType[MessageType["Call"] = 3] = "Call"; + MessageType[MessageType["ChannelNameChange"] = 4] = "ChannelNameChange"; + MessageType[MessageType["ChannelIconChange"] = 5] = "ChannelIconChange"; + MessageType[MessageType["ChannelPinnedMessage"] = 6] = "ChannelPinnedMessage"; + MessageType[MessageType["GuildMemberJoin"] = 7] = "GuildMemberJoin"; + MessageType[MessageType["UserPremiumGuildSubscription"] = 8] = "UserPremiumGuildSubscription"; + MessageType[MessageType["UserPremiumGuildSubscriptionTier1"] = 9] = "UserPremiumGuildSubscriptionTier1"; + MessageType[MessageType["UserPremiumGuildSubscriptionTier2"] = 10] = "UserPremiumGuildSubscriptionTier2"; + MessageType[MessageType["UserPremiumGuildSubscriptionTier3"] = 11] = "UserPremiumGuildSubscriptionTier3"; + MessageType[MessageType["ChannelFollowAdd"] = 12] = "ChannelFollowAdd"; + MessageType[MessageType["GuildDiscoveryDisqualified"] = 13] = "GuildDiscoveryDisqualified"; + MessageType[MessageType["GuildDiscoveryRequalified"] = 14] = "GuildDiscoveryRequalified"; +})(MessageType || (MessageType = {})); +/** + * Message activity types + */ +var MessageActivityType; +(function (MessageActivityType) { + MessageActivityType[MessageActivityType["Join"] = 1] = "Join"; + MessageActivityType[MessageActivityType["Spectate"] = 2] = "Spectate"; + MessageActivityType[MessageActivityType["Listen"] = 3] = "Listen"; + MessageActivityType[MessageActivityType["JoinRequest"] = 4] = "JoinRequest"; +})(MessageActivityType = exports.MessageActivityType || (exports.MessageActivityType = {})); +/** + * Represents a message sent in a {@link TextChannel} within Discord + */ +class Message extends base_1.BaseStruct { + constructor(bot, message, channel) { + super(bot, message); + this.channel = channel; + this.reactions = new message_1.MessageReactionsController(this); + this.deleted = false; + this.init(message); + } + /** + * @ignore + * @param {GatewayStruct} message The message data + * @returns {this} + */ + init(message) { + if (message.guild_id) { + this.guild = this.bot.guilds.cache.get(message.guild_id); + } + this.id = message.id; + this.webhookId = message.webhook_id; + if (!this.webhookId) { + this.author = new User_1.User(this.bot, message.author); + } + this.content = message.content; + this.sentAt = new Timestamp_1.Timestamp(message.timestamp); + this.editedAt = message.edited_timestamp ? new Timestamp_1.Timestamp(message.edited_timestamp) : null; + this.tts = message.tts; + this.mentionsEveryone = message.mention_everyone; + this.mentions = new MessageMentions_1.MessageMentions(this, { + users: message.mentions, + roles: message.mention_roles, + crosspostedChannels: message.mention_channels, + }); + this.attachments = new Collection_1.default(message.attachments.map((attachment) => [ + attachment.id, + new MessageAttachment_1.MessageAttachment(this, attachment), + ])); + this.embeds = message.embeds.map((embed) => new MessageEmbed_1.MessageEmbed(embed)); + if (message.reactions) { + this.reactions.cache.addMany(message.reactions.map((reaction) => new MessageReaction_1.MessageReaction(this, reaction))); + } + this.nonce = message.nonce; + this.pinned = message.pinned; + this.type = message.type; + if (message.activity) { + this.activity = { + type: message.activity.type, + partyId: message.activity.party_id, + }; + } + if (message.application) { + this.application = { + id: message.application.id, + coverImage: message.application.cover_image, + description: message.application.description, + icon: message.application.icon, + name: message.application.name, + }; + } + if (message.message_reference) { + this.messageReference = { + messageId: message.message_reference.message_id, + channelId: message.message_reference.channel_id, + guildId: message.message_reference.guild_id, + }; + } + if (message.flags) { + this.flags = new flags_1.MessageFlags(message.flags); + } + return this; + } + /** + * Creates a reaction for this message. + * Requires the {@link Permission.ReadMessageHistory} permission. + * Additionally, if nobody else has reacted to the message using this emoji, this requires the {@link Permission.AddReactions} permission + * @param {EmojiResolvable} emoji The emoji to react to this message with + * @returns {Promise} + */ + react(emoji) { + return this.bot.api.addMessageReaction(this.channel.id, this.id, emoji); + } + /** + * Edits a previously sent message. + * The fields `content`, `embed` and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the {@link Permission.ManageMessages} permission in the corresponding channel. + * @param {string | MessageEditData} data The updated message data. + * Can be: + * 1. Raw content to be edited to + * @example ```typescript + * message.edit('Updated content!'); + * ``` + * 2. A {@link MessageEditData} object, containing any of the fields + * @example ```typescript + * message.edit({ content: 'Updated content!', embed: { title: 'My Embed!' } }); + * ``` + * @returns {Promise} + */ + edit(data) { + return this.bot.api.editMessage(this.channel.id, this.id, data); + } + /** + * Deletes a message. + * If operating on a {@link GuildChannel} and trying to delete a message that was not sent by the current user, this endpoint requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete() { + return this.bot.api.deleteMessage(this.channel.id, this.id); + } + /** + * Pins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + pin() { + return this.bot.api.pinMessage(this.channel.id, this.id); + } + /** + * Unpins this message. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + unpin() { + return this.bot.api.unpinMessage(this.channel.id, this.id); + } +} +exports.Message = Message; diff --git a/lib/structures/message/MessageAttachment.d.ts b/lib/structures/message/MessageAttachment.d.ts new file mode 100644 index 000000000..76d0917c8 --- /dev/null +++ b/lib/structures/message/MessageAttachment.d.ts @@ -0,0 +1,46 @@ +import { Message } from './Message'; +import { Snowflake, Dimensions, Nullable } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +/** + * Represents an attachment added to a {@link Message} + + * @extends BaseStruct + */ +export declare class MessageAttachment extends BaseStruct { + /** + * The {@link Message} associated to this attachment + */ + message: Message; + /** + * The attachment's ID + */ + id: Snowflake; + /** + * Name of the attached file + */ + filename: string; + /** + * Size of the attached file in bytes + */ + size: number; + /** + * Source URL of the attached file + */ + url: string; + /** + * A proxies URL of the attached file + */ + proxyURL: string; + /** + * The width and height of the file. + * Possibly null if the attached file is not an image + */ + dimensions: Nullable; + constructor(message: Message, attachment: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} attachment The attachment data + * @returns {this} + */ + init(attachment: GatewayStruct): this; +} diff --git a/lib/structures/message/MessageAttachment.js b/lib/structures/message/MessageAttachment.js new file mode 100644 index 000000000..2d9b84be2 --- /dev/null +++ b/lib/structures/message/MessageAttachment.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageAttachment = void 0; +const base_1 = require("../base"); +/** + * Represents an attachment added to a {@link Message} + + * @extends BaseStruct + */ +class MessageAttachment extends base_1.BaseStruct { + constructor(message, attachment) { + super(message.bot, attachment); + this.message = message; + this.init(attachment); + } + /** + * @ignore + * @param {GatewayStruct} attachment The attachment data + * @returns {this} + */ + init(attachment) { + this.id = attachment.id; + this.filename = attachment.filename; + this.size = attachment.size; + this.url = attachment.url; + this.proxyURL = attachment.proxy_url; + this.dimensions = { + height: attachment.height, + width: attachment.width, + }; + return this; + } +} +exports.MessageAttachment = MessageAttachment; diff --git a/lib/structures/message/MessageEmbed.d.ts b/lib/structures/message/MessageEmbed.d.ts new file mode 100644 index 000000000..dc23ef7f9 --- /dev/null +++ b/lib/structures/message/MessageEmbed.d.ts @@ -0,0 +1,234 @@ +import { Dimensions } from '../../types'; +import { Timestamp } from '../Timestamp'; +import { GatewayStruct } from '../base'; +/** + * Embed types are "loosely defined" and, for the most part, are not used by our clients for rendering. Embed attributes power what is rendered. Embed types should be considered deprecated and might be removed in a future API version. + */ +export declare enum MessageEmbedType { + Rich = "rich", + Image = "image", + Video = "video", + Gifv = "gifv", + Article = "article", + Link = "link" +} +/** + * Message embed footer data + */ +export interface MessageEmbedFooter { + /** + * Footer text + */ + text: string; + /** + * URL of footer icon (only supports http(s) and attachments) + */ + iconURL?: string; + /** + * A proxied URL of footer icon + */ + proxyIconURL?: string; +} +/** + * Message embed image data + */ +export interface MessageEmbedImage { + /** + * Source URL of image (only supports http(s) and attachments) + */ + url?: string; + /** + * A proxied URL of the image + */ + proxyURL?: string; + /** + * {@link Dimensions} object containing the dimensions of the image + */ + dimensions: Partial; +} +/** + * Message embed thumbnail data + */ +export interface MessageEmbedThumbnail { + /** + * Source URL of thumbnail (only supports http(s) and attachments) + */ + url?: string; + /** + * A proxied URL of the thumbnail + */ + proxyURL?: string; + /** + * {@link Dimensions} object containing the dimensions of the thumbnail image + */ + dimensions: Partial; +} +/** + * Message embed video data + */ +export interface MessageEmbedVideo { + /** + * Source URL of the video + */ + url?: string; + /** + * {@link Dimensions} object containing the dimensions of the video + */ + dimensions: Partial; +} +/** + * Message embed provider data + */ +export interface MessageEmbedProvider { + /** + * Name of the provider + */ + name?: string; + /** + * URL of the provider + */ + url?: string; +} +/** + * Message embed author data + */ +export interface MessageEmbedAuthor { + /** + * Name of the author + */ + name?: string; + /** + * URL of the author + */ + url?: string; + /** + * URL of the author's icon (only supports http(s) and attachments) + */ + iconURL?: string; + /** + * A proxied URL of the author's icon + */ + proxyIconURL?: string; +} +/** + * Message embed field + */ +export interface MessageEmbedField { + /** + * Name of the embed field + */ + name: string; + /** + * The text content of the embed field + */ + content: string; + /** + * Whether or not this field should be displayed inline + */ + inline?: boolean; +} +/** + * Options for when creating message embeds + */ +export interface MessageEmbedOptions { + title?: string; + type?: MessageEmbedType; + description?: string; + url?: string; + timestamp?: number | Timestamp; + color?: number; + footer?: MessageEmbedFooter; + image?: MessageEmbedImage; + thumbnail?: MessageEmbedThumbnail; + video?: MessageEmbedVideo; + provider?: MessageEmbedProvider; + author?: MessageEmbedAuthor; + fields?: MessageEmbedField[]; +} +/** + * Represents an embed contained in a {@link Message} + */ +export declare class MessageEmbed implements MessageEmbedOptions { + /** + * The structure used to initialize this MessageEmbed object + * @ignore + */ + structure: GatewayStruct; + /** + * Title of this embed + */ + title: string | undefined; + /** + * Type of this embed (always "rich" for webhook embeds) + */ + type: MessageEmbedType | undefined; + /** + * Description of this embed + */ + description: string | undefined; + /** + * URL of this embed + */ + url: string | undefined; + /** + * Timestamp of this embed's content + */ + timestamp: Timestamp | undefined; + /** + * Color code of the embed + */ + color: number | undefined; + /** + * {@link MessageEmbedFooter} object containing this embed's footer data + */ + footer: MessageEmbedFooter | undefined; + /** + * {@link MessageEmbedImage} object containing this embed's image data + */ + image: MessageEmbedImage | undefined; + /** + * {@link MessageEmbedThumbnail} object containing this embed's thumbnail data + */ + thumbnail: MessageEmbedThumbnail | undefined; + /** + * {@link MessageEmbedVideo} object containing this embed's video data + */ + video: MessageEmbedVideo | undefined; + /** + * {@link MessageEmbedProvider} object containing this embed's provider data + */ + provider: MessageEmbedProvider | undefined; + /** + * {@link MessageEmbedFooter} object containing this embed's author data + */ + author: MessageEmbedAuthor | undefined; + /** + * {@link MessageEmbedField} array containing this embed's fields + */ + fields: MessageEmbedField[] | undefined; + constructor(embed: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} embed The embed data + * @returns {this} + */ + init(embed: GatewayStruct): this; + /** + * Returns the gateway structure of a given embed data + * @param {MessageEmbedOptions} embed The embed data + * @returns {GatewayStruct} + */ + static dataToStructure(embed: MessageEmbedOptions): GatewayStruct; + /** + * Returns the dimensions from a gateway structure + * @param {{height: number, width: number}} struct The gateway structure including the dimensions information + * @returns {Partial} + */ + private static getDimensions; + /** + * Creates a new MessageEmbed object from the given message embed options + * @param {MessageEmbedOptions} options The message embed + * @returns {MessageEmbed} + */ + static from(options: MessageEmbedOptions): MessageEmbed; +} diff --git a/lib/structures/message/MessageEmbed.js b/lib/structures/message/MessageEmbed.js new file mode 100644 index 000000000..ea799d22b --- /dev/null +++ b/lib/structures/message/MessageEmbed.js @@ -0,0 +1,151 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageEmbed = exports.MessageEmbedType = void 0; +const Timestamp_1 = require("../Timestamp"); +/** + * Embed types are "loosely defined" and, for the most part, are not used by our clients for rendering. Embed attributes power what is rendered. Embed types should be considered deprecated and might be removed in a future API version. + */ +var MessageEmbedType; +(function (MessageEmbedType) { + MessageEmbedType["Rich"] = "rich"; + MessageEmbedType["Image"] = "image"; + MessageEmbedType["Video"] = "video"; + MessageEmbedType["Gifv"] = "gifv"; + MessageEmbedType["Article"] = "article"; + MessageEmbedType["Link"] = "link"; +})(MessageEmbedType = exports.MessageEmbedType || (exports.MessageEmbedType = {})); +// TODO: Link this description to a guide page about Discord message embeds +/** + * Represents an embed contained in a {@link Message} + */ +class MessageEmbed { + constructor(embed) { + this.init(embed); + } + /** + * @ignore + * @param {GatewayStruct} embed The embed data + * @returns {this} + */ + init(embed) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; + this.structure = embed; + this.title = embed.title; + this.type = embed.type; + this.description = embed.description; + this.url = embed.url; + this.timestamp = new Timestamp_1.Timestamp(embed.timestamp); + this.color = embed.color; + this.footer = { + text: (_a = embed.footer) === null || _a === void 0 ? void 0 : _a.text, + iconURL: (_b = embed.footer) === null || _b === void 0 ? void 0 : _b.icon_url, + proxyIconURL: (_c = embed.footer) === null || _c === void 0 ? void 0 : _c.proxy_icon_url, + }; + this.image = { + url: (_d = embed.image) === null || _d === void 0 ? void 0 : _d.url, + proxyURL: (_e = embed.image) === null || _e === void 0 ? void 0 : _e.proxy_url, + dimensions: MessageEmbed.getDimensions(embed.image), + }; + this.thumbnail = { + url: (_f = embed.thumbnail) === null || _f === void 0 ? void 0 : _f.url, + proxyURL: (_g = embed.thumbnail) === null || _g === void 0 ? void 0 : _g.proxy_url, + dimensions: MessageEmbed.getDimensions(embed.thumbnail), + }; + this.video = { + url: (_h = embed.video) === null || _h === void 0 ? void 0 : _h.url, + dimensions: MessageEmbed.getDimensions(embed.video), + }; + this.provider = { + name: (_j = embed.video) === null || _j === void 0 ? void 0 : _j.name, + url: (_k = embed.video) === null || _k === void 0 ? void 0 : _k.url, + }; + this.author = { + name: (_l = embed.author) === null || _l === void 0 ? void 0 : _l.name, + url: (_m = embed.author) === null || _m === void 0 ? void 0 : _m.url, + iconURL: (_o = embed.author) === null || _o === void 0 ? void 0 : _o.icon_url, + proxyIconURL: (_p = embed.author) === null || _p === void 0 ? void 0 : _p.proxy_icon_url, + }; + this.fields = (_q = embed.fields) === null || _q === void 0 ? void 0 : _q.map((field) => ({ + name: field.name, + content: field.value, + inline: field.inline, + })); + return this; + } + /** + * Returns the gateway structure of a given embed data + * @param {MessageEmbedOptions} embed The embed data + * @returns {GatewayStruct} + */ + static dataToStructure(embed) { + return { + title: embed.title, + type: embed.type, + description: embed.description, + url: embed.url, + timestamp: embed.timestamp && + (embed.timestamp instanceof Timestamp_1.Timestamp + ? embed.timestamp.date + : new Date(embed.timestamp).toISOString()), + color: embed.color, + footer: embed.footer && { + text: embed.footer.text, + icon_url: embed.footer.iconURL, + proxy_icon_url: embed.footer.proxyIconURL, + }, + image: embed.image && { + url: embed.image.url, + proxy_url: embed.image.proxyURL, + height: embed.image.dimensions.height, + width: embed.image.dimensions.width, + }, + thumbnail: embed.thumbnail && { + url: embed.thumbnail.url, + proxy_url: embed.thumbnail.proxyURL, + height: embed.thumbnail.dimensions.height, + width: embed.thumbnail.dimensions.width, + }, + video: embed.video && { + url: embed.video.url, + height: embed.video.dimensions.height, + width: embed.video.dimensions.width, + }, + provider: embed.provider && { + name: embed.provider.name, + url: embed.provider.url, + }, + author: embed.author && { + name: embed.author.name, + url: embed.author.url, + icon_url: embed.author.iconURL, + proxy_icon_url: embed.author.proxyIconURL, + }, + fields: embed.fields && + embed.fields.map((field) => ({ + name: field.name, + value: field.content, + inline: field.inline, + })), + }; + } + /** + * Returns the dimensions from a gateway structure + * @param {{height: number, width: number}} struct The gateway structure including the dimensions information + * @returns {Partial} + */ + static getDimensions(struct) { + return { + height: struct === null || struct === void 0 ? void 0 : struct.height, + width: struct === null || struct === void 0 ? void 0 : struct.width, + }; + } + /** + * Creates a new MessageEmbed object from the given message embed options + * @param {MessageEmbedOptions} options The message embed + * @returns {MessageEmbed} + */ + static from(options) { + return new MessageEmbed(MessageEmbed.dataToStructure(options)); + } +} +exports.MessageEmbed = MessageEmbed; diff --git a/lib/structures/message/MessageMentions.d.ts b/lib/structures/message/MessageMentions.d.ts new file mode 100644 index 000000000..e46d25476 --- /dev/null +++ b/lib/structures/message/MessageMentions.d.ts @@ -0,0 +1,54 @@ +import { Message } from './Message'; +import Collection from '../../Collection'; +import { Snowflake } from '../../types'; +import { Role } from '../Role'; +import { User } from '../User'; +import { BaseStruct, GatewayStruct } from '../base'; +import { GuildChannel } from '../channels'; +import { Member } from '../member/Member'; +interface MentionTypes { + users: GatewayStruct[]; + roles: Snowflake[]; + crosspostedChannels: GatewayStruct[]; +} +/** + * Represents all mentions (members, roles, channels) that appear in a {@link Message} + + * @extends BaseStruct + */ +export declare class MessageMentions extends BaseStruct { + /** + * The {@link Message} associated to these mentions + */ + message: Message; + /** + * {@link Collection} of all {@link User}s mentioned in this message + */ + users: Collection; + /** + * {@link Collection} of all {@link Member}s mentioned in this message + */ + members: Collection; + /** + * {@link Collection} of all {@link Role}s mentioned in this message + */ + roles: Collection; + crosspostedChannels: Collection; + /** + * Cache for all channel mentions found in the message + */ + private _channels; + constructor(message: Message, mentions: Partial); + /** + * @ignore + * @param {GatewayStruct} mentions The message mentions data + * @returns {this} + */ + init(mentions: Partial): this; + /** + * Fetches or retrieves from cache all channels mentioned in the message + * @type {Collection | undefined} + */ + get channels(): Collection | undefined; +} +export {}; diff --git a/lib/structures/message/MessageMentions.js b/lib/structures/message/MessageMentions.js new file mode 100644 index 000000000..a6aa42dc6 --- /dev/null +++ b/lib/structures/message/MessageMentions.js @@ -0,0 +1,75 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageMentions = void 0; +const Collection_1 = __importDefault(require("../../Collection")); +const User_1 = require("../User"); +const base_1 = require("../base"); +const Member_1 = require("../member/Member"); +const mentionsRegexp = { + channels: new RegExp(/<#(\d{17,19})>/g), +}; +/** + * Represents all mentions (members, roles, channels) that appear in a {@link Message} + + * @extends BaseStruct + */ +class MessageMentions extends base_1.BaseStruct { + constructor(message, mentions) { + super(message.bot, mentions); + this.message = message; + this.users = new Collection_1.default(); + this.members = new Collection_1.default(); + this.roles = new Collection_1.default(); + this.crosspostedChannels = new Collection_1.default(); + this.init(mentions); + } + /** + * @ignore + * @param {GatewayStruct} mentions The message mentions data + * @returns {this} + */ + init(mentions) { + if (mentions.users) { + this.users.merge(mentions.users.map(user => [user.id, new User_1.User(this.bot, user)])); + } + // We have to use mentions.users to obtain the members + if (mentions.users && this.message.guild) { + this.members.merge(mentions.users + .filter(user => user.member) + .map(user => [ + user.id, + new Member_1.Member(this.bot, Object.assign(Object.assign({}, user.member), { user }), this.message.guild), + ])); + } + if (mentions.roles && this.message.guild) { + this.roles.merge(this.message.guild.roles.cache.filter(role => mentions.roles.includes(role.id))); + } + if (mentions.crosspostedChannels && this.message.guild) { + this.crosspostedChannels.merge(this.message.guild.channels.cache.filter(channel => mentions.crosspostedChannels.some(mention => mention.guild_id === channel.guild.id && mention.id === channel.id))); + } + return this; + } + /** + * Fetches or retrieves from cache all channels mentioned in the message + * @type {Collection | undefined} + */ + get channels() { + if (this._channels) + return this._channels; + if (this.message.guild) { + this._channels = new Collection_1.default(); + let matches; + while ((matches = mentionsRegexp.channels.exec(this.message.content))) { + const channel = this.message.guild.channels.cache.get(matches[1]); + if (channel) { + this._channels.set(channel.id, channel); + } + } + return this._channels; + } + } +} +exports.MessageMentions = MessageMentions; diff --git a/lib/structures/message/MessageReaction.d.ts b/lib/structures/message/MessageReaction.d.ts new file mode 100644 index 000000000..da4733f42 --- /dev/null +++ b/lib/structures/message/MessageReaction.d.ts @@ -0,0 +1,55 @@ +import { Message } from './Message'; +import Collection from '../../Collection'; +import { ReactionUsersController } from '../../controllers/reaction'; +import { Snowflake } from '../../types'; +import { Emoji } from '../Emoji'; +import { BaseStruct, GatewayStruct } from '../base'; +import { Member } from '../member/Member'; +/** + * Holds all users that reacted to a {@link Message} with a specific {@link Emoji} + */ +export declare class MessageReaction extends BaseStruct { + /** + * The message this reaction is attached to + */ + message: Message; + /** + * The times this emoji has been used to react + */ + count: number; + /** + * Whether the bot reacted using this emoji + */ + botReacted: boolean | undefined; + /** + * The reaction's users controller + */ + users: ReactionUsersController; + /** + * The members that added this reaction + */ + members: Collection; + /** + * The emoji this reaction used. This emoji is partial + */ + emoji: Emoji; + constructor(message: Message, reaction: GatewayStruct); + /** + * @ignore + * @param {GatewayStruct} reaction The reaction data + * @returns {this} + */ + init(reaction: GatewayStruct): this; + /** + * Deletes all reactions for this emoji. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete(): Promise; + /** + * The ID of the emoji this reaction stores + * Serves as an identifier for this reaction + * @type {string} + */ + get id(): string; +} diff --git a/lib/structures/message/MessageReaction.js b/lib/structures/message/MessageReaction.js new file mode 100644 index 000000000..72410ac58 --- /dev/null +++ b/lib/structures/message/MessageReaction.js @@ -0,0 +1,51 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageReaction = void 0; +const Collection_1 = __importDefault(require("../../Collection")); +const reaction_1 = require("../../controllers/reaction"); +const Emoji_1 = require("../Emoji"); +const base_1 = require("../base"); +/** + * Holds all users that reacted to a {@link Message} with a specific {@link Emoji} + */ +class MessageReaction extends base_1.BaseStruct { + constructor(message, reaction) { + super(message.bot, reaction); + this.message = message; + this.users = new reaction_1.ReactionUsersController(this); + this.members = new Collection_1.default(); + this.emoji = new Emoji_1.Emoji(this.bot, reaction.emoji); + this.init(reaction); + } + /** + * @ignore + * @param {GatewayStruct} reaction The reaction data + * @returns {this} + */ + init(reaction) { + this.count = reaction.count || 0; + this.botReacted = reaction.me; + this.emoji = new Emoji_1.Emoji(this.bot, reaction.emoji); + return this; + } + /** + * Deletes all reactions for this emoji. + * Requires the {@link Permission.ManageMessages} permission + * @returns {Promise} + */ + delete() { + return this.bot.api.removeMessageReactionsEmoji(this.message.channel.id, this.message.id, this.emoji); + } + /** + * The ID of the emoji this reaction stores + * Serves as an identifier for this reaction + * @type {string} + */ + get id() { + return this.emoji.id; + } +} +exports.MessageReaction = MessageReaction; diff --git a/lib/structures/message/index.d.ts b/lib/structures/message/index.d.ts new file mode 100644 index 000000000..a33bee20a --- /dev/null +++ b/lib/structures/message/index.d.ts @@ -0,0 +1,5 @@ +export * from './Message'; +export * from './MessageAttachment'; +export * from './MessageEmbed'; +export * from './MessageMentions'; +export * from './MessageReaction'; diff --git a/lib/structures/message/index.js b/lib/structures/message/index.js new file mode 100644 index 000000000..42b822a21 --- /dev/null +++ b/lib/structures/message/index.js @@ -0,0 +1,17 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Message"), exports); +__exportStar(require("./MessageAttachment"), exports); +__exportStar(require("./MessageEmbed"), exports); +__exportStar(require("./MessageMentions"), exports); +__exportStar(require("./MessageReaction"), exports); diff --git a/lib/structures/voice/Connection.d.ts b/lib/structures/voice/Connection.d.ts new file mode 100644 index 000000000..bf9f2ae1e --- /dev/null +++ b/lib/structures/voice/Connection.d.ts @@ -0,0 +1,20 @@ +import { GuildVoice } from './GuildVoice'; +import { UDPSocket } from './UDPSocket'; +import { VoiceWebSocket } from './VoiceWebSocket'; +export declare class Connection { + sockets: { + ws: VoiceWebSocket; + udp: UDPSocket; + }; + token: string; + active: boolean; + private _endpoint; + /** + * The endpoint voice websocket is going to connect + * @type {string} + */ + set endpoint(val: string); + get endpoint(): string; + voice: GuildVoice; + constructor(voice: GuildVoice); +} diff --git a/lib/structures/voice/Connection.js b/lib/structures/voice/Connection.js new file mode 100644 index 000000000..777271a01 --- /dev/null +++ b/lib/structures/voice/Connection.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Connection = void 0; +const UDPSocket_1 = require("./UDPSocket"); +const VoiceWebSocket_1 = require("./VoiceWebSocket"); +class Connection { + constructor(voice) { + this.active = false; + this._endpoint = ''; + this.voice = voice; + } + /** + * The endpoint voice websocket is going to connect + * @type {string} + */ + set endpoint(val) { + if (this._endpoint === val) + return; + else { + if (this.sockets.ws) { + this.sockets.ws.close(); + this.sockets.ws.removeAllListeners(); + } + if (this.sockets.udp) { + this.sockets.udp.close(); + this.sockets.udp.removeAllListeners(); + } + this.active = true; + this.sockets = { + ws: new VoiceWebSocket_1.VoiceWebSocket(this), + udp: new UDPSocket_1.UDPSocket(this), + }; + } + } + get endpoint() { + return this._endpoint; + } +} +exports.Connection = Connection; diff --git a/lib/structures/voice/GuildVoice.d.ts b/lib/structures/voice/GuildVoice.d.ts new file mode 100644 index 000000000..0ef7cef9c --- /dev/null +++ b/lib/structures/voice/GuildVoice.d.ts @@ -0,0 +1,24 @@ +import { Connection } from './Connection'; +import { Guild } from '..'; +import { Bot } from '../../bot'; +import { Snowflake } from '../../types'; +/** + * Represents a voice connection of a guild + */ +export declare class GuildVoice { + /** + * The {@link Bot} operating this structure + */ + bot: Bot; + /** + * The {@link Guild} that this voice connection belongs + * @param guild Guild + */ + guild: Guild; + connection: Connection; + constructor(guild: Guild); + join(channelId: Snowflake, options: { + mute?: boolean; + deaf?: boolean; + }): Promise; +} diff --git a/lib/structures/voice/GuildVoice.js b/lib/structures/voice/GuildVoice.js new file mode 100644 index 000000000..c27b36b3a --- /dev/null +++ b/lib/structures/voice/GuildVoice.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GuildVoice = void 0; +const Connection_1 = require("./Connection"); +const socket_1 = require("../../socket"); +/** + * Represents a voice connection of a guild + */ +class GuildVoice { + constructor(guild) { + this.connection = new Connection_1.Connection(this); + this.guild = guild; + this.bot = guild.bot; + } + join(channelId, options) { + this.guild.shard.send({ + op: 2, + d: { + guild_id: this.guild.id, + channel_id: channelId, + self_mute: !!options.mute, + self_deaf: !!options.deaf, + }, + }); + return new Promise(resolve => { + const listener = ((guild, voiceServer) => { + if (guild.id === this.guild.id) { + this.connection.endpoint = voiceServer.endpoint; + this.connection.token = voiceServer.token; + this.bot.events.removeListener(socket_1.BotEvent.VoiceServerUpdate, listener); + resolve(this.connection); + } + }).bind(this); + this.bot.events.on(socket_1.BotEvent.VoiceServerUpdate, listener); + }); + } +} +exports.GuildVoice = GuildVoice; diff --git a/lib/structures/voice/UDPSocket.d.ts b/lib/structures/voice/UDPSocket.d.ts new file mode 100644 index 000000000..9891f3748 --- /dev/null +++ b/lib/structures/voice/UDPSocket.d.ts @@ -0,0 +1,34 @@ +/// +import { Socket } from 'dgram'; +import { EventEmitter } from 'events'; +import { Readable } from 'stream'; +import { Connection } from './Connection'; +export declare class UDPSocket extends EventEmitter { + connection: Connection; + socket: Socket; + private auth?; + private nonce; + secretKeys: Buffer; + /** + * PCM Raw + */ + PCMOut: Readable; + /** + * Opus Encoded + */ + OpusOut: Readable; + private OpusEncoder; + constructor(connection: Connection); + discoverIP(server: { + ip: string; + port: number; + ssrc: number; + }): Promise<{ + ip: string; + port: number; + }>; + start(): void; + stop(): void; + decryptPackage(buffer: Buffer): Buffer | Error; + close(): void; +} diff --git a/lib/structures/voice/UDPSocket.js b/lib/structures/voice/UDPSocket.js new file mode 100644 index 000000000..af7613c25 --- /dev/null +++ b/lib/structures/voice/UDPSocket.js @@ -0,0 +1,110 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UDPSocket = void 0; +const dgram_1 = require("dgram"); +const events_1 = require("events"); +const stream_1 = require("stream"); +const socket_1 = require("../../socket"); +let LibSodium; +let OpusScript; +try { + LibSodium = require("sodium-native"); //eslint-disable-line +} +catch (err) { } //eslint-disable-line +try { + OpusScript = require("opusscript").OpusScript; //eslint-disable-line +} +catch (err) { } //eslint-disable-line +class UDPSocket extends events_1.EventEmitter { + constructor(connection) { + super(); + this.nonce = Buffer.alloc(24); + /** + * PCM Raw + */ + this.PCMOut = new stream_1.Readable(); + /** + * Opus Encoded + */ + this.OpusOut = new stream_1.Readable(); + this.connection = connection; + this.socket = dgram_1.createSocket('udp4'); + } + // This method should be called before the udp connection started! + discoverIP(server) { + return __awaiter(this, void 0, void 0, function* () { + this.auth = server; + return new Promise(resolve => { + const message = Buffer.alloc(70); + message.writeUIntBE(this.auth.ssrc, 0, 4); + this.socket.send(message, 0, message.length, this.auth.port, this.auth.ip); + this.socket.once('message', message => { + const local = { ip: '', port: 0 }; + for (let i = 4; i < message.indexOf(0, i); i++) { + local.ip += String.fromCharCode(message[i]); + } + local.port = parseInt(message.readUIntBE(message.length - 2, 2).toString(10)); + resolve(local); + }); + }); + }); + } + start() { + if (!OpusScript) + throw new Error('OpusScript not found!'); + if (!LibSodium) + throw new Error('LibSodium not found!'); + if (!this.OpusEncoder) { + this.OpusEncoder = new OpusScript(OpusScript.VALID_SAMPLING_RATES[4], 2, OpusScript.Application.AUDIO); + this.OpusEncoder.encoderCTL(4002, 64000); + } + this.socket.on('message', message => { + const data = this.decryptPackage(message); + if (data instanceof Error) + return this.connection.voice.bot.events.emit(socket_1.BotEvent.Debug, data); + this.OpusOut.push(data); + this.PCMOut.push(this.OpusEncoder.decode(data)); + }); + } + stop() { + this.socket.removeAllListeners(); + } + decryptPackage(buffer) { + buffer.copy(this.nonce, 0, 0, 12); + const nonce = Buffer.alloc(24); + buffer.copy(nonce, 0, 0, 12); + let data; + data = Buffer.allocUnsafe(buffer.length - 12 - LibSodium.crypto_secretbox_MACBYTES); + try { + LibSodium.crypto_secretbox_open_easy(data, buffer.slice(12), this.nonce, Buffer.from(this.secretKeys)); + } + catch (err) { + return err; + } + if ((buffer[0] & 0b1111) > 0) { + data = data.slice(buffer[0] & (0b1111 * 4)); + } + if (buffer[0] & 0b10000) { + const l = (data[2] << 8) | data[3]; + let index = 4 + l * 4; + while (data[index] == 0) { + ++index; + } + data = data.slice(index); + } + return data; + } + close() { + this.socket.close(); + } +} +exports.UDPSocket = UDPSocket; diff --git a/lib/structures/voice/VoiceHeartbeats.d.ts b/lib/structures/voice/VoiceHeartbeats.d.ts new file mode 100644 index 000000000..f650e0bad --- /dev/null +++ b/lib/structures/voice/VoiceHeartbeats.d.ts @@ -0,0 +1,38 @@ +/// +import { VoiceWebSocket } from './VoiceWebSocket'; +interface HeartbeatInterval { + timeout: number; + executor?: NodeJS.Timeout; +} +/** + * Handles the sending and receiving of Discord heartbeats + */ +export declare class VoiceHeartbeats { + private readonly ws; + private readonly sequence; + private acked; + interval: HeartbeatInterval; + constructor(ws: VoiceWebSocket); + /** + * Starts the heartbeat interval + */ + start(): void; + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat(): void; + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat(): void; + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + private get heartbeatData(); + /** + * Called when a heartbeat is acked + */ + receivedAck(): void; +} +export {}; diff --git a/lib/structures/voice/VoiceHeartbeats.js b/lib/structures/voice/VoiceHeartbeats.js new file mode 100644 index 000000000..d8dcdf974 --- /dev/null +++ b/lib/structures/voice/VoiceHeartbeats.js @@ -0,0 +1,54 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoiceHeartbeats = void 0; +/** + * Handles the sending and receiving of Discord heartbeats + */ +class VoiceHeartbeats { + constructor(ws) { + this.ws = ws; + this.sequence = ws.sequnce; + this.acked = true; + this.interval = { + timeout: 0, + }; + } + /** + * Starts the heartbeat interval + */ + start() { + this.interval.executor = setInterval(this.sendHeartbeat.bind(this), this.interval.timeout); + } + /** + * Sends a heartbeat and checks if the last one acked + */ + sendHeartbeat() { + if (!this.acked) + return; // Instead of reconnecting to the voice ws it just skips this heartbeat. + this.acked = false; + this.ws.send(this.heartbeatData); + } + /** + * Resets the interval timeout and stops the interval + */ + stopHeartbeat() { + this.interval.timeout = -1; + if (this.interval.executor) { + clearInterval(this.interval.executor); + } + } + /** + * The data required for when sending a heartbeat + * @type {HeartbeatData} + */ + get heartbeatData() { + return { op: 3, d: this.interval.timeout }; + } + /** + * Called when a heartbeat is acked + */ + receivedAck() { + this.acked = true; + } +} +exports.VoiceHeartbeats = VoiceHeartbeats; diff --git a/lib/structures/voice/VoiceState.d.ts b/lib/structures/voice/VoiceState.d.ts new file mode 100644 index 000000000..73aa55187 --- /dev/null +++ b/lib/structures/voice/VoiceState.d.ts @@ -0,0 +1,35 @@ +import { Bot } from '../../bot'; +import { Nullable, Snowflake } from '../../types'; +import { BaseStruct, GatewayStruct } from '../base'; +import { GuildVoiceChannel } from '../channels/GuildVoiceChannel'; +import { Member } from '../member'; +interface VoiceStateData { + guild_id: Snowflake; + channel_id: Nullable; + user_id: Snowflake; + session_id: string; + deaf: boolean; + mute: boolean; + self_deaf: boolean; + self_mute: boolean; + self_stream: boolean; + self_video: boolean; + suppress: boolean; +} +declare enum MUTE_STATE { + SELF = 0, + FORCE = 1, + NONE = 2 +} +export declare class VoiceState extends BaseStruct { + private channelId; + sessionId: string; + deafen: MUTE_STATE; + muted: MUTE_STATE; + member: Member; + static MUTE_STATE: typeof MUTE_STATE; + constructor(bot: Bot, member: Member, voiceState: GatewayStruct); + init(voiceState: VoiceStateData): this; + get currentChannel(): Nullable; +} +export {}; diff --git a/lib/structures/voice/VoiceState.js b/lib/structures/voice/VoiceState.js new file mode 100644 index 000000000..ff5d7daa7 --- /dev/null +++ b/lib/structures/voice/VoiceState.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoiceState = void 0; +const base_1 = require("../base"); +var MUTE_STATE; +(function (MUTE_STATE) { + MUTE_STATE[MUTE_STATE["SELF"] = 0] = "SELF"; + MUTE_STATE[MUTE_STATE["FORCE"] = 1] = "FORCE"; + MUTE_STATE[MUTE_STATE["NONE"] = 2] = "NONE"; +})(MUTE_STATE || (MUTE_STATE = {})); +class VoiceState extends base_1.BaseStruct { + constructor(bot, member, voiceState) { + super(bot, voiceState); + this.member = member; + this.init(voiceState); + } + init(voiceState) { + if (voiceState.self_mute) + this.muted = MUTE_STATE.SELF; + else if (voiceState.mute) + this.muted = MUTE_STATE.FORCE; + else + this.muted = MUTE_STATE.NONE; + if (voiceState.self_deaf) + this.deafen = MUTE_STATE.SELF; + else if (voiceState.deaf) + this.deafen = MUTE_STATE.FORCE; + else + this.deafen = MUTE_STATE.NONE; + this.channelId = voiceState.channel_id; + return this; + } + get currentChannel() { + const channel = this.bot.channels.cache.get(this.channelId); + return channel ? channel : null; + } +} +exports.VoiceState = VoiceState; +VoiceState.MUTE_STATE = MUTE_STATE; diff --git a/lib/structures/voice/VoiceWebSocket.d.ts b/lib/structures/voice/VoiceWebSocket.d.ts new file mode 100644 index 000000000..1a902e936 --- /dev/null +++ b/lib/structures/voice/VoiceWebSocket.d.ts @@ -0,0 +1,41 @@ +/// +import { EventEmitter } from 'events'; +import { Connection } from './Connection'; +import { PayloadData } from '../../socket'; +export declare enum VOICE_OPCODES { + IDENTIFY = 0, + SELECT_PROTOCOL = 1, + READY = 2, + HEARTBEAT = 3, + SESSION_DESCRIPTION = 4, + SPEAKING = 5, + HEARTBEAT_ACK = 6, + RESUME = 7, + HELLO = 8, + RESUMED = 9, + DISCONNECT = 13 +} +/** + * A payload thats gonna be sent to the Discord Voice Server + */ +export interface VoiceCommand { + op: VOICE_OPCODES; + d: PayloadData; +} +/** + * A payload received from the Discord Voice Server gateway + */ +export interface VoicePayload extends VoiceCommand { + s: number; +} +export declare class VoiceWebSocket extends EventEmitter { + connection: Connection; + private ws?; + private hearbeat?; + sequnce: number; + constructor(connection: Connection); + open(): void; + private onMessage; + send(data: VoiceCommand): void; + close(): void; +} diff --git a/lib/structures/voice/VoiceWebSocket.js b/lib/structures/voice/VoiceWebSocket.js new file mode 100644 index 000000000..7508842b3 --- /dev/null +++ b/lib/structures/voice/VoiceWebSocket.js @@ -0,0 +1,99 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoiceWebSocket = exports.VOICE_OPCODES = void 0; +const events_1 = require("events"); +const ws_1 = __importDefault(require("ws")); +const VoiceHeartbeats_1 = require("./VoiceHeartbeats"); +var VOICE_OPCODES; +(function (VOICE_OPCODES) { + VOICE_OPCODES[VOICE_OPCODES["IDENTIFY"] = 0] = "IDENTIFY"; + VOICE_OPCODES[VOICE_OPCODES["SELECT_PROTOCOL"] = 1] = "SELECT_PROTOCOL"; + VOICE_OPCODES[VOICE_OPCODES["READY"] = 2] = "READY"; + VOICE_OPCODES[VOICE_OPCODES["HEARTBEAT"] = 3] = "HEARTBEAT"; + VOICE_OPCODES[VOICE_OPCODES["SESSION_DESCRIPTION"] = 4] = "SESSION_DESCRIPTION"; + VOICE_OPCODES[VOICE_OPCODES["SPEAKING"] = 5] = "SPEAKING"; + VOICE_OPCODES[VOICE_OPCODES["HEARTBEAT_ACK"] = 6] = "HEARTBEAT_ACK"; + VOICE_OPCODES[VOICE_OPCODES["RESUME"] = 7] = "RESUME"; + VOICE_OPCODES[VOICE_OPCODES["HELLO"] = 8] = "HELLO"; + VOICE_OPCODES[VOICE_OPCODES["RESUMED"] = 9] = "RESUMED"; + VOICE_OPCODES[VOICE_OPCODES["DISCONNECT"] = 13] = "DISCONNECT"; +})(VOICE_OPCODES = exports.VOICE_OPCODES || (exports.VOICE_OPCODES = {})); +class VoiceWebSocket extends events_1.EventEmitter { + constructor(connection) { + super(); + this.connection = connection; + } + open() { + if (!this.connection.endpoint) + throw new Error('A voice gateway connection was tried to be established before endpoint declaration.'); + this.ws = new ws_1.default(`wss://${this.connection.endpoint.split(':')[0]}/?v=4`); + this.ws.on('message', this.onMessage.bind(this)); + this.hearbeat = new VoiceHeartbeats_1.VoiceHeartbeats(this); + this.send({ + op: VOICE_OPCODES.IDENTIFY, + d: { + server_id: this.connection.voice.guild.id, + user_id: this.connection.voice.bot.user.id, + session_id: this.connection.voice.guild.shard.sessionId, + token: this.connection.token, + }, + }); + } + onMessage(message) { + return __awaiter(this, void 0, void 0, function* () { + this.sequnce = message.s; + switch (message.op) { + case VOICE_OPCODES.HELLO: { + this.hearbeat.interval.timeout = message.d.heartbeat_interval; + this.hearbeat.start(); + break; + } + case VOICE_OPCODES.READY: { + const ip = yield this.connection.sockets.udp.discoverIP({ + ip: message.d.ip, + port: message.d.port, + ssrc: message.d.ssrc, + }); + this.send({ + op: VOICE_OPCODES.SELECT_PROTOCOL, + d: { + protocol: 'udp', + data: { + address: ip.ip, + port: ip.port, + mode: 'xsalsa20_poly1305', + }, + }, + }); + break; + } + case VOICE_OPCODES.SESSION_DESCRIPTION: { + this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys); + break; + } + } + }); + } + send(data) { + if (this.ws) + this.ws.send(JSON.stringify(data)); + } + close() { + var _a, _b; + (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close(); + (_b = this.hearbeat) === null || _b === void 0 ? void 0 : _b.stopHeartbeat(); + } +} +exports.VoiceWebSocket = VoiceWebSocket; diff --git a/lib/structures/voice/index.d.ts b/lib/structures/voice/index.d.ts new file mode 100644 index 000000000..3babae3d0 --- /dev/null +++ b/lib/structures/voice/index.d.ts @@ -0,0 +1,6 @@ +export * from './Connection'; +export * from './GuildVoice'; +export * from './UDPSocket'; +export * from './VoiceHeartbeats'; +export * from './VoiceState'; +export * from './VoiceWebSocket'; diff --git a/lib/structures/voice/index.js b/lib/structures/voice/index.js new file mode 100644 index 000000000..bbb6c6e29 --- /dev/null +++ b/lib/structures/voice/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./Connection"), exports); +__exportStar(require("./GuildVoice"), exports); +__exportStar(require("./UDPSocket"), exports); +__exportStar(require("./VoiceHeartbeats"), exports); +__exportStar(require("./VoiceState"), exports); +__exportStar(require("./VoiceWebSocket"), exports); diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts new file mode 100644 index 000000000..fcb073fef --- /dev/null +++ b/lib/types/index.d.ts @@ -0,0 +1 @@ +export * from './types'; diff --git a/lib/types/index.js b/lib/types/index.js new file mode 100644 index 000000000..a022a97e2 --- /dev/null +++ b/lib/types/index.js @@ -0,0 +1,13 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./types"), exports); diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts new file mode 100644 index 000000000..84e086bb4 --- /dev/null +++ b/lib/types/types.d.ts @@ -0,0 +1,19 @@ +/** + * Discord Snowflake. + * More information can be found here {@link https://discordapp.com/developers/docs/reference#snowflakes} + */ +export declare type Snowflake = string; +export declare type ShardId = number; +/** + * Turns all fields of a given type to be nullable + */ +export declare type Nullable = { + [P in keyof T]: T[P] | null; +} | null; +/** + * Width and height dimensions. Mostly used for images and/or videos + */ +export interface Dimensions { + height: number; + width: number; +} diff --git a/lib/types/types.js b/lib/types/types.js new file mode 100644 index 000000000..c8ad2e549 --- /dev/null +++ b/lib/types/types.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); From 3a7f57199d22bcd66724b938e0eb1187dcf67aea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 00:42:45 +0000 Subject: [PATCH 17/48] ci(Compile): compiled latest push --- lib/bot/handlers/events/events.d.ts | 2 +- lib/controllers/guild/GuildChannelsController.d.ts | 8 +++++++- lib/controllers/guild/GuildChannelsController.js | 14 ++++++++++++++ lib/socket/handlers/voiceStateUpdate.js | 7 ++----- lib/structures/channels/GuildVoiceChannel.d.ts | 7 ++++--- lib/structures/channels/GuildVoiceChannel.js | 7 ++++--- lib/structures/channels/utils/ChannelUtils.js | 3 +++ lib/structures/guild/Guild.d.ts | 4 ++-- lib/structures/guild/Guild.js | 8 ++++---- lib/structures/member/Member.d.ts | 2 +- lib/structures/voice/Connection.d.ts | 8 ++++---- lib/structures/voice/Connection.js | 14 ++++++-------- lib/structures/voice/GuildVoice.d.ts | 4 ++-- lib/structures/voice/GuildVoice.js | 10 ++++------ lib/structures/voice/UDPSocket.d.ts | 4 ++-- lib/structures/voice/UDPSocket.js | 3 ++- lib/structures/voice/VoiceHeartbeats.d.ts | 4 ++-- lib/structures/voice/VoiceHeartbeats.js | 3 ++- lib/structures/voice/VoiceState.d.ts | 4 ++-- lib/structures/voice/VoiceState.js | 3 ++- lib/structures/voice/VoiceWebSocket.d.ts | 4 ++-- lib/structures/voice/VoiceWebSocket.js | 8 ++++---- 22 files changed, 76 insertions(+), 55 deletions(-) diff --git a/lib/bot/handlers/events/events.d.ts b/lib/bot/handlers/events/events.d.ts index e61dd9518..988325da0 100644 --- a/lib/bot/handlers/events/events.d.ts +++ b/lib/bot/handlers/events/events.d.ts @@ -6,7 +6,7 @@ import { Channel, GuildChannel } from '../../../structures/channels'; import { Guild, GuildUnavailable, GuildBan } from '../../../structures/guild'; import { Member, MemberPresence } from '../../../structures/member'; import { Message, PartialMessage, MessageReaction } from '../../../structures/message'; -import VoiceState from '../../../structures/voice/VoiceState'; +import { VoiceState } from '../../../structures/voice/VoiceState'; import { Snowflake } from '../../../types'; /** * Sent when all shards become ready diff --git a/lib/controllers/guild/GuildChannelsController.d.ts b/lib/controllers/guild/GuildChannelsController.d.ts index 45fd2dcaf..578f432cc 100644 --- a/lib/controllers/guild/GuildChannelsController.d.ts +++ b/lib/controllers/guild/GuildChannelsController.d.ts @@ -1,6 +1,6 @@ import Collection from '../../Collection'; import { Positions } from '../../api'; -import { GuildChannel, CreateGuildChannelOptions, GuildTextChannel } from '../../structures/channels'; +import { GuildChannel, CreateGuildChannelOptions, GuildTextChannel, GuildVoiceChannel } from '../../structures/channels'; import { Guild } from '../../structures/guild'; import { Snowflake } from '../../types'; import { BaseCreateController, BaseDeleteController, BaseFetchAllController, BaseFetchController } from '../base'; @@ -20,6 +20,12 @@ export declare class GuildChannelsController extends BaseFetchController} */ getText(id: Snowflake): Promise; + /** + * Gets or fetches a guild voice channel by its ID + * @param {Snowflake} id The ID of the guild voice channel + * @returns {Promise} + */ + getVoice(id: Snowflake): Promise; /** * Creates a new guild channel in the guild associated to this controller. * Requires the {@link Permission.ManageChannels} diff --git a/lib/controllers/guild/GuildChannelsController.js b/lib/controllers/guild/GuildChannelsController.js index 761abaa2f..69c60f230 100644 --- a/lib/controllers/guild/GuildChannelsController.js +++ b/lib/controllers/guild/GuildChannelsController.js @@ -35,6 +35,20 @@ class GuildChannelsController extends base_1.BaseFetchController { return channel; }); } + /** + * Gets or fetches a guild voice channel by its ID + * @param {Snowflake} id The ID of the guild voice channel + * @returns {Promise} + */ + getVoice(id) { + return __awaiter(this, void 0, void 0, function* () { + const channel = yield this.get(id); + if (!(channel instanceof channels_1.GuildVoiceChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + return channel; + }); + } /** * Creates a new guild channel in the guild associated to this controller. * Requires the {@link Permission.ManageChannels} diff --git a/lib/socket/handlers/voiceStateUpdate.js b/lib/socket/handlers/voiceStateUpdate.js index 22c7ba253..9fd4243dd 100644 --- a/lib/socket/handlers/voiceStateUpdate.js +++ b/lib/socket/handlers/voiceStateUpdate.js @@ -8,15 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); const __1 = require(".."); -const VoiceState_1 = __importDefault(require("../../structures/voice/VoiceState")); +const VoiceState_1 = require("../../structures/voice/VoiceState"); exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { const oldVoiceState = bot.guilds.cache.get(d.guild_id).voiceStates.get(d.user_id); - const newVoiceState = new VoiceState_1.default(bot, oldVoiceState.member, d); + const newVoiceState = new VoiceState_1.VoiceState(bot, oldVoiceState.member, d); bot.events.emit(__1.BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); bot.guilds.cache.get(d.guild_id).voiceStates.set(d.user_id, newVoiceState); }); diff --git a/lib/structures/channels/GuildVoiceChannel.d.ts b/lib/structures/channels/GuildVoiceChannel.d.ts index 3d323f470..f9d26bd83 100644 --- a/lib/structures/channels/GuildVoiceChannel.d.ts +++ b/lib/structures/channels/GuildVoiceChannel.d.ts @@ -1,11 +1,12 @@ -import { Guild, GuildChannel } from '..'; +import { GuildChannel } from './GuildChannel'; import { Bot } from '../../bot'; import { GatewayStruct } from '../base'; -import Connection from '../voice/Connection'; +import { Guild } from '../guild/Guild'; +import { Connection } from '../voice/Connection'; /** * Represents a Voice channel */ -export default class GuildVoiceChannel extends GuildChannel { +export declare class GuildVoiceChannel extends GuildChannel { constructor(bot: Bot, guildChannel: GatewayStruct, guild: Guild); join(mute?: boolean, deaf?: boolean): Promise; } diff --git a/lib/structures/channels/GuildVoiceChannel.js b/lib/structures/channels/GuildVoiceChannel.js index 76f4c8cae..60c8c9a53 100644 --- a/lib/structures/channels/GuildVoiceChannel.js +++ b/lib/structures/channels/GuildVoiceChannel.js @@ -1,10 +1,11 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const __1 = require(".."); +exports.GuildVoiceChannel = void 0; +const GuildChannel_1 = require("./GuildChannel"); /** * Represents a Voice channel */ -class GuildVoiceChannel extends __1.GuildChannel { +class GuildVoiceChannel extends GuildChannel_1.GuildChannel { constructor(bot, guildChannel, guild) { super(bot, guildChannel, guild); } @@ -12,4 +13,4 @@ class GuildVoiceChannel extends __1.GuildChannel { return this.guild.voice.join(this.id, { mute, deaf }); } } -exports.default = GuildVoiceChannel; +exports.GuildVoiceChannel = GuildVoiceChannel; diff --git a/lib/structures/channels/utils/ChannelUtils.js b/lib/structures/channels/utils/ChannelUtils.js index 18e249471..af67111cf 100644 --- a/lib/structures/channels/utils/ChannelUtils.js +++ b/lib/structures/channels/utils/ChannelUtils.js @@ -15,6 +15,7 @@ const DMChannel_1 = require("../DMChannel"); const GuildCategoryChannel_1 = require("../GuildCategoryChannel"); const GuildChannel_1 = require("../GuildChannel"); const GuildTextChannel_1 = require("../GuildTextChannel"); +const GuildVoiceChannel_1 = require("../GuildVoiceChannel"); /** * Handles channel-related util methods */ @@ -52,6 +53,8 @@ class ChannelUtils { channel = new GuildCategoryChannel_1.GuildCategoryChannel(bot, data, guild); break; case Channel_1.ChannelType.GuildVoice: + channel = new GuildVoiceChannel_1.GuildVoiceChannel(bot, data, guild); + break; case Channel_1.ChannelType.GuildNews: case Channel_1.ChannelType.GuildStore: channel = new GuildChannel_1.GuildChannel(bot, data, guild); diff --git a/lib/structures/guild/Guild.d.ts b/lib/structures/guild/Guild.d.ts index dc11abc1d..1896a3fad 100644 --- a/lib/structures/guild/Guild.d.ts +++ b/lib/structures/guild/Guild.d.ts @@ -14,8 +14,8 @@ import { GatewayStruct } from '../base'; import { GuildChannel, GuildTextChannel } from '../channels'; import { GuildSystemChannelFlags, PermissionFlags } from '../flags'; import { Member, MemberPresence } from '../member'; -import GuildVoice from '../voice/GuildVoice'; -import VoiceState from '../voice/VoiceState'; +import { GuildVoice } from '../voice/GuildVoice'; +import { VoiceState } from '../voice/VoiceState'; /** * Guild verification levels */ diff --git a/lib/structures/guild/Guild.js b/lib/structures/guild/Guild.js index 2cbcd1104..fa85d337e 100644 --- a/lib/structures/guild/Guild.js +++ b/lib/structures/guild/Guild.js @@ -17,8 +17,8 @@ const Role_1 = require("../Role"); const utils_1 = require("../channels/utils"); const flags_1 = require("../flags"); const member_1 = require("../member"); -const GuildVoice_1 = __importDefault(require("../voice/GuildVoice")); -const VoiceState_1 = __importDefault(require("../voice/VoiceState")); +const GuildVoice_1 = require("../voice/GuildVoice"); +const VoiceState_1 = require("../voice/VoiceState"); /** * Guild verification levels */ @@ -90,7 +90,7 @@ class Guild extends GuildPreview_1.GuildPreview { this.bans = new guild_2.GuildBansController(this); this.integrations = new guild_2.GuildIntegrationsController(this); this.webhooks = new GuildWebhooksController_1.GuildWebhooksController(this); - this.voice = new GuildVoice_1.default(this); + this.voice = new GuildVoice_1.GuildVoice(this); this.voiceStates = new Collection_1.default(); if (guild.channels) { this.channels.cache.addMany(guild.channels.map((channel) => utils_1.ChannelUtils.createGuildChannel(this.bot, channel, this))); @@ -106,7 +106,7 @@ class Guild extends GuildPreview_1.GuildPreview { } if (guild.voice_states) { for (const voicestate of guild.voice_states) { - this.voiceStates.set(voicestate.user_id, new VoiceState_1.default(this.bot, this.members.cache.get(voicestate.user_id), voicestate)); + this.voiceStates.set(voicestate.user_id, new VoiceState_1.VoiceState(this.bot, this.members.cache.get(voicestate.user_id), voicestate)); } } this.owner = this.members.cache.get(guild.owner_id); diff --git a/lib/structures/member/Member.d.ts b/lib/structures/member/Member.d.ts index 6e0885b92..9c2a94495 100644 --- a/lib/structures/member/Member.d.ts +++ b/lib/structures/member/Member.d.ts @@ -7,7 +7,7 @@ import { Timestamp } from '../Timestamp'; import { User } from '../User'; import { GatewayStruct, BaseGuildStruct } from '../base'; import { Guild } from '../guild'; -import VoiceState from '../voice/VoiceState'; +import { VoiceState } from '../voice/VoiceState'; /** * Options used when modifying a guild member */ diff --git a/lib/structures/voice/Connection.d.ts b/lib/structures/voice/Connection.d.ts index d5edde41d..bf9f2ae1e 100644 --- a/lib/structures/voice/Connection.d.ts +++ b/lib/structures/voice/Connection.d.ts @@ -1,7 +1,7 @@ -import GuildVoice from './GuildVoice'; -import UDPSocket from './UDPSocket'; -import VoiceWebSocket from './VoiceWebSocket'; -export default class Connection { +import { GuildVoice } from './GuildVoice'; +import { UDPSocket } from './UDPSocket'; +import { VoiceWebSocket } from './VoiceWebSocket'; +export declare class Connection { sockets: { ws: VoiceWebSocket; udp: UDPSocket; diff --git a/lib/structures/voice/Connection.js b/lib/structures/voice/Connection.js index 10e0e1c7c..777271a01 100644 --- a/lib/structures/voice/Connection.js +++ b/lib/structures/voice/Connection.js @@ -1,10 +1,8 @@ "use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const UDPSocket_1 = __importDefault(require("./UDPSocket")); -const VoiceWebSocket_1 = __importDefault(require("./VoiceWebSocket")); +exports.Connection = void 0; +const UDPSocket_1 = require("./UDPSocket"); +const VoiceWebSocket_1 = require("./VoiceWebSocket"); class Connection { constructor(voice) { this.active = false; @@ -29,8 +27,8 @@ class Connection { } this.active = true; this.sockets = { - ws: new VoiceWebSocket_1.default(this), - udp: new UDPSocket_1.default(this), + ws: new VoiceWebSocket_1.VoiceWebSocket(this), + udp: new UDPSocket_1.UDPSocket(this), }; } } @@ -38,4 +36,4 @@ class Connection { return this._endpoint; } } -exports.default = Connection; +exports.Connection = Connection; diff --git a/lib/structures/voice/GuildVoice.d.ts b/lib/structures/voice/GuildVoice.d.ts index b30d992b0..0ef7cef9c 100644 --- a/lib/structures/voice/GuildVoice.d.ts +++ b/lib/structures/voice/GuildVoice.d.ts @@ -1,11 +1,11 @@ -import Connection from './Connection'; +import { Connection } from './Connection'; import { Guild } from '..'; import { Bot } from '../../bot'; import { Snowflake } from '../../types'; /** * Represents a voice connection of a guild */ -export default class GuildVoice { +export declare class GuildVoice { /** * The {@link Bot} operating this structure */ diff --git a/lib/structures/voice/GuildVoice.js b/lib/structures/voice/GuildVoice.js index 32226f2b0..c27b36b3a 100644 --- a/lib/structures/voice/GuildVoice.js +++ b/lib/structures/voice/GuildVoice.js @@ -1,16 +1,14 @@ "use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Connection_1 = __importDefault(require("./Connection")); +exports.GuildVoice = void 0; +const Connection_1 = require("./Connection"); const socket_1 = require("../../socket"); /** * Represents a voice connection of a guild */ class GuildVoice { constructor(guild) { - this.connection = new Connection_1.default(this); + this.connection = new Connection_1.Connection(this); this.guild = guild; this.bot = guild.bot; } @@ -37,4 +35,4 @@ class GuildVoice { }); } } -exports.default = GuildVoice; +exports.GuildVoice = GuildVoice; diff --git a/lib/structures/voice/UDPSocket.d.ts b/lib/structures/voice/UDPSocket.d.ts index 31a56d97c..9891f3748 100644 --- a/lib/structures/voice/UDPSocket.d.ts +++ b/lib/structures/voice/UDPSocket.d.ts @@ -2,8 +2,8 @@ import { Socket } from 'dgram'; import { EventEmitter } from 'events'; import { Readable } from 'stream'; -import Connection from './Connection'; -export default class UDPSocket extends EventEmitter { +import { Connection } from './Connection'; +export declare class UDPSocket extends EventEmitter { connection: Connection; socket: Socket; private auth?; diff --git a/lib/structures/voice/UDPSocket.js b/lib/structures/voice/UDPSocket.js index 88069ef26..af7613c25 100644 --- a/lib/structures/voice/UDPSocket.js +++ b/lib/structures/voice/UDPSocket.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.UDPSocket = void 0; const dgram_1 = require("dgram"); const events_1 = require("events"); const stream_1 = require("stream"); @@ -106,4 +107,4 @@ class UDPSocket extends events_1.EventEmitter { this.socket.close(); } } -exports.default = UDPSocket; +exports.UDPSocket = UDPSocket; diff --git a/lib/structures/voice/VoiceHeartbeats.d.ts b/lib/structures/voice/VoiceHeartbeats.d.ts index ca4478c16..f650e0bad 100644 --- a/lib/structures/voice/VoiceHeartbeats.d.ts +++ b/lib/structures/voice/VoiceHeartbeats.d.ts @@ -1,5 +1,5 @@ /// -import VoiceWebSocket from './VoiceWebSocket'; +import { VoiceWebSocket } from './VoiceWebSocket'; interface HeartbeatInterval { timeout: number; executor?: NodeJS.Timeout; @@ -7,7 +7,7 @@ interface HeartbeatInterval { /** * Handles the sending and receiving of Discord heartbeats */ -export default class VoiceHeartbeats { +export declare class VoiceHeartbeats { private readonly ws; private readonly sequence; private acked; diff --git a/lib/structures/voice/VoiceHeartbeats.js b/lib/structures/voice/VoiceHeartbeats.js index 77e92ba22..d8dcdf974 100644 --- a/lib/structures/voice/VoiceHeartbeats.js +++ b/lib/structures/voice/VoiceHeartbeats.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoiceHeartbeats = void 0; /** * Handles the sending and receiving of Discord heartbeats */ @@ -50,4 +51,4 @@ class VoiceHeartbeats { this.acked = true; } } -exports.default = VoiceHeartbeats; +exports.VoiceHeartbeats = VoiceHeartbeats; diff --git a/lib/structures/voice/VoiceState.d.ts b/lib/structures/voice/VoiceState.d.ts index 28d8cc770..73aa55187 100644 --- a/lib/structures/voice/VoiceState.d.ts +++ b/lib/structures/voice/VoiceState.d.ts @@ -1,7 +1,7 @@ import { Bot } from '../../bot'; import { Nullable, Snowflake } from '../../types'; import { BaseStruct, GatewayStruct } from '../base'; -import GuildVoiceChannel from '../channels/GuildVoiceChannel'; +import { GuildVoiceChannel } from '../channels/GuildVoiceChannel'; import { Member } from '../member'; interface VoiceStateData { guild_id: Snowflake; @@ -21,7 +21,7 @@ declare enum MUTE_STATE { FORCE = 1, NONE = 2 } -export default class VoiceState extends BaseStruct { +export declare class VoiceState extends BaseStruct { private channelId; sessionId: string; deafen: MUTE_STATE; diff --git a/lib/structures/voice/VoiceState.js b/lib/structures/voice/VoiceState.js index 7410d92a2..ff5d7daa7 100644 --- a/lib/structures/voice/VoiceState.js +++ b/lib/structures/voice/VoiceState.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoiceState = void 0; const base_1 = require("../base"); var MUTE_STATE; (function (MUTE_STATE) { @@ -34,5 +35,5 @@ class VoiceState extends base_1.BaseStruct { return channel ? channel : null; } } -exports.default = VoiceState; +exports.VoiceState = VoiceState; VoiceState.MUTE_STATE = MUTE_STATE; diff --git a/lib/structures/voice/VoiceWebSocket.d.ts b/lib/structures/voice/VoiceWebSocket.d.ts index 3aa89ca0e..1a902e936 100644 --- a/lib/structures/voice/VoiceWebSocket.d.ts +++ b/lib/structures/voice/VoiceWebSocket.d.ts @@ -1,6 +1,6 @@ /// import { EventEmitter } from 'events'; -import Connection from './Connection'; +import { Connection } from './Connection'; import { PayloadData } from '../../socket'; export declare enum VOICE_OPCODES { IDENTIFY = 0, @@ -28,7 +28,7 @@ export interface VoiceCommand { export interface VoicePayload extends VoiceCommand { s: number; } -export default class VoiceWebSocket extends EventEmitter { +export declare class VoiceWebSocket extends EventEmitter { connection: Connection; private ws?; private hearbeat?; diff --git a/lib/structures/voice/VoiceWebSocket.js b/lib/structures/voice/VoiceWebSocket.js index 8644342b7..7508842b3 100644 --- a/lib/structures/voice/VoiceWebSocket.js +++ b/lib/structures/voice/VoiceWebSocket.js @@ -12,10 +12,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.VOICE_OPCODES = void 0; +exports.VoiceWebSocket = exports.VOICE_OPCODES = void 0; const events_1 = require("events"); const ws_1 = __importDefault(require("ws")); -const VoiceHeartbeats_1 = __importDefault(require("./VoiceHeartbeats")); +const VoiceHeartbeats_1 = require("./VoiceHeartbeats"); var VOICE_OPCODES; (function (VOICE_OPCODES) { VOICE_OPCODES[VOICE_OPCODES["IDENTIFY"] = 0] = "IDENTIFY"; @@ -40,7 +40,7 @@ class VoiceWebSocket extends events_1.EventEmitter { throw new Error('A voice gateway connection was tried to be established before endpoint declaration.'); this.ws = new ws_1.default(`wss://${this.connection.endpoint.split(':')[0]}/?v=4`); this.ws.on('message', this.onMessage.bind(this)); - this.hearbeat = new VoiceHeartbeats_1.default(this); + this.hearbeat = new VoiceHeartbeats_1.VoiceHeartbeats(this); this.send({ op: VOICE_OPCODES.IDENTIFY, d: { @@ -96,4 +96,4 @@ class VoiceWebSocket extends events_1.EventEmitter { (_b = this.hearbeat) === null || _b === void 0 ? void 0 : _b.stopHeartbeat(); } } -exports.default = VoiceWebSocket; +exports.VoiceWebSocket = VoiceWebSocket; From 0837a584b9e40cbd63d8979b946c5165473d84f2 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 09:33:54 +0300 Subject: [PATCH 18/48] fix(Sharding): instead of undefined, shardId will be 1 --- src/bot/Bot.ts | 4 ++-- src/socket/handlers/voiceServerUpdate.ts | 2 +- src/socket/handlers/voiceStateUpdate.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bot/Bot.ts b/src/bot/Bot.ts index c6fb6ad28..1ed37c930 100644 --- a/src/bot/Bot.ts +++ b/src/bot/Bot.ts @@ -155,8 +155,8 @@ export class Bot { const shardAmount = parseInt(process.env.SHARDS_AMOUNT as string); this.shardOptions = { - id: shardId, - amount: shardAmount, + id: shardId || 0, + amount: shardAmount || 1, }; this.commands = new CommandsHandler(); diff --git a/src/socket/handlers/voiceServerUpdate.ts b/src/socket/handlers/voiceServerUpdate.ts index 8cb35ec45..5701670c0 100644 --- a/src/socket/handlers/voiceServerUpdate.ts +++ b/src/socket/handlers/voiceServerUpdate.ts @@ -1,5 +1,5 @@ import { Payload, BotEvent } from '..'; -import { Bot } from '../..'; +import { Bot } from '../../index'; export default async ({ d }: Payload, bot: Bot): Promise => { bot.events.emit(BotEvent.VoiceServerUpdate, bot.guilds.cache.get(d.guild_id)!, d); diff --git a/src/socket/handlers/voiceStateUpdate.ts b/src/socket/handlers/voiceStateUpdate.ts index 4ff5525a3..0edbef6dd 100644 --- a/src/socket/handlers/voiceStateUpdate.ts +++ b/src/socket/handlers/voiceStateUpdate.ts @@ -1,5 +1,5 @@ import { Payload, BotEvent } from '..'; -import { Bot } from '../..'; +import { Bot } from '../../index'; import { VoiceState } from '../../structures/voice/VoiceState'; export default async ({ d }: Payload, bot: Bot): Promise => { From 2e11ad3060aa368e06e17aad935207eaa12e543a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 06:35:17 +0000 Subject: [PATCH 19/48] ci(Compile): compiled latest push --- lib/bot/Bot.js | 4 ++-- lib/socket/handlers/voiceServerUpdate.d.ts | 2 +- lib/socket/handlers/voiceStateUpdate.d.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/bot/Bot.js b/lib/bot/Bot.js index 62df8eebb..0d84aa1d6 100644 --- a/lib/bot/Bot.js +++ b/lib/bot/Bot.js @@ -37,8 +37,8 @@ class Bot { const shardId = parseInt(process.env.SHARD_ID); const shardAmount = parseInt(process.env.SHARDS_AMOUNT); this.shardOptions = { - id: shardId, - amount: shardAmount, + id: shardId || 0, + amount: shardAmount || 1, }; this.commands = new command_1.CommandsHandler(); this.events = new events_1.EventsHandler(); diff --git a/lib/socket/handlers/voiceServerUpdate.d.ts b/lib/socket/handlers/voiceServerUpdate.d.ts index e5b7c949a..feba86fd1 100644 --- a/lib/socket/handlers/voiceServerUpdate.d.ts +++ b/lib/socket/handlers/voiceServerUpdate.d.ts @@ -1,4 +1,4 @@ import { Payload } from '..'; -import { Bot } from '../..'; +import { Bot } from '../../index'; declare const _default: ({ d }: Payload, bot: Bot) => Promise; export default _default; diff --git a/lib/socket/handlers/voiceStateUpdate.d.ts b/lib/socket/handlers/voiceStateUpdate.d.ts index e5b7c949a..feba86fd1 100644 --- a/lib/socket/handlers/voiceStateUpdate.d.ts +++ b/lib/socket/handlers/voiceStateUpdate.d.ts @@ -1,4 +1,4 @@ import { Payload } from '..'; -import { Bot } from '../..'; +import { Bot } from '../../index'; declare const _default: ({ d }: Payload, bot: Bot) => Promise; export default _default; From 424fbb715bb566b00f6df93fcecda4826b9e467d Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 09:48:53 +0300 Subject: [PATCH 20/48] fix(Message): fixed message member laways undefined --- src/structures/message/Message.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/structures/message/Message.ts b/src/structures/message/Message.ts index 238688917..c7d40ca3a 100644 --- a/src/structures/message/Message.ts +++ b/src/structures/message/Message.ts @@ -11,7 +11,7 @@ import { EmojiResolvable } from '../Emoji'; import { Timestamp } from '../Timestamp'; import { User } from '../User'; import { BaseStruct, GatewayStruct } from '../base'; -import { TextBasedChannel } from '../channels'; +import { ChannelType, GuildTextChannel, TextBasedChannel } from '../channels'; import { MessageFlags } from '../flags'; import { Guild } from '../guild'; import { Member } from '../member/Member'; @@ -189,12 +189,6 @@ export class Message extends BaseStruct { */ public author: User | undefined; - /** - * The member properties for this message's author. - * Might not exist if message was sent over a DM - */ - public member: Member | undefined; - /** * The content of the message */ @@ -434,4 +428,13 @@ export class Message extends BaseStruct { public unpin(): Promise { return this.bot.api.unpinMessage(this.channel.id, this.id); } + + /** + * The member properties for this message's author. + * Might not exist if message was sent over a DM + */ + get member(): Member | undefined { + if (this.channel.type === ChannelType.DM) return undefined; + else (this.channel).guild.members.get(this.author!.id)!; + } } From dd231513d32efe2f4594a622f80391072de55b59 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 10:30:17 +0300 Subject: [PATCH 21/48] feat(VoiceState): add VoiceState to all members --- src/socket/handlers/voiceStateUpdate.ts | 6 ++++- src/structures/guild/Guild.ts | 19 +++++++++++++++ src/structures/voice/VoiceState.ts | 31 +++++++++++-------------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/socket/handlers/voiceStateUpdate.ts b/src/socket/handlers/voiceStateUpdate.ts index 0edbef6dd..8942834ab 100644 --- a/src/socket/handlers/voiceStateUpdate.ts +++ b/src/socket/handlers/voiceStateUpdate.ts @@ -4,7 +4,11 @@ import { VoiceState } from '../../structures/voice/VoiceState'; export default async ({ d }: Payload, bot: Bot): Promise => { const oldVoiceState = bot.guilds.cache.get(d.guild_id)!.voiceStates.get(d.user_id)!; - const newVoiceState = new VoiceState(bot, oldVoiceState.member, d); + const newVoiceState = new VoiceState( + bot, + bot.guilds.cache.get(d.guild_id)!.members.cache.get(d.user_id)!, + d, + ); bot.events.emit(BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); diff --git a/src/structures/guild/Guild.ts b/src/structures/guild/Guild.ts index c9c647c73..837cc5ad1 100644 --- a/src/structures/guild/Guild.ts +++ b/src/structures/guild/Guild.ts @@ -514,6 +514,25 @@ export class Guild extends GuildPreview { } } + const nonVoicedMembers = this.members.cache + .filter(x => !x.voice) + .map(x => + Object.assign( + x, + guild.members.find((y: { id: Snowflake; mute: boolean; deaf: boolean }) => y.id === x.id), + ), + ).toArray; + + for (const nonVoiceMember of nonVoicedMembers) { + this.voiceStates.set( + nonVoiceMember.id, + new VoiceState(this.bot, nonVoiceMember, { + deaf: nonVoiceMember.deaf, + mute: nonVoiceMember.mute, + }), + ); + } + this.owner = this.members.cache.get(guild.owner_id); this.ownerId = guild.owner_id; diff --git a/src/structures/voice/VoiceState.ts b/src/structures/voice/VoiceState.ts index e9977f44e..72e129085 100644 --- a/src/structures/voice/VoiceState.ts +++ b/src/structures/voice/VoiceState.ts @@ -2,6 +2,7 @@ import { Bot } from '../../bot'; import { Nullable, Snowflake } from '../../types'; import { BaseStruct, GatewayStruct } from '../base'; import { GuildVoiceChannel } from '../channels/GuildVoiceChannel'; +import { MuteFlags, MUTE_STATE } from '../flags/MuteFlags'; import { Member } from '../member'; interface VoiceStateData { @@ -18,22 +19,14 @@ interface VoiceStateData { suppress: boolean; } -enum MUTE_STATE { - SELF, - FORCE, - NONE, -} - export class VoiceState extends BaseStruct { private channelId!: Nullable; public sessionId!: string; - public deafen!: MUTE_STATE; - public muted!: MUTE_STATE; + public deafen!: MuteFlags; + public muted!: MuteFlags; public member: Member; - static MUTE_STATE = MUTE_STATE; - - constructor(bot: Bot, member: Member, voiceState: GatewayStruct) { + constructor(bot: Bot, member: Member, voiceState: GatewayStruct & Partial) { super(bot, voiceState); this.member = member; @@ -42,19 +35,23 @@ export class VoiceState extends BaseStruct { } public init(voiceState: VoiceStateData): this { - if (voiceState.self_mute) this.muted = MUTE_STATE.SELF; - else if (voiceState.mute) this.muted = MUTE_STATE.FORCE; - else this.muted = MUTE_STATE.NONE; + if (voiceState.self_mute) this.muted = new MuteFlags(MUTE_STATE.SELF); + if (voiceState.mute) this.muted = new MuteFlags(MUTE_STATE.FORCE); - if (voiceState.self_deaf) this.deafen = MUTE_STATE.SELF; - else if (voiceState.deaf) this.deafen = MUTE_STATE.FORCE; - else this.deafen = MUTE_STATE.NONE; + if (voiceState.self_deaf) new MuteFlags(MUTE_STATE.SELF); + if (voiceState.deaf) this.deafen = new MuteFlags(MUTE_STATE.FORCE); this.channelId = voiceState.channel_id; + this.sessionId = voiceState.session_id; + return this; } + isOnVoice(): boolean { + return !!this.sessionId; + } + get currentChannel(): Nullable { const channel = this.bot.channels.cache.get(this.channelId!) as GuildVoiceChannel | undefined; From e934c941401f7ac9b8cc4dc16394af5e709017df Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 10:30:59 +0300 Subject: [PATCH 22/48] fix(Message): member value, get properly --- src/structures/message/Message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/message/Message.ts b/src/structures/message/Message.ts index c7d40ca3a..0ddc87b9b 100644 --- a/src/structures/message/Message.ts +++ b/src/structures/message/Message.ts @@ -435,6 +435,6 @@ export class Message extends BaseStruct { */ get member(): Member | undefined { if (this.channel.type === ChannelType.DM) return undefined; - else (this.channel).guild.members.get(this.author!.id)!; + else (this.channel).guild.members.cache.get(this.author!.id); } } From 86f5c65b243604ec56b90752e6244ae662fe01d4 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 10:31:42 +0300 Subject: [PATCH 23/48] feat(MuteFlags): these flags describe current mute/deaf --- src/structures/flags/MuteFlags.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/structures/flags/MuteFlags.ts diff --git a/src/structures/flags/MuteFlags.ts b/src/structures/flags/MuteFlags.ts new file mode 100644 index 000000000..02961e443 --- /dev/null +++ b/src/structures/flags/MuteFlags.ts @@ -0,0 +1,13 @@ +import { Flags } from './Flags'; + +export enum MUTE_STATE { + SELF = 1 << 2, + FORCE = 1 << 1, +} + +export class MuteFlags extends Flags { + // Mute flags are the states of mutes and deafens + constructor(flags: number) { + super(flags); + } +} From 09474af50dd208504c244d35c86c5bf509824fd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 08:01:42 +0000 Subject: [PATCH 24/48] ci(Compile): compiled latest push --- lib/socket/handlers/voiceStateUpdate.js | 2 +- lib/structures/flags/MuteFlags.d.ts | 8 +++++++ lib/structures/flags/MuteFlags.js | 16 ++++++++++++++ lib/structures/guild/Guild.js | 9 ++++++++ lib/structures/message/Message.d.ts | 10 ++++----- lib/structures/message/Message.js | 11 ++++++++++ lib/structures/voice/VoiceState.d.ts | 14 +++++-------- lib/structures/voice/VoiceState.js | 28 ++++++++++--------------- 8 files changed, 66 insertions(+), 32 deletions(-) create mode 100644 lib/structures/flags/MuteFlags.d.ts create mode 100644 lib/structures/flags/MuteFlags.js diff --git a/lib/socket/handlers/voiceStateUpdate.js b/lib/socket/handlers/voiceStateUpdate.js index 9fd4243dd..effe38602 100644 --- a/lib/socket/handlers/voiceStateUpdate.js +++ b/lib/socket/handlers/voiceStateUpdate.js @@ -13,7 +13,7 @@ const __1 = require(".."); const VoiceState_1 = require("../../structures/voice/VoiceState"); exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { const oldVoiceState = bot.guilds.cache.get(d.guild_id).voiceStates.get(d.user_id); - const newVoiceState = new VoiceState_1.VoiceState(bot, oldVoiceState.member, d); + const newVoiceState = new VoiceState_1.VoiceState(bot, bot.guilds.cache.get(d.guild_id).members.cache.get(d.user_id), d); bot.events.emit(__1.BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); bot.guilds.cache.get(d.guild_id).voiceStates.set(d.user_id, newVoiceState); }); diff --git a/lib/structures/flags/MuteFlags.d.ts b/lib/structures/flags/MuteFlags.d.ts new file mode 100644 index 000000000..35d6f8db4 --- /dev/null +++ b/lib/structures/flags/MuteFlags.d.ts @@ -0,0 +1,8 @@ +import { Flags } from './Flags'; +export declare enum MUTE_STATE { + SELF = 4, + FORCE = 2 +} +export declare class MuteFlags extends Flags { + constructor(flags: number); +} diff --git a/lib/structures/flags/MuteFlags.js b/lib/structures/flags/MuteFlags.js new file mode 100644 index 000000000..881899401 --- /dev/null +++ b/lib/structures/flags/MuteFlags.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MuteFlags = exports.MUTE_STATE = void 0; +const Flags_1 = require("./Flags"); +var MUTE_STATE; +(function (MUTE_STATE) { + MUTE_STATE[MUTE_STATE["SELF"] = 4] = "SELF"; + MUTE_STATE[MUTE_STATE["FORCE"] = 2] = "FORCE"; +})(MUTE_STATE = exports.MUTE_STATE || (exports.MUTE_STATE = {})); +class MuteFlags extends Flags_1.Flags { + // Mute flags are the states of mutes and deafens + constructor(flags) { + super(flags); + } +} +exports.MuteFlags = MuteFlags; diff --git a/lib/structures/guild/Guild.js b/lib/structures/guild/Guild.js index fa85d337e..442f0cb24 100644 --- a/lib/structures/guild/Guild.js +++ b/lib/structures/guild/Guild.js @@ -109,6 +109,15 @@ class Guild extends GuildPreview_1.GuildPreview { this.voiceStates.set(voicestate.user_id, new VoiceState_1.VoiceState(this.bot, this.members.cache.get(voicestate.user_id), voicestate)); } } + const nonVoicedMembers = this.members.cache + .filter(x => !x.voice) + .map(x => Object.assign(x, guild.members.find((y) => y.id === x.id))).toArray; + for (const nonVoiceMember of nonVoicedMembers) { + this.voiceStates.set(nonVoiceMember.id, new VoiceState_1.VoiceState(this.bot, nonVoiceMember, { + deaf: nonVoiceMember.deaf, + mute: nonVoiceMember.mute, + })); + } this.owner = this.members.cache.get(guild.owner_id); this.ownerId = guild.owner_id; if (guild.permissions) { diff --git a/lib/structures/message/Message.d.ts b/lib/structures/message/Message.d.ts index 0624262b6..4bacb917e 100644 --- a/lib/structures/message/Message.d.ts +++ b/lib/structures/message/Message.d.ts @@ -164,11 +164,6 @@ export declare class Message extends BaseStruct { * Might not be a valid {@link User} object if message was generated by a webhook */ author: User | undefined; - /** - * The member properties for this message's author. - * Might not exist if message was sent over a DM - */ - member: Member | undefined; /** * The content of the message */ @@ -291,5 +286,10 @@ export declare class Message extends BaseStruct { * @returns {Promise} */ unpin(): Promise; + /** + * The member properties for this message's author. + * Might not exist if message was sent over a DM + */ + get member(): Member | undefined; } export {}; diff --git a/lib/structures/message/Message.js b/lib/structures/message/Message.js index caa1cfc20..c1622e34c 100644 --- a/lib/structures/message/Message.js +++ b/lib/structures/message/Message.js @@ -13,6 +13,7 @@ const message_1 = require("../../controllers/message"); const Timestamp_1 = require("../Timestamp"); const User_1 = require("../User"); const base_1 = require("../base"); +const channels_1 = require("../channels"); const flags_1 = require("../flags"); /** * The type of a message @@ -170,5 +171,15 @@ class Message extends base_1.BaseStruct { unpin() { return this.bot.api.unpinMessage(this.channel.id, this.id); } + /** + * The member properties for this message's author. + * Might not exist if message was sent over a DM + */ + get member() { + if (this.channel.type === channels_1.ChannelType.DM) + return undefined; + else + this.channel.guild.members.cache.get(this.author.id); + } } exports.Message = Message; diff --git a/lib/structures/voice/VoiceState.d.ts b/lib/structures/voice/VoiceState.d.ts index 73aa55187..28ef375d5 100644 --- a/lib/structures/voice/VoiceState.d.ts +++ b/lib/structures/voice/VoiceState.d.ts @@ -2,6 +2,7 @@ import { Bot } from '../../bot'; import { Nullable, Snowflake } from '../../types'; import { BaseStruct, GatewayStruct } from '../base'; import { GuildVoiceChannel } from '../channels/GuildVoiceChannel'; +import { MuteFlags } from '../flags/MuteFlags'; import { Member } from '../member'; interface VoiceStateData { guild_id: Snowflake; @@ -16,20 +17,15 @@ interface VoiceStateData { self_video: boolean; suppress: boolean; } -declare enum MUTE_STATE { - SELF = 0, - FORCE = 1, - NONE = 2 -} export declare class VoiceState extends BaseStruct { private channelId; sessionId: string; - deafen: MUTE_STATE; - muted: MUTE_STATE; + deafen: MuteFlags; + muted: MuteFlags; member: Member; - static MUTE_STATE: typeof MUTE_STATE; - constructor(bot: Bot, member: Member, voiceState: GatewayStruct); + constructor(bot: Bot, member: Member, voiceState: GatewayStruct & Partial); init(voiceState: VoiceStateData): this; + isOnVoice(): boolean; get currentChannel(): Nullable; } export {}; diff --git a/lib/structures/voice/VoiceState.js b/lib/structures/voice/VoiceState.js index ff5d7daa7..f925780c2 100644 --- a/lib/structures/voice/VoiceState.js +++ b/lib/structures/voice/VoiceState.js @@ -2,12 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.VoiceState = void 0; const base_1 = require("../base"); -var MUTE_STATE; -(function (MUTE_STATE) { - MUTE_STATE[MUTE_STATE["SELF"] = 0] = "SELF"; - MUTE_STATE[MUTE_STATE["FORCE"] = 1] = "FORCE"; - MUTE_STATE[MUTE_STATE["NONE"] = 2] = "NONE"; -})(MUTE_STATE || (MUTE_STATE = {})); +const MuteFlags_1 = require("../flags/MuteFlags"); class VoiceState extends base_1.BaseStruct { constructor(bot, member, voiceState) { super(bot, voiceState); @@ -16,24 +11,23 @@ class VoiceState extends base_1.BaseStruct { } init(voiceState) { if (voiceState.self_mute) - this.muted = MUTE_STATE.SELF; - else if (voiceState.mute) - this.muted = MUTE_STATE.FORCE; - else - this.muted = MUTE_STATE.NONE; + this.muted = new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.SELF); + if (voiceState.mute) + this.muted = new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.FORCE); if (voiceState.self_deaf) - this.deafen = MUTE_STATE.SELF; - else if (voiceState.deaf) - this.deafen = MUTE_STATE.FORCE; - else - this.deafen = MUTE_STATE.NONE; + new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.SELF); + if (voiceState.deaf) + this.deafen = new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.FORCE); this.channelId = voiceState.channel_id; + this.sessionId = voiceState.session_id; return this; } + isOnVoice() { + return !!this.sessionId; + } get currentChannel() { const channel = this.bot.channels.cache.get(this.channelId); return channel ? channel : null; } } exports.VoiceState = VoiceState; -VoiceState.MUTE_STATE = MUTE_STATE; From ff4663a70a44b38f8fa04e06ba931ff8ebe1fdc6 Mon Sep 17 00:00:00 2001 From: Sardonyx Date: Tue, 1 Sep 2020 11:15:30 +0300 Subject: [PATCH 25/48] fix(Message): member value, added return I forgot to add `return` --- src/structures/message/Message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/message/Message.ts b/src/structures/message/Message.ts index 0ddc87b9b..0b48f1e75 100644 --- a/src/structures/message/Message.ts +++ b/src/structures/message/Message.ts @@ -435,6 +435,6 @@ export class Message extends BaseStruct { */ get member(): Member | undefined { if (this.channel.type === ChannelType.DM) return undefined; - else (this.channel).guild.members.cache.get(this.author!.id); + else return (this.channel).guild.members.cache.get(this.author!.id); } } From 9fe5cd8440c3deb167fe712bde24959982777763 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 08:16:10 +0000 Subject: [PATCH 26/48] ci(Compile): compiled latest push --- lib/structures/message/Message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/message/Message.js b/lib/structures/message/Message.js index c1622e34c..e2a32ebd1 100644 --- a/lib/structures/message/Message.js +++ b/lib/structures/message/Message.js @@ -179,7 +179,7 @@ class Message extends base_1.BaseStruct { if (this.channel.type === channels_1.ChannelType.DM) return undefined; else - this.channel.guild.members.cache.get(this.author.id); + return this.channel.guild.members.cache.get(this.author.id); } } exports.Message = Message; From 0cc335c321245226945a1af4d372aba0fc287363 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Tue, 1 Sep 2020 12:21:22 +0300 Subject: [PATCH 27/48] refactor: targeted es2019 async/await isnt supported below and in the compiled files there are many useless functions --- tsconfig.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 3997b8940..08048b07a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,14 @@ { "compilerOptions": { "module": "commonjs", - "target": "es6", + "target": "es2019", "declaration": true, "outDir": "./lib", "esModuleInterop": true, "resolveJsonModule": true, - "strict": true + "strict": true, + "lib": ["es2019", "es2020.bigint", "es2020.string", "es2020.symbol.wellknown"], + "types": ["node"] }, "include": [ "src" From 2756c2ef839305f70e6bb15374319485ec733d9d Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 03:24:36 +0300 Subject: [PATCH 28/48] feat(Sharding): add optional sharding --- src/bot/Bot.ts | 9 +++++++++ src/socket/BotSocket.ts | 24 +++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/bot/Bot.ts b/src/bot/Bot.ts index 1ed37c930..5cb6bc8db 100644 --- a/src/bot/Bot.ts +++ b/src/bot/Bot.ts @@ -47,6 +47,10 @@ export const botOptions: BotOptions = { websocket: { v: version, }, + shards: { + enabled: false, + size: 'default', + }, }; /** @@ -62,6 +66,11 @@ export interface BotOptions { * Bot cache options */ cache: CacheOptions; + + /** + * Shard Options + */ + shards: Partial<{ enabled: boolean; size: 'default' | number }>; } /** diff --git a/src/socket/BotSocket.ts b/src/socket/BotSocket.ts index c7d06ab90..fd4ffaad8 100644 --- a/src/socket/BotSocket.ts +++ b/src/socket/BotSocket.ts @@ -45,16 +45,22 @@ export class BotSocket { * @returns {Promise} */ public async startShards(timeout = recommendedShardTimeout): Promise { - const { - url: gatewayURL, - shards: suggestedShards, - session_start_limit: sessionStartLimit, - } = await this.gateway; + let amount: number; - this.gatewayURL = gatewayURL; - this.sessionStartLimit = sessionStartLimit; + const { url, shards: shard_count, session_start_limit } = await this.gateway; //only call this endpoint to retrieve a new URL if they are unable to properly establish a connection using the cached version of the URL. - const { id, amount = suggestedShards } = this.bot.shardOptions; + this.gatewayURL = url; + + if ( + (this.bot.options.shards.size === 'default' || !this.bot.options.shards.size) && + this.bot.options.shards.enabled + ) + amount = shard_count; + else if (!this.bot.options.shards.enabled) amount = 1; + else amount = this.bot.options.shards.size as number; + this.sessionStartLimit = session_start_limit; + + const { id } = this.bot.shardOptions; const shards = id !== undefined ? [id] : Array.from({ length: amount }).map((_, i) => i); @@ -68,7 +74,7 @@ export class BotSocket { this.shards.set(shardId, botShard); - botShard.connect(); + await botShard.connect(); // eslint-disable-next-line no-await-in-loop await new Promise(resolve => setTimeout(resolve, timeout)); From 432991519bbac0747813314653cdf182365df2cb Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 03:25:12 +0300 Subject: [PATCH 29/48] fix(Message): add return statement in else --- src/structures/message/Message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/message/Message.ts b/src/structures/message/Message.ts index 0ddc87b9b..0b48f1e75 100644 --- a/src/structures/message/Message.ts +++ b/src/structures/message/Message.ts @@ -435,6 +435,6 @@ export class Message extends BaseStruct { */ get member(): Member | undefined { if (this.channel.type === ChannelType.DM) return undefined; - else (this.channel).guild.members.cache.get(this.author!.id); + else return (this.channel).guild.members.cache.get(this.author!.id); } } From 003bfdae4a92f21aa797440a85eb75d37149507c Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 03:27:15 +0300 Subject: [PATCH 30/48] fix(GuildVoice): fixed sending op code 2 instead of 4 opcode 2 is identify --- src/structures/voice/GuildVoice.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/structures/voice/GuildVoice.ts b/src/structures/voice/GuildVoice.ts index 10a1ae8cf..df5959024 100644 --- a/src/structures/voice/GuildVoice.ts +++ b/src/structures/voice/GuildVoice.ts @@ -28,17 +28,7 @@ export class GuildVoice { } join(channelId: Snowflake, options: { mute?: boolean; deaf?: boolean }): Promise { - this.guild.shard.send({ - op: 2, - d: { - guild_id: this.guild.id, - channel_id: channelId, - self_mute: !!options.mute, - self_deaf: !!options.deaf, - }, - }); - - return new Promise(resolve => { + return new Promise(resolve => { const listener = ((guild: Guild, voiceServer: { token: string; endpoint: string }) => { if (guild.id === this.guild.id) { this.connection.endpoint = voiceServer.endpoint; @@ -52,6 +42,16 @@ export class GuildVoice { }).bind(this); this.bot.events.on(BotEvent.VoiceServerUpdate, listener); + + this.bot.connection.shards.first!.send({ + op: 4, + d: { + guild_id: this.guild.id, + channel_id: channelId, + self_mute: !!options.mute, + self_deaf: !!options.deaf, + }, + }); }); } } From ce833e9bdbe7ec862d945345836191d10a56d9b0 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 03:27:59 +0300 Subject: [PATCH 31/48] fix(Connection): fixed 'ws' and 'udp' of undefined error --- src/structures/voice/Connection.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/structures/voice/Connection.ts b/src/structures/voice/Connection.ts index 9eda1e665..50b949ec3 100644 --- a/src/structures/voice/Connection.ts +++ b/src/structures/voice/Connection.ts @@ -3,7 +3,10 @@ import { UDPSocket } from './UDPSocket'; import { VoiceWebSocket } from './VoiceWebSocket'; export class Connection { - public sockets!: { + public sockets: { + ws: VoiceWebSocket; + udp: UDPSocket; + } = {} as { ws: VoiceWebSocket; udp: UDPSocket; }; From 69560d2813c6f09db0e4cae786e31500b27bda15 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 03:28:21 +0300 Subject: [PATCH 32/48] feat(VoiceStream): add VoiceStream --- src/structures/voice/UDPSocket.ts | 6 +++--- src/structures/voice/VoiceStream.ts | 33 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/structures/voice/VoiceStream.ts diff --git a/src/structures/voice/UDPSocket.ts b/src/structures/voice/UDPSocket.ts index cee4bd2f9..fb08f3a17 100644 --- a/src/structures/voice/UDPSocket.ts +++ b/src/structures/voice/UDPSocket.ts @@ -1,7 +1,7 @@ import { createSocket, Socket } from 'dgram'; import { EventEmitter } from 'events'; -import { Readable } from 'stream'; import { Connection } from './Connection'; +import { VoiceStream } from './VoiceStream'; import { BotEvent } from '../../socket'; let LibSodium: typeof import('sodium-native'); @@ -29,12 +29,12 @@ export class UDPSocket extends EventEmitter { /** * PCM Raw */ - public PCMOut = new Readable(); + public PCMOut = new VoiceStream(); /** * Opus Encoded */ - public OpusOut = new Readable(); + public OpusOut = new VoiceStream(); private OpusEncoder!: import('opusscript').OpusScript; diff --git a/src/structures/voice/VoiceStream.ts b/src/structures/voice/VoiceStream.ts new file mode 100644 index 000000000..067702414 --- /dev/null +++ b/src/structures/voice/VoiceStream.ts @@ -0,0 +1,33 @@ +import { Readable } from 'stream'; +import TypedEventEmitter from 'typed-emitter'; + +export class VoiceStream extends (Readable as new () => TypedEventEmitter & Readable) { //eslint-disable-line prettier/prettier + private source: Buffer; + + constructor() { + super(); + + this.source = Buffer.alloc(1, 0); + } + + _read(size: number): Buffer { + return Buffer.from(this.source.slice(this.source.length - size)); + } + + push(data: Buffer): boolean { + const res = super.push(data); + this.source = Buffer.concat([this.source, data]); + return res; + } +} + +interface VoiceStreamEvents { + data: [Buffer]; + readable: []; + end: []; + close: []; + drain: []; + resume: []; + pause: []; + error: [Error]; +} From 39b76125ffc4f51b03b10292fee82767a71a72cb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 00:30:28 +0000 Subject: [PATCH 33/48] ci(Compile): compiled latest push --- lib/api/APIFile.js | 27 +- lib/api/APIRequest.js | 68 +- lib/api/APISerializer.js | 104 +-- lib/api/BotAPI.js | 789 +++++++----------- lib/api/rateLimit/RateLimitBucket.js | 73 +- lib/api/rateLimit/RateLimitQueue.js | 35 +- lib/bot/Bot.d.ts | 7 + lib/bot/Bot.js | 9 +- lib/bot/BotConnection.js | 15 +- lib/bot/handlers/HandlerItem.js | 19 +- lib/bot/handlers/command/CommandsHandler.js | 27 +- lib/bot/handlers/events/EventsHandler.js | 19 +- lib/controllers/base/BaseFetchController.js | 15 +- lib/controllers/bot/BotChannelsController.js | 60 +- lib/controllers/bot/BotGuildsController.js | 19 +- lib/controllers/bot/BotUsersController.js | 41 +- .../channel/ChannelMessagesController.js | 29 +- .../channel/ChannelPinsController.js | 41 +- lib/controllers/guild/GuildBansController.js | 29 +- .../guild/GuildChannelInvitesController.js | 19 +- .../guild/GuildChannelWebhooksController.js | 47 +- .../guild/GuildChannelsController.js | 73 +- .../guild/GuildEmojisController.js | 29 +- .../guild/GuildIntegrationsController.js | 19 +- .../guild/GuildInvitesController.js | 29 +- .../guild/GuildMembersController.js | 35 +- lib/controllers/guild/GuildRolesController.js | 19 +- .../guild/GuildWebhooksController.js | 19 +- .../reaction/ReactionUsersController.js | 41 +- lib/sharding/BotCommunication.js | 133 ++- lib/sharding/BotShard.js | 95 +-- lib/sharding/BotShardManager.js | 27 +- lib/socket/BotSocket.js | 53 +- lib/socket/BotSocketShard.js | 102 ++- lib/socket/handlers/channelCreate.js | 15 +- lib/socket/handlers/channelDelete.js | 15 +- lib/socket/handlers/channelPinsUpdate.js | 15 +- lib/socket/handlers/channelUpdate.js | 15 +- lib/socket/handlers/guildBanAdd.js | 15 +- lib/socket/handlers/guildBanRemove.js | 15 +- lib/socket/handlers/guildDelete.js | 15 +- lib/socket/handlers/guildEmojisUpdate.js | 15 +- .../handlers/guildIntegrationsUpdate.js | 15 +- lib/socket/handlers/guildMemberAdd.js | 15 +- lib/socket/handlers/guildMemberRemove.js | 15 +- lib/socket/handlers/guildMemberUpdate.js | 23 +- lib/socket/handlers/guildMembersChunk.js | 17 +- lib/socket/handlers/guildRoleCreate.js | 15 +- lib/socket/handlers/guildRoleDelete.js | 15 +- lib/socket/handlers/guildRoleUpdate.js | 15 +- lib/socket/handlers/guildUpdate.js | 13 +- lib/socket/handlers/inviteDelete.js | 17 +- lib/socket/handlers/messageCreate.js | 15 +- lib/socket/handlers/messageDelete.js | 15 +- lib/socket/handlers/messageDeleteBulk.js | 15 +- lib/socket/handlers/messageReactionAdd.js | 17 +- lib/socket/handlers/messageReactionRemove.js | 15 +- .../handlers/messageReactionRemoveAll.js | 15 +- .../handlers/messageReactionRemoveEmoji.js | 15 +- lib/socket/handlers/messageUpdate.js | 17 +- lib/socket/handlers/presenceUpdate.js | 17 +- lib/socket/handlers/typingStart.js | 15 +- lib/socket/handlers/userUpdate.js | 13 +- lib/socket/handlers/voiceServerUpdate.js | 13 +- lib/socket/handlers/voiceStateUpdate.js | 13 +- lib/socket/handlers/webhooksUpdate.js | 17 +- lib/socket/utils/ReactionHandlersUtils.js | 19 +- lib/structures/ImageURI.js | 23 +- lib/structures/User.js | 17 +- lib/structures/Webhook.js | 19 +- lib/structures/base/BaseStruct.js | 2 +- lib/structures/channels/utils/ChannelUtils.js | 23 +- lib/structures/message/MessageMentions.js | 2 +- lib/structures/voice/Connection.js | 1 + lib/structures/voice/GuildVoice.js | 18 +- lib/structures/voice/UDPSocket.d.ts | 6 +- lib/structures/voice/UDPSocket.js | 43 +- lib/structures/voice/VoiceStream.d.ts | 21 + lib/structures/voice/VoiceStream.js | 19 + lib/structures/voice/VoiceWebSocket.js | 71 +- 80 files changed, 1048 insertions(+), 1894 deletions(-) create mode 100644 lib/structures/voice/VoiceStream.d.ts create mode 100644 lib/structures/voice/VoiceStream.js diff --git a/lib/api/APIFile.js b/lib/api/APIFile.js index 5dbaec6f2..90e27300f 100644 --- a/lib/api/APIFile.js +++ b/lib/api/APIFile.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -43,16 +34,14 @@ class APIFile { * Reads the content of this file as a readable stream * @returns {Promise} */ - read() { - return __awaiter(this, void 0, void 0, function* () { - if (this.type === APIFileType.File) { - return fs_1.default.createReadStream(this.path); - } - else { - const { body } = yield node_fetch_1.default(this.path); - return body; - } - }); + async read() { + if (this.type === APIFileType.File) { + return fs_1.default.createReadStream(this.path); + } + else { + const { body } = await node_fetch_1.default(this.path); + return body; + } } } exports.APIFile = APIFile; diff --git a/lib/api/APIRequest.js b/lib/api/APIRequest.js index 0dc99f460..c45f5371f 100644 --- a/lib/api/APIRequest.js +++ b/lib/api/APIRequest.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -44,7 +35,10 @@ class APIRequest { else { this.params = Object.entries(params) .filter(([, value]) => value !== undefined) - .reduce((params, [key, value]) => (Object.assign(Object.assign({}, params), { [key]: value })), {}); + .reduce((params, [key, value]) => ({ + ...params, + [key]: value, + }), {}); } } } @@ -52,19 +46,17 @@ class APIRequest { * Sends the API request * @returns {Promise} */ - send() { - return __awaiter(this, void 0, void 0, function* () { - const { url, headers } = this; - const body = yield this.body(); - // Adds the headers from the form-data body - if (body && body instanceof form_data_1.default) { - Object.assign(headers, body.getHeaders()); - } - return node_fetch_1.default(url, { - method: this.method, - body, - headers, - }); + async send() { + const { url, headers } = this; + const body = await this.body(); + // Adds the headers from the form-data body + if (body && body instanceof form_data_1.default) { + Object.assign(headers, body.getHeaders()); + } + return node_fetch_1.default(url, { + method: this.method, + body, + headers, }); } /** @@ -93,22 +85,20 @@ class APIRequest { * Returns a form-data body if files need to be sent * @returns {Promise} */ - bodyFiles() { - return __awaiter(this, void 0, void 0, function* () { - const { files, params } = this; - if (!files) - throw new Error('No files found!'); - const body = new form_data_1.default(); - // Adds all the files to the form-data - for (const file of files) { - body.append(file.name, yield file.read(), file.name); - } - // Adds additional params to the 'payload_json' field - if (params) { - body.append('payload_json', JSON.stringify(params)); - } - return body; - }); + async bodyFiles() { + const { files, params } = this; + if (!files) + throw new Error('No files found!'); + const body = new form_data_1.default(); + // Adds all the files to the form-data + for (const file of files) { + body.append(file.name, await file.read(), file.name); + } + // Adds additional params to the 'payload_json' field + if (params) { + body.append('payload_json', JSON.stringify(params)); + } + return body; } /** * Returns the headers required for the request diff --git a/lib/api/APISerializer.js b/lib/api/APISerializer.js index 92caaff87..78b233101 100644 --- a/lib/api/APISerializer.js +++ b/lib/api/APISerializer.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.APISerializer = void 0; const structures_1 = require("../structures"); @@ -157,14 +148,12 @@ class APISerializer { * @param {CreateEmojiOptions} options The create emoji options * @returns {Promise} */ - static createEmojiOptions(options) { - return __awaiter(this, void 0, void 0, function* () { - return { - name: options.name, - image: yield options.image.stringify(), - roles: options.roles, - }; - }); + static async createEmojiOptions(options) { + return { + name: options.name, + image: await options.image.stringify(), + roles: options.roles, + }; } /** * Returns the serialized fetch guild options for when fetching a guild @@ -181,27 +170,25 @@ class APISerializer { * @param {ModifyGuildOptions} options The modify guild options * @returns {Promise} */ - static modifyGuildOptions(options) { + static async modifyGuildOptions(options) { var _a, _b, _c, _d, _e, _f; - return __awaiter(this, void 0, void 0, function* () { - return { - name: options.name, - region: options.region, - verification_level: (_a = options.levels) === null || _a === void 0 ? void 0 : _a.verification, - default_message_notifications: (_b = options.levels) === null || _b === void 0 ? void 0 : _b.notifications, - explicit_content_filter: (_c = options.levels) === null || _c === void 0 ? void 0 : _c.explicitContent, - afk_channel_id: (_e = (_d = options.afk) === null || _d === void 0 ? void 0 : _d.channel) === null || _e === void 0 ? void 0 : _e.id, - afk_timeout: (_f = options.afk) === null || _f === void 0 ? void 0 : _f.timeout, - icon: options.icon && (yield options.icon.stringify()), - owner_id: options.ownerId, - splash: options.splash && (yield options.splash.stringify()), - banner: options.banner && (yield options.banner.stringify()), - system_channel_id: options.systemChannelId, - rules_channel_id: options.rulesChannelId, - public_updates_channel_id: options.updatesChannelId, - preferred_locale: options.locale, - }; - }); + return { + name: options.name, + region: options.region, + verification_level: (_a = options.levels) === null || _a === void 0 ? void 0 : _a.verification, + default_message_notifications: (_b = options.levels) === null || _b === void 0 ? void 0 : _b.notifications, + explicit_content_filter: (_c = options.levels) === null || _c === void 0 ? void 0 : _c.explicitContent, + afk_channel_id: (_e = (_d = options.afk) === null || _d === void 0 ? void 0 : _d.channel) === null || _e === void 0 ? void 0 : _e.id, + afk_timeout: (_f = options.afk) === null || _f === void 0 ? void 0 : _f.timeout, + icon: options.icon && (await options.icon.stringify()), + owner_id: options.ownerId, + splash: options.splash && (await options.splash.stringify()), + banner: options.banner && (await options.banner.stringify()), + system_channel_id: options.systemChannelId, + rules_channel_id: options.rulesChannelId, + public_updates_channel_id: options.updatesChannelId, + preferred_locale: options.locale, + }; } /** * Returns the serialized fetch all members options for when fetching all members in a guild @@ -272,7 +259,10 @@ class APISerializer { * @returns {Params} */ static pruneOptions(options) { - return (options && Object.assign(Object.assign({}, APISerializer.pruneCountOptions(options)), { compute_prune_count: options.computePruneCount })); + return (options && { + ...APISerializer.pruneCountOptions(options), + compute_prune_count: options.computePruneCount, + }); } /** * Returns the serialized create integration options for when creating new guild integrations @@ -314,13 +304,11 @@ class APISerializer { * @param {ModifyBotUserOptions} options The modify bot user options * @returns {Promise} */ - static modifyBotUserOptions(options) { - return __awaiter(this, void 0, void 0, function* () { - return { - username: options.username, - avatar: options.avatar && (yield options.avatar.stringify()), - }; - }); + static async modifyBotUserOptions(options) { + return { + username: options.username, + avatar: options.avatar && (await options.avatar.stringify()), + }; } /** * Returns the serialized fetch guilds options for when fetching the guilds the bot's user is in @@ -359,27 +347,23 @@ class APISerializer { * @param {CreateWebhookOptions} options The create webhook options * @returns {Promise} */ - static createWebhookOptions(options) { - return __awaiter(this, void 0, void 0, function* () { - return { - name: options.name, - avatar: options.avatar && (yield options.avatar.stringify()), - }; - }); + static async createWebhookOptions(options) { + return { + name: options.name, + avatar: options.avatar && (await options.avatar.stringify()), + }; } /** * Returns the serialized modify webhook options for when modifying webhooks * @param {ModifyWebhookOptions} options The modify webhook options * @returns {Promise} */ - static modifyWebhookOptions(options) { - return __awaiter(this, void 0, void 0, function* () { - return { - name: options.name, - avatar: options.avatar && (yield options.avatar.stringify()), - channel_id: options.channelId, - }; - }); + static async modifyWebhookOptions(options) { + return { + name: options.name, + avatar: options.avatar && (await options.avatar.stringify()), + channel_id: options.channelId, + }; } } exports.APISerializer = APISerializer; diff --git a/lib/api/BotAPI.js b/lib/api/BotAPI.js index 57a82d3ed..5b919d980 100644 --- a/lib/api/BotAPI.js +++ b/lib/api/BotAPI.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -40,33 +31,27 @@ class BotAPI { * @param {Snowflake} channelId The ID of the channel you wish to fetch * @returns {Promise} */ - fetchChannel(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Get); - return new channels_1.Channel(this.bot, channel); - }); + async fetchChannel(channelId) { + const channel = await this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Get); + return new channels_1.Channel(this.bot, channel); } /** * Fetches a guild channel by its ID * @param {Snowflake} channelId The ID of the guild channel you wish to fetch * @returns {Promise} */ - fetchGuildChannel(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.fetchChannel(channelId); - return utils_1.ChannelUtils.createGuildChannel(this.bot, channel.structure, yield utils_1.ChannelUtils.getChannelGuild(this.bot, channel)); - }); + async fetchGuildChannel(channelId) { + const channel = await this.fetchChannel(channelId); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channel.structure, await utils_1.ChannelUtils.getChannelGuild(this.bot, channel)); } /** * Fetches a DM channel by its ID * @param {Snowflake} channelId The ID of the DM channel you wish to fetch * @returns {Promise} */ - fetchDMChannel(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.fetchChannel(channelId); - return utils_1.ChannelUtils.createDMChannel(this.bot, channel.structure); - }); + async fetchDMChannel(channelId) { + const channel = await this.fetchChannel(channelId); + return utils_1.ChannelUtils.createDMChannel(this.bot, channel.structure); } /** * Updates a {@link GuildChannel}'s settings. Requires the {@link Permission.ManageChannels} permission for the guild @@ -74,11 +59,9 @@ class BotAPI { * @param {GuildChannelOptions} options The modified channel's settings * @returns {Promise} */ - modifyGuildChannel(channelId, options) { - return __awaiter(this, void 0, void 0, function* () { - const channelData = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.guildChannelOptions(options)); - return utils_1.ChannelUtils.createGuildChannel(this.bot, channelData, yield utils_1.ChannelUtils.getChannelGuild(this.bot, channelData)); - }); + async modifyGuildChannel(channelId, options) { + const channelData = await this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.guildChannelOptions(options)); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channelData, await utils_1.ChannelUtils.getChannelGuild(this.bot, channelData)); } /** * Deletes a {@link GuildChannel}, or closes a {@link DMChannel}. @@ -86,11 +69,9 @@ class BotAPI { * @param {Snowflake} channelId The ID of the channel * @returns {Promise} */ - deleteChannel(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const channelData = yield this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Delete); - return utils_1.ChannelUtils.create(this.bot, channelData); - }); + async deleteChannel(channelId) { + const channelData = await this.requests.send(endpoints_1.EndpointRoute.Channel, { channelId }, constants_1.HttpMethod.Delete); + return utils_1.ChannelUtils.create(this.bot, channelData); } /** * Deletes a {@link GuildChannel}. @@ -98,14 +79,12 @@ class BotAPI { * @param {Snowflake} channelId The ID of the guild channel you wish to delete * @returns {Promise} */ - deleteGuildChannel(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.deleteChannel(channelId); - if (!(channel instanceof channels_1.GuildChannel)) { - throw new TypeError('The deleted channel is not a guild channel'); - } - return channel; - }); + async deleteGuildChannel(channelId) { + const channel = await this.deleteChannel(channelId); + if (!(channel instanceof channels_1.GuildChannel)) { + throw new TypeError('The deleted channel is not a guild channel'); + } + return channel; } /** * Fetches some messages in a text channel @@ -113,12 +92,10 @@ class BotAPI { * @param {FetchSomeMessagesOptions} options The options for the fetch operation * @returns {Promise>} */ - fetchSomeMessages(channelId, options) { - return __awaiter(this, void 0, void 0, function* () { - const messages = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMessagesOptions(options))); - const channel = yield this.bot.channels.getText(channelId); - return new Collection_1.default(messages.map(message => [message.id, new message_1.Message(this.bot, message, channel)])); - }); + async fetchSomeMessages(channelId, options) { + const messages = (await this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMessagesOptions(options))); + const channel = await this.bot.channels.getText(channelId); + return new Collection_1.default(messages.map(message => [message.id, new message_1.Message(this.bot, message, channel)])); } /** * Fetches a message in a text channel by their IDs @@ -126,12 +103,10 @@ class BotAPI { * @param {Snowflake} messageId The ID of the message you wish to fetch * @returns {Promise} */ - fetchMessage(channelId, messageId) { - return __awaiter(this, void 0, void 0, function* () { - const message = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Get); - const channel = yield this.bot.channels.getText(channelId); - return new message_1.Message(this.bot, message, channel); - }); + async fetchMessage(channelId, messageId) { + const message = await this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Get); + const channel = await this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, message, channel); } /** * Posts a message to a {@link GuildTextChannel} or {@link DMChannel}. @@ -152,28 +127,26 @@ class BotAPI { * @param {MessageOptions} options The message's options * @returns {Promise} */ - sendMessage(channelId, data, options) { - return __awaiter(this, void 0, void 0, function* () { - // Default params to be sent in the request - let params = Object.assign({}, options); - let files; - if (typeof data === 'string') { - // The params should only include the raw content - params['content'] = data; - } - else if (data instanceof message_1.MessageEmbed) { - // The params should only include the given embed structure - params['embed'] = data.structure; - } - else { - // The params should include all given data fields - params = yield APISerializer_1.APISerializer.messageData(data); - files = data.files; - } - const messageData = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Post, params, files); - const channel = yield this.bot.channels.getText(channelId); - return new message_1.Message(this.bot, messageData, channel); - }); + async sendMessage(channelId, data, options) { + // Default params to be sent in the request + let params = { ...options }; + let files; + if (typeof data === 'string') { + // The params should only include the raw content + params['content'] = data; + } + else if (data instanceof message_1.MessageEmbed) { + // The params should only include the given embed structure + params['embed'] = data.structure; + } + else { + // The params should include all given data fields + params = await APISerializer_1.APISerializer.messageData(data); + files = data.files; + } + const messageData = await this.requests.send(endpoints_1.EndpointRoute.ChannelMessages, { channelId }, constants_1.HttpMethod.Post, params, files); + const channel = await this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, messageData, channel); } /** * Creates a reaction for a message. This method requires the {@link Permission.ReadMessageHistory} permission to be present on the Bot. Additionally, if nobody else has reacted to the message using this emoji, this method requires the {@link Permission.AddReactions} permission to be present on the Bot. @@ -182,14 +155,12 @@ class BotAPI { * @param {string} emoji The emoji to react with to the message * @returns {Promise} */ - addMessageReaction(channelId, messageId, emoji) { - return __awaiter(this, void 0, void 0, function* () { - const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); - if (!identifier) { - throw new Error(`Invalid emoji for addMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); - } - yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { channelId, messageId, emoji: encodeURI(identifier) }, constants_1.HttpMethod.Put); - }); + async addMessageReaction(channelId, messageId, emoji) { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for addMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); + } + await this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { channelId, messageId, emoji: encodeURI(identifier) }, constants_1.HttpMethod.Put); } /** * Deletes a reaction a user reacted with. @@ -200,19 +171,17 @@ class BotAPI { * @param {Snowflake} userId The ID of the user of which reaction should be removed * @returns {Promise} */ - removeMessageReaction(channelId, messageId, emoji, userId = '@me') { - return __awaiter(this, void 0, void 0, function* () { - const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); - if (!identifier) { - throw new Error(`Invalid emoji for removeMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji}) user ${userId}`); - } - yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { - channelId, - messageId, - emoji: encodeURI(identifier), - userId, - }, constants_1.HttpMethod.Delete); - }); + async removeMessageReaction(channelId, messageId, emoji, userId = '@me') { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for removeMessageReaction request to channel (${channelId}) message (${messageId}) emoji (${emoji}) user ${userId}`); + } + await this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmojiUser, { + channelId, + messageId, + emoji: encodeURI(identifier), + userId, + }, constants_1.HttpMethod.Delete); } /** * Fetches a list of users that reacted with a particular emoji on a message @@ -222,15 +191,13 @@ class BotAPI { * @param {FetchReactionUsersOptions} options A set of options for this operation * @returns {Promise>} */ - fetchReactionUsers(channelId, messageId, emoji, options) { - return __awaiter(this, void 0, void 0, function* () { - const users = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { - channelId, - messageId, - emoji, - }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchReactionUsersOptions(options))); - return new Collection_1.default(users.map(user => [user.id, new structures_1.User(this.bot, user)])); - }); + async fetchReactionUsers(channelId, messageId, emoji, options) { + const users = (await this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { + channelId, + messageId, + emoji, + }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchReactionUsersOptions(options))); + return new Collection_1.default(users.map(user => [user.id, new structures_1.User(this.bot, user)])); } /** * Removes all reactions on a message. This method requires the {@link Permission.ManageMessages} permission to be present on the Bot @@ -238,13 +205,11 @@ class BotAPI { * @param {Snowflake} messageId The ID of the message of which to remove all reactions * @returns {Promise} */ - removeMessageReactions(channelId, messageId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactions, { - channelId, - messageId, - }, constants_1.HttpMethod.Delete); - }); + async removeMessageReactions(channelId, messageId) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactions, { + channelId, + messageId, + }, constants_1.HttpMethod.Delete); } /** * Deletes all reactions for an emoji. This method requires the {@link Permission.ManageMessages} permission ot be present on the Bot. @@ -253,18 +218,16 @@ class BotAPI { * @param {EmojiResolvable} emoji The reaction emoji you wish to delete * @returns {Promise} */ - removeMessageReactionsEmoji(channelId, messageId, emoji) { - return __awaiter(this, void 0, void 0, function* () { - const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); - if (!identifier) { - throw new Error(`Invalid emoji for removeMessageReactionsEmoji request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); - } - yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { - channelId, - messageId, - emoji: encodeURI(identifier), - }, constants_1.HttpMethod.Delete); - }); + async removeMessageReactionsEmoji(channelId, messageId, emoji) { + const identifier = structures_1.Emoji.resolve(this.bot.emojis, emoji); + if (!identifier) { + throw new Error(`Invalid emoji for removeMessageReactionsEmoji request to channel (${channelId}) message (${messageId}) emoji (${emoji})`); + } + await this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesReactionsEmoji, { + channelId, + messageId, + emoji: encodeURI(identifier), + }, constants_1.HttpMethod.Delete); } /** * Edits a previously sent message. @@ -283,22 +246,20 @@ class BotAPI { * ``` * @returns {Promise} */ - editMessage(channelId, messageId, data) { + async editMessage(channelId, messageId, data) { var _a; - return __awaiter(this, void 0, void 0, function* () { - const params = {}; - if (typeof data === 'string') { - // The given data is the new message content - params['content'] = data; - } - else { - // The given data should be passed to the endpoint - Object.assign(params, Object.assign(Object.assign({}, APISerializer_1.APISerializer.messageData(data)), { flags: (_a = data.flags) === null || _a === void 0 ? void 0 : _a.bits })); - } - const messageData = yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Patch, params); - const channel = yield this.bot.channels.getText(channelId); - return new message_1.Message(this.bot, messageData, channel); - }); + const params = {}; + if (typeof data === 'string') { + // The given data is the new message content + params['content'] = data; + } + else { + // The given data should be passed to the endpoint + Object.assign(params, { ...APISerializer_1.APISerializer.messageData(data), flags: (_a = data.flags) === null || _a === void 0 ? void 0 : _a.bits }); + } + const messageData = await this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Patch, params); + const channel = await this.bot.channels.getText(channelId); + return new message_1.Message(this.bot, messageData, channel); } /** * Deletes a message. @@ -307,10 +268,8 @@ class BotAPI { * @param {Snowflake} messageId The ID of the message you wish to delete * @returns {Promise} */ - deleteMessage(channelId, messageId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); - }); + async deleteMessage(channelId, messageId) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); } /** * Deletes multiple messages in a single request. @@ -319,11 +278,9 @@ class BotAPI { * @param {Snowflake[]} messages An array of the messages IDs you wish to delete * @returns {Promise} */ - bulkDeleteMessages(channelId, messages) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesBulkDelete, { channelId }, constants_1.HttpMethod.Post, { - messages, - }); + async bulkDeleteMessages(channelId, messages) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelMessagesBulkDelete, { channelId }, constants_1.HttpMethod.Post, { + messages, }); } /** @@ -334,13 +291,11 @@ class BotAPI { * @param {PermissionOverwriteFlags} flags The permissions you wish to modify * @returns {Promise} */ - modifyGuildChannelPermissions(channelId, permissible, flags) { - return __awaiter(this, void 0, void 0, function* () { - const params = APISerializer_1.APISerializer.guildChannelPermissions(permissible, flags); - yield this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible.id }, constants_1.HttpMethod.Put, params); - const channel = yield this.bot.channels.getGuildChannel(channelId); - return new structures_1.PermissionOverwrite(this.bot, Object.assign(Object.assign({}, params), { id: permissible.id }), channel); - }); + async modifyGuildChannelPermissions(channelId, permissible, flags) { + const params = APISerializer_1.APISerializer.guildChannelPermissions(permissible, flags); + await this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible.id }, constants_1.HttpMethod.Put, params); + const channel = await this.bot.channels.getGuildChannel(channelId); + return new structures_1.PermissionOverwrite(this.bot, { ...params, id: permissible.id }, channel); } /** * Fetches a list of invites for a channel. @@ -348,11 +303,9 @@ class BotAPI { * @param {Snowflake} channelId The ID of the channel to fetch invites in * @returns {Promise>} */ - fetchChannelInvites(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const invites = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Get)); - return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite)])); - }); + async fetchChannelInvites(channelId) { + const invites = (await this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Get)); + return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite)])); } /** * Creates a new invite for a guild channel. @@ -361,11 +314,9 @@ class BotAPI { * @param {InviteOptions} options The new invite options * @returns {Promise} */ - createChannelInvite(channelId, options) { - return __awaiter(this, void 0, void 0, function* () { - const invite = yield this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.inviteOptions(options)); - return new structures_1.Invite(this.bot, invite); - }); + async createChannelInvite(channelId, options) { + const invite = await this.requests.send(endpoints_1.EndpointRoute.ChannelInvites, { channelId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.inviteOptions(options)); + return new structures_1.Invite(this.bot, invite); } /** * Deletes a channel permission overwrite for a user or role in a guild channel. @@ -374,10 +325,8 @@ class BotAPI { * @param {Snowflake} permissible The ID of the user or role you wish to delete from the channel's permission overwrites * @returns {Promise} */ - deleteGuildChannelPermission(channelId, permissible) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible }, constants_1.HttpMethod.Delete); - }); + async deleteGuildChannelPermission(channelId, permissible) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelPermissionsOverwrite, { channelId, overwriteId: permissible }, constants_1.HttpMethod.Delete); } /** * Posts a typing indicator for a specified text channel. @@ -386,22 +335,18 @@ class BotAPI { * @param {Snowflake} channelId The ID of the text channel to trigger typing in * @returns {Promise} */ - triggerTextChannelTyping(channelId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelTyping, { channelId }, constants_1.HttpMethod.Post); - }); + async triggerTextChannelTyping(channelId) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelTyping, { channelId }, constants_1.HttpMethod.Post); } /** * Fetches all pinned messages in a text channel * @param {Snowflake} channelId The ID of the channel * @returns {Promise>} */ - fetchChannelPins(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const pins = (yield this.requests.send(endpoints_1.EndpointRoute.ChannelPins, { channelId }, constants_1.HttpMethod.Get)); - const channel = yield this.bot.channels.getText(channelId); - return new Collection_1.default(pins.map(pin => [pin.id, new message_1.Message(this.bot, pin, channel)])); - }); + async fetchChannelPins(channelId) { + const pins = (await this.requests.send(endpoints_1.EndpointRoute.ChannelPins, { channelId }, constants_1.HttpMethod.Get)); + const channel = await this.bot.channels.getText(channelId); + return new Collection_1.default(pins.map(pin => [pin.id, new message_1.Message(this.bot, pin, channel)])); } /** * Pins a message in a text channel. @@ -410,10 +355,8 @@ class BotAPI { * @param {Snowflake} messageId The ID of the message you wish to pin * @returns {Promise} */ - pinMessage(channelId, messageId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Put); - }); + async pinMessage(channelId, messageId) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Put); } /** * Unpins a message in a text channel. @@ -422,22 +365,18 @@ class BotAPI { * @param {Snowflake} messageId The ID of the message you wish to unpin * @returns {Promise} */ - unpinMessage(channelId, messageId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); - }); + async unpinMessage(channelId, messageId) { + await this.requests.send(endpoints_1.EndpointRoute.ChannelPinsMessage, { channelId, messageId }, constants_1.HttpMethod.Delete); } /** * Fetches all emojis in a guild * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchGuildEmojis(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const emojis = (yield this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Get)); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(emojis.map(emoji => [emoji.id, new guild_1.GuildEmoji(this.bot, emoji, guild)])); - }); + async fetchGuildEmojis(guildId) { + const emojis = (await this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Get)); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(emojis.map(emoji => [emoji.id, new guild_1.GuildEmoji(this.bot, emoji, guild)])); } /** * Fetches an emoji in a given guild @@ -445,12 +384,10 @@ class BotAPI { * @param {Snowflake} emojiId The ID of the emoji * @returns {Promise} */ - fetchGuildEmoji(guildId, emojiId) { - return __awaiter(this, void 0, void 0, function* () { - const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new guild_1.GuildEmoji(this.bot, emoji, guild); - }); + async fetchGuildEmoji(guildId, emojiId) { + const emoji = await this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); } /** * Creates a new emoji for a guild. @@ -459,12 +396,10 @@ class BotAPI { * @param {CreateEmojiOptions} options The options for the new emoji * @returns {Promise} */ - createGuildEmoji(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Post, yield APISerializer_1.APISerializer.createEmojiOptions(options)); - const guild = yield this.bot.guilds.get(guildId); - return new guild_1.GuildEmoji(this.bot, emoji, guild); - }); + async createGuildEmoji(guildId, options) { + const emoji = await this.requests.send(endpoints_1.EndpointRoute.GuildEmojis, { guildId }, constants_1.HttpMethod.Post, await APISerializer_1.APISerializer.createEmojiOptions(options)); + const guild = await this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); } /** * Modifies a given guild emoji. @@ -474,12 +409,10 @@ class BotAPI { * @param {ModifyEmojiOptions} options The options for the updated emoji * @returns {Promise} The updated emoji */ - modifyGuildEmoji(guildId, emojiId, options) { - return __awaiter(this, void 0, void 0, function* () { - const emoji = yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyEmojiOptions(options)); - const guild = yield this.bot.guilds.get(guildId); - return new guild_1.GuildEmoji(this.bot, emoji, guild); - }); + async modifyGuildEmoji(guildId, emojiId, options) { + const emoji = await this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyEmojiOptions(options)); + const guild = await this.bot.guilds.get(guildId); + return new guild_1.GuildEmoji(this.bot, emoji, guild); } /** * Deletes a given guild emoji @@ -487,10 +420,8 @@ class BotAPI { * @param {Snowflake} emojiId The ID of the emoji to delete * @returns {Promise} */ - deleteGuildEmoji(guildId, emojiId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Delete); - }); + async deleteGuildEmoji(guildId, emojiId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildEmoji, { guildId, emojiId }, constants_1.HttpMethod.Delete); } /** * Fetches a guild by its ID and additional options @@ -498,11 +429,9 @@ class BotAPI { * @param {FetchGuildOptions} options The additional options for the fetch operation * @returns {Promise} */ - fetchGuild(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const guild = yield this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildOptions(options)); - return new guild_1.Guild(this.bot, guild); - }); + async fetchGuild(guildId, options) { + const guild = await this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildOptions(options)); + return new guild_1.Guild(this.bot, guild); } /** * Fetches a guild preview by its guild ID. @@ -510,11 +439,9 @@ class BotAPI { * @param {Snowflake} guildId The ID of the guild * @returns {Promise} */ - fetchGuildPreview(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const preview = yield this.requests.send(endpoints_1.EndpointRoute.GuildPreview, { guildId }, constants_1.HttpMethod.Get); - return new guild_1.GuildPreview(this.bot, preview); - }); + async fetchGuildPreview(guildId) { + const preview = await this.requests.send(endpoints_1.EndpointRoute.GuildPreview, { guildId }, constants_1.HttpMethod.Get); + return new guild_1.GuildPreview(this.bot, preview); } /** * Modifies a guild's settings. @@ -523,26 +450,22 @@ class BotAPI { * @param {ModifyGuildOptions} options The new options for the updated guild * @returns {Promise} */ - modifyGuild(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const guild = yield this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyGuildOptions(options)); - return new guild_1.Guild(this.bot, guild); - }); + async modifyGuild(guildId, options) { + const guild = await this.requests.send(endpoints_1.EndpointRoute.Guild, { guildId }, constants_1.HttpMethod.Patch, await APISerializer_1.APISerializer.modifyGuildOptions(options)); + return new guild_1.Guild(this.bot, guild); } /** * Fetches all guild channels in a given guild * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchGuildChannels(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const channels = (yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Get)); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(channels.map(channel => [ - channel.id, - utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild), - ])); - }); + async fetchGuildChannels(guildId) { + const channels = (await this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Get)); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(channels.map(channel => [ + channel.id, + utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild), + ])); } /** * Creates a new guild channel in a guild. @@ -551,12 +474,10 @@ class BotAPI { * @param {CreateGuildChannelOptions} options The options for the new guild channel * @returns {Promise} */ - createGuildChannel(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createGuildChannelOptions(options)); - const guild = yield this.bot.guilds.get(guildId); - return utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild); - }); + async createGuildChannel(guildId, options) { + const channel = await this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createGuildChannelOptions(options)); + const guild = await this.bot.guilds.get(guildId); + return utils_1.ChannelUtils.createGuildChannel(this.bot, channel, guild); } /** * Modifies the positions of a set of channels for the guild. @@ -565,10 +486,8 @@ class BotAPI { * @param {Positions} positions The new positions for the guild channels * @returns {Promise} */ - modifyGuildChannelsPositions(guildId, positions) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); - }); + async modifyGuildChannelsPositions(guildId, positions) { + await this.requests.send(endpoints_1.EndpointRoute.GuildChannels, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); } /** * Fetches a guild member by its user ID @@ -576,12 +495,10 @@ class BotAPI { * @param {Snowflake} userId The ID of the member user * @returns {Promise} */ - fetchMember(guildId, userId) { - return __awaiter(this, void 0, void 0, function* () { - const member = yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new member_1.Member(this.bot, member, guild); - }); + async fetchMember(guildId, userId) { + const member = await this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new member_1.Member(this.bot, member, guild); } /** * Fetches all members in a guild @@ -589,12 +506,10 @@ class BotAPI { * @param {FetchSomeMembersOptions} options The options for the fetch operation * @returns {Promise>} */ - fetchSomeMembers(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const members = (yield this.requests.send(endpoints_1.EndpointRoute.GuildMembers, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMembersOptions(options))); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(members.map(member => [member.user.id, new member_1.Member(this.bot, member, guild)])); - }); + async fetchSomeMembers(guildId, options) { + const members = (await this.requests.send(endpoints_1.EndpointRoute.GuildMembers, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchSomeMembersOptions(options))); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(members.map(member => [member.user.id, new member_1.Member(this.bot, member, guild)])); } /** * Modifies attributes of a guild member @@ -603,10 +518,8 @@ class BotAPI { * @param {ModifyMemberOptions} options The options to modify for the member * @returns {Promise} */ - modifyMember(guildId, userId, options) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyMemberOptions(options)); - }); + async modifyMember(guildId, userId, options) { + await this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyMemberOptions(options)); } /** * Modify a guild member's nickname. @@ -632,11 +545,9 @@ class BotAPI { * @param {string} nick The new nickname for the bot * @returns {Promise} */ - modifyBotNickname(guildId, nick) { - return __awaiter(this, void 0, void 0, function* () { - return yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberBotNick, { guildId }, constants_1.HttpMethod.Patch, { - nick, - }); + async modifyBotNickname(guildId, nick) { + return await this.requests.send(endpoints_1.EndpointRoute.GuildMemberBotNick, { guildId }, constants_1.HttpMethod.Patch, { + nick, }); } /** @@ -647,10 +558,8 @@ class BotAPI { * @param {Snowflake} roleId The ID of the role * @returns {Promise} */ - memberAddRole(guildId, userId, roleId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Put); - }); + async memberAddRole(guildId, userId, roleId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Put); } /** * Removes a role from a guild member. @@ -660,10 +569,8 @@ class BotAPI { * @param {Snowflake} roleId The ID of the role * @returns {Promise} */ - memberRemoveRole(guildId, userId, roleId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Delete); - }); + async memberRemoveRole(guildId, userId, roleId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildMemberRole, { guildId, userId, roleId }, constants_1.HttpMethod.Delete); } /** * Removes a member from a guild. @@ -672,22 +579,18 @@ class BotAPI { * @param {Snowflake} userId The ID of the user to remove * @returns {Promise} */ - removeMember(guildId, userId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Delete); - }); + async removeMember(guildId, userId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildMember, { guildId, userId }, constants_1.HttpMethod.Delete); } /** * Fetches all bans in a guild by a guild ID * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchGuildBans(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const bans = yield this.requests.send(endpoints_1.EndpointRoute.GuildBans, { guildId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(bans.map(ban => [ban.user.id, new guild_2.GuildBan(this.bot, ban, guild)])); - }); + async fetchGuildBans(guildId) { + const bans = await this.requests.send(endpoints_1.EndpointRoute.GuildBans, { guildId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(bans.map(ban => [ban.user.id, new guild_2.GuildBan(this.bot, ban, guild)])); } /** * Fetches a ban in a guild by a user ID @@ -695,12 +598,10 @@ class BotAPI { * @param {Snowflake} userId The ID of the user * @returns {Promise} */ - fetchGuildBan(guildId, userId) { - return __awaiter(this, void 0, void 0, function* () { - const ban = yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new guild_2.GuildBan(this.bot, ban, guild); - }); + async fetchGuildBan(guildId, userId) { + const ban = await this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new guild_2.GuildBan(this.bot, ban, guild); } /** * Bans a member from a guild, and optionally deletes the previous messages sent by the banner member. @@ -710,10 +611,8 @@ class BotAPI { * @param {MemberBanOptions} options The options for the ban * @returns {Promise} */ - banMember(guildId, userId, options) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Put, APISerializer_1.APISerializer.banMemberOptions(options)); - }); + async banMember(guildId, userId, options) { + await this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Put, APISerializer_1.APISerializer.banMemberOptions(options)); } /** * Unbans a member from a guild. @@ -722,22 +621,18 @@ class BotAPI { * @param {Snowflake} userId The ID of the user to unban * @returns {Promise} */ - unbanMember(guildId, userId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Delete); - }); + async unbanMember(guildId, userId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildBan, { guildId, userId }, constants_1.HttpMethod.Delete); } /** * Fetches all roles in a guild by its ID * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchRoles(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const roles = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); - }); + async fetchRoles(guildId) { + const roles = await this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); } /** * Creates a new role in a guild. @@ -746,12 +641,10 @@ class BotAPI { * @param {RoleOptions} options The options for the created role * @returns {Promise} */ - createRole(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const role = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.roleOptions(options)); - const guild = yield this.bot.guilds.get(guildId); - return new structures_1.Role(this.bot, role, guild); - }); + async createRole(guildId, options) { + const role = await this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.roleOptions(options)); + const guild = await this.bot.guilds.get(guildId); + return new structures_1.Role(this.bot, role, guild); } /** * Modifies the positions of a set of roles for a guild. @@ -760,12 +653,10 @@ class BotAPI { * @param {Positions} positions The new roles positions * @returns {Promise>} A collection of all the guild's roles */ - modifyRolesPositions(guildId, positions) { - return __awaiter(this, void 0, void 0, function* () { - const roles = yield this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); - }); + async modifyRolesPositions(guildId, positions) { + const roles = await this.requests.send(endpoints_1.EndpointRoute.GuildRoles, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.positions(positions)); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(roles.map(role => [role.id, new structures_1.Role(this.bot, role, guild)])); } /** * Modifies a role. @@ -775,12 +666,10 @@ class BotAPI { * @param {RoleOptions} options The options for the updated role * @returns {Promise} The updated role */ - modifyRole(guildId, roleId, options) { - return __awaiter(this, void 0, void 0, function* () { - const role = yield this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.roleOptions(options)); - const guild = yield this.bot.guilds.get(guildId); - return new structures_1.Role(this.bot, role, guild); - }); + async modifyRole(guildId, roleId, options) { + const role = await this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.roleOptions(options)); + const guild = await this.bot.guilds.get(guildId); + return new structures_1.Role(this.bot, role, guild); } /** * Deletes a role in a guild. @@ -789,10 +678,8 @@ class BotAPI { * @param {Snowflake} roleId The ID of the role * @returns {Promise} */ - deleteRole(guildId, roleId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Delete); - }); + async deleteRole(guildId, roleId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildRole, { guildId, roleId }, constants_1.HttpMethod.Delete); } /** * Returns the number of members that would be removed in a prune operation. @@ -801,11 +688,9 @@ class BotAPI { * @param {PruneCountOptions} options Options for the prune * @returns {Promise} */ - guildPruneCount(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const { pruned } = yield this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.pruneCountOptions(options)); - return pruned; - }); + async guildPruneCount(guildId, options) { + const { pruned } = await this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.pruneCountOptions(options)); + return pruned; } /** * Begins a prune operation on a guild. @@ -814,11 +699,9 @@ class BotAPI { * @param {PruneOptions} options The options for the prune operation * @returns {Promise} The number of members that were removed in the prune operation, or null if the {@link PruneOptions.computePruneCount} is false */ - guildPrune(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const { pruned } = yield this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.pruneOptions(options)); - return pruned; - }); + async guildPrune(guildId, options) { + const { pruned } = await this.requests.send(endpoints_1.EndpointRoute.GuildPrune, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.pruneOptions(options)); + return pruned; } /** * Fetches all invites (with metadata) in a guild. @@ -826,12 +709,10 @@ class BotAPI { * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchGuildInvites(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const invites = yield this.requests.send(endpoints_1.EndpointRoute.GuildInvites, { guildId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite, guild)])); - }); + async fetchGuildInvites(guildId) { + const invites = await this.requests.send(endpoints_1.EndpointRoute.GuildInvites, { guildId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(invites.map(invite => [invite.code, new structures_1.Invite(this.bot, invite, guild)])); } /** * Fetches all guild integrations in a guild @@ -839,15 +720,13 @@ class BotAPI { * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchGuildIntegrations(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const integrations = yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegrations, { guildId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new Collection_1.default(integrations.map(integration => [ - integration.id, - new guild_2.GuildIntegration(this.bot, integration, guild), - ])); - }); + async fetchGuildIntegrations(guildId) { + const integrations = await this.requests.send(endpoints_1.EndpointRoute.GuildIntegrations, { guildId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new Collection_1.default(integrations.map(integration => [ + integration.id, + new guild_2.GuildIntegration(this.bot, integration, guild), + ])); } /** * Attaches an integration from the Bot to this guild. @@ -856,10 +735,8 @@ class BotAPI { * @param {CreateIntegrationOptions} options The options for the new integration * @returns {Promise} */ - createGuildIntegration(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createIntegrationOptions(options)); - }); + async createGuildIntegration(guildId, options) { + await this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId }, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createIntegrationOptions(options)); } /** * Modifies the behavior and settings of a guild integration. @@ -869,10 +746,8 @@ class BotAPI { * @param {ModifyIntegrationOptions} options The options for the modified guild integration * @returns {Promise} */ - modifyGuildIntegration(guildId, integrationId, options) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyIntegrationOptions(options)); - }); + async modifyGuildIntegration(guildId, integrationId, options) { + await this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyIntegrationOptions(options)); } /** * Deletes the attached integration for a guild. @@ -881,10 +756,8 @@ class BotAPI { * @param {Snowflake} integrationId The ID of the guild integration * @returns {Promise} */ - deleteGuildIntegration(guildId, integrationId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Delete); - }); + async deleteGuildIntegration(guildId, integrationId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildIntegration, { guildId, integrationId }, constants_1.HttpMethod.Delete); } /** * Syncs a guild integration. @@ -893,10 +766,8 @@ class BotAPI { * @param {Snowflake} integrationId The ID of the guild integration * @returns {Promise} */ - syncGuildIntegration(guildId, integrationId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.GuildIntegrationSync, { guildId, integrationId }, constants_1.HttpMethod.Post); - }); + async syncGuildIntegration(guildId, integrationId) { + await this.requests.send(endpoints_1.EndpointRoute.GuildIntegrationSync, { guildId, integrationId }, constants_1.HttpMethod.Post); } /** * Fetches a guild's widget object. @@ -904,12 +775,10 @@ class BotAPI { * @param {Snowflake} guildId The ID of the guild * @returns {Promise} */ - fetchGuildWidget(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const widget = yield this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Get); - const guild = yield this.bot.guilds.get(guildId); - return new guild_2.GuildWidget(this.bot, widget, guild); - }); + async fetchGuildWidget(guildId) { + const widget = await this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Get); + const guild = await this.bot.guilds.get(guildId); + return new guild_2.GuildWidget(this.bot, widget, guild); } /** * Modifies this guild widget. @@ -918,12 +787,10 @@ class BotAPI { * @param {ModifyWidgetOptions} options The options for the updated guild widget * @returns {Promise} The updated guild widget */ - modifyGuildWidget(guildId, options) { - return __awaiter(this, void 0, void 0, function* () { - const widget = yield this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyWidgetOptions(options)); - const guild = yield this.bot.guilds.get(guildId); - return new guild_2.GuildWidget(this.bot, widget, guild); - }); + async modifyGuildWidget(guildId, options) { + const widget = await this.requests.send(endpoints_1.EndpointRoute.GuildWidget, { guildId }, constants_1.HttpMethod.Patch, APISerializer_1.APISerializer.modifyWidgetOptions(options)); + const guild = await this.bot.guilds.get(guildId); + return new guild_2.GuildWidget(this.bot, widget, guild); } /** * Fetches this guild's vanity URL. @@ -931,83 +798,69 @@ class BotAPI { * @param {Snowflake} guildId The ID of the guild * @returns {Promise} */ - fetchGuildVanityURL(guildId) { - return __awaiter(this, void 0, void 0, function* () { - return yield this.requests.send(endpoints_1.EndpointRoute.GuildVanityURL, { guildId }, constants_1.HttpMethod.Get); - }); + async fetchGuildVanityURL(guildId) { + return await this.requests.send(endpoints_1.EndpointRoute.GuildVanityURL, { guildId }, constants_1.HttpMethod.Get); } /** * Fetches the bot user * @returns {Promise} */ - fetchBotUser() { - return __awaiter(this, void 0, void 0, function* () { - const user = yield this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Get); - return new structures_1.BotUser(this.bot, user); - }); + async fetchBotUser() { + const user = await this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Get); + return new structures_1.BotUser(this.bot, user); } /** * Fetches a user by its ID * @param {Snowflake} userId The user ID * @returns {Promise} */ - fetchUser(userId) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield this.requests.send(endpoints_1.EndpointRoute.User, { userId }, constants_1.HttpMethod.Get); - return new structures_1.User(this.bot, user); - }); + async fetchUser(userId) { + const user = await this.requests.send(endpoints_1.EndpointRoute.User, { userId }, constants_1.HttpMethod.Get); + return new structures_1.User(this.bot, user); } /** * Modifies this bot's user account settings * @param {ModifyBotUserOptions} options The options for the modified bot user * @returns {Promise} */ - modifyBotUser(options) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyBotUserOptions(options)); - return new structures_1.BotUser(this.bot, user); - }); + async modifyBotUser(options) { + const user = await this.requests.send(endpoints_1.EndpointRoute.UserBot, {}, constants_1.HttpMethod.Patch, await APISerializer_1.APISerializer.modifyBotUserOptions(options)); + return new structures_1.BotUser(this.bot, user); } /** * Fetches the guilds the bot user is a member of * @param {FetchGuildsOptions} options The options for the fetch operation * @returns {Promise>} */ - fetchBotGuilds(options) { - return __awaiter(this, void 0, void 0, function* () { - const guilds = yield this.requests.send(endpoints_1.EndpointRoute.UserBotGuilds, {}, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildsOptions(options)); - return new Collection_1.default(guilds.map(guild => [ - guild.id, - { - id: guild.id, - name: guild.name, - icon: guild.icon, - owner: guild.owner, - permissions: new flags_1.PermissionFlags(guild.permissions_new), - }, - ])); - }); + async fetchBotGuilds(options) { + const guilds = await this.requests.send(endpoints_1.EndpointRoute.UserBotGuilds, {}, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchGuildsOptions(options)); + return new Collection_1.default(guilds.map(guild => [ + guild.id, + { + id: guild.id, + name: guild.name, + icon: guild.icon, + owner: guild.owner, + permissions: new flags_1.PermissionFlags(guild.permissions_new), + }, + ])); } /** * Leaves a guild by its ID * @param {Snowflake} guildId The ID of the guild * @returns {Promise} */ - leaveGuild(guildId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.UserBotGuild, { guildId }, constants_1.HttpMethod.Delete); - }); + async leaveGuild(guildId) { + await this.requests.send(endpoints_1.EndpointRoute.UserBotGuild, { guildId }, constants_1.HttpMethod.Delete); } /** * Creates a new DM channel between a user and the bot user * @param {Snowflake} userId The ID of the user * @returns {Promise} */ - createDM(userId) { - return __awaiter(this, void 0, void 0, function* () { - const dmChannel = yield this.requests.send(endpoints_1.EndpointRoute.UserBotChannels, {}, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createDM(userId)); - return new channels_1.DMChannel(this.bot, dmChannel); - }); + async createDM(userId) { + const dmChannel = await this.requests.send(endpoints_1.EndpointRoute.UserBotChannels, {}, constants_1.HttpMethod.Post, APISerializer_1.APISerializer.createDM(userId)); + return new channels_1.DMChannel(this.bot, dmChannel); } /** * Fetches an invite by its invite code @@ -1015,11 +868,9 @@ class BotAPI { * @param {FetchInviteOptions} options An additional set of options for the invite * @returns {Promise} */ - fetchInvite(inviteCode, options) { - return __awaiter(this, void 0, void 0, function* () { - const invite = yield this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchInviteOptions(options)); - return new structures_1.Invite(this.bot, invite); - }); + async fetchInvite(inviteCode, options) { + const invite = await this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Get, APISerializer_1.APISerializer.fetchInviteOptions(options)); + return new structures_1.Invite(this.bot, invite); } /** * Deletes an invite by its invite code. @@ -1027,11 +878,9 @@ class BotAPI { * @param {string} inviteCode The invite code * @returns {Promise} */ - deleteInvite(inviteCode) { - return __awaiter(this, void 0, void 0, function* () { - const invite = yield this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Delete); - return new structures_1.Invite(this.bot, invite); - }); + async deleteInvite(inviteCode) { + const invite = await this.requests.send(endpoints_1.EndpointRoute.Invite, { inviteCode }, constants_1.HttpMethod.Delete); + return new structures_1.Invite(this.bot, invite); } /** * Creates a new webhook for a guild channel. @@ -1040,12 +889,10 @@ class BotAPI { * @param {CreateWebhookOptions} options The options for the new webhook * @returns {Promise} */ - createWebhook(channelId, options) { - return __awaiter(this, void 0, void 0, function* () { - const webhook = yield this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Post, yield APISerializer_1.APISerializer.createWebhookOptions(options)); - const channel = yield this.bot.channels.getGuildChannel(channelId); - return new structures_1.Webhook(this.bot, webhook, channel); - }); + async createWebhook(channelId, options) { + const webhook = await this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Post, await APISerializer_1.APISerializer.createWebhookOptions(options)); + const channel = await this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); } /** * Fetches all webhooks in a guild channel. @@ -1053,41 +900,35 @@ class BotAPI { * @param {Snowflake} channelId The ID of the guild channel * @returns {Promise>} */ - fetchWebhooks(channelId) { - return __awaiter(this, void 0, void 0, function* () { - const webhooks = yield this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Get); - const channel = yield this.bot.channels.getGuildChannel(channelId); - return new Collection_1.default(webhooks.map(webhook => [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)])); - }); + async fetchWebhooks(channelId) { + const webhooks = await this.requests.send(endpoints_1.EndpointRoute.ChannelWebhooks, { channelId }, constants_1.HttpMethod.Get); + const channel = await this.bot.channels.getGuildChannel(channelId); + return new Collection_1.default(webhooks.map(webhook => [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)])); } /** * Fetches all webhooks in a guild * @param {Snowflake} guildId The ID of the guild * @returns {Promise>} */ - fetchGuildWebhooks(guildId) { - return __awaiter(this, void 0, void 0, function* () { - const webhooks = yield this.requests.send(endpoints_1.EndpointRoute.GuildWebhooks, { guildId }, constants_1.HttpMethod.Get); - return new Collection_1.default(yield Promise.all(webhooks.map((webhook) => __awaiter(this, void 0, void 0, function* () { - const { channel_id: channelId } = webhook; - // Fetch the guild channel associated to this webhook - const channel = yield this.bot.channels.getGuildChannel(channelId); - return [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)]; - })))); - }); + async fetchGuildWebhooks(guildId) { + const webhooks = await this.requests.send(endpoints_1.EndpointRoute.GuildWebhooks, { guildId }, constants_1.HttpMethod.Get); + return new Collection_1.default(await Promise.all(webhooks.map(async (webhook) => { + const { channel_id: channelId } = webhook; + // Fetch the guild channel associated to this webhook + const channel = await this.bot.channels.getGuildChannel(channelId); + return [webhook.id, new structures_1.Webhook(this.bot, webhook, channel)]; + }))); } /** * Fetches a webhook by its ID * @param {Snowflake} webhookId The ID of the webhook * @returns {Promise} */ - fetchWebhook(webhookId) { - return __awaiter(this, void 0, void 0, function* () { - const webhook = yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Get); - const { channel_id: channelId } = webhook; - const channel = yield this.bot.channels.getGuildChannel(channelId); - return new structures_1.Webhook(this.bot, webhook, channel); - }); + async fetchWebhook(webhookId) { + const webhook = await this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Get); + const { channel_id: channelId } = webhook; + const channel = await this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); } /** * Modifies a webhook by its ID @@ -1095,13 +936,11 @@ class BotAPI { * @param {ModifyWebhookOptions} options The options for the modified webhook * @returns {Promise} */ - modifyWebhook(webhookId, options) { - return __awaiter(this, void 0, void 0, function* () { - const webhook = yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Patch, yield APISerializer_1.APISerializer.modifyWebhookOptions(options)); - const { channel_id: channelId } = webhook; - const channel = yield this.bot.channels.getGuildChannel(channelId); - return new structures_1.Webhook(this.bot, webhook, channel); - }); + async modifyWebhook(webhookId, options) { + const webhook = await this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Patch, await APISerializer_1.APISerializer.modifyWebhookOptions(options)); + const { channel_id: channelId } = webhook; + const channel = await this.bot.channels.getGuildChannel(channelId); + return new structures_1.Webhook(this.bot, webhook, channel); } /** * Deletes a webhook permanently. @@ -1109,10 +948,8 @@ class BotAPI { * @param {Snowflake} webhookId The ID of the webhook * @returns {Promise} */ - deleteWebhook(webhookId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Delete); - }); + async deleteWebhook(webhookId) { + await this.requests.send(endpoints_1.EndpointRoute.Webhook, { webhookId }, constants_1.HttpMethod.Delete); } } exports.BotAPI = BotAPI; diff --git a/lib/api/rateLimit/RateLimitBucket.js b/lib/api/rateLimit/RateLimitBucket.js index ec1726f7f..bb6ac7ae4 100644 --- a/lib/api/rateLimit/RateLimitBucket.js +++ b/lib/api/rateLimit/RateLimitBucket.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -38,44 +29,40 @@ class RateLimitBucket { * @param {RequestFile[]} files The files sent in this request * @returns {Promise} */ - send(endpoint, params, files) { - return __awaiter(this, void 0, void 0, function* () { - // The rate limit is reached. Add request to the queue - if (this.remaining !== undefined && this.remaining <= 0) { - this.bot.debug(`You reached the rate limit for ${endpoint}. Your request will still go through, but make sure you don't do this again!`); - return this.queue.add(endpoint, params, files); - } - // Decrements the remaining number of requests (to later be updated by the fixed value) - if (this.remaining) { - this.remaining--; - } - // Creates a new API request - const apiRequest = new APIRequest_1.APIRequest(this.token, endpoint, params, this.method, APIRequest_1.APIRequest.parseFiles(files)); - const response = yield apiRequest.send(); - // Sets the rate limit information given from the response's headers - this.setLimits(response.headers); - const json = response.status !== endpoints_1.StatusCode.NoContent - ? (yield response.json()) - : undefined; - if (json) { - // Validates the response before proceeding - this.validateResponse(response, json); - } - return json; - }); + async send(endpoint, params, files) { + // The rate limit is reached. Add request to the queue + if (this.remaining !== undefined && this.remaining <= 0) { + this.bot.debug(`You reached the rate limit for ${endpoint}. Your request will still go through, but make sure you don't do this again!`); + return this.queue.add(endpoint, params, files); + } + // Decrements the remaining number of requests (to later be updated by the fixed value) + if (this.remaining) { + this.remaining--; + } + // Creates a new API request + const apiRequest = new APIRequest_1.APIRequest(this.token, endpoint, params, this.method, APIRequest_1.APIRequest.parseFiles(files)); + const response = await apiRequest.send(); + // Sets the rate limit information given from the response's headers + this.setLimits(response.headers); + const json = response.status !== endpoints_1.StatusCode.NoContent + ? (await response.json()) + : undefined; + if (json) { + // Validates the response before proceeding + this.validateResponse(response, json); + } + return json; } /** * Refill this bucket */ - refillBucket() { - return __awaiter(this, void 0, void 0, function* () { - // Set the remaining number of requests in this bucket back to its limit - this.remaining = this.limit; - // Delete the stored setTimeout function - this.timeout = undefined; - // Empties the queue - yield this.queue.free(); - }); + async refillBucket() { + // Set the remaining number of requests in this bucket back to its limit + this.remaining = this.limit; + // Delete the stored setTimeout function + this.timeout = undefined; + // Empties the queue + await this.queue.free(); } /** * Validates the response. Throws an error in case it is not valid diff --git a/lib/api/rateLimit/RateLimitQueue.js b/lib/api/rateLimit/RateLimitQueue.js index 712c2341f..d408fdd4b 100644 --- a/lib/api/rateLimit/RateLimitQueue.js +++ b/lib/api/rateLimit/RateLimitQueue.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.RateLimitQueue = void 0; /** @@ -42,20 +33,18 @@ class RateLimitQueue extends Array { * Frees the queue for the remaining capacity of the bucket * @returns {Promise} */ - free() { - return __awaiter(this, void 0, void 0, function* () { - // Runs until either the queue's or the bucket's capacity empties - while (this.length && this.bucket.remaining && this.bucket.remaining > 0) { - const nextRequest = this.shift(); - if (!nextRequest) - break; - const { endpoint, params, files, callback } = nextRequest; - // Sends the request - const data = yield this.bucket.send(endpoint, params, files); - // Executes the callback function - callback(data); - } - }); + async free() { + // Runs until either the queue's or the bucket's capacity empties + while (this.length && this.bucket.remaining && this.bucket.remaining > 0) { + const nextRequest = this.shift(); + if (!nextRequest) + break; + const { endpoint, params, files, callback } = nextRequest; + // Sends the request + const data = await this.bucket.send(endpoint, params, files); + // Executes the callback function + callback(data); + } } } exports.RateLimitQueue = RateLimitQueue; diff --git a/lib/bot/Bot.d.ts b/lib/bot/Bot.d.ts index 987e66306..b10fb1c5f 100644 --- a/lib/bot/Bot.d.ts +++ b/lib/bot/Bot.d.ts @@ -50,6 +50,13 @@ export interface BotOptions { * Bot cache options */ cache: CacheOptions; + /** + * Shard Options + */ + shards: Partial<{ + enabled: boolean; + size: 'default' | number; + }>; } /** * The bot is the main operator of the API. diff --git a/lib/bot/Bot.js b/lib/bot/Bot.js index 0d84aa1d6..c6ff7cc2d 100644 --- a/lib/bot/Bot.js +++ b/lib/bot/Bot.js @@ -23,6 +23,10 @@ exports.botOptions = { websocket: { v: api_1.version, }, + shards: { + enabled: false, + size: 'default', + }, }; /** * The bot is the main operator of the API. @@ -31,7 +35,10 @@ exports.botOptions = { class Bot { constructor(token, options) { this.token = token; - this.options = Object.assign(Object.assign({}, exports.botOptions), options); + this.options = { + ...exports.botOptions, + ...options, + }; this.api = new api_1.BotAPI(this, this.token); // Sets bot sharding data const shardId = parseInt(process.env.SHARD_ID); diff --git a/lib/bot/BotConnection.js b/lib/bot/BotConnection.js index 309b60dec..e6964a2d3 100644 --- a/lib/bot/BotConnection.js +++ b/lib/bot/BotConnection.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotConnection = void 0; const socket_1 = require("../socket"); @@ -22,10 +13,8 @@ class BotConnection { * Creates a new bot connection * @returns {Promise} */ - connect() { - return __awaiter(this, void 0, void 0, function* () { - yield this.socket.startShards(); - }); + async connect() { + await this.socket.startShards(); } /** * Closes the currently running connection diff --git a/lib/bot/handlers/HandlerItem.js b/lib/bot/handlers/HandlerItem.js index 8ef116cf7..f929c920b 100644 --- a/lib/bot/handlers/HandlerItem.js +++ b/lib/bot/handlers/HandlerItem.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.HandlerItem = void 0; /** @@ -39,12 +30,10 @@ class HandlerItem { * @returns {Promise} * @ignore */ - runAll(event, ...args) { - return __awaiter(this, void 0, void 0, function* () { - for (const handler of this.handlers[event]) { - yield handler.bind(this)(...args); - } - }); + async runAll(event, ...args) { + for (const handler of this.handlers[event]) { + await handler.bind(this)(...args); + } } /** * Registers an event handler in the {@link EventEmitter} associated to this Command diff --git a/lib/bot/handlers/command/CommandsHandler.js b/lib/bot/handlers/command/CommandsHandler.js index c0cec94ea..84141d789 100644 --- a/lib/bot/handlers/command/CommandsHandler.js +++ b/lib/bot/handlers/command/CommandsHandler.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -28,16 +19,14 @@ class CommandsHandler { * @param {any} args The arguments to give the command's {@link HandlerEvent.Execute} event handler * @returns {boolean} */ - execute(name, ...args) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.commands.has(name)) - return false; - const command = this.commands.get(name); - yield command.runAll(Handler_1.HandlerEvent.Before); - yield command.runAll(Handler_1.HandlerEvent.Execute, ...args); - yield command.runAll(Handler_1.HandlerEvent.After); - return true; - }); + async execute(name, ...args) { + if (!this.commands.has(name)) + return false; + const command = this.commands.get(name); + await command.runAll(Handler_1.HandlerEvent.Before); + await command.runAll(Handler_1.HandlerEvent.Execute, ...args); + await command.runAll(Handler_1.HandlerEvent.After); + return true; } /** * Registers a command to this handler diff --git a/lib/bot/handlers/events/EventsHandler.js b/lib/bot/handlers/events/EventsHandler.js index c032815e6..d1176498b 100644 --- a/lib/bot/handlers/events/EventsHandler.js +++ b/lib/bot/handlers/events/EventsHandler.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.EventsHandler = void 0; const events_1 = require("events"); @@ -21,11 +12,11 @@ class EventsHandler extends events_1.EventEmitter { * @param {Event} event The event to be fired */ register(event) { - this.on(event.name, ((...args) => __awaiter(this, void 0, void 0, function* () { - yield event.runAll(Handler_1.HandlerEvent.Before); - yield event.runAll(Handler_1.HandlerEvent.Execute, ...args); - yield event.runAll(Handler_1.HandlerEvent.After); - }))); + this.on(event.name, (async (...args) => { + await event.runAll(Handler_1.HandlerEvent.Before); + await event.runAll(Handler_1.HandlerEvent.Execute, ...args); + await event.runAll(Handler_1.HandlerEvent.After); + })); } /** * Asynchronously waits until an event is executed, and returns its arguments in an array diff --git a/lib/controllers/base/BaseFetchController.js b/lib/controllers/base/BaseFetchController.js index be9f73dfd..55a64c182 100644 --- a/lib/controllers/base/BaseFetchController.js +++ b/lib/controllers/base/BaseFetchController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseFetchController = void 0; const BaseController_1 = require("./BaseController"); @@ -21,10 +12,8 @@ class BaseFetchController extends BaseController_1.BaseController { * @param {Snowflake | string} id The ID of the item you wish to get or fetch * @returns {Promise} */ - get(id) { - return __awaiter(this, void 0, void 0, function* () { - return this.cache.get(id) || this.fetch(id); - }); + async get(id) { + return this.cache.get(id) || this.fetch(id); } } exports.BaseFetchController = BaseFetchController; diff --git a/lib/controllers/bot/BotChannelsController.js b/lib/controllers/bot/BotChannelsController.js index dc7514d86..6e2857877 100644 --- a/lib/controllers/bot/BotChannelsController.js +++ b/lib/controllers/bot/BotChannelsController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotChannelsController = void 0; const channels_1 = require("../../structures/channels"); @@ -34,48 +25,39 @@ class BotChannelsController extends base_1.BaseFetchController { return this.bot.api.fetchChannel(id); } /** @inheritDoc */ - get(id) { - const _super = Object.create(null, { - get: { get: () => super.get } - }); - return __awaiter(this, void 0, void 0, function* () { - const channel = yield _super.get.call(this, id); - if (channel instanceof channels_1.GuildChannel) { - channel.guild.channels.cache.add(channel); - } - else if (channel instanceof channels_1.DMChannel) { - channel.recipient.dm = channel; - } - return channel; - }); + async get(id) { + const channel = await super.get(id); + if (channel instanceof channels_1.GuildChannel) { + channel.guild.channels.cache.add(channel); + } + else if (channel instanceof channels_1.DMChannel) { + channel.recipient.dm = channel; + } + return channel; } /** * Gets or fetches a text channel by its ID * @param {Snowflake} id The ID of the text channel * @returns {Promise} */ - getText(id) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.get(id); - if (!(channel instanceof channels_1.GuildTextChannel || channel instanceof channels_1.DMChannel)) { - throw new TypeError('The channel is not a valid text channel'); - } - return channel; - }); + async getText(id) { + const channel = await this.get(id); + if (!(channel instanceof channels_1.GuildTextChannel || channel instanceof channels_1.DMChannel)) { + throw new TypeError('The channel is not a valid text channel'); + } + return channel; } /** * Gets or fetches a guild channel by its ID * @param {Snowflake} id The ID of the text channel * @returns {Promise} */ - getGuildChannel(id) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.get(id); - if (!(channel instanceof channels_1.GuildChannel)) { - throw new TypeError('The channel is not a valid guild channel'); - } - return channel; - }); + async getGuildChannel(id) { + const channel = await this.get(id); + if (!(channel instanceof channels_1.GuildChannel)) { + throw new TypeError('The channel is not a valid guild channel'); + } + return channel; } } exports.BotChannelsController = BotChannelsController; diff --git a/lib/controllers/bot/BotGuildsController.js b/lib/controllers/bot/BotGuildsController.js index 4daac7b1a..c031f0bb9 100644 --- a/lib/controllers/bot/BotGuildsController.js +++ b/lib/controllers/bot/BotGuildsController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotGuildsController = void 0; const base_1 = require("../base"); @@ -22,12 +13,10 @@ class BotGuildsController extends base_1.BaseFetchController { * @param {FetchGuildOptions} options The additional options for the fetch operation * @returns {Promise} */ - fetch(id, options) { - return __awaiter(this, void 0, void 0, function* () { - const guild = yield this.bot.api.fetchGuild(id, options); - this.cache.add(guild); - return guild; - }); + async fetch(id, options) { + const guild = await this.bot.api.fetchGuild(id, options); + this.cache.add(guild); + return guild; } /** * Fetches a guild preview by its guild ID diff --git a/lib/controllers/bot/BotUsersController.js b/lib/controllers/bot/BotUsersController.js index e11665e51..b14f69c5a 100644 --- a/lib/controllers/bot/BotUsersController.js +++ b/lib/controllers/bot/BotUsersController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotUsersController = void 0; const base_1 = require("../base"); @@ -23,30 +14,26 @@ class BotUsersController extends base_1.BaseFetchController { * Fetches the bot user * @returns {Promise} */ - fetchBot() { - return __awaiter(this, void 0, void 0, function* () { - const user = yield this.bot.api.fetchBotUser(); - this.cache.add(user); - if (this.bot.user) { - this.bot.user.update(user.structure); - } - else { - this.bot.user = user; - } - return user; - }); + async fetchBot() { + const user = await this.bot.api.fetchBotUser(); + this.cache.add(user); + if (this.bot.user) { + this.bot.user.update(user.structure); + } + else { + this.bot.user = user; + } + return user; } /** * Fetches a user by its ID * @param {Snowflake} id The user ID * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield this.bot.api.fetchUser(id); - this.cache.add(user); - return user; - }); + async fetch(id) { + const user = await this.bot.api.fetchUser(id); + this.cache.add(user); + return user; } } exports.BotUsersController = BotUsersController; diff --git a/lib/controllers/channel/ChannelMessagesController.js b/lib/controllers/channel/ChannelMessagesController.js index ee6175334..56c681f0f 100644 --- a/lib/controllers/channel/ChannelMessagesController.js +++ b/lib/controllers/channel/ChannelMessagesController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChannelMessagesController = void 0; const base_1 = require("../base"); @@ -33,24 +24,20 @@ class ChannelMessagesController extends base_1.BaseFetchController { * @param {Snowflake} id The ID of the message you wish to fetch * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const message = yield this.bot.api.fetchMessage(this.channel.id, id); - this.cache.add(message); - return message; - }); + async fetch(id) { + const message = await this.bot.api.fetchMessage(this.channel.id, id); + this.cache.add(message); + return message; } /** * Fetches and caches some messages in the channel * @param {FetchSomeMessagesOptions} options The options for the fetch operation * @returns {Promise>} */ - fetchSome(options) { - return __awaiter(this, void 0, void 0, function* () { - const messages = yield this.bot.api.fetchSomeMessages(this.channel.id, options); - this.cache.merge(messages); - return messages; - }); + async fetchSome(options) { + const messages = await this.bot.api.fetchSomeMessages(this.channel.id, options); + this.cache.merge(messages); + return messages; } } exports.ChannelMessagesController = ChannelMessagesController; diff --git a/lib/controllers/channel/ChannelPinsController.js b/lib/controllers/channel/ChannelPinsController.js index aece5d1d2..95263f53a 100644 --- a/lib/controllers/channel/ChannelPinsController.js +++ b/lib/controllers/channel/ChannelPinsController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChannelPinsController = void 0; const base_1 = require("../base"); @@ -24,12 +15,10 @@ class ChannelPinsController extends base_1.BaseFetchAllController { * Fetches all messages in the text channel and caches them * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const pins = yield this.bot.api.fetchChannelPins(this.channel.id); - this.cache.merge(pins); - return pins; - }); + async fetchAll() { + const pins = await this.bot.api.fetchChannelPins(this.channel.id); + this.cache.merge(pins); + return pins; } /** * Pins a message in a text channel. @@ -37,13 +26,11 @@ class ChannelPinsController extends base_1.BaseFetchAllController { * @param {Snowflake} id The ID of the message * @returns {Promise} */ - pin(id) { - return __awaiter(this, void 0, void 0, function* () { - yield this.bot.api.pinMessage(this.channel.id, id); - // Cache the pinned message - const message = yield this.channel.messages.get(id); - this.cache.add(message); - }); + async pin(id) { + await this.bot.api.pinMessage(this.channel.id, id); + // Cache the pinned message + const message = await this.channel.messages.get(id); + this.cache.add(message); } /** * Unpins a message in a text channel. @@ -51,12 +38,10 @@ class ChannelPinsController extends base_1.BaseFetchAllController { * @param {Snowflake} id The ID of the message * @returns {Promise} */ - unpin(id) { - return __awaiter(this, void 0, void 0, function* () { - yield this.bot.api.unpinMessage(this.channel.id, id); - // Remove the unpinned message from the cache - this.cache.delete(id); - }); + async unpin(id) { + await this.bot.api.unpinMessage(this.channel.id, id); + // Remove the unpinned message from the cache + this.cache.delete(id); } } exports.ChannelPinsController = ChannelPinsController; diff --git a/lib/controllers/guild/GuildBansController.js b/lib/controllers/guild/GuildBansController.js index e81ff71b5..44824603c 100644 --- a/lib/controllers/guild/GuildBansController.js +++ b/lib/controllers/guild/GuildBansController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildBansController = void 0; const base_1 = require("../base"); @@ -24,24 +15,20 @@ class GuildBansController extends base_1.BaseFetchController { * Fetches all bans in the guilds * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const bans = yield this.bot.api.fetchGuildBans(this.guild.id); - this.cache.merge(bans); - return bans; - }); + async fetchAll() { + const bans = await this.bot.api.fetchGuildBans(this.guild.id); + this.cache.merge(bans); + return bans; } /** * Fetches a guild ban by a user ID * @param {Snowflake} id The ID of the user * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const ban = yield this.bot.api.fetchGuildBan(this.guild.id, id); - this.cache.add(ban); - return ban; - }); + async fetch(id) { + const ban = await this.bot.api.fetchGuildBan(this.guild.id, id); + this.cache.add(ban); + return ban; } /** * Unbans a member from the guild. diff --git a/lib/controllers/guild/GuildChannelInvitesController.js b/lib/controllers/guild/GuildChannelInvitesController.js index 6ad0c7a25..39711de20 100644 --- a/lib/controllers/guild/GuildChannelInvitesController.js +++ b/lib/controllers/guild/GuildChannelInvitesController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildChannelInvitesController = void 0; const base_1 = require("../base"); @@ -25,12 +16,10 @@ class GuildChannelInvitesController extends base_1.BaseFetchAllController { * Requires the {@link Permission.ManageChannels} permission * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const invites = yield this.bot.api.fetchChannelInvites(this.channel.id); - this.cache.merge(invites); - return invites; - }); + async fetchAll() { + const invites = await this.bot.api.fetchChannelInvites(this.channel.id); + this.cache.merge(invites); + return invites; } /** * Creates a new invite for a guild channel. diff --git a/lib/controllers/guild/GuildChannelWebhooksController.js b/lib/controllers/guild/GuildChannelWebhooksController.js index 4f3b2837e..32c01168f 100644 --- a/lib/controllers/guild/GuildChannelWebhooksController.js +++ b/lib/controllers/guild/GuildChannelWebhooksController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildChannelWebhooksController = void 0; const base_1 = require("../base"); @@ -26,36 +17,30 @@ class GuildChannelWebhooksController extends base_1.BaseFetchController { * @param {CreateWebhookOptions} options The options for the new webhook * @returns {Promise} */ - create(options) { - return __awaiter(this, void 0, void 0, function* () { - const webhook = yield this.bot.api.createWebhook(this.channel.id, options); - this.cache.add(webhook); - return webhook; - }); + async create(options) { + const webhook = await this.bot.api.createWebhook(this.channel.id, options); + this.cache.add(webhook); + return webhook; } /** * Fetches all webhooks in this guild channel. * Requires the {@link Permission.ManageWebhooks} permission * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const webhooks = yield this.bot.api.fetchWebhooks(this.channel.id); - this.cache.merge(webhooks); - return webhooks; - }); + async fetchAll() { + const webhooks = await this.bot.api.fetchWebhooks(this.channel.id); + this.cache.merge(webhooks); + return webhooks; } /** * Fetches a webhook by its ID * @param {Snowflake} id The ID of the webhook * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const webhook = yield this.bot.api.fetchWebhook(id); - this.cache.add(webhook); - return webhook; - }); + async fetch(id) { + const webhook = await this.bot.api.fetchWebhook(id); + this.cache.add(webhook); + return webhook; } /** * Deletes a webhook permanently. @@ -63,11 +48,9 @@ class GuildChannelWebhooksController extends base_1.BaseFetchController { * @param {Snowflake} id The ID of the webhook * @returns {Promise} */ - delete(id) { - return __awaiter(this, void 0, void 0, function* () { - yield this.bot.api.deleteWebhook(id); - this.cache.delete(id); - }); + async delete(id) { + await this.bot.api.deleteWebhook(id); + this.cache.delete(id); } } exports.GuildChannelWebhooksController = GuildChannelWebhooksController; diff --git a/lib/controllers/guild/GuildChannelsController.js b/lib/controllers/guild/GuildChannelsController.js index 69c60f230..30e9b0e52 100644 --- a/lib/controllers/guild/GuildChannelsController.js +++ b/lib/controllers/guild/GuildChannelsController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildChannelsController = void 0; const channels_1 = require("../../structures/channels"); @@ -26,28 +17,24 @@ class GuildChannelsController extends base_1.BaseFetchController { * @param {Snowflake} id The ID of the guild text channel * @returns {Promise} */ - getText(id) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.get(id); - if (!(channel instanceof channels_1.GuildTextChannel)) { - throw new TypeError('The channel is not a valid guild text channel'); - } - return channel; - }); + async getText(id) { + const channel = await this.get(id); + if (!(channel instanceof channels_1.GuildTextChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + return channel; } /** * Gets or fetches a guild voice channel by its ID * @param {Snowflake} id The ID of the guild voice channel * @returns {Promise} */ - getVoice(id) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.get(id); - if (!(channel instanceof channels_1.GuildVoiceChannel)) { - throw new TypeError('The channel is not a valid guild text channel'); - } - return channel; - }); + async getVoice(id) { + const channel = await this.get(id); + if (!(channel instanceof channels_1.GuildVoiceChannel)) { + throw new TypeError('The channel is not a valid guild text channel'); + } + return channel; } /** * Creates a new guild channel in the guild associated to this controller. @@ -71,24 +58,20 @@ class GuildChannelsController extends base_1.BaseFetchController { * @param {Snowflake} id The ID of the guild channel you wish to fetch * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const channel = yield this.bot.api.fetchGuildChannel(id); - this.cache.add(channel); - this.bot.channels.cache.add(channel); - return channel; - }); + async fetch(id) { + const channel = await this.bot.api.fetchGuildChannel(id); + this.cache.add(channel); + this.bot.channels.cache.add(channel); + return channel; } /** * Fetches and caches all channels the guild associated to this controller * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const channels = yield this.bot.api.fetchGuildChannels(this.guild.id); - this.cache.merge(channels); - return channels; - }); + async fetchAll() { + const channels = await this.bot.api.fetchGuildChannels(this.guild.id); + this.cache.merge(channels); + return channels; } /** * Swaps the positions of 2 guild channels with one another @@ -96,11 +79,9 @@ class GuildChannelsController extends base_1.BaseFetchController { * @param {GuildChannel} channel2 The second guild channel * @returns {Promise} */ - swap(channel1, channel2) { - return __awaiter(this, void 0, void 0, function* () { - const positions = { [channel1.id]: channel2.position, [channel2.id]: channel1.position }; - return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); - }); + async swap(channel1, channel2) { + const positions = { [channel1.id]: channel2.position, [channel2.id]: channel1.position }; + return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); } /** * Modifies the positions of a set of channels for the guild. @@ -108,10 +89,8 @@ class GuildChannelsController extends base_1.BaseFetchController { * @param {Positions} positions The new positions for the guild channels * @returns {Promise} */ - modifyPositions(positions) { - return __awaiter(this, void 0, void 0, function* () { - return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); - }); + async modifyPositions(positions) { + return this.bot.api.modifyGuildChannelsPositions(this.guild.id, positions); } } exports.GuildChannelsController = GuildChannelsController; diff --git a/lib/controllers/guild/GuildEmojisController.js b/lib/controllers/guild/GuildEmojisController.js index d20459b82..e376a707f 100644 --- a/lib/controllers/guild/GuildEmojisController.js +++ b/lib/controllers/guild/GuildEmojisController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildEmojisController = void 0; const base_1 = require("../base"); @@ -33,23 +24,19 @@ class GuildEmojisController extends base_1.BaseFetchController { * @param {Snowflake} id The ID of the guild emoji * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const emoji = yield this.bot.api.fetchGuildEmoji(this.guild.id, id); - this.cache.add(emoji); - return emoji; - }); + async fetch(id) { + const emoji = await this.bot.api.fetchGuildEmoji(this.guild.id, id); + this.cache.add(emoji); + return emoji; } /** * Fetches all emojis in a guild * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const emojis = yield this.bot.api.fetchGuildEmojis(this.guild.id); - this.cache.merge(emojis); - return emojis; - }); + async fetchAll() { + const emojis = await this.bot.api.fetchGuildEmojis(this.guild.id); + this.cache.merge(emojis); + return emojis; } } exports.GuildEmojisController = GuildEmojisController; diff --git a/lib/controllers/guild/GuildIntegrationsController.js b/lib/controllers/guild/GuildIntegrationsController.js index 0f27ba138..7e2d56a4a 100644 --- a/lib/controllers/guild/GuildIntegrationsController.js +++ b/lib/controllers/guild/GuildIntegrationsController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildIntegrationsController = void 0; const base_1 = require("../base"); @@ -25,12 +16,10 @@ class GuildIntegrationsController extends base_1.BaseFetchAllController { * Requires the {@link Permission.ManageGuild} permission * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const integrations = yield this.bot.api.fetchGuildIntegrations(this.guild.id); - this.cache.merge(integrations); - return integrations; - }); + async fetchAll() { + const integrations = await this.bot.api.fetchGuildIntegrations(this.guild.id); + this.cache.merge(integrations); + return integrations; } /** * Attaches an integration from the Bot to this guild. diff --git a/lib/controllers/guild/GuildInvitesController.js b/lib/controllers/guild/GuildInvitesController.js index f6834301a..57551f300 100644 --- a/lib/controllers/guild/GuildInvitesController.js +++ b/lib/controllers/guild/GuildInvitesController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildInvitesController = void 0; const base_1 = require("../base"); @@ -34,24 +25,20 @@ class GuildInvitesController extends base_1.BaseFetchController { * @param {FetchInviteOptions} options An additional set of options for the invite * @returns {Promise} */ - fetch(code, options) { - return __awaiter(this, void 0, void 0, function* () { - const invite = yield this.bot.api.fetchInvite(code, options); - this.cache.add(invite); - return invite; - }); + async fetch(code, options) { + const invite = await this.bot.api.fetchInvite(code, options); + this.cache.add(invite); + return invite; } /** * Fetches all invites (with metadata) in this guild. * Requires the {@link Permission.ManageGuild} permission * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const invites = yield this.bot.api.fetchGuildInvites(this.guild.id); - this.cache.merge(invites); - return invites; - }); + async fetchAll() { + const invites = await this.bot.api.fetchGuildInvites(this.guild.id); + this.cache.merge(invites); + return invites; } } exports.GuildInvitesController = GuildInvitesController; diff --git a/lib/controllers/guild/GuildMembersController.js b/lib/controllers/guild/GuildMembersController.js index beb8241ab..206ade8ce 100644 --- a/lib/controllers/guild/GuildMembersController.js +++ b/lib/controllers/guild/GuildMembersController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildMembersController = void 0; const base_1 = require("../base"); @@ -25,34 +16,28 @@ class GuildMembersController extends base_1.BaseFetchController { * @param {Snowflake} id The ID of the guild member * @returns {Promise} */ - fetch(id) { - return __awaiter(this, void 0, void 0, function* () { - const member = yield this.bot.api.fetchMember(this.guild.id, id); - this.cache.add(member); - return member; - }); + async fetch(id) { + const member = await this.bot.api.fetchMember(this.guild.id, id); + this.cache.add(member); + return member; } /** * Fetches some guild members in this guild and caches them * @param {FetchSomeMembersOptions} options The options for the fetch operation * @returns {Promise>} */ - fetchSome(options) { - return __awaiter(this, void 0, void 0, function* () { - const members = yield this.bot.api.fetchSomeMembers(this.guild.id, options); - this.cache.merge(members); - return members; - }); + async fetchSome(options) { + const members = await this.bot.api.fetchSomeMembers(this.guild.id, options); + this.cache.merge(members); + return members; } /** * Removes a user from this guild by its user ID * @param {Snowflake} id The ID of the user * @returns {Promise} */ - remove(id) { - return __awaiter(this, void 0, void 0, function* () { - return this.bot.api.removeMember(this.guild.id, id); - }); + async remove(id) { + return this.bot.api.removeMember(this.guild.id, id); } /** * Returns the bot member in the guild diff --git a/lib/controllers/guild/GuildRolesController.js b/lib/controllers/guild/GuildRolesController.js index ad86f30e8..bbd8fab53 100644 --- a/lib/controllers/guild/GuildRolesController.js +++ b/lib/controllers/guild/GuildRolesController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildRolesController = void 0; const base_1 = require("../base"); @@ -24,12 +15,10 @@ class GuildRolesController extends base_1.BaseFetchAllController { * Fetches all roles in this guild * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const roles = yield this.bot.api.fetchRoles(this.guild.id); - this.cache.merge(roles); - return roles; - }); + async fetchAll() { + const roles = await this.bot.api.fetchRoles(this.guild.id); + this.cache.merge(roles); + return roles; } /** * Creates a new role in this guild. diff --git a/lib/controllers/guild/GuildWebhooksController.js b/lib/controllers/guild/GuildWebhooksController.js index 1fbf70de3..8e5a2c99f 100644 --- a/lib/controllers/guild/GuildWebhooksController.js +++ b/lib/controllers/guild/GuildWebhooksController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.GuildWebhooksController = void 0; const base_1 = require("../base"); @@ -24,12 +15,10 @@ class GuildWebhooksController extends base_1.BaseFetchAllController { * Fetches all webhooks in this guild * @returns {Promise>} */ - fetchAll() { - return __awaiter(this, void 0, void 0, function* () { - const webhooks = yield this.bot.api.fetchGuildWebhooks(this.guild.id); - this.cache.merge(webhooks); - return webhooks; - }); + async fetchAll() { + const webhooks = await this.bot.api.fetchGuildWebhooks(this.guild.id); + this.cache.merge(webhooks); + return webhooks; } } exports.GuildWebhooksController = GuildWebhooksController; diff --git a/lib/controllers/reaction/ReactionUsersController.js b/lib/controllers/reaction/ReactionUsersController.js index 3ee5eac42..c4b944e3c 100644 --- a/lib/controllers/reaction/ReactionUsersController.js +++ b/lib/controllers/reaction/ReactionUsersController.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReactionUsersController = void 0; const base_1 = require("../base"); @@ -26,23 +17,21 @@ class ReactionUsersController extends base_1.BaseFetchAllController { * @param {FetchReactionUsersOptions} options A set of options for this operation * @returns {Promise} */ - fetchAll(options) { - return __awaiter(this, void 0, void 0, function* () { - const users = yield this.bot.api.fetchReactionUsers(this.message.channel.id, this.message.id, this.reaction.id, options); - this.cache.merge(users); - if (this.bot.user && users.has(this.bot.user.id)) { - // The bot reacted to this reaction - this.reaction.botReacted = true; - } - if (this.message.guild) { - // The message was sent in a guild - // All users are also members in that guild - this.reaction.members.merge(users - .filter(user => this.message.guild.members.cache.has(user.id)) - .map(user => this.message.guild.members.cache.get(user.id))); - } - return users; - }); + async fetchAll(options) { + const users = await this.bot.api.fetchReactionUsers(this.message.channel.id, this.message.id, this.reaction.id, options); + this.cache.merge(users); + if (this.bot.user && users.has(this.bot.user.id)) { + // The bot reacted to this reaction + this.reaction.botReacted = true; + } + if (this.message.guild) { + // The message was sent in a guild + // All users are also members in that guild + this.reaction.members.merge(users + .filter(user => this.message.guild.members.cache.has(user.id)) + .map(user => this.message.guild.members.cache.get(user.id))); + } + return users; } } exports.ReactionUsersController = ReactionUsersController; diff --git a/lib/sharding/BotCommunication.js b/lib/sharding/BotCommunication.js index 0d7a34728..53baaada8 100644 --- a/lib/sharding/BotCommunication.js +++ b/lib/sharding/BotCommunication.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotCommunication = void 0; const events_1 = require("events"); @@ -22,32 +13,30 @@ class BotCommunication extends events_1.EventEmitter { * @param {ShardEmitCommunicationEventRequest | ShardEmitBotEventRequest} message The message received from the parent process * @returns {Promise} */ - onMessage(message) { - return __awaiter(this, void 0, void 0, function* () { - switch (message.action) { - // Tells the Bot to dispatch an event and return its result - case "emitCommunicationEvent" /* EmitCommunicationEvent */: - if (process.send) { - const data = yield this.emit(message.payload.event); - const reply = { - payload: { - data, - }, - identifier: message.identifier, - }; - process.send(reply); - } - break; - // Tells the Bot to emit an event to BotEvents - case "emitBotEvent" /* EmitBotEvent */: - this.bot.events.emit(message.payload.event, ...message.payload.args); - break; - // Tells the Bot to disconnect from its current connection - case "emitDisconnect" /* EmitDisconnect */: - this.bot.connection.disconnect(message.payload); - break; - } - }); + async onMessage(message) { + switch (message.action) { + // Tells the Bot to dispatch an event and return its result + case "emitCommunicationEvent" /* EmitCommunicationEvent */: + if (process.send) { + const data = await this.emit(message.payload.event); + const reply = { + payload: { + data, + }, + identifier: message.identifier, + }; + process.send(reply); + } + break; + // Tells the Bot to emit an event to BotEvents + case "emitBotEvent" /* EmitBotEvent */: + this.bot.events.emit(message.payload.event, ...message.payload.args); + break; + // Tells the Bot to disconnect from its current connection + case "emitDisconnect" /* EmitDisconnect */: + this.bot.connection.disconnect(message.payload); + break; + } } /** * {@link EventEmitter} 'on' override to resolve with the listener's returned value @@ -76,27 +65,25 @@ class BotCommunication extends events_1.EventEmitter { * @param {string} event The registered event to be emitted * @returns {Promise} */ - broadcast(event) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise(resolve => { - const { identifier } = BotCommunication; - const listener = ({ payload: { event: event, data }, identifier: responseIdentifier, }) => { - if (event === "broadcastResponses" /* BroadcastResponses */ && - identifier === responseIdentifier && - Array.isArray(data)) { - resolve(data); - } - }; - process.on('message', listener); - const request = { - action: "broadcast" /* Broadcast */, - payload: event, - identifier, - }; - if (process.send) { - process.send(request); + async broadcast(event) { + return new Promise(resolve => { + const { identifier } = BotCommunication; + const listener = ({ payload: { event: event, data }, identifier: responseIdentifier, }) => { + if (event === "broadcastResponses" /* BroadcastResponses */ && + identifier === responseIdentifier && + Array.isArray(data)) { + resolve(data); } - }); + }; + process.on('message', listener); + const request = { + action: "broadcast" /* Broadcast */, + payload: event, + identifier, + }; + if (process.send) { + process.send(request); + } }); } /** @@ -105,26 +92,24 @@ class BotCommunication extends events_1.EventEmitter { * @param {ShardId} shardId The ID of the shard where this event should be emitted * @returns {Promise} */ - send(event, shardId) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise(resolve => { - const { identifier } = BotCommunication; - const listener = ({ payload: { event, data }, identifier: responseIdentifier, }) => { - if (event === "sendResponse" /* SendResponse */ && - identifier === responseIdentifier) { - resolve(data); - } - }; - process.on('message', listener); - const message = { - action: "send" /* Send */, - payload: { event, shardId }, - identifier, - }; - if (process.send) { - process.send(message); + async send(event, shardId) { + return new Promise(resolve => { + const { identifier } = BotCommunication; + const listener = ({ payload: { event, data }, identifier: responseIdentifier, }) => { + if (event === "sendResponse" /* SendResponse */ && + identifier === responseIdentifier) { + resolve(data); } - }); + }; + process.on('message', listener); + const message = { + action: "send" /* Send */, + payload: { event, shardId }, + identifier, + }; + if (process.send) { + process.send(message); + } }); } /** diff --git a/lib/sharding/BotShard.js b/lib/sharding/BotShard.js index 79235d41e..d725fc9ee 100644 --- a/lib/sharding/BotShard.js +++ b/lib/sharding/BotShard.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -51,52 +42,50 @@ class BotShard { * @param {ShardBroadcastRequest | ShardSendRequest | ShardChangedStateRequest} request The request received from the child process * @returns {Promise} */ - onMessage(request) { - return __awaiter(this, void 0, void 0, function* () { - const { identifier } = request; - switch (request.action) { - // Broadcast requested by the shard - case "broadcast" /* Broadcast */: { - const results = yield this.manager.broadcast(request.payload); - const response = { - payload: { - event: "broadcastResponses" /* BroadcastResponses */, - data: results, - }, - identifier, - }; - this.process.send(response); - break; - } - // Single shard send requested by the shard - case "send" /* Send */: { - const { event, shardId } = request.payload; - const result = yield this.manager.send(event, shardId); - const response = { - payload: { - event: "sendResponse" /* SendResponse */, - data: result, - }, - identifier, - }; - this.process.send(response); - break; - } - // The shard changed its state - case "shardChangedState" /* ShardChangedState */: { - this.state = request.payload.state; - if (this.manager.checkShardsState(request.payload.state)) { - this.manager.emitEvent(request.payload.botEvent, []); - } - break; - } - // The shard requests all connected shards to disconnect - case "disconnectAll" /* DisconnectAll */: { - yield this.manager.disconnectAll(request.payload); - break; + async onMessage(request) { + const { identifier } = request; + switch (request.action) { + // Broadcast requested by the shard + case "broadcast" /* Broadcast */: { + const results = await this.manager.broadcast(request.payload); + const response = { + payload: { + event: "broadcastResponses" /* BroadcastResponses */, + data: results, + }, + identifier, + }; + this.process.send(response); + break; + } + // Single shard send requested by the shard + case "send" /* Send */: { + const { event, shardId } = request.payload; + const result = await this.manager.send(event, shardId); + const response = { + payload: { + event: "sendResponse" /* SendResponse */, + data: result, + }, + identifier, + }; + this.process.send(response); + break; + } + // The shard changed its state + case "shardChangedState" /* ShardChangedState */: { + this.state = request.payload.state; + if (this.manager.checkShardsState(request.payload.state)) { + this.manager.emitEvent(request.payload.botEvent, []); } + break; } - }); + // The shard requests all connected shards to disconnect + case "disconnectAll" /* DisconnectAll */: { + await this.manager.disconnectAll(request.payload); + break; + } + } } /** * Sends a message to the child process in order to emit a registered given event diff --git a/lib/sharding/BotShardManager.js b/lib/sharding/BotShardManager.js index 0f4ca8db6..61545ca4c 100644 --- a/lib/sharding/BotShardManager.js +++ b/lib/sharding/BotShardManager.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -29,16 +20,14 @@ class BotShardManager { * Starts the shards and stores them inside a {@link Collection} * @returns {Promise} */ - start() { - return __awaiter(this, void 0, void 0, function* () { - for (let i = 0; i < this.shardsAmount; i++) { - const shard = new BotShard_1.BotShard(this, i); - this.shards.set(shard.id, shard); - } - for (const [, shard] of this.shards) { - shard.spawn(); - } - }); + async start() { + for (let i = 0; i < this.shardsAmount; i++) { + const shard = new BotShard_1.BotShard(this, i); + this.shards.set(shard.id, shard); + } + for (const [, shard] of this.shards) { + shard.spawn(); + } } /** * Emits an event on all shards initiated with this manager diff --git a/lib/socket/BotSocket.js b/lib/socket/BotSocket.js index 01e2bbba8..97c88d8cb 100644 --- a/lib/socket/BotSocket.js +++ b/lib/socket/BotSocket.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -32,25 +23,31 @@ class BotSocket { * @param {number} [timeout=5500] Time in milliseconds to wait before establishing a new shard * @returns {Promise} */ - startShards(timeout = constants_1.recommendedShardTimeout) { - return __awaiter(this, void 0, void 0, function* () { - const { url: gatewayURL, shards: suggestedShards, session_start_limit: sessionStartLimit, } = yield this.gateway; - this.gatewayURL = gatewayURL; - this.sessionStartLimit = sessionStartLimit; - const { id, amount = suggestedShards } = this.bot.shardOptions; - const shards = id !== undefined ? [id] : Array.from({ length: amount }).map((_, i) => i); - for (const shardId of shards) { - const botShard = new BotSocketShard_1.BotSocketShard(this, this.token, { - amount, - id: shardId, - }); - botShard.configure(); - this.shards.set(shardId, botShard); - botShard.connect(); - // eslint-disable-next-line no-await-in-loop - yield new Promise(resolve => setTimeout(resolve, timeout)); - } - }); + async startShards(timeout = constants_1.recommendedShardTimeout) { + let amount; + const { url, shards: shard_count, session_start_limit } = await this.gateway; //only call this endpoint to retrieve a new URL if they are unable to properly establish a connection using the cached version of the URL. + this.gatewayURL = url; + if ((this.bot.options.shards.size === 'default' || !this.bot.options.shards.size) && + this.bot.options.shards.enabled) + amount = shard_count; + else if (!this.bot.options.shards.enabled) + amount = 1; + else + amount = this.bot.options.shards.size; + this.sessionStartLimit = session_start_limit; + const { id } = this.bot.shardOptions; + const shards = id !== undefined ? [id] : Array.from({ length: amount }).map((_, i) => i); + for (const shardId of shards) { + const botShard = new BotSocketShard_1.BotSocketShard(this, this.token, { + amount, + id: shardId, + }); + botShard.configure(); + this.shards.set(shardId, botShard); + await botShard.connect(); + // eslint-disable-next-line no-await-in-loop + await new Promise(resolve => setTimeout(resolve, timeout)); + } } /** * Stops and disconnects all active shards started by this process diff --git a/lib/socket/BotSocketShard.js b/lib/socket/BotSocketShard.js index 965817291..20b22a2b9 100644 --- a/lib/socket/BotSocketShard.js +++ b/lib/socket/BotSocketShard.js @@ -18,15 +18,6 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -81,7 +72,12 @@ class BotSocketShard { catch (err) { // Do not use data compressing } - this.options = Object.assign({ v: api_1.version, encoding: erlpack ? 'etf' : 'json', compress: zlib && 'zlib-stream' }, this.bot.options.websocket); + this.options = { + v: api_1.version, + encoding: erlpack ? 'etf' : 'json', + compress: zlib && 'zlib-stream', + ...this.bot.options.websocket, + }; this.bot.debug(this.options, 'options'); } /** @@ -89,24 +85,22 @@ class BotSocketShard { * @param {boolean} resume Whether to resume a previous connection * @returns {Promise} */ - connect(resume = false) { - return __awaiter(this, void 0, void 0, function* () { - this.bot.debug('Connecting...'); - if (this.state === 0 /* Connecting */) - return; - this.state = 0 /* Connecting */; - const { gatewayURL, sessionStartLimit } = this.botSocket; - const socketURL = this.socketURL(gatewayURL); - yield this.handleSessionLimit(sessionStartLimit); - this.ws = new ws_1.default(socketURL); - this.ws.on('message', this.onMessage.bind(this)); - this.ws.on('close', this.onClose.bind(this)); - this.heartbeats = new BotHeartbeats_1.BotHeartbeats(this); - this.botSocket.sessionStartLimit.remaining--; - if (resume && this.lastSequence) { - this.ws.on('open', this.resume.bind(this)); - } - }); + async connect(resume = false) { + this.bot.debug('Connecting...'); + if (this.state === 0 /* Connecting */) + return; + this.state = 0 /* Connecting */; + const { gatewayURL, sessionStartLimit } = this.botSocket; + const socketURL = this.socketURL(gatewayURL); + await this.handleSessionLimit(sessionStartLimit); + this.ws = new ws_1.default(socketURL); + this.ws.on('message', this.onMessage.bind(this)); + this.ws.on('close', this.onClose.bind(this)); + this.heartbeats = new BotHeartbeats_1.BotHeartbeats(this); + this.botSocket.sessionStartLimit.remaining--; + if (resume && this.lastSequence) { + this.ws.on('open', this.resume.bind(this)); + } } /** * Decompresses data from the WebSocket if the compress option is sent to the gateway @@ -201,7 +195,11 @@ class BotSocketShard { const { id, amount } = this.shard; this.send({ op: 2 /* Identify */, - d: Object.assign(Object.assign({}, properties_1.identify), { token: this.token, shard: [id, amount] }), + d: { + ...properties_1.identify, + token: this.token, + shard: [id, amount], + }, }); } /** @@ -249,23 +247,21 @@ class BotSocketShard { * @param {number} code WebSocket closure code * @param {string} reason The reason for the WebSocket closure */ - onClose(code, reason) { - return __awaiter(this, void 0, void 0, function* () { - this.bot.debug('Close', code, reason); - this.state = 3 /* Closed */; - // Emit the 'ShardClose' event to the Bot - this.bot.events.emit(constants_1.BotEvent.ShardClose, this); - // Tell the BotSocket that the shard has been closed - this.botSocket.shardChangedState(3 /* Closed */, sharding_1.BotShardState.Closed, constants_1.BotEvent.Close); - this.heartbeats.stopHeartbeat(); - if (!constants_1.UnreconnectableGatewayCloseCodes.includes(code)) { - if (this.retryTimeout) { - yield new Promise(resolve => setTimeout(resolve, this.retryTimeout)); - } - this.retryTimeout += 1000; - yield this.connect(!constants_1.UnresumeableGatewayCloseCodes.includes(code)); + async onClose(code, reason) { + this.bot.debug('Close', code, reason); + this.state = 3 /* Closed */; + // Emit the 'ShardClose' event to the Bot + this.bot.events.emit(constants_1.BotEvent.ShardClose, this); + // Tell the BotSocket that the shard has been closed + this.botSocket.shardChangedState(3 /* Closed */, sharding_1.BotShardState.Closed, constants_1.BotEvent.Close); + this.heartbeats.stopHeartbeat(); + if (!constants_1.UnreconnectableGatewayCloseCodes.includes(code)) { + if (this.retryTimeout) { + await new Promise(resolve => setTimeout(resolve, this.retryTimeout)); } - }); + this.retryTimeout += 1000; + await this.connect(!constants_1.UnresumeableGatewayCloseCodes.includes(code)); + } } /** * Sends a request to the gateway in order to resume from the last connection @@ -286,15 +282,13 @@ class BotSocketShard { * from connecting to the gateway * @returns {Promise} */ - handleSessionLimit(sessionLimit) { - return __awaiter(this, void 0, void 0, function* () { - const { remaining, reset_after: resetAfter } = sessionLimit; - this.bot.debug(remaining, resetAfter, 'Handle session limit'); - if (remaining === 0) { - console.error(`Maximum number of daily Discord API connections exceeded! You will have to wait ${resetAfter}ms before attempting a new connection`); - yield new Promise(resolve => setTimeout(resolve, resetAfter)); - } - }); + async handleSessionLimit(sessionLimit) { + const { remaining, reset_after: resetAfter } = sessionLimit; + this.bot.debug(remaining, resetAfter, 'Handle session limit'); + if (remaining === 0) { + console.error(`Maximum number of daily Discord API connections exceeded! You will have to wait ${resetAfter}ms before attempting a new connection`); + await new Promise(resolve => setTimeout(resolve, resetAfter)); + } } /** * Packs and sends the data to the WebSocket diff --git a/lib/socket/handlers/channelCreate.js b/lib/socket/handlers/channelCreate.js index e4493b39a..595a1b144 100644 --- a/lib/socket/handlers/channelCreate.js +++ b/lib/socket/handlers/channelCreate.js @@ -1,18 +1,9 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../../structures/channels/utils"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { - const channel = yield utils_1.ChannelUtils.create(bot, d); +exports.default = async ({ d }, bot) => { + const channel = await utils_1.ChannelUtils.create(bot, d); utils_1.ChannelUtils.cache(bot, channel); bot.events.emit(constants_1.BotEvent.ChannelCreate, channel); -}); +}; diff --git a/lib/socket/handlers/channelDelete.js b/lib/socket/handlers/channelDelete.js index cc2fb6aeb..1af2ea94c 100644 --- a/lib/socket/handlers/channelDelete.js +++ b/lib/socket/handlers/channelDelete.js @@ -1,18 +1,9 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../../structures/channels/utils"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { - const channel = yield utils_1.ChannelUtils.create(bot, d); +exports.default = async ({ d }, bot) => { + const channel = await utils_1.ChannelUtils.create(bot, d); utils_1.ChannelUtils.delete(bot, channel); bot.events.emit(constants_1.BotEvent.ChannelDelete, channel); -}); +}; diff --git a/lib/socket/handlers/channelPinsUpdate.js b/lib/socket/handlers/channelPinsUpdate.js index fab2c7278..98d7f832a 100644 --- a/lib/socket/handlers/channelPinsUpdate.js +++ b/lib/socket/handlers/channelPinsUpdate.js @@ -1,20 +1,11 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const structures_1 = require("../../structures"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { channel_id: channelId, last_pin_timestamp: lastPinTimestamp } = d; - const channel = yield bot.channels.getText(channelId); + const channel = await bot.channels.getText(channelId); const oldPinTimestamp = channel.pins.lastPinTimestamp; channel.pins.lastPinTimestamp = new structures_1.Timestamp(lastPinTimestamp); bot.events.emit(constants_1.BotEvent.ChannelPinsUpdate, channel, oldPinTimestamp); -}); +}; diff --git a/lib/socket/handlers/channelUpdate.js b/lib/socket/handlers/channelUpdate.js index 94dbaf0cf..efb999f11 100644 --- a/lib/socket/handlers/channelUpdate.js +++ b/lib/socket/handlers/channelUpdate.js @@ -1,18 +1,9 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { id } = d; - const channel = yield bot.channels.get(id); + const channel = await bot.channels.get(id); const { before, after } = channel.update(d); bot.events.emit(constants_1.BotEvent.ChannelUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/guildBanAdd.js b/lib/socket/handlers/guildBanAdd.js index 9f4184701..e461c168d 100644 --- a/lib/socket/handlers/guildBanAdd.js +++ b/lib/socket/handlers/guildBanAdd.js @@ -1,21 +1,12 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const guild_1 = require("../../structures/guild"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, user } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const ban = new guild_1.GuildBan(bot, { user }, guild); // Add the member to the guild's bans controller guild.bans.cache.add(ban); bot.events.emit(constants_1.BotEvent.GuildBanAdd, ban); -}); +}; diff --git a/lib/socket/handlers/guildBanRemove.js b/lib/socket/handlers/guildBanRemove.js index 08633a264..4c8176d86 100644 --- a/lib/socket/handlers/guildBanRemove.js +++ b/lib/socket/handlers/guildBanRemove.js @@ -1,21 +1,12 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, user } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); // Retrieve the ban if cached const ban = guild.bans.cache.has(user.id) ? guild.bans.cache.get(user.id) : undefined; // Remove the member from the Guild's bans Collection guild.bans.cache.delete(user.id); bot.events.emit(constants_1.BotEvent.GuildBanRemove, ban); -}); +}; diff --git a/lib/socket/handlers/guildDelete.js b/lib/socket/handlers/guildDelete.js index ee58a7569..c942b4d1f 100644 --- a/lib/socket/handlers/guildDelete.js +++ b/lib/socket/handlers/guildDelete.js @@ -1,19 +1,10 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const guild_1 = require("../../structures/guild"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { unavailable, id } = d; - const guild = unavailable ? new guild_1.GuildUnavailable(bot, d) : yield bot.guilds.get(id); + const guild = unavailable ? new guild_1.GuildUnavailable(bot, d) : await bot.guilds.get(id); guild_1.Guild.delete(bot, guild); bot.events.emit(constants_1.BotEvent.GuildDelete, guild); -}); +}; diff --git a/lib/socket/handlers/guildEmojisUpdate.js b/lib/socket/handlers/guildEmojisUpdate.js index 050cc1ec4..0b072dcbc 100644 --- a/lib/socket/handlers/guildEmojisUpdate.js +++ b/lib/socket/handlers/guildEmojisUpdate.js @@ -1,23 +1,14 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const controllers_1 = require("../../controllers"); const guild_1 = require("../../structures/guild"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, emojis } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const before = guild.emojis.cache; const after = new controllers_1.ControllerCache(emojis.map((emoji) => [emoji.id, new guild_1.GuildEmoji(bot, emoji, guild)])); guild.emojis.cache = after; bot.emojis.merge(guild.emojis.cache); bot.events.emit(constants_1.BotEvent.GuildEmojisUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/guildIntegrationsUpdate.js b/lib/socket/handlers/guildIntegrationsUpdate.js index 738f656e2..f5093d9c9 100644 --- a/lib/socket/handlers/guildIntegrationsUpdate.js +++ b/lib/socket/handlers/guildIntegrationsUpdate.js @@ -1,17 +1,8 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); bot.events.emit(constants_1.BotEvent.GuildIntegrationsUpdate, guild); -}); +}; diff --git a/lib/socket/handlers/guildMemberAdd.js b/lib/socket/handlers/guildMemberAdd.js index 8bb3a8a6e..02341b0ef 100644 --- a/lib/socket/handlers/guildMemberAdd.js +++ b/lib/socket/handlers/guildMemberAdd.js @@ -1,19 +1,10 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const Member_1 = require("../../structures/member/Member"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const member = new Member_1.Member(bot, d, guild); // Cache the member in the guild's members cache guild.members.cache.add(member); @@ -22,4 +13,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () bot.users.cache.add(member.user); } bot.events.emit(constants_1.BotEvent.GuildMemberAdd, member); -}); +}; diff --git a/lib/socket/handlers/guildMemberRemove.js b/lib/socket/handlers/guildMemberRemove.js index 01a85efa2..24edf28a8 100644 --- a/lib/socket/handlers/guildMemberRemove.js +++ b/lib/socket/handlers/guildMemberRemove.js @@ -1,21 +1,12 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const structures_1 = require("../../structures"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, user } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const member = guild.members.cache.get(user.id) || new structures_1.User(bot, user); // Remove the member from the guild's members cache guild.members.cache.delete(member.id); bot.events.emit(constants_1.BotEvent.GuildMemberRemove, member); -}); +}; diff --git a/lib/socket/handlers/guildMemberUpdate.js b/lib/socket/handlers/guildMemberUpdate.js index 95d7d3ac1..12a8bb3b5 100644 --- a/lib/socket/handlers/guildMemberUpdate.js +++ b/lib/socket/handlers/guildMemberUpdate.js @@ -1,19 +1,14 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, user } = d; - const guild = yield bot.guilds.get(guildId); - const member = yield guild.members.get(user.id); - const { before, after } = member.update(Object.assign({ nick: member.nick, joined_at: member.joinedAt.date }, d)); + const guild = await bot.guilds.get(guildId); + const member = await guild.members.get(user.id); + const { before, after } = member.update({ + nick: member.nick, + joined_at: member.joinedAt.date, + ...d, + }); bot.events.emit(constants_1.BotEvent.GuildMemberUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/guildMembersChunk.js b/lib/socket/handlers/guildMembersChunk.js index 6b6918205..7f3ef6e1b 100644 --- a/lib/socket/handlers/guildMembersChunk.js +++ b/lib/socket/handlers/guildMembersChunk.js @@ -1,29 +1,20 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const member_1 = require("../../structures/member"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { not_found: notFound, guild_id: guildId, members, presences, nonce, chunk_index: chunkIndex, chunk_count: chunkCount, } = d; if (notFound) { throw new Error('An invalid ID was passed to the Guild Members request'); } - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); // Add the new members to the guild's members cache guild.members.cache.addMany(members.map((member) => new member_1.Member(bot, member, guild))); // Assign the presence returned from the event if (presences) { for (const presence of presences) { const { id } = presence.user; - const member = yield guild.members.get(id); + const member = await guild.members.get(id); if (member.presence) { // Re-initialize the member presence class with the updated presence member.presence.init(presence); @@ -44,4 +35,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () // This is the last chunk of the request, activate the GuildMembersChunkFinish event bot.events.emit(constants_1.BotEvent.GuildMembersChunkFinish, guild, nonce); } -}); +}; diff --git a/lib/socket/handlers/guildRoleCreate.js b/lib/socket/handlers/guildRoleCreate.js index d4e5c33fd..52fad1193 100644 --- a/lib/socket/handlers/guildRoleCreate.js +++ b/lib/socket/handlers/guildRoleCreate.js @@ -1,21 +1,12 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const structures_1 = require("../../structures"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const role = new structures_1.Role(bot, d.role, guild); // Add role to the guild's roles cache guild.roles.cache.add(role); bot.events.emit(constants_1.BotEvent.GuildRoleCreate, role); -}); +}; diff --git a/lib/socket/handlers/guildRoleDelete.js b/lib/socket/handlers/guildRoleDelete.js index 2679d61d1..2f7608e7f 100644 --- a/lib/socket/handlers/guildRoleDelete.js +++ b/lib/socket/handlers/guildRoleDelete.js @@ -1,22 +1,13 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, role_id: roleId } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const role = guild.roles.cache.get(roleId); if (!role) return; // Remove the role from the guild's roles cache guild.roles.cache.delete(role.id); bot.events.emit(constants_1.BotEvent.GuildRoleDelete, role); -}); +}; diff --git a/lib/socket/handlers/guildRoleUpdate.js b/lib/socket/handlers/guildRoleUpdate.js index b6481b1e3..3539fcca4 100644 --- a/lib/socket/handlers/guildRoleUpdate.js +++ b/lib/socket/handlers/guildRoleUpdate.js @@ -1,21 +1,12 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId } = d; - const guild = yield bot.guilds.get(guildId); + const guild = await bot.guilds.get(guildId); const role = guild.roles.cache.get(d.role.id); if (!role) return; const { before, after } = role.update(d.role); bot.events.emit(constants_1.BotEvent.GuildRoleUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/guildUpdate.js b/lib/socket/handlers/guildUpdate.js index 8d6bcf7cc..d5ce23a4f 100644 --- a/lib/socket/handlers/guildUpdate.js +++ b/lib/socket/handlers/guildUpdate.js @@ -1,21 +1,12 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const guild_1 = require("../../structures/guild"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { id } = d; const guild = guild_1.Guild.find(bot, id); if (!guild) return; const { before, after } = guild.update(d); bot.events.emit(constants_1.BotEvent.GuildUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/inviteDelete.js b/lib/socket/handlers/inviteDelete.js index 664beac9b..28f7e856a 100644 --- a/lib/socket/handlers/inviteDelete.js +++ b/lib/socket/handlers/inviteDelete.js @@ -1,20 +1,11 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const channels_1 = require("../../structures/channels"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, channel_id: channelId, code } = d; - const guild = yield bot.guilds.get(guildId); - const channel = yield bot.channels.get(channelId); + const guild = await bot.guilds.get(guildId); + const channel = await bot.channels.get(channelId); const invite = (guild === null || guild === void 0 ? void 0 : guild.invites.cache.get(code)) || { channelId: channelId, guild, @@ -27,4 +18,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () channel.invites.cache.delete(invite.code); } bot.events.emit(constants_1.BotEvent.InviteDelete, invite); -}); +}; diff --git a/lib/socket/handlers/messageCreate.js b/lib/socket/handlers/messageCreate.js index 92cda18bf..9e1bc79cf 100644 --- a/lib/socket/handlers/messageCreate.js +++ b/lib/socket/handlers/messageCreate.js @@ -1,23 +1,14 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const message_1 = require("../../structures/message"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { channel_id: channelId } = d; - const channel = yield bot.channels.getText(channelId); + const channel = await bot.channels.getText(channelId); const message = new message_1.Message(bot, d, channel); // Add the message to the cache channel.messages.cache.add(message); // Set the last message ID of the channel to that message channel.lastMessageId = message.id; bot.events.emit(constants_1.BotEvent.MessageCreate, message); -}); +}; diff --git a/lib/socket/handlers/messageDelete.js b/lib/socket/handlers/messageDelete.js index 7421447eb..8e2dc5c48 100644 --- a/lib/socket/handlers/messageDelete.js +++ b/lib/socket/handlers/messageDelete.js @@ -1,20 +1,11 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const channels_1 = require("../../structures/channels"); const message_1 = require("../../structures/message"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { id, channel_id: channelId } = d; - const channel = yield bot.channels.getText(channelId); + const channel = await bot.channels.getText(channelId); const guild = channel instanceof channels_1.GuildTextChannel ? channel.guild : undefined; const message = channel.messages.cache.get(id) || { id, @@ -27,4 +18,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () } channel.messages.cache.delete(message.id); bot.events.emit(constants_1.BotEvent.MessageDelete, message); -}); +}; diff --git a/lib/socket/handlers/messageDeleteBulk.js b/lib/socket/handlers/messageDeleteBulk.js index 795d0fda9..79dd6a201 100644 --- a/lib/socket/handlers/messageDeleteBulk.js +++ b/lib/socket/handlers/messageDeleteBulk.js @@ -1,19 +1,10 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const message_1 = require("../../structures/message"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { ids, channel_id: channelId } = d; - const channel = yield bot.channels.getText(channelId); + const channel = await bot.channels.getText(channelId); // Use the Message class if the message is cached, otherwise use the message ID const messages = ids.map((id) => channel.messages.cache.get(id) || id); for (const message of messages) { @@ -23,4 +14,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () } } bot.events.emit(constants_1.BotEvent.MessageDeleteBulk, channel, messages); -}); +}; diff --git a/lib/socket/handlers/messageReactionAdd.js b/lib/socket/handlers/messageReactionAdd.js index 9573c1a8f..56c91363f 100644 --- a/lib/socket/handlers/messageReactionAdd.js +++ b/lib/socket/handlers/messageReactionAdd.js @@ -1,24 +1,15 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const Member_1 = require("../../structures/member/Member"); const message_1 = require("../../structures/message"); const constants_1 = require("../constants"); const utils_1 = require("../utils"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { var _a, _b; const { user_id: userId } = d; const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); const { emoji } = handlersUtils; - const message = yield handlersUtils.getMessage(); + const message = await handlersUtils.getMessage(); const { guild } = message; const { id } = emoji; // Set the reaction object for this Emoji if one hasn't been set before @@ -30,7 +21,7 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () reaction.botReacted = userId === ((_b = bot.user) === null || _b === void 0 ? void 0 : _b.id); // Increment the count of the reaction reaction.count++; - const user = yield bot.users.get(userId); + const user = await bot.users.get(userId); const member = d.member && guild ? new Member_1.Member(bot, d.member, guild) : undefined; // Add the user to the Collection of users who reacted with this reaction if (user) { @@ -41,4 +32,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () reaction.members.set(member.id, member); } bot.events.emit(constants_1.BotEvent.MessageReactionAdd, reaction, member || user); -}); +}; diff --git a/lib/socket/handlers/messageReactionRemove.js b/lib/socket/handlers/messageReactionRemove.js index 2be170147..c8e05c921 100644 --- a/lib/socket/handlers/messageReactionRemove.js +++ b/lib/socket/handlers/messageReactionRemove.js @@ -1,22 +1,13 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); const utils_1 = require("../utils"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { var _a; const { user_id: userId } = d; const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); const { emoji } = handlersUtils; - const message = yield handlersUtils.getMessage(); + const message = await handlersUtils.getMessage(); const { id } = emoji; // Validates that the reaction is cached if (!message.reactions.cache.has(id)) @@ -43,4 +34,4 @@ exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () message.reactions.cache.delete(id); } bot.events.emit(constants_1.BotEvent.MessageReactionRemove, reaction, member || user); -}); +}; diff --git a/lib/socket/handlers/messageReactionRemoveAll.js b/lib/socket/handlers/messageReactionRemoveAll.js index efc8bfefb..0a4ea9bcc 100644 --- a/lib/socket/handlers/messageReactionRemoveAll.js +++ b/lib/socket/handlers/messageReactionRemoveAll.js @@ -1,19 +1,10 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); const utils_1 = require("../utils"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); - const message = yield handlersUtils.getMessage(); + const message = await handlersUtils.getMessage(); message.reactions.cache.clear(); bot.events.emit(constants_1.BotEvent.MessageReactionRemoveAll, message); -}); +}; diff --git a/lib/socket/handlers/messageReactionRemoveEmoji.js b/lib/socket/handlers/messageReactionRemoveEmoji.js index eb7d3e618..fb398fe36 100644 --- a/lib/socket/handlers/messageReactionRemoveEmoji.js +++ b/lib/socket/handlers/messageReactionRemoveEmoji.js @@ -1,22 +1,13 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); const utils_1 = require("../utils"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const handlersUtils = new utils_1.ReactionHandlersUtils(bot, d); const { emoji } = handlersUtils; - const message = yield handlersUtils.getMessage(); + const message = await handlersUtils.getMessage(); const { id } = emoji; const reaction = message.reactions.cache.get(id); message.reactions.cache.delete(id); bot.events.emit(constants_1.BotEvent.MessageReactionRemoveEmoji, reaction); -}); +}; diff --git a/lib/socket/handlers/messageUpdate.js b/lib/socket/handlers/messageUpdate.js index fc3c304f3..e5d099461 100644 --- a/lib/socket/handlers/messageUpdate.js +++ b/lib/socket/handlers/messageUpdate.js @@ -1,19 +1,10 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { channel_id: channelId, id } = d; - const channel = yield bot.channels.getText(channelId); - const message = yield channel.messages.get(id); + const channel = await bot.channels.getText(channelId); + const message = await channel.messages.get(id); const { before, after } = message.update(d); bot.events.emit(constants_1.BotEvent.MessageUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/presenceUpdate.js b/lib/socket/handlers/presenceUpdate.js index 644601677..48b7260a1 100644 --- a/lib/socket/handlers/presenceUpdate.js +++ b/lib/socket/handlers/presenceUpdate.js @@ -1,23 +1,14 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const member_1 = require("../../structures/member"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { user: { id: memberId }, guild_id: guildId, } = d; - const guild = yield bot.guilds.get(guildId); - const member = yield guild.members.get(memberId); + const guild = await bot.guilds.get(guildId); + const member = await guild.members.get(memberId); const { presence } = member; // Manual update due to presence's possibility of being undefined const before = presence === null || presence === void 0 ? void 0 : presence.clone(); const after = (presence === null || presence === void 0 ? void 0 : presence.init(d)) || (member.presence = new member_1.MemberPresence(bot, d, member)); bot.events.emit(constants_1.BotEvent.PresenceUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/typingStart.js b/lib/socket/handlers/typingStart.js index 9f28464ec..35b2dafd8 100644 --- a/lib/socket/handlers/typingStart.js +++ b/lib/socket/handlers/typingStart.js @@ -1,23 +1,14 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const channels_1 = require("../../structures/channels"); const member_1 = require("../../structures/member"); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { channel_id: channelId, timestamp, member } = d; - const channel = yield bot.channels.getText(channelId); + const channel = await bot.channels.getText(channelId); // Retrieve user / member from the channel const user = channel instanceof channels_1.GuildTextChannel ? new member_1.Member(bot, member, channel.guild) : channel.recipient; bot.events.emit(constants_1.BotEvent.TypingStart, channel, user, timestamp); -}); +}; diff --git a/lib/socket/handlers/userUpdate.js b/lib/socket/handlers/userUpdate.js index 28c715f92..5d08f09d9 100644 --- a/lib/socket/handlers/userUpdate.js +++ b/lib/socket/handlers/userUpdate.js @@ -1,20 +1,11 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { id } = d; const user = bot.users.cache.get(id); if (!user) return; const { before, after } = user.update(d); bot.events.emit(constants_1.BotEvent.UserUpdate, before, after); -}); +}; diff --git a/lib/socket/handlers/voiceServerUpdate.js b/lib/socket/handlers/voiceServerUpdate.js index 9a8636bf5..96055200b 100644 --- a/lib/socket/handlers/voiceServerUpdate.js +++ b/lib/socket/handlers/voiceServerUpdate.js @@ -1,15 +1,6 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const __1 = require(".."); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { bot.events.emit(__1.BotEvent.VoiceServerUpdate, bot.guilds.cache.get(d.guild_id), d); -}); +}; diff --git a/lib/socket/handlers/voiceStateUpdate.js b/lib/socket/handlers/voiceStateUpdate.js index effe38602..aeadea7b9 100644 --- a/lib/socket/handlers/voiceStateUpdate.js +++ b/lib/socket/handlers/voiceStateUpdate.js @@ -1,19 +1,10 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const __1 = require(".."); const VoiceState_1 = require("../../structures/voice/VoiceState"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const oldVoiceState = bot.guilds.cache.get(d.guild_id).voiceStates.get(d.user_id); const newVoiceState = new VoiceState_1.VoiceState(bot, bot.guilds.cache.get(d.guild_id).members.cache.get(d.user_id), d); bot.events.emit(__1.BotEvent.VoiceStateUpdate, oldVoiceState, newVoiceState); bot.guilds.cache.get(d.guild_id).voiceStates.set(d.user_id, newVoiceState); -}); +}; diff --git a/lib/socket/handlers/webhooksUpdate.js b/lib/socket/handlers/webhooksUpdate.js index 70addf334..72cd65e43 100644 --- a/lib/socket/handlers/webhooksUpdate.js +++ b/lib/socket/handlers/webhooksUpdate.js @@ -1,18 +1,9 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); -exports.default = ({ d }, bot) => __awaiter(void 0, void 0, void 0, function* () { +exports.default = async ({ d }, bot) => { const { guild_id: guildId, channel_id: channelId } = d; - const guild = yield bot.guilds.get(guildId); - const channel = yield guild.channels.get(channelId); + const guild = await bot.guilds.get(guildId); + const channel = await guild.channels.get(channelId); bot.events.emit(constants_1.BotEvent.WebhooksUpdate, channel); -}); +}; diff --git a/lib/socket/utils/ReactionHandlersUtils.js b/lib/socket/utils/ReactionHandlersUtils.js index 25fc4deda..5d0722249 100644 --- a/lib/socket/utils/ReactionHandlersUtils.js +++ b/lib/socket/utils/ReactionHandlersUtils.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReactionHandlersUtils = void 0; const HandlersUtils_1 = require("./HandlersUtils"); @@ -27,12 +18,10 @@ class ReactionHandlersUtils extends HandlersUtils_1.HandlersUtils { * Returns the message extracted from the event data * @type {Message | undefined} */ - getMessage() { - return __awaiter(this, void 0, void 0, function* () { - const { channel_id: channelId, message_id: messageId } = this.data; - const channel = yield this.bot.channels.getText(channelId); - return channel.messages.get(messageId); - }); + async getMessage() { + const { channel_id: channelId, message_id: messageId } = this.data; + const channel = await this.bot.channels.getText(channelId); + return channel.messages.get(messageId); } } exports.ReactionHandlersUtils = ReactionHandlersUtils; diff --git a/lib/structures/ImageURI.js b/lib/structures/ImageURI.js index 09747726f..1e432f335 100644 --- a/lib/structures/ImageURI.js +++ b/lib/structures/ImageURI.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -35,14 +26,12 @@ class ImageURI { * Returns the image mime and base64 data as a formatted string * @returns {string} */ - stringify() { - return __awaiter(this, void 0, void 0, function* () { - const { image, mime } = this; - if (!mime) { - throw new Error(`Invalid mime type for image ${this.path}`); - } - return `data:${mime};base64,${yield image}`; - }); + async stringify() { + const { image, mime } = this; + if (!mime) { + throw new Error(`Invalid mime type for image ${this.path}`); + } + return `data:${mime};base64,${await image}`; } /** * Returns the image as base64 diff --git a/lib/structures/User.js b/lib/structures/User.js index 08f15aff0..6a5c523f7 100644 --- a/lib/structures/User.js +++ b/lib/structures/User.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.User = exports.NitroType = void 0; const Avatar_1 = require("./Avatar"); @@ -79,11 +70,9 @@ class User extends base_1.BaseStruct { * @param {any} args Identical to the arguments of {@link TextChannel.sendMessage} * @returns {any} Identical to the return type of {@link TextChannel.sendMessage} */ - sendMessage(...args) { - return __awaiter(this, void 0, void 0, function* () { - const dm = this.dm || (yield this.createDM()); - return dm.sendMessage(...args); - }); + async sendMessage(...args) { + const dm = this.dm || (await this.createDM()); + return dm.sendMessage(...args); } /** * Combines a user's username and hashtag and generates a full name diff --git a/lib/structures/Webhook.js b/lib/structures/Webhook.js index 7c94bebf7..949201fc7 100644 --- a/lib/structures/Webhook.js +++ b/lib/structures/Webhook.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.Webhook = exports.WebhookType = void 0; const User_1 = require("./User"); @@ -48,12 +39,10 @@ class Webhook extends base_1.BaseGuildStruct { * @param {ModifyWebhookOptions} options The options for the modified webhook * @returns {Promise} */ - modify(options) { - return __awaiter(this, void 0, void 0, function* () { - const webhook = yield this.bot.api.modifyWebhook(this.id, options); - this.update(webhook); - return webhook; - }); + async modify(options) { + const webhook = await this.bot.api.modifyWebhook(this.id, options); + this.update(webhook); + return webhook; } } exports.Webhook = Webhook; diff --git a/lib/structures/base/BaseStruct.js b/lib/structures/base/BaseStruct.js index 4f5151bf6..1eff90a60 100644 --- a/lib/structures/base/BaseStruct.js +++ b/lib/structures/base/BaseStruct.js @@ -33,7 +33,7 @@ class BaseStruct { */ update(data) { const clone = this.clone(); - return { before: clone, after: this.init(Object.assign(Object.assign({}, this.structure), data)) }; + return { before: clone, after: this.init({ ...this.structure, ...data }) }; } } exports.BaseStruct = BaseStruct; diff --git a/lib/structures/channels/utils/ChannelUtils.js b/lib/structures/channels/utils/ChannelUtils.js index af67111cf..93e75fe75 100644 --- a/lib/structures/channels/utils/ChannelUtils.js +++ b/lib/structures/channels/utils/ChannelUtils.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChannelUtils = void 0; const Channel_1 = require("../Channel"); @@ -27,14 +18,12 @@ class ChannelUtils { * @param {Guild | undefined} guild_ The guild associated to the channel * @returns {Promise} */ - static create(bot, data, guild_) { - return __awaiter(this, void 0, void 0, function* () { - const { guild_id: guildId } = data; - const guild = guild_ || (guildId && (yield bot.guilds.get(guildId))); - return guild - ? ChannelUtils.createGuildChannel(bot, data, guild) - : ChannelUtils.createDMChannel(bot, data); - }); + static async create(bot, data, guild_) { + const { guild_id: guildId } = data; + const guild = guild_ || (guildId && (await bot.guilds.get(guildId))); + return guild + ? ChannelUtils.createGuildChannel(bot, data, guild) + : ChannelUtils.createDMChannel(bot, data); } /** * Creates a new {@link GuildChannel} instance, initialized relatively to its type diff --git a/lib/structures/message/MessageMentions.js b/lib/structures/message/MessageMentions.js index a6aa42dc6..a457eaa58 100644 --- a/lib/structures/message/MessageMentions.js +++ b/lib/structures/message/MessageMentions.js @@ -41,7 +41,7 @@ class MessageMentions extends base_1.BaseStruct { .filter(user => user.member) .map(user => [ user.id, - new Member_1.Member(this.bot, Object.assign(Object.assign({}, user.member), { user }), this.message.guild), + new Member_1.Member(this.bot, { ...user.member, user }, this.message.guild), ])); } if (mentions.roles && this.message.guild) { diff --git a/lib/structures/voice/Connection.js b/lib/structures/voice/Connection.js index 777271a01..4f69c54a3 100644 --- a/lib/structures/voice/Connection.js +++ b/lib/structures/voice/Connection.js @@ -5,6 +5,7 @@ const UDPSocket_1 = require("./UDPSocket"); const VoiceWebSocket_1 = require("./VoiceWebSocket"); class Connection { constructor(voice) { + this.sockets = {}; this.active = false; this._endpoint = ''; this.voice = voice; diff --git a/lib/structures/voice/GuildVoice.js b/lib/structures/voice/GuildVoice.js index c27b36b3a..b9ac1c272 100644 --- a/lib/structures/voice/GuildVoice.js +++ b/lib/structures/voice/GuildVoice.js @@ -13,15 +13,6 @@ class GuildVoice { this.bot = guild.bot; } join(channelId, options) { - this.guild.shard.send({ - op: 2, - d: { - guild_id: this.guild.id, - channel_id: channelId, - self_mute: !!options.mute, - self_deaf: !!options.deaf, - }, - }); return new Promise(resolve => { const listener = ((guild, voiceServer) => { if (guild.id === this.guild.id) { @@ -32,6 +23,15 @@ class GuildVoice { } }).bind(this); this.bot.events.on(socket_1.BotEvent.VoiceServerUpdate, listener); + this.bot.connection.shards.first.send({ + op: 4, + d: { + guild_id: this.guild.id, + channel_id: channelId, + self_mute: !!options.mute, + self_deaf: !!options.deaf, + }, + }); }); } } diff --git a/lib/structures/voice/UDPSocket.d.ts b/lib/structures/voice/UDPSocket.d.ts index 9891f3748..87a132d77 100644 --- a/lib/structures/voice/UDPSocket.d.ts +++ b/lib/structures/voice/UDPSocket.d.ts @@ -1,8 +1,8 @@ /// import { Socket } from 'dgram'; import { EventEmitter } from 'events'; -import { Readable } from 'stream'; import { Connection } from './Connection'; +import { VoiceStream } from './VoiceStream'; export declare class UDPSocket extends EventEmitter { connection: Connection; socket: Socket; @@ -12,11 +12,11 @@ export declare class UDPSocket extends EventEmitter { /** * PCM Raw */ - PCMOut: Readable; + PCMOut: VoiceStream; /** * Opus Encoded */ - OpusOut: Readable; + OpusOut: VoiceStream; private OpusEncoder; constructor(connection: Connection); discoverIP(server: { diff --git a/lib/structures/voice/UDPSocket.js b/lib/structures/voice/UDPSocket.js index af7613c25..c34742e9f 100644 --- a/lib/structures/voice/UDPSocket.js +++ b/lib/structures/voice/UDPSocket.js @@ -1,18 +1,9 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.UDPSocket = void 0; const dgram_1 = require("dgram"); const events_1 = require("events"); -const stream_1 = require("stream"); +const VoiceStream_1 = require("./VoiceStream"); const socket_1 = require("../../socket"); let LibSodium; let OpusScript; @@ -31,30 +22,28 @@ class UDPSocket extends events_1.EventEmitter { /** * PCM Raw */ - this.PCMOut = new stream_1.Readable(); + this.PCMOut = new VoiceStream_1.VoiceStream(); /** * Opus Encoded */ - this.OpusOut = new stream_1.Readable(); + this.OpusOut = new VoiceStream_1.VoiceStream(); this.connection = connection; this.socket = dgram_1.createSocket('udp4'); } // This method should be called before the udp connection started! - discoverIP(server) { - return __awaiter(this, void 0, void 0, function* () { - this.auth = server; - return new Promise(resolve => { - const message = Buffer.alloc(70); - message.writeUIntBE(this.auth.ssrc, 0, 4); - this.socket.send(message, 0, message.length, this.auth.port, this.auth.ip); - this.socket.once('message', message => { - const local = { ip: '', port: 0 }; - for (let i = 4; i < message.indexOf(0, i); i++) { - local.ip += String.fromCharCode(message[i]); - } - local.port = parseInt(message.readUIntBE(message.length - 2, 2).toString(10)); - resolve(local); - }); + async discoverIP(server) { + this.auth = server; + return new Promise(resolve => { + const message = Buffer.alloc(70); + message.writeUIntBE(this.auth.ssrc, 0, 4); + this.socket.send(message, 0, message.length, this.auth.port, this.auth.ip); + this.socket.once('message', message => { + const local = { ip: '', port: 0 }; + for (let i = 4; i < message.indexOf(0, i); i++) { + local.ip += String.fromCharCode(message[i]); + } + local.port = parseInt(message.readUIntBE(message.length - 2, 2).toString(10)); + resolve(local); }); }); } diff --git a/lib/structures/voice/VoiceStream.d.ts b/lib/structures/voice/VoiceStream.d.ts new file mode 100644 index 000000000..924c04135 --- /dev/null +++ b/lib/structures/voice/VoiceStream.d.ts @@ -0,0 +1,21 @@ +/// +import { Readable } from 'stream'; +import TypedEventEmitter from 'typed-emitter'; +declare const VoiceStream_base: new () => TypedEventEmitter & Readable; +export declare class VoiceStream extends VoiceStream_base { + private source; + constructor(); + _read(size: number): Buffer; + push(data: Buffer): boolean; +} +interface VoiceStreamEvents { + data: [Buffer]; + readable: []; + end: []; + close: []; + drain: []; + resume: []; + pause: []; + error: [Error]; +} +export {}; diff --git a/lib/structures/voice/VoiceStream.js b/lib/structures/voice/VoiceStream.js new file mode 100644 index 000000000..94e693d31 --- /dev/null +++ b/lib/structures/voice/VoiceStream.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoiceStream = void 0; +const stream_1 = require("stream"); +class VoiceStream extends stream_1.Readable { + constructor() { + super(); + this.source = Buffer.alloc(1, 0); + } + _read(size) { + return Buffer.from(this.source.slice(this.source.length - size)); + } + push(data) { + const res = super.push(data); + this.source = Buffer.concat([this.source, data]); + return res; + } +} +exports.VoiceStream = VoiceStream; diff --git a/lib/structures/voice/VoiceWebSocket.js b/lib/structures/voice/VoiceWebSocket.js index 7508842b3..ddca3a156 100644 --- a/lib/structures/voice/VoiceWebSocket.js +++ b/lib/structures/voice/VoiceWebSocket.js @@ -1,13 +1,4 @@ "use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -51,40 +42,38 @@ class VoiceWebSocket extends events_1.EventEmitter { }, }); } - onMessage(message) { - return __awaiter(this, void 0, void 0, function* () { - this.sequnce = message.s; - switch (message.op) { - case VOICE_OPCODES.HELLO: { - this.hearbeat.interval.timeout = message.d.heartbeat_interval; - this.hearbeat.start(); - break; - } - case VOICE_OPCODES.READY: { - const ip = yield this.connection.sockets.udp.discoverIP({ - ip: message.d.ip, - port: message.d.port, - ssrc: message.d.ssrc, - }); - this.send({ - op: VOICE_OPCODES.SELECT_PROTOCOL, - d: { - protocol: 'udp', - data: { - address: ip.ip, - port: ip.port, - mode: 'xsalsa20_poly1305', - }, + async onMessage(message) { + this.sequnce = message.s; + switch (message.op) { + case VOICE_OPCODES.HELLO: { + this.hearbeat.interval.timeout = message.d.heartbeat_interval; + this.hearbeat.start(); + break; + } + case VOICE_OPCODES.READY: { + const ip = await this.connection.sockets.udp.discoverIP({ + ip: message.d.ip, + port: message.d.port, + ssrc: message.d.ssrc, + }); + this.send({ + op: VOICE_OPCODES.SELECT_PROTOCOL, + d: { + protocol: 'udp', + data: { + address: ip.ip, + port: ip.port, + mode: 'xsalsa20_poly1305', }, - }); - break; - } - case VOICE_OPCODES.SESSION_DESCRIPTION: { - this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys); - break; - } + }, + }); + break; } - }); + case VOICE_OPCODES.SESSION_DESCRIPTION: { + this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys); + break; + } + } } send(data) { if (this.ws) From 5232c7e2c77ad290c3afa278d39fb2ac3c396a01 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 08:13:43 +0300 Subject: [PATCH 34/48] refactor(MuteState): rename --- src/structures/flags/MuteFlags.ts | 10 +++------- src/structures/voice/VoiceState.ts | 10 +++++----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/structures/flags/MuteFlags.ts b/src/structures/flags/MuteFlags.ts index 02961e443..c37e8b1d7 100644 --- a/src/structures/flags/MuteFlags.ts +++ b/src/structures/flags/MuteFlags.ts @@ -1,13 +1,9 @@ import { Flags } from './Flags'; -export enum MUTE_STATE { +export enum MuteState { SELF = 1 << 2, FORCE = 1 << 1, + NONE = 1 << 0, } -export class MuteFlags extends Flags { - // Mute flags are the states of mutes and deafens - constructor(flags: number) { - super(flags); - } -} +export class MuteFlags extends Flags {} diff --git a/src/structures/voice/VoiceState.ts b/src/structures/voice/VoiceState.ts index 72e129085..c70c3e5c9 100644 --- a/src/structures/voice/VoiceState.ts +++ b/src/structures/voice/VoiceState.ts @@ -2,7 +2,7 @@ import { Bot } from '../../bot'; import { Nullable, Snowflake } from '../../types'; import { BaseStruct, GatewayStruct } from '../base'; import { GuildVoiceChannel } from '../channels/GuildVoiceChannel'; -import { MuteFlags, MUTE_STATE } from '../flags/MuteFlags'; +import { MuteFlags, MuteState } from '../flags/MuteFlags'; import { Member } from '../member'; interface VoiceStateData { @@ -35,11 +35,11 @@ export class VoiceState extends BaseStruct { } public init(voiceState: VoiceStateData): this { - if (voiceState.self_mute) this.muted = new MuteFlags(MUTE_STATE.SELF); - if (voiceState.mute) this.muted = new MuteFlags(MUTE_STATE.FORCE); + if (voiceState.self_mute) this.muted = new MuteFlags(MuteState.SELF); + if (voiceState.mute) this.muted = new MuteFlags(MuteState.FORCE); - if (voiceState.self_deaf) new MuteFlags(MUTE_STATE.SELF); - if (voiceState.deaf) this.deafen = new MuteFlags(MUTE_STATE.FORCE); + if (voiceState.self_deaf) new MuteFlags(MuteState.SELF); + if (voiceState.deaf) this.deafen = new MuteFlags(MuteState.FORCE); this.channelId = voiceState.channel_id; From 0d53672efccaff7aa4e5020b0b356c8a5694b556 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 08:15:00 +0300 Subject: [PATCH 35/48] feat(Readable): added Readable instead of VoiceStream --- src/structures/voice/Connection.ts | 9 +++++++++ src/structures/voice/GuildVoice.ts | 14 +++++++++++++- src/structures/voice/Readable.ts | 5 +++++ src/structures/voice/UDPSocket.ts | 6 +++--- 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/structures/voice/Readable.ts diff --git a/src/structures/voice/Connection.ts b/src/structures/voice/Connection.ts index 50b949ec3..cd888f831 100644 --- a/src/structures/voice/Connection.ts +++ b/src/structures/voice/Connection.ts @@ -1,4 +1,5 @@ import { GuildVoice } from './GuildVoice'; +import { Readable } from './Readable'; import { UDPSocket } from './UDPSocket'; import { VoiceWebSocket } from './VoiceWebSocket'; @@ -47,6 +48,14 @@ export class Connection { return this._endpoint; } + public get PCMOut(): Readable { + return this.sockets.udp.PCMOut; + } + + public get OpusOut(): Readable { + return this.sockets.udp.OpusOut; + } + public voice: GuildVoice; constructor(voice: GuildVoice) { diff --git a/src/structures/voice/GuildVoice.ts b/src/structures/voice/GuildVoice.ts index df5959024..63fd66ba0 100644 --- a/src/structures/voice/GuildVoice.ts +++ b/src/structures/voice/GuildVoice.ts @@ -3,6 +3,7 @@ import { Guild } from '..'; import { Bot } from '../../bot'; import { BotEvent } from '../../socket'; import { Snowflake } from '../../types'; +import { MuteFlags } from '../flags/MuteFlags'; /** * Represents a voice connection of a guild @@ -27,7 +28,18 @@ export class GuildVoice { this.bot = guild.bot; } - join(channelId: Snowflake, options: { mute?: boolean; deaf?: boolean }): Promise { + get deaf(): MuteFlags { + return this.guild.voiceStates.get(this.bot.user!.id)!.deafen; + } + + get mute(): MuteFlags { + return this.guild.voiceStates.get(this.bot.user!.id)!.muted; + } + + join( + channelId: Snowflake | null, + options: { mute?: boolean; deaf?: boolean }, + ): Promise { return new Promise(resolve => { const listener = ((guild: Guild, voiceServer: { token: string; endpoint: string }) => { if (guild.id === this.guild.id) { diff --git a/src/structures/voice/Readable.ts b/src/structures/voice/Readable.ts new file mode 100644 index 000000000..d348222ae --- /dev/null +++ b/src/structures/voice/Readable.ts @@ -0,0 +1,5 @@ +import { Readable as _Readable } from 'stream'; + +export class Readable extends _Readable { + _read() {} //eslint-disable-line +} diff --git a/src/structures/voice/UDPSocket.ts b/src/structures/voice/UDPSocket.ts index fb08f3a17..82f583145 100644 --- a/src/structures/voice/UDPSocket.ts +++ b/src/structures/voice/UDPSocket.ts @@ -1,7 +1,7 @@ import { createSocket, Socket } from 'dgram'; import { EventEmitter } from 'events'; import { Connection } from './Connection'; -import { VoiceStream } from './VoiceStream'; +import { Readable } from './Readable'; import { BotEvent } from '../../socket'; let LibSodium: typeof import('sodium-native'); @@ -29,12 +29,12 @@ export class UDPSocket extends EventEmitter { /** * PCM Raw */ - public PCMOut = new VoiceStream(); + public PCMOut = new Readable(); /** * Opus Encoded */ - public OpusOut = new VoiceStream(); + public OpusOut = new Readable(); private OpusEncoder!: import('opusscript').OpusScript; From f76b9032a94e6201f26e357f6a731e4b68d61c94 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 08:15:53 +0300 Subject: [PATCH 36/48] revert(VoiceStream): removed --- src/structures/voice/VoiceStream.ts | 33 ----------------------------- 1 file changed, 33 deletions(-) delete mode 100644 src/structures/voice/VoiceStream.ts diff --git a/src/structures/voice/VoiceStream.ts b/src/structures/voice/VoiceStream.ts deleted file mode 100644 index 067702414..000000000 --- a/src/structures/voice/VoiceStream.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Readable } from 'stream'; -import TypedEventEmitter from 'typed-emitter'; - -export class VoiceStream extends (Readable as new () => TypedEventEmitter & Readable) { //eslint-disable-line prettier/prettier - private source: Buffer; - - constructor() { - super(); - - this.source = Buffer.alloc(1, 0); - } - - _read(size: number): Buffer { - return Buffer.from(this.source.slice(this.source.length - size)); - } - - push(data: Buffer): boolean { - const res = super.push(data); - this.source = Buffer.concat([this.source, data]); - return res; - } -} - -interface VoiceStreamEvents { - data: [Buffer]; - readable: []; - end: []; - close: []; - drain: []; - resume: []; - pause: []; - error: [Error]; -} From d26534880bd87edd08f47ab951c14c69c902e019 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 20:55:28 +0300 Subject: [PATCH 37/48] fix(VoiceWs): fixed endpoint being undefined and sending IDENTIFY before HELLO --- src/structures/voice/Connection.ts | 4 ++++ src/structures/voice/VoiceWebSocket.ts | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/structures/voice/Connection.ts b/src/structures/voice/Connection.ts index cd888f831..0774ebfd8 100644 --- a/src/structures/voice/Connection.ts +++ b/src/structures/voice/Connection.ts @@ -26,6 +26,8 @@ export class Connection { public set endpoint(val: string) { if (this._endpoint === val) return; else { + this._endpoint = val; + if (this.sockets.ws) { this.sockets.ws.close(); this.sockets.ws.removeAllListeners(); @@ -41,6 +43,8 @@ export class Connection { ws: new VoiceWebSocket(this), udp: new UDPSocket(this), }; + + this.sockets.ws.open(); } } diff --git a/src/structures/voice/VoiceWebSocket.ts b/src/structures/voice/VoiceWebSocket.ts index c13bbdfeb..debdf64b5 100644 --- a/src/structures/voice/VoiceWebSocket.ts +++ b/src/structures/voice/VoiceWebSocket.ts @@ -58,24 +58,26 @@ export class VoiceWebSocket extends EventEmitter { this.ws.on('message', this.onMessage.bind(this)); this.hearbeat = new VoiceHeartbeats(this); - - this.send({ - op: VOICE_OPCODES.IDENTIFY, - d: { - server_id: this.connection.voice.guild.id, - user_id: this.connection.voice.bot.user!.id, - session_id: this.connection.voice.guild.shard.sessionId, - token: this.connection.token, - }, - }); } private async onMessage(message: VoicePayload) { this.sequnce = message.s; + + this.connection.voice.bot.debug(message.op, VOICE_OPCODES[message.op], 'op - t'); + switch (message.op) { case VOICE_OPCODES.HELLO: { this.hearbeat!.interval.timeout = message.d.heartbeat_interval; this.hearbeat!.start(); + this.send({ + op: VOICE_OPCODES.IDENTIFY, + d: { + server_id: this.connection.voice.guild.id, + user_id: this.connection.voice.bot.user!.id, + session_id: this.connection.voice.guild.shard.sessionId, + token: this.connection.token, + }, + }); break; } @@ -102,6 +104,7 @@ export class VoiceWebSocket extends EventEmitter { case VOICE_OPCODES.SESSION_DESCRIPTION: { this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys as number[]); + this.connection.sockets.udp.start(); break; } } From 83542763a254d0e766189e5eb491e5102ecb6237 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 20:59:03 +0300 Subject: [PATCH 38/48] fix(VoiceWs): not parsing the incoming message --- src/structures/voice/VoiceWebSocket.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/structures/voice/VoiceWebSocket.ts b/src/structures/voice/VoiceWebSocket.ts index debdf64b5..91496d0b1 100644 --- a/src/structures/voice/VoiceWebSocket.ts +++ b/src/structures/voice/VoiceWebSocket.ts @@ -60,7 +60,9 @@ export class VoiceWebSocket extends EventEmitter { this.hearbeat = new VoiceHeartbeats(this); } - private async onMessage(message: VoicePayload) { + private async onMessage(data: string) { + const message: VoicePayload = JSON.parse(data); + this.sequnce = message.s; this.connection.voice.bot.debug(message.op, VOICE_OPCODES[message.op], 'op - t'); From 22cb59d2b9daf842f64a5872fa11abaef83f9d09 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 21:03:22 +0300 Subject: [PATCH 39/48] fix(VoiceWs): fixed sending incorrect sessionID --- src/structures/voice/VoiceWebSocket.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/structures/voice/VoiceWebSocket.ts b/src/structures/voice/VoiceWebSocket.ts index 91496d0b1..f9a4c9267 100644 --- a/src/structures/voice/VoiceWebSocket.ts +++ b/src/structures/voice/VoiceWebSocket.ts @@ -76,7 +76,9 @@ export class VoiceWebSocket extends EventEmitter { d: { server_id: this.connection.voice.guild.id, user_id: this.connection.voice.bot.user!.id, - session_id: this.connection.voice.guild.shard.sessionId, + session_id: this.connection.voice.guild.voiceStates.get( + this.connection.voice.bot.user!.id, + )?.sessionId, token: this.connection.token, }, }); From 03d39c5493fe56254f475160e599a94096f2e01b Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 21:13:36 +0300 Subject: [PATCH 40/48] ci(Compile): changed npm script The reason behind this is local testing. When you try to run npm build, it will give you a dist folder, but if you run ci:build it will give you a lib folder --- .github/workflows/compile.yml | 2 +- .github/workflows/test.yml | 2 +- .gitignore | 1 + .npmignore | 3 ++- package.json | 1 + tsconfig.json | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 1129516ba..609d1d130 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -19,7 +19,7 @@ jobs: run: npm ci - name: Build package - run: npm run build + run: npm run ci:build - name: Commit uses: EndBug/add-and-commit@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea092d608..36b900a44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: run: npm ci - name: Build package - run: npm run build + run: npm run ci:build jest: name: Jest diff --git a/.gitignore b/.gitignore index b98c7cc9d..27aca786b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ tests/*.gif # Compiled output /lib +/dist # Test bot credentials tests/config.json diff --git a/.npmignore b/.npmignore index 40a35d314..2992de368 100644 --- a/.npmignore +++ b/.npmignore @@ -7,4 +7,5 @@ tests .prettierrc jest.config.js typedoc.json -tsconfig.json \ No newline at end of file +tsconfig.json +dist \ No newline at end of file diff --git a/package.json b/package.json index 3583d96ad..57b176035 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "scripts": { "start": "ts-node src/index.ts", "build": "tsc", + "ci:build": "tsc --outDir ./lib", "test": "jest", "lint": "eslint src --ext .ts --ext .js", "lint:fix": "eslint --fix src --ext .ts --ext .js", diff --git a/tsconfig.json b/tsconfig.json index 08048b07a..47b90da72 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "module": "commonjs", "target": "es2019", "declaration": true, - "outDir": "./lib", + "outDir": "./dist", "esModuleInterop": true, "resolveJsonModule": true, "strict": true, From d8959a97ccae88f0ab4b17620b3f6011d07d0893 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 21:17:46 +0300 Subject: [PATCH 41/48] fix(VoiceWs): a typo of a property name --- src/structures/voice/VoiceWebSocket.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/voice/VoiceWebSocket.ts b/src/structures/voice/VoiceWebSocket.ts index f9a4c9267..6ffe8c16e 100644 --- a/src/structures/voice/VoiceWebSocket.ts +++ b/src/structures/voice/VoiceWebSocket.ts @@ -107,7 +107,7 @@ export class VoiceWebSocket extends EventEmitter { } case VOICE_OPCODES.SESSION_DESCRIPTION: { - this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys as number[]); + this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_key as number[]); this.connection.sockets.udp.start(); break; } From c484cce47227d278fc2128f39aba86f3ebc29d42 Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 21:22:24 +0300 Subject: [PATCH 42/48] fix(UdpSocket): fixed stoping process on error by handling it --- src/structures/voice/UDPSocket.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/structures/voice/UDPSocket.ts b/src/structures/voice/UDPSocket.ts index 82f583145..ac1d0e223 100644 --- a/src/structures/voice/UDPSocket.ts +++ b/src/structures/voice/UDPSocket.ts @@ -2,8 +2,6 @@ import { createSocket, Socket } from 'dgram'; import { EventEmitter } from 'events'; import { Connection } from './Connection'; import { Readable } from './Readable'; -import { BotEvent } from '../../socket'; - let LibSodium: typeof import('sodium-native'); let OpusScript: typeof import('opusscript').OpusScript; @@ -12,7 +10,7 @@ try { } catch (err) {} //eslint-disable-line try { - OpusScript = require("opusscript").OpusScript //eslint-disable-line + OpusScript = require("opusscript") //eslint-disable-line } catch (err) {} //eslint-disable-line export class UDPSocket extends EventEmitter { @@ -91,11 +89,14 @@ export class UDPSocket extends EventEmitter { this.socket.on('message', message => { const data = this.decryptPackage(message); - if (data instanceof Error) return this.connection.voice.bot.events.emit(BotEvent.Debug, data); - - this.OpusOut.push(data); + if (data instanceof Error) return this.connection.voice.bot.debug('Error:', data); - this.PCMOut.push(this.OpusEncoder.decode(data)); + try { + this.PCMOut.push(this.OpusEncoder.decode(data)); + this.OpusOut.push(data); + } catch (error) { + this.connection.voice.bot.debug('Error: ' + error); + } }); } From 5ff26bb504c64a4be5001d96910900238d9e6f5c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 18:25:31 +0000 Subject: [PATCH 43/48] ci(Compile): compiled latest push --- lib/structures/flags/MuteFlags.d.ts | 8 ++++---- lib/structures/flags/MuteFlags.js | 17 +++++++---------- lib/structures/voice/Connection.d.ts | 3 +++ lib/structures/voice/Connection.js | 8 ++++++++ lib/structures/voice/GuildVoice.d.ts | 5 ++++- lib/structures/voice/GuildVoice.js | 6 ++++++ lib/structures/voice/Readable.d.ts | 5 +++++ lib/structures/voice/Readable.js | 8 ++++++++ lib/structures/voice/UDPSocket.d.ts | 6 +++--- lib/structures/voice/UDPSocket.js | 20 ++++++++++++-------- lib/structures/voice/VoiceState.js | 8 ++++---- lib/structures/voice/VoiceWebSocket.js | 26 +++++++++++++++----------- 12 files changed, 79 insertions(+), 41 deletions(-) create mode 100644 lib/structures/voice/Readable.d.ts create mode 100644 lib/structures/voice/Readable.js diff --git a/lib/structures/flags/MuteFlags.d.ts b/lib/structures/flags/MuteFlags.d.ts index 35d6f8db4..4dfcd959a 100644 --- a/lib/structures/flags/MuteFlags.d.ts +++ b/lib/structures/flags/MuteFlags.d.ts @@ -1,8 +1,8 @@ import { Flags } from './Flags'; -export declare enum MUTE_STATE { +export declare enum MuteState { SELF = 4, - FORCE = 2 + FORCE = 2, + NONE = 1 } -export declare class MuteFlags extends Flags { - constructor(flags: number); +export declare class MuteFlags extends Flags { } diff --git a/lib/structures/flags/MuteFlags.js b/lib/structures/flags/MuteFlags.js index 881899401..198941241 100644 --- a/lib/structures/flags/MuteFlags.js +++ b/lib/structures/flags/MuteFlags.js @@ -1,16 +1,13 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.MuteFlags = exports.MUTE_STATE = void 0; +exports.MuteFlags = exports.MuteState = void 0; const Flags_1 = require("./Flags"); -var MUTE_STATE; -(function (MUTE_STATE) { - MUTE_STATE[MUTE_STATE["SELF"] = 4] = "SELF"; - MUTE_STATE[MUTE_STATE["FORCE"] = 2] = "FORCE"; -})(MUTE_STATE = exports.MUTE_STATE || (exports.MUTE_STATE = {})); +var MuteState; +(function (MuteState) { + MuteState[MuteState["SELF"] = 4] = "SELF"; + MuteState[MuteState["FORCE"] = 2] = "FORCE"; + MuteState[MuteState["NONE"] = 1] = "NONE"; +})(MuteState = exports.MuteState || (exports.MuteState = {})); class MuteFlags extends Flags_1.Flags { - // Mute flags are the states of mutes and deafens - constructor(flags) { - super(flags); - } } exports.MuteFlags = MuteFlags; diff --git a/lib/structures/voice/Connection.d.ts b/lib/structures/voice/Connection.d.ts index bf9f2ae1e..1460c5ddd 100644 --- a/lib/structures/voice/Connection.d.ts +++ b/lib/structures/voice/Connection.d.ts @@ -1,4 +1,5 @@ import { GuildVoice } from './GuildVoice'; +import { Readable } from './Readable'; import { UDPSocket } from './UDPSocket'; import { VoiceWebSocket } from './VoiceWebSocket'; export declare class Connection { @@ -15,6 +16,8 @@ export declare class Connection { */ set endpoint(val: string); get endpoint(): string; + get PCMOut(): Readable; + get OpusOut(): Readable; voice: GuildVoice; constructor(voice: GuildVoice); } diff --git a/lib/structures/voice/Connection.js b/lib/structures/voice/Connection.js index 4f69c54a3..1446a788d 100644 --- a/lib/structures/voice/Connection.js +++ b/lib/structures/voice/Connection.js @@ -18,6 +18,7 @@ class Connection { if (this._endpoint === val) return; else { + this._endpoint = val; if (this.sockets.ws) { this.sockets.ws.close(); this.sockets.ws.removeAllListeners(); @@ -31,10 +32,17 @@ class Connection { ws: new VoiceWebSocket_1.VoiceWebSocket(this), udp: new UDPSocket_1.UDPSocket(this), }; + this.sockets.ws.open(); } } get endpoint() { return this._endpoint; } + get PCMOut() { + return this.sockets.udp.PCMOut; + } + get OpusOut() { + return this.sockets.udp.OpusOut; + } } exports.Connection = Connection; diff --git a/lib/structures/voice/GuildVoice.d.ts b/lib/structures/voice/GuildVoice.d.ts index 0ef7cef9c..890cb17ec 100644 --- a/lib/structures/voice/GuildVoice.d.ts +++ b/lib/structures/voice/GuildVoice.d.ts @@ -2,6 +2,7 @@ import { Connection } from './Connection'; import { Guild } from '..'; import { Bot } from '../../bot'; import { Snowflake } from '../../types'; +import { MuteFlags } from '../flags/MuteFlags'; /** * Represents a voice connection of a guild */ @@ -17,7 +18,9 @@ export declare class GuildVoice { guild: Guild; connection: Connection; constructor(guild: Guild); - join(channelId: Snowflake, options: { + get deaf(): MuteFlags; + get mute(): MuteFlags; + join(channelId: Snowflake | null, options: { mute?: boolean; deaf?: boolean; }): Promise; diff --git a/lib/structures/voice/GuildVoice.js b/lib/structures/voice/GuildVoice.js index b9ac1c272..9dba8f647 100644 --- a/lib/structures/voice/GuildVoice.js +++ b/lib/structures/voice/GuildVoice.js @@ -12,6 +12,12 @@ class GuildVoice { this.guild = guild; this.bot = guild.bot; } + get deaf() { + return this.guild.voiceStates.get(this.bot.user.id).deafen; + } + get mute() { + return this.guild.voiceStates.get(this.bot.user.id).muted; + } join(channelId, options) { return new Promise(resolve => { const listener = ((guild, voiceServer) => { diff --git a/lib/structures/voice/Readable.d.ts b/lib/structures/voice/Readable.d.ts new file mode 100644 index 000000000..7fdf8cadc --- /dev/null +++ b/lib/structures/voice/Readable.d.ts @@ -0,0 +1,5 @@ +/// +import { Readable as _Readable } from 'stream'; +export declare class Readable extends _Readable { + _read(): void; +} diff --git a/lib/structures/voice/Readable.js b/lib/structures/voice/Readable.js new file mode 100644 index 000000000..75e7a7d79 --- /dev/null +++ b/lib/structures/voice/Readable.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Readable = void 0; +const stream_1 = require("stream"); +class Readable extends stream_1.Readable { + _read() { } //eslint-disable-line +} +exports.Readable = Readable; diff --git a/lib/structures/voice/UDPSocket.d.ts b/lib/structures/voice/UDPSocket.d.ts index 87a132d77..abd8f31e5 100644 --- a/lib/structures/voice/UDPSocket.d.ts +++ b/lib/structures/voice/UDPSocket.d.ts @@ -2,7 +2,7 @@ import { Socket } from 'dgram'; import { EventEmitter } from 'events'; import { Connection } from './Connection'; -import { VoiceStream } from './VoiceStream'; +import { Readable } from './Readable'; export declare class UDPSocket extends EventEmitter { connection: Connection; socket: Socket; @@ -12,11 +12,11 @@ export declare class UDPSocket extends EventEmitter { /** * PCM Raw */ - PCMOut: VoiceStream; + PCMOut: Readable; /** * Opus Encoded */ - OpusOut: VoiceStream; + OpusOut: Readable; private OpusEncoder; constructor(connection: Connection); discoverIP(server: { diff --git a/lib/structures/voice/UDPSocket.js b/lib/structures/voice/UDPSocket.js index c34742e9f..ac4b506eb 100644 --- a/lib/structures/voice/UDPSocket.js +++ b/lib/structures/voice/UDPSocket.js @@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.UDPSocket = void 0; const dgram_1 = require("dgram"); const events_1 = require("events"); -const VoiceStream_1 = require("./VoiceStream"); -const socket_1 = require("../../socket"); +const Readable_1 = require("./Readable"); let LibSodium; let OpusScript; try { @@ -12,7 +11,7 @@ try { } catch (err) { } //eslint-disable-line try { - OpusScript = require("opusscript").OpusScript; //eslint-disable-line + OpusScript = require("opusscript"); //eslint-disable-line } catch (err) { } //eslint-disable-line class UDPSocket extends events_1.EventEmitter { @@ -22,11 +21,11 @@ class UDPSocket extends events_1.EventEmitter { /** * PCM Raw */ - this.PCMOut = new VoiceStream_1.VoiceStream(); + this.PCMOut = new Readable_1.Readable(); /** * Opus Encoded */ - this.OpusOut = new VoiceStream_1.VoiceStream(); + this.OpusOut = new Readable_1.Readable(); this.connection = connection; this.socket = dgram_1.createSocket('udp4'); } @@ -59,9 +58,14 @@ class UDPSocket extends events_1.EventEmitter { this.socket.on('message', message => { const data = this.decryptPackage(message); if (data instanceof Error) - return this.connection.voice.bot.events.emit(socket_1.BotEvent.Debug, data); - this.OpusOut.push(data); - this.PCMOut.push(this.OpusEncoder.decode(data)); + return this.connection.voice.bot.debug('Error:', data); + try { + this.PCMOut.push(this.OpusEncoder.decode(data)); + this.OpusOut.push(data); + } + catch (error) { + this.connection.voice.bot.debug('Error: ' + error); + } }); } stop() { diff --git a/lib/structures/voice/VoiceState.js b/lib/structures/voice/VoiceState.js index f925780c2..7990a2847 100644 --- a/lib/structures/voice/VoiceState.js +++ b/lib/structures/voice/VoiceState.js @@ -11,13 +11,13 @@ class VoiceState extends base_1.BaseStruct { } init(voiceState) { if (voiceState.self_mute) - this.muted = new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.SELF); + this.muted = new MuteFlags_1.MuteFlags(MuteFlags_1.MuteState.SELF); if (voiceState.mute) - this.muted = new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.FORCE); + this.muted = new MuteFlags_1.MuteFlags(MuteFlags_1.MuteState.FORCE); if (voiceState.self_deaf) - new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.SELF); + new MuteFlags_1.MuteFlags(MuteFlags_1.MuteState.SELF); if (voiceState.deaf) - this.deafen = new MuteFlags_1.MuteFlags(MuteFlags_1.MUTE_STATE.FORCE); + this.deafen = new MuteFlags_1.MuteFlags(MuteFlags_1.MuteState.FORCE); this.channelId = voiceState.channel_id; this.sessionId = voiceState.session_id; return this; diff --git a/lib/structures/voice/VoiceWebSocket.js b/lib/structures/voice/VoiceWebSocket.js index ddca3a156..34210f34f 100644 --- a/lib/structures/voice/VoiceWebSocket.js +++ b/lib/structures/voice/VoiceWebSocket.js @@ -32,22 +32,25 @@ class VoiceWebSocket extends events_1.EventEmitter { this.ws = new ws_1.default(`wss://${this.connection.endpoint.split(':')[0]}/?v=4`); this.ws.on('message', this.onMessage.bind(this)); this.hearbeat = new VoiceHeartbeats_1.VoiceHeartbeats(this); - this.send({ - op: VOICE_OPCODES.IDENTIFY, - d: { - server_id: this.connection.voice.guild.id, - user_id: this.connection.voice.bot.user.id, - session_id: this.connection.voice.guild.shard.sessionId, - token: this.connection.token, - }, - }); } - async onMessage(message) { + async onMessage(data) { + var _a; + const message = JSON.parse(data); this.sequnce = message.s; + this.connection.voice.bot.debug(message.op, VOICE_OPCODES[message.op], 'op - t'); switch (message.op) { case VOICE_OPCODES.HELLO: { this.hearbeat.interval.timeout = message.d.heartbeat_interval; this.hearbeat.start(); + this.send({ + op: VOICE_OPCODES.IDENTIFY, + d: { + server_id: this.connection.voice.guild.id, + user_id: this.connection.voice.bot.user.id, + session_id: (_a = this.connection.voice.guild.voiceStates.get(this.connection.voice.bot.user.id)) === null || _a === void 0 ? void 0 : _a.sessionId, + token: this.connection.token, + }, + }); break; } case VOICE_OPCODES.READY: { @@ -70,7 +73,8 @@ class VoiceWebSocket extends events_1.EventEmitter { break; } case VOICE_OPCODES.SESSION_DESCRIPTION: { - this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_keys); + this.connection.sockets.udp.secretKeys = Buffer.from(message.d.secret_key); + this.connection.sockets.udp.start(); break; } } From 30de755b60b7669a2c1faa13e6d6fc848316bc2d Mon Sep 17 00:00:00 2001 From: Can Bora Ciner Date: Wed, 2 Sep 2020 21:49:56 +0300 Subject: [PATCH 44/48] chore(Tsconfig): remove unneccessary libs and add 'jest' as a type --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 47b90da72..b8d35eb93 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,8 +7,8 @@ "esModuleInterop": true, "resolveJsonModule": true, "strict": true, - "lib": ["es2019", "es2020.bigint", "es2020.string", "es2020.symbol.wellknown"], - "types": ["node"] + "lib": ["es2019"], + "types": ["node", "jest"] }, "include": [ "src" From 94c8a381eb5893760973d34de7add39c1f6b3050 Mon Sep 17 00:00:00 2001 From: Sardonyx Date: Wed, 2 Sep 2020 21:57:09 +0300 Subject: [PATCH 45/48] ci(Compile): remove old release to save space --- .github/workflows/compile.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 609d1d130..680675f7f 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -18,16 +18,19 @@ jobs: - name: Install dependencies run: npm ci + - name: Remove the old release + run: rm -rf ./lib + - name: Build package run: npm run ci:build - + - name: Commit uses: EndBug/add-and-commit@v4 with: add: 'lib' force: true - message: 'ci(Compile): compiled latest push' + message: 'ci(Compile): Compile the project @ $GITHUB_SHA' author_email: 41898282+github-actions[bot]@users.noreply.github.com author_name: github-actions[bot] env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d47dbc2c2ed7fc9db64ac69adc7c8e1e37dd0637 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 18:57:44 +0000 Subject: [PATCH 46/48] ci(Compile): Compile the project @ $GITHUB_SHA --- lib/structures/voice/VoiceStream.d.ts | 21 --------------------- lib/structures/voice/VoiceStream.js | 19 ------------------- 2 files changed, 40 deletions(-) delete mode 100644 lib/structures/voice/VoiceStream.d.ts delete mode 100644 lib/structures/voice/VoiceStream.js diff --git a/lib/structures/voice/VoiceStream.d.ts b/lib/structures/voice/VoiceStream.d.ts deleted file mode 100644 index 924c04135..000000000 --- a/lib/structures/voice/VoiceStream.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -/// -import { Readable } from 'stream'; -import TypedEventEmitter from 'typed-emitter'; -declare const VoiceStream_base: new () => TypedEventEmitter & Readable; -export declare class VoiceStream extends VoiceStream_base { - private source; - constructor(); - _read(size: number): Buffer; - push(data: Buffer): boolean; -} -interface VoiceStreamEvents { - data: [Buffer]; - readable: []; - end: []; - close: []; - drain: []; - resume: []; - pause: []; - error: [Error]; -} -export {}; diff --git a/lib/structures/voice/VoiceStream.js b/lib/structures/voice/VoiceStream.js deleted file mode 100644 index 94e693d31..000000000 --- a/lib/structures/voice/VoiceStream.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VoiceStream = void 0; -const stream_1 = require("stream"); -class VoiceStream extends stream_1.Readable { - constructor() { - super(); - this.source = Buffer.alloc(1, 0); - } - _read(size) { - return Buffer.from(this.source.slice(this.source.length - size)); - } - push(data) { - const res = super.push(data); - this.source = Buffer.concat([this.source, data]); - return res; - } -} -exports.VoiceStream = VoiceStream; From 73ee639a1a9e77698150a5fd8cb08bf8a277db27 Mon Sep 17 00:00:00 2001 From: Sardonyx Date: Thu, 3 Sep 2020 00:22:41 +0300 Subject: [PATCH 47/48] Update compile.yml --- .github/workflows/compile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 680675f7f..88e498c84 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -29,7 +29,7 @@ jobs: with: add: 'lib' force: true - message: 'ci(Compile): Compile the project @ $GITHUB_SHA' + message: 'ci(Compile): Compile the project @ $env:GITHUB_SHA' author_email: 41898282+github-actions[bot]@users.noreply.github.com author_name: github-actions[bot] env: From b6bb49264d37a6569d5a1e6f3785cfedf12da113 Mon Sep 17 00:00:00 2001 From: Sardonyx Date: Thu, 3 Sep 2020 01:14:39 +0300 Subject: [PATCH 48/48] revert(Npm): changed erlpack version back --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57b176035..e0d0afc81 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "ws": "^7.3.1" }, "peerDependencies": { - "erlpack": "git+https://github.com/discord/erlpack.git", + "erlpack": "^0.1.3", "zlib-sync": "^0.1.7", "opusscript": "0.0.7", "sodium-native": "^3.2.0"