summaryrefslogtreecommitdiff
path: root/node_modules/discord.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/discord.js')
-rw-r--r--node_modules/discord.js/.tern-project21
-rw-r--r--node_modules/discord.js/LICENSE190
-rw-r--r--node_modules/discord.js/README.md86
-rw-r--r--node_modules/discord.js/browser.js9
-rw-r--r--node_modules/discord.js/package.json155
-rw-r--r--node_modules/discord.js/src/client/Client.js564
-rw-r--r--node_modules/discord.js/src/client/ClientDataManager.js149
-rw-r--r--node_modules/discord.js/src/client/ClientDataResolver.js376
-rw-r--r--node_modules/discord.js/src/client/ClientManager.js74
-rw-r--r--node_modules/discord.js/src/client/WebhookClient.js118
-rw-r--r--node_modules/discord.js/src/client/actions/Action.js23
-rw-r--r--node_modules/discord.js/src/client/actions/ActionsManager.js43
-rw-r--r--node_modules/discord.js/src/client/actions/ChannelCreate.js11
-rw-r--r--node_modules/discord.js/src/client/actions/ChannelDelete.js38
-rw-r--r--node_modules/discord.js/src/client/actions/ChannelUpdate.js74
-rw-r--r--node_modules/discord.js/src/client/actions/GuildBanRemove.js13
-rw-r--r--node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js19
-rw-r--r--node_modules/discord.js/src/client/actions/GuildDelete.js57
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojiCreate.js17
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojiDelete.js18
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js17
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js38
-rw-r--r--node_modules/discord.js/src/client/actions/GuildMemberGet.js10
-rw-r--r--node_modules/discord.js/src/client/actions/GuildMemberRemove.js41
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRoleCreate.js26
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRoleDelete.js42
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRoleUpdate.js41
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js19
-rw-r--r--node_modules/discord.js/src/client/actions/GuildSync.js29
-rw-r--r--node_modules/discord.js/src/client/actions/GuildUpdate.js34
-rw-r--r--node_modules/discord.js/src/client/actions/InviteCreate.js29
-rw-r--r--node_modules/discord.js/src/client/actions/InviteDelete.js25
-rw-r--r--node_modules/discord.js/src/client/actions/MessageCreate.js53
-rw-r--r--node_modules/discord.js/src/client/actions/MessageDelete.js35
-rw-r--r--node_modules/discord.js/src/client/actions/MessageDeleteBulk.js26
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionAdd.js37
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionRemove.js37
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js25
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionRemoveEmoji.js27
-rw-r--r--node_modules/discord.js/src/client/actions/MessageUpdate.js40
-rw-r--r--node_modules/discord.js/src/client/actions/UserGet.js11
-rw-r--r--node_modules/discord.js/src/client/actions/UserNoteUpdate.js30
-rw-r--r--node_modules/discord.js/src/client/actions/UserUpdate.js33
-rw-r--r--node_modules/discord.js/src/client/rest/APIRequest.js52
-rw-r--r--node_modules/discord.js/src/client/rest/DiscordAPIError.js60
-rw-r--r--node_modules/discord.js/src/client/rest/RESTManager.js58
-rw-r--r--node_modules/discord.js/src/client/rest/RESTMethods.js1067
-rw-r--r--node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js90
-rw-r--r--node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js54
-rw-r--r--node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js132
-rw-r--r--node_modules/discord.js/src/client/rest/UserAgentManager.js25
-rw-r--r--node_modules/discord.js/src/client/voice/ClientVoiceManager.js86
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceBroadcast.js366
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceConnection.js598
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceUDPClient.js127
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceWebSocket.js246
-rw-r--r--node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js331
-rw-r--r--node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js60
-rw-r--r--node_modules/discord.js/src/client/voice/opus/DiscordJsOpusEngine.js34
-rw-r--r--node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js34
-rw-r--r--node_modules/discord.js/src/client/voice/opus/OpusEngineList.js29
-rw-r--r--node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js39
-rw-r--r--node_modules/discord.js/src/client/voice/player/AudioPlayer.js170
-rw-r--r--node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js17
-rw-r--r--node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js220
-rw-r--r--node_modules/discord.js/src/client/voice/util/SecretKey.js16
-rw-r--r--node_modules/discord.js/src/client/voice/util/Secretbox.js33
-rw-r--r--node_modules/discord.js/src/client/voice/util/Silence.js16
-rw-r--r--node_modules/discord.js/src/client/voice/util/SingleSilence.js17
-rw-r--r--node_modules/discord.js/src/client/voice/util/VolumeInterface.js86
-rw-r--r--node_modules/discord.js/src/client/websocket/WebSocketConnection.js509
-rw-r--r--node_modules/discord.js/src/client/websocket/WebSocketManager.js90
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js113
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js17
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js20
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js37
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js23
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js20
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js22
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildIntegrationsUpdate.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js17
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js13
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js18
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js33
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/InviteCreate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/InviteDelete.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js17
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveEmoji.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js76
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js84
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/Resumed.js26
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js68
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js21
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js12
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/UserSettingsUpdate.js18
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js53
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/WebhooksUpdate.js19
-rw-r--r--node_modules/discord.js/src/index.js71
-rw-r--r--node_modules/discord.js/src/sharding/Shard.js282
-rw-r--r--node_modules/discord.js/src/sharding/ShardClientUtil.js146
-rw-r--r--node_modules/discord.js/src/sharding/ShardingManager.js220
-rw-r--r--node_modules/discord.js/src/structures/Attachment.js75
-rw-r--r--node_modules/discord.js/src/structures/CategoryChannel.js22
-rw-r--r--node_modules/discord.js/src/structures/Channel.js78
-rw-r--r--node_modules/discord.js/src/structures/ClientUser.js447
-rw-r--r--node_modules/discord.js/src/structures/ClientUserChannelOverride.js30
-rw-r--r--node_modules/discord.js/src/structures/ClientUserGuildSettings.js60
-rw-r--r--node_modules/discord.js/src/structures/ClientUserSettings.js80
-rw-r--r--node_modules/discord.js/src/structures/DMChannel.js76
-rw-r--r--node_modules/discord.js/src/structures/Emoji.js273
-rw-r--r--node_modules/discord.js/src/structures/GroupDMChannel.js246
-rw-r--r--node_modules/discord.js/src/structures/Guild.js1711
-rw-r--r--node_modules/discord.js/src/structures/GuildAuditLogs.js462
-rw-r--r--node_modules/discord.js/src/structures/GuildChannel.js601
-rw-r--r--node_modules/discord.js/src/structures/GuildMember.js636
-rw-r--r--node_modules/discord.js/src/structures/Integration.js151
-rw-r--r--node_modules/discord.js/src/structures/Invite.js164
-rw-r--r--node_modules/discord.js/src/structures/Message.js661
-rw-r--r--node_modules/discord.js/src/structures/MessageAttachment.js79
-rw-r--r--node_modules/discord.js/src/structures/MessageCollector.js97
-rw-r--r--node_modules/discord.js/src/structures/MessageEmbed.js386
-rw-r--r--node_modules/discord.js/src/structures/MessageMentions.js185
-rw-r--r--node_modules/discord.js/src/structures/MessageReaction.js107
-rw-r--r--node_modules/discord.js/src/structures/NewsChannel.js24
-rw-r--r--node_modules/discord.js/src/structures/OAuth2Application.js157
-rw-r--r--node_modules/discord.js/src/structures/PartialGuild.js51
-rw-r--r--node_modules/discord.js/src/structures/PartialGuildChannel.js44
-rw-r--r--node_modules/discord.js/src/structures/PermissionOverwrites.js69
-rw-r--r--node_modules/discord.js/src/structures/Presence.js301
-rw-r--r--node_modules/discord.js/src/structures/ReactionCollector.js89
-rw-r--r--node_modules/discord.js/src/structures/ReactionEmoji.js98
-rw-r--r--node_modules/discord.js/src/structures/RichEmbed.js331
-rw-r--r--node_modules/discord.js/src/structures/Role.js376
-rw-r--r--node_modules/discord.js/src/structures/StoreChannel.js25
-rw-r--r--node_modules/discord.js/src/structures/Team.js109
-rw-r--r--node_modules/discord.js/src/structures/TeamMember.js67
-rw-r--r--node_modules/discord.js/src/structures/TextChannel.js154
-rw-r--r--node_modules/discord.js/src/structures/User.js336
-rw-r--r--node_modules/discord.js/src/structures/UserConnection.js48
-rw-r--r--node_modules/discord.js/src/structures/UserProfile.js62
-rw-r--r--node_modules/discord.js/src/structures/VoiceChannel.js146
-rw-r--r--node_modules/discord.js/src/structures/VoiceRegion.js50
-rw-r--r--node_modules/discord.js/src/structures/Webhook.js377
-rw-r--r--node_modules/discord.js/src/structures/interfaces/Collector.js208
-rw-r--r--node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js635
-rw-r--r--node_modules/discord.js/src/structures/shared/resolvePermissions.js26
-rw-r--r--node_modules/discord.js/src/util/BitField.js160
-rw-r--r--node_modules/discord.js/src/util/Collection.js532
-rw-r--r--node_modules/discord.js/src/util/Constants.js943
-rw-r--r--node_modules/discord.js/src/util/MessageFlags.js36
-rw-r--r--node_modules/discord.js/src/util/Permissions.js234
-rw-r--r--node_modules/discord.js/src/util/Snowflake.js82
-rw-r--r--node_modules/discord.js/src/util/SystemChannelFlags.js31
-rw-r--r--node_modules/discord.js/src/util/Util.js238
-rw-r--r--node_modules/discord.js/typings/discord.js-test.ts69
-rw-r--r--node_modules/discord.js/typings/index.d.ts2440
175 files changed, 24352 insertions, 0 deletions
diff --git a/node_modules/discord.js/.tern-project b/node_modules/discord.js/.tern-project
new file mode 100644
index 0000000..8f37bf0
--- /dev/null
+++ b/node_modules/discord.js/.tern-project
@@ -0,0 +1,21 @@
+{
+ "ecmaVersion": 7,
+ "libs": [],
+ "loadEagerly": [
+ "./src/*.js"
+ ],
+ "dontLoad": [
+ "node_modules/**"
+ ],
+ "plugins": {
+ "es_modules": {},
+ "node": {},
+ "doc_comment": {
+ "fullDocs": true,
+ "strong": true
+ },
+ "webpack": {
+ "configPath": "./webpack.config.js",
+ }
+ }
+}
diff --git a/node_modules/discord.js/LICENSE b/node_modules/discord.js/LICENSE
new file mode 100644
index 0000000..90cf110
--- /dev/null
+++ b/node_modules/discord.js/LICENSE
@@ -0,0 +1,190 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2017 Amish Shah
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/node_modules/discord.js/README.md b/node_modules/discord.js/README.md
new file mode 100644
index 0000000..abb18fb
--- /dev/null
+++ b/node_modules/discord.js/README.md
@@ -0,0 +1,86 @@
+<div align="center">
+ <br />
+ <p>
+ <a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
+ </p>
+ <br />
+ <p>
+ <a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
+ <a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
+ <a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
+ <a href="https://travis-ci.org/discordjs/discord.js"><img src="https://travis-ci.org/discordjs/discord.js.svg" alt="Build status" /></a>
+ <a href="https://david-dm.org/discordjs/discord.js"><img src="https://img.shields.io/david/discordjs/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
+ </p>
+ <p>
+ <a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
+ </p>
+</div>
+
+## About
+discord.js is a powerful [node.js](https://nodejs.org) module that allows you to interact with the
+[Discord API](https://discordapp.com/developers/docs/intro) very easily.
+
+- Object-oriented
+- Predictable abstractions
+- Performant
+- 100% coverage of the Discord API
+
+## Installation
+**Node.js 6.0.0 or newer is required.**
+Ignore any warnings about unmet peer dependencies, as they're all optional.
+
+Without voice support: `npm install discord.js`
+With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discord.js @discordjs/opus`
+With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript`
+
+### Audio engines
+The preferred audio engine is @discordjs/opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose @discordjs/opus.
+Using opusscript is only recommended for development environments where @discordjs/opus is tough to get working.
+For production bots, using @discordjs/opus should be considered a necessity, especially if they're going to be running on multiple servers.
+
+### Optional packages
+- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the WebSocket when *not* using uws (`npm install bufferutil`)
+- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack`)
+- One of the following packages can be installed for faster voice packet encryption and decryption:
+ - [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium`)
+ - [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers`)
+
+## Example usage
+```js
+const Discord = require('discord.js');
+const client = new Discord.Client();
+
+client.on('ready', () => {
+ console.log(`Logged in as ${client.user.tag}!`);
+});
+
+client.on('message', msg => {
+ if (msg.content === 'ping') {
+ msg.reply('pong');
+ }
+});
+
+client.login('token');
+```
+
+## Links
+* [Website](https://discord.js.org/) ([source](https://github.com/discordjs/website))
+* [Documentation](https://discord.js.org/#/docs)
+* [Guide](https://discordjs.guide/) ([source](https://github.com/discordjs/guide))
+* [Discord.js Discord server](https://discord.gg/bRCvFy9)
+* [Discord API Discord server](https://discord.gg/discord-api)
+* [GitHub](https://github.com/discordjs/discord.js)
+* [NPM](https://www.npmjs.com/package/discord.js)
+* [Related libraries](https://discordapi.com/unofficial/libs.html)
+
+### Extensions
+* [RPC](https://www.npmjs.com/package/discord-rpc) ([source](https://github.com/discordjs/RPC))
+
+## Contributing
+Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
+[documentation](https://discord.js.org/#/docs).
+See [the contribution guide](https://github.com/discordjs/discord.js/blob/master/.github/CONTRIBUTING.md) if you'd like to submit a PR.
+
+## Help
+If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
+nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).
diff --git a/node_modules/discord.js/browser.js b/node_modules/discord.js/browser.js
new file mode 100644
index 0000000..9f9341e
--- /dev/null
+++ b/node_modules/discord.js/browser.js
@@ -0,0 +1,9 @@
+const browser = typeof window !== 'undefined';
+const webpack = !!process.env.__DISCORD_WEBPACK__;
+
+const Discord = require('./');
+
+module.exports = Discord;
+if (browser && webpack) window.Discord = Discord; // eslint-disable-line no-undef
+// eslint-disable-next-line no-console
+else if (!browser) console.warn('Warning: Attempting to use browser version of Discord.js in a non-browser environment!');
diff --git a/node_modules/discord.js/package.json b/node_modules/discord.js/package.json
new file mode 100644
index 0000000..961d988
--- /dev/null
+++ b/node_modules/discord.js/package.json
@@ -0,0 +1,155 @@
+{
+ "_from": "discord.js@^11.6.3",
+ "_id": "[email protected]",
+ "_inBundle": false,
+ "_integrity": "sha512-dwIzHB5GPU9O/WiNw42d0yco0JED5bOjCPiHwU9VSSCkEhnyOdx5iqt7Dl2PQZAMNzWPI/t9oyCziOuzFlwm3Q==",
+ "_location": "/discord.js",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "discord.js@^11.6.3",
+ "name": "discord.js",
+ "escapedName": "discord.js",
+ "rawSpec": "^11.6.3",
+ "saveSpec": null,
+ "fetchSpec": "^11.6.3"
+ },
+ "_requiredBy": [
+ "/"
+ ],
+ "_resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.6.3.tgz",
+ "_shasum": "134dbc6b4878a1dd4e32c85a670010799bf8d33e",
+ "_spec": "discord.js@^11.6.3",
+ "_where": "E:\\Documents\\GitHub\\s5nical",
+ "author": {
+ "name": "Amish Shah",
+ "email": "[email protected]"
+ },
+ "browser": {
+ "ws": false,
+ "uws": false,
+ "@discordjs/uws": false,
+ "erlpack": false,
+ "prism-media": false,
+ "opusscript": false,
+ "node-opus": false,
+ "@discordjs/opus": false,
+ "tweetnacl": false,
+ "sodium": false,
+ "src/sharding/Shard.js": false,
+ "src/sharding/ShardClientUtil.js": false,
+ "src/sharding/ShardingManager.js": false,
+ "src/client/voice/dispatcher/StreamDispatcher.js": false,
+ "src/client/voice/opus/BaseOpusEngine.js": false,
+ "src/client/voice/opus/NodeOpusEngine.js": false,
+ "src/client/voice/opus/DiscordJsOpusEngine.js": false,
+ "src/client/voice/opus/OpusEngineList.js": false,
+ "src/client/voice/opus/OpusScriptEngine.js": false,
+ "src/client/voice/pcm/ConverterEngine.js": false,
+ "src/client/voice/pcm/ConverterEngineList.js": false,
+ "src/client/voice/pcm/FfmpegConverterEngine.js": false,
+ "src/client/voice/player/AudioPlayer.js": false,
+ "src/client/voice/receiver/VoiceReadable.js": false,
+ "src/client/voice/receiver/VoiceReceiver.js": false,
+ "src/client/voice/util/Secretbox.js": false,
+ "src/client/voice/util/SecretKey.js": false,
+ "src/client/voice/util/VolumeInterface.js": false,
+ "src/client/voice/ClientVoiceManager.js": false,
+ "src/client/voice/VoiceBroadcast.js": false,
+ "src/client/voice/VoiceConnection.js": false,
+ "src/client/voice/VoiceUDPClient.js": false,
+ "src/client/voice/VoiceWebSocket.js": false
+ },
+ "bugs": {
+ "url": "https://github.com/discordjs/discord.js/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "long": "^4.0.0",
+ "prism-media": "^0.0.4",
+ "snekfetch": "^3.6.4",
+ "tweetnacl": "^1.0.0",
+ "ws": "^6.0.0"
+ },
+ "deprecated": false,
+ "description": "A powerful library for interacting with the Discord API",
+ "devDependencies": {
+ "@types/node": "^9.4.6",
+ "discord.js-docgen": "github:discordjs/docgen",
+ "eslint": "^5.4.0",
+ "parallel-webpack": "^2.3.0",
+ "tslint": "^3.15.1",
+ "tslint-config-typings": "^0.2.4",
+ "typescript": "^3.0.1",
+ "uglifyjs-webpack-plugin": "^1.3.0",
+ "webpack": "^4.17.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "homepage": "https://github.com/discordjs/discord.js#readme",
+ "keywords": [
+ "discord",
+ "api",
+ "bot",
+ "client",
+ "node",
+ "discordapp"
+ ],
+ "license": "Apache-2.0",
+ "main": "./src/index",
+ "name": "discord.js",
+ "peerDependencies": {
+ "@discordjs/uws": "^10.149.0",
+ "bufferutil": "^4.0.0",
+ "erlpack": "discordapp/erlpack",
+ "libsodium-wrappers": "^0.7.3",
+ "@discordjs/opus": "^0.1.0",
+ "node-opus": "^0.2.7",
+ "opusscript": "^0.0.6",
+ "sodium": "^2.0.3"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "erlpack": {
+ "optional": true
+ },
+ "@discordjs/opus": {
+ "optional": true
+ },
+ "node-opus": {
+ "optional": true
+ },
+ "opusscript": {
+ "optional": true
+ },
+ "sodium": {
+ "optional": true
+ },
+ "libsodium-wrappers": {
+ "optional": true
+ },
+ "uws": {
+ "optional": true
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/discordjs/discord.js.git"
+ },
+ "runkitExampleFilename": "./docs/examples/ping.js",
+ "scripts": {
+ "docs": "docgen --source src --custom docs/index.yml --output docs/docs.json",
+ "docs:test": "docgen --source src --custom docs/index.yml",
+ "lint": "eslint src",
+ "lint:fix": "eslint --fix src",
+ "lint:typings": "tslint typings/index.d.ts typings/discord.js-test.ts",
+ "test": "npm run lint && npm run docs:test",
+ "webpack": "parallel-webpack"
+ },
+ "types": "./typings/index.d.ts",
+ "version": "11.6.3"
+}
diff --git a/node_modules/discord.js/src/client/Client.js b/node_modules/discord.js/src/client/Client.js
new file mode 100644
index 0000000..9ad7c95
--- /dev/null
+++ b/node_modules/discord.js/src/client/Client.js
@@ -0,0 +1,564 @@
+const EventEmitter = require('events');
+const Constants = require('../util/Constants');
+const Permissions = require('../util/Permissions');
+const Util = require('../util/Util');
+const RESTManager = require('./rest/RESTManager');
+const ClientDataManager = require('./ClientDataManager');
+const ClientManager = require('./ClientManager');
+const ClientDataResolver = require('./ClientDataResolver');
+const ClientVoiceManager = require('./voice/ClientVoiceManager');
+const WebSocketManager = require('./websocket/WebSocketManager');
+const ActionsManager = require('./actions/ActionsManager');
+const Collection = require('../util/Collection');
+const Presence = require('../structures/Presence').Presence;
+const ShardClientUtil = require('../sharding/ShardClientUtil');
+const VoiceBroadcast = require('./voice/VoiceBroadcast');
+
+/**
+ * The main hub for interacting with the Discord API, and the starting point for any bot.
+ * @extends {EventEmitter}
+ */
+class Client extends EventEmitter {
+ /**
+ * @param {ClientOptions} [options] Options for the client
+ */
+ constructor(options = {}) {
+ super();
+
+ // Obtain shard details from environment
+ if (!options.shardId && 'SHARD_ID' in process.env) options.shardId = Number(process.env.SHARD_ID);
+ if (!options.shardCount && 'SHARD_COUNT' in process.env) options.shardCount = Number(process.env.SHARD_COUNT);
+
+ /**
+ * The options the client was instantiated with
+ * @type {ClientOptions}
+ */
+ this.options = Util.mergeDefault(Constants.DefaultOptions, options);
+ this._validateOptions();
+
+ /**
+ * The REST manager of the client
+ * @type {RESTManager}
+ * @private
+ */
+ this.rest = new RESTManager(this);
+
+ /**
+ * The data manager of the client
+ * @type {ClientDataManager}
+ * @private
+ */
+ this.dataManager = new ClientDataManager(this);
+
+ /**
+ * The manager of the client
+ * @type {ClientManager}
+ * @private
+ */
+ this.manager = new ClientManager(this);
+
+ /**
+ * The WebSocket manager of the client
+ * @type {WebSocketManager}
+ * @private
+ */
+ this.ws = new WebSocketManager(this);
+
+ /**
+ * The data resolver of the client
+ * @type {ClientDataResolver}
+ * @private
+ */
+ this.resolver = new ClientDataResolver(this);
+
+ /**
+ * The action manager of the client
+ * @type {ActionsManager}
+ * @private
+ */
+ this.actions = new ActionsManager(this);
+
+ /**
+ * The voice manager of the client (`null` in browsers)
+ * @type {?ClientVoiceManager}
+ * @private
+ */
+ this.voice = !this.browser ? new ClientVoiceManager(this) : null;
+
+ /**
+ * The shard helpers for the client
+ * (only if the process was spawned as a child, such as from a {@link ShardingManager})
+ * @type {?ShardClientUtil}
+ */
+ this.shard = process.send ? ShardClientUtil.singleton(this) : null;
+
+ /**
+ * All of the {@link User} objects that have been cached at any point, mapped by their IDs
+ * @type {Collection<Snowflake, User>}
+ */
+ this.users = new Collection();
+
+ /**
+ * All of the guilds the client is currently handling, mapped by their IDs -
+ * as long as sharding isn't being used, this will be *every* guild the bot is a member of
+ * @type {Collection<Snowflake, Guild>}
+ */
+ this.guilds = new Collection();
+
+ /**
+ * All of the {@link Channel}s that the client is currently handling, mapped by their IDs -
+ * as long as sharding isn't being used, this will be *every* channel in *every* guild, and all DM channels
+ * @type {Collection<Snowflake, Channel>}
+ */
+ this.channels = new Collection();
+
+ /**
+ * Presences that have been received for the client user's friends, mapped by user IDs
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, Presence>}
+ * @deprecated
+ */
+ this.presences = new Collection();
+
+ Object.defineProperty(this, 'token', { writable: true });
+ if (!this.token && 'CLIENT_TOKEN' in process.env) {
+ /**
+ * Authorization token for the logged in user/bot
+ * <warn>This should be kept private at all times.</warn>
+ * @type {?string}
+ */
+ this.token = process.env.CLIENT_TOKEN;
+ } else {
+ this.token = null;
+ }
+
+ /**
+ * User that the client is logged in as
+ * @type {?ClientUser}
+ */
+ this.user = null;
+
+ /**
+ * Time at which the client was last regarded as being in the `READY` state
+ * (each time the client disconnects and successfully reconnects, this will be overwritten)
+ * @type {?Date}
+ */
+ this.readyAt = null;
+
+ /**
+ * Active voice broadcasts that have been created
+ * @type {VoiceBroadcast[]}
+ */
+ this.broadcasts = [];
+
+ /**
+ * Previous heartbeat pings of the websocket (most recent first, limited to three elements)
+ * @type {number[]}
+ */
+ this.pings = [];
+
+ /**
+ * Timeouts set by {@link Client#setTimeout} that are still active
+ * @type {Set<Timeout>}
+ * @private
+ */
+ this._timeouts = new Set();
+
+ /**
+ * Intervals set by {@link Client#setInterval} that are still active
+ * @type {Set<Timeout>}
+ * @private
+ */
+ this._intervals = new Set();
+
+ if (this.options.messageSweepInterval > 0) {
+ this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000);
+ }
+ }
+
+ /**
+ * Timestamp of the latest ping's start time
+ * @type {number}
+ * @private
+ */
+ get _pingTimestamp() {
+ return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0;
+ }
+
+ /**
+ * Current status of the client's connection to Discord
+ * @type {Status}
+ * @readonly
+ */
+ get status() {
+ return this.ws.connection ? this.ws.connection.status : Constants.Status.IDLE;
+ }
+
+ /**
+ * How long it has been since the client last entered the `READY` state in milliseconds
+ * @type {?number}
+ * @readonly
+ */
+ get uptime() {
+ return this.readyAt ? Date.now() - this.readyAt : null;
+ }
+
+ /**
+ * Average heartbeat ping of the websocket, obtained by averaging the {@link Client#pings} property
+ * @type {number}
+ * @readonly
+ */
+ get ping() {
+ return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length;
+ }
+
+ /**
+ * All active voice connections that have been established, mapped by guild ID
+ * @type {Collection<Snowflake, VoiceConnection>}
+ * @readonly
+ */
+ get voiceConnections() {
+ if (this.browser) return new Collection();
+ return this.voice.connections;
+ }
+
+ /**
+ * All custom emojis that the client has access to, mapped by their IDs
+ * @type {Collection<Snowflake, Emoji>}
+ * @readonly
+ */
+ get emojis() {
+ const emojis = new Collection();
+ for (const guild of this.guilds.values()) {
+ for (const emoji of guild.emojis.values()) emojis.set(emoji.id, emoji);
+ }
+ return emojis;
+ }
+
+ /**
+ * Timestamp of the time the client was last `READY` at
+ * @type {?number}
+ * @readonly
+ */
+ get readyTimestamp() {
+ return this.readyAt ? this.readyAt.getTime() : null;
+ }
+
+ /**
+ * Whether the client is in a browser environment
+ * @type {boolean}
+ * @readonly
+ */
+ get browser() {
+ return typeof window !== 'undefined';
+ }
+
+ /**
+ * Creates a voice broadcast.
+ * @returns {VoiceBroadcast}
+ */
+ createVoiceBroadcast() {
+ const broadcast = new VoiceBroadcast(this);
+ this.broadcasts.push(broadcast);
+ return broadcast;
+ }
+
+ /**
+ * Logs the client in, establishing a websocket connection to Discord.
+ * <info>Both bot and regular user accounts are supported, but it is highly recommended to use a bot account whenever
+ * possible. User accounts are subject to harsher ratelimits and other restrictions that don't apply to bot accounts.
+ * Bot accounts also have access to many features that user accounts cannot utilise. Automating a user account is
+ * considered a violation of Discord's ToS.</info>
+ * @param {string} token Token of the account to log in with
+ * @returns {Promise<string>} Token of the account used
+ * @example
+ * client.login('my token')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ login(token = this.token) {
+ return this.rest.methods.login(token);
+ }
+
+ /**
+ * Logs out, terminates the connection to Discord, and destroys the client.
+ * @returns {Promise}
+ */
+ destroy() {
+ for (const t of this._timeouts) clearTimeout(t);
+ for (const i of this._intervals) clearInterval(i);
+ this._timeouts.clear();
+ this._intervals.clear();
+ return this.manager.destroy();
+ }
+
+ /**
+ * Requests a sync of guild data with Discord.
+ * <info>This can be done automatically every 30 seconds by enabling {@link ClientOptions#sync}.</info>
+ * <warn>This is only available when using a user account.</warn>
+ * @param {Guild[]|Collection<Snowflake, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
+ * @deprecated
+ */
+ syncGuilds(guilds = this.guilds) {
+ if (this.user.bot) return;
+ this.ws.send({
+ op: 12,
+ d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
+ });
+ }
+
+ /**
+ * Obtains a user from Discord, or the user cache if it's already available.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {Snowflake} id ID of the user
+ * @param {boolean} [cache=true] Whether to cache the new user object if it isn't already
+ * @returns {Promise<User>}
+ */
+ fetchUser(id, cache = true) {
+ if (this.users.has(id)) return Promise.resolve(this.users.get(id));
+ return this.rest.methods.getUser(id, cache);
+ }
+
+ /**
+ * Obtains an invite from Discord.
+ * @param {InviteResolvable} invite Invite code or URL
+ * @returns {Promise<Invite>}
+ * @example
+ * client.fetchInvite('https://discord.gg/bRCvFy9')
+ * .then(invite => console.log(`Obtained invite with code: ${invite.code}`))
+ * .catch(console.error);
+ */
+ fetchInvite(invite) {
+ const code = this.resolver.resolveInviteCode(invite);
+ return this.rest.methods.getInvite(code);
+ }
+
+ /**
+ * Obtains a webhook from Discord.
+ * @param {Snowflake} id ID of the webhook
+ * @param {string} [token] Token for the webhook
+ * @returns {Promise<Webhook>}
+ * @example
+ * client.fetchWebhook('id', 'token')
+ * .then(webhook => console.log(`Obtained webhook with name: ${webhook.name}`))
+ * .catch(console.error);
+ */
+ fetchWebhook(id, token) {
+ return this.rest.methods.getWebhook(id, token);
+ }
+
+ /**
+ * Obtains the available voice regions from Discord.
+ * @returns {Promise<Collection<string, VoiceRegion>>}
+ * @example
+ * client.fetchVoiceRegions()
+ * .then(regions => console.log(`Available regions are: ${regions.map(region => region.name).join(', ')}`))
+ * .catch(console.error);
+ */
+ fetchVoiceRegions() {
+ return this.rest.methods.fetchVoiceRegions();
+ }
+
+ /**
+ * Sweeps all text-based channels' messages and removes the ones older than the max message lifetime.
+ * If the message has been edited, the time of the edit is used rather than the time of the original message.
+ * @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds)
+ * will be removed from the caches. The default is based on {@link ClientOptions#messageCacheLifetime}
+ * @returns {number} Amount of messages that were removed from the caches,
+ * or -1 if the message cache lifetime is unlimited
+ */
+ sweepMessages(lifetime = this.options.messageCacheLifetime) {
+ if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('The lifetime must be a number.');
+ if (lifetime <= 0) {
+ this.emit('debug', 'Didn\'t sweep messages - lifetime is unlimited');
+ return -1;
+ }
+
+ const lifetimeMs = lifetime * 1000;
+ const now = Date.now();
+ let channels = 0;
+ let messages = 0;
+
+ for (const channel of this.channels.values()) {
+ if (!channel.messages) continue;
+ channels++;
+
+ messages += channel.messages.sweep(
+ message => now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs
+ );
+ }
+
+ this.emit('debug', `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`);
+ return messages;
+ }
+
+ /**
+ * Obtains the OAuth Application of the bot from Discord.
+ * <warn>Bots can only fetch their own profile.</warn>
+ * @param {Snowflake} [id='@me'] ID of application to fetch
+ * @returns {Promise<OAuth2Application>}
+ * @example
+ * client.fetchApplication()
+ * .then(application => console.log(`Obtained application with name: ${application.name}`))
+ * .catch(console.error);
+ */
+ fetchApplication(id = '@me') {
+ if (id !== '@me') process.emitWarning('fetchApplication: use "@me" as an argument', 'DeprecationWarning');
+ return this.rest.methods.getApplication(id);
+ }
+
+ /**
+ * Generates a link that can be used to invite the bot to a guild.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {PermissionResolvable} [permissions] Permissions to request
+ * @returns {Promise<string>}
+ * @example
+ * client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'])
+ * .then(link => console.log(`Generated bot invite link: ${link}`))
+ * .catch(console.error);
+ */
+ generateInvite(permissions) {
+ permissions = Permissions.resolve(permissions);
+ return this.fetchApplication().then(application =>
+ `https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
+ );
+ }
+
+ /**
+ * Sets a timeout that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ setTimeout(fn, delay, ...args) {
+ const timeout = setTimeout(() => {
+ fn(...args);
+ this._timeouts.delete(timeout);
+ }, delay);
+ this._timeouts.add(timeout);
+ return timeout;
+ }
+
+ /**
+ * Clears a timeout.
+ * @param {Timeout} timeout Timeout to cancel
+ */
+ clearTimeout(timeout) {
+ clearTimeout(timeout);
+ this._timeouts.delete(timeout);
+ }
+
+ /**
+ * Sets an interval that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ setInterval(fn, delay, ...args) {
+ const interval = setInterval(fn, delay, ...args);
+ this._intervals.add(interval);
+ return interval;
+ }
+
+ /**
+ * Clears an interval.
+ * @param {Timeout} interval Interval to cancel
+ */
+ clearInterval(interval) {
+ clearInterval(interval);
+ this._intervals.delete(interval);
+ }
+
+ /**
+ * Adds a ping to {@link Client#pings}.
+ * @param {number} startTime Starting time of the ping
+ * @private
+ */
+ _pong(startTime) {
+ this.pings.unshift(Date.now() - startTime);
+ if (this.pings.length > 3) this.pings.length = 3;
+ this.ws.lastHeartbeatAck = true;
+ }
+
+ /**
+ * Adds/updates a friend's presence in {@link Client#presences}.
+ * @param {Snowflake} id ID of the user
+ * @param {Object} presence Raw presence object from Discord
+ * @private
+ */
+ _setPresence(id, presence) {
+ if (this.presences.has(id)) {
+ this.presences.get(id).update(presence);
+ return;
+ }
+ this.presences.set(id, new Presence(presence, this));
+ }
+
+ /**
+ * Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
+ * with the client as `this`.
+ * @param {string} script Script to eval
+ * @returns {*}
+ * @private
+ */
+ _eval(script) {
+ return eval(script);
+ }
+
+ /**
+ * Validates the client options.
+ * @param {ClientOptions} [options=this.options] Options to validate
+ * @private
+ */
+ _validateOptions(options = this.options) { // eslint-disable-line complexity
+ if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) {
+ throw new TypeError('The shardCount option must be a number.');
+ }
+ if (typeof options.shardId !== 'number' || isNaN(options.shardId)) {
+ throw new TypeError('The shardId option must be a number.');
+ }
+ if (options.shardCount < 0) throw new RangeError('The shardCount option must be at least 0.');
+ if (options.shardId < 0) throw new RangeError('The shardId option must be at least 0.');
+ if (options.shardId !== 0 && options.shardId >= options.shardCount) {
+ throw new RangeError('The shardId option must be less than shardCount.');
+ }
+ if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) {
+ throw new TypeError('The messageCacheMaxSize option must be a number.');
+ }
+ if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
+ throw new TypeError('The messageCacheLifetime option must be a number.');
+ }
+ if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
+ throw new TypeError('The messageSweepInterval option must be a number.');
+ }
+ if (typeof options.fetchAllMembers !== 'boolean') {
+ throw new TypeError('The fetchAllMembers option must be a boolean.');
+ }
+ if (typeof options.disableEveryone !== 'boolean') {
+ throw new TypeError('The disableEveryone option must be a boolean.');
+ }
+ if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
+ throw new TypeError('The restWsBridgeTimeout option must be a number.');
+ }
+ if (!(options.disabledEvents instanceof Array)) throw new TypeError('The disabledEvents option must be an Array.');
+ if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
+ throw new TypeError('The retryLimit options must be a number.');
+ }
+ }
+}
+
+module.exports = Client;
+
+/**
+ * Emitted for general warnings.
+ * @event Client#warn
+ * @param {string} info The warning
+ */
+
+/**
+ * Emitted for general debugging information.
+ * @event Client#debug
+ * @param {string} info The debug information
+ */
diff --git a/node_modules/discord.js/src/client/ClientDataManager.js b/node_modules/discord.js/src/client/ClientDataManager.js
new file mode 100644
index 0000000..4f0f2d7
--- /dev/null
+++ b/node_modules/discord.js/src/client/ClientDataManager.js
@@ -0,0 +1,149 @@
+const Constants = require('../util/Constants');
+const Util = require('../util/Util');
+const Guild = require('../structures/Guild');
+const User = require('../structures/User');
+const Emoji = require('../structures/Emoji');
+const GuildChannel = require('../structures/GuildChannel');
+const TextChannel = require('../structures/TextChannel');
+const VoiceChannel = require('../structures/VoiceChannel');
+const CategoryChannel = require('../structures/CategoryChannel');
+const NewsChannel = require('../structures/NewsChannel');
+const StoreChannel = require('../structures/StoreChannel');
+const DMChannel = require('../structures/DMChannel');
+const GroupDMChannel = require('../structures/GroupDMChannel');
+
+class ClientDataManager {
+ constructor(client) {
+ this.client = client;
+ }
+
+ get pastReady() {
+ return this.client.ws.connection.status === Constants.Status.READY;
+ }
+
+ newGuild(data) {
+ const already = this.client.guilds.has(data.id);
+ const guild = new Guild(this.client, data);
+ this.client.guilds.set(guild.id, guild);
+ if (this.pastReady && !already) {
+ /**
+ * Emitted whenever the client joins a guild.
+ * @event Client#guildCreate
+ * @param {Guild} guild The created guild
+ */
+ if (this.client.options.fetchAllMembers) {
+ guild.fetchMembers().then(() => { this.client.emit(Constants.Events.GUILD_CREATE, guild); });
+ } else {
+ this.client.emit(Constants.Events.GUILD_CREATE, guild);
+ }
+ }
+
+ return guild;
+ }
+
+ newUser(data, cache = true) {
+ if (this.client.users.has(data.id)) return this.client.users.get(data.id);
+ const user = new User(this.client, data);
+ if (cache) this.client.users.set(user.id, user);
+ return user;
+ }
+
+ newChannel(data, guild) {
+ const already = this.client.channels.has(data.id);
+ let channel;
+ if (data.type === Constants.ChannelTypes.DM) {
+ channel = new DMChannel(this.client, data);
+ } else if (data.type === Constants.ChannelTypes.GROUP_DM) {
+ channel = new GroupDMChannel(this.client, data);
+ } else {
+ guild = guild || this.client.guilds.get(data.guild_id);
+ if (already) {
+ channel = this.client.channels.get(data.id);
+ } else if (guild) {
+ switch (data.type) {
+ case Constants.ChannelTypes.TEXT:
+ channel = new TextChannel(guild, data);
+ break;
+ case Constants.ChannelTypes.VOICE:
+ channel = new VoiceChannel(guild, data);
+ break;
+ case Constants.ChannelTypes.CATEGORY:
+ channel = new CategoryChannel(guild, data);
+ break;
+ case Constants.ChannelTypes.NEWS:
+ channel = new NewsChannel(guild, data);
+ break;
+ case Constants.ChannelTypes.STORE:
+ channel = new StoreChannel(guild, data);
+ break;
+ }
+
+ guild.channels.set(channel.id, channel);
+ }
+ }
+
+ if (channel && !already) {
+ if (this.pastReady) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
+ this.client.channels.set(channel.id, channel);
+ return channel;
+ } else if (already) {
+ return channel;
+ }
+
+ return null;
+ }
+
+ newEmoji(data, guild) {
+ const already = guild.emojis.has(data.id);
+ if (data && !already) {
+ let emoji = new Emoji(guild, data);
+ this.client.emit(Constants.Events.GUILD_EMOJI_CREATE, emoji);
+ guild.emojis.set(emoji.id, emoji);
+ return emoji;
+ } else if (already) {
+ return guild.emojis.get(data.id);
+ }
+
+ return null;
+ }
+
+ killEmoji(emoji) {
+ if (!(emoji instanceof Emoji && emoji.guild)) return;
+ this.client.emit(Constants.Events.GUILD_EMOJI_DELETE, emoji);
+ emoji.guild.emojis.delete(emoji.id);
+ }
+
+ killGuild(guild) {
+ const already = this.client.guilds.has(guild.id);
+ this.client.guilds.delete(guild.id);
+ if (already && this.pastReady) this.client.emit(Constants.Events.GUILD_DELETE, guild);
+ }
+
+ killUser(user) {
+ this.client.users.delete(user.id);
+ }
+
+ killChannel(channel) {
+ this.client.channels.delete(channel.id);
+ if (channel instanceof GuildChannel) channel.guild.channels.delete(channel.id);
+ }
+
+ updateGuild(currentGuild, newData) {
+ const oldGuild = Util.cloneObject(currentGuild);
+ currentGuild.setup(newData);
+ if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
+ }
+
+ updateChannel(currentChannel, newData) {
+ currentChannel.setup(newData);
+ }
+
+ updateEmoji(currentEmoji, newData) {
+ const oldEmoji = Util.cloneObject(currentEmoji);
+ currentEmoji.setup(newData);
+ this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
+ return currentEmoji;
+ }
+}
+
+module.exports = ClientDataManager;
diff --git a/node_modules/discord.js/src/client/ClientDataResolver.js b/node_modules/discord.js/src/client/ClientDataResolver.js
new file mode 100644
index 0000000..50a7bb9
--- /dev/null
+++ b/node_modules/discord.js/src/client/ClientDataResolver.js
@@ -0,0 +1,376 @@
+const path = require('path');
+const fs = require('fs');
+const snekfetch = require('snekfetch');
+
+const Constants = require('../util/Constants');
+const convertToBuffer = require('../util/Util').convertToBuffer;
+const User = require('../structures/User');
+const Message = require('../structures/Message');
+const Guild = require('../structures/Guild');
+const Channel = require('../structures/Channel');
+const GuildMember = require('../structures/GuildMember');
+const Emoji = require('../structures/Emoji');
+const ReactionEmoji = require('../structures/ReactionEmoji');
+const Role = require('../structures/Role');
+
+/**
+ * The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
+ * extracting a User from a Message object.
+ * @private
+ */
+class ClientDataResolver {
+ /**
+ * @param {Client} client The client the resolver is for
+ */
+ constructor(client) {
+ this.client = client;
+ }
+
+ /**
+ * Data that resolves to give a User object. This can be:
+ * * A User object
+ * * A Snowflake
+ * * A Message object (resolves to the message author)
+ * * A Guild object (owner of the guild)
+ * * A GuildMember object
+ * @typedef {User|Snowflake|Message|Guild|GuildMember} UserResolvable
+ */
+
+ /**
+ * Resolves a UserResolvable to a User object.
+ * @param {UserResolvable} user The UserResolvable to identify
+ * @returns {?User}
+ */
+ resolveUser(user) {
+ if (user instanceof User) return user;
+ if (typeof user === 'string') return this.client.users.get(user) || null;
+ if (user instanceof GuildMember) return user.user;
+ if (user instanceof Message) return user.author;
+ if (user instanceof Guild) return this.resolveUser(user.ownerID);
+ return null;
+ }
+
+ /**
+ * Resolves a UserResolvable to a user ID string.
+ * @param {UserResolvable} user The UserResolvable to identify
+ * @returns {?Snowflake}
+ */
+ resolveUserID(user) {
+ if (user instanceof User || user instanceof GuildMember) return user.id;
+ if (typeof user === 'string') return user || null;
+ if (user instanceof Message) return user.author.id;
+ if (user instanceof Guild) return user.ownerID;
+ return null;
+ }
+
+ /**
+ * Data that resolves to give a Guild object. This can be:
+ * * A Guild object
+ * * A Snowflake
+ * @typedef {Guild|Snowflake} GuildResolvable
+ */
+
+ /**
+ * Resolves a GuildResolvable to a Guild object.
+ * @param {GuildResolvable} guild The GuildResolvable to identify
+ * @returns {?Guild}
+ */
+ resolveGuild(guild) {
+ if (guild instanceof Guild) return guild;
+ if (typeof guild === 'string') return this.client.guilds.get(guild) || null;
+ return null;
+ }
+
+ /**
+ * Data that resolves to give a GuildMember object. This can be:
+ * * A GuildMember object
+ * * A User object
+ * @typedef {GuildMember|User} GuildMemberResolvable
+ */
+
+ /**
+ * Resolves a GuildMemberResolvable to a GuildMember object.
+ * @param {GuildResolvable} guild The guild that the member is part of
+ * @param {UserResolvable} user The user that is part of the guild
+ * @returns {?GuildMember}
+ */
+ resolveGuildMember(guild, user) {
+ if (user instanceof GuildMember) return user;
+ guild = this.resolveGuild(guild);
+ user = this.resolveUser(user);
+ if (!guild || !user) return null;
+ return guild.members.get(user.id) || null;
+ }
+
+ /**
+ * Data that can be resolved to a Role object. This can be:
+ * * A Role
+ * * A Snowflake
+ * @typedef {Role|Snowflake} RoleResolvable
+ */
+
+ /**
+ * Resolves a RoleResolvable to a Role object.
+ * @param {GuildResolvable} guild The guild that this role is part of
+ * @param {RoleResolvable} role The role resolvable to resolve
+ * @returns {?Role}
+ */
+ resolveRole(guild, role) {
+ if (role instanceof Role) return role;
+ guild = this.resolveGuild(guild);
+ if (!guild) return null;
+ if (typeof role === 'string') return guild.roles.get(role);
+ return null;
+ }
+
+ /**
+ * Data that can be resolved to give a Channel object. This can be:
+ * * A Channel object
+ * * A Message object (the channel the message was sent in)
+ * * A Guild object (the #general channel)
+ * * A Snowflake
+ * @typedef {Channel|Guild|Message|Snowflake} ChannelResolvable
+ */
+
+ /**
+ * Resolves a ChannelResolvable to a Channel object.
+ * @param {ChannelResolvable} channel The channel resolvable to resolve
+ * @returns {?Channel}
+ */
+ resolveChannel(channel) {
+ if (channel instanceof Channel) return channel;
+ if (typeof channel === 'string') return this.client.channels.get(channel) || null;
+ if (channel instanceof Message) return channel.channel;
+ if (channel instanceof Guild) return channel.channels.get(channel.id) || null;
+ return null;
+ }
+
+ /**
+ * Resolves a ChannelResolvable to a channel ID.
+ * @param {ChannelResolvable} channel The channel resolvable to resolve
+ * @returns {?Snowflake}
+ */
+ resolveChannelID(channel) {
+ if (channel instanceof Channel) return channel.id;
+ if (typeof channel === 'string') return channel;
+ if (channel instanceof Message) return channel.channel.id;
+ if (channel instanceof Guild) return channel.defaultChannel.id;
+ return null;
+ }
+
+ /**
+ * Data that can be resolved to give an invite code. This can be:
+ * * An invite code
+ * * An invite URL
+ * @typedef {string} InviteResolvable
+ */
+
+ /**
+ * Resolves InviteResolvable to an invite code.
+ * @param {InviteResolvable} data The invite resolvable to resolve
+ * @returns {string}
+ */
+ resolveInviteCode(data) {
+ const inviteRegex = /discord(?:app\.com\/invite|\.gg(?:\/invite)?)\/([\w-]{2,255})/i;
+ const match = inviteRegex.exec(data);
+ if (match && match[1]) return match[1];
+ return data;
+ }
+
+ /**
+ * Data that can be resolved to give a string. This can be:
+ * * A string
+ * * An array (joined with a new line delimiter to give a string)
+ * * Any value
+ * @typedef {string|Array|*} StringResolvable
+ */
+
+ /**
+ * Resolves a StringResolvable to a string.
+ * @param {StringResolvable} data The string resolvable to resolve
+ * @returns {string}
+ */
+ resolveString(data) {
+ if (typeof data === 'string') return data;
+ if (data instanceof Array) return data.join('\n');
+ return String(data);
+ }
+
+
+ /**
+ * Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
+ * @param {BufferResolvable|Base64Resolvable} image The image to be resolved
+ * @returns {Promise<?string>}
+ */
+ resolveImage(image) {
+ if (!image) return Promise.resolve(null);
+ if (typeof image === 'string' && image.startsWith('data:')) {
+ return Promise.resolve(image);
+ }
+ return this.resolveFile(image).then(this.resolveBase64);
+ }
+
+ /**
+ * Data that resolves to give a Base64 string, typically for image uploading. This can be:
+ * * A Buffer
+ * * A base64 string
+ * @typedef {Buffer|string} Base64Resolvable
+ */
+
+ /**
+ * Resolves a Base64Resolvable to a Base 64 image.
+ * @param {Base64Resolvable} data The base 64 resolvable you want to resolve
+ * @returns {?string}
+ */
+ resolveBase64(data) {
+ if (data instanceof Buffer) return `data:image/jpg;base64,${data.toString('base64')}`;
+ return data;
+ }
+
+ /**
+ * Data that can be resolved to give a Buffer. This can be:
+ * * A Buffer
+ * * The path to a local file
+ * * A URL
+ * * A Stream
+ * @typedef {string|Buffer} BufferResolvable
+ */
+
+ /**
+ * @external Stream
+ * @see {@link https://nodejs.org/api/stream.html}
+ */
+
+ /**
+ * Resolves a BufferResolvable to a Buffer.
+ * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
+ * @returns {Promise<Buffer>}
+ */
+ resolveFile(resource) {
+ if (resource instanceof Buffer) return Promise.resolve(resource);
+ if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertToBuffer(resource));
+
+ if (typeof resource === 'string') {
+ if (/^https?:\/\//.test(resource)) {
+ return snekfetch.get(resource).then(res => res.body instanceof Buffer ? res.body : Buffer.from(res.text));
+ }
+ return new Promise((resolve, reject) => {
+ const file = path.resolve(resource);
+ fs.stat(file, (err, stats) => {
+ if (err) return reject(err);
+ if (!stats || !stats.isFile()) return reject(new Error(`The file could not be found: ${file}`));
+ fs.readFile(file, (err2, data) => {
+ if (err2) reject(err2);
+ else resolve(data);
+ });
+ return null;
+ });
+ });
+ } else if (resource && resource.pipe && typeof resource.pipe === 'function') {
+ return new Promise((resolve, reject) => {
+ const buffers = [];
+ resource.once('error', reject);
+ resource.on('data', data => buffers.push(data));
+ resource.once('end', () => resolve(Buffer.concat(buffers)));
+ });
+ }
+
+ return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
+ }
+
+ /**
+ * Data that can be resolved to give an emoji identifier. This can be:
+ * * The unicode representation of an emoji
+ * * A custom emoji ID
+ * * An Emoji object
+ * * A ReactionEmoji object
+ * @typedef {string|Emoji|ReactionEmoji} EmojiIdentifierResolvable
+ */
+
+ /**
+ * Resolves an EmojiResolvable to an emoji identifier.
+ * @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
+ * @returns {?string}
+ */
+ resolveEmojiIdentifier(emoji) {
+ if (emoji instanceof Emoji || emoji instanceof ReactionEmoji) return emoji.identifier;
+ if (typeof emoji === 'string') {
+ if (this.client.emojis.has(emoji)) return this.client.emojis.get(emoji).identifier;
+ else if (!emoji.includes('%')) return encodeURIComponent(emoji);
+ else return emoji;
+ }
+ return null;
+ }
+
+ /**
+ * Can be a Hex Literal, Hex String, Number, RGB Array, or one of the following
+ * ```
+ * [
+ * 'DEFAULT',
+ * 'WHITE',
+ * 'AQUA',
+ * 'GREEN',
+ * 'BLUE',
+ * 'PURPLE',
+ * 'LUMINOUS_VIVID_PINK',
+ * 'GOLD',
+ * 'ORANGE',
+ * 'RED',
+ * 'GREY',
+ * 'DARKER_GREY',
+ * 'NAVY',
+ * 'DARK_AQUA',
+ * 'DARK_GREEN',
+ * 'DARK_BLUE',
+ * 'DARK_PURPLE',
+ * 'DARK_VIVID_PINK',
+ * 'DARK_GOLD',
+ * 'DARK_ORANGE',
+ * 'DARK_RED',
+ * 'DARK_GREY',
+ * 'LIGHT_GREY',
+ * 'DARK_NAVY',
+ * 'RANDOM',
+ * ]
+ * ```
+ * or something like
+ * ```
+ * [255, 0, 255]
+ * ```
+ * for purple
+ * @typedef {string|number|Array} ColorResolvable
+ */
+
+ /**
+ * Resolves a ColorResolvable into a color number.
+ * @param {ColorResolvable} color Color to resolve
+ * @returns {number} A color
+ */
+ static resolveColor(color) {
+ if (typeof color === 'string') {
+ if (color === 'RANDOM') return Math.floor(Math.random() * (0xFFFFFF + 1));
+ if (color === 'DEFAULT') return 0;
+ color = Constants.Colors[color] || parseInt(color.replace('#', ''), 16);
+ } else if (color instanceof Array) {
+ color = (color[0] << 16) + (color[1] << 8) + color[2];
+ }
+
+ if (color < 0 || color > 0xFFFFFF) {
+ throw new RangeError('Color must be within the range 0 - 16777215 (0xFFFFFF).');
+ } else if (color && isNaN(color)) {
+ throw new TypeError('Unable to convert color to a number.');
+ }
+
+ return color;
+ }
+
+ /**
+ * @param {ColorResolvable} color Color to resolve
+ * @returns {number} A color
+ */
+ resolveColor(color) {
+ return this.constructor.resolveColor(color);
+ }
+}
+
+module.exports = ClientDataResolver;
diff --git a/node_modules/discord.js/src/client/ClientManager.js b/node_modules/discord.js/src/client/ClientManager.js
new file mode 100644
index 0000000..0f2480c
--- /dev/null
+++ b/node_modules/discord.js/src/client/ClientManager.js
@@ -0,0 +1,74 @@
+const Constants = require('../util/Constants');
+const WebSocketConnection = require('./websocket/WebSocketConnection');
+
+/**
+ * Manages the state and background tasks of the client.
+ * @private
+ */
+class ClientManager {
+ constructor(client) {
+ /**
+ * The client that instantiated this Manager
+ * @type {Client}
+ */
+ this.client = client;
+
+ /**
+ * The heartbeat interval
+ * @type {?number}
+ */
+ this.heartbeatInterval = null;
+ }
+
+ /**
+ * The status of the client
+ * @type {number}
+ */
+ get status() {
+ return this.connection ? this.connection.status : Constants.Status.IDLE;
+ }
+
+ /**
+ * Connects the client to the WebSocket.
+ * @param {string} token The authorization token
+ * @param {Function} resolve Function to run when connection is successful
+ * @param {Function} reject Function to run when connection fails
+ */
+ connectToWebSocket(token, resolve, reject) {
+ this.client.emit(Constants.Events.DEBUG, `Authenticated using token ${token}`);
+ this.client.token = token;
+ const timeout = this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
+ this.client.rest.methods.getGateway().then(res => {
+ const protocolVersion = Constants.DefaultOptions.ws.version;
+ const gateway = `${res.url}/?v=${protocolVersion}&encoding=${WebSocketConnection.ENCODING}`;
+ this.client.emit(Constants.Events.DEBUG, `Using gateway ${gateway}`);
+ this.client.ws.connect(gateway);
+ this.client.ws.connection.once('error', reject);
+ this.client.ws.connection.once('close', event => {
+ if (event.code === 4004) reject(new Error(Constants.Errors.BAD_LOGIN));
+ if (event.code === 4010) reject(new Error(Constants.Errors.INVALID_SHARD));
+ if (event.code === 4011) reject(new Error(Constants.Errors.SHARDING_REQUIRED));
+ });
+ this.client.once(Constants.Events.READY, () => {
+ resolve(token);
+ this.client.clearTimeout(timeout);
+ });
+ }, reject);
+ }
+
+ destroy() {
+ this.client.ws.destroy();
+ this.client.rest.destroy();
+ if (!this.client.user) return Promise.resolve();
+ if (this.client.user.bot) {
+ this.client.token = null;
+ return Promise.resolve();
+ } else {
+ return this.client.rest.methods.logout().then(() => {
+ this.client.token = null;
+ });
+ }
+ }
+}
+
+module.exports = ClientManager;
diff --git a/node_modules/discord.js/src/client/WebhookClient.js b/node_modules/discord.js/src/client/WebhookClient.js
new file mode 100644
index 0000000..99291b5
--- /dev/null
+++ b/node_modules/discord.js/src/client/WebhookClient.js
@@ -0,0 +1,118 @@
+const Webhook = require('../structures/Webhook');
+const RESTManager = require('./rest/RESTManager');
+const ClientDataResolver = require('./ClientDataResolver');
+const Constants = require('../util/Constants');
+const Util = require('../util/Util');
+
+/**
+ * The webhook client.
+ * @extends {Webhook}
+ */
+class WebhookClient extends Webhook {
+ /**
+ * @param {Snowflake} id ID of the webhook
+ * @param {string} token Token of the webhook
+ * @param {ClientOptions} [options] Options for the client
+ * @example
+ * // Create a new webhook and send a message
+ * const hook = new Discord.WebhookClient('1234', 'abcdef');
+ * hook.sendMessage('This will send a message').catch(console.error);
+ */
+ constructor(id, token, options) {
+ super(null, id, token);
+
+ /**
+ * The options the client was instantiated with
+ * @type {ClientOptions}
+ */
+ this.options = Util.mergeDefault(Constants.DefaultOptions, options);
+
+ /**
+ * The REST manager of the client
+ * @type {RESTManager}
+ * @private
+ */
+ this.rest = new RESTManager(this);
+
+ /**
+ * The data resolver of the client
+ * @type {ClientDataResolver}
+ * @private
+ */
+ this.resolver = new ClientDataResolver(this);
+
+ /**
+ * Timeouts set by {@link WebhookClient#setTimeout} that are still active
+ * @type {Set<Timeout>}
+ * @private
+ */
+ this._timeouts = new Set();
+
+ /**
+ * Intervals set by {@link WebhookClient#setInterval} that are still active
+ * @type {Set<Timeout>}
+ * @private
+ */
+ this._intervals = new Set();
+ }
+
+ /**
+ * Sets a timeout that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ setTimeout(fn, delay, ...args) {
+ const timeout = setTimeout(() => {
+ fn(...args);
+ this._timeouts.delete(timeout);
+ }, delay);
+ this._timeouts.add(timeout);
+ return timeout;
+ }
+
+ /**
+ * Clears a timeout.
+ * @param {Timeout} timeout Timeout to cancel
+ */
+ clearTimeout(timeout) {
+ clearTimeout(timeout);
+ this._timeouts.delete(timeout);
+ }
+
+ /**
+ * Sets an interval that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ setInterval(fn, delay, ...args) {
+ const interval = setInterval(fn, delay, ...args);
+ this._intervals.add(interval);
+ return interval;
+ }
+
+ /**
+ * Clears an interval.
+ * @param {Timeout} interval Interval to cancel
+ */
+ clearInterval(interval) {
+ clearInterval(interval);
+ this._intervals.delete(interval);
+ }
+
+
+ /**
+ * Destroys the client.
+ */
+ destroy() {
+ for (const t of this._timeouts) clearTimeout(t);
+ for (const i of this._intervals) clearInterval(i);
+ this._timeouts.clear();
+ this._intervals.clear();
+ }
+}
+
+module.exports = WebhookClient;
diff --git a/node_modules/discord.js/src/client/actions/Action.js b/node_modules/discord.js/src/client/actions/Action.js
new file mode 100644
index 0000000..8fdadc9
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/Action.js
@@ -0,0 +1,23 @@
+/*
+
+ABOUT ACTIONS
+
+Actions are similar to WebSocket Packet Handlers, but since introducing
+the REST API methods, in order to prevent rewriting code to handle data,
+"actions" have been introduced. They're basically what Packet Handlers
+used to be but they're strictly for manipulating data and making sure
+that WebSocket events don't clash with REST methods.
+
+*/
+
+class GenericAction {
+ constructor(client) {
+ this.client = client;
+ }
+
+ handle(data) {
+ return data;
+ }
+}
+
+module.exports = GenericAction;
diff --git a/node_modules/discord.js/src/client/actions/ActionsManager.js b/node_modules/discord.js/src/client/actions/ActionsManager.js
new file mode 100644
index 0000000..f9739cc
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ActionsManager.js
@@ -0,0 +1,43 @@
+class ActionsManager {
+ constructor(client) {
+ this.client = client;
+
+ this.register(require('./MessageCreate'));
+ this.register(require('./MessageDelete'));
+ this.register(require('./MessageDeleteBulk'));
+ this.register(require('./MessageUpdate'));
+ this.register(require('./MessageReactionAdd'));
+ this.register(require('./MessageReactionRemove'));
+ this.register(require('./MessageReactionRemoveEmoji'));
+ this.register(require('./MessageReactionRemoveAll'));
+ this.register(require('./ChannelCreate'));
+ this.register(require('./ChannelDelete'));
+ this.register(require('./ChannelUpdate'));
+ this.register(require('./GuildDelete'));
+ this.register(require('./GuildUpdate'));
+ this.register(require('./GuildMemberGet'));
+ this.register(require('./GuildMemberRemove'));
+ this.register(require('./GuildBanRemove'));
+ this.register(require('./GuildRoleCreate'));
+ this.register(require('./GuildRoleDelete'));
+ this.register(require('./GuildRoleUpdate'));
+ this.register(require('./InviteCreate'));
+ this.register(require('./InviteDelete'));
+ this.register(require('./UserGet'));
+ this.register(require('./UserUpdate'));
+ this.register(require('./UserNoteUpdate'));
+ this.register(require('./GuildSync'));
+ this.register(require('./GuildEmojiCreate'));
+ this.register(require('./GuildEmojiDelete'));
+ this.register(require('./GuildEmojiUpdate'));
+ this.register(require('./GuildEmojisUpdate'));
+ this.register(require('./GuildRolesPositionUpdate'));
+ this.register(require('./GuildChannelsPositionUpdate'));
+ }
+
+ register(Action) {
+ this[Action.name.replace(/Action$/, '')] = new Action(this.client);
+ }
+}
+
+module.exports = ActionsManager;
diff --git a/node_modules/discord.js/src/client/actions/ChannelCreate.js b/node_modules/discord.js/src/client/actions/ChannelCreate.js
new file mode 100644
index 0000000..83b1aa0
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ChannelCreate.js
@@ -0,0 +1,11 @@
+const Action = require('./Action');
+
+class ChannelCreateAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const channel = client.dataManager.newChannel(data);
+ return { channel };
+ }
+}
+
+module.exports = ChannelCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/ChannelDelete.js b/node_modules/discord.js/src/client/actions/ChannelDelete.js
new file mode 100644
index 0000000..6def629
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ChannelDelete.js
@@ -0,0 +1,38 @@
+const Action = require('./Action');
+const DMChannel = require('../../structures/DMChannel');
+
+class ChannelDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ let channel = client.channels.get(data.id);
+ if (channel) {
+ client.dataManager.killChannel(channel);
+ this.deleted.set(channel.id, channel);
+ this.scheduleForDeletion(channel.id);
+ } else {
+ channel = this.deleted.get(data.id) || null;
+ }
+ if (channel) {
+ if (channel.messages && !(channel instanceof DMChannel)) {
+ for (const message of channel.messages.values()) {
+ message.deleted = true;
+ }
+ }
+ channel.deleted = true;
+ }
+
+ return { channel };
+ }
+
+ scheduleForDeletion(id) {
+ this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+module.exports = ChannelDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/ChannelUpdate.js b/node_modules/discord.js/src/client/actions/ChannelUpdate.js
new file mode 100644
index 0000000..ba1c4ef
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ChannelUpdate.js
@@ -0,0 +1,74 @@
+const Action = require('./Action');
+const TextChannel = require('../../structures/TextChannel');
+const VoiceChannel = require('../../structures/VoiceChannel');
+const CategoryChannel = require('../../structures/CategoryChannel');
+const NewsChannel = require('../../structures/NewsChannel');
+const StoreChannel = require('../../structures/StoreChannel');
+const Constants = require('../../util/Constants');
+const ChannelTypes = Constants.ChannelTypes;
+const Util = require('../../util/Util');
+
+class ChannelUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ let channel = client.channels.get(data.id);
+ if (channel) {
+ const oldChannel = Util.cloneObject(channel);
+
+ // If the channel is changing types, we need to follow a different process
+ if (ChannelTypes[channel.type.toUpperCase()] !== data.type) {
+ // Determine which channel class we're changing to
+ let channelClass;
+ switch (data.type) {
+ case ChannelTypes.TEXT:
+ channelClass = TextChannel;
+ break;
+ case ChannelTypes.VOICE:
+ channelClass = VoiceChannel;
+ break;
+ case ChannelTypes.CATEGORY:
+ channelClass = CategoryChannel;
+ break;
+ case ChannelTypes.NEWS:
+ channelClass = NewsChannel;
+ break;
+ case ChannelTypes.STORE:
+ channelClass = StoreChannel;
+ break;
+ }
+
+ // Create the new channel instance and copy over cached data
+ const newChannel = new channelClass(channel.guild, data);
+ if (channel.messages && newChannel.messages) {
+ for (const [id, message] of channel.messages) newChannel.messages.set(id, message);
+ }
+
+ channel = newChannel;
+ this.client.channels.set(channel.id, channel);
+ } else {
+ channel.setup(data);
+ }
+
+ client.emit(Constants.Events.CHANNEL_UPDATE, oldChannel, channel);
+ return {
+ old: oldChannel,
+ updated: channel,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a channel is updated - e.g. name change, topic change.
+ * @event Client#channelUpdate
+ * @param {Channel} oldChannel The channel before the update
+ * @param {Channel} newChannel The channel after the update
+ */
+
+module.exports = ChannelUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildBanRemove.js b/node_modules/discord.js/src/client/actions/GuildBanRemove.js
new file mode 100644
index 0000000..0276a52
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildBanRemove.js
@@ -0,0 +1,13 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildBanRemove extends Action {
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ const user = client.dataManager.newUser(data.user);
+ if (guild && user) client.emit(Constants.Events.GUILD_BAN_REMOVE, guild, user);
+ }
+}
+
+module.exports = GuildBanRemove;
diff --git a/node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js b/node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js
new file mode 100644
index 0000000..ea184ef
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js
@@ -0,0 +1,19 @@
+const Action = require('./Action');
+
+class GuildChannelsPositionUpdate extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ for (const partialChannel of data.channels) {
+ const channel = guild.channels.get(partialChannel.id);
+ if (channel) channel.position = partialChannel.position;
+ }
+ }
+
+ return { guild };
+ }
+}
+
+module.exports = GuildChannelsPositionUpdate;
diff --git a/node_modules/discord.js/src/client/actions/GuildDelete.js b/node_modules/discord.js/src/client/actions/GuildDelete.js
new file mode 100644
index 0000000..8222323
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildDelete.js
@@ -0,0 +1,57 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ let guild = client.guilds.get(data.id);
+ if (guild) {
+ for (const channel of guild.channels.values()) {
+ if (channel.type === 'text') channel.stopTyping(true);
+ }
+
+ if (guild.available && data.unavailable) {
+ // Guild is unavailable
+ guild.available = false;
+ client.emit(Constants.Events.GUILD_UNAVAILABLE, guild);
+
+ // Stops the GuildDelete packet thinking a guild was actually deleted,
+ // handles emitting of event itself
+ return {
+ guild: null,
+ };
+ }
+
+ for (const channel of guild.channels.values()) this.client.channels.delete(channel.id);
+ if (guild.voiceConnection) guild.voiceConnection.disconnect();
+
+ // Delete guild
+ client.guilds.delete(guild.id);
+ this.deleted.set(guild.id, guild);
+ this.scheduleForDeletion(guild.id);
+ } else {
+ guild = this.deleted.get(data.id) || null;
+ }
+ if (guild) guild.deleted = true;
+
+ return { guild };
+ }
+
+ scheduleForDeletion(id) {
+ this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+/**
+ * Emitted whenever a guild becomes unavailable, likely due to a server outage.
+ * @event Client#guildUnavailable
+ * @param {Guild} guild The guild that has become unavailable
+ */
+
+module.exports = GuildDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js b/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js
new file mode 100644
index 0000000..79f55ba
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js
@@ -0,0 +1,17 @@
+const Action = require('./Action');
+
+class GuildEmojiCreateAction extends Action {
+ handle(guild, createdEmoji) {
+ const client = this.client;
+ const emoji = client.dataManager.newEmoji(createdEmoji, guild);
+ return { emoji };
+ }
+}
+
+/**
+ * Emitted whenever a custom emoji is created in a guild.
+ * @event Client#emojiCreate
+ * @param {Emoji} emoji The emoji that was created
+ */
+
+module.exports = GuildEmojiCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js b/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js
new file mode 100644
index 0000000..044679c
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js
@@ -0,0 +1,18 @@
+const Action = require('./Action');
+
+class GuildEmojiDeleteAction extends Action {
+ handle(emoji) {
+ const client = this.client;
+ client.dataManager.killEmoji(emoji);
+ emoji.deleted = true;
+ return { emoji };
+ }
+}
+
+/**
+ * Emitted whenever a custom guild emoji is deleted.
+ * @event Client#emojiDelete
+ * @param {Emoji} emoji The emoji that was deleted
+ */
+
+module.exports = GuildEmojiDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js b/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js
new file mode 100644
index 0000000..ff6ae65
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js
@@ -0,0 +1,17 @@
+const Action = require('./Action');
+
+class GuildEmojiUpdateAction extends Action {
+ handle(oldEmoji, newEmoji) {
+ const emoji = this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
+ return { emoji };
+ }
+}
+
+/**
+ * Emitted whenever a custom guild emoji is updated.
+ * @event Client#emojiUpdate
+ * @param {Emoji} oldEmoji The old emoji
+ * @param {Emoji} newEmoji The new emoji
+ */
+
+module.exports = GuildEmojiUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js b/node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js
new file mode 100644
index 0000000..8656a34
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js
@@ -0,0 +1,38 @@
+const Action = require('./Action');
+
+function mappify(iterable) {
+ const map = new Map();
+ for (const x of iterable) map.set(...x);
+ return map;
+}
+
+class GuildEmojisUpdateAction extends Action {
+ handle(data) {
+ const guild = this.client.guilds.get(data.guild_id);
+ if (!guild || !guild.emojis) return;
+
+ const deletions = mappify(guild.emojis.entries());
+
+ for (const emoji of data.emojis) {
+ // Determine type of emoji event
+ const cachedEmoji = guild.emojis.get(emoji.id);
+ if (cachedEmoji) {
+ deletions.delete(emoji.id);
+ if (!cachedEmoji.equals(emoji, true)) {
+ // Emoji updated
+ this.client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji);
+ }
+ } else {
+ // Emoji added
+ this.client.actions.GuildEmojiCreate.handle(guild, emoji);
+ }
+ }
+
+ for (const emoji of deletions.values()) {
+ // Emoji deleted
+ this.client.actions.GuildEmojiDelete.handle(emoji);
+ }
+ }
+}
+
+module.exports = GuildEmojisUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildMemberGet.js b/node_modules/discord.js/src/client/actions/GuildMemberGet.js
new file mode 100644
index 0000000..ecb7a8f
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildMemberGet.js
@@ -0,0 +1,10 @@
+const Action = require('./Action');
+
+class GuildMemberGetAction extends Action {
+ handle(guild, data) {
+ const member = guild._addMember(data, false);
+ return { member };
+ }
+}
+
+module.exports = GuildMemberGetAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildMemberRemove.js b/node_modules/discord.js/src/client/actions/GuildMemberRemove.js
new file mode 100644
index 0000000..6682b63
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildMemberRemove.js
@@ -0,0 +1,41 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildMemberRemoveAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ let member = null;
+ if (guild) {
+ member = guild.members.get(data.user.id);
+ guild.memberCount--;
+ if (member) {
+ guild._removeMember(member);
+ this.deleted.set(guild.id + data.user.id, member);
+ if (client.status === Constants.Status.READY) client.emit(Constants.Events.GUILD_MEMBER_REMOVE, member);
+ this.scheduleForDeletion(guild.id, data.user.id);
+ } else {
+ member = this.deleted.get(guild.id + data.user.id) || null;
+ }
+ if (member) member.deleted = true;
+ }
+ return { guild, member };
+ }
+
+ scheduleForDeletion(guildID, userID) {
+ this.client.setTimeout(() => this.deleted.delete(guildID + userID), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+/**
+ * Emitted whenever a member leaves a guild, or is kicked.
+ * @event Client#guildMemberRemove
+ * @param {GuildMember} member The member that has left/been kicked from the guild
+ */
+
+module.exports = GuildMemberRemoveAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildRoleCreate.js b/node_modules/discord.js/src/client/actions/GuildRoleCreate.js
new file mode 100644
index 0000000..b4f320f
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRoleCreate.js
@@ -0,0 +1,26 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const Role = require('../../structures/Role');
+
+class GuildRoleCreate extends Action {
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ let role;
+ if (guild) {
+ const already = guild.roles.has(data.role.id);
+ role = new Role(guild, data.role);
+ guild.roles.set(role.id, role);
+ if (!already) client.emit(Constants.Events.GUILD_ROLE_CREATE, role);
+ }
+ return { role };
+ }
+}
+
+/**
+ * Emitted whenever a role is created.
+ * @event Client#roleCreate
+ * @param {Role} role The role that was created
+ */
+
+module.exports = GuildRoleCreate;
diff --git a/node_modules/discord.js/src/client/actions/GuildRoleDelete.js b/node_modules/discord.js/src/client/actions/GuildRoleDelete.js
new file mode 100644
index 0000000..deafc85
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRoleDelete.js
@@ -0,0 +1,42 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildRoleDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ let role;
+
+ if (guild) {
+ role = guild.roles.get(data.role_id);
+ if (role) {
+ guild.roles.delete(data.role_id);
+ this.deleted.set(guild.id + data.role_id, role);
+ this.scheduleForDeletion(guild.id, data.role_id);
+ client.emit(Constants.Events.GUILD_ROLE_DELETE, role);
+ } else {
+ role = this.deleted.get(guild.id + data.role_id) || null;
+ }
+ if (role) role.deleted = true;
+ }
+
+ return { role };
+ }
+
+ scheduleForDeletion(guildID, roleID) {
+ this.client.setTimeout(() => this.deleted.delete(guildID + roleID), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+/**
+ * Emitted whenever a guild role is deleted.
+ * @event Client#roleDelete
+ * @param {Role} role The role that was deleted
+ */
+
+module.exports = GuildRoleDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js b/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js
new file mode 100644
index 0000000..7d127ab
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js
@@ -0,0 +1,41 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const Util = require('../../util/Util');
+
+class GuildRoleUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+
+ if (guild) {
+ const roleData = data.role;
+ let oldRole = null;
+
+ const role = guild.roles.get(roleData.id);
+ if (role) {
+ oldRole = Util.cloneObject(role);
+ role.setup(data.role);
+ client.emit(Constants.Events.GUILD_ROLE_UPDATE, oldRole, role);
+ }
+
+ return {
+ old: oldRole,
+ updated: role,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a guild role is updated.
+ * @event Client#roleUpdate
+ * @param {Role} oldRole The role before the update
+ * @param {Role} newRole The role after the update
+ */
+
+module.exports = GuildRoleUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js b/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js
new file mode 100644
index 0000000..9094965
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js
@@ -0,0 +1,19 @@
+const Action = require('./Action');
+
+class GuildRolesPositionUpdate extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ for (const partialRole of data.roles) {
+ const role = guild.roles.get(partialRole.id);
+ if (role) role.position = partialRole.position;
+ }
+ }
+
+ return { guild };
+ }
+}
+
+module.exports = GuildRolesPositionUpdate;
diff --git a/node_modules/discord.js/src/client/actions/GuildSync.js b/node_modules/discord.js/src/client/actions/GuildSync.js
new file mode 100644
index 0000000..3d3a47b
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildSync.js
@@ -0,0 +1,29 @@
+const Action = require('./Action');
+
+class GuildSync extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.id);
+ if (guild) {
+ if (data.presences) {
+ for (const presence of data.presences) guild._setPresence(presence.user.id, presence);
+ }
+
+ if (data.members) {
+ for (const syncMember of data.members) {
+ const member = guild.members.get(syncMember.user.id);
+ if (member) {
+ guild._updateMember(member, syncMember);
+ } else {
+ guild._addMember(syncMember, false);
+ }
+ }
+ }
+
+ if ('large' in data) guild.large = data.large;
+ }
+ }
+}
+
+module.exports = GuildSync;
diff --git a/node_modules/discord.js/src/client/actions/GuildUpdate.js b/node_modules/discord.js/src/client/actions/GuildUpdate.js
new file mode 100644
index 0000000..6806d1c
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildUpdate.js
@@ -0,0 +1,34 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const Util = require('../../util/Util');
+
+class GuildUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.id);
+ if (guild) {
+ const oldGuild = Util.cloneObject(guild);
+ guild.setup(data);
+ client.emit(Constants.Events.GUILD_UPDATE, oldGuild, guild);
+ return {
+ old: oldGuild,
+ updated: guild,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a guild is updated - e.g. name change.
+ * @event Client#guildUpdate
+ * @param {Guild} oldGuild The guild before the update
+ * @param {Guild} newGuild The guild after the update
+ */
+
+module.exports = GuildUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/InviteCreate.js b/node_modules/discord.js/src/client/actions/InviteCreate.js
new file mode 100644
index 0000000..36e8cf1
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/InviteCreate.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const Action = require('./Action');
+const Invite = require('../../structures/Invite');
+const { Events } = require('../../util/Constants');
+
+class InviteCreateAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ const channel = client.channels.get(data.channel_id);
+ if (guild && channel) {
+ const inviteData = Object.assign(data, { guild, channel });
+ const invite = new Invite(client, inviteData);
+ /**
+ * Emitted when an invite is created.
+ * <info> This event only triggers if the client has `MANAGE_GUILD` permissions for the guild,
+ * or `MANAGE_CHANNEL` permissions for the channel.</info>
+ * @event Client#inviteCreate
+ * @param {Invite} invite The invite that was created
+ */
+ client.emit(Events.INVITE_CREATE, invite);
+ return { invite };
+ }
+ return { invite: null };
+ }
+}
+
+module.exports = InviteCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/InviteDelete.js b/node_modules/discord.js/src/client/actions/InviteDelete.js
new file mode 100644
index 0000000..a25eb88
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/InviteDelete.js
@@ -0,0 +1,25 @@
+const Action = require('./Action');
+const Invite = require('../../structures/Invite');
+const { Events } = require('../../util/Constants');
+
+class InviteDeleteAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ const channel = client.channels.get(data.channel_id);
+ if (guild && channel) {
+ const inviteData = Object.assign(data, { guild, channel });
+ const invite = new Invite(client, inviteData);
+ /**
+ * Emitted when an invite is deleted.
+ * <info> This event only triggers if the client has `MANAGE_GUILD` permissions for the guild,
+ * or `MANAGE_CHANNEL` permissions for the channel.</info>
+ * @event Client#inviteDelete
+ * @param {Invite} invite The invite that was deleted
+ */
+ client.emit(Events.INVITE_DELETE, invite);
+ }
+ }
+}
+
+module.exports = InviteDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageCreate.js b/node_modules/discord.js/src/client/actions/MessageCreate.js
new file mode 100644
index 0000000..532f80c
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageCreate.js
@@ -0,0 +1,53 @@
+const Action = require('./Action');
+const Message = require('../../structures/Message');
+
+class MessageCreateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const channel = client.channels.get((data instanceof Array ? data[0] : data).channel_id);
+ const user = client.users.get((data instanceof Array ? data[0] : data).author.id);
+ if (channel) {
+ const member = channel.guild ? channel.guild.member(user) : null;
+ if (data instanceof Array) {
+ const messages = new Array(data.length);
+ for (let i = 0; i < data.length; i++) {
+ messages[i] = channel._cacheMessage(new Message(channel, data[i], client));
+ }
+ const lastMessage = messages[messages.length - 1];
+ channel.lastMessageID = lastMessage.id;
+ if (user) {
+ user.lastMessageID = lastMessage.id;
+ user.lastMessage = lastMessage;
+ }
+ if (member) {
+ member.lastMessageID = lastMessage.id;
+ member.lastMessage = lastMessage;
+ }
+ return {
+ messages,
+ };
+ } else {
+ const message = channel._cacheMessage(new Message(channel, data, client));
+ channel.lastMessageID = data.id;
+ if (user) {
+ user.lastMessageID = data.id;
+ user.lastMessage = message;
+ }
+ if (member) {
+ member.lastMessageID = data.id;
+ member.lastMessage = message;
+ }
+ return {
+ message,
+ };
+ }
+ }
+
+ return {
+ message: null,
+ };
+ }
+}
+
+module.exports = MessageCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageDelete.js b/node_modules/discord.js/src/client/actions/MessageDelete.js
new file mode 100644
index 0000000..9dc0828
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageDelete.js
@@ -0,0 +1,35 @@
+const Action = require('./Action');
+
+class MessageDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+ const channel = client.channels.get(data.channel_id);
+ let message;
+
+ if (channel) {
+ message = channel.messages.get(data.id);
+ if (message) {
+ channel.messages.delete(message.id);
+ this.deleted.set(channel.id + message.id, message);
+ this.scheduleForDeletion(channel.id, message.id);
+ } else {
+ message = this.deleted.get(channel.id + data.id) || null;
+ }
+ if (message) message.deleted = true;
+ }
+
+ return { message };
+ }
+
+ scheduleForDeletion(channelID, messageID) {
+ this.client.setTimeout(() => this.deleted.delete(channelID + messageID),
+ this.client.options.restWsBridgeTimeout);
+ }
+}
+
+module.exports = MessageDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js b/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js
new file mode 100644
index 0000000..7ee2455
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js
@@ -0,0 +1,26 @@
+const Action = require('./Action');
+const Collection = require('../../util/Collection');
+const Constants = require('../../util/Constants');
+
+class MessageDeleteBulkAction extends Action {
+ handle(data) {
+ const messages = new Collection();
+ const channel = this.client.channels.get(data.channel_id);
+
+ if (channel) {
+ for (const id of data.ids) {
+ const message = channel.messages.get(id);
+ if (message) {
+ message.deleted = true;
+ messages.set(message.id, message);
+ channel.messages.delete(id);
+ }
+ }
+ }
+
+ if (messages.size > 0) this.client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages);
+ return { messages };
+ }
+}
+
+module.exports = MessageDeleteBulkAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionAdd.js b/node_modules/discord.js/src/client/actions/MessageReactionAdd.js
new file mode 100644
index 0000000..d962953
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionAdd.js
@@ -0,0 +1,37 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+/*
+{ user_id: 'id',
+ message_id: 'id',
+ emoji: { name: '�', id: null },
+ channel_id: 'id' } }
+*/
+
+class MessageReactionAdd extends Action {
+ handle(data) {
+ const user = this.client.users.get(data.user_id);
+ if (!user) return false;
+ // Verify channel
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+ // Verify message
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+ if (!data.emoji) return false;
+ // Verify reaction
+ const reaction = message._addReaction(data.emoji, user);
+ if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_ADD, reaction, user);
+
+ return { message, reaction, user };
+ }
+}
+
+/**
+ * Emitted whenever a reaction is added to a cached message.
+ * @event Client#messageReactionAdd
+ * @param {MessageReaction} messageReaction The reaction object
+ * @param {User} user The user that applied the emoji or reaction emoji
+ */
+
+module.exports = MessageReactionAdd;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionRemove.js b/node_modules/discord.js/src/client/actions/MessageReactionRemove.js
new file mode 100644
index 0000000..2403c3b
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionRemove.js
@@ -0,0 +1,37 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+/*
+{ user_id: 'id',
+ message_id: 'id',
+ emoji: { name: '�', id: null },
+ channel_id: 'id' } }
+*/
+
+class MessageReactionRemove extends Action {
+ handle(data) {
+ const user = this.client.users.get(data.user_id);
+ if (!user) return false;
+ // Verify channel
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+ // Verify message
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+ if (!data.emoji) return false;
+ // Verify reaction
+ const reaction = message._removeReaction(data.emoji, user);
+ if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE, reaction, user);
+
+ return { message, reaction, user };
+ }
+}
+
+/**
+ * Emitted whenever a reaction is removed from a cached message.
+ * @event Client#messageReactionRemove
+ * @param {MessageReaction} messageReaction The reaction object
+ * @param {User} user The user whose emoji or reaction emoji was removed
+ */
+
+module.exports = MessageReactionRemove;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js b/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js
new file mode 100644
index 0000000..e365646
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js
@@ -0,0 +1,25 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class MessageReactionRemoveAll extends Action {
+ handle(data) {
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+
+ message._clearReactions();
+ this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_ALL, message);
+
+ return { message };
+ }
+}
+
+/**
+ * Emitted whenever all reactions are removed from a cached message.
+ * @event Client#messageReactionRemoveAll
+ * @param {Message} message The message the reactions were removed from
+ */
+
+module.exports = MessageReactionRemoveAll;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionRemoveEmoji.js b/node_modules/discord.js/src/client/actions/MessageReactionRemoveEmoji.js
new file mode 100644
index 0000000..12b4445
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionRemoveEmoji.js
@@ -0,0 +1,27 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class MessageReactionRemoveEmoji extends Action {
+ handle(data) {
+ // Verify channel
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+ // Verify message
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+ if (!data.emoji) return false;
+ // Verify reaction
+ const reaction = message._removeReaction(data.emoji);
+ if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_EMOJI, reaction);
+
+ return { message, reaction };
+ }
+}
+
+/**
+ * Emitted whenever a reaction emoji is removed from a cached message.
+ * @event Client#messageReactionRemoveEmoji
+ * @param {MessageReaction} messageReaction The reaction object
+ */
+
+module.exports = MessageReactionRemoveEmoji;
diff --git a/node_modules/discord.js/src/client/actions/MessageUpdate.js b/node_modules/discord.js/src/client/actions/MessageUpdate.js
new file mode 100644
index 0000000..4e0392d
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageUpdate.js
@@ -0,0 +1,40 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class MessageUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const channel = client.channels.get(data.channel_id);
+ if (channel) {
+ const message = channel.messages.get(data.id);
+ if (message) {
+ message.patch(data);
+ client.emit(Constants.Events.MESSAGE_UPDATE, message._edits[0], message);
+ return {
+ old: message._edits[0],
+ updated: message,
+ };
+ }
+
+ return {
+ old: message,
+ updated: message,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a message is updated - e.g. embed or content change.
+ * @event Client#messageUpdate
+ * @param {Message} oldMessage The message before the update
+ * @param {Message} newMessage The message after the update
+ */
+
+module.exports = MessageUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/UserGet.js b/node_modules/discord.js/src/client/actions/UserGet.js
new file mode 100644
index 0000000..0ee85ae
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/UserGet.js
@@ -0,0 +1,11 @@
+const Action = require('./Action');
+
+class UserGetAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const user = client.dataManager.newUser(data);
+ return { user };
+ }
+}
+
+module.exports = UserGetAction;
diff --git a/node_modules/discord.js/src/client/actions/UserNoteUpdate.js b/node_modules/discord.js/src/client/actions/UserNoteUpdate.js
new file mode 100644
index 0000000..4c2cc21
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/UserNoteUpdate.js
@@ -0,0 +1,30 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class UserNoteUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const oldNote = client.user.notes.get(data.id);
+ const note = data.note.length ? data.note : null;
+
+ client.user.notes.set(data.id, note);
+
+ client.emit(Constants.Events.USER_NOTE_UPDATE, data.id, oldNote, note);
+
+ return {
+ old: oldNote,
+ updated: note,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a note is updated.
+ * @event Client#userNoteUpdate
+ * @param {User} user The user the note belongs to
+ * @param {string} oldNote The note content before the update
+ * @param {string} newNote The note content after the update
+ */
+
+module.exports = UserNoteUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/UserUpdate.js b/node_modules/discord.js/src/client/actions/UserUpdate.js
new file mode 100644
index 0000000..6b917d1
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/UserUpdate.js
@@ -0,0 +1,33 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const Util = require('../../util/Util');
+
+class UserUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ if (client.user) {
+ if (client.user.equals(data)) {
+ return {
+ old: client.user,
+ updated: client.user,
+ };
+ }
+
+ const oldUser = Util.cloneObject(client.user);
+ client.user.patch(data);
+ client.emit(Constants.Events.USER_UPDATE, oldUser, client.user);
+ return {
+ old: oldUser,
+ updated: client.user,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+module.exports = UserUpdateAction;
diff --git a/node_modules/discord.js/src/client/rest/APIRequest.js b/node_modules/discord.js/src/client/rest/APIRequest.js
new file mode 100644
index 0000000..3492fa7
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/APIRequest.js
@@ -0,0 +1,52 @@
+const snekfetch = require('snekfetch');
+const Constants = require('../../util/Constants');
+
+class APIRequest {
+ constructor(rest, method, path, auth, data, files, reason) {
+ this.rest = rest;
+ this.client = rest.client;
+ this.method = method;
+ this.path = path.toString();
+ this.auth = auth;
+ this.data = data;
+ this.files = files;
+ this.route = this.getRoute(this.path);
+ this.reason = reason;
+ }
+
+ getRoute(url) {
+ let route = url.split('?')[0];
+ if (route.includes('/channels/') || route.includes('/guilds/')) {
+ const startInd = route.includes('/channels/') ? route.indexOf('/channels/') : route.indexOf('/guilds/');
+ const majorID = route.substring(startInd).split('/')[2];
+ route = route.replace(/(\d{8,})/g, ':id').replace(':id', majorID);
+ }
+ return route;
+ }
+
+ getAuth() {
+ if (this.client.token && this.client.user && this.client.user.bot) {
+ return `Bot ${this.client.token}`;
+ } else if (this.client.token) {
+ return this.client.token;
+ }
+ throw new Error(Constants.Errors.NO_TOKEN);
+ }
+
+ gen() {
+ const API = `${this.client.options.http.host}/api/v${this.client.options.http.version}`;
+ const request = snekfetch[this.method](`${API}${this.path}`);
+ if (this.auth) request.set('Authorization', this.getAuth());
+ if (this.reason) request.set('X-Audit-Log-Reason', encodeURIComponent(this.reason));
+ if (!this.rest.client.browser) request.set('User-Agent', this.rest.userAgentManager.userAgent);
+ if (this.files) {
+ for (const file of this.files) if (file && file.file) request.attach(file.name, file.file, file.name);
+ if (typeof this.data !== 'undefined') request.attach('payload_json', JSON.stringify(this.data));
+ } else if (this.data) {
+ request.send(this.data);
+ }
+ return request;
+ }
+}
+
+module.exports = APIRequest;
diff --git a/node_modules/discord.js/src/client/rest/DiscordAPIError.js b/node_modules/discord.js/src/client/rest/DiscordAPIError.js
new file mode 100644
index 0000000..f5f8336
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/DiscordAPIError.js
@@ -0,0 +1,60 @@
+/**
+ * Represents an error from the Discord API.
+ * @extends Error
+ */
+class DiscordAPIError extends Error {
+ constructor(path, error, method) {
+ super();
+ const flattened = this.constructor.flattenErrors(error.errors || error).join('\n');
+ this.name = 'DiscordAPIError';
+ this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message || flattened;
+
+ /**
+ * The path of the request relative to the HTTP endpoint
+ * @type {string}
+ */
+ this.path = path;
+
+ /**
+ * HTTP error code returned by Discord
+ * @type {number}
+ */
+ this.code = error.code;
+
+ /**
+ * The HTTP method used for the request
+ * @type {string}
+ */
+ this.method = method;
+ }
+
+ /**
+ * Flattens an errors object returned from the API into an array.
+ * @param {Object} obj Discord errors object
+ * @param {string} [key] Used internally to determine key names of nested fields
+ * @returns {string[]}
+ * @private
+ */
+ static flattenErrors(obj, key = '') {
+ let messages = [];
+
+ for (const k of Object.keys(obj)) {
+ if (k === 'message') continue;
+ const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;
+
+ if (obj[k]._errors) {
+ messages.push(`${newKey}: ${obj[k]._errors.map(e => e.message).join(' ')}`);
+ } else if (obj[k].code || obj[k].message) {
+ messages.push(`${obj[k].code ? `${obj[k].code}: ` : ''}: ${obj[k].message}`.trim());
+ } else if (typeof obj[k] === 'string') {
+ messages.push(obj[k]);
+ } else {
+ messages = messages.concat(this.flattenErrors(obj[k], newKey));
+ }
+ }
+
+ return messages;
+ }
+}
+
+module.exports = DiscordAPIError;
diff --git a/node_modules/discord.js/src/client/rest/RESTManager.js b/node_modules/discord.js/src/client/rest/RESTManager.js
new file mode 100644
index 0000000..ee64712
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RESTManager.js
@@ -0,0 +1,58 @@
+const UserAgentManager = require('./UserAgentManager');
+const RESTMethods = require('./RESTMethods');
+const SequentialRequestHandler = require('./RequestHandlers/Sequential');
+const BurstRequestHandler = require('./RequestHandlers/Burst');
+const APIRequest = require('./APIRequest');
+const Constants = require('../../util/Constants');
+
+class RESTManager {
+ constructor(client) {
+ this.client = client;
+ this.handlers = {};
+ this.userAgentManager = new UserAgentManager(this);
+ this.methods = new RESTMethods(this);
+ this.rateLimitedEndpoints = {};
+ this.globallyRateLimited = false;
+ }
+
+ destroy() {
+ for (const handlerKey of Object.keys(this.handlers)) {
+ const handler = this.handlers[handlerKey];
+ if (handler.destroy) handler.destroy();
+ }
+ }
+
+ push(handler, apiRequest) {
+ return new Promise((resolve, reject) => {
+ handler.push({
+ request: apiRequest,
+ resolve,
+ reject,
+ retries: 0,
+ });
+ });
+ }
+
+ getRequestHandler() {
+ switch (this.client.options.apiRequestMethod) {
+ case 'sequential':
+ return SequentialRequestHandler;
+ case 'burst':
+ return BurstRequestHandler;
+ default:
+ throw new Error(Constants.Errors.INVALID_RATE_LIMIT_METHOD);
+ }
+ }
+
+ makeRequest(method, url, auth, data, file, reason) {
+ const apiRequest = new APIRequest(this, method, url, auth, data, file, reason);
+ if (!this.handlers[apiRequest.route]) {
+ const RequestHandlerType = this.getRequestHandler();
+ this.handlers[apiRequest.route] = new RequestHandlerType(this, apiRequest.route);
+ }
+
+ return this.push(this.handlers[apiRequest.route], apiRequest);
+ }
+}
+
+module.exports = RESTManager;
diff --git a/node_modules/discord.js/src/client/rest/RESTMethods.js b/node_modules/discord.js/src/client/rest/RESTMethods.js
new file mode 100644
index 0000000..59f3c65
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RESTMethods.js
@@ -0,0 +1,1067 @@
+const querystring = require('querystring');
+const long = require('long');
+const Permissions = require('../../util/Permissions');
+const Constants = require('../../util/Constants');
+const Endpoints = Constants.Endpoints;
+const Collection = require('../../util/Collection');
+const Util = require('../../util/Util');
+const resolvePermissions = require('../../structures/shared/resolvePermissions');
+
+const RichEmbed = require('../../structures/RichEmbed');
+const User = require('../../structures/User');
+const GuildMember = require('../../structures/GuildMember');
+const Message = require('../../structures/Message');
+const Role = require('../../structures/Role');
+const Invite = require('../../structures/Invite');
+const Webhook = require('../../structures/Webhook');
+const UserProfile = require('../../structures/UserProfile');
+const OAuth2Application = require('../../structures/OAuth2Application');
+const Channel = require('../../structures/Channel');
+const GroupDMChannel = require('../../structures/GroupDMChannel');
+const Guild = require('../../structures/Guild');
+const VoiceRegion = require('../../structures/VoiceRegion');
+const GuildAuditLogs = require('../../structures/GuildAuditLogs');
+
+const MessageFlags = require('../../util/MessageFlags');
+
+class RESTMethods {
+ constructor(restManager) {
+ this.rest = restManager;
+ this.client = restManager.client;
+ this._ackToken = null;
+ }
+
+ login(token = this.client.token) {
+ return new Promise((resolve, reject) => {
+ if (!token || typeof token !== 'string') throw new Error(Constants.Errors.INVALID_TOKEN);
+ token = token.replace(/^Bot\s*/i, '');
+ this.client.manager.connectToWebSocket(token, resolve, reject);
+ }).catch(e => {
+ this.client.destroy();
+ return Promise.reject(e);
+ });
+ }
+
+ logout() {
+ return this.rest.makeRequest('post', Endpoints.logout, true, {});
+ }
+
+ getGateway(bot = false) {
+ return this.rest.makeRequest('get', bot ? Endpoints.gateway.bot : Endpoints.gateway, true);
+ }
+
+ fetchVoiceRegions(guildID) {
+ let endpoint;
+ if (guildID) endpoint = Endpoints.Guild(guildID).voiceRegions;
+ else endpoint = Endpoints.voiceRegions;
+ return this.rest.makeRequest('get', endpoint, true).then(res => {
+ const regions = new Collection();
+ for (const region of res) regions.set(region.id, new VoiceRegion(region));
+ return regions;
+ });
+ }
+
+ fetchEmbed(guildID) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guildID).embed, true).then(data => ({
+ enabled: data.enabled,
+ channel: data.channel_id ? this.client.channels.get(data.channel_id) : null,
+ }));
+ }
+
+ sendMessage(channel, content, { tts, nonce, embed, disableEveryone, split, code, reply } = {}, files = null) {
+ return new Promise((resolve, reject) => { // eslint-disable-line complexity
+ if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content);
+
+ // The nonce has to be a uint64 :<
+ if (typeof nonce !== 'undefined') {
+ nonce = parseInt(nonce);
+ if (isNaN(nonce) || nonce < 0) throw new RangeError('Message nonce must fit in an unsigned 64-bit integer.');
+ }
+
+ if (content) {
+ if (split && typeof split !== 'object') split = {};
+
+ // Wrap everything in a code block
+ if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
+ content = Util.escapeMarkdown(this.client.resolver.resolveString(content), true);
+ content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
+ if (split) {
+ split.prepend = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n`;
+ split.append = '\n```';
+ }
+ }
+
+ // Add zero-width spaces to @everyone/@here
+ if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) {
+ content = content.replace(/@(everyone|here)/g, '@\u200b$1');
+ }
+
+ // Add the reply prefix
+ if (reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') {
+ const id = this.client.resolver.resolveUserID(reply);
+ const mention = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`;
+ content = `${mention}${content ? `, ${content}` : ''}`;
+ if (split) split.prepend = `${mention}, ${split.prepend || ''}`;
+ }
+
+ // Split the content
+ if (split) content = Util.splitMessage(content, split);
+ } else if (reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') {
+ const id = this.client.resolver.resolveUserID(reply);
+ content = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`;
+ }
+
+ const send = chan => {
+ if (content instanceof Array) {
+ const messages = [];
+ (function sendChunk(list, index) {
+ const options = index === list.length - 1 ? { tts, embed, files } : { tts };
+ chan.send(list[index], options).then(message => {
+ messages.push(message);
+ if (index >= list.length - 1) return resolve(messages);
+ return sendChunk(list, ++index);
+ }).catch(reject);
+ }(content, 0));
+ } else {
+ this.rest.makeRequest('post', Endpoints.Channel(chan).messages, true, {
+ content, tts, nonce, embed,
+ }, files).then(data => resolve(this.client.actions.MessageCreate.handle(data).message), reject);
+ }
+ };
+
+ if (channel instanceof User || channel instanceof GuildMember) this.createDM(channel).then(send, reject);
+ else send(channel);
+ });
+ }
+
+ updateMessage(message, content, { flags, embed, code, reply } = {}) {
+ if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content);
+
+ if (typeof flags !== 'undefined') flags = MessageFlags.resolve(flags);
+
+ // Wrap everything in a code block
+ if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
+ content = Util.escapeMarkdown(this.client.resolver.resolveString(content), true);
+ content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
+ }
+
+ // Add the reply prefix
+ if (reply && message.channel.type !== 'dm') {
+ const id = this.client.resolver.resolveUserID(reply);
+ const mention = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`;
+ content = `${mention}${content ? `, ${content}` : ''}`;
+ }
+
+ if (embed instanceof RichEmbed) embed = embed.toJSON();
+
+ return this.rest.makeRequest('patch', Endpoints.Message(message), true, {
+ content, embed, flags,
+ }).then(data => this.client.actions.MessageUpdate.handle(data).updated);
+ }
+
+ deleteMessage(message) {
+ return this.rest.makeRequest('delete', Endpoints.Message(message), true)
+ .then(() =>
+ this.client.actions.MessageDelete.handle({
+ id: message.id,
+ channel_id: message.channel.id,
+ }).message
+ );
+ }
+
+ ackMessage(message) {
+ return this.rest.makeRequest('post', Endpoints.Message(message).ack, true, { token: this._ackToken }).then(res => {
+ if (res.token) this._ackToken = res.token;
+ return message;
+ });
+ }
+
+ ackTextChannel(channel) {
+ return this.rest.makeRequest('post', Endpoints.Channel(channel).Message(channel.lastMessageID).ack, true, {
+ token: this._ackToken,
+ }).then(res => {
+ if (res.token) this._ackToken = res.token;
+ return channel;
+ });
+ }
+
+ ackGuild(guild) {
+ return this.rest.makeRequest('post', Endpoints.Guild(guild).ack, true).then(() => guild);
+ }
+
+ bulkDeleteMessages(channel, messages) {
+ return this.rest.makeRequest('post', Endpoints.Channel(channel).messages.bulkDelete, true, {
+ messages: messages,
+ }).then(() =>
+ this.client.actions.MessageDeleteBulk.handle({
+ channel_id: channel.id,
+ ids: messages,
+ }).messages
+ );
+ }
+
+ search(target, options) {
+ if (typeof options === 'string') options = { content: options };
+ if (options.before) {
+ if (!(options.before instanceof Date)) options.before = new Date(options.before);
+ options.maxID = long.fromNumber(options.before.getTime() - 14200704e5).shiftLeft(22).toString();
+ }
+ if (options.after) {
+ if (!(options.after instanceof Date)) options.after = new Date(options.after);
+ options.minID = long.fromNumber(options.after.getTime() - 14200704e5).shiftLeft(22).toString();
+ }
+ if (options.during) {
+ if (!(options.during instanceof Date)) options.during = new Date(options.during);
+ const t = options.during.getTime() - 14200704e5;
+ options.minID = long.fromNumber(t).shiftLeft(22).toString();
+ options.maxID = long.fromNumber(t + 86400000).shiftLeft(22).toString();
+ }
+ if (options.channel) options.channel = this.client.resolver.resolveChannelID(options.channel);
+ if (options.author) options.author = this.client.resolver.resolveUserID(options.author);
+ if (options.mentions) options.mentions = this.client.resolver.resolveUserID(options.options.mentions);
+ options = {
+ content: options.content,
+ max_id: options.maxID,
+ min_id: options.minID,
+ has: options.has,
+ channel_id: options.channel,
+ author_id: options.author,
+ author_type: options.authorType,
+ context_size: options.contextSize,
+ sort_by: options.sortBy,
+ sort_order: options.sortOrder,
+ limit: options.limit,
+ offset: options.offset,
+ mentions: options.mentions,
+ mentions_everyone: options.mentionsEveryone,
+ link_hostname: options.linkHostname,
+ embed_provider: options.embedProvider,
+ embed_type: options.embedType,
+ attachment_filename: options.attachmentFilename,
+ attachment_extension: options.attachmentExtension,
+ include_nsfw: options.nsfw,
+ };
+
+ for (const key of Object.keys(options)) if (options[key] === undefined) delete options[key];
+ const queryString = (querystring.stringify(options).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
+
+ let endpoint;
+ if (target instanceof Channel) {
+ endpoint = Endpoints.Channel(target).search;
+ } else if (target instanceof Guild) {
+ endpoint = Endpoints.Guild(target).search;
+ } else {
+ throw new TypeError('Target must be a TextChannel, DMChannel, GroupDMChannel, or Guild.');
+ }
+ return this.rest.makeRequest('get', `${endpoint}?${queryString}`, true).then(body => {
+ const messages = body.messages.map(x =>
+ x.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))
+ );
+ return {
+ totalResults: body.total_results,
+ messages,
+ };
+ });
+ }
+
+ createChannel(guild, name, options) {
+ const {
+ type,
+ topic,
+ nsfw,
+ bitrate,
+ userLimit,
+ parent,
+ permissionOverwrites,
+ position,
+ rateLimitPerUser,
+ reason,
+ } = options;
+ return this.rest.makeRequest('post', Endpoints.Guild(guild).channels, true, {
+ name,
+ topic,
+ type: type ? Constants.ChannelTypes[type.toUpperCase()] : Constants.ChannelTypes.TEXT,
+ nsfw,
+ bitrate,
+ user_limit: userLimit,
+ parent_id: parent instanceof Channel ? parent.id : parent,
+ permission_overwrites: resolvePermissions.call(this, permissionOverwrites, guild),
+ position,
+ rate_limit_per_user: rateLimitPerUser,
+ },
+ undefined,
+ reason).then(data => this.client.actions.ChannelCreate.handle(data).channel);
+ }
+
+ createDM(recipient) {
+ const dmChannel = this.getExistingDM(recipient);
+ if (dmChannel) return Promise.resolve(dmChannel);
+ return this.rest.makeRequest('post', Endpoints.User(this.client.user).channels, true, {
+ recipient_id: recipient.id,
+ }).then(data => this.client.actions.ChannelCreate.handle(data).channel);
+ }
+
+ createGroupDM(options) {
+ const data = this.client.user.bot ?
+ { access_tokens: options.accessTokens, nicks: options.nicks } :
+ { recipients: options.recipients };
+ return this.rest.makeRequest('post', Endpoints.User('@me').channels, true, data)
+ .then(res => new GroupDMChannel(this.client, res));
+ }
+
+ addUserToGroupDM(channel, options) {
+ const data = this.client.user.bot ?
+ { nick: options.nick, access_token: options.accessToken } :
+ { recipient: options.id };
+ return this.rest.makeRequest('put', Endpoints.Channel(channel).Recipient(options.id), true, data)
+ .then(() => channel);
+ }
+
+ removeUserFromGroupDM(channel, userId) {
+ return this.rest.makeRequest('delete', Endpoints.Channel(channel).Recipient(userId), true)
+ .then(() => channel);
+ }
+
+ updateGroupDMChannel(channel, _data) {
+ const data = {};
+ data.name = _data.name;
+ data.icon = _data.icon;
+ return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data).then(() => channel);
+ }
+
+ getExistingDM(recipient) {
+ return this.client.channels.find(channel =>
+ channel.recipient && channel.recipient.id === recipient.id
+ );
+ }
+
+ deleteChannel(channel, reason) {
+ if (channel instanceof User || channel instanceof GuildMember) channel = this.getExistingDM(channel);
+ if (!channel) return Promise.reject(new Error('No channel to delete.'));
+ return this.rest.makeRequest('delete', Endpoints.Channel(channel), true, undefined, undefined, reason)
+ .then(data => {
+ data.id = channel.id;
+ return this.client.actions.ChannelDelete.handle(data).channel;
+ });
+ }
+
+ updateChannel(channel, _data, reason) {
+ const data = {};
+ data.name = (_data.name || channel.name).trim();
+ data.topic = typeof _data.topic === 'undefined' ? channel.topic : _data.topic;
+ data.nsfw = typeof _data.nsfw === 'undefined' ? channel.nsfw : _data.nsfw;
+ data.position = _data.position || channel.position;
+ data.bitrate = _data.bitrate || (channel.bitrate ? channel.bitrate * 1000 : undefined);
+ data.user_limit = typeof _data.userLimit !== 'undefined' ? _data.userLimit : channel.userLimit;
+ data.parent_id = _data.parent instanceof Channel ? _data.parent.id : _data.parent;
+ data.permission_overwrites = _data.permissionOverwrites ?
+ resolvePermissions.call(this, _data.permissionOverwrites, channel.guild) : undefined;
+ data.rate_limit_per_user = typeof _data.rateLimitPerUser !== 'undefined' ?
+ _data.rateLimitPerUser : channel.rateLimitPerUser;
+ return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data, undefined, reason).then(newData =>
+ this.client.actions.ChannelUpdate.handle(newData).updated
+ );
+ }
+
+ leaveGuild(guild) {
+ if (guild.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.'));
+ return this.rest.makeRequest('delete', Endpoints.User('@me').Guild(guild.id), true).then(() =>
+ this.client.actions.GuildDelete.handle({ id: guild.id }).guild
+ );
+ }
+
+ createGuild(options) {
+ options.icon = this.client.resolver.resolveBase64(options.icon) || null;
+ options.region = options.region || 'us-central';
+ return new Promise((resolve, reject) => {
+ this.rest.makeRequest('post', Endpoints.guilds, true, options).then(data => {
+ if (this.client.guilds.has(data.id)) return resolve(this.client.guilds.get(data.id));
+
+ const handleGuild = guild => {
+ if (guild.id === data.id) {
+ this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild);
+ this.client.clearTimeout(timeout);
+ resolve(guild);
+ }
+ };
+ this.client.on(Constants.Events.GUILD_CREATE, handleGuild);
+
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild);
+ reject(new Error('Took too long to receive guild data.'));
+ }, 10000);
+ return undefined;
+ }, reject);
+ });
+ }
+
+ // Untested but probably will work
+ deleteGuild(guild) {
+ return this.rest.makeRequest('delete', Endpoints.Guild(guild), true).then(() =>
+ this.client.actions.GuildDelete.handle({ id: guild.id }).guild
+ );
+ }
+
+ getUser(userID, cache) {
+ return this.rest.makeRequest('get', Endpoints.User(userID), true).then(data => {
+ if (cache) return this.client.actions.UserGet.handle(data).user;
+ else return new User(this.client, data);
+ });
+ }
+
+ updateCurrentUser(_data, password) {
+ const user = this.client.user;
+ const data = {};
+ data.username = _data.username || user.username;
+ data.avatar = typeof _data.avatar === 'undefined' ? user.avatar : this.client.resolver.resolveBase64(_data.avatar);
+ if (!user.bot) {
+ data.email = _data.email || user.email;
+ data.password = password;
+ if (_data.new_password) data.new_password = _data.newPassword;
+ }
+ return this.rest.makeRequest('patch', Endpoints.User('@me'), true, data).then(newData =>
+ this.client.actions.UserUpdate.handle(newData).updated
+ );
+ }
+
+ updateGuild(guild, data, reason) {
+ return this.rest.makeRequest('patch', Endpoints.Guild(guild), true, data, undefined, reason).then(newData =>
+ this.client.actions.GuildUpdate.handle(newData).updated
+ );
+ }
+
+ kickGuildMember(guild, member, reason) {
+ return this.rest.makeRequest(
+ 'delete', Endpoints.Guild(guild).Member(member), true,
+ undefined, undefined, reason)
+ .then(() => member);
+ }
+
+ createGuildRole(guild, data, reason) {
+ if (data.color) data.color = this.client.resolver.resolveColor(data.color);
+ if (data.permissions) data.permissions = Permissions.resolve(data.permissions);
+ return this.rest.makeRequest('post', Endpoints.Guild(guild).roles, true, data, undefined, reason).then(r => {
+ const { role } = this.client.actions.GuildRoleCreate.handle({
+ guild_id: guild.id,
+ role: r,
+ });
+ if (data.position) return role.setPosition(data.position, reason);
+ return role;
+ });
+ }
+
+ deleteGuildRole(role, reason) {
+ return this.rest.makeRequest(
+ 'delete', Endpoints.Guild(role.guild).Role(role.id), true,
+ undefined, undefined, reason)
+ .then(() =>
+ this.client.actions.GuildRoleDelete.handle({
+ guild_id: role.guild.id,
+ role_id: role.id,
+ }).role
+ );
+ }
+
+ setChannelOverwrite(channel, payload) {
+ return this.rest.makeRequest('put', `${Endpoints.Channel(channel).permissions}/${payload.id}`, true, payload);
+ }
+
+ deletePermissionOverwrites(overwrite, reason) {
+ return this.rest.makeRequest(
+ 'delete', `${Endpoints.Channel(overwrite.channel).permissions}/${overwrite.id}`,
+ true, undefined, undefined, reason
+ ).then(() => overwrite);
+ }
+
+ getChannelMessages(channel, payload = {}) {
+ const params = [];
+ if (payload.limit) params.push(`limit=${payload.limit}`);
+ if (payload.around) params.push(`around=${payload.around}`);
+ else if (payload.before) params.push(`before=${payload.before}`);
+ else if (payload.after) params.push(`after=${payload.after}`);
+
+ let endpoint = Endpoints.Channel(channel).messages;
+ if (params.length > 0) endpoint += `?${params.join('&')}`;
+ return this.rest.makeRequest('get', endpoint, true);
+ }
+
+ getChannelMessage(channel, messageID) {
+ const msg = channel.messages.get(messageID);
+ if (msg) return Promise.resolve(msg);
+ return this.rest.makeRequest('get', Endpoints.Channel(channel).Message(messageID), true);
+ }
+
+ putGuildMember(guild, userID, options) {
+ options.access_token = options.accessToken;
+ if (options.roles) {
+ const roles = options.roles;
+ if (roles instanceof Collection || (roles instanceof Array && roles[0] instanceof Role)) {
+ options.roles = roles.map(role => role.id);
+ }
+ }
+ return this.rest.makeRequest('put', Endpoints.Guild(guild).Member(userID), true, options)
+ .then(data => this.client.actions.GuildMemberGet.handle(guild, data).member);
+ }
+
+ getGuild(guild) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guild), true);
+ }
+
+ getGuildMember(guild, userID, cache) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guild).Member(userID), true).then(data => {
+ if (cache) return this.client.actions.GuildMemberGet.handle(guild, data).member;
+ else return new GuildMember(guild, data);
+ });
+ }
+
+ updateGuildMember(member, data, reason) {
+ if (data.channel) {
+ const channel = this.client.resolver.resolveChannel(data.channel);
+ if (!channel || channel.guild.id !== member.guild.id || channel.type !== 'voice') {
+ return Promise.reject(new Error('Could not resolve channel to a guild voice channel.'));
+ }
+ data.channel_id = channel.id;
+ data.channel = undefined;
+ } else if (data.channel === null) {
+ data.channel_id = null;
+ data.channel = undefined;
+ }
+ if (data.roles) data.roles = [...new Set(data.roles.map(role => role instanceof Role ? role.id : role))];
+
+ let endpoint = Endpoints.Member(member);
+ // Fix your endpoints, discord ;-;
+ if (member.id === this.client.user.id) {
+ const keys = Object.keys(data);
+ if (keys.length === 1 && keys[0] === 'nick') {
+ endpoint = Endpoints.Member(member).nickname;
+ }
+ }
+
+ return this.rest.makeRequest('patch', endpoint, true, data, undefined, reason).then(newData =>
+ member.guild._updateMember(member, newData).mem
+ );
+ }
+
+ addMemberRole(member, role, reason) {
+ return new Promise((resolve, reject) => {
+ if (member._roles.includes(role.id)) return resolve(member);
+
+ const listener = (oldMember, newMember) => {
+ if (newMember.id === member.id && !oldMember._roles.includes(role.id) && newMember._roles.includes(role.id)) {
+ this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ resolve(newMember);
+ }
+ };
+
+ this.client.on(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ reject(new Error('Adding the role timed out.'));
+ }, 10e3);
+
+ return this.rest.makeRequest('put', Endpoints.Member(member).Role(role.id), true, undefined, undefined, reason)
+ .catch(err => {
+ this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ this.client.clearTimeout(timeout);
+ reject(err);
+ });
+ });
+ }
+
+ removeMemberRole(member, role, reason) {
+ return new Promise((resolve, reject) => {
+ if (!member._roles.includes(role.id)) return resolve(member);
+
+ const listener = (oldMember, newMember) => {
+ if (newMember.id === member.id && oldMember._roles.includes(role.id) && !newMember._roles.includes(role.id)) {
+ this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ resolve(newMember);
+ }
+ };
+
+ this.client.on(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ reject(new Error('Removing the role timed out.'));
+ }, 10e3);
+
+ return this.rest.makeRequest('delete', Endpoints.Member(member).Role(role.id), true, undefined, undefined, reason)
+ .catch(err => {
+ this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
+ this.client.clearTimeout(timeout);
+ reject(err);
+ });
+ });
+ }
+
+ sendTyping(channelID) {
+ return this.rest.makeRequest('post', Endpoints.Channel(channelID).typing, true);
+ }
+
+ banGuildMember(guild, member, options) {
+ const id = this.client.resolver.resolveUserID(member);
+ if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.'));
+
+ const url = `${Endpoints.Guild(guild).bans}/${id}?${querystring.stringify(options)}`;
+ return this.rest.makeRequest('put', url, true).then(() => {
+ if (member instanceof GuildMember) return member;
+ const user = this.client.resolver.resolveUser(id);
+ if (user) {
+ member = this.client.resolver.resolveGuildMember(guild, user);
+ return member || user;
+ }
+ return id;
+ });
+ }
+
+ unbanGuildMember(guild, member, reason) {
+ return new Promise((resolve, reject) => {
+ const id = this.client.resolver.resolveUserID(member);
+ if (!id) throw new Error('Couldn\'t resolve the user ID to unban.');
+
+ const listener = (eGuild, eUser) => {
+ if (eGuild.id === guild.id && eUser.id === id) {
+ this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
+ this.client.clearTimeout(timeout);
+ resolve(eUser);
+ }
+ };
+ this.client.on(Constants.Events.GUILD_BAN_REMOVE, listener);
+
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
+ reject(new Error('Took too long to receive the ban remove event.'));
+ }, 10000);
+
+ this.rest.makeRequest('delete', `${Endpoints.Guild(guild).bans}/${id}`, true, undefined, undefined, reason)
+ .catch(err => {
+ this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
+ this.client.clearTimeout(timeout);
+ reject(err);
+ });
+ });
+ }
+
+ getGuildBan(guild, user) {
+ const id = this.client.resolver.resolveUserID(user);
+ return this.rest.makeRequest('get', `${Endpoints.Guild(guild).bans}/${id}`, true).then(ban => ({
+ reason: ban.reason,
+ user: this.client.dataManager.newUser(ban.user),
+ }));
+ }
+
+ getGuildBans(guild) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guild).bans, true).then(bans =>
+ bans.reduce((collection, ban) => {
+ collection.set(ban.user.id, {
+ reason: ban.reason,
+ user: this.client.dataManager.newUser(ban.user),
+ });
+ return collection;
+ }, new Collection())
+ );
+ }
+
+ updateGuildRole(role, _data, reason) {
+ const data = {};
+ data.name = _data.name || role.name;
+ data.position = typeof _data.position !== 'undefined' ? _data.position : role.position;
+ data.color = _data.color === null ? null : this.client.resolver.resolveColor(_data.color || role.color);
+ data.hoist = typeof _data.hoist !== 'undefined' ? _data.hoist : role.hoist;
+ data.mentionable = typeof _data.mentionable !== 'undefined' ? _data.mentionable : role.mentionable;
+
+ if (typeof _data.permissions !== 'undefined') data.permissions = Permissions.resolve(_data.permissions);
+ else data.permissions = role.permissions;
+
+ return this.rest.makeRequest('patch', Endpoints.Guild(role.guild).Role(role.id), true, data, undefined, reason)
+ .then(_role =>
+ this.client.actions.GuildRoleUpdate.handle({
+ role: _role,
+ guild_id: role.guild.id,
+ }).updated
+ );
+ }
+
+ pinMessage(message) {
+ return this.rest.makeRequest('put', Endpoints.Channel(message.channel).Pin(message.id), true)
+ .then(() => message);
+ }
+
+ unpinMessage(message) {
+ return this.rest.makeRequest('delete', Endpoints.Channel(message.channel).Pin(message.id), true)
+ .then(() => message);
+ }
+
+ getChannelPinnedMessages(channel) {
+ return this.rest.makeRequest('get', Endpoints.Channel(channel).pins, true);
+ }
+
+ createChannelInvite(channel, options, reason) {
+ const payload = {};
+ payload.temporary = options.temporary;
+ payload.max_age = options.maxAge;
+ payload.max_uses = options.maxUses;
+ payload.unique = options.unique;
+ return this.rest.makeRequest('post', Endpoints.Channel(channel).invites, true, payload, undefined, reason)
+ .then(invite => new Invite(this.client, invite));
+ }
+
+ deleteInvite(invite, reason) {
+ return this.rest.makeRequest('delete', Endpoints.Invite(invite.code), true, undefined, undefined, reason)
+ .then(() => invite);
+ }
+
+ getInvite(code) {
+ return this.rest.makeRequest('get', Endpoints.Invite(code), true).then(invite =>
+ new Invite(this.client, invite)
+ );
+ }
+
+ getGuildInvites(guild) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guild).invites, true).then(inviteItems => {
+ const invites = new Collection();
+ for (const inviteItem of inviteItems) {
+ const invite = new Invite(this.client, inviteItem);
+ invites.set(invite.code, invite);
+ }
+ return invites;
+ });
+ }
+
+ getGuildVanityCode(guild) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guild).vanityURL, true)
+ .then(res => res.code);
+ }
+
+ pruneGuildMembers(guild, days, dry, reason) {
+ return this.rest.makeRequest(dry ?
+ 'get' :
+ 'post',
+ `${Endpoints.Guild(guild).prune}?days=${days}`, true, undefined, undefined, reason)
+ .then(data => data.pruned);
+ }
+
+ createEmoji(guild, image, name, roles, reason) {
+ const data = { image, name };
+ if (roles) data.roles = roles.map(r => r.id ? r.id : r);
+ return this.rest.makeRequest('post', Endpoints.Guild(guild).emojis, true, data, undefined, reason)
+ .then(emoji => this.client.actions.GuildEmojiCreate.handle(guild, emoji).emoji);
+ }
+
+ updateEmoji(emoji, _data, reason) {
+ const data = {};
+ if (_data.name) data.name = _data.name;
+ if (_data.roles) data.roles = _data.roles.map(r => r.id ? r.id : r);
+ return this.rest.makeRequest('patch', Endpoints.Guild(emoji.guild).Emoji(emoji.id), true, data, undefined, reason)
+ .then(newEmoji => this.client.actions.GuildEmojiUpdate.handle(emoji, newEmoji).emoji);
+ }
+
+ deleteEmoji(emoji, reason) {
+ return this.rest.makeRequest('delete', Endpoints.Guild(emoji.guild).Emoji(emoji.id), true, undefined, reason)
+ .then(() => this.client.actions.GuildEmojiDelete.handle(emoji).emoji);
+ }
+
+ getGuildAuditLogs(guild, options = {}) {
+ if (options.before && options.before instanceof GuildAuditLogs.Entry) options.before = options.before.id;
+ if (options.after && options.after instanceof GuildAuditLogs.Entry) options.after = options.after.id;
+ if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type];
+
+ const queryString = (querystring.stringify({
+ before: options.before,
+ after: options.after,
+ limit: options.limit,
+ user_id: this.client.resolver.resolveUserID(options.user),
+ action_type: options.type,
+ }).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
+
+ return this.rest.makeRequest('get', `${Endpoints.Guild(guild).auditLogs}?${queryString}`, true)
+ .then(data => GuildAuditLogs.build(guild, data));
+ }
+
+ getWebhook(id, token) {
+ return this.rest.makeRequest('get', Endpoints.Webhook(id, token), !token).then(data =>
+ new Webhook(this.client, data)
+ );
+ }
+
+ getGuildWebhooks(guild) {
+ return this.rest.makeRequest('get', Endpoints.Guild(guild).webhooks, true).then(data => {
+ const hooks = new Collection();
+ for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
+ return hooks;
+ });
+ }
+
+ getChannelWebhooks(channel) {
+ return this.rest.makeRequest('get', Endpoints.Channel(channel).webhooks, true).then(data => {
+ const hooks = new Collection();
+ for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
+ return hooks;
+ });
+ }
+
+ createWebhook(channel, name, avatar, reason) {
+ return this.rest.makeRequest('post', Endpoints.Channel(channel).webhooks, true, { name, avatar }, undefined, reason)
+ .then(data => new Webhook(this.client, data));
+ }
+
+ editWebhook(webhook, options, reason) {
+ let endpoint;
+ let auth;
+
+ // Changing the channel of a webhook or specifying a reason requires a bot token
+ if (options.channel_id || reason) {
+ endpoint = Endpoints.Webhook(webhook.id);
+ auth = true;
+ } else {
+ endpoint = Endpoints.Webhook(webhook.id, webhook.token);
+ auth = false;
+ }
+
+ return this.rest.makeRequest('patch', endpoint, auth, options, undefined, reason).then(data => {
+ webhook.name = data.name;
+ webhook.avatar = data.avatar;
+ webhook.channelID = data.channel_id;
+ return webhook;
+ });
+ }
+
+ deleteWebhook(webhook, reason) {
+ return this.rest.makeRequest(
+ 'delete', Endpoints.Webhook(webhook.id, webhook.token),
+ false, undefined, undefined, reason);
+ }
+
+ sendWebhookMessage(webhook, content, { avatarURL, tts, embeds, username } = {}, files = null) {
+ return new Promise((resolve, reject) => {
+ username = username || webhook.name;
+
+ if (content instanceof Array) {
+ const messages = [];
+ (function sendChunk(list, index) {
+ const options = index === list.length - 1 ? { tts, embeds, files } : { tts };
+ webhook.send(list[index], options).then(message => {
+ messages.push(message);
+ if (index >= list.length - 1) return resolve(messages);
+ return sendChunk(list, ++index);
+ }).catch(reject);
+ }(content, 0));
+ } else {
+ this.rest.makeRequest('post', `${Endpoints.Webhook(webhook.id, webhook.token)}?wait=true`, false, {
+ username,
+ avatar_url: avatarURL,
+ content,
+ tts,
+ embeds,
+ }, files).then(data => {
+ if (!this.client.channels) resolve(data);
+ else resolve(this.client.actions.MessageCreate.handle(data).message);
+ }, reject);
+ }
+ });
+ }
+
+ sendSlackWebhookMessage(webhook, body) {
+ return this.rest.makeRequest(
+ 'post', `${Endpoints.Webhook(webhook.id, webhook.token)}/slack?wait=true`, false, body
+ );
+ }
+
+ fetchUserProfile(user) {
+ return this.rest.makeRequest('get', Endpoints.User(user).profile, true).then(data =>
+ new UserProfile(user, data)
+ );
+ }
+
+ fetchMentions(options) {
+ if (options.guild instanceof Guild) options.guild = options.guild.id;
+ Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options);
+
+ return this.rest.makeRequest(
+ 'get', Endpoints.User('@me').Mentions(options.limit, options.roles, options.everyone, options.guild), true
+ ).then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client)));
+ }
+
+ addFriend(user) {
+ return this.rest.makeRequest('post', Endpoints.User('@me'), true, {
+ username: user.username,
+ discriminator: user.discriminator,
+ }).then(() => user);
+ }
+
+ removeFriend(user) {
+ return this.rest.makeRequest('delete', Endpoints.User('@me').Relationship(user.id), true)
+ .then(() => user);
+ }
+
+ blockUser(user) {
+ return this.rest.makeRequest('put', Endpoints.User('@me').Relationship(user.id), true, { type: 2 })
+ .then(() => user);
+ }
+
+ unblockUser(user) {
+ return this.rest.makeRequest('delete', Endpoints.User('@me').Relationship(user.id), true)
+ .then(() => user);
+ }
+
+ updateEmbed(guildID, embed, reason) {
+ return this.rest.makeRequest('patch', Endpoints.Guild(guildID).embed, true, {
+ enabled: embed.enabled,
+ channel_id: this.client.resolver.resolveChannelID(embed.channel),
+ }, undefined, reason);
+ }
+
+ setRolePositions(guildID, roles) {
+ return this.rest.makeRequest('patch', Endpoints.Guild(guildID).roles, true, roles).then(() =>
+ this.client.actions.GuildRolesPositionUpdate.handle({
+ guild_id: guildID,
+ roles,
+ }).guild
+ );
+ }
+
+ setChannelPositions(guildID, channels) {
+ return this.rest.makeRequest('patch', Endpoints.Guild(guildID).channels, true, channels).then(() =>
+ this.client.actions.GuildChannelsPositionUpdate.handle({
+ guild_id: guildID,
+ channels,
+ }).guild
+ );
+ }
+
+ addMessageReaction(message, emoji) {
+ return this.rest.makeRequest(
+ 'put', Endpoints.Message(message).Reaction(emoji).User('@me'), true
+ ).then(() =>
+ message._addReaction(Util.parseEmoji(emoji), message.client.user)
+ );
+ }
+
+ removeMessageReaction(message, emoji, userID) {
+ const endpoint = Endpoints.Message(message).Reaction(emoji).User(userID === this.client.user.id ? '@me' : userID);
+ return this.rest.makeRequest('delete', endpoint, true).then(() =>
+ this.client.actions.MessageReactionRemove.handle({
+ user_id: userID,
+ message_id: message.id,
+ emoji: Util.parseEmoji(emoji),
+ channel_id: message.channel.id,
+ }).reaction
+ );
+ }
+
+ removeMessageReactionEmoji(message, emoji) {
+ const endpoint = Endpoints.Message(message).Reaction(emoji);
+ return this.rest.makeRequest('delete', endpoint, true).then(() =>
+ this.client.actions.MessageReactionRemoveEmoji.handle({
+ message_id: message.id,
+ emoji: Util.parseEmoji(emoji),
+ channel_id: message.channel.id,
+ }).reaction
+ );
+ }
+
+ removeMessageReactions(message) {
+ return this.rest.makeRequest('delete', Endpoints.Message(message).reactions, true)
+ .then(() => message);
+ }
+
+ getMessageReactionUsers(message, emoji, options) {
+ const queryString = (querystring.stringify(options).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
+
+ return this.rest.makeRequest('get', `${Endpoints.Message(message).Reaction(emoji)}?${queryString}`, true);
+ }
+
+ getApplication(id) {
+ return this.rest.makeRequest('get', Endpoints.OAUTH2.Application(id), true).then(app =>
+ new OAuth2Application(this.client, app)
+ );
+ }
+
+ resetApplication(id) {
+ return this.rest.makeRequest('post', Endpoints.OAUTH2.Application(id).resetToken, true)
+ .then(() => this.rest.makeRequest('post', Endpoints.OAUTH2.Application(id).resetSecret, true))
+ .then(app => new OAuth2Application(this.client, app));
+ }
+
+ setNote(user, note) {
+ return this.rest.makeRequest('put', Endpoints.User(user).note, true, { note }).then(() => user);
+ }
+
+ acceptInvite(code) {
+ if (code.id) code = code.id;
+ return new Promise((resolve, reject) =>
+ this.rest.makeRequest('post', Endpoints.Invite(code), true).then(res => {
+ const handler = guild => {
+ if (guild.id === res.id) {
+ resolve(guild);
+ this.client.removeListener(Constants.Events.GUILD_CREATE, handler);
+ }
+ };
+ this.client.on(Constants.Events.GUILD_CREATE, handler);
+ this.client.setTimeout(() => {
+ this.client.removeListener(Constants.Events.GUILD_CREATE, handler);
+ reject(new Error('Accepting invite timed out'));
+ }, 120e3);
+ })
+ );
+ }
+
+ patchUserSettings(data) {
+ return this.rest.makeRequest('patch', Constants.Endpoints.User('@me').settings, true, data);
+ }
+
+ patchClientUserGuildSettings(guildID, data) {
+ return this.rest.makeRequest('patch', Constants.Endpoints.User('@me').Guild(guildID).settings, true, data);
+ }
+
+ getIntegrations(guild) {
+ return this.rest.makeRequest(
+ 'get',
+ Constants.Endpoints.Guild(guild.id).integrations,
+ true
+ );
+ }
+
+ createIntegration(guild, data, reason) {
+ return this.rest.makeRequest(
+ 'post',
+ Constants.Endpoints.Guild(guild.id).integrations,
+ true,
+ data,
+ undefined,
+ reason
+ );
+ }
+
+ syncIntegration(integration) {
+ return this.rest.makeRequest(
+ 'post',
+ Constants.Endpoints.Guild(integration.guild.id).Integration(integration.id),
+ true
+ );
+ }
+
+ editIntegration(integration, data, reason) {
+ return this.rest.makeRequest(
+ 'patch',
+ Constants.Endpoints.Guild(integration.guild.id).Integration(integration.id),
+ true,
+ data,
+ undefined,
+ reason
+ );
+ }
+
+ deleteIntegration(integration, reason) {
+ return this.rest.makeRequest(
+ 'delete',
+ Constants.Endpoints.Guild(integration.guild.id).Integration(integration.id),
+ true,
+ undefined,
+ undefined,
+ reason
+ );
+ }
+}
+
+module.exports = RESTMethods;
diff --git a/node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js b/node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js
new file mode 100644
index 0000000..eceee65
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js
@@ -0,0 +1,90 @@
+const RequestHandler = require('./RequestHandler');
+const DiscordAPIError = require('../DiscordAPIError');
+const { Events: { RATE_LIMIT } } = require('../../../util/Constants');
+
+class BurstRequestHandler extends RequestHandler {
+ constructor(restManager, endpoint) {
+ super(restManager, endpoint);
+
+ this.client = restManager.client;
+
+ this.limit = Infinity;
+ this.resetTime = null;
+ this.remaining = 1;
+ this.timeDifference = 0;
+
+ this.resetTimeout = null;
+ }
+
+ push(request) {
+ super.push(request);
+ this.handle();
+ }
+
+ execute(item) {
+ if (!item) return;
+ item.request.gen().end((err, res) => {
+ if (res && res.headers) {
+ this.limit = Number(res.headers['x-ratelimit-limit']);
+ this.resetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
+ this.remaining = Number(res.headers['x-ratelimit-remaining']);
+ this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
+ }
+ if (err) {
+ if (err.status === 429) {
+ this.queue.unshift(item);
+ if (res.headers['x-ratelimit-global']) this.globalLimit = true;
+ if (this.resetTimeout) return;
+ this.resetTimeout = this.client.setTimeout(() => {
+ this.remaining = this.limit;
+ this.globalLimit = false;
+ this.handle();
+ this.resetTimeout = null;
+ }, Number(res.headers['retry-after']) + this.client.options.restTimeOffset);
+ } else if (err.status >= 500 && err.status < 600) {
+ if (item.retries === this.client.options.retryLimit) {
+ item.reject(err);
+ this.handle();
+ } else {
+ item.retries++;
+ this.queue.unshift(item);
+ this.resetTimeout = this.client.setTimeout(() => {
+ this.handle();
+ this.resetTimeout = null;
+ }, 1e3 + this.client.options.restTimeOffset);
+ }
+ } else {
+ item.reject(err.status >= 400 && err.status < 500 ?
+ new DiscordAPIError(res.request.path, res.body, res.request.method) : err);
+ this.handle();
+ }
+ } else {
+ if (this.remaining === 0) {
+ if (this.client.listenerCount(RATE_LIMIT)) {
+ this.client.emit(RATE_LIMIT, {
+ limit: this.limit,
+ timeDifference: this.timeDifference,
+ path: item.request.path,
+ method: item.request.method,
+ });
+ }
+ }
+ this.globalLimit = false;
+ const data = res && res.body ? res.body : {};
+ item.resolve(data);
+ this.handle();
+ }
+ });
+ }
+
+ handle() {
+ super.handle();
+ if (this.queue.length === 0) return;
+ if ((this.remaining <= 0 || this.globalLimit) && Date.now() - this.timeDifference < this.resetTime) return;
+ this.execute(this.queue.shift());
+ this.remaining--;
+ this.handle();
+ }
+}
+
+module.exports = BurstRequestHandler;
diff --git a/node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js b/node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js
new file mode 100644
index 0000000..c5bad20
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js
@@ -0,0 +1,54 @@
+/**
+ * A base class for different types of rate limiting handlers for the REST API.
+ * @private
+ */
+class RequestHandler {
+ /**
+ * @param {RESTManager} restManager The REST manager to use
+ */
+ constructor(restManager) {
+ /**
+ * The RESTManager that instantiated this RequestHandler
+ * @type {RESTManager}
+ */
+ this.restManager = restManager;
+
+ /**
+ * A list of requests that have yet to be processed
+ * @type {APIRequest[]}
+ */
+ this.queue = [];
+ }
+
+ /**
+ * Whether or not the client is being rate limited on every endpoint
+ * @type {boolean}
+ * @readonly
+ */
+ get globalLimit() {
+ return this.restManager.globallyRateLimited;
+ }
+
+ set globalLimit(value) {
+ this.restManager.globallyRateLimited = value;
+ }
+
+ /**
+ * Push a new API request into this bucket.
+ * @param {APIRequest} request The new request to push into the queue
+ */
+ push(request) {
+ this.queue.push(request);
+ }
+
+ /**
+ * Attempts to get this RequestHandler to process its current queue.
+ */
+ handle() {} // eslint-disable-line no-empty-function
+
+ destroy() {
+ this.queue = [];
+ }
+}
+
+module.exports = RequestHandler;
diff --git a/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js b/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js
new file mode 100644
index 0000000..9d25bdb
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js
@@ -0,0 +1,132 @@
+const RequestHandler = require('./RequestHandler');
+const DiscordAPIError = require('../DiscordAPIError');
+const { Events: { RATE_LIMIT } } = require('../../../util/Constants');
+
+/**
+ * Handles API Requests sequentially, i.e. we wait until the current request is finished before moving onto
+ * the next. This plays a _lot_ nicer in terms of avoiding 429's when there is more than one session of the account,
+ * but it can be slower.
+ * @extends {RequestHandler}
+ * @private
+ */
+class SequentialRequestHandler extends RequestHandler {
+ /**
+ * @param {RESTManager} restManager The REST manager to use
+ * @param {string} endpoint The endpoint to handle
+ */
+ constructor(restManager, endpoint) {
+ super(restManager, endpoint);
+
+ /**
+ * The client that instantiated this handler
+ * @type {Client}
+ */
+ this.client = restManager.client;
+
+ /**
+ * The endpoint that this handler is handling
+ * @type {string}
+ */
+ this.endpoint = endpoint;
+
+ /**
+ * The time difference between Discord's Dates and the local computer's Dates. A positive number means the local
+ * computer's time is ahead of Discord's
+ * @type {number}
+ */
+ this.timeDifference = 0;
+
+ /**
+ * Whether the queue is being processed or not
+ * @type {boolean}
+ */
+ this.busy = false;
+ }
+
+ push(request) {
+ super.push(request);
+ this.handle();
+ }
+
+ /**
+ * Performs a request then resolves a promise to indicate its readiness for a new request.
+ * @param {APIRequest} item The item to execute
+ * @returns {Promise<?Object|Error>}
+ */
+ execute(item) {
+ this.busy = true;
+ return new Promise(resolve => {
+ item.request.gen().end((err, res) => {
+ if (res && res.headers) {
+ this.requestLimit = Number(res.headers['x-ratelimit-limit']);
+ this.requestResetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
+ this.requestRemaining = Number(res.headers['x-ratelimit-remaining']);
+ this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
+ }
+ if (err) {
+ if (err.status === 429) {
+ this.queue.unshift(item);
+ this.client.setTimeout(() => {
+ this.globalLimit = false;
+ resolve();
+ }, Number(res.headers['retry-after']) + this.client.options.restTimeOffset);
+ if (res.headers['x-ratelimit-global']) this.globalLimit = true;
+ } else if (err.status >= 500 && err.status < 600) {
+ if (item.retries === this.client.options.retryLimit) {
+ item.reject(err);
+ resolve();
+ } else {
+ item.retries++;
+ this.queue.unshift(item);
+ this.client.setTimeout(resolve, 1e3 + this.client.options.restTimeOffset);
+ }
+ } else {
+ item.reject(err.status >= 400 && err.status < 500 ?
+ new DiscordAPIError(res.request.path, res.body, res.request.method) : err);
+ resolve(err);
+ }
+ } else {
+ this.globalLimit = false;
+ const data = res && res.body ? res.body : {};
+ item.resolve(data);
+ if (this.requestRemaining === 0) {
+ if (this.client.listenerCount(RATE_LIMIT)) {
+ /**
+ * Emitted when the client hits a rate limit while making a request
+ * @event Client#rateLimit
+ * @param {Object} rateLimitInfo Object containing the rate limit info
+ * @param {number} rateLimitInfo.limit Number of requests that can be made to this endpoint
+ * @param {number} rateLimitInfo.timeDifference Delta-T in ms between your system and Discord servers
+ * @param {string} rateLimitInfo.path Path used for request that triggered this event
+ * @param {string} rateLimitInfo.method HTTP method used for request that triggered this event
+ */
+ this.client.emit(RATE_LIMIT, {
+ limit: this.requestLimit,
+ timeDifference: this.timeDifference,
+ path: item.request.path,
+ method: item.request.method,
+ });
+ }
+ this.client.setTimeout(
+ () => resolve(data),
+ this.requestResetTime - Date.now() + this.timeDifference + this.client.options.restTimeOffset
+ );
+ } else {
+ resolve(data);
+ }
+ }
+ });
+ });
+ }
+
+ handle() {
+ super.handle();
+ if (this.busy || this.remaining === 0 || this.queue.length === 0 || this.globalLimit) return;
+ this.execute(this.queue.shift()).then(() => {
+ this.busy = false;
+ this.handle();
+ });
+ }
+}
+
+module.exports = SequentialRequestHandler;
diff --git a/node_modules/discord.js/src/client/rest/UserAgentManager.js b/node_modules/discord.js/src/client/rest/UserAgentManager.js
new file mode 100644
index 0000000..13e5528
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/UserAgentManager.js
@@ -0,0 +1,25 @@
+const Constants = require('../../util/Constants');
+
+class UserAgentManager {
+ constructor() {
+ this.build(this.constructor.DEFAULT);
+ }
+
+ set({ url, version } = {}) {
+ this.build({
+ url: url || this.constructor.DFEAULT.url,
+ version: version || this.constructor.DEFAULT.version,
+ });
+ }
+
+ build(ua) {
+ this.userAgent = `DiscordBot (${ua.url}, ${ua.version}) Node.js/${process.version}`;
+ }
+}
+
+UserAgentManager.DEFAULT = {
+ url: Constants.Package.homepage.split('#')[0],
+ version: Constants.Package.version,
+};
+
+module.exports = UserAgentManager;
diff --git a/node_modules/discord.js/src/client/voice/ClientVoiceManager.js b/node_modules/discord.js/src/client/voice/ClientVoiceManager.js
new file mode 100644
index 0000000..d8d3f9f
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/ClientVoiceManager.js
@@ -0,0 +1,86 @@
+const Collection = require('../../util/Collection');
+const VoiceConnection = require('./VoiceConnection');
+
+/**
+ * Manages all the voice stuff for the client.
+ * @private
+ */
+class ClientVoiceManager {
+ constructor(client) {
+ /**
+ * The client that instantiated this voice manager
+ * @type {Client}
+ */
+ this.client = client;
+
+ /**
+ * A collection mapping connection IDs to the Connection objects
+ * @type {Collection<Snowflake, VoiceConnection>}
+ */
+ this.connections = new Collection();
+
+ this.client.on('self.voiceServer', this.onVoiceServer.bind(this));
+ this.client.on('self.voiceStateUpdate', this.onVoiceStateUpdate.bind(this));
+ }
+
+ onVoiceServer({ guild_id, token, endpoint }) {
+ const connection = this.connections.get(guild_id);
+ if (connection) connection.setTokenAndEndpoint(token, endpoint);
+ }
+
+ onVoiceStateUpdate({ guild_id, session_id, channel_id }) {
+ const connection = this.connections.get(guild_id);
+ if (!connection) return;
+ if (!channel_id) {
+ connection._disconnect();
+ this.connections.delete(guild_id);
+ return;
+ }
+
+ connection.channel = this.client.channels.get(channel_id);
+ connection.setSessionID(session_id);
+ }
+
+ /**
+ * Sets up a request to join a voice channel.
+ * @param {VoiceChannel} channel The voice channel to join
+ * @returns {Promise<VoiceConnection>}
+ */
+ joinChannel(channel) {
+ return new Promise((resolve, reject) => {
+ if (!channel.joinable) {
+ if (channel.full) {
+ throw new Error('You do not have permission to join this voice channel; it is full.');
+ } else {
+ throw new Error('You do not have permission to join this voice channel.');
+ }
+ }
+
+ let connection = this.connections.get(channel.guild.id);
+
+ if (connection) {
+ if (connection.channel.id !== channel.id) {
+ this.connections.get(channel.guild.id).updateChannel(channel);
+ }
+ resolve(connection);
+ return;
+ } else {
+ connection = new VoiceConnection(this, channel);
+ this.connections.set(channel.guild.id, connection);
+ }
+
+ connection.once('failed', reason => {
+ this.connections.delete(channel.guild.id);
+ reject(reason);
+ });
+
+ connection.once('authenticated', () => {
+ connection.once('ready', () => resolve(connection));
+ connection.once('error', reject);
+ connection.once('disconnect', () => this.connections.delete(channel.guild.id));
+ });
+ });
+ }
+}
+
+module.exports = ClientVoiceManager;
diff --git a/node_modules/discord.js/src/client/voice/VoiceBroadcast.js b/node_modules/discord.js/src/client/voice/VoiceBroadcast.js
new file mode 100644
index 0000000..3a78d87
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceBroadcast.js
@@ -0,0 +1,366 @@
+const VolumeInterface = require('./util/VolumeInterface');
+const Prism = require('prism-media');
+const OpusEncoders = require('./opus/OpusEngineList');
+const Collection = require('../../util/Collection');
+
+const ffmpegArguments = [
+ '-analyzeduration', '0',
+ '-loglevel', '0',
+ '-f', 's16le',
+ '-ar', '48000',
+ '-ac', '2',
+];
+
+/**
+ * A voice broadcast can be played across multiple voice connections for improved shared-stream efficiency.
+ *
+ * Example usage:
+ * ```js
+ * const broadcast = client.createVoiceBroadcast();
+ * broadcast.playFile('./music.mp3');
+ * // Play "music.mp3" in all voice connections that the client is in
+ * for (const connection of client.voiceConnections.values()) {
+ * connection.playBroadcast(broadcast);
+ * }
+ * ```
+ * @implements {VolumeInterface}
+ */
+class VoiceBroadcast extends VolumeInterface {
+ constructor(client) {
+ super();
+ /**
+ * The client that created the broadcast
+ * @type {Client}
+ */
+ this.client = client;
+ this._dispatchers = new Collection();
+ this._encoders = new Collection();
+ /**
+ * The audio transcoder that this broadcast uses
+ * @type {Prism}
+ */
+ this.prism = new Prism();
+ /**
+ * The current audio transcoder that is being used
+ * @type {Object}
+ */
+ this.currentTranscoder = null;
+ this.tickInterval = null;
+ this._volume = 1;
+ }
+
+ /**
+ * An array of subscribed dispatchers
+ * @type {StreamDispatcher[]}
+ * @readonly
+ */
+ get dispatchers() {
+ let d = [];
+ for (const container of this._dispatchers.values()) {
+ d = d.concat(Array.from(container.values()));
+ }
+ return d;
+ }
+
+ get _playableStream() {
+ const currentTranscoder = this.currentTranscoder;
+ if (!currentTranscoder) return null;
+ const transcoder = currentTranscoder.transcoder;
+ const options = currentTranscoder.options;
+ return (transcoder && transcoder.output) || options.stream;
+ }
+
+ unregisterDispatcher(dispatcher, old) {
+ const volume = old || dispatcher.volume;
+
+ /**
+ * Emitted whenever a stream dispatcher unsubscribes from the broadcast.
+ * @event VoiceBroadcast#unsubscribe
+ * @param {StreamDispatcher} dispatcher The unsubscribed dispatcher
+ */
+ this.emit('unsubscribe', dispatcher);
+ for (const container of this._dispatchers.values()) {
+ container.delete(dispatcher);
+
+ if (!container.size) {
+ this._encoders.get(volume).destroy();
+ this._dispatchers.delete(volume);
+ this._encoders.delete(volume);
+ }
+ }
+ }
+
+ registerDispatcher(dispatcher) {
+ if (!this._dispatchers.has(dispatcher.volume)) {
+ this._dispatchers.set(dispatcher.volume, new Set());
+ this._encoders.set(dispatcher.volume, OpusEncoders.fetch());
+ }
+ const container = this._dispatchers.get(dispatcher.volume);
+ if (!container.has(dispatcher)) {
+ container.add(dispatcher);
+ dispatcher.once('end', () => this.unregisterDispatcher(dispatcher));
+ dispatcher.on('volumeChange', (o, n) => {
+ this.unregisterDispatcher(dispatcher, o);
+ if (!this._dispatchers.has(n)) {
+ this._dispatchers.set(n, new Set());
+ this._encoders.set(n, OpusEncoders.fetch());
+ }
+ this._dispatchers.get(n).add(dispatcher);
+ });
+ /**
+ * Emitted whenever a stream dispatcher subscribes to the broadcast.
+ * @event VoiceBroadcast#subscribe
+ * @param {StreamDispatcher} dispatcher The subscribed dispatcher
+ */
+ this.emit('subscribe', dispatcher);
+ }
+ }
+
+ killCurrentTranscoder() {
+ if (this.currentTranscoder) {
+ if (this.currentTranscoder.transcoder) this.currentTranscoder.transcoder.kill();
+ this.currentTranscoder = null;
+ this.emit('end');
+ }
+ }
+
+ /**
+ * Plays any audio stream across the broadcast.
+ * @param {ReadableStream} stream The audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {VoiceBroadcast}
+ * @example
+ * // Play streams using ytdl-core
+ * const ytdl = require('ytdl-core');
+ * const streamOptions = { seek: 0, volume: 1 };
+ * const broadcast = client.createVoiceBroadcast();
+ *
+ * voiceChannel.join()
+ * .then(connection => {
+ * const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', { filter : 'audioonly' });
+ * broadcast.playStream(stream);
+ * const dispatcher = connection.playBroadcast(broadcast);
+ * })
+ * .catch(console.error);
+ */
+ playStream(stream, options = {}) {
+ this.setVolume(options.volume || 1);
+ return this._playTranscodable(stream, options);
+ }
+
+ /**
+ * Play the given file in the voice connection.
+ * @param {string} file The absolute path to the file
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ * @example
+ * // Play files natively
+ * const broadcast = client.createVoiceBroadcast();
+ *
+ * voiceChannel.join()
+ * .then(connection => {
+ * broadcast.playFile('C:/Users/Discord/Desktop/music.mp3');
+ * const dispatcher = connection.playBroadcast(broadcast);
+ * })
+ * .catch(console.error);
+ */
+ playFile(file, options = {}) {
+ this.setVolume(options.volume || 1);
+ return this._playTranscodable(`file:${file}`, options);
+ }
+
+ _playTranscodable(media, options) {
+ this.killCurrentTranscoder();
+ const transcoder = this.prism.transcode({
+ type: 'ffmpeg',
+ media,
+ ffmpegArguments: ffmpegArguments.concat(['-ss', String(options.seek || 0)]),
+ });
+ /**
+ * Emitted whenever an error occurs.
+ * @event VoiceBroadcast#error
+ * @param {Error} error The error that occurred
+ */
+ transcoder.once('error', e => {
+ if (this.listenerCount('error') > 0) this.emit('error', e);
+ /**
+ * Emitted whenever the VoiceBroadcast has any warnings.
+ * @event VoiceBroadcast#warn
+ * @param {string|Error} warning The warning that was raised
+ */
+ else this.emit('warn', e);
+ });
+ /**
+ * Emitted once the broadcast (the audio stream) ends.
+ * @event VoiceBroadcast#end
+ */
+ transcoder.once('end', () => this.killCurrentTranscoder());
+ this.currentTranscoder = {
+ transcoder,
+ options,
+ };
+ transcoder.output.once('readable', () => this._startPlaying());
+ return this;
+ }
+
+ /**
+ * Plays a stream of 16-bit signed stereo PCM.
+ * @param {ReadableStream} stream The audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {VoiceBroadcast}
+ */
+ playConvertedStream(stream, options = {}) {
+ this.killCurrentTranscoder();
+ this.setVolume(options.volume || 1);
+ this.currentTranscoder = { options: { stream } };
+ stream.once('readable', () => this._startPlaying());
+ return this;
+ }
+
+ /**
+ * Plays an Opus encoded stream.
+ * <warn>Note that inline volume is not compatible with this method.</warn>
+ * @param {ReadableStream} stream The Opus audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ */
+ playOpusStream(stream) {
+ this.currentTranscoder = { options: { stream }, opus: true };
+ stream.once('readable', () => this._startPlaying());
+ return this;
+ }
+
+ /**
+ * Play an arbitrary input that can be [handled by ffmpeg](https://ffmpeg.org/ffmpeg-protocols.html#Description)
+ * @param {string} input The arbitrary input
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {VoiceBroadcast}
+ */
+ playArbitraryInput(input, options = {}) {
+ this.setVolume(options.volume || 1);
+ options.input = input;
+ return this._playTranscodable(input, options);
+ }
+
+ /**
+ * Pauses the entire broadcast - all dispatchers also pause.
+ */
+ pause() {
+ this.paused = true;
+ for (const container of this._dispatchers.values()) {
+ for (const dispatcher of container.values()) {
+ dispatcher.pause();
+ }
+ }
+ }
+
+ /**
+ * Resumes the entire broadcast - all dispatchers also resume.
+ */
+ resume() {
+ this.paused = false;
+ for (const container of this._dispatchers.values()) {
+ for (const dispatcher of container.values()) {
+ dispatcher.resume();
+ }
+ }
+ }
+
+ _startPlaying() {
+ if (this.tickInterval) clearInterval(this.tickInterval);
+ // Old code?
+ // this.tickInterval = this.client.setInterval(this.tick.bind(this), 20);
+ this._startTime = Date.now();
+ this._count = 0;
+ this._pausedTime = 0;
+ this._missed = 0;
+ this.tick();
+ }
+
+ tick() {
+ if (!this._playableStream) return;
+ if (this.paused) {
+ this._pausedTime += 20;
+ setTimeout(() => this.tick(), 20);
+ return;
+ }
+
+ const opus = this.currentTranscoder.opus;
+ const buffer = this.readStreamBuffer();
+
+ if (!buffer) {
+ this._missed++;
+ if (this._missed < 5) {
+ this._pausedTime += 200;
+ setTimeout(() => this.tick(), 200);
+ } else {
+ this.killCurrentTranscoder();
+ }
+ return;
+ }
+
+ this._missed = 0;
+
+ let packetMatrix = {};
+
+ const getOpusPacket = volume => {
+ if (packetMatrix[volume]) return packetMatrix[volume];
+
+ const opusEncoder = this._encoders.get(volume);
+ const opusPacket = opusEncoder.encode(this.applyVolume(buffer, this._volume * volume));
+ packetMatrix[volume] = opusPacket;
+ return opusPacket;
+ };
+
+ for (const dispatcher of this.dispatchers) {
+ if (opus) {
+ dispatcher.processPacket(buffer);
+ continue;
+ }
+
+ const volume = dispatcher.volume;
+ dispatcher.processPacket(getOpusPacket(volume));
+ }
+
+ const next = 20 + (this._startTime + this._pausedTime + (this._count * 20) - Date.now());
+ this._count++;
+ setTimeout(() => this.tick(), next);
+ }
+
+ readStreamBuffer() {
+ const opus = this.currentTranscoder.opus;
+ const bufferLength = (opus ? 80 : 1920) * 2;
+ let buffer = this._playableStream.read(bufferLength);
+ if (opus) return buffer;
+ if (!buffer) return null;
+
+ if (buffer.length !== bufferLength) {
+ const newBuffer = Buffer.alloc(bufferLength).fill(0);
+ buffer.copy(newBuffer);
+ buffer = newBuffer;
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Stop the current stream from playing without unsubscribing dispatchers.
+ */
+ end() {
+ this.killCurrentTranscoder();
+ }
+
+ /**
+ * End the current broadcast, all subscribed dispatchers will also end.
+ */
+ destroy() {
+ this.end();
+ for (const container of this._dispatchers.values()) {
+ for (const dispatcher of container.values()) {
+ dispatcher.destroy('end', 'broadcast ended');
+ }
+ }
+ }
+}
+
+module.exports = VoiceBroadcast;
diff --git a/node_modules/discord.js/src/client/voice/VoiceConnection.js b/node_modules/discord.js/src/client/voice/VoiceConnection.js
new file mode 100644
index 0000000..2fc6338
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceConnection.js
@@ -0,0 +1,598 @@
+const VoiceWebSocket = require('./VoiceWebSocket');
+const VoiceUDP = require('./VoiceUDPClient');
+const Util = require('../../util/Util');
+const Constants = require('../../util/Constants');
+const AudioPlayer = require('./player/AudioPlayer');
+const VoiceReceiver = require('./receiver/VoiceReceiver');
+const SingleSilence = require('./util/SingleSilence');
+const EventEmitter = require('events').EventEmitter;
+const Prism = require('prism-media');
+
+// The delay between packets when a user is considered to have stopped speaking
+// https://github.com/discordjs/discord.js/issues/3524#issuecomment-540373200
+const DISCORD_SPEAKING_DELAY = 250;
+
+/**
+ * Represents a connection to a guild's voice server.
+ * ```js
+ * // Obtained using:
+ * voiceChannel.join()
+ * .then(connection => {
+ *
+ * });
+ * ```
+ * @extends {EventEmitter}
+ */
+class VoiceConnection extends EventEmitter {
+ constructor(voiceManager, channel) {
+ super();
+
+ /**
+ * The voice manager that instantiated this connection
+ * @type {ClientVoiceManager}
+ */
+ this.voiceManager = voiceManager;
+
+ /**
+ * The client that instantiated this connection
+ * @type {Client}
+ */
+ this.client = voiceManager.client;
+
+ /**
+ * @external Prism
+ * @see {@link https://github.com/hydrabolt/prism-media}
+ */
+
+ /**
+ * The audio transcoder for this connection
+ * @type {Prism}
+ */
+ this.prism = new Prism();
+
+ /**
+ * The voice channel this connection is currently serving
+ * @type {VoiceChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * The current status of the voice connection
+ * @type {VoiceStatus}
+ */
+ this.status = Constants.VoiceStatus.AUTHENTICATING;
+
+ /**
+ * Whether we're currently transmitting audio
+ * @type {boolean}
+ */
+ this.speaking = false;
+
+ /**
+ * An array of Voice Receivers that have been created for this connection
+ * @type {VoiceReceiver[]}
+ */
+ this.receivers = [];
+
+ /**
+ * The authentication data needed to connect to the voice server
+ * @type {Object}
+ * @private
+ */
+ this.authentication = {};
+
+ /**
+ * The audio player for this voice connection
+ * @type {AudioPlayer}
+ */
+ this.player = new AudioPlayer(this);
+
+ this.player.on('debug', m => {
+ /**
+ * Debug info from the connection.
+ * @event VoiceConnection#debug
+ * @param {string} message The debug message
+ */
+ this.emit('debug', `audio player - ${m}`);
+ });
+
+ this.player.on('error', e => {
+ /**
+ * Warning info from the connection.
+ * @event VoiceConnection#warn
+ * @param {string|Error} warning The warning
+ */
+ this.emit('warn', e);
+ });
+
+ /**
+ * Map SSRC to user id
+ * @type {Map<number, Snowflake>}
+ * @private
+ */
+ this.ssrcMap = new Map();
+
+ /**
+ * Map user id to speaking timeout
+ * @type {Map<Snowflake, Timeout>}
+ * @private
+ */
+ this.speakingTimeouts = new Map();
+
+ /**
+ * Object that wraps contains the `ws` and `udp` sockets of this voice connection
+ * @type {Object}
+ * @private
+ */
+ this.sockets = {};
+
+ this.authenticate();
+ }
+
+ /**
+ * The current stream dispatcher (if any)
+ * @type {?StreamDispatcher}
+ * @readonly
+ */
+ get dispatcher() {
+ return this.player.dispatcher;
+ }
+
+ /**
+ * Sets whether the voice connection should display as "speaking" or not.
+ * @param {boolean} value Whether or not to speak
+ * @private
+ */
+ setSpeaking(value) {
+ if (this.speaking === value) return;
+ if (this.status !== Constants.VoiceStatus.CONNECTED) return;
+ this.speaking = value;
+ this.sockets.ws.sendPacket({
+ op: Constants.VoiceOPCodes.SPEAKING,
+ d: {
+ speaking: true,
+ delay: 0,
+ },
+ }).catch(e => {
+ this.emit('debug', e);
+ });
+ }
+
+ /**
+ * Sends a request to the main gateway to join a voice channel.
+ * @param {Object} [options] The options to provide
+ */
+ sendVoiceStateUpdate(options = {}) {
+ options = Util.mergeDefault({
+ guild_id: this.channel.guild.id,
+ channel_id: this.channel.id,
+ self_mute: false,
+ self_deaf: false,
+ }, options);
+
+ this.client.ws.send({
+ op: Constants.OPCodes.VOICE_STATE_UPDATE,
+ d: options,
+ });
+ }
+
+ /**
+ * Set the token and endpoint required to connect to the voice servers.
+ * @param {string} token The voice token
+ * @param {string} endpoint The voice endpoint
+ * @returns {void}
+ */
+ setTokenAndEndpoint(token, endpoint) {
+ if (!endpoint) {
+ // Signifies awaiting endpoint stage
+ return;
+ }
+
+ if (!token) {
+ this.authenticateFailed('Token not provided from voice server packet.');
+ return;
+ }
+
+ endpoint = endpoint.match(/([^:]*)/)[0];
+
+ if (!endpoint) {
+ this.authenticateFailed('Invalid endpoint received.');
+ return;
+ }
+
+ if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
+ this.authentication.token = token;
+ this.authentication.endpoint = endpoint;
+ this.checkAuthenticated();
+ } else if (token !== this.authentication.token || endpoint !== this.authentication.endpoint) {
+ this.reconnect(token, endpoint);
+ }
+ }
+
+ /**
+ * Sets the Session ID for the connection.
+ * @param {string} sessionID The voice session ID
+ */
+ setSessionID(sessionID) {
+ if (!sessionID) {
+ this.authenticateFailed('Session ID not supplied.');
+ return;
+ }
+
+ if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
+ this.authentication.sessionID = sessionID;
+ this.checkAuthenticated();
+ } else if (sessionID !== this.authentication.sessionID) {
+ this.authentication.sessionID = sessionID;
+ /**
+ * Emitted when a new session ID is received.
+ * @event VoiceConnection#newSession
+ * @private
+ */
+ this.emit('newSession', sessionID);
+ }
+ }
+
+ /**
+ * Checks whether the voice connection is authenticated.
+ * @private
+ */
+ checkAuthenticated() {
+ const { token, endpoint, sessionID } = this.authentication;
+
+ if (token && endpoint && sessionID) {
+ this.client.clearTimeout(this.connectTimeout);
+ this.status = Constants.VoiceStatus.CONNECTING;
+ /**
+ * Emitted when we successfully initiate a voice connection.
+ * @event VoiceConnection#authenticated
+ */
+ this.emit('authenticated');
+ this.connect();
+ }
+ }
+
+ /**
+ * Invoked when we fail to initiate a voice connection.
+ * @param {string} reason The reason for failure
+ * @private
+ */
+ authenticateFailed(reason) {
+ this.client.clearTimeout(this.connectTimeout);
+ if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
+ /**
+ * Emitted when we fail to initiate a voice connection.
+ * @event VoiceConnection#failed
+ * @param {Error} error The encountered error
+ */
+ this.emit('failed', new Error(reason));
+ } else {
+ /**
+ * Emitted whenever the connection encounters an error.
+ * @event VoiceConnection#error
+ * @param {Error} error The encountered error
+ */
+ this.emit('error', new Error(reason));
+ }
+ this.status = Constants.VoiceStatus.DISCONNECTED;
+ }
+
+ /**
+ * Move to a different voice channel in the same guild.
+ * @param {VoiceChannel} channel The channel to move to
+ * @private
+ */
+ updateChannel(channel) {
+ this.channel = channel;
+ this.sendVoiceStateUpdate();
+ }
+
+ /**
+ * Attempts to authenticate to the voice server.
+ * @private
+ */
+ authenticate() {
+ this.sendVoiceStateUpdate();
+ this.connectTimeout = this.client.setTimeout(
+ () => this.authenticateFailed(new Error('Connection not established within 15 seconds.')), 15000);
+ }
+
+ /**
+ * Attempts to reconnect to the voice server (typically after a region change).
+ * @param {string} token The voice token
+ * @param {string} endpoint The voice endpoint
+ * @private
+ */
+ reconnect(token, endpoint) {
+ this.authentication.token = token;
+ this.authentication.endpoint = endpoint;
+
+ this.status = Constants.VoiceStatus.RECONNECTING;
+ /**
+ * Emitted when the voice connection is reconnecting (typically after a region change).
+ * @event VoiceConnection#reconnecting
+ */
+ this.emit('reconnecting');
+ this.connect();
+ }
+
+ /**
+ * Disconnect the voice connection, causing a disconnect and closing event to be emitted.
+ */
+ disconnect() {
+ this.emit('closing');
+ this.sendVoiceStateUpdate({
+ channel_id: null,
+ });
+
+ this._disconnect();
+ }
+
+ /**
+ * Internally disconnects (doesn't send disconnect packet).
+ * @private
+ */
+ _disconnect() {
+ this.player.destroy();
+ this.cleanup();
+ this.status = Constants.VoiceStatus.DISCONNECTED;
+ /**
+ * Emitted when the voice connection disconnects.
+ * @event VoiceConnection#disconnect
+ */
+ this.emit('disconnect');
+ }
+
+ /**
+ * Cleans up after disconnect.
+ * @private
+ */
+ cleanup() {
+ const { ws, udp } = this.sockets;
+
+ if (ws) {
+ ws.removeAllListeners('error');
+ ws.removeAllListeners('ready');
+ ws.removeAllListeners('sessionDescription');
+ ws.removeAllListeners('startSpeaking');
+ ws.shutdown();
+ }
+
+ if (udp) udp.removeAllListeners('error');
+
+ this.sockets.ws = null;
+ this.sockets.udp = null;
+ }
+
+ /**
+ * Connect the voice connection.
+ * @private
+ */
+ connect() {
+ if (this.status !== Constants.VoiceStatus.RECONNECTING) {
+ if (this.sockets.ws) throw new Error('There is already an existing WebSocket connection.');
+ if (this.sockets.udp) throw new Error('There is already an existing UDP connection.');
+ }
+
+ if (this.sockets.ws) this.sockets.ws.shutdown();
+ if (this.sockets.udp) this.sockets.udp.shutdown();
+
+ this.sockets.ws = new VoiceWebSocket(this);
+ this.sockets.udp = new VoiceUDP(this);
+
+ const { ws, udp } = this.sockets;
+
+ ws.on('error', err => this.emit('error', err));
+ udp.on('error', err => this.emit('error', err));
+ ws.on('ready', this.onReady.bind(this));
+ ws.on('sessionDescription', this.onSessionDescription.bind(this));
+ ws.on('startSpeaking', this.onStartSpeaking.bind(this));
+ }
+
+ /**
+ * Invoked when the voice websocket is ready.
+ * @param {Object} data The received data
+ * @private
+ */
+ onReady({ port, ssrc, ip }) {
+ this.authentication.port = port;
+ this.authentication.ssrc = ssrc;
+ this.sockets.udp.createUDPSocket(ip);
+ this.sockets.udp.socket.on('message', this.onUDPMessage.bind(this));
+ }
+
+ /**
+ * Invoked when a session description is received.
+ * @param {string} mode The encryption mode
+ * @param {string} secret The secret key
+ * @private
+ */
+ onSessionDescription(mode, secret) {
+ this.authentication.encryptionMode = mode;
+ this.authentication.secretKey = secret;
+
+ this.status = Constants.VoiceStatus.CONNECTED;
+ const ready = () => {
+ /**
+ * Emitted once the connection is ready, when a promise to join a voice channel resolves,
+ * the connection will already be ready.
+ * @event VoiceConnection#ready
+ */
+ this.emit('ready');
+ };
+ if (this.dispatcher) {
+ ready();
+ } else {
+ // This serves to provide support for voice receive, sending audio is required to receive it.
+ this.playOpusStream(new SingleSilence()).once('end', ready);
+ }
+ }
+
+ /**
+ * Invoked whenever a user initially starts speaking.
+ * @param {Object} data The speaking data
+ * @private
+ */
+ onStartSpeaking({ user_id, ssrc }) {
+ this.ssrcMap.set(+ssrc, user_id);
+ }
+
+ /**
+ * Invoked when a speaking event is received.
+ * @param {Object} data The received data
+ * @private
+ */
+ onSpeaking({ user_id, speaking }) {
+ const guild = this.channel.guild;
+ const user = this.client.users.get(user_id);
+ if (!speaking) {
+ for (const receiver of this.receivers) {
+ receiver.stoppedSpeaking(user);
+ }
+ }
+ /**
+ * Emitted whenever a user starts/stops speaking.
+ * @event VoiceConnection#speaking
+ * @param {User} user The user that has started/stopped speaking
+ * @param {boolean} speaking Whether or not the user is speaking
+ */
+ if (this.status === Constants.VoiceStatus.CONNECTED) this.emit('speaking', user, speaking);
+ guild._memberSpeakUpdate(user_id, speaking);
+ }
+
+ /**
+ * Handles synthesizing of the speaking event.
+ * @param {Buffer} buffer Received packet from the UDP socket
+ * @private
+ */
+ onUDPMessage(buffer) {
+ const ssrc = +buffer.readUInt32BE(8).toString(10);
+ const user = this.client.users.get(this.ssrcMap.get(ssrc));
+ if (!user) return;
+
+ let speakingTimeout = this.speakingTimeouts.get(ssrc);
+ if (typeof speakingTimeout === 'undefined') {
+ this.onSpeaking({ user_id: user.id, ssrc, speaking: true });
+ } else {
+ this.client.clearTimeout(speakingTimeout);
+ }
+
+ speakingTimeout = this.client.setTimeout(() => {
+ try {
+ this.onSpeaking({ user_id: user.id, ssrc, speaking: false });
+ this.client.clearTimeout(speakingTimeout);
+ this.speakingTimeouts.delete(ssrc);
+ } catch (ex) {
+ // Connection already closed, ignore
+ }
+ }, DISCORD_SPEAKING_DELAY);
+ this.speakingTimeouts.set(ssrc, speakingTimeout);
+ }
+
+ /**
+ * Options that can be passed to stream-playing methods:
+ * @typedef {Object} StreamOptions
+ * @property {number} [seek=0] The time to seek to
+ * @property {number} [volume=1] The volume to play at
+ * @property {number} [passes=1] How many times to send the voice packet to reduce packet loss
+ * @property {number|string} [bitrate=48000] The bitrate (quality) of the audio.
+ * If set to 'auto', the voice channel's bitrate will be used
+ */
+
+ /**
+ * Play the given file in the voice connection.
+ * @param {string} file The absolute path to the file
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ * @example
+ * // Play files natively
+ * voiceChannel.join()
+ * .then(connection => {
+ * const dispatcher = connection.playFile('C:/Users/Discord/Desktop/music.mp3');
+ * })
+ * .catch(console.error);
+ */
+ playFile(file, options) {
+ return this.player.playUnknownStream(`file:${file}`, options);
+ }
+
+ /**
+ * Play an arbitrary input that can be [handled by ffmpeg](https://ffmpeg.org/ffmpeg-protocols.html#Description)
+ * @param {string} input the arbitrary input
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ */
+ playArbitraryInput(input, options) {
+ return this.player.playUnknownStream(input, options);
+ }
+
+ /**
+ * Plays and converts an audio stream in the voice connection.
+ * @param {ReadableStream} stream The audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ * @example
+ * // Play streams using ytdl-core
+ * const ytdl = require('ytdl-core');
+ * const streamOptions = { seek: 0, volume: 1 };
+ * voiceChannel.join()
+ * .then(connection => {
+ * const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', { filter : 'audioonly' });
+ * const dispatcher = connection.playStream(stream, streamOptions);
+ * })
+ * .catch(console.error);
+ */
+ playStream(stream, options) {
+ return this.player.playUnknownStream(stream, options);
+ }
+
+ /**
+ * Plays a stream of 16-bit signed stereo PCM.
+ * @param {ReadableStream} stream The audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ */
+ playConvertedStream(stream, options) {
+ return this.player.playPCMStream(stream, options);
+ }
+
+ /**
+ * Plays an Opus encoded stream.
+ * <warn>Note that inline volume is not compatible with this method.</warn>
+ * @param {ReadableStream} stream The Opus audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ */
+ playOpusStream(stream, options) {
+ return this.player.playOpusStream(stream, options);
+ }
+
+ /**
+ * Plays a voice broadcast.
+ * @param {VoiceBroadcast} broadcast The broadcast to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ * @example
+ * // Play a broadcast
+ * const broadcast = client
+ * .createVoiceBroadcast()
+ * .playFile('./test.mp3');
+ * const dispatcher = voiceConnection.playBroadcast(broadcast);
+ */
+ playBroadcast(broadcast, options) {
+ return this.player.playBroadcast(broadcast, options);
+ }
+
+ /**
+ * Creates a VoiceReceiver so you can start listening to voice data.
+ * It's recommended to only create one of these.
+ * @returns {VoiceReceiver}
+ */
+ createReceiver() {
+ const receiver = new VoiceReceiver(this);
+ this.receivers.push(receiver);
+ return receiver;
+ }
+}
+
+module.exports = VoiceConnection;
diff --git a/node_modules/discord.js/src/client/voice/VoiceUDPClient.js b/node_modules/discord.js/src/client/voice/VoiceUDPClient.js
new file mode 100644
index 0000000..cf1e388
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceUDPClient.js
@@ -0,0 +1,127 @@
+const udp = require('dgram');
+const Constants = require('../../util/Constants');
+const EventEmitter = require('events').EventEmitter;
+
+/**
+ * Represents a UDP client for a Voice Connection.
+ * @extends {EventEmitter}
+ * @private
+ */
+class VoiceConnectionUDPClient extends EventEmitter {
+ constructor(voiceConnection) {
+ super();
+
+ /**
+ * The voice connection that this UDP client serves
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = voiceConnection;
+
+ /**
+ * The UDP socket
+ * @type {?Socket}
+ */
+ this.socket = null;
+
+ /**
+ * The address of the Discord voice server
+ * @type {?string}
+ */
+ this.discordAddress = null;
+
+ /**
+ * The local IP address
+ * @type {?string}
+ */
+ this.localAddress = null;
+
+ /**
+ * The local port
+ * @type {?string}
+ */
+ this.localPort = null;
+
+ this.voiceConnection.on('closing', this.shutdown.bind(this));
+ }
+
+ shutdown() {
+ if (this.socket) {
+ this.socket.removeAllListeners('message');
+ try {
+ this.socket.close();
+ } finally {
+ this.socket = null;
+ }
+ }
+ }
+
+ /**
+ * The port of the Discord voice server
+ * @type {number}
+ * @readonly
+ */
+ get discordPort() {
+ return this.voiceConnection.authentication.port;
+ }
+
+ /**
+ * Send a packet to the UDP client.
+ * @param {Object} packet The packet to send
+ * @returns {Promise<Object>}
+ */
+ send(packet) {
+ return new Promise((resolve, reject) => {
+ if (!this.socket) throw new Error('Tried to send a UDP packet, but there is no socket available.');
+ if (!this.discordAddress || !this.discordPort) throw new Error('Malformed UDP address or port.');
+ this.socket.send(packet, 0, packet.length, this.discordPort, this.discordAddress, error => {
+ if (error) reject(error); else resolve(packet);
+ });
+ });
+ }
+
+ createUDPSocket(address) {
+ this.discordAddress = address;
+ const socket = this.socket = udp.createSocket('udp4');
+
+ socket.once('message', message => {
+ const packet = parseLocalPacket(message);
+ if (packet.error) {
+ this.emit('error', packet.error);
+ return;
+ }
+
+ this.localAddress = packet.address;
+ this.localPort = packet.port;
+
+ this.voiceConnection.sockets.ws.sendPacket({
+ op: Constants.VoiceOPCodes.SELECT_PROTOCOL,
+ d: {
+ protocol: 'udp',
+ data: {
+ address: packet.address,
+ port: packet.port,
+ mode: 'xsalsa20_poly1305',
+ },
+ },
+ });
+ });
+
+ const blankMessage = Buffer.alloc(70);
+ blankMessage.writeUIntBE(this.voiceConnection.authentication.ssrc, 0, 4);
+ this.send(blankMessage);
+ }
+}
+
+function parseLocalPacket(message) {
+ try {
+ const packet = Buffer.from(message);
+ let address = '';
+ for (let i = 4; i < packet.indexOf(0, i); i++) address += String.fromCharCode(packet[i]);
+ const port = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10);
+ return { address, port };
+ } catch (error) {
+ return { error };
+ }
+}
+
+module.exports = VoiceConnectionUDPClient;
diff --git a/node_modules/discord.js/src/client/voice/VoiceWebSocket.js b/node_modules/discord.js/src/client/voice/VoiceWebSocket.js
new file mode 100644
index 0000000..2269007
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceWebSocket.js
@@ -0,0 +1,246 @@
+const Constants = require('../../util/Constants');
+const SecretKey = require('./util/SecretKey');
+const EventEmitter = require('events').EventEmitter;
+
+let WebSocket;
+try {
+ WebSocket = require('@discordjs/uws');
+} catch (err) {
+ WebSocket = require('ws');
+}
+
+/**
+ * Represents a Voice Connection's WebSocket.
+ * @extends {EventEmitter}
+ * @private
+ */
+class VoiceWebSocket extends EventEmitter {
+ constructor(voiceConnection) {
+ super();
+
+ /**
+ * The client of this voice WebSocket
+ * @type {Client}
+ */
+ this.client = voiceConnection.voiceManager.client;
+
+ /**
+ * The Voice Connection that this WebSocket serves
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = voiceConnection;
+
+ /**
+ * How many connection attempts have been made
+ * @type {number}
+ */
+ this.attempts = 0;
+
+ this.connect();
+ this.dead = false;
+ this.voiceConnection.on('closing', this.shutdown.bind(this));
+ }
+
+ shutdown() {
+ this.dead = true;
+ this.reset();
+ }
+
+ /**
+ * Resets the current WebSocket.
+ */
+ reset() {
+ if (this.ws) {
+ if (this.ws.readyState !== WebSocket.CLOSED) this.ws.close();
+ this.ws = null;
+ }
+ this.clearHeartbeat();
+ }
+
+ /**
+ * Starts connecting to the Voice WebSocket Server.
+ */
+ connect() {
+ if (this.dead) return;
+ if (this.ws) this.reset();
+ if (this.attempts >= 5) {
+ this.emit('debug', new Error(`Too many connection attempts (${this.attempts}).`));
+ return;
+ }
+
+ this.attempts++;
+
+ /**
+ * The actual WebSocket used to connect to the Voice WebSocket Server.
+ * @type {WebSocket}
+ */
+ this.ws = new WebSocket(`wss://${this.voiceConnection.authentication.endpoint}`);
+ this.ws.onopen = this.onOpen.bind(this);
+ this.ws.onmessage = this.onMessage.bind(this);
+ this.ws.onclose = this.onClose.bind(this);
+ this.ws.onerror = this.onError.bind(this);
+ }
+
+ /**
+ * Sends data to the WebSocket if it is open.
+ * @param {string} data The data to send to the WebSocket
+ * @returns {Promise<string>}
+ */
+ send(data) {
+ return new Promise((resolve, reject) => {
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ throw new Error(`Voice websocket not open to send ${data}.`);
+ }
+ this.ws.send(data, null, error => {
+ if (error) reject(error); else resolve(data);
+ });
+ });
+ }
+
+ /**
+ * JSON.stringify's a packet and then sends it to the WebSocket Server.
+ * @param {Object} packet The packet to send
+ * @returns {Promise<string>}
+ */
+ sendPacket(packet) {
+ try {
+ packet = JSON.stringify(packet);
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ return this.send(packet);
+ }
+
+ /**
+ * Called whenever the WebSocket opens.
+ */
+ onOpen() {
+ this.sendPacket({
+ op: Constants.OPCodes.DISPATCH,
+ d: {
+ server_id: this.voiceConnection.channel.guild.id,
+ user_id: this.client.user.id,
+ token: this.voiceConnection.authentication.token,
+ session_id: this.voiceConnection.authentication.sessionID,
+ },
+ }).catch(() => {
+ this.emit('error', new Error('Tried to send join packet, but the WebSocket is not open.'));
+ });
+ }
+
+ /**
+ * Called whenever a message is received from the WebSocket.
+ * @param {MessageEvent} event The message event that was received
+ * @returns {void}
+ */
+ onMessage(event) {
+ try {
+ return this.onPacket(JSON.parse(event.data));
+ } catch (error) {
+ return this.onError(error);
+ }
+ }
+
+ /**
+ * Called whenever the connection to the WebSocket server is lost.
+ */
+ onClose() {
+ if (!this.dead) this.client.setTimeout(this.connect.bind(this), this.attempts * 1000);
+ }
+
+ /**
+ * Called whenever an error occurs with the WebSocket.
+ * @param {Error} error The error that occurred
+ */
+ onError(error) {
+ this.emit('error', error);
+ }
+
+ /**
+ * Called whenever a valid packet is received from the WebSocket.
+ * @param {Object} packet The received packet
+ */
+ onPacket(packet) {
+ switch (packet.op) {
+ case Constants.VoiceOPCodes.READY:
+ this.setHeartbeat(packet.d.heartbeat_interval);
+ /**
+ * Emitted once the voice WebSocket receives the ready packet.
+ * @param {Object} packet The received packet
+ * @event VoiceWebSocket#ready
+ */
+ this.emit('ready', packet.d);
+ break;
+ case Constants.VoiceOPCodes.SESSION_DESCRIPTION:
+ /**
+ * Emitted once the Voice Websocket receives a description of this voice session.
+ * @param {string} encryptionMode The type of encryption being used
+ * @param {SecretKey} secretKey The secret key used for encryption
+ * @event VoiceWebSocket#sessionDescription
+ */
+ this.emit('sessionDescription', packet.d.mode, new SecretKey(packet.d.secret_key));
+ break;
+ case Constants.VoiceOPCodes.SPEAKING:
+ /**
+ * Emitted whenever a speaking packet is received.
+ * @param {Object} data
+ * @event VoiceWebSocket#startSpeaking
+ */
+ this.emit('startSpeaking', packet.d);
+ break;
+ default:
+ /**
+ * Emitted when an unhandled packet is received.
+ * @param {Object} packet
+ * @event VoiceWebSocket#unknownPacket
+ */
+ this.emit('unknownPacket', packet);
+ break;
+ }
+ }
+
+ /**
+ * Sets an interval at which to send a heartbeat packet to the WebSocket.
+ * @param {number} interval The interval at which to send a heartbeat packet
+ */
+ setHeartbeat(interval) {
+ if (!interval || isNaN(interval)) {
+ this.onError(new Error('Tried to set voice heartbeat but no valid interval was specified.'));
+ return;
+ }
+ if (this.heartbeatInterval) {
+ /**
+ * Emitted whenver the voice WebSocket encounters a non-fatal error.
+ * @param {string} warn The warning
+ * @event VoiceWebSocket#warn
+ */
+ this.emit('warn', 'A voice heartbeat interval is being overwritten');
+ clearInterval(this.heartbeatInterval);
+ }
+ this.heartbeatInterval = this.client.setInterval(this.sendHeartbeat.bind(this), interval);
+ }
+
+ /**
+ * Clears a heartbeat interval, if one exists.
+ */
+ clearHeartbeat() {
+ if (!this.heartbeatInterval) {
+ this.emit('warn', 'Tried to clear a heartbeat interval that does not exist');
+ return;
+ }
+ clearInterval(this.heartbeatInterval);
+ this.heartbeatInterval = null;
+ }
+
+ /**
+ * Sends a heartbeat packet.
+ */
+ sendHeartbeat() {
+ this.sendPacket({ op: Constants.VoiceOPCodes.HEARTBEAT, d: null }).catch(() => {
+ this.emit('warn', 'Tried to send heartbeat, but connection is not open');
+ this.clearHeartbeat();
+ });
+ }
+}
+
+module.exports = VoiceWebSocket;
diff --git a/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js b/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js
new file mode 100644
index 0000000..77734ca
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js
@@ -0,0 +1,331 @@
+const VolumeInterface = require('../util/VolumeInterface');
+const VoiceBroadcast = require('../VoiceBroadcast');
+const Constants = require('../../../util/Constants');
+
+const secretbox = require('../util/Secretbox');
+
+const nonce = Buffer.alloc(24);
+nonce.fill(0);
+
+/**
+ * The class that sends voice packet data to the voice connection.
+ * ```js
+ * // Obtained using:
+ * voiceChannel.join().then(connection => {
+ * // You can play a file or a stream here:
+ * const dispatcher = connection.playFile('./file.mp3');
+ * });
+ * ```
+ * @implements {VolumeInterface}
+ */
+class StreamDispatcher extends VolumeInterface {
+ constructor(player, stream, streamOptions) {
+ super(streamOptions);
+ /**
+ * The Audio Player that controls this dispatcher
+ * @type {AudioPlayer}
+ */
+ this.player = player;
+ /**
+ * The stream that the dispatcher plays
+ * @type {ReadableStream|VoiceBroadcast}
+ */
+ this.stream = stream;
+ if (!(this.stream instanceof VoiceBroadcast)) this.startStreaming();
+ this.streamOptions = streamOptions;
+
+ const data = this.streamingData;
+ data.length = 20;
+ data.missed = 0;
+
+ /**
+ * Whether playing is paused
+ * @type {boolean}
+ */
+ this.paused = false;
+ /**
+ * Whether this dispatcher has been destroyed
+ * @type {boolean}
+ */
+ this.destroyed = false;
+
+ this._opus = streamOptions.opus;
+ }
+
+ /**
+ * How many passes the dispatcher should take when sending packets to reduce packet loss. Values over 5
+ * aren't recommended, as it means you are using 5x more bandwidth. You _can_ edit this at runtime
+ * @type {number}
+ * @readonly
+ */
+ get passes() {
+ return this.streamOptions.passes || 1;
+ }
+
+ set passes(n) {
+ this.streamOptions.passes = n;
+ }
+
+ get streamingData() {
+ return this.player.streamingData;
+ }
+
+ /**
+ * How long the stream dispatcher has been "speaking" for
+ * @type {number}
+ * @readonly
+ */
+ get time() {
+ return this.streamingData.count * (this.streamingData.length || 0);
+ }
+
+ /**
+ * The total time, taking into account pauses and skips, that the dispatcher has been streaming for
+ * @type {number}
+ * @readonly
+ */
+ get totalStreamTime() {
+ return this.time + this.streamingData.pausedTime;
+ }
+
+ /**
+ * Stops sending voice packets to the voice connection (stream may still progress however).
+ */
+ pause() { this.setPaused(true); }
+
+ /**
+ * Resumes sending voice packets to the voice connection (may be further on in the stream than when paused).
+ */
+ resume() { this.setPaused(false); }
+
+
+ /**
+ * Stops the current stream permanently and emits an `end` event.
+ * @param {string} [reason='user'] An optional reason for stopping the dispatcher
+ */
+ end(reason = 'user') {
+ this.destroy('end', reason);
+ }
+
+ setSpeaking(value) {
+ if (this.speaking === value) return;
+ if (this.player.voiceConnection.status !== Constants.VoiceStatus.CONNECTED) return;
+ this.speaking = value;
+ /**
+ * Emitted when the dispatcher starts/stops speaking.
+ * @event StreamDispatcher#speaking
+ * @param {boolean} value Whether or not the dispatcher is speaking
+ */
+ this.emit('speaking', value);
+ }
+
+
+ /**
+ * Set the bitrate of the current Opus encoder.
+ * @param {number} bitrate New bitrate, in kbps
+ * If set to 'auto', the voice channel's bitrate will be used
+ */
+ setBitrate(bitrate) {
+ this.player.setBitrate(bitrate);
+ }
+
+ sendBuffer(buffer, sequence, timestamp, opusPacket) {
+ opusPacket = opusPacket || this.player.opusEncoder.encode(buffer);
+ const packet = this.createPacket(sequence, timestamp, opusPacket);
+ this.sendPacket(packet);
+ }
+
+ sendPacket(packet) {
+ let repeats = this.passes;
+ /**
+ * Emitted whenever the dispatcher has debug information.
+ * @event StreamDispatcher#debug
+ * @param {string} info The debug info
+ */
+ this.setSpeaking(true);
+ while (repeats-- && this.player.voiceConnection.sockets.udp) {
+ this.player.voiceConnection.sockets.udp.send(packet)
+ .catch(e => {
+ this.setSpeaking(false);
+ this.emit('debug', `Failed to send a packet ${e}`);
+ });
+ }
+ }
+
+ createPacket(sequence, timestamp, buffer) {
+ const packetBuffer = Buffer.alloc(buffer.length + 28);
+ packetBuffer.fill(0);
+ packetBuffer[0] = 0x80;
+ packetBuffer[1] = 0x78;
+
+ packetBuffer.writeUIntBE(sequence, 2, 2);
+ packetBuffer.writeUIntBE(timestamp, 4, 4);
+ packetBuffer.writeUIntBE(this.player.voiceConnection.authentication.ssrc, 8, 4);
+
+ packetBuffer.copy(nonce, 0, 0, 12);
+ buffer = secretbox.methods.close(buffer, nonce, this.player.voiceConnection.authentication.secretKey.key);
+ for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i];
+
+ return packetBuffer;
+ }
+
+ processPacket(packet) {
+ try {
+ if (this.destroyed || !this.player.voiceConnection.authentication.secretKey) {
+ this.setSpeaking(false);
+ return;
+ }
+
+ const data = this.streamingData;
+
+ if (this.paused) {
+ this.setSpeaking(false);
+ data.pausedTime = data.length * 10;
+ return;
+ }
+
+ if (!packet) {
+ data.missed++;
+ data.pausedTime += data.length * 10;
+ return;
+ }
+
+ this.started();
+ this.missed = 0;
+
+ this.stepStreamingData();
+ this.sendBuffer(null, data.sequence, data.timestamp, packet);
+ } catch (e) {
+ this.destroy('error', e);
+ }
+ }
+
+ process() {
+ try {
+ if (this.destroyed) {
+ this.setSpeaking(false);
+ return;
+ }
+
+ const data = this.streamingData;
+
+ if (data.missed >= 5) {
+ this.destroy('end', 'Stream is not generating quickly enough.');
+ return;
+ }
+
+ if (this.paused) {
+ this.setSpeaking(false);
+ // Old code?
+ // data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
+ data.pausedTime += data.length * 10;
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), data.length * 10);
+ return;
+ }
+
+ this.started();
+
+ const buffer = this.readStreamBuffer();
+ if (!buffer) {
+ data.missed++;
+ data.pausedTime += data.length * 10;
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), data.length * 10);
+ return;
+ }
+
+ data.missed = 0;
+
+ this.stepStreamingData();
+
+ if (this._opus) {
+ this.sendBuffer(null, data.sequence, data.timestamp, buffer);
+ } else {
+ this.sendBuffer(buffer, data.sequence, data.timestamp);
+ }
+
+ const nextTime = data.length + (data.startTime + data.pausedTime + (data.count * data.length) - Date.now());
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), nextTime);
+ } catch (e) {
+ this.destroy('error', e);
+ }
+ }
+
+ readStreamBuffer() {
+ const data = this.streamingData;
+ const bufferLength = (this._opus ? 80 : 1920) * data.channels;
+ let buffer = this.stream.read(bufferLength);
+ if (this._opus) return buffer;
+ if (!buffer) return null;
+
+ if (buffer.length !== bufferLength) {
+ const newBuffer = Buffer.alloc(bufferLength).fill(0);
+ buffer.copy(newBuffer);
+ buffer = newBuffer;
+ }
+
+ buffer = this.applyVolume(buffer);
+ return buffer;
+ }
+
+ started() {
+ const data = this.streamingData;
+
+ if (!data.startTime) {
+ /**
+ * Emitted once the dispatcher starts streaming.
+ * @event StreamDispatcher#start
+ */
+ this.emit('start');
+ data.startTime = Date.now();
+ }
+ }
+
+ stepStreamingData() {
+ const data = this.streamingData;
+ data.count++;
+ data.sequence = data.sequence < 65535 ? data.sequence + 1 : 0;
+ data.timestamp = (data.timestamp + 960) < 4294967295 ? data.timestamp + 960 : 0;
+ }
+
+ destroy(type, reason) {
+ if (this.destroyed) return;
+ this.destroyed = true;
+ this.setSpeaking(false);
+ this.emit(type, reason);
+ /**
+ * Emitted once the dispatcher ends.
+ * @param {string} [reason] The reason the dispatcher ended
+ * @event StreamDispatcher#end
+ */
+ if (type !== 'end') this.emit('end', `destroyed due to ${type} - ${reason}`);
+ }
+
+ startStreaming() {
+ if (!this.stream) {
+ /**
+ * Emitted if the dispatcher encounters an error.
+ * @event StreamDispatcher#error
+ * @param {string} error The error message
+ */
+ this.emit('error', 'No stream');
+ return;
+ }
+
+ this.stream.on('end', err => this.destroy('end', err || 'stream'));
+ this.stream.on('error', err => this.destroy('error', err));
+
+ const data = this.streamingData;
+ data.length = 20;
+ data.missed = 0;
+
+ this.stream.once('readable', () => {
+ data.startTime = null;
+ data.count = 0;
+ this.process();
+ });
+ }
+
+ setPaused(paused) { this.setSpeaking(!(this.paused = paused)); }
+}
+
+module.exports = StreamDispatcher;
diff --git a/node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js b/node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js
new file mode 100644
index 0000000..a510449
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js
@@ -0,0 +1,60 @@
+/**
+ * The base opus encoding engine.
+ * @private
+ */
+class BaseOpus {
+ /**
+ * @param {Object} [options] The options to apply to the Opus engine
+ * @param {number} [options.bitrate=48] The desired bitrate (kbps)
+ * @param {boolean} [options.fec=false] Whether to enable forward error correction
+ * @param {number} [options.plp=0] The expected packet loss percentage
+ */
+ constructor({ bitrate = 48, fec = false, plp = 0 } = {}) {
+ this.ctl = {
+ BITRATE: 4002,
+ FEC: 4012,
+ PLP: 4014,
+ };
+
+ this.samplingRate = 48000;
+ this.channels = 2;
+
+ /**
+ * The desired bitrate (kbps)
+ * @type {number}
+ */
+ this.bitrate = bitrate;
+
+ /**
+ * Miscellaneous Opus options
+ * @type {Object}
+ */
+ this.options = { fec, plp };
+ }
+
+ init() {
+ try {
+ this.setBitrate(this.bitrate);
+
+ // Set FEC (forward error correction)
+ if (this.options.fec) this.setFEC(this.options.fec);
+
+ // Set PLP (expected packet loss percentage)
+ if (this.options.plp) this.setPLP(this.options.plp);
+ } catch (err) {
+ // Opus engine likely has no support for libopus CTL
+ }
+ }
+
+ encode(buffer) {
+ return buffer;
+ }
+
+ decode(buffer) {
+ return buffer;
+ }
+
+ destroy() {} // eslint-disable-line no-empty-function
+}
+
+module.exports = BaseOpus;
diff --git a/node_modules/discord.js/src/client/voice/opus/DiscordJsOpusEngine.js b/node_modules/discord.js/src/client/voice/opus/DiscordJsOpusEngine.js
new file mode 100644
index 0000000..a3759c2
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/DiscordJsOpusEngine.js
@@ -0,0 +1,34 @@
+const OpusEngine = require('./BaseOpusEngine');
+
+class DiscordJsOpusEngine extends OpusEngine {
+ constructor(player) {
+ super(player);
+ const opus = require('@discordjs/opus');
+ this.encoder = new opus.OpusEncoder(this.samplingRate, this.channels);
+ super.init();
+ }
+
+ setBitrate(bitrate) {
+ this.encoder.setBitrate(Math.min(128, Math.max(16, bitrate)) * 1000);
+ }
+
+ setFEC(enabled) {
+ this.encoder.applyEncoderCTL(this.ctl.FEC, enabled ? 1 : 0);
+ }
+
+ setPLP(percent) {
+ this.encoder.applyEncoderCTL(this.ctl.PLP, Math.min(100, Math.max(0, percent * 100)));
+ }
+
+ encode(buffer) {
+ super.encode(buffer);
+ return this.encoder.encode(buffer, 1920);
+ }
+
+ decode(buffer) {
+ super.decode(buffer);
+ return this.encoder.decode(buffer, 1920);
+ }
+}
+
+module.exports = DiscordJsOpusEngine;
diff --git a/node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js b/node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js
new file mode 100644
index 0000000..a9ffc55
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js
@@ -0,0 +1,34 @@
+const OpusEngine = require('./BaseOpusEngine');
+
+class NodeOpusEngine extends OpusEngine {
+ constructor(player) {
+ super(player);
+ const opus = require('node-opus');
+ this.encoder = new opus.OpusEncoder(this.samplingRate, this.channels);
+ super.init();
+ }
+
+ setBitrate(bitrate) {
+ this.encoder.applyEncoderCTL(this.ctl.BITRATE, Math.min(128, Math.max(16, bitrate)) * 1000);
+ }
+
+ setFEC(enabled) {
+ this.encoder.applyEncoderCTL(this.ctl.FEC, enabled ? 1 : 0);
+ }
+
+ setPLP(percent) {
+ this.encoder.applyEncoderCTL(this.ctl.PLP, Math.min(100, Math.max(0, percent * 100)));
+ }
+
+ encode(buffer) {
+ super.encode(buffer);
+ return this.encoder.encode(buffer, 1920);
+ }
+
+ decode(buffer) {
+ super.decode(buffer);
+ return this.encoder.decode(buffer, 1920);
+ }
+}
+
+module.exports = NodeOpusEngine;
diff --git a/node_modules/discord.js/src/client/voice/opus/OpusEngineList.js b/node_modules/discord.js/src/client/voice/opus/OpusEngineList.js
new file mode 100644
index 0000000..cf63fbc
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/OpusEngineList.js
@@ -0,0 +1,29 @@
+const list = [
+ require('./DiscordJsOpusEngine'),
+ require('./NodeOpusEngine'),
+ require('./OpusScriptEngine'),
+];
+
+function fetch(Encoder, engineOptions) {
+ try {
+ return new Encoder(engineOptions);
+ } catch (err) {
+ if (err.message.includes('Cannot find module')) return null;
+
+ // The Opus engine exists, but another error occurred.
+ throw err;
+ }
+}
+
+exports.add = encoder => {
+ list.push(encoder);
+};
+
+exports.fetch = engineOptions => {
+ for (const encoder of list) {
+ const fetched = fetch(encoder, engineOptions);
+ if (fetched) return fetched;
+ }
+
+ throw new Error('Couldn\'t find an Opus engine.');
+};
diff --git a/node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js b/node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js
new file mode 100644
index 0000000..271a068
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js
@@ -0,0 +1,39 @@
+const OpusEngine = require('./BaseOpusEngine');
+
+class OpusScriptEngine extends OpusEngine {
+ constructor(player) {
+ super(player);
+ const OpusScript = require('opusscript');
+ this.encoder = new OpusScript(this.samplingRate, this.channels);
+ super.init();
+ }
+
+ setBitrate(bitrate) {
+ this.encoder.encoderCTL(this.ctl.BITRATE, Math.min(128, Math.max(16, bitrate)) * 1000);
+ }
+
+ setFEC(enabled) {
+ this.encoder.encoderCTL(this.ctl.FEC, enabled ? 1 : 0);
+ }
+
+ setPLP(percent) {
+ this.encoder.encoderCTL(this.ctl.PLP, Math.min(100, Math.max(0, percent * 100)));
+ }
+
+ encode(buffer) {
+ super.encode(buffer);
+ return this.encoder.encode(buffer, 960);
+ }
+
+ decode(buffer) {
+ super.decode(buffer);
+ return this.encoder.decode(buffer);
+ }
+
+ destroy() {
+ super.destroy();
+ this.encoder.delete();
+ }
+}
+
+module.exports = OpusScriptEngine;
diff --git a/node_modules/discord.js/src/client/voice/player/AudioPlayer.js b/node_modules/discord.js/src/client/voice/player/AudioPlayer.js
new file mode 100644
index 0000000..9b30c85
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/player/AudioPlayer.js
@@ -0,0 +1,170 @@
+const EventEmitter = require('events').EventEmitter;
+const Prism = require('prism-media');
+const StreamDispatcher = require('../dispatcher/StreamDispatcher');
+const Collection = require('../../../util/Collection');
+const OpusEncoders = require('../opus/OpusEngineList');
+
+const ffmpegArguments = [
+ '-analyzeduration', '0',
+ '-loglevel', '0',
+ '-f', 's16le',
+ '-ar', '48000',
+ '-ac', '2',
+];
+
+/**
+ * An Audio Player for a Voice Connection.
+ * @private
+ * @extends {EventEmitter}
+ */
+class AudioPlayer extends EventEmitter {
+ constructor(voiceConnection) {
+ super();
+ /**
+ * The voice connection that the player serves
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = voiceConnection;
+ /**
+ * The prism transcoder that the player uses
+ * @type {Prism}
+ */
+ this.prism = new Prism();
+ this.streams = new Collection();
+ this.currentStream = {};
+ this.streamingData = {
+ channels: 2,
+ count: 0,
+ sequence: 0,
+ timestamp: 0,
+ pausedTime: 0,
+ };
+ this.voiceConnection.once('closing', () => this.destroyCurrentStream());
+ }
+
+ /**
+ * The current transcoder
+ * @type {?Object}
+ * @readonly
+ */
+ get transcoder() {
+ return this.currentStream.transcoder;
+ }
+
+ /**
+ * The current dispatcher
+ * @type {?StreamDispatcher}
+ * @readonly
+ */
+ get dispatcher() {
+ return this.currentStream.dispatcher;
+ }
+
+ destroy() {
+ if (this.opusEncoder) this.opusEncoder.destroy();
+ this.opusEncoder = null;
+ }
+
+ destroyCurrentStream() {
+ const transcoder = this.transcoder;
+ const dispatcher = this.dispatcher;
+ if (transcoder) transcoder.kill();
+ if (dispatcher) {
+ const end = dispatcher.listeners('end')[0];
+ const error = dispatcher.listeners('error')[0];
+ if (end) dispatcher.removeListener('end', end);
+ if (error) dispatcher.removeListener('error', error);
+ dispatcher.destroy('end');
+ }
+ this.currentStream = {};
+ }
+
+ /**
+ * Set the bitrate of the current Opus encoder.
+ * @param {number} value New bitrate, in kbps
+ * If set to 'auto', the voice channel's bitrate will be used
+ */
+ setBitrate(value) {
+ if (!value) return;
+ if (!this.opusEncoder) return;
+ const bitrate = value === 'auto' ? this.voiceConnection.channel.bitrate : value;
+ this.opusEncoder.setBitrate(bitrate);
+ }
+
+ playUnknownStream(stream, options = {}) {
+ this.destroy();
+ this.opusEncoder = OpusEncoders.fetch(options);
+ const transcoder = this.prism.transcode({
+ type: 'ffmpeg',
+ media: stream,
+ ffmpegArguments: ffmpegArguments.concat(['-ss', String(options.seek || 0)]),
+ });
+ this.destroyCurrentStream();
+ this.currentStream = {
+ transcoder: transcoder,
+ output: transcoder.output,
+ input: stream,
+ };
+ transcoder.on('error', e => {
+ this.destroyCurrentStream();
+ if (this.listenerCount('error') > 0) this.emit('error', e);
+ this.emit('warn', `prism transcoder error - ${e}`);
+ });
+ return this.playPCMStream(transcoder.output, options, true);
+ }
+
+ playPCMStream(stream, options = {}, fromUnknown = false) {
+ this.destroy();
+ this.opusEncoder = OpusEncoders.fetch(options);
+ this.setBitrate(options.bitrate);
+ const dispatcher = this.createDispatcher(stream, options);
+ if (fromUnknown) {
+ this.currentStream.dispatcher = dispatcher;
+ } else {
+ this.destroyCurrentStream();
+ this.currentStream = {
+ dispatcher,
+ input: stream,
+ output: stream,
+ };
+ }
+ return dispatcher;
+ }
+
+ playOpusStream(stream, options = {}) {
+ options.opus = true;
+ this.destroyCurrentStream();
+ const dispatcher = this.createDispatcher(stream, options);
+ this.currentStream = {
+ dispatcher,
+ input: stream,
+ output: stream,
+ };
+ return dispatcher;
+ }
+
+ playBroadcast(broadcast, options) {
+ this.destroyCurrentStream();
+ const dispatcher = this.createDispatcher(broadcast, options);
+ this.currentStream = {
+ dispatcher,
+ broadcast,
+ input: broadcast,
+ output: broadcast,
+ };
+ broadcast.registerDispatcher(dispatcher);
+ return dispatcher;
+ }
+
+ createDispatcher(stream, { seek = 0, volume = 1, passes = 1, opus } = {}) {
+ const options = { seek, volume, passes, opus };
+
+ const dispatcher = new StreamDispatcher(this, stream, options);
+ dispatcher.on('end', () => this.destroyCurrentStream());
+ dispatcher.on('error', () => this.destroyCurrentStream());
+ dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value));
+ return dispatcher;
+ }
+}
+
+module.exports = AudioPlayer;
diff --git a/node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js b/node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js
new file mode 100644
index 0000000..d349428
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js
@@ -0,0 +1,17 @@
+const Readable = require('stream').Readable;
+
+class VoiceReadable extends Readable {
+ constructor() {
+ super();
+ this._packets = [];
+ this.open = true;
+ }
+
+ _read() {} // eslint-disable-line no-empty-function
+
+ _push(d) {
+ if (this.open) this.push(d);
+ }
+}
+
+module.exports = VoiceReadable;
diff --git a/node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js b/node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js
new file mode 100644
index 0000000..e37b273
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js
@@ -0,0 +1,220 @@
+const EventEmitter = require('events').EventEmitter;
+const secretbox = require('../util/Secretbox');
+const Readable = require('./VoiceReadable');
+const OpusEncoders = require('../opus/OpusEngineList');
+
+const nonce = Buffer.alloc(24);
+nonce.fill(0);
+
+/**
+ * Receives voice data from a voice connection.
+ * ```js
+ * // Obtained using:
+ * voiceChannel.join()
+ * .then(connection => {
+ * const receiver = connection.createReceiver();
+ * });
+ * ```
+ * @extends {EventEmitter}
+ */
+class VoiceReceiver extends EventEmitter {
+ constructor(connection) {
+ super();
+ /*
+ Need a queue because we don't get the ssrc of the user speaking until after the first few packets,
+ so we queue up unknown SSRCs until they become known, then empty the queue
+ */
+ this.queues = new Map();
+ this.pcmStreams = new Map();
+ this.opusStreams = new Map();
+ this.opusEncoders = new Map();
+
+ /**
+ * Whether or not this receiver has been destroyed
+ * @type {boolean}
+ */
+ this.destroyed = false;
+
+ /**
+ * The VoiceConnection that instantiated this
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = connection;
+
+ this._listener = msg => {
+ const ssrc = +msg.readUInt32BE(8).toString(10);
+ const user = connection.client.users.get(connection.ssrcMap.get(ssrc));
+ if (!user) {
+ if (!this.queues.has(ssrc)) this.queues.set(ssrc, []);
+ this.queues.get(ssrc).push(msg);
+ } else {
+ if (this.queues.get(ssrc)) {
+ this.queues.get(ssrc).push(msg);
+ this.queues.get(ssrc).map(m => this.handlePacket(m, user));
+ this.queues.delete(ssrc);
+ return;
+ }
+ this.handlePacket(msg, user);
+ }
+ };
+ this.voiceConnection.sockets.udp.socket.on('message', this._listener);
+ }
+
+ /**
+ * If this VoiceReceiver has been destroyed, running `recreate()` will recreate the listener.
+ * This avoids you having to create a new receiver.
+ * <info>Any streams that you had prior to destroying the receiver will not be recreated.</info>
+ */
+ recreate() {
+ if (!this.destroyed) return;
+ this.voiceConnection.sockets.udp.socket.on('message', this._listener);
+ this.destroyed = false;
+ }
+
+ /**
+ * Destroy this VoiceReceiver, also ending any streams that it may be controlling.
+ */
+ destroy() {
+ this.voiceConnection.sockets.udp.socket.removeListener('message', this._listener);
+ for (const [id, stream] of this.pcmStreams) {
+ stream._push(null);
+ this.pcmStreams.delete(id);
+ }
+ for (const [id, stream] of this.opusStreams) {
+ stream._push(null);
+ this.opusStreams.delete(id);
+ }
+ for (const [id, encoder] of this.opusEncoders) {
+ encoder.destroy();
+ this.opusEncoders.delete(id);
+ }
+ this.destroyed = true;
+ }
+
+ /**
+ * Invoked when a user stops speaking.
+ * @param {User} user The user that stopped speaking
+ * @private
+ */
+ stoppedSpeaking(user) {
+ const opusStream = this.opusStreams.get(user.id);
+ const pcmStream = this.pcmStreams.get(user.id);
+ const opusEncoder = this.opusEncoders.get(user.id);
+ if (opusStream) {
+ opusStream.push(null);
+ opusStream.open = false;
+ this.opusStreams.delete(user.id);
+ }
+ if (pcmStream) {
+ pcmStream.push(null);
+ pcmStream.open = false;
+ this.pcmStreams.delete(user.id);
+ }
+ if (opusEncoder) {
+ opusEncoder.destroy();
+ this.opusEncoders.delete(user.id);
+ }
+ }
+
+ /**
+ * Creates a readable stream for a user that provides opus data while the user is speaking. When the user
+ * stops speaking, the stream is destroyed.
+ * @param {UserResolvable} user The user to create the stream for
+ * @returns {ReadableStream}
+ */
+ createOpusStream(user) {
+ user = this.voiceConnection.voiceManager.client.resolver.resolveUser(user);
+ if (!user) throw new Error('Couldn\'t resolve the user to create Opus stream.');
+ if (this.opusStreams.get(user.id)) throw new Error('There is already an existing stream for that user.');
+ const stream = new Readable();
+ this.opusStreams.set(user.id, stream);
+ return stream;
+ }
+
+ /**
+ * Creates a readable stream for a user that provides PCM data while the user is speaking. When the user
+ * stops speaking, the stream is destroyed. The stream is 16-bit signed stereo PCM at 48KHz.
+ * @param {UserResolvable} user The user to create the stream for
+ * @returns {ReadableStream}
+ */
+ createPCMStream(user) {
+ user = this.voiceConnection.voiceManager.client.resolver.resolveUser(user);
+ if (!user) throw new Error('Couldn\'t resolve the user to create PCM stream.');
+ if (this.pcmStreams.get(user.id)) throw new Error('There is already an existing stream for that user.');
+ const stream = new Readable();
+ this.pcmStreams.set(user.id, stream);
+ return stream;
+ }
+
+ handlePacket(msg, user) {
+ msg.copy(nonce, 0, 0, 12);
+ let data = secretbox.methods.open(msg.slice(12), nonce, this.voiceConnection.authentication.secretKey.key);
+ if (!data) {
+ /**
+ * Emitted whenever a voice packet experiences a problem.
+ * @event VoiceReceiver#warn
+ * @param {string} reason The reason for the warning. If it happened because the voice packet could not be
+ * decrypted, this would be `decrypt`. If it happened because the voice packet could not be decoded into
+ * PCM, this would be `decode`
+ * @param {string} message The warning message
+ */
+ this.emit('warn', 'decrypt', 'Failed to decrypt voice packet');
+ return;
+ }
+ data = Buffer.from(data);
+
+ // Strip RTP Header Extensions (one-byte only)
+ if (data[0] === 0xBE && data[1] === 0xDE && data.length > 4) {
+ const headerExtensionLength = data.readUInt16BE(2);
+ let offset = 4;
+ for (let i = 0; i < headerExtensionLength; i++) {
+ const byte = data[offset];
+ offset++;
+ if (byte === 0) {
+ continue;
+ }
+ offset += 1 + (0b1111 & (byte >> 4));
+ }
+ // Skip over undocumented Discord byte
+ offset++;
+
+ data = data.slice(offset);
+ }
+
+ if (this.opusStreams.get(user.id)) this.opusStreams.get(user.id)._push(data);
+ /**
+ * Emitted whenever voice data is received from the voice connection. This is _always_ emitted (unlike PCM).
+ * @event VoiceReceiver#opus
+ * @param {User} user The user that is sending the buffer (is speaking)
+ * @param {Buffer} buffer The opus buffer
+ */
+ this.emit('opus', user, data);
+ if (this.listenerCount('pcm') > 0 || this.pcmStreams.size > 0) {
+ if (!this.opusEncoders.get(user.id)) this.opusEncoders.set(user.id, OpusEncoders.fetch());
+ const { pcm, error } = VoiceReceiver._tryDecode(this.opusEncoders.get(user.id), data);
+ if (error) {
+ this.emit('warn', 'decode', `Failed to decode packet voice to PCM because: ${error.message}`);
+ return;
+ }
+ if (this.pcmStreams.get(user.id)) this.pcmStreams.get(user.id)._push(pcm);
+ /**
+ * Emits decoded voice data when it's received. For performance reasons, the decoding will only
+ * happen if there is at least one `pcm` listener on this receiver.
+ * @event VoiceReceiver#pcm
+ * @param {User} user The user that is sending the buffer (is speaking)
+ * @param {Buffer} buffer The decoded buffer
+ */
+ this.emit('pcm', user, pcm);
+ }
+ }
+
+ static _tryDecode(encoder, data) {
+ try {
+ return { pcm: encoder.decode(data) };
+ } catch (error) {
+ return { error };
+ }
+ }
+}
+
+module.exports = VoiceReceiver;
diff --git a/node_modules/discord.js/src/client/voice/util/SecretKey.js b/node_modules/discord.js/src/client/voice/util/SecretKey.js
new file mode 100644
index 0000000..670e9e5
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/util/SecretKey.js
@@ -0,0 +1,16 @@
+/**
+ * Represents a Secret Key used in encryption over voice.
+ * @private
+ */
+class SecretKey {
+ constructor(key) {
+ /**
+ * The key used for encryption
+ * @type {Uint8Array}
+ */
+ this.key = new Uint8Array(new ArrayBuffer(key.length));
+ for (const index of Object.keys(key)) this.key[index] = key[index];
+ }
+}
+
+module.exports = SecretKey;
diff --git a/node_modules/discord.js/src/client/voice/util/Secretbox.js b/node_modules/discord.js/src/client/voice/util/Secretbox.js
new file mode 100644
index 0000000..a833d51
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/util/Secretbox.js
@@ -0,0 +1,33 @@
+const libs = {
+ sodium: sodium => ({
+ open: sodium.api.crypto_secretbox_open_easy,
+ close: sodium.api.crypto_secretbox_easy,
+ }),
+ 'libsodium-wrappers': sodium => ({
+ open: sodium.crypto_secretbox_open_easy,
+ close: sodium.crypto_secretbox_easy,
+ }),
+ tweetnacl: tweetnacl => ({
+ open: tweetnacl.secretbox.open,
+ close: tweetnacl.secretbox,
+ }),
+};
+
+exports.methods = {};
+
+for (const libName of Object.keys(libs)) {
+ try {
+ const lib = require(libName);
+ if (libName === 'libsodium-wrappers' && lib.ready) {
+ lib.ready.then(() => {
+ exports.methods = libs[libName](lib);
+ }).catch(() => {
+ const tweetnacl = require('tweetnacl');
+ exports.methods = libs.tweetnacl(tweetnacl);
+ }).catch(() => undefined);
+ } else {
+ exports.methods = libs[libName](lib);
+ }
+ break;
+ } catch (err) {} // eslint-disable-line no-empty
+}
diff --git a/node_modules/discord.js/src/client/voice/util/Silence.js b/node_modules/discord.js/src/client/voice/util/Silence.js
new file mode 100644
index 0000000..239ceb4
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/util/Silence.js
@@ -0,0 +1,16 @@
+const { Readable } = require('stream');
+
+const SILENCE_FRAME = Buffer.from([0xF8, 0xFF, 0xFE]);
+
+/**
+ * A readable emitting silent opus frames.
+ * @extends {Readable}
+ * @private
+ */
+class Silence extends Readable {
+ _read() {
+ this.push(SILENCE_FRAME);
+ }
+}
+
+module.exports = Silence;
diff --git a/node_modules/discord.js/src/client/voice/util/SingleSilence.js b/node_modules/discord.js/src/client/voice/util/SingleSilence.js
new file mode 100644
index 0000000..b59341e
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/util/SingleSilence.js
@@ -0,0 +1,17 @@
+const Silence = require('./Silence');
+
+/**
+ * Only emits a single silent opus frame.
+ * This is used as a workaround for Discord now requiring
+ * silence to be sent before being able to receive audio.
+ * @extends {Silence}
+ * @private
+ */
+class SingleSilence extends Silence {
+ _read() {
+ super._read();
+ this.push(null);
+ }
+}
+
+module.exports = SingleSilence;
diff --git a/node_modules/discord.js/src/client/voice/util/VolumeInterface.js b/node_modules/discord.js/src/client/voice/util/VolumeInterface.js
new file mode 100644
index 0000000..62e6da5
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/util/VolumeInterface.js
@@ -0,0 +1,86 @@
+const EventEmitter = require('events');
+
+/**
+ * An interface class for volume transformation.
+ * @extends {EventEmitter}
+ */
+class VolumeInterface extends EventEmitter {
+ constructor({ volume = 1 } = {}) {
+ super();
+ this.setVolume(volume);
+ }
+
+ /**
+ * The current volume of the broadcast
+ * @readonly
+ * @type {number}
+ */
+ get volume() {
+ return this._volume;
+ }
+
+ /**
+ * The current volume of the broadcast in decibels
+ * @readonly
+ * @type {number}
+ */
+ get volumeDecibels() {
+ return Math.log10(this._volume) * 20;
+ }
+
+ /**
+ * The current volume of the broadcast from a logarithmic scale
+ * @readonly
+ * @type {number}
+ */
+ get volumeLogarithmic() {
+ return Math.pow(this._volume, 1 / 1.660964);
+ }
+
+ applyVolume(buffer, volume) {
+ volume = volume || this._volume;
+ if (volume === 1) return buffer;
+
+ const out = Buffer.alloc(buffer.length);
+ for (let i = 0; i < buffer.length; i += 2) {
+ if (i >= buffer.length - 1) break;
+ const uint = Math.min(32767, Math.max(-32767, Math.floor(volume * buffer.readInt16LE(i))));
+ out.writeInt16LE(uint, i);
+ }
+
+ return out;
+ }
+
+ /**
+ * Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double.
+ * @param {number} volume The volume that you want to set
+ */
+ setVolume(volume) {
+ /**
+ * Emitted when the volume of this interface changes.
+ * @event VolumeInterface#volumeChange
+ * @param {number} oldVolume The old volume of this interface
+ * @param {number} newVolume The new volume of this interface
+ */
+ this.emit('volumeChange', this._volume, volume);
+ this._volume = volume;
+ }
+
+ /**
+ * Set the volume in decibels.
+ * @param {number} db The decibels
+ */
+ setVolumeDecibels(db) {
+ this.setVolume(Math.pow(10, db / 20));
+ }
+
+ /**
+ * Set the volume so that a perceived value of 0.5 is half the perceived volume etc.
+ * @param {number} value The value for the volume
+ */
+ setVolumeLogarithmic(value) {
+ this.setVolume(Math.pow(value, 1.660964));
+ }
+}
+
+module.exports = VolumeInterface;
diff --git a/node_modules/discord.js/src/client/websocket/WebSocketConnection.js b/node_modules/discord.js/src/client/websocket/WebSocketConnection.js
new file mode 100644
index 0000000..4ea078b
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/WebSocketConnection.js
@@ -0,0 +1,509 @@
+const browser = typeof window !== 'undefined';
+const EventEmitter = require('events');
+const Constants = require('../../util/Constants');
+const zlib = require('zlib');
+const PacketManager = require('./packets/WebSocketPacketManager');
+const erlpack = (function findErlpack() {
+ try {
+ const e = require('erlpack');
+ if (!e.pack) return null;
+ return e;
+ } catch (e) {
+ return null;
+ }
+}());
+
+const WebSocket = (function findWebSocket() {
+ if (browser) return window.WebSocket; // eslint-disable-line no-undef
+ try {
+ const uws = require('@discordjs/uws');
+ process.emitWarning('uws support is being removed in the next version of discord.js',
+ 'DeprecationWarning', findWebSocket);
+ return uws;
+ } catch (e) {
+ return require('ws');
+ }
+}());
+
+/**
+ * Abstracts a WebSocket connection with decoding/encoding for the Discord gateway.
+ * @private
+ */
+class WebSocketConnection extends EventEmitter {
+ /**
+ * @param {WebSocketManager} manager The WebSocket manager
+ * @param {string} gateway The WebSocket gateway to connect to
+ */
+ constructor(manager, gateway) {
+ super();
+ /**
+ * The WebSocket Manager of this connection
+ * @type {WebSocketManager}
+ */
+ this.manager = manager;
+
+ /**
+ * The client this belongs to
+ * @type {Client}
+ */
+ this.client = manager.client;
+
+ /**
+ * The WebSocket connection itself
+ * @type {WebSocket}
+ */
+ this.ws = null;
+
+ /**
+ * The current sequence of the WebSocket
+ * @type {number}
+ */
+ this.sequence = -1;
+
+ /**
+ * The current status of the client
+ * @type {Status}
+ */
+ this.status = Constants.Status.IDLE;
+
+ /**
+ * The Packet Manager of the connection
+ * @type {WebSocketPacketManager}
+ */
+ this.packetManager = new PacketManager(this);
+
+ /**
+ * The last time a ping was sent (a timestamp)
+ * @type {number}
+ */
+ this.lastPingTimestamp = 0;
+
+ /**
+ * Contains the rate limit queue and metadata
+ * @type {Object}
+ */
+ this.ratelimit = {
+ queue: [],
+ remaining: 120,
+ total: 120,
+ time: 60e3,
+ resetTimer: null,
+ };
+ this.connect(gateway);
+
+ /**
+ * Events that are disabled (will not be processed)
+ * @type {Object}
+ */
+ this.disabledEvents = {};
+
+ /**
+ * The sequence on WebSocket close
+ * @type {number}
+ */
+ this.closeSequence = 0;
+
+ /**
+ * Whether or not the WebSocket is expecting to be closed
+ * @type {boolean}
+ */
+ this.expectingClose = false;
+ for (const event of this.client.options.disabledEvents) this.disabledEvents[event] = true;
+ }
+
+ /**
+ * Causes the client to be marked as ready and emits the ready event.
+ * @returns {void}
+ */
+ triggerReady() {
+ if (this.status === Constants.Status.READY) {
+ this.debug('Tried to mark self as ready, but already ready');
+ return;
+ }
+ /**
+ * Emitted when the client becomes ready to start working.
+ * @event Client#ready
+ */
+ this.status = Constants.Status.READY;
+ this.client.emit(Constants.Events.READY);
+ this.packetManager.handleQueue();
+ }
+
+ /**
+ * Checks whether the client is ready to be marked as ready.
+ * @returns {void}
+ */
+ checkIfReady() {
+ if (this.status === Constants.Status.READY || this.status === Constants.Status.NEARLY) return false;
+ let unavailableGuilds = 0;
+ for (const guild of this.client.guilds.values()) {
+ if (!guild.available) unavailableGuilds++;
+ }
+ if (unavailableGuilds === 0) {
+ this.status = Constants.Status.NEARLY;
+ if (!this.client.options.fetchAllMembers) return this.triggerReady();
+ // Fetch all members before marking self as ready
+ const promises = this.client.guilds.map(g => g.fetchMembers());
+ Promise.all(promises)
+ .then(() => this.triggerReady())
+ .catch(e => {
+ this.debug(`Failed to fetch all members before ready! ${e}`);
+ this.triggerReady();
+ });
+ }
+ return true;
+ }
+
+ // Util
+ /**
+ * Emits a debug message.
+ * @param {string} message Debug message
+ * @returns {void}
+ */
+ debug(message) {
+ if (message instanceof Error) message = message.stack;
+ return this.manager.debug(`[connection] ${message}`);
+ }
+
+ /**
+ * Attempts to serialise data from the WebSocket.
+ * @param {string|Object} data Data to unpack
+ * @returns {Object}
+ */
+ unpack(data) {
+ if (data instanceof ArrayBuffer) data = Buffer.from(new Uint8Array(data));
+
+ if (erlpack && typeof data !== 'string') return erlpack.unpack(data);
+ else if (data instanceof Buffer) data = zlib.inflateSync(data).toString();
+
+ return JSON.parse(data);
+ }
+
+ /**
+ * Packs an object ready to be sent.
+ * @param {Object} data Data to pack
+ * @returns {string|Buffer}
+ */
+ pack(data) {
+ return erlpack ? erlpack.pack(data) : JSON.stringify(data);
+ }
+
+ /**
+ * Processes the current WebSocket queue.
+ */
+ processQueue() {
+ if (this.ratelimit.remaining === 0) return;
+ if (this.ratelimit.queue.length === 0) return;
+ if (this.ratelimit.remaining === this.ratelimit.total) {
+ this.ratelimit.resetTimer = this.client.setTimeout(() => {
+ this.ratelimit.remaining = this.ratelimit.total;
+ this.processQueue();
+ }, this.ratelimit.time);
+ }
+ while (this.ratelimit.remaining > 0) {
+ const item = this.ratelimit.queue.shift();
+ if (!item) return;
+ this._send(item);
+ this.ratelimit.remaining--;
+ }
+ }
+
+ /**
+ * Sends data, bypassing the queue.
+ * @param {Object} data Packet to send
+ * @returns {void}
+ */
+ _send(data) {
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ this.debug(`Tried to send packet ${JSON.stringify(data)} but no WebSocket is available!`);
+ return;
+ }
+ this.ws.send(this.pack(data));
+ }
+
+ /**
+ * Adds data to the queue to be sent.
+ * @param {Object} data Packet to send
+ * @returns {void}
+ */
+ send(data) {
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ this.debug(`Tried to send packet ${JSON.stringify(data)} but no WebSocket is available!`);
+ return;
+ }
+ this.ratelimit.queue.push(data);
+ this.processQueue();
+ }
+
+ /**
+ * Creates a connection to a gateway.
+ * @param {string} gateway The gateway to connect to
+ * @param {number} [after=0] How long to wait before connecting
+ * @param {boolean} [force=false] Whether or not to force a new connection even if one already exists
+ * @returns {boolean}
+ */
+ connect(gateway = this.gateway, after = 0, force = false) {
+ if (after) return this.client.setTimeout(() => this.connect(gateway, 0, force), after); // eslint-disable-line
+ if (this.ws && !force) {
+ this.debug('WebSocket connection already exists');
+ return false;
+ } else if (typeof gateway !== 'string') {
+ this.debug(`Tried to connect to an invalid gateway: ${gateway}`);
+ return false;
+ }
+ this.expectingClose = false;
+ this.gateway = gateway;
+ this.debug(`Connecting to ${gateway}`);
+ const ws = this.ws = new WebSocket(gateway);
+ if (browser) ws.binaryType = 'arraybuffer';
+ ws.onmessage = this.onMessage.bind(this);
+ ws.onopen = this.onOpen.bind(this);
+ ws.onerror = this.onError.bind(this);
+ ws.onclose = this.onClose.bind(this);
+ this.status = Constants.Status.CONNECTING;
+ return true;
+ }
+
+ /**
+ * Destroys the connection.
+ * @returns {boolean}
+ */
+ destroy() {
+ const ws = this.ws;
+ if (!ws) {
+ this.debug('Attempted to destroy WebSocket but no connection exists!');
+ return false;
+ }
+ this.heartbeat(-1);
+ this.expectingClose = true;
+ ws.close(1000);
+ this.packetManager.handleQueue();
+ this.ws = null;
+ this.status = Constants.Status.DISCONNECTED;
+ this.ratelimit.remaining = this.ratelimit.total;
+ return true;
+ }
+
+ /**
+ * Called whenever a message is received.
+ * @param {Event} event Event received
+ * @returns {boolean}
+ */
+ onMessage(event) {
+ let data;
+ try {
+ data = this.unpack(event.data);
+ } catch (err) {
+ this.emit('debug', err);
+ }
+ return this.onPacket(data);
+ }
+
+ /**
+ * Sets the current sequence of the connection.
+ * @param {number} s New sequence
+ */
+ setSequence(s) {
+ this.sequence = s > this.sequence ? s : this.sequence;
+ }
+
+ /**
+ * Called whenever a packet is received.
+ * @param {Object} packet Received packet
+ * @returns {boolean}
+ */
+ onPacket(packet) {
+ if (!packet) {
+ this.debug('Received null packet');
+ return false;
+ }
+ this.client.emit('raw', packet);
+ switch (packet.op) {
+ case Constants.OPCodes.HELLO:
+ return this.heartbeat(packet.d.heartbeat_interval);
+ case Constants.OPCodes.RECONNECT:
+ return this.reconnect();
+ case Constants.OPCodes.INVALID_SESSION:
+ if (!packet.d) this.sessionID = null;
+ this.sequence = -1;
+ this.debug('Session invalidated -- will identify with a new session');
+ return this.identify(packet.d ? 2500 : 0);
+ case Constants.OPCodes.HEARTBEAT_ACK:
+ return this.ackHeartbeat();
+ case Constants.OPCodes.HEARTBEAT:
+ return this.heartbeat();
+ default:
+ return this.packetManager.handle(packet);
+ }
+ }
+
+ /**
+ * Called whenever a connection is opened to the gateway.
+ * @param {Event} event Received open event
+ */
+ onOpen(event) {
+ if (event && event.target && event.target.url) this.gateway = event.target.url;
+ this.debug(`Connected to gateway ${this.gateway}`);
+ this.identify();
+ }
+
+ /**
+ * Causes a reconnection to the gateway.
+ */
+ reconnect() {
+ this.debug('Attemping to reconnect in 5500ms...');
+ /**
+ * Emitted whenever the client tries to reconnect to the WebSocket.
+ * @event Client#reconnecting
+ */
+ this.client.emit(Constants.Events.RECONNECTING);
+ this.connect(this.gateway, 5500, true);
+ }
+
+ /**
+ * Called whenever an error occurs with the WebSocket.
+ * @param {Error} error The error that occurred
+ */
+ onError(error) {
+ if (error && error.message === 'uWs client connection error') {
+ this.reconnect();
+ return;
+ }
+ /**
+ * Emitted whenever the client's WebSocket encounters a connection error.
+ * @event Client#error
+ * @param {Error} error The encountered error
+ */
+ this.client.emit(Constants.Events.ERROR, error);
+ }
+
+ /**
+ * @external CloseEvent
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
+ */
+
+ /**
+ * Called whenever a connection to the gateway is closed.
+ * @param {CloseEvent} event Close event that was received
+ */
+ onClose(event) {
+ this.debug(`${this.expectingClose ? 'Client' : 'Server'} closed the WebSocket connection: ${event.code}`);
+ this.closeSequence = this.sequence;
+ // Reset the state before trying to fix anything
+ this.emit('close', event);
+ this.heartbeat(-1);
+ // Should we reconnect?
+ if (event.code === 1000 ? this.expectingClose : Constants.WSCodes[event.code]) {
+ this.expectingClose = false;
+ /**
+ * Emitted when the client's WebSocket disconnects and will no longer attempt to reconnect.
+ * @event Client#disconnect
+ * @param {CloseEvent} event The WebSocket close event
+ */
+ this.client.emit(Constants.Events.DISCONNECT, event);
+ this.debug(Constants.WSCodes[event.code]);
+ this.destroy();
+ return;
+ }
+ this.expectingClose = false;
+ this.reconnect();
+ }
+
+ // Heartbeat
+ /**
+ * Acknowledges a heartbeat.
+ */
+ ackHeartbeat() {
+ this.debug(`Heartbeat acknowledged, latency of ${Date.now() - this.lastPingTimestamp}ms`);
+ this.client._pong(this.lastPingTimestamp);
+ }
+
+ /**
+ * Sends a heartbeat or sets an interval for sending heartbeats.
+ * @param {number} [time] If -1, clears the interval, any other number sets an interval
+ * If no value is given, a heartbeat will be sent instantly
+ */
+ heartbeat(time) {
+ if (!isNaN(time)) {
+ if (time === -1) {
+ this.debug('Clearing heartbeat interval');
+ this.client.clearInterval(this.heartbeatInterval);
+ this.heartbeatInterval = null;
+ } else {
+ this.debug(`Setting a heartbeat interval for ${time}ms`);
+ this.heartbeatInterval = this.client.setInterval(() => this.heartbeat(), time);
+ }
+ return;
+ }
+ this.debug('Sending a heartbeat');
+ this.lastPingTimestamp = Date.now();
+ this.send({
+ op: Constants.OPCodes.HEARTBEAT,
+ d: this.sequence,
+ });
+ }
+
+ // Identification
+ /**
+ * Identifies the client on a connection.
+ * @param {number} [after] How long to wait before identifying
+ * @returns {void}
+ */
+ identify(after) {
+ if (after) return this.client.setTimeout(this.identify.bind(this), after);
+ return this.sessionID ? this.identifyResume() : this.identifyNew();
+ }
+
+ /**
+ * Identifies as a new connection on the gateway.
+ * @returns {void}
+ */
+ identifyNew() {
+ if (!this.client.token) {
+ this.debug('No token available to identify a new session with');
+ return;
+ }
+ // Clone the generic payload and assign the token
+ const d = Object.assign({ token: this.client.token }, this.client.options.ws);
+
+ // Sharding stuff
+ const { shardId, shardCount } = this.client.options;
+ if (shardCount > 0) d.shard = [Number(shardId), Number(shardCount)];
+
+ // Send the payload
+ this.debug('Identifying as a new session');
+ this.send({ op: Constants.OPCodes.IDENTIFY, d });
+ }
+
+ /**
+ * Resumes a session on the gateway.
+ * @returns {void}
+ */
+ identifyResume() {
+ if (!this.sessionID) {
+ this.debug('Warning: wanted to resume but session ID not available; identifying as a new session instead');
+ return this.identifyNew();
+ }
+ this.debug(`Attempting to resume session ${this.sessionID}`);
+
+ const d = {
+ token: this.client.token,
+ session_id: this.sessionID,
+ seq: this.sequence,
+ };
+
+ return this.send({
+ op: Constants.OPCodes.RESUME,
+ d,
+ });
+ }
+}
+
+/**
+ * Encoding the WebSocket connections will use.
+ * @type {string}
+ */
+WebSocketConnection.ENCODING = erlpack ? 'etf' : 'json';
+WebSocketConnection.WebSocket = WebSocket;
+
+module.exports = WebSocketConnection;
diff --git a/node_modules/discord.js/src/client/websocket/WebSocketManager.js b/node_modules/discord.js/src/client/websocket/WebSocketManager.js
new file mode 100644
index 0000000..9ef073f
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/WebSocketManager.js
@@ -0,0 +1,90 @@
+const EventEmitter = require('events').EventEmitter;
+const Constants = require('../../util/Constants');
+const WebSocketConnection = require('./WebSocketConnection');
+
+/**
+ * WebSocket Manager of the client.
+ * @private
+ */
+class WebSocketManager extends EventEmitter {
+ constructor(client) {
+ super();
+ /**
+ * The client that instantiated this WebSocketManager
+ * @type {Client}
+ */
+ this.client = client;
+
+ /**
+ * The WebSocket connection of this manager
+ * @type {?WebSocketConnection}
+ */
+ this.connection = null;
+ }
+
+ /**
+ * Sends a heartbeat on the available connection.
+ * @returns {void}
+ */
+ heartbeat() {
+ if (!this.connection) return this.debug('No connection to heartbeat');
+ return this.connection.heartbeat();
+ }
+
+ /**
+ * Emits a debug event.
+ * @param {string} message Debug message
+ * @returns {void}
+ */
+ debug(message) {
+ return this.client.emit('debug', `[ws] ${message}`);
+ }
+
+ /**
+ * Destroy the client.
+ * @returns {void} Whether or not destruction was successful
+ */
+ destroy() {
+ if (!this.connection) {
+ this.debug('Attempted to destroy WebSocket but no connection exists!');
+ return false;
+ }
+ return this.connection.destroy();
+ }
+
+ /**
+ * Send a packet on the available WebSocket.
+ * @param {Object} packet Packet to send
+ * @returns {void}
+ */
+ send(packet) {
+ if (!this.connection) {
+ this.debug('No connection to websocket');
+ return;
+ }
+ this.connection.send(packet);
+ }
+
+ /**
+ * Connects the client to a gateway.
+ * @param {string} gateway The gateway to connect to
+ * @returns {boolean}
+ */
+ connect(gateway) {
+ if (!this.connection) {
+ this.connection = new WebSocketConnection(this, gateway);
+ return true;
+ }
+ switch (this.connection.status) {
+ case Constants.Status.IDLE:
+ case Constants.Status.DISCONNECTED:
+ this.connection.connect(gateway, 5500);
+ return true;
+ default:
+ this.debug(`Couldn't connect to ${gateway} as the websocket is at state ${this.connection.status}`);
+ return false;
+ }
+ }
+}
+
+module.exports = WebSocketManager;
diff --git a/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js b/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js
new file mode 100644
index 0000000..c84b16b
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js
@@ -0,0 +1,113 @@
+const Constants = require('../../../util/Constants');
+
+const BeforeReadyWhitelist = [
+ Constants.WSEvents.READY,
+ Constants.WSEvents.RESUMED,
+ Constants.WSEvents.GUILD_CREATE,
+ Constants.WSEvents.GUILD_DELETE,
+ Constants.WSEvents.GUILD_MEMBERS_CHUNK,
+ Constants.WSEvents.GUILD_MEMBER_ADD,
+ Constants.WSEvents.GUILD_MEMBER_REMOVE,
+];
+
+class WebSocketPacketManager {
+ constructor(connection) {
+ this.ws = connection;
+ this.handlers = {};
+ this.queue = [];
+
+ this.register(Constants.WSEvents.READY, require('./handlers/Ready'));
+ this.register(Constants.WSEvents.RESUMED, require('./handlers/Resumed'));
+ this.register(Constants.WSEvents.GUILD_CREATE, require('./handlers/GuildCreate'));
+ this.register(Constants.WSEvents.GUILD_DELETE, require('./handlers/GuildDelete'));
+ this.register(Constants.WSEvents.GUILD_UPDATE, require('./handlers/GuildUpdate'));
+ this.register(Constants.WSEvents.GUILD_BAN_ADD, require('./handlers/GuildBanAdd'));
+ this.register(Constants.WSEvents.GUILD_BAN_REMOVE, require('./handlers/GuildBanRemove'));
+ this.register(Constants.WSEvents.GUILD_MEMBER_ADD, require('./handlers/GuildMemberAdd'));
+ this.register(Constants.WSEvents.GUILD_MEMBER_REMOVE, require('./handlers/GuildMemberRemove'));
+ this.register(Constants.WSEvents.GUILD_MEMBER_UPDATE, require('./handlers/GuildMemberUpdate'));
+ this.register(Constants.WSEvents.GUILD_ROLE_CREATE, require('./handlers/GuildRoleCreate'));
+ this.register(Constants.WSEvents.GUILD_ROLE_DELETE, require('./handlers/GuildRoleDelete'));
+ this.register(Constants.WSEvents.GUILD_ROLE_UPDATE, require('./handlers/GuildRoleUpdate'));
+ this.register(Constants.WSEvents.GUILD_EMOJIS_UPDATE, require('./handlers/GuildEmojisUpdate'));
+ this.register(Constants.WSEvents.GUILD_MEMBERS_CHUNK, require('./handlers/GuildMembersChunk'));
+ this.register(Constants.WSEvents.GUILD_INTEGRATIONS_UPDATE, require('./handlers/GuildIntegrationsUpdate'));
+ this.register(Constants.WSEvents.INVITE_CREATE, require('./handlers/InviteCreate'));
+ this.register(Constants.WSEvents.INVITE_DELETE, require('./handlers/InviteDelete'));
+ this.register(Constants.WSEvents.CHANNEL_CREATE, require('./handlers/ChannelCreate'));
+ this.register(Constants.WSEvents.CHANNEL_DELETE, require('./handlers/ChannelDelete'));
+ this.register(Constants.WSEvents.CHANNEL_UPDATE, require('./handlers/ChannelUpdate'));
+ this.register(Constants.WSEvents.CHANNEL_PINS_UPDATE, require('./handlers/ChannelPinsUpdate'));
+ this.register(Constants.WSEvents.PRESENCE_UPDATE, require('./handlers/PresenceUpdate'));
+ this.register(Constants.WSEvents.USER_UPDATE, require('./handlers/UserUpdate'));
+ this.register(Constants.WSEvents.USER_NOTE_UPDATE, require('./handlers/UserNoteUpdate'));
+ this.register(Constants.WSEvents.USER_SETTINGS_UPDATE, require('./handlers/UserSettingsUpdate'));
+ this.register(Constants.WSEvents.USER_GUILD_SETTINGS_UPDATE, require('./handlers/UserGuildSettingsUpdate'));
+ this.register(Constants.WSEvents.VOICE_STATE_UPDATE, require('./handlers/VoiceStateUpdate'));
+ this.register(Constants.WSEvents.TYPING_START, require('./handlers/TypingStart'));
+ this.register(Constants.WSEvents.MESSAGE_CREATE, require('./handlers/MessageCreate'));
+ this.register(Constants.WSEvents.MESSAGE_DELETE, require('./handlers/MessageDelete'));
+ this.register(Constants.WSEvents.MESSAGE_UPDATE, require('./handlers/MessageUpdate'));
+ this.register(Constants.WSEvents.MESSAGE_DELETE_BULK, require('./handlers/MessageDeleteBulk'));
+ this.register(Constants.WSEvents.VOICE_SERVER_UPDATE, require('./handlers/VoiceServerUpdate'));
+ this.register(Constants.WSEvents.GUILD_SYNC, require('./handlers/GuildSync'));
+ this.register(Constants.WSEvents.RELATIONSHIP_ADD, require('./handlers/RelationshipAdd'));
+ this.register(Constants.WSEvents.RELATIONSHIP_REMOVE, require('./handlers/RelationshipRemove'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_ADD, require('./handlers/MessageReactionAdd'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE, require('./handlers/MessageReactionRemove'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE_EMOJI, require('./handlers/MessageReactionRemoveEmoji'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE_ALL, require('./handlers/MessageReactionRemoveAll'));
+ this.register(Constants.WSEvents.WEBHOOKS_UPDATE, require('./handlers/WebhooksUpdate'));
+ }
+
+ get client() {
+ return this.ws.client;
+ }
+
+ register(event, Handler) {
+ this.handlers[event] = new Handler(this);
+ }
+
+ handleQueue() {
+ this.queue.forEach((element, index) => {
+ this.handle(this.queue[index], true);
+ this.queue.splice(index, 1);
+ });
+ }
+
+ handle(packet, queue = false) {
+ if (packet.op === Constants.OPCodes.HEARTBEAT_ACK) {
+ this.ws.client._pong(this.ws.client._pingTimestamp);
+ this.ws.lastHeartbeatAck = true;
+ this.ws.client.emit('debug', 'Heartbeat acknowledged');
+ } else if (packet.op === Constants.OPCodes.HEARTBEAT) {
+ this.client.ws.send({
+ op: Constants.OPCodes.HEARTBEAT,
+ d: this.client.ws.sequence,
+ });
+ this.ws.client.emit('debug', 'Received gateway heartbeat');
+ }
+
+ if (this.ws.status === Constants.Status.RECONNECTING) {
+ this.ws.reconnecting = false;
+ this.ws.checkIfReady();
+ }
+
+ this.ws.setSequence(packet.s);
+
+ if (this.ws.disabledEvents[packet.t] !== undefined) return false;
+
+ if (this.ws.status !== Constants.Status.READY) {
+ if (BeforeReadyWhitelist.indexOf(packet.t) === -1) {
+ this.queue.push(packet);
+ return false;
+ }
+ }
+
+ if (!queue && this.queue.length > 0) this.handleQueue();
+ if (this.handlers[packet.t]) return this.handlers[packet.t].handle(packet);
+ return false;
+ }
+}
+
+module.exports = WebSocketPacketManager;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js b/node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js
new file mode 100644
index 0000000..c1c2a5a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js
@@ -0,0 +1,11 @@
+class AbstractHandler {
+ constructor(packetManager) {
+ this.packetManager = packetManager;
+ }
+
+ handle(packet) {
+ return packet;
+ }
+}
+
+module.exports = AbstractHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js
new file mode 100644
index 0000000..04cb298
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js
@@ -0,0 +1,17 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class ChannelCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.ChannelCreate.handle(data);
+ }
+}
+
+/**
+ * Emitted whenever a channel is created.
+ * @event Client#channelCreate
+ * @param {Channel} channel The channel that was created
+ */
+
+module.exports = ChannelCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js
new file mode 100644
index 0000000..b25f585
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js
@@ -0,0 +1,20 @@
+const AbstractHandler = require('./AbstractHandler');
+
+const Constants = require('../../../../util/Constants');
+
+class ChannelDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.ChannelDelete.handle(data);
+ if (response.channel) client.emit(Constants.Events.CHANNEL_DELETE, response.channel);
+ }
+}
+
+/**
+ * Emitted whenever a channel is deleted.
+ * @event Client#channelDelete
+ * @param {Channel} channel The channel that was deleted
+ */
+
+module.exports = ChannelDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js
new file mode 100644
index 0000000..16ffe1c
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js
@@ -0,0 +1,37 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+/*
+{ t: 'CHANNEL_PINS_UPDATE',
+ s: 666,
+ op: 0,
+ d:
+ { last_pin_timestamp: '2016-08-28T17:37:13.171774+00:00',
+ channel_id: '314866471639044027' } }
+*/
+
+class ChannelPinsUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const channel = client.channels.get(data.channel_id);
+ const time = new Date(data.last_pin_timestamp);
+ if (channel && time) {
+ // Discord sends null for last_pin_timestamp if the last pinned message was removed
+ channel.lastPinTimestamp = time.getTime() || null;
+
+ client.emit(Constants.Events.CHANNEL_PINS_UPDATE, channel, time);
+ }
+ }
+}
+
+/**
+ * Emitted whenever the pins of a channel are updated. Due to the nature of the WebSocket event, not much information
+ * can be provided easily here - you need to manually check the pins yourself.
+ * <warn>The `time` parameter will be a Unix Epoch Date object when there are no pins left.</warn>
+ * @event Client#channelPinsUpdate
+ * @param {Channel} channel The channel that the pins update occured in
+ * @param {Date} time The time when the last pinned message was pinned
+ */
+
+module.exports = ChannelPinsUpdate;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js
new file mode 100644
index 0000000..fa535b1
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class ChannelUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.ChannelUpdate.handle(data);
+ }
+}
+
+module.exports = ChannelUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js
new file mode 100644
index 0000000..60ce72d
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js
@@ -0,0 +1,23 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class GuildBanAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ const user = client.users.get(data.user.id);
+ if (guild && user) client.emit(Constants.Events.GUILD_BAN_ADD, guild, user);
+ }
+}
+
+/**
+ * Emitted whenever a member is banned from a guild.
+ * @event Client#guildBanAdd
+ * @param {Guild} guild The guild that the ban occurred in
+ * @param {User} user The user that was banned
+ */
+
+module.exports = GuildBanAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js
new file mode 100644
index 0000000..c4edbde
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js
@@ -0,0 +1,20 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildBanRemoveHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildBanRemove.handle(data);
+ }
+}
+
+/**
+ * Emitted whenever a member is unbanned from a guild.
+ * @event Client#guildBanRemove
+ * @param {Guild} guild The guild that the unban occurred in
+ * @param {User} user The user that was unbanned
+ */
+
+module.exports = GuildBanRemoveHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js
new file mode 100644
index 0000000..d7c1803
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js
@@ -0,0 +1,22 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ const guild = client.guilds.get(data.id);
+ if (guild) {
+ if (!guild.available && !data.unavailable) {
+ // A newly available guild
+ guild.setup(data);
+ this.packetManager.ws.checkIfReady();
+ }
+ } else {
+ // A new guild
+ client.dataManager.newGuild(data);
+ }
+ }
+}
+
+module.exports = GuildCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js
new file mode 100644
index 0000000..35e3c53
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class GuildDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.GuildDelete.handle(data);
+ if (response.guild) client.emit(Constants.Events.GUILD_DELETE, response.guild);
+ }
+}
+
+/**
+ * Emitted whenever a guild is deleted/left.
+ * @event Client#guildDelete
+ * @param {Guild} guild The guild that was deleted
+ */
+
+module.exports = GuildDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js
new file mode 100644
index 0000000..2906e74
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildEmojisUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildEmojisUpdate.handle(data);
+ }
+}
+
+module.exports = GuildEmojisUpdate;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildIntegrationsUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildIntegrationsUpdate.js
new file mode 100644
index 0000000..5adfb5b
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildIntegrationsUpdate.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const { Events } = require('../../../../util/Constants');
+
+class GuildIntegrationsHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) client.emit(Events.GUILD_INTEGRATIONS_UPDATE, guild);
+ }
+}
+
+module.exports = GuildIntegrationsHandler;
+
+/**
+ * Emitted whenever a guild integration is updated
+ * @event Client#guildIntegrationsUpdate
+ * @param {Guild} guild The guild whose integrations were updated
+ */
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js
new file mode 100644
index 0000000..d4d122f
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js
@@ -0,0 +1,17 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildMemberAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ guild.memberCount++;
+ guild._addMember(data);
+ }
+ }
+}
+
+module.exports = GuildMemberAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js
new file mode 100644
index 0000000..6ec1bfe
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js
@@ -0,0 +1,13 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildMemberRemoveHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildMemberRemove.handle(data);
+ }
+}
+
+module.exports = GuildMemberRemoveHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js
new file mode 100644
index 0000000..94ac71f
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js
@@ -0,0 +1,18 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildMemberUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ const member = guild.members.get(data.user.id);
+ if (member) guild._updateMember(member, data);
+ }
+ }
+}
+
+module.exports = GuildMemberUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js
new file mode 100644
index 0000000..4458644
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js
@@ -0,0 +1,33 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+// Uncomment in v12
+// const Collection = require('../../../../util/Collection');
+
+class GuildMembersChunkHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ if (!guild) return;
+
+ // Uncomment in v12
+ // const members = new Collection();
+ //
+ // for (const member of data.members) members.set(member.id, guild._addMember(member, false));
+
+ const members = data.members.map(member => guild._addMember(member, false));
+
+ client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, members, guild);
+
+ client.ws.lastHeartbeatAck = true;
+ }
+}
+
+/**
+ * Emitted whenever a chunk of guild members is received (all members come from the same guild).
+ * @event Client#guildMembersChunk
+ * @param {GuildMember[]} members The members in the chunk
+ * @param {Guild} guild The guild related to the member chunk
+ */
+
+module.exports = GuildMembersChunkHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js
new file mode 100644
index 0000000..8581d53
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildRoleCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildRoleCreate.handle(data);
+ }
+}
+
+module.exports = GuildRoleCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js
new file mode 100644
index 0000000..63439b0
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildRoleDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildRoleDelete.handle(data);
+ }
+}
+
+module.exports = GuildRoleDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js
new file mode 100644
index 0000000..6fbdc10
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildRoleUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildRoleUpdate.handle(data);
+ }
+}
+
+module.exports = GuildRoleUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js
new file mode 100644
index 0000000..0b9f5aa
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildSyncHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildSync.handle(data);
+ }
+}
+
+module.exports = GuildSyncHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js
new file mode 100644
index 0000000..70eff52
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildUpdate.handle(data);
+ }
+}
+
+module.exports = GuildUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/InviteCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/InviteCreate.js
new file mode 100644
index 0000000..00efb67
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/InviteCreate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class InviteCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.InviteCreate.handle(data);
+ }
+}
+
+module.exports = InviteCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/InviteDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/InviteDelete.js
new file mode 100644
index 0000000..0b8da42
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/InviteDelete.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class InviteDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.InviteDelete.handle(data);
+ }
+}
+
+module.exports = InviteDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js
new file mode 100644
index 0000000..beed34d
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class MessageCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.MessageCreate.handle(data);
+ if (response.message) client.emit(Constants.Events.MESSAGE_CREATE, response.message);
+ }
+}
+
+/**
+ * Emitted whenever a message is created.
+ * @event Client#message
+ * @param {Message} message The created message
+ */
+
+module.exports = MessageCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js
new file mode 100644
index 0000000..043c70a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class MessageDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.MessageDelete.handle(data);
+ if (response.message) client.emit(Constants.Events.MESSAGE_DELETE, response.message);
+ }
+}
+
+/**
+ * Emitted whenever a message is deleted.
+ * @event Client#messageDelete
+ * @param {Message} message The deleted message
+ */
+
+module.exports = MessageDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js
new file mode 100644
index 0000000..db02df0
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js
@@ -0,0 +1,17 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageDeleteBulkHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageDeleteBulk.handle(data);
+ }
+}
+
+/**
+ * Emitted whenever messages are deleted in bulk.
+ * @event Client#messageDeleteBulk
+ * @param {Collection<Snowflake, Message>} messages The deleted messages, mapped by their ID
+ */
+
+module.exports = MessageDeleteBulkHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js
new file mode 100644
index 0000000..a58db70
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionAdd.handle(data);
+ }
+}
+
+module.exports = MessageReactionAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js
new file mode 100644
index 0000000..cddde70
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionRemove extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionRemove.handle(data);
+ }
+}
+
+module.exports = MessageReactionRemove;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js
new file mode 100644
index 0000000..303da9c
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionRemoveAll extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionRemoveAll.handle(data);
+ }
+}
+
+module.exports = MessageReactionRemoveAll;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveEmoji.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveEmoji.js
new file mode 100644
index 0000000..c16af74
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveEmoji.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionRemoveEmoji extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionRemoveEmoji.handle(data);
+ }
+}
+
+module.exports = MessageReactionRemoveEmoji;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js
new file mode 100644
index 0000000..527632d
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageUpdate.handle(data);
+ }
+}
+
+module.exports = MessageUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js
new file mode 100644
index 0000000..8bcf659
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js
@@ -0,0 +1,76 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+const Util = require('../../../../util/Util');
+
+class PresenceUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ let user = client.users.get(data.user.id);
+ const guild = client.guilds.get(data.guild_id);
+
+ // Step 1
+ if (!user) {
+ if (data.user.username) {
+ user = client.dataManager.newUser(data.user);
+ } else {
+ return;
+ }
+ }
+
+ const oldUser = Util.cloneObject(user);
+ user.patch(data.user);
+ if (!user.equals(oldUser)) {
+ client.emit(Constants.Events.USER_UPDATE, oldUser, user);
+ }
+
+ if (guild) {
+ let member = guild.members.get(user.id);
+ if (!member && data.status !== 'offline') {
+ member = guild._addMember({
+ user,
+ roles: data.roles,
+ deaf: false,
+ mute: false,
+ }, false);
+ client.emit(Constants.Events.GUILD_MEMBER_AVAILABLE, member);
+ }
+ if (member) {
+ if (client.listenerCount(Constants.Events.PRESENCE_UPDATE) === 0) {
+ guild._setPresence(user.id, data);
+ return;
+ }
+ const oldMember = Util.cloneObject(member);
+ if (member.presence) {
+ oldMember.frozenPresence = Util.cloneObject(member.presence);
+ }
+ guild._setPresence(user.id, data);
+ client.emit(Constants.Events.PRESENCE_UPDATE, oldMember, member);
+ } else {
+ guild._setPresence(user.id, data);
+ }
+ }
+ }
+}
+
+/**
+ * Emitted whenever a guild member's presence changes, or they change one of their details.
+ * @event Client#presenceUpdate
+ * @param {GuildMember} oldMember The member before the presence update
+ * @param {GuildMember} newMember The member after the presence update
+ */
+
+/**
+ * Emitted whenever a user's details (e.g. username) are changed.
+ * @event Client#userUpdate
+ * @param {User} oldUser The user before the update
+ * @param {User} newUser The user after the update
+ */
+
+/**
+ * Emitted whenever a member becomes available in a large guild.
+ * @event Client#guildMemberAvailable
+ * @param {GuildMember} member The member that became available
+ */
+
+module.exports = PresenceUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js b/node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js
new file mode 100644
index 0000000..3eb80a2
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js
@@ -0,0 +1,84 @@
+const AbstractHandler = require('./AbstractHandler');
+
+const ClientUser = require('../../../../structures/ClientUser');
+
+class ReadyHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ client.ws.heartbeat();
+
+ data.user.user_settings = data.user_settings;
+ data.user.user_guild_settings = data.user_guild_settings;
+
+ const clientUser = new ClientUser(client, data.user);
+ client.user = clientUser;
+ client.readyAt = new Date();
+ client.users.set(clientUser.id, clientUser);
+
+ for (const guild of data.guilds) if (!client.guilds.has(guild.id)) client.dataManager.newGuild(guild);
+ for (const privateDM of data.private_channels) client.dataManager.newChannel(privateDM);
+
+ for (const relation of data.relationships) {
+ const user = client.dataManager.newUser(relation.user);
+ if (relation.type === 1) {
+ client.user.friends.set(user.id, user);
+ } else if (relation.type === 2) {
+ client.user.blocked.set(user.id, user);
+ }
+ }
+
+ data.presences = data.presences || [];
+ for (const presence of data.presences) {
+ client.dataManager.newUser(presence.user);
+ client._setPresence(presence.user.id, presence);
+ }
+
+ if (data.notes) {
+ for (const user of Object.keys(data.notes)) {
+ let note = data.notes[user];
+ if (!note.length) note = null;
+
+ client.user.notes.set(user, note);
+ }
+ }
+
+ if (!client.user.bot && client.options.sync) client.setInterval(client.syncGuilds.bind(client), 30000);
+
+ if (!client.users.has('1')) {
+ client.dataManager.newUser({
+ id: '1',
+ username: 'Clyde',
+ discriminator: '0000',
+ avatar: 'https://discordapp.com/assets/f78426a064bc9dd24847519259bc42af.png',
+ bot: true,
+ status: 'online',
+ game: null,
+ verified: true,
+ });
+ }
+
+ const t = client.setTimeout(() => {
+ client.ws.connection.triggerReady();
+ }, 1200 * data.guilds.length);
+
+ const guildCount = data.guilds.length;
+
+ if (client.getMaxListeners() !== 0) client.setMaxListeners(client.getMaxListeners() + guildCount);
+
+ client.once('ready', () => {
+ client.syncGuilds();
+ if (client.getMaxListeners() !== 0) client.setMaxListeners(client.getMaxListeners() - guildCount);
+ client.clearTimeout(t);
+ });
+
+ const ws = this.packetManager.ws;
+
+ ws.sessionID = data.session_id;
+ client.emit('debug', `READY ${ws.sessionID}`);
+ ws.checkIfReady();
+ }
+}
+
+module.exports = ReadyHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js
new file mode 100644
index 0000000..122b4c5
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class RelationshipAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ if (data.type === 1) {
+ client.fetchUser(data.id).then(user => {
+ client.user.friends.set(user.id, user);
+ });
+ } else if (data.type === 2) {
+ client.fetchUser(data.id).then(user => {
+ client.user.blocked.set(user.id, user);
+ });
+ }
+ }
+}
+
+module.exports = RelationshipAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js
new file mode 100644
index 0000000..b57326a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class RelationshipRemoveHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ if (data.type === 2) {
+ if (client.user.blocked.has(data.id)) {
+ client.user.blocked.delete(data.id);
+ }
+ } else if (data.type === 1) {
+ if (client.user.friends.has(data.id)) {
+ client.user.friends.delete(data.id);
+ }
+ }
+ }
+}
+
+module.exports = RelationshipRemoveHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/Resumed.js b/node_modules/discord.js/src/client/websocket/packets/handlers/Resumed.js
new file mode 100644
index 0000000..a0a51c0
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/Resumed.js
@@ -0,0 +1,26 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class ResumedHandler extends AbstractHandler {
+ handle() {
+ const client = this.packetManager.client;
+ const ws = client.ws.connection;
+
+ ws.status = Constants.Status.READY;
+ this.packetManager.handleQueue();
+
+ const replayed = ws.sequence - ws.closeSequence;
+
+ ws.debug(`RESUMED | replayed ${replayed} events.`);
+ client.emit(Constants.Events.RESUME, replayed);
+ ws.heartbeat();
+ }
+}
+
+/**
+ * Emitted whenever a WebSocket resumes.
+ * @event Client#resume
+ * @param {number} replayed The number of events that were replayed
+ */
+
+module.exports = ResumedHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js b/node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js
new file mode 100644
index 0000000..a7f5a36
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js
@@ -0,0 +1,68 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class TypingStartHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const channel = client.channels.get(data.channel_id);
+ const user = client.users.get(data.user_id);
+ const timestamp = new Date(data.timestamp * 1000);
+
+ if (channel && user) {
+ if (channel.type === 'voice') {
+ client.emit(Constants.Events.WARN, `Discord sent a typing packet to voice channel ${channel.id}`);
+ return;
+ }
+ if (channel._typing.has(user.id)) {
+ const typing = channel._typing.get(user.id);
+ typing.lastTimestamp = timestamp;
+ typing.resetTimeout(tooLate(channel, user));
+ } else {
+ channel._typing.set(user.id, new TypingData(client, timestamp, timestamp, tooLate(channel, user)));
+ client.emit(Constants.Events.TYPING_START, channel, user);
+ }
+ }
+ }
+}
+
+class TypingData {
+ constructor(client, since, lastTimestamp, _timeout) {
+ this.client = client;
+ this.since = since;
+ this.lastTimestamp = lastTimestamp;
+ this._timeout = _timeout;
+ }
+
+ resetTimeout(_timeout) {
+ this.client.clearTimeout(this._timeout);
+ this._timeout = _timeout;
+ }
+
+ get elapsedTime() {
+ return Date.now() - this.since;
+ }
+}
+
+function tooLate(channel, user) {
+ return channel.client.setTimeout(() => {
+ channel.client.emit(Constants.Events.TYPING_STOP, channel, user, channel._typing.get(user.id));
+ channel._typing.delete(user.id);
+ }, 6000);
+}
+
+/**
+ * Emitted whenever a user starts typing in a channel.
+ * @event Client#typingStart
+ * @param {Channel} channel The channel the user started typing in
+ * @param {User} user The user that started typing
+ */
+
+/**
+ * Emitted whenever a user stops typing in a channel.
+ * @event Client#typingStop
+ * @param {Channel} channel The channel the user stopped typing in
+ * @param {User} user The user that stopped typing
+ */
+
+module.exports = TypingStartHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js
new file mode 100644
index 0000000..90bca4c
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js
@@ -0,0 +1,21 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+const ClientUserGuildSettings = require('../../../../structures/ClientUserGuildSettings');
+
+class UserGuildSettingsUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const settings = client.user.guildSettings.get(packet.d.guild_id);
+ if (settings) settings.patch(packet.d);
+ else client.user.guildSettings.set(packet.d.guild_id, new ClientUserGuildSettings(packet.d, client));
+ client.emit(Constants.Events.USER_GUILD_SETTINGS_UPDATE, client.user.guildSettings.get(packet.d.guild_id));
+ }
+}
+
+/**
+ * Emitted whenever the client user's settings update.
+ * @event Client#clientUserGuildSettingsUpdate
+ * @param {ClientUserGuildSettings} clientUserGuildSettings The new client user guild settings
+ */
+
+module.exports = UserGuildSettingsUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js
new file mode 100644
index 0000000..1e4777a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js
@@ -0,0 +1,12 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class UserNoteUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ client.actions.UserNoteUpdate.handle(data);
+ }
+}
+
+module.exports = UserNoteUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/UserSettingsUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/UserSettingsUpdate.js
new file mode 100644
index 0000000..903b64b
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/UserSettingsUpdate.js
@@ -0,0 +1,18 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class UserSettingsUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ client.user.settings.patch(packet.d);
+ client.emit(Constants.Events.USER_SETTINGS_UPDATE, client.user.settings);
+ }
+}
+
+/**
+ * Emitted when the client user's settings update.
+ * @event Client#clientUserSettingsUpdate
+ * @param {ClientUserSettings} clientUserSettings The new client user settings
+ */
+
+module.exports = UserSettingsUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js
new file mode 100644
index 0000000..bc34f34
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class UserUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.UserUpdate.handle(data);
+ }
+}
+
+module.exports = UserUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js
new file mode 100644
index 0000000..97885d6
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+
+/*
+{
+ "token": "my_token",
+ "guild_id": "41771983423143937",
+ "endpoint": "smart.loyal.discord.gg"
+}
+*/
+
+class VoiceServerUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.emit('self.voiceServer', data);
+ }
+}
+
+module.exports = VoiceServerUpdate;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js
new file mode 100644
index 0000000..2b0b40f
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js
@@ -0,0 +1,53 @@
+const AbstractHandler = require('./AbstractHandler');
+
+const Constants = require('../../../../util/Constants');
+const Util = require('../../../../util/Util');
+
+class VoiceStateUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ const member = guild.members.get(data.user_id);
+ if (member) {
+ const oldVoiceChannelMember = Util.cloneObject(member);
+ if (member.voiceChannel && member.voiceChannel.id !== data.channel_id) {
+ member.voiceChannel.members.delete(oldVoiceChannelMember.id);
+ }
+
+ // If the member left the voice channel, unset their speaking property
+ if (!data.channel_id) member.speaking = null;
+
+ if (member.user.id === client.user.id) {
+ client.emit('self.voiceStateUpdate', data);
+ }
+
+ const newChannel = client.channels.get(data.channel_id);
+ if (newChannel) {
+ newChannel.members.set(member.id, member);
+ member.guild.channels.set(data.channel_id, newChannel);
+ }
+
+ member.serverMute = data.mute;
+ member.serverDeaf = data.deaf;
+ member.selfMute = data.self_mute;
+ member.selfDeaf = data.self_deaf;
+ member.selfStream = data.self_stream || false;
+ member.voiceSessionID = data.session_id;
+ member.voiceChannelID = data.channel_id;
+ client.emit(Constants.Events.VOICE_STATE_UPDATE, oldVoiceChannelMember, member);
+ }
+ }
+ }
+}
+
+/**
+ * Emitted whenever a user changes voice state - e.g. joins/leaves a channel, mutes/unmutes.
+ * @event Client#voiceStateUpdate
+ * @param {GuildMember} oldMember The member before the voice state update
+ * @param {GuildMember} newMember The member after the voice state update
+ */
+
+module.exports = VoiceStateUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/WebhooksUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/WebhooksUpdate.js
new file mode 100644
index 0000000..7ed2721
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/WebhooksUpdate.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const { Events } = require('../../../../util/Constants');
+
+class WebhooksUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const channel = client.channels.get(data.channel_id);
+ if (channel) client.emit(Events.WEBHOOKS_UPDATE, channel);
+ }
+}
+
+/**
+ * Emitted whenever a guild text channel has its webhooks changed.
+ * @event Client#webhookUpdate
+ * @param {TextChannel} channel The channel that had a webhook update
+ */
+
+module.exports = WebhooksUpdate;
diff --git a/node_modules/discord.js/src/index.js b/node_modules/discord.js/src/index.js
new file mode 100644
index 0000000..f1fe01f
--- /dev/null
+++ b/node_modules/discord.js/src/index.js
@@ -0,0 +1,71 @@
+const Util = require('./util/Util');
+
+module.exports = {
+ // "Root" classes (starting points)
+ Client: require('./client/Client'),
+ Shard: require('./sharding/Shard'),
+ ShardClientUtil: require('./sharding/ShardClientUtil'),
+ ShardingManager: require('./sharding/ShardingManager'),
+ WebhookClient: require('./client/WebhookClient'),
+
+ // Utilities
+ BitField: require('./util/BitField'),
+ Collection: require('./util/Collection'),
+ Constants: require('./util/Constants'),
+ DiscordAPIError: require('./client/rest/DiscordAPIError'),
+ EvaluatedPermissions: require('./util/Permissions'),
+ MessageFlags: require('./util/MessageFlags'),
+ Permissions: require('./util/Permissions'),
+ Snowflake: require('./util/Snowflake'),
+ SnowflakeUtil: require('./util/Snowflake'),
+ SystemChannelFlags: require('./util/SystemChannelFlags'),
+ Util: Util,
+ util: Util,
+ version: require('../package').version,
+
+ // Shortcuts to Util methods
+ escapeMarkdown: Util.escapeMarkdown,
+ fetchRecommendedShards: Util.fetchRecommendedShards,
+ resolveString: Util.resolveString,
+ splitMessage: Util.splitMessage,
+
+ // Structures
+ Attachment: require('./structures/Attachment'),
+ CategoryChannel: require('./structures/CategoryChannel'),
+ Channel: require('./structures/Channel'),
+ ClientUser: require('./structures/ClientUser'),
+ ClientUserSettings: require('./structures/ClientUserSettings'),
+ Collector: require('./structures/interfaces/Collector'),
+ DMChannel: require('./structures/DMChannel'),
+ Emoji: require('./structures/Emoji'),
+ Game: require('./structures/Presence').Game,
+ GroupDMChannel: require('./structures/GroupDMChannel'),
+ Guild: require('./structures/Guild'),
+ GuildAuditLogs: require('./structures/GuildAuditLogs'),
+ GuildChannel: require('./structures/GuildChannel'),
+ GuildMember: require('./structures/GuildMember'),
+ Integration: require('./structures/Integration'),
+ Invite: require('./structures/Invite'),
+ Message: require('./structures/Message'),
+ MessageAttachment: require('./structures/MessageAttachment'),
+ MessageCollector: require('./structures/MessageCollector'),
+ MessageEmbed: require('./structures/MessageEmbed'),
+ MessageMentions: require('./structures/MessageMentions'),
+ MessageReaction: require('./structures/MessageReaction'),
+ NewsChannel: require('./structures/NewsChannel'),
+ OAuth2Application: require('./structures/OAuth2Application'),
+ ClientOAuth2Application: require('./structures/OAuth2Application'),
+ PartialGuild: require('./structures/PartialGuild'),
+ PartialGuildChannel: require('./structures/PartialGuildChannel'),
+ PermissionOverwrites: require('./structures/PermissionOverwrites'),
+ Presence: require('./structures/Presence').Presence,
+ ReactionEmoji: require('./structures/ReactionEmoji'),
+ ReactionCollector: require('./structures/ReactionCollector'),
+ RichEmbed: require('./structures/RichEmbed'),
+ Role: require('./structures/Role'),
+ StoreChannel: require('./structures/StoreChannel'),
+ TextChannel: require('./structures/TextChannel'),
+ User: require('./structures/User'),
+ VoiceChannel: require('./structures/VoiceChannel'),
+ Webhook: require('./structures/Webhook'),
+};
diff --git a/node_modules/discord.js/src/sharding/Shard.js b/node_modules/discord.js/src/sharding/Shard.js
new file mode 100644
index 0000000..b6fddb4
--- /dev/null
+++ b/node_modules/discord.js/src/sharding/Shard.js
@@ -0,0 +1,282 @@
+const childProcess = require('child_process');
+const EventEmitter = require('events');
+const path = require('path');
+const Util = require('../util/Util');
+
+/**
+ * Represents a Shard spawned by the ShardingManager.
+ */
+class Shard extends EventEmitter {
+ /**
+ * @param {ShardingManager} manager The sharding manager
+ * @param {number} id The ID of this shard
+ * @param {Array} [args=[]] Command line arguments to pass to the script
+ */
+ constructor(manager, id, args = []) {
+ super();
+ /**
+ * Manager that created the shard
+ * @type {ShardingManager}
+ */
+ this.manager = manager;
+
+ /**
+ * ID of the shard
+ * @type {number}
+ */
+ this.id = id;
+
+ /**
+ * The environment variables for the shard
+ * @type {Object}
+ */
+ this.env = Object.assign({}, process.env, {
+ SHARD_ID: this.id,
+ SHARD_COUNT: this.manager.totalShards,
+ CLIENT_TOKEN: this.manager.token,
+ });
+
+ /**
+ * Whether the shard's {@link Client} is ready
+ * @type {boolean}
+ */
+ this.ready = false;
+
+ this._evals = new Map();
+ this._fetches = new Map();
+
+ /**
+ * Listener function for the {@link ChildProcess}' `exit` event
+ * @type {Function}
+ * @private
+ */
+ this._exitListener = this._handleExit.bind(this, undefined);
+
+ /**
+ * Process of the shard
+ * @type {ChildProcess}
+ */
+ this.process = null;
+
+ this.spawn(args);
+ }
+
+ /**
+ * Forks a child process for the shard.
+ * <warn>You should not need to call this manually.</warn>
+ * @param {Array} [args=this.manager.args] Command line arguments to pass to the script
+ * @param {Array} [execArgv=this.manager.execArgv] Command line arguments to pass to the process executable
+ * @returns {ChildProcess}
+ */
+ spawn(args = this.manager.args, execArgv = this.manager.execArgv) {
+ this.process = childProcess.fork(path.resolve(this.manager.file), args, {
+ env: this.env, execArgv,
+ })
+ .on('exit', this._exitListener)
+ .on('message', this._handleMessage.bind(this));
+
+ /**
+ * Emitted upon the creation of the shard's child process.
+ * @event Shard#spawn
+ * @param {ChildProcess} process Child process that was created
+ */
+ this.emit('spawn', this.process);
+
+ return new Promise((resolve, reject) => {
+ this.once('ready', resolve);
+ this.once('disconnect', () => reject(new Error(`Shard ${this.id}'s Client disconnected before becoming ready.`)));
+ this.once('death', () => reject(new Error(`Shard ${this.id}'s process exited before its Client became ready.`)));
+ setTimeout(() => reject(new Error(`Shard ${this.id}'s Client took too long to become ready.`)), 30000);
+ }).then(() => this.process);
+ }
+
+ /**
+ * Immediately kills the shard's process and does not restart it.
+ */
+ kill() {
+ this.process.removeListener('exit', this._exitListener);
+ this.process.kill();
+ this._handleExit(false);
+ }
+
+ /**
+ * Kills and restarts the shard's process.
+ * @param {number} [delay=500] How long to wait between killing the process and restarting it (in milliseconds)
+ * @returns {Promise<ChildProcess>}
+ */
+ respawn(delay = 500) {
+ this.kill();
+ if (delay > 0) return Util.delayFor(delay).then(() => this.spawn());
+ return this.spawn();
+ }
+
+ /**
+ * Sends a message to the shard's process.
+ * @param {*} message Message to send to the shard
+ * @returns {Promise<Shard>}
+ */
+ send(message) {
+ return new Promise((resolve, reject) => {
+ this.process.send(message, err => {
+ if (err) reject(err); else resolve(this);
+ });
+ });
+ }
+
+ /**
+ * Fetches a client property value of the shard.
+ * @param {string} prop Name of the client property to get, using periods for nesting
+ * @returns {Promise<*>}
+ * @example
+ * shard.fetchClientValue('guilds.size')
+ * .then(count => console.log(`${count} guilds in shard ${shard.id}`))
+ * .catch(console.error);
+ */
+ fetchClientValue(prop) {
+ if (this._fetches.has(prop)) return this._fetches.get(prop);
+
+ const promise = new Promise((resolve, reject) => {
+ const listener = message => {
+ if (!message || message._fetchProp !== prop) return;
+ this.process.removeListener('message', listener);
+ this._fetches.delete(prop);
+ resolve(message._result);
+ };
+ this.process.on('message', listener);
+
+ this.send({ _fetchProp: prop }).catch(err => {
+ this.process.removeListener('message', listener);
+ this._fetches.delete(prop);
+ reject(err);
+ });
+ });
+
+ this._fetches.set(prop, promise);
+ return promise;
+ }
+
+ /**
+ * Evaluates a script on the shard, in the context of the client.
+ * @param {string} script JavaScript to run on the shard
+ * @returns {Promise<*>} Result of the script execution
+ */
+ eval(script) {
+ if (this._evals.has(script)) return this._evals.get(script);
+
+ const promise = new Promise((resolve, reject) => {
+ const listener = message => {
+ if (!message || message._eval !== script) return;
+ this.process.removeListener('message', listener);
+ this._evals.delete(script);
+ if (!message._error) resolve(message._result); else reject(Util.makeError(message._error));
+ };
+ this.process.on('message', listener);
+
+ this.send({ _eval: script }).catch(err => {
+ this.process.removeListener('message', listener);
+ this._evals.delete(script);
+ reject(err);
+ });
+ });
+
+ this._evals.set(script, promise);
+ return promise;
+ }
+
+ /**
+ * Handles an IPC message.
+ * @param {*} message Message received
+ * @private
+ */
+ _handleMessage(message) {
+ if (message) {
+ // Shard is ready
+ if (message._ready) {
+ this.ready = true;
+ /**
+ * Emitted upon the shard's {@link Client#ready} event.
+ * @event Shard#ready
+ */
+ this.emit('ready');
+ return;
+ }
+
+ // Shard has disconnected
+ if (message._disconnect) {
+ this.ready = false;
+ /**
+ * Emitted upon the shard's {@link Client#disconnect} event.
+ * @event Shard#disconnect
+ */
+ this.emit('disconnect');
+ return;
+ }
+
+ // Shard is attempting to reconnect
+ if (message._reconnecting) {
+ this.ready = false;
+ /**
+ * Emitted upon the shard's {@link Client#reconnecting} event.
+ * @event Shard#reconnecting
+ */
+ this.emit('reconnecting');
+ return;
+ }
+
+ // Shard is requesting a property fetch
+ if (message._sFetchProp) {
+ this.manager.fetchClientValues(message._sFetchProp).then(
+ results => this.send({ _sFetchProp: message._sFetchProp, _result: results }),
+ err => this.send({ _sFetchProp: message._sFetchProp, _error: Util.makePlainError(err) })
+ );
+ return;
+ }
+
+ // Shard is requesting an eval broadcast
+ if (message._sEval) {
+ this.manager.broadcastEval(message._sEval).then(
+ results => this.send({ _sEval: message._sEval, _result: results }),
+ err => this.send({ _sEval: message._sEval, _error: Util.makePlainError(err) })
+ );
+ return;
+ }
+ }
+
+ /**
+ * Emitted upon recieving a message from a shard.
+ * @event ShardingManager#message
+ * @param {Shard} shard Shard that sent the message
+ * @param {*} message Message that was received
+ */
+ this.manager.emit('message', this, message);
+
+ /**
+ * Emitted upon recieving a message from the child process.
+ * @event Shard#message
+ * @param {*} message Message that was received
+ */
+ this.emit('message', message);
+ }
+
+ /**
+ * Handles the shard's process exiting.
+ * @param {boolean} [respawn=this.manager.respawn] Whether to spawn the shard again
+ * @private
+ */
+ _handleExit(respawn = this.manager.respawn) {
+ /**
+ * Emitted upon the shard's child process exiting.
+ * @event Shard#death
+ * @param {ChildProcess} process Child process that exited
+ */
+ this.emit('death', this.process);
+
+ this.process = null;
+ this._evals.clear();
+ this._fetches.clear();
+
+ if (respawn) this.manager.createShard(this.id);
+ }
+}
+
+module.exports = Shard;
diff --git a/node_modules/discord.js/src/sharding/ShardClientUtil.js b/node_modules/discord.js/src/sharding/ShardClientUtil.js
new file mode 100644
index 0000000..28571f1
--- /dev/null
+++ b/node_modules/discord.js/src/sharding/ShardClientUtil.js
@@ -0,0 +1,146 @@
+const Util = require('../util/Util');
+
+/**
+ * Helper class for sharded clients spawned as a child process, such as from a ShardingManager.
+ */
+class ShardClientUtil {
+ /**
+ * @param {Client} client The client of the current shard
+ */
+ constructor(client) {
+ this.client = client;
+ process.on('message', this._handleMessage.bind(this));
+ client.on('ready', () => { process.send({ _ready: true }); });
+ client.on('disconnect', () => { process.send({ _disconnect: true }); });
+ client.on('reconnecting', () => { process.send({ _reconnecting: true }); });
+ }
+
+ /**
+ * ID of this shard
+ * @type {number}
+ * @readonly
+ */
+ get id() {
+ return this.client.options.shardId;
+ }
+
+ /**
+ * Total number of shards
+ * @type {number}
+ * @readonly
+ */
+ get count() {
+ return this.client.options.shardCount;
+ }
+
+ /**
+ * Sends a message to the master process.
+ * @param {*} message Message to send
+ * @returns {Promise<void>}
+ */
+ send(message) {
+ return new Promise((resolve, reject) => {
+ process.send(message, err => {
+ if (err) reject(err); else resolve();
+ });
+ });
+ }
+
+ /**
+ * Fetches a client property value of each shard.
+ * @param {string} prop Name of the client property to get, using periods for nesting
+ * @returns {Promise<Array>}
+ * @example
+ * client.shard.fetchClientValues('guilds.size')
+ * .then(results => {
+ * console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`);
+ * })
+ * .catch(console.error);
+ */
+ fetchClientValues(prop) {
+ return new Promise((resolve, reject) => {
+ const listener = message => {
+ if (!message || message._sFetchProp !== prop) return;
+ process.removeListener('message', listener);
+ if (!message._error) resolve(message._result); else reject(Util.makeError(message._error));
+ };
+ process.on('message', listener);
+
+ this.send({ _sFetchProp: prop }).catch(err => {
+ process.removeListener('message', listener);
+ reject(err);
+ });
+ });
+ }
+
+ /**
+ * Evaluates a script on all shards, in the context of the Clients.
+ * @param {string} script JavaScript to run on each shard
+ * @returns {Promise<Array>} Results of the script execution
+ */
+ broadcastEval(script) {
+ return new Promise((resolve, reject) => {
+ const listener = message => {
+ if (!message || message._sEval !== script) return;
+ process.removeListener('message', listener);
+ if (!message._error) resolve(message._result); else reject(Util.makeError(message._error));
+ };
+ process.on('message', listener);
+
+ this.send({ _sEval: script }).catch(err => {
+ process.removeListener('message', listener);
+ reject(err);
+ });
+ });
+ }
+
+ /**
+ * Handles an IPC message.
+ * @param {*} message Message received
+ * @private
+ */
+ _handleMessage(message) {
+ if (!message) return;
+ if (message._fetchProp) {
+ const props = message._fetchProp.split('.');
+ let value = this.client;
+ for (const prop of props) value = value[prop];
+ this._respond('fetchProp', { _fetchProp: message._fetchProp, _result: value });
+ } else if (message._eval) {
+ try {
+ this._respond('eval', { _eval: message._eval, _result: this.client._eval(message._eval) });
+ } catch (err) {
+ this._respond('eval', { _eval: message._eval, _error: Util.makePlainError(err) });
+ }
+ }
+ }
+
+ /**
+ * Sends a message to the master process, emitting an error from the client upon failure.
+ * @param {string} type Type of response to send
+ * @param {*} message Message to send
+ * @private
+ */
+ _respond(type, message) {
+ this.send(message).catch(err => {
+ err.message = `Error when sending ${type} response to master process: ${err.message}`;
+ this.client.emit('error', err);
+ });
+ }
+
+ /**
+ * Creates/gets the singleton of this class.
+ * @param {Client} client The client to use
+ * @returns {ShardClientUtil}
+ */
+ static singleton(client) {
+ if (!this._singleton) {
+ this._singleton = new this(client);
+ } else {
+ client.emit('warn', 'Multiple clients created in child process; only the first will handle sharding helpers.');
+ }
+ return this._singleton;
+ }
+}
+
+module.exports = ShardClientUtil;
diff --git a/node_modules/discord.js/src/sharding/ShardingManager.js b/node_modules/discord.js/src/sharding/ShardingManager.js
new file mode 100644
index 0000000..5b07299
--- /dev/null
+++ b/node_modules/discord.js/src/sharding/ShardingManager.js
@@ -0,0 +1,220 @@
+const path = require('path');
+const fs = require('fs');
+const EventEmitter = require('events').EventEmitter;
+const Shard = require('./Shard');
+const Collection = require('../util/Collection');
+const Util = require('../util/Util');
+
+/**
+ * This is a utility class that can be used to help you spawn shards of your client. Each shard is completely separate
+ * from the other. The Shard Manager takes a path to a file and spawns it under the specified amount of shards safely.
+ * If you do not select an amount of shards, the manager will automatically decide the best amount.
+ * @extends {EventEmitter}
+ */
+class ShardingManager extends EventEmitter {
+ /**
+ * @param {string} file Path to your shard script file
+ * @param {Object} [options] Options for the sharding manager
+ * @param {number|string} [options.totalShards='auto'] Number of shards to spawn, or "auto"
+ * @param {boolean} [options.respawn=true] Whether shards should automatically respawn upon exiting
+ * @param {string[]} [options.shardArgs=[]] Arguments to pass to the shard script when spawning
+ * @param {string} [options.token] Token to use for automatic shard count and passing to shards
+ */
+ constructor(file, options = {}) {
+ super();
+ options = Util.mergeDefault({
+ totalShards: 'auto',
+ respawn: true,
+ shardArgs: [],
+ token: null,
+ }, options);
+
+ /**
+ * Path to the shard script file
+ * @type {string}
+ */
+ this.file = file;
+ if (!file) throw new Error('File must be specified.');
+ if (!path.isAbsolute(file)) this.file = path.resolve(process.cwd(), file);
+ const stats = fs.statSync(this.file);
+ if (!stats.isFile()) throw new Error('File path does not point to a file.');
+
+ /**
+ * Amount of shards that this manager is going to spawn
+ * @type {number|string}
+ */
+ this.totalShards = options.totalShards;
+ if (this.totalShards !== 'auto') {
+ if (typeof this.totalShards !== 'number' || isNaN(this.totalShards)) {
+ throw new TypeError('Amount of shards must be a number.');
+ }
+ if (this.totalShards < 1) throw new RangeError('Amount of shards must be at least 1.');
+ if (this.totalShards !== Math.floor(this.totalShards)) {
+ throw new RangeError('Amount of shards must be an integer.');
+ }
+ }
+
+ /**
+ * Whether shards should automatically respawn upon exiting
+ * @type {boolean}
+ */
+ this.respawn = options.respawn;
+
+ /**
+ * An array of arguments to pass to shards
+ * @type {string[]}
+ */
+ this.shardArgs = options.shardArgs;
+
+ /**
+ * Arguments for the shard's process executable
+ * @type {?string[]}
+ */
+ this.execArgv = options.execArgv;
+
+ /**
+ * Token to use for obtaining the automatic shard count, and passing to shards
+ * @type {?string}
+ */
+ this.token = options.token ? options.token.replace(/^Bot\s*/i, '') : null;
+
+ /**
+ * A collection of shards that this manager has spawned
+ * @type {Collection<number, Shard>}
+ */
+ this.shards = new Collection();
+ }
+
+ /**
+ * Spawns a single shard.
+ * @param {number} id The ID of the shard to spawn. **This is usually not necessary**
+ * @returns {Promise<Shard>}
+ */
+ createShard(id = this.shards.size) {
+ const shard = new Shard(this, id, this.shardArgs);
+ this.shards.set(id, shard);
+ /**
+ * Emitted upon launching a shard.
+ * @event ShardingManager#launch
+ * @param {Shard} shard Shard that was launched
+ */
+ this.emit('launch', shard);
+ return Promise.resolve(shard);
+ }
+
+ /**
+ * Spawns multiple shards.
+ * @param {number} [amount=this.totalShards] Number of shards to spawn
+ * @param {number} [delay=7500] How long to wait in between spawning each shard (in milliseconds)
+ * @returns {Promise<Collection<number, Shard>>}
+ */
+ spawn(amount = this.totalShards, delay = 7500) {
+ if (amount === 'auto') {
+ return Util.fetchRecommendedShards(this.token).then(count => {
+ this.totalShards = count;
+ return this._spawn(count, delay);
+ });
+ } else {
+ if (typeof amount !== 'number' || isNaN(amount)) throw new TypeError('Amount of shards must be a number.');
+ if (amount < 1) throw new RangeError('Amount of shards must be at least 1.');
+ if (amount !== Math.floor(amount)) throw new TypeError('Amount of shards must be an integer.');
+ return this._spawn(amount, delay);
+ }
+ }
+
+ /**
+ * Actually spawns shards, unlike that poser above >:(
+ * @param {number} amount Number of shards to spawn
+ * @param {number} delay How long to wait in between spawning each shard (in milliseconds)
+ * @returns {Promise<Collection<number, Shard>>}
+ * @private
+ */
+ _spawn(amount, delay) {
+ return new Promise(resolve => {
+ if (this.shards.size >= amount) throw new Error(`Already spawned ${this.shards.size} shards.`);
+ this.totalShards = amount;
+
+ this.createShard();
+ if (this.shards.size >= this.totalShards) {
+ resolve(this.shards);
+ return;
+ }
+
+ if (delay <= 0) {
+ while (this.shards.size < this.totalShards) this.createShard();
+ resolve(this.shards);
+ } else {
+ const interval = setInterval(() => {
+ this.createShard();
+ if (this.shards.size >= this.totalShards) {
+ clearInterval(interval);
+ resolve(this.shards);
+ }
+ }, delay);
+ }
+ });
+ }
+
+ /**
+ * Send a message to all shards.
+ * @param {*} message Message to be sent to the shards
+ * @returns {Promise<Shard[]>}
+ */
+ broadcast(message) {
+ const promises = [];
+ for (const shard of this.shards.values()) promises.push(shard.send(message));
+ return Promise.all(promises);
+ }
+
+ /**
+ * Evaluates a script on all shards, in the context of the Clients.
+ * @param {string} script JavaScript to run on each shard
+ * @returns {Promise<Array>} Results of the script execution
+ */
+ broadcastEval(script) {
+ const promises = [];
+ for (const shard of this.shards.values()) promises.push(shard.eval(script));
+ return Promise.all(promises);
+ }
+
+ /**
+ * Fetches a client property value of each shard.
+ * @param {string} prop Name of the client property to get, using periods for nesting
+ * @returns {Promise<Array>}
+ * @example
+ * manager.fetchClientValues('guilds.size')
+ * .then(results => {
+ * console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`);
+ * })
+ * .catch(console.error);
+ */
+ fetchClientValues(prop) {
+ if (this.shards.size === 0) return Promise.reject(new Error('No shards have been spawned.'));
+ if (this.shards.size !== this.totalShards) return Promise.reject(new Error('Still spawning shards.'));
+ const promises = [];
+ for (const shard of this.shards.values()) promises.push(shard.fetchClientValue(prop));
+ return Promise.all(promises);
+ }
+
+ /**
+ * Kills all running shards and respawns them.
+ * @param {number} [shardDelay=5000] How long to wait between shards (in milliseconds)
+ * @param {number} [respawnDelay=500] How long to wait between killing a shard's process and restarting it
+ * (in milliseconds)
+ * @param {boolean} [waitForReady=true] Whether to wait for a shard to become ready before continuing to another
+ * @param {number} [currentShardIndex=0] The shard index to start respawning at
+ * @returns {Promise<Collection<number, Shard>>}
+ */
+ respawnAll(shardDelay = 5000, respawnDelay = 500, waitForReady = true, currentShardIndex = 0) {
+ let s = 0;
+ const shard = this.shards.get(currentShardIndex);
+ const promises = [shard.respawn(respawnDelay, waitForReady)];
+ if (++s < this.shards.size && shardDelay > 0) promises.push(Util.delayFor(shardDelay));
+ return Promise.all(promises).then(() => {
+ if (++currentShardIndex === this.shards.size) return this.shards;
+ return this.respawnAll(shardDelay, respawnDelay, waitForReady, currentShardIndex);
+ });
+ }
+}
+
+module.exports = ShardingManager;
diff --git a/node_modules/discord.js/src/structures/Attachment.js b/node_modules/discord.js/src/structures/Attachment.js
new file mode 100644
index 0000000..216b61c
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Attachment.js
@@ -0,0 +1,75 @@
+/**
+ * Represents an attachment in a message.
+ * @param {BufferResolvable|Stream} file The file
+ * @param {string} [name] The name of the file, if any
+ */
+class Attachment {
+ constructor(file, name) {
+ this.file = null;
+ if (name) this.setAttachment(file, name);
+ else this._attach(file);
+ }
+
+ /**
+ * The name of the file
+ * @type {?string}
+ * @readonly
+ */
+ get name() {
+ return this.file.name;
+ }
+
+ /**
+ * The file
+ * @type {?BufferResolvable|Stream}
+ * @readonly
+ */
+ get attachment() {
+ return this.file.attachment;
+ }
+
+ /**
+ * Set the file of this attachment.
+ * @param {BufferResolvable|Stream} file The file
+ * @param {string} name The name of the file
+ * @returns {Attachment} This attachment
+ */
+ setAttachment(file, name) {
+ this.file = { attachment: file, name };
+ return this;
+ }
+
+ /**
+ * Set the file of this attachment.
+ * @param {BufferResolvable|Stream} attachment The file
+ * @returns {Attachment} This attachment
+ */
+ setFile(attachment) {
+ this.file = { attachment };
+ return this;
+ }
+
+ /**
+ * Set the name of this attachment.
+ * @param {string} name The name of the image
+ * @returns {Attachment} This attachment
+ */
+ setName(name) {
+ this.file.name = name;
+ return this;
+ }
+
+ /**
+ * Set the file of this attachment.
+ * @param {BufferResolvable|Stream} file The file
+ * @param {string} name The name of the file
+ * @returns {void}
+ * @private
+ */
+ _attach(file, name) {
+ if (typeof file === 'string') this.file = file;
+ else this.setAttachment(file, name);
+ }
+}
+
+module.exports = Attachment;
diff --git a/node_modules/discord.js/src/structures/CategoryChannel.js b/node_modules/discord.js/src/structures/CategoryChannel.js
new file mode 100644
index 0000000..9ba263a
--- /dev/null
+++ b/node_modules/discord.js/src/structures/CategoryChannel.js
@@ -0,0 +1,22 @@
+const GuildChannel = require('./GuildChannel');
+
+/**
+ * Represents a guild category channel on Discord.
+ * @extends {GuildChannel}
+ */
+class CategoryChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'category';
+ }
+ /**
+ * The channels that are part of this category
+ * @type {?Collection<Snowflake, GuildChannel>}
+ * @readonly
+ */
+ get children() {
+ return this.guild.channels.filter(c => c.parentID === this.id);
+ }
+}
+
+module.exports = CategoryChannel;
diff --git a/node_modules/discord.js/src/structures/Channel.js b/node_modules/discord.js/src/structures/Channel.js
new file mode 100644
index 0000000..732945d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Channel.js
@@ -0,0 +1,78 @@
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents any channel on Discord.
+ */
+class Channel {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the Channel
+ * @name Channel#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The type of the channel, either:
+ * * `dm` - a DM channel
+ * * `group` - a Group DM channel
+ * * `text` - a guild text channel
+ * * `voice` - a guild voice channel
+ * * `category` - a guild category channel
+ * * `news` - a guild news channel
+ * * `store` - a guild store channel
+ * @type {string}
+ */
+ this.type = null;
+
+ /**
+ * Whether the channel has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The unique ID of the channel
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+ }
+
+ /**
+ * The timestamp the channel was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the channel was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Deletes the channel.
+ * @returns {Promise<Channel>}
+ * @example
+ * // Delete the channel
+ * channel.delete()
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ delete() {
+ return this.client.rest.methods.deleteChannel(this);
+ }
+}
+
+module.exports = Channel;
diff --git a/node_modules/discord.js/src/structures/ClientUser.js b/node_modules/discord.js/src/structures/ClientUser.js
new file mode 100644
index 0000000..4ae8c6e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUser.js
@@ -0,0 +1,447 @@
+const User = require('./User');
+const Collection = require('../util/Collection');
+const ClientUserSettings = require('./ClientUserSettings');
+const ClientUserGuildSettings = require('./ClientUserGuildSettings');
+const Constants = require('../util/Constants');
+const util = require('util');
+
+/**
+ * Represents the logged in client's Discord user.
+ * @extends {User}
+ */
+class ClientUser extends User {
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * Whether or not this account has been verified
+ * @type {boolean}
+ */
+ this.verified = data.verified;
+
+ /**
+ * The email of this account
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?string}
+ * @deprecated
+ */
+ this.email = data.email;
+ this.localPresence = {};
+ this._typing = new Map();
+
+ /**
+ * A Collection of friends for the logged in user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, User>}
+ * @deprecated
+ */
+ this.friends = new Collection();
+
+ /**
+ * A Collection of blocked users for the logged in user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, User>}
+ * @deprecated
+ */
+ this.blocked = new Collection();
+
+ /**
+ * A Collection of notes for the logged in user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, string>}
+ * @deprecated
+ */
+ this.notes = new Collection();
+
+ /**
+ * If the user has Discord premium (nitro)
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?boolean}
+ * @deprecated
+ */
+ this.premium = typeof data.premium === 'boolean' ? data.premium : null;
+
+ /**
+ * If the user has MFA enabled on their account
+ * @type {boolean}
+ */
+ this.mfaEnabled = data.mfa_enabled;
+
+ /**
+ * If the user has ever used a mobile device on Discord
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?boolean}
+ * @deprecated
+ */
+ this.mobile = typeof data.mobile === 'boolean' ? data.mobile : null;
+
+ /**
+ * Various settings for this user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?ClientUserSettings}
+ * @deprecated
+ */
+ this.settings = data.user_settings ? new ClientUserSettings(this, data.user_settings) : null;
+
+ /**
+ * All of the user's guild settings
+ * <warn>This is only filled when using a user account</warn>
+ * @type {Collection<Snowflake, ClientUserGuildSettings>}
+ * @deprecated
+ */
+ this.guildSettings = new Collection();
+ if (data.user_guild_settings) {
+ for (const settings of data.user_guild_settings) {
+ this.guildSettings.set(settings.guild_id, new ClientUserGuildSettings(settings, this.client));
+ }
+ }
+ }
+
+ edit(data) {
+ return this.client.rest.methods.updateCurrentUser(data);
+ }
+
+ /**
+ * Set the username of the logged in client.
+ * <info>Changing usernames in Discord is heavily rate limited, with only 2 requests
+ * every hour. Use this sparingly!</info>
+ * @param {string} username The new username
+ * @param {string} [password] Current password (only for user accounts)
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set username
+ * client.user.setUsername('discordjs')
+ * .then(user => console.log(`My new username is ${user.username}`))
+ * .catch(console.error);
+ */
+ setUsername(username, password) {
+ return this.client.rest.methods.updateCurrentUser({ username }, password);
+ }
+
+ /**
+ * Changes the email for the client user's account.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {string} email New email to change to
+ * @param {string} password Current password
+ * @returns {Promise<ClientUser>}
+ * @deprecated
+ * @example
+ * // Set email
+ * client.user.setEmail('[email protected]', 'some amazing password 123')
+ * .then(user => console.log(`My new email is ${user.email}`))
+ * .catch(console.error);
+ */
+ setEmail(email, password) {
+ return this.client.rest.methods.updateCurrentUser({ email }, password);
+ }
+
+ /**
+ * Changes the password for the client user's account.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {string} newPassword New password to change to
+ * @param {string} oldPassword Current password
+ * @returns {Promise<ClientUser>}
+ * @deprecated
+ * @example
+ * // Set password
+ * client.user.setPassword('some new amazing password 456', 'some amazing password 123')
+ * .then(user => console.log('New password set!'))
+ * .catch(console.error);
+ */
+ setPassword(newPassword, oldPassword) {
+ return this.client.rest.methods.updateCurrentUser({ password: newPassword }, oldPassword);
+ }
+
+ /**
+ * Set the avatar of the logged in client.
+ * @param {BufferResolvable|Base64Resolvable} avatar The new avatar
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set avatar
+ * client.user.setAvatar('./avatar.png')
+ * .then(user => console.log(`New avatar set!`))
+ * .catch(console.error);
+ */
+ setAvatar(avatar) {
+ return this.client.resolver.resolveImage(avatar).then(data =>
+ this.client.rest.methods.updateCurrentUser({ avatar: data })
+ );
+ }
+
+ /**
+ * Data resembling a raw Discord presence.
+ * @typedef {Object} PresenceData
+ * @property {PresenceStatus} [status] Status of the user
+ * @property {boolean} [afk] Whether the user is AFK
+ * @property {Object} [game] Game the user is playing
+ * @property {string} [game.name] Name of the game
+ * @property {string} [game.url] Twitch stream URL
+ * @property {?ActivityType|number} [game.type] Type of the activity
+ */
+
+ /**
+ * Sets the full presence of the client user.
+ * @param {PresenceData} data Data for the presence
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set the client user's presence
+ * client.user.setPresence({ game: { name: 'with discord.js' }, status: 'idle' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setPresence(data) {
+ // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}}
+ return new Promise(resolve => {
+ let status = this.localPresence.status || this.presence.status;
+ let game = this.localPresence.game;
+ let afk = this.localPresence.afk || this.presence.afk;
+
+ if (!game && this.presence.game) {
+ game = {
+ name: this.presence.game.name,
+ type: this.presence.game.type,
+ url: this.presence.game.url,
+ };
+ }
+
+ if (data.status) {
+ if (typeof data.status !== 'string') throw new TypeError('Status must be a string');
+ if (this.bot) {
+ status = data.status;
+ } else {
+ this.settings.update(Constants.UserSettingsMap.status, data.status);
+ status = 'invisible';
+ }
+ }
+
+ if (data.game) {
+ game = data.game;
+ game.type = game.url && typeof game.type === 'undefined' ? 1 : game.type || 0;
+ if (typeof game.type === 'string') {
+ game.type = Constants.ActivityTypes.indexOf(game.type.toUpperCase());
+ }
+ } else if (typeof data.game !== 'undefined') {
+ game = null;
+ }
+
+ if (typeof data.afk !== 'undefined') afk = data.afk;
+ afk = Boolean(afk);
+
+ this.localPresence = { status, game, afk };
+ this.localPresence.since = 0;
+ this.localPresence.game = this.localPresence.game || null;
+
+ this.client.ws.send({
+ op: 3,
+ d: this.localPresence,
+ });
+
+ this.client._setPresence(this.id, this.localPresence);
+
+ resolve(this);
+ });
+ }
+
+ /**
+ * A user's status. Must be one of:
+ * * `online`
+ * * `idle`
+ * * `invisible`
+ * * `dnd` (do not disturb)
+ * @typedef {string} PresenceStatus
+ */
+
+ /**
+ * Sets the status of the client user.
+ * @param {PresenceStatus} status Status to change to
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set the client user's status
+ * client.user.setStatus('idle')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setStatus(status) {
+ return this.setPresence({ status });
+ }
+
+ /**
+ * Sets the game the client user is playing.
+ * @param {?string} game Game being played
+ * @param {?string} [streamingURL] Twitch stream URL
+ * @returns {Promise<ClientUser>}
+ * @deprecated
+ */
+ setGame(game, streamingURL) {
+ if (!game) return this.setPresence({ game: null });
+ return this.setPresence({
+ game: {
+ name: game,
+ url: streamingURL,
+ },
+ });
+ }
+
+ /**
+ * Sets the activity the client user is playing.
+ * @param {?string} name Activity being played
+ * @param {Object} [options] Options for setting the activity
+ * @param {string} [options.url] Twitch stream URL
+ * @param {ActivityType|number} [options.type] Type of the activity
+ * @returns {Promise<Presence>}
+ * @example
+ * client.user.setActivity('YouTube', { type: 'WATCHING' })
+ * .then(presence => console.log(`Activity set to ${presence.game ? presence.game.name : 'none'}`))
+ * .catch(console.error);
+ */
+ setActivity(name, { url, type } = {}) {
+ if (!name) return this.setPresence({ game: null });
+ return this.setPresence({
+ game: { name, type, url },
+ }).then(clientUser => clientUser.presence);
+ }
+
+ /**
+ * Sets/removes the AFK flag for the client user.
+ * @param {boolean} afk Whether or not the user is AFK
+ * @returns {Promise<ClientUser>}
+ */
+ setAFK(afk) {
+ return this.setPresence({ afk });
+ }
+
+ /**
+ * Fetches messages that mentioned the client's user.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {Object} [options] Options for the fetch
+ * @param {number} [options.limit=25] Maximum number of mentions to retrieve
+ * @param {boolean} [options.roles=true] Whether to include role mentions
+ * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions
+ * @param {GuildResolvable} [options.guild] Limit the search to a specific guild
+ * @returns {Promise<Message[]>}
+ * @deprecated
+ * @example
+ * // Fetch mentions
+ * client.user.fetchMentions()
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Fetch mentions from a guild
+ * client.user.fetchMentions({ guild: '222078108977594368' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchMentions(options = {}) {
+ return this.client.rest.methods.fetchMentions(options);
+ }
+
+ /**
+ * Send a friend request.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {UserResolvable} user The user to send the friend request to
+ * @returns {Promise<User>} The user the friend request was sent to
+ * @deprecated
+ */
+ addFriend(user) {
+ user = this.client.resolver.resolveUser(user);
+ return this.client.rest.methods.addFriend(user);
+ }
+
+ /**
+ * Remove a friend.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {UserResolvable} user The user to remove from your friends
+ * @returns {Promise<User>} The user that was removed
+ * @deprecated
+ */
+ removeFriend(user) {
+ user = this.client.resolver.resolveUser(user);
+ return this.client.rest.methods.removeFriend(user);
+ }
+
+ /**
+ * Creates a guild.
+ * <warn>This is only available to bots in less than 10 guilds and user accounts.</warn>
+ * @param {string} name The name of the guild
+ * @param {string} [region] The region for the server
+ * @param {BufferResolvable|Base64Resolvable} [icon=null] The icon for the guild
+ * @returns {Promise<Guild>} The guild that was created
+ */
+ createGuild(name, region, icon = null) {
+ if (typeof icon === 'string' && icon.startsWith('data:')) {
+ return this.client.rest.methods.createGuild({ name, icon, region });
+ } else {
+ return this.client.resolver.resolveImage(icon).then(data =>
+ this.client.rest.methods.createGuild({ name, icon: data, region })
+ );
+ }
+ }
+
+ /**
+ * An object containing either a user or access token, and an optional nickname.
+ * @typedef {Object} GroupDMRecipientOptions
+ * @property {UserResolvable|Snowflake} [user] User to add to the Group DM
+ * (only available if a user is creating the DM)
+ * @property {string} [accessToken] Access token to use to add a user to the Group DM
+ * (only available if a bot is creating the DM)
+ * @property {string} [nick] Permanent nickname (only available if a bot is creating the DM)
+ */
+
+ /**
+ * Creates a Group DM.
+ * @param {GroupDMRecipientOptions[]} recipients The recipients
+ * @returns {Promise<GroupDMChannel>}
+ * @example
+ * // Create a Group DM with a token provided from OAuth
+ * client.user.createGroupDM([{
+ * user: '66564597481480192',
+ * accessToken: token
+ * }])
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ createGroupDM(recipients) {
+ return this.client.rest.methods.createGroupDM({
+ recipients: recipients.map(u => this.client.resolver.resolveUserID(u.user)),
+ accessTokens: recipients.map(u => u.accessToken),
+ nicks: recipients.reduce((o, r) => {
+ if (r.nick) o[r.user ? r.user.id : r.id] = r.nick;
+ return o;
+ }, {}),
+ });
+ }
+
+ /**
+ * Accepts an invite to join a guild.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {Invite|string} invite Invite or code to accept
+ * @returns {Promise<Guild>} Joined guild
+ * @deprecated
+ */
+ acceptInvite(invite) {
+ return this.client.rest.methods.acceptInvite(invite);
+ }
+}
+
+ClientUser.prototype.acceptInvite =
+ util.deprecate(ClientUser.prototype.acceptInvite, 'ClientUser#acceptInvite: userbot methods will be removed');
+
+ClientUser.prototype.setGame =
+ util.deprecate(ClientUser.prototype.setGame, 'ClientUser#setGame: use ClientUser#setActivity instead');
+
+ClientUser.prototype.addFriend =
+ util.deprecate(ClientUser.prototype.addFriend, 'ClientUser#addFriend: userbot methods will be removed');
+
+ClientUser.prototype.removeFriend =
+ util.deprecate(ClientUser.prototype.removeFriend, 'ClientUser#removeFriend: userbot methods will be removed');
+
+ClientUser.prototype.setPassword =
+ util.deprecate(ClientUser.prototype.setPassword, 'ClientUser#setPassword: userbot methods will be removed');
+
+ClientUser.prototype.setEmail =
+ util.deprecate(ClientUser.prototype.setEmail, 'ClientUser#setEmail: userbot methods will be removed');
+
+ClientUser.prototype.fetchMentions =
+ util.deprecate(ClientUser.prototype.fetchMentions, 'ClientUser#fetchMentions: userbot methods will be removed');
+
+module.exports = ClientUser;
diff --git a/node_modules/discord.js/src/structures/ClientUserChannelOverride.js b/node_modules/discord.js/src/structures/ClientUserChannelOverride.js
new file mode 100644
index 0000000..93efa45
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUserChannelOverride.js
@@ -0,0 +1,30 @@
+const Constants = require('../util/Constants');
+
+/**
+ * A wrapper around the ClientUser's channel overrides.
+ */
+class ClientUserChannelOverride {
+ constructor(data) {
+ this.patch(data);
+ }
+
+ /**
+ * Patch the data contained in this class with new partial data.
+ * @param {Object} data Data to patch this with
+ * @returns {void}
+ * @private
+ */
+ patch(data) {
+ for (const key of Object.keys(Constants.UserChannelOverrideMap)) {
+ const value = Constants.UserChannelOverrideMap[key];
+ if (!data.hasOwnProperty(key)) continue;
+ if (typeof value === 'function') {
+ this[value.name] = value(data[key]);
+ } else {
+ this[value] = data[key];
+ }
+ }
+ }
+}
+
+module.exports = ClientUserChannelOverride;
diff --git a/node_modules/discord.js/src/structures/ClientUserGuildSettings.js b/node_modules/discord.js/src/structures/ClientUserGuildSettings.js
new file mode 100644
index 0000000..5a28747
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUserGuildSettings.js
@@ -0,0 +1,60 @@
+const Constants = require('../util/Constants');
+const Collection = require('../util/Collection');
+const ClientUserChannelOverride = require('./ClientUserChannelOverride');
+
+/**
+ * A wrapper around the ClientUser's guild settings.
+ */
+class ClientUserGuildSettings {
+ constructor(data, client) {
+ /**
+ * The client that created the instance of the ClientUserGuildSettings
+ * @name ClientUserGuildSettings#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ /**
+ * The ID of the guild this settings are for
+ * @type {Snowflake}
+ */
+ this.guildID = data.guild_id;
+ this.channelOverrides = new Collection();
+ this.patch(data);
+ }
+
+ /**
+ * Patch the data contained in this class with new partial data.
+ * @param {Object} data Data to patch this with
+ * @returns {void}
+ * @private
+ */
+ patch(data) {
+ for (const key of Object.keys(Constants.UserGuildSettingsMap)) {
+ const value = Constants.UserGuildSettingsMap[key];
+ if (!data.hasOwnProperty(key)) continue;
+ if (key === 'channel_overrides') {
+ for (const channel of data[key]) {
+ this.channelOverrides.set(channel.channel_id,
+ new ClientUserChannelOverride(channel));
+ }
+ } else if (typeof value === 'function') {
+ this[value.name] = value(data[key]);
+ } else {
+ this[value] = data[key];
+ }
+ }
+ }
+
+ /**
+ * Update a specific property of the guild settings.
+ * @param {string} name Name of property
+ * @param {value} value Value to patch
+ * @returns {Promise<Object>}
+ */
+ update(name, value) {
+ return this.client.rest.methods.patchClientUserGuildSettings(this.guildID, { [name]: value });
+ }
+}
+
+module.exports = ClientUserGuildSettings;
diff --git a/node_modules/discord.js/src/structures/ClientUserSettings.js b/node_modules/discord.js/src/structures/ClientUserSettings.js
new file mode 100644
index 0000000..6b18c03
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUserSettings.js
@@ -0,0 +1,80 @@
+const Constants = require('../util/Constants');
+const Util = require('../util/Util');
+
+/**
+ * A wrapper around the ClientUser's settings.
+ */
+class ClientUserSettings {
+ constructor(user, data) {
+ this.user = user;
+ this.patch(data);
+ }
+
+ /**
+ * Patch the data contained in this class with new partial data.
+ * @param {Object} data Data to patch this with
+ * @returns {void}
+ * @private
+ */
+ patch(data) {
+ for (const key of Object.keys(Constants.UserSettingsMap)) {
+ const value = Constants.UserSettingsMap[key];
+ if (!data.hasOwnProperty(key)) continue;
+ if (typeof value === 'function') {
+ this[value.name] = value(data[key]);
+ } else {
+ this[value] = data[key];
+ }
+ }
+ }
+
+ /**
+ * Update a specific property of of user settings.
+ * @param {string} name Name of property
+ * @param {*} value Value to patch
+ * @returns {Promise<Object>}
+ */
+ update(name, value) {
+ return this.user.client.rest.methods.patchUserSettings({ [name]: value });
+ }
+
+ /**
+ * Sets the position at which this guild will appear in the Discord client.
+ * @param {Guild} guild The guild to move
+ * @param {number} position Absolute or relative position
+ * @param {boolean} [relative=false] Whether to position relatively or absolutely
+ * @returns {Promise<Guild>}
+ */
+ setGuildPosition(guild, position, relative) {
+ const temp = Object.assign([], this.guildPositions);
+ Util.moveElementInArray(temp, guild.id, position, relative);
+ return this.update('guild_positions', temp).then(() => guild);
+ }
+
+ /**
+ * Add a guild to the list of restricted guilds.
+ * @param {Guild} guild The guild to add
+ * @returns {Promise<Guild>}
+ */
+ addRestrictedGuild(guild) {
+ const temp = Object.assign([], this.restrictedGuilds);
+ if (temp.includes(guild.id)) return Promise.reject(new Error('Guild is already restricted'));
+ temp.push(guild.id);
+ return this.update('restricted_guilds', temp).then(() => guild);
+ }
+
+ /**
+ * Remove a guild from the list of restricted guilds.
+ * @param {Guild} guild The guild to remove
+ * @returns {Promise<Guild>}
+ */
+ removeRestrictedGuild(guild) {
+ const temp = Object.assign([], this.restrictedGuilds);
+ const index = temp.indexOf(guild.id);
+ if (index < 0) return Promise.reject(new Error('Guild is not restricted'));
+ temp.splice(index, 1);
+ return this.update('restricted_guilds', temp).then(() => guild);
+ }
+}
+
+module.exports = ClientUserSettings;
diff --git a/node_modules/discord.js/src/structures/DMChannel.js b/node_modules/discord.js/src/structures/DMChannel.js
new file mode 100644
index 0000000..0a6a3d9
--- /dev/null
+++ b/node_modules/discord.js/src/structures/DMChannel.js
@@ -0,0 +1,76 @@
+const Channel = require('./Channel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Collection = require('../util/Collection');
+
+/**
+ * Represents a direct message channel between two users.
+ * @extends {Channel}
+ * @implements {TextBasedChannel}
+ */
+class DMChannel extends Channel {
+ constructor(client, data) {
+ super(client, data);
+ this.type = 'dm';
+ this.messages = new Collection();
+ this._typing = new Map();
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The recipient on the other end of the DM
+ * @type {User}
+ */
+ this.recipient = this.client.dataManager.newUser(data.recipients[0]);
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = data.last_message_id;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the recipient's mention instead of the
+ * DM channel object.
+ * @returns {string}
+ */
+ toString() {
+ return this.recipient.toString();
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastPinAt() {}
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendFiles() {}
+ sendCode() {}
+ fetchMessage() {}
+ fetchMessages() {}
+ fetchPinnedMessages() {}
+ search() {}
+ startTyping() {}
+ stopTyping() {}
+ get typing() {}
+ get typingCount() {}
+ createCollector() {}
+ createMessageCollector() {}
+ awaitMessages() {}
+ // Doesn't work on DM channels; bulkDelete() {}
+ acknowledge() {}
+ _cacheMessage() {}
+}
+
+TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']);
+
+module.exports = DMChannel;
diff --git a/node_modules/discord.js/src/structures/Emoji.js b/node_modules/discord.js/src/structures/Emoji.js
new file mode 100644
index 0000000..f514b81
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Emoji.js
@@ -0,0 +1,273 @@
+const Constants = require('../util/Constants');
+const Collection = require('../util/Collection');
+const Permissions = require('../util/Permissions');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a custom emoji.
+ */
+class Emoji {
+ constructor(guild, data) {
+ /**
+ * The client that instantiated this object
+ * @name Emoji#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: guild.client });
+
+ /**
+ * The guild this emoji is part of
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * Whether this emoji has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the emoji
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the emoji
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * Whether or not this emoji requires colons surrounding it
+ * @type {boolean}
+ */
+ this.requiresColons = data.require_colons;
+
+ /**
+ * Whether this emoji is managed by an external service
+ * @type {boolean}
+ */
+ this.managed = data.managed;
+
+ /**
+ * Whether this emoji is animated
+ * @type {boolean}
+ */
+ this.animated = data.animated;
+
+ /**
+ * Whether this emoji is available
+ * @type {boolean}
+ * @name Emoji#available
+ */
+ if (typeof data.available !== 'undefined') this.available = data.available;
+
+ this._roles = data.roles;
+ }
+
+ /**
+ * The timestamp the emoji was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the emoji was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Whether the emoji is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return !this.managed && this.guild.me.hasPermission(Permissions.FLAGS.MANAGE_EMOJIS);
+ }
+
+ /**
+ * A collection of roles this emoji is active for (empty if all), mapped by role ID
+ * @type {Collection<Snowflake, Role>}
+ * @readonly
+ */
+ get roles() {
+ const roles = new Collection();
+ for (const role of this._roles) {
+ if (this.guild.roles.has(role)) roles.set(role, this.guild.roles.get(role));
+ }
+ return roles;
+ }
+
+ /**
+ * The URL to the emoji file
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).Emoji(this.id, this.animated ? 'gif' : 'png');
+ }
+
+ /**
+ * The identifier of this emoji, used for message reactions
+ * @type {string}
+ * @readonly
+ */
+ get identifier() {
+ if (this.id) return `${this.name}:${this.id}`;
+ return encodeURIComponent(this.name);
+ }
+
+ /**
+ * Data for editing an emoji.
+ * @typedef {Object} EmojiEditData
+ * @property {string} [name] The name of the emoji
+ * @property {Collection<Snowflake, Role>|Array<Snowflake|Role>} [roles] Roles to restrict emoji to
+ */
+
+ /**
+ * Edits the emoji.
+ * @param {EmojiEditData} data The new data for the emoji
+ * @param {string} [reason] Reason for editing this emoji
+ * @returns {Promise<Emoji>}
+ * @example
+ * // Edit an emoji
+ * emoji.edit({name: 'newemoji'})
+ * .then(e => console.log(`Edited emoji ${e}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateEmoji(this, data, reason);
+ }
+
+ /**
+ * Set the name of the emoji.
+ * @param {string} name The new name for the emoji
+ * @param {string} [reason] The reason for changing the emoji's name
+ * @returns {Promise<Emoji>}
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Fetches the author for this emoji
+ * @returns {Promise<User>}
+ */
+ fetchAuthor() {
+ if (this.managed) return Promise.reject(new Error('Emoji is managed and has no Author.'));
+ if (!this.guild.me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS)) {
+ return Promise.reject(
+ new Error(`Client must have Manage Emoji permission in guild ${this.guild} to see emoji authors.`)
+ );
+ }
+ return this.client.rest.makeRequest('get', Constants.Endpoints.Guild(this.guild).Emoji(this.id), true)
+ .then(emoji => this.client.dataManager.newUser(emoji.user));
+ }
+
+ /**
+ * Add a role to the list of roles that can use this emoji.
+ * @param {Role} role The role to add
+ * @returns {Promise<Emoji>}
+ */
+ addRestrictedRole(role) {
+ return this.addRestrictedRoles([role]);
+ }
+
+ /**
+ * Add multiple roles to the list of roles that can use this emoji.
+ * @param {Role[]} roles Roles to add
+ * @returns {Promise<Emoji>}
+ */
+ addRestrictedRoles(roles) {
+ const newRoles = new Collection(this.roles);
+ for (const role of roles) {
+ if (this.guild.roles.has(role.id)) newRoles.set(role.id, role);
+ }
+ return this.edit({ roles: newRoles });
+ }
+
+ /**
+ * Remove a role from the list of roles that can use this emoji.
+ * @param {Role} role The role to remove
+ * @returns {Promise<Emoji>}
+ */
+ removeRestrictedRole(role) {
+ return this.removeRestrictedRoles([role]);
+ }
+
+ /**
+ * Remove multiple roles from the list of roles that can use this emoji.
+ * @param {Role[]} roles Roles to remove
+ * @returns {Promise<Emoji>}
+ */
+ removeRestrictedRoles(roles) {
+ const newRoles = new Collection(this.roles);
+ for (const role of roles) {
+ if (newRoles.has(role.id)) newRoles.delete(role.id);
+ }
+ return this.edit({ roles: newRoles });
+ }
+
+
+ /**
+ * Deletes the emoji.
+ * @param {string} [reason] Reason for deleting the emoji
+ * @returns {Promise<Emoji>}
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteEmoji(this, reason);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the emoji mention rather than the object.
+ * @returns {string}
+ * @example
+ * // Send an emoji:
+ * const emoji = guild.emojis.first();
+ * msg.reply(`Hello! ${emoji}`);
+ */
+ toString() {
+ if (!this.id || !this.requiresColons) {
+ return this.name;
+ }
+
+ return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;
+ }
+
+ /**
+ * Whether this emoji is the same as another one.
+ * @param {Emoji|Object} other The emoji to compare it to
+ * @returns {boolean} Whether the emoji is equal to the given emoji or not
+ */
+ equals(other) {
+ if (other instanceof Emoji) {
+ return (
+ other.id === this.id &&
+ other.name === this.name &&
+ other.managed === this.managed &&
+ other.requiresColons === this.requiresColons
+ );
+ } else {
+ return (
+ other.id === this.id &&
+ other.name === this.name
+ );
+ }
+ }
+}
+
+module.exports = Emoji;
diff --git a/node_modules/discord.js/src/structures/GroupDMChannel.js b/node_modules/discord.js/src/structures/GroupDMChannel.js
new file mode 100644
index 0000000..0febf6c
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GroupDMChannel.js
@@ -0,0 +1,246 @@
+const Channel = require('./Channel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Collection = require('../util/Collection');
+const Constants = require('../util/Constants');
+
+/*
+{ type: 3,
+ recipients:
+ [ { username: 'Charlie',
+ id: '123',
+ discriminator: '6631',
+ avatar: '123' },
+ { username: 'Ben',
+ id: '123',
+ discriminator: '2055',
+ avatar: '123' },
+ { username: 'Adam',
+ id: '123',
+ discriminator: '2406',
+ avatar: '123' } ],
+ owner_id: '123',
+ name: null,
+ last_message_id: '123',
+ id: '123',
+ icon: null }
+*/
+
+/**
+ * Represents a Group DM on Discord.
+ * @extends {Channel}
+ * @implements {TextBasedChannel}
+ */
+class GroupDMChannel extends Channel {
+ constructor(client, data) {
+ super(client, data);
+ this.type = 'group';
+ this.messages = new Collection();
+ this._typing = new Map();
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The name of this Group DM, can be null if one isn't set
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * A hash of this Group DM icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The user ID of this Group DM's owner
+ * @type {string}
+ */
+ this.ownerID = data.owner_id;
+
+ /**
+ * If the DM is managed by an application
+ * @type {boolean}
+ */
+ this.managed = data.managed;
+
+ /**
+ * Application ID of the application that made this Group DM, if applicable
+ * @type {?string}
+ */
+ this.applicationID = data.application_id;
+
+ if (data.nicks) {
+ /**
+ * Nicknames for group members
+ * @type {?Collection<Snowflake, string>}
+ */
+ this.nicks = new Collection(data.nicks.map(n => [n.id, n.nick]));
+ }
+
+ if (!this.recipients) {
+ /**
+ * A collection of the recipients of this DM, mapped by their ID
+ * @type {Collection<Snowflake, User>}
+ */
+ this.recipients = new Collection();
+ }
+
+ if (data.recipients) {
+ for (const recipient of data.recipients) {
+ const user = this.client.dataManager.newUser(recipient);
+ this.recipients.set(user.id, user);
+ }
+ }
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = data.last_message_id;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+ }
+
+ /**
+ * The owner of this Group DM
+ * @type {User}
+ * @readonly
+ */
+ get owner() {
+ return this.client.users.get(this.ownerID);
+ }
+
+ /**
+ * The URL to this guild's icon
+ * @type {?string}
+ * @readonly
+ */
+ get iconURL() {
+ if (!this.icon) return null;
+ return Constants.Endpoints.Channel(this).Icon(this.client.options.http.cdn, this.icon);
+ }
+
+ edit(data) {
+ const _data = {};
+ if (data.name) _data.name = data.name;
+ if (typeof data.icon !== 'undefined') _data.icon = data.icon;
+ return this.client.rest.methods.updateGroupDMChannel(this, _data);
+ }
+
+ /**
+ * Whether this channel equals another channel. It compares all properties, so for most operations
+ * it is advisable to just compare `channel.id === channel2.id` as it is much faster and is often
+ * what most users need.
+ * @param {GroupDMChannel} channel Channel to compare with
+ * @returns {boolean}
+ */
+ equals(channel) {
+ const equal = channel &&
+ this.id === channel.id &&
+ this.name === channel.name &&
+ this.icon === channel.icon &&
+ this.ownerID === channel.ownerID;
+
+ if (equal) {
+ return this.recipients.equals(channel.recipients);
+ }
+
+ return equal;
+ }
+
+ /**
+ * Add a user to the DM
+ * @param {UserResolvable|string} accessTokenOrID Access token or user resolvable
+ * @param {string} [nick] Permanent nickname to give the user (only available if a bot is creating the DM)
+ * @returns {Promise<GroupDMChannel>}
+ */
+
+ addUser(accessTokenOrID, nick) {
+ return this.client.rest.methods.addUserToGroupDM(this, {
+ nick,
+ id: this.client.resolver.resolveUserID(accessTokenOrID),
+ accessToken: accessTokenOrID,
+ });
+ }
+
+ /**
+ * Set a new GroupDMChannel icon.
+ * @param {Base64Resolvable|BufferResolvable} icon The new icon of the group dm
+ * @returns {Promise<GroupDMChannel>}
+ * @example
+ * // Edit the group dm icon
+ * channel.setIcon('./icon.png')
+ * .then(updated => console.log('Updated the channel icon'))
+ * .catch(console.error);
+ */
+ setIcon(icon) {
+ return this.client.resolver.resolveImage(icon).then(data => this.edit({ icon: data }));
+ }
+
+ /**
+ * Sets a new name for this Group DM.
+ * @param {string} name New name for this Group DM
+ * @returns {Promise<GroupDMChannel>}
+ */
+ setName(name) {
+ return this.edit({ name });
+ }
+
+ /**
+ * Removes a user from this Group DM.
+ * @param {UserResolvable} user User to remove
+ * @returns {Promise<GroupDMChannel>}
+ */
+ removeUser(user) {
+ const id = this.client.resolver.resolveUserID(user);
+ return this.client.rest.methods.removeUserFromGroupDM(this, id);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the channel's name instead of the Channel object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from My Group DM!
+ * console.log(`Hello from ${channel}!`);
+ * @example
+ * // Logs: Hello from My Group DM!
+ * console.log(`Hello from ' + channel + '!');
+ */
+ toString() {
+ return this.name;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastPinAt() {}
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendFiles() {}
+ sendCode() {}
+ fetchMessage() {}
+ fetchMessages() {}
+ fetchPinnedMessages() {}
+ search() {}
+ startTyping() {}
+ stopTyping() {}
+ get typing() {}
+ get typingCount() {}
+ createCollector() {}
+ createMessageCollector() {}
+ awaitMessages() {}
+ // Doesn't work on Group DMs; bulkDelete() {}
+ acknowledge() {}
+ _cacheMessage() {}
+}
+
+TextBasedChannel.applyToClass(GroupDMChannel, true, ['bulkDelete']);
+
+module.exports = GroupDMChannel;
diff --git a/node_modules/discord.js/src/structures/Guild.js b/node_modules/discord.js/src/structures/Guild.js
new file mode 100644
index 0000000..69acfe1
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Guild.js
@@ -0,0 +1,1711 @@
+const util = require('util');
+const Long = require('long');
+const User = require('./User');
+const Role = require('./Role');
+const Emoji = require('./Emoji');
+const Presence = require('./Presence').Presence;
+const GuildMember = require('./GuildMember');
+const Integration = require('./Integration');
+const Constants = require('../util/Constants');
+const Collection = require('../util/Collection');
+const Util = require('../util/Util');
+const Snowflake = require('../util/Snowflake');
+const SystemChannelFlags = require('../util/SystemChannelFlags');
+
+/**
+ * Represents a guild (or a server) on Discord.
+ * <info>It's recommended to see if a guild is available before performing operations or reading data from it. You can
+ * check this with `guild.available`.</info>
+ */
+class Guild {
+ constructor(client, data) {
+ /**
+ * The client that created the instance of the guild
+ * @name Guild#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * A collection of members that are in this guild. The key is the member's ID, the value is the member
+ * @type {Collection<Snowflake, GuildMember>}
+ */
+ this.members = new Collection();
+
+ /**
+ * A collection of channels that are in this guild. The key is the channel's ID, the value is the channel
+ * @type {Collection<Snowflake, GuildChannel>}
+ */
+ this.channels = new Collection();
+
+ /**
+ * A collection of roles that are in this guild. The key is the role's ID, the value is the role
+ * @type {Collection<Snowflake, Role>}
+ */
+ this.roles = new Collection();
+
+ /**
+ * A collection of presences in this guild
+ * @type {Collection<Snowflake, Presence>}
+ */
+ this.presences = new Collection();
+
+ /**
+ * Whether the bot has been removed from the guild
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (!data) return;
+ if (data.unavailable) {
+ /**
+ * Whether the guild is available to access. If it is not available, it indicates a server outage
+ * @type {boolean}
+ */
+ this.available = false;
+
+ /**
+ * The Unique ID of the guild, useful for comparisons
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+ } else {
+ this.setup(data);
+ if (!data.channels) this.available = false;
+ }
+ }
+
+ /* eslint-disable complexity */
+ /**
+ * Sets up the guild.
+ * @param {*} data The raw data of the guild
+ * @private
+ */
+ setup(data) {
+ /**
+ * The name of the guild
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The hash of the guild icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The hash of the guild splash image (VIP only)
+ * @type {?string}
+ */
+ this.splash = data.splash;
+
+ /**
+ * The region the guild is located in
+ * @type {string}
+ */
+ this.region = data.region;
+
+ /**
+ * The full amount of members in this guild
+ * @type {number}
+ */
+ this.memberCount = data.member_count || this.memberCount;
+
+ /**
+ * Whether the guild is "large" (has more than 250 members)
+ * @type {boolean}
+ */
+ this.large = Boolean('large' in data ? data.large : this.large);
+
+ /**
+ * An array of guild features
+ * @type {Object[]}
+ */
+ this.features = data.features;
+
+ /**
+ * The ID of the application that created this guild (if applicable)
+ * @type {?Snowflake}
+ */
+ this.applicationID = data.application_id;
+
+ /**
+ * The time in seconds before a user is counted as "away from keyboard"
+ * @type {?number}
+ */
+ this.afkTimeout = data.afk_timeout;
+
+ /**
+ * The ID of the voice channel where AFK members are moved
+ * @type {?string}
+ */
+ this.afkChannelID = data.afk_channel_id;
+
+ /**
+ * The ID of the system channel
+ * @type {?Snowflake}
+ */
+ this.systemChannelID = data.system_channel_id;
+
+ /**
+ * Whether embedded images are enabled on this guild
+ * @type {boolean}
+ */
+ this.embedEnabled = data.embed_enabled;
+
+ /**
+ * The verification level of the guild
+ * @type {number}
+ */
+ this.verificationLevel = data.verification_level;
+
+ /**
+ * The explicit content filter level of the guild
+ * @type {number}
+ */
+ this.explicitContentFilter = data.explicit_content_filter;
+
+ /**
+ * The required MFA level for the guild
+ * @type {number}
+ */
+ this.mfaLevel = data.mfa_level;
+
+ /**
+ * The timestamp the client user joined the guild at
+ * @type {number}
+ */
+ this.joinedTimestamp = data.joined_at ? new Date(data.joined_at).getTime() : this.joinedTimestamp;
+
+ /**
+ * The value set for a guild's default message notifications
+ * @type {DefaultMessageNotifications|number}
+ */
+ this.defaultMessageNotifications = Constants.DefaultMessageNotifications[data.default_message_notifications] ||
+ data.default_message_notifications;
+
+ /**
+ * The value for the guild's system channel flags
+ * @type {Readonly<SystemChannelFlags>}
+ */
+ this.systemChannelFlags = new SystemChannelFlags(data.system_channel_flags).freeze();
+
+ /**
+ * The type of premium tier:
+ * * 0: NONE
+ * * 1: TIER_1
+ * * 2: TIER_2
+ * * 3: TIER_3
+ * @typedef {number} PremiumTier
+ */
+
+ /**
+ * The premium tier on this guild
+ * @type {PremiumTier}
+ */
+ this.premiumTier = data.premium_tier;
+
+ /**
+ * The total number of users currently boosting this server
+ * @type {?number}
+ * @name Guild#premiumSubscriptionCount
+ */
+ if (typeof data.premium_subscription_count !== 'undefined') {
+ this.premiumSubscriptionCount = data.premium_subscription_count;
+ }
+
+ /**
+ * The hash of the guild banner
+ * @type {?string}
+ */
+ this.banner = data.banner;
+
+ /**
+ * The description of the guild, if any
+ * @type {?string}
+ */
+ this.description = data.description;
+
+ /**
+ * The embed channel ID, if enabled
+ * @type {?string}
+ * @name Guild#embedChannelID
+ */
+ if (typeof data.embed_channel_id !== 'undefined') this.embedChannelID = data.embed_channel_id;
+
+ /**
+ * The maximum amount of members the guild can have
+ * <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
+ * @type {?number}
+ * @name Guild#maximumMembers
+ */
+ if (typeof data.max_members !== 'undefined') this.maximumMembers = data.max_members || 250000;
+
+ /**
+ * The maximum amount of presences the guild can have
+ * <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
+ * @type {?number}
+ * @name Guild#maximumPresences
+ */
+ if (typeof data.max_presences !== 'undefined') this.maximumPresences = data.max_presences || 5000;
+
+ /**
+ * Whether widget images are enabled on this guild
+ * @type {?boolean}
+ * @name Guild#widgetEnabled
+ */
+ if (typeof data.widget_enabled !== 'undefined') this.widgetEnabled = data.widget_enabled;
+
+ /**
+ * The widget channel ID, if enabled
+ * @type {?string}
+ * @name Guild#widgetChannelID
+ */
+ if (typeof data.widget_channel_id !== 'undefined') this.widgetChannelID = data.widget_channel_id;
+
+ /**
+ * The vanity URL code of the guild, if any
+ * @type {?string}
+ */
+ this.vanityURLCode = data.vanity_url_code;
+
+ this.id = data.id;
+ this.available = !data.unavailable;
+ this.features = data.features || this.features || [];
+
+ /**
+ * The ID of the rules channel for the guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?Snowflake}
+ */
+ this.rulesChannelID = data.rules_channel_id;
+
+ /**
+ * The ID of the public updates channel for the guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?Snowflake}
+ */
+ this.publicUpdatesChannelID = data.public_updates_channel_id;
+
+ if (data.members) {
+ this.members.clear();
+ for (const guildUser of data.members) this._addMember(guildUser, false);
+ }
+
+ if (data.owner_id) {
+ /**
+ * The user ID of this guild's owner
+ * @type {Snowflake}
+ */
+ this.ownerID = data.owner_id;
+ }
+
+ if (data.channels) {
+ this.channels.clear();
+ for (const channel of data.channels) this.client.dataManager.newChannel(channel, this);
+ }
+
+ if (data.roles) {
+ this.roles.clear();
+ for (const role of data.roles) {
+ const newRole = new Role(this, role);
+ this.roles.set(newRole.id, newRole);
+ }
+ }
+
+ if (data.presences) {
+ for (const presence of data.presences) {
+ this._setPresence(presence.user.id, presence);
+ }
+ }
+
+ this._rawVoiceStates = new Collection();
+ if (data.voice_states) {
+ for (const voiceState of data.voice_states) {
+ this._rawVoiceStates.set(voiceState.user_id, voiceState);
+ const member = this.members.get(voiceState.user_id);
+ const voiceChannel = this.channels.get(voiceState.channel_id);
+ if (member && voiceChannel) {
+ member.serverMute = voiceState.mute;
+ member.serverDeaf = voiceState.deaf;
+ member.selfMute = voiceState.self_mute;
+ member.selfDeaf = voiceState.self_deaf;
+ member.selfStream = voiceState.self_stream || false;
+ member.voiceSessionID = voiceState.session_id;
+ member.voiceChannelID = voiceState.channel_id;
+ voiceChannel.members.set(member.user.id, member);
+ }
+ }
+ }
+
+ if (!this.emojis) {
+ /**
+ * A collection of emojis that are in this guild
+ * The key is the emoji's ID, the value is the emoji
+ * @type {Collection<Snowflake, Emoji>}
+ */
+ this.emojis = new Collection();
+ for (const emoji of data.emojis) this.emojis.set(emoji.id, new Emoji(this, emoji));
+ } else {
+ this.client.actions.GuildEmojisUpdate.handle({
+ guild_id: this.id,
+ emojis: data.emojis,
+ });
+ }
+ }
+
+ /**
+ * The timestamp the guild was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the guild was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Embed channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get embedChannel() {
+ return this.channels.get(this.embedChannelID) || null;
+ }
+
+ /**
+ * Widget channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get widgetChannel() {
+ return this.channels.get(this.widgetChannelID) || null;
+ }
+
+ /**
+ * The time the client user joined the guild
+ * @type {Date}
+ * @readonly
+ */
+ get joinedAt() {
+ return new Date(this.joinedTimestamp);
+ }
+
+ /**
+ * If this guild is verified
+ * @type {boolean}
+ * @readonly
+ */
+ get verified() {
+ return this.features.includes('VERIFIED');
+ }
+
+ /**
+ * The URL to this guild's icon
+ * @type {?string}
+ * @readonly
+ */
+ get iconURL() {
+ if (!this.icon) return null;
+ return Constants.Endpoints.Guild(this).Icon(this.client.options.http.cdn, this.icon);
+ }
+
+ /**
+ * The URL to this guild's banner.
+ * @type {?string}
+ * @readonly
+ */
+ get bannerURL() {
+ if (!this.banner) return null;
+ return Constants.Endpoints.Guild(this).Banner(this.client.options.http.cdn, this.banner);
+ }
+
+ /**
+ * The acronym that shows up in place of a guild icon.
+ * @type {string}
+ * @readonly
+ */
+ get nameAcronym() {
+ return this.name.replace(/\w+/g, name => name[0]).replace(/\s/g, '');
+ }
+
+ /**
+ * The URL to this guild's splash
+ * @type {?string}
+ * @readonly
+ */
+ get splashURL() {
+ if (!this.splash) return null;
+ return Constants.Endpoints.Guild(this).Splash(this.client.options.http.cdn, this.splash);
+ }
+
+ /**
+ * The owner of the guild
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get owner() {
+ return this.members.get(this.ownerID);
+ }
+
+ /**
+ * AFK voice channel for this guild
+ * @type {?VoiceChannel}
+ * @readonly
+ */
+ get afkChannel() {
+ return this.client.channels.get(this.afkChannelID) || null;
+ }
+
+ /**
+ * System channel for this guild
+ * @type {?GuildChannel}
+ * @readonly
+ */
+ get systemChannel() {
+ return this.client.channels.get(this.systemChannelID) || null;
+ }
+
+ /**
+ * If the client is connected to any voice channel in this guild, this will be the relevant VoiceConnection
+ * @type {?VoiceConnection}
+ * @readonly
+ */
+ get voiceConnection() {
+ if (this.client.browser) return null;
+ return this.client.voice.connections.get(this.id) || null;
+ }
+
+ /**
+ * The position of this guild
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?number}
+ * @readonly
+ * @deprecated
+ */
+ get position() {
+ if (this.client.user.bot) return null;
+ if (!this.client.user.settings.guildPositions) return null;
+ return this.client.user.settings.guildPositions.indexOf(this.id);
+ }
+
+ /**
+ * Whether the guild is muted
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get muted() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).muted;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ /**
+ * The type of message that should notify you
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?MessageNotificationType}
+ * @readonly
+ * @deprecated
+ */
+ get messageNotifications() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).messageNotifications;
+ } catch (err) {
+ return null;
+ }
+ }
+
+ /**
+ * Whether to receive mobile push notifications
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get mobilePush() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).mobilePush;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ /**
+ * Whether to suppress everyone messages
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get suppressEveryone() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).suppressEveryone;
+ } catch (err) {
+ return null;
+ }
+ }
+
+ /**
+ * The `@everyone` role of the guild
+ * @type {Role}
+ * @readonly
+ */
+ get defaultRole() {
+ return this.roles.get(this.id);
+ }
+
+ /**
+ * Rules channel for this guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get rulesChannel() {
+ return this.client.channels.get(this.rulesChannelID) || null;
+ }
+
+ /**
+ * Public updates channel for this guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get publicUpdatesChannel() {
+ return this.client.channels.get(this.publicUpdatesChannelID) || null;
+ }
+
+ /**
+ * The client user as a GuildMember of this guild
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get me() {
+ return this.members.get(this.client.user.id);
+ }
+
+ /**
+ * Fetches a collection of roles in the current guild sorted by position
+ * @type {Collection<Snowflake, Role>}
+ * @readonly
+ * @private
+ */
+ get _sortedRoles() {
+ return this._sortPositionWithID(this.roles);
+ }
+
+ /**
+ * Returns the GuildMember form of a User object, if the user is present in the guild.
+ * @param {UserResolvable} user The user that you want to obtain the GuildMember of
+ * @returns {?GuildMember}
+ * @example
+ * // Get the guild member of a user
+ * const member = guild.member(message.author);
+ */
+ member(user) {
+ return this.client.resolver.resolveGuildMember(this, user);
+ }
+
+ /**
+ * Fetches this guild.
+ * @returns {Promise<Guild>}
+ */
+ fetch() {
+ return this.client.rest.methods.getGuild(this).then(data => {
+ this.setup(data);
+
+ return this;
+ });
+ }
+
+ /**
+ * An object containing information about a guild member's ban.
+ * @typedef {Object} BanInfo
+ * @property {User} user User that was banned
+ * @property {?string} reason Reason the user was banned
+ */
+
+ /**
+ * Fetch a ban for a user.
+ * @returns {Promise<BanInfo>}
+ * @param {UserResolvable} user The user to fetch the ban for
+ * @example
+ * // Get ban
+ * guild.fetchBan(message.author)
+ * .then(({ user, reason }) => console.log(`${user.tag} was banned for the reason: ${reason}.`))
+ * .catch(console.error);
+ */
+ fetchBan(user) {
+ return this.client.rest.methods.getGuildBan(this, user);
+ }
+
+ /**
+ * Fetch a collection of banned users in this guild.
+ * @returns {Promise<Collection<Snowflake, User|BanInfo>>}
+ * @param {boolean} [withReasons=false] Whether or not to include the ban reason(s)
+ * @example
+ * // Fetch bans in guild
+ * guild.fetchBans()
+ * .then(bans => console.log(`This guild has ${bans.size} bans`))
+ * .catch(console.error);
+ */
+ fetchBans(withReasons = false) {
+ if (withReasons) return this.client.rest.methods.getGuildBans(this);
+ return this.client.rest.methods.getGuildBans(this)
+ .then(bans => {
+ const users = new Collection();
+ for (const ban of bans.values()) users.set(ban.user.id, ban.user);
+ return users;
+ });
+ }
+
+ /**
+ * Fetches a collection of integrations to this guild.
+ * Resolves with a collection mapping integrations by their ids.
+ * @returns {Promise<Collection<string, Integration>>}
+ * @example
+ * // Fetch integrations
+ * guild.fetchIntegrations()
+ * .then(integrations => console.log(`Fetched ${integrations.size} integrations`))
+ * .catch(console.error);
+ */
+ fetchIntegrations() {
+ return this.client.rest.methods.getIntegrations(this).then(data =>
+ data.reduce((collection, integration) =>
+ collection.set(integration.id, new Integration(this.client, integration, this)),
+ new Collection())
+ );
+ }
+
+ /**
+ * The data for creating an integration.
+ * @typedef {Object} IntegrationData
+ * @property {string} id The integration id
+ * @property {string} type The integration type
+ */
+
+ /**
+ * Creates an integration by attaching an integration object
+ * @param {IntegrationData} data The data for thes integration
+ * @param {string} reason Reason for creating the integration
+ * @returns {Promise<Guild>}
+ */
+ createIntegration(data, reason) {
+ return this.client.rest.methods.createIntegration(this, data, reason)
+ .then(() => this);
+ }
+
+ /**
+ * Fetch a collection of invites to this guild.
+ * Resolves with a collection mapping invites by their codes.
+ * @returns {Promise<Collection<string, Invite>>}
+ * @example
+ * // Fetch invites
+ * guild.fetchInvites()
+ * .then(invites => console.log(`Fetched ${invites.size} invites`))
+ * .catch(console.error);
+ * @example
+ * // Fetch invite creator by their id
+ * guild.fetchInvites()
+ * .then(invites => console.log(invites.find(invite => invite.inviter.id === '84484653687267328')))
+ * .catch(console.error);
+ */
+ fetchInvites() {
+ return this.client.rest.methods.getGuildInvites(this);
+ }
+
+ /**
+ * Fetches the vanity url invite code to this guild.
+ * Resolves with a string matching the vanity url invite code, not the full url.
+ * @returns {Promise<string>}
+ * @example
+ * // Fetch invites
+ * guild.fetchVanityCode()
+ * .then(code => {
+ * console.log(`Vanity URL: https://discord.gg/${code}`);
+ * })
+ * .catch(console.error);
+ */
+ fetchVanityCode() {
+ if (!this.features.includes('VANITY_URL')) {
+ return Promise.reject(new Error('This guild does not have the VANITY_URL feature enabled.'));
+ }
+ return this.client.rest.methods.getGuildVanityCode(this);
+ }
+
+
+ /**
+ * Fetch all webhooks for the guild.
+ * @returns {Promise<Collection<Snowflake, Webhook>>}
+ * @example
+ * // Fetch webhooks
+ * guild.fetchWebhooks()
+ * .then(webhooks => console.log(`Fetched ${webhooks.size} webhooks`))
+ * .catch(console.error);
+ */
+ fetchWebhooks() {
+ return this.client.rest.methods.getGuildWebhooks(this);
+ }
+
+ /**
+ * Fetch available voice regions.
+ * @returns {Promise<Collection<string, VoiceRegion>>}
+ * @example
+ * // Fetch voice regions
+ * guild.fetchVoiceRegions()
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchVoiceRegions() {
+ return this.client.rest.methods.fetchVoiceRegions(this.id);
+ }
+
+ /**
+ * The Guild Embed object
+ * @typedef {Object} GuildEmbedData
+ * @property {boolean} enabled Whether the embed is enabled
+ * @property {?ChannelResolvable} channel The embed channel
+ */
+
+ /**
+ * Fetches the guild embed.
+ * @returns {Promise<GuildEmbedData>}
+ * @example
+ * // Fetches the guild embed
+ * guild.fetchEmbed()
+ * .then(embed => console.log(`The embed is ${embed.enabled ? 'enabled' : 'disabled'}`))
+ * .catch(console.error);
+ */
+ fetchEmbed() {
+ return this.client.rest.methods.fetchEmbed(this.id);
+ }
+
+ /**
+ * Fetch audit logs for this guild.
+ * @param {Object} [options={}] Options for fetching audit logs
+ * @param {Snowflake|GuildAuditLogsEntry} [options.before] Limit to entries from before specified entry
+ * @param {Snowflake|GuildAuditLogsEntry} [options.after] Limit to entries from after specified entry
+ * @param {number} [options.limit] Limit number of entries
+ * @param {UserResolvable} [options.user] Only show entries involving this user
+ * @param {AuditLogAction} [options.type] Only show entries involving this action type
+ * @returns {Promise<GuildAuditLogs>}
+ * @example
+ * // Output audit log entries
+ * guild.fetchAuditLogs()
+ * .then(audit => console.log(audit.entries.first()))
+ * .catch(console.error);
+ */
+ fetchAuditLogs(options) {
+ return this.client.rest.methods.getGuildAuditLogs(this, options);
+ }
+
+ /**
+ * Adds a user to the guild using OAuth2. Requires the `CREATE_INSTANT_INVITE` permission.
+ * @param {UserResolvable} user User to add to the guild
+ * @param {Object} options Options for the addition
+ * @param {string} options.accessToken An OAuth2 access token for the user with the `guilds.join` scope granted to the
+ * bot's application
+ * @param {string} [options.nick] Nickname to give the member (requires `MANAGE_NICKNAMES`)
+ * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} [options.roles] Roles to add to the member
+ * (requires `MANAGE_ROLES`)
+ * @param {boolean} [options.mute] Whether the member should be muted (requires `MUTE_MEMBERS`)
+ * @param {boolean} [options.deaf] Whether the member should be deafened (requires `DEAFEN_MEMBERS`)
+ * @returns {Promise<GuildMember>}
+ */
+ addMember(user, options) {
+ user = this.client.resolver.resolveUserID(user);
+ if (this.members.has(user)) return Promise.resolve(this.members.get(user));
+ return this.client.rest.methods.putGuildMember(this, user, options);
+ }
+
+ /**
+ * Fetch a single guild member from a user.
+ * @param {UserResolvable} user The user to fetch the member for
+ * @param {boolean} [cache=true] Insert the member into the members cache
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Fetch a guild member
+ * guild.fetchMember(message.author)
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchMember(user, cache = true) {
+ const userID = this.client.resolver.resolveUserID(user);
+ if (!userID) return Promise.reject(new Error('Invalid id provided.'));
+ const member = this.members.get(userID);
+ if (member && member.joinedTimestamp) return Promise.resolve(member);
+ return this.client.rest.methods.getGuildMember(this, userID, cache);
+ }
+
+ /**
+ * Fetches all the members in the guild, even if they are offline. If the guild has less than 250 members,
+ * this should not be necessary.
+ * @param {string} [query=''] Limit fetch to members with similar usernames
+ * @param {number} [limit=0] Maximum number of members to request
+ * @returns {Promise<Guild>}
+ * @example
+ * // Fetch guild members
+ * guild.fetchMembers()
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Fetches a maximum of 1 member with the given query
+ * guild.fetchMembers('hydrabolt', 1)
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchMembers(query = '', limit = 0) {
+ return new Promise((resolve, reject) => {
+ if (this.memberCount === this.members.size) {
+ resolve(this);
+ return;
+ }
+ this.client.ws.send({
+ op: Constants.OPCodes.REQUEST_GUILD_MEMBERS,
+ d: {
+ guild_id: this.id,
+ query,
+ limit,
+ },
+ });
+ const handler = (members, guild) => {
+ if (guild.id !== this.id) return;
+ if (this.memberCount === this.members.size || members.length < 1000) {
+ this.client.removeListener(Constants.Events.GUILD_MEMBERS_CHUNK, handler);
+ resolve(this);
+ }
+ };
+ this.client.on(Constants.Events.GUILD_MEMBERS_CHUNK, handler);
+ this.client.setTimeout(() => reject(new Error('Members didn\'t arrive in time.')), 120 * 1000);
+ });
+ }
+
+ /**
+ * Performs a search within the entire guild.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {MessageSearchOptions} [options={}] Options to pass to the search
+ * @returns {Promise<MessageSearchResult>}
+ * @deprecated
+ * @example
+ * guild.search({
+ * content: 'discord.js',
+ * before: '2016-11-17'
+ * })
+ * .then(res => {
+ * const hit = res.messages[0].find(m => m.hit).content;
+ * console.log(`I found: **${hit}**, total results: ${res.totalResults}`);
+ * })
+ * .catch(console.error);
+ */
+ search(options = {}) {
+ return this.client.rest.methods.search(this, options);
+ }
+
+ /**
+ * The data for editing a guild.
+ * @typedef {Object} GuildEditData
+ * @property {string} [name] The name of the guild
+ * @property {string} [region] The region of the guild
+ * @property {number} [verificationLevel] The verification level of the guild
+ * @property {number} [explicitContentFilter] The level of the explicit content filter
+ * @property {ChannelResolvable} [afkChannel] The AFK channel of the guild
+ * @property {ChannelResolvable} [systemChannel] The system channel of the guild
+ * @property {number} [afkTimeout] The AFK timeout of the guild
+ * @property {Base64Resolvable} [icon] The icon of the guild
+ * @property {Base64Resolvable} [banner] The banner of the guild
+ * @property {GuildMemberResolvable} [owner] The owner of the guild
+ * @property {Base64Resolvable} [splash] The splash screen of the guild
+ * @property {SystemChannelFlagsResolvable} [systemChannelFlags] The system channel flags of the guild
+ */
+
+ /**
+ * Updates the guild with new information - e.g. a new name.
+ * @param {GuildEditData} data The data to update the guild with
+ * @param {string} [reason] Reason for editing the guild
+ * @returns {Promise<Guild>}
+ * @example
+ * // Set the guild name and region
+ * guild.edit({
+ * name: 'Discord Guild',
+ * region: 'london',
+ * })
+ * .then(g => console.log(`Changed guild name to ${g} and region to ${g.region}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ const _data = {};
+ if (data.name) _data.name = data.name;
+ if (data.region) _data.region = data.region;
+ if (typeof data.verificationLevel !== 'undefined') _data.verification_level = Number(data.verificationLevel);
+ if (typeof data.afkChannel !== 'undefined') {
+ _data.afk_channel_id = this.client.resolver.resolveChannelID(data.afkChannel);
+ }
+ if (typeof data.systemChannel !== 'undefined') {
+ _data.system_channel_id = this.client.resolver.resolveChannelID(data.systemChannel);
+ }
+ if (data.afkTimeout) _data.afk_timeout = Number(data.afkTimeout);
+ if (typeof data.icon !== 'undefined') _data.icon = data.icon;
+ if (data.owner) _data.owner_id = this.client.resolver.resolveUser(data.owner).id;
+ if (typeof data.splash !== 'undefined') _data.splash = data.splash;
+ if (typeof data.banner !== 'undefined') _data.banner = data.banner;
+ if (typeof data.explicitContentFilter !== 'undefined') {
+ _data.explicit_content_filter = Number(data.explicitContentFilter);
+ }
+ if (typeof data.defaultMessageNotifications !== 'undefined') {
+ _data.default_message_notifications = typeof data.defaultMessageNotifications === 'string' ?
+ Constants.DefaultMessageNotifications.indexOf(data.defaultMessageNotifications) :
+ Number(data.defaultMessageNotifications);
+ }
+ if (typeof data.systemChannelFlags !== 'undefined') {
+ _data.system_channel_flags = SystemChannelFlags.resolve(data.systemChannelFlags);
+ }
+ return this.client.rest.methods.updateGuild(this, _data, reason);
+ }
+
+ /**
+ * Sets a new guild banner.
+ * @param {BufferResolvable|Base64Resolvable} banner The new banner of the guild
+ * @param {string} [reason] Reason for changing the guild's banner
+ * @returns {Guild}
+ */
+ setBanner(banner, reason) {
+ return this.client.resolver.resolveImage(banner).then(data => this.edit({ banner: data }, reason));
+ }
+
+ /**
+ * Edit the level of the explicit content filter.
+ * @param {number} explicitContentFilter The new level of the explicit content filter
+ * @param {string} [reason] Reason for changing the level of the guild's explicit content filter
+ * @returns {Promise<Guild>}
+ */
+ setExplicitContentFilter(explicitContentFilter, reason) {
+ return this.edit({ explicitContentFilter }, reason);
+ }
+
+ /**
+ * Edits the setting of the default message notifications of the guild.
+ * @param {DefaultMessageNotifications|number} defaultMessageNotifications
+ * The new setting for the default message notifications
+ * @param {string} [reason] Reason for changing the setting of the default message notifications
+ * @returns {Promise<Guild>}
+ */
+ setDefaultMessageNotifications(defaultMessageNotifications, reason) {
+ return this.edit({ defaultMessageNotifications }, reason);
+ }
+
+ /**
+ * Edits the flags of the default message notifications of the guild.
+ * @param {SystemChannelFlagsResolvable} systemChannelFlags The new flags for the default message notifications
+ * @param {string} [reason] Reason for changing the flags of the default message notifications
+ * @returns {Promise<Guild>}
+ */
+ setSystemChannelFlags(systemChannelFlags, reason) {
+ return this.edit({ systemChannelFlags }, reason);
+ }
+
+ /**
+ * Edit the name of the guild.
+ * @param {string} name The new name of the guild
+ * @param {string} [reason] Reason for changing the guild's name
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild name
+ * guild.setName('Discord Guild')
+ * .then(g => console.log(`Updated guild name to ${g}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Edit the region of the guild.
+ * @param {string} region The new region of the guild
+ * @param {string} [reason] Reason for changing the guild's region
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild region
+ * guild.setRegion('london')
+ * .then(g => console.log(`Updated guild region to ${g.region}`))
+ * .catch(console.error);
+ */
+ setRegion(region, reason) {
+ return this.edit({ region }, reason);
+ }
+
+ /**
+ * Edit the verification level of the guild.
+ * @param {number} verificationLevel The new verification level of the guild
+ * @param {string} [reason] Reason for changing the guild's verification level
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild verification level
+ * guild.setVerificationLevel(1)
+ * .then(g => console.log(`Updated guild verification level to ${g.verificationLevel}`))
+ * .catch(console.error);
+ */
+ setVerificationLevel(verificationLevel, reason) {
+ return this.edit({ verificationLevel }, reason);
+ }
+
+ /**
+ * Edit the AFK channel of the guild.
+ * @param {ChannelResolvable} afkChannel The new AFK channel
+ * @param {string} [reason] Reason for changing the guild's AFK channel
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild AFK channel
+ * guild.setAFKChannel(channel)
+ * .then(g => console.log(`Updated guild AFK channel to ${g.afkChannel.name}`))
+ * .catch(console.error);
+ */
+ setAFKChannel(afkChannel, reason) {
+ return this.edit({ afkChannel }, reason);
+ }
+
+ /**
+ * Edit the system channel of the guild.
+ * @param {ChannelResolvable} systemChannel The new system channel
+ * @param {string} [reason] Reason for changing the guild's system channel
+ * @returns {Promise<Guild>}
+ */
+ setSystemChannel(systemChannel, reason) {
+ return this.edit({ systemChannel }, reason);
+ }
+
+ /**
+ * Edit the AFK timeout of the guild.
+ * @param {number} afkTimeout The time in seconds that a user must be idle to be considered AFK
+ * @param {string} [reason] Reason for changing the guild's AFK timeout
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild AFK channel
+ * guild.setAFKTimeout(60)
+ * .then(g => console.log(`Updated guild AFK timeout to ${g.afkTimeout}`))
+ * .catch(console.error);
+ */
+ setAFKTimeout(afkTimeout, reason) {
+ return this.edit({ afkTimeout }, reason);
+ }
+
+ /**
+ * Set a new guild icon.
+ * @param {Base64Resolvable|BufferResolvable} icon The new icon of the guild
+ * @param {string} [reason] Reason for changing the guild's icon
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild icon
+ * guild.setIcon('./icon.png')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setIcon(icon, reason) {
+ return this.client.resolver.resolveImage(icon).then(data => this.edit({ icon: data, reason }));
+ }
+
+ /**
+ * Sets a new owner of the guild.
+ * @param {GuildMemberResolvable} owner The new owner of the guild
+ * @param {string} [reason] Reason for setting the new owner
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild owner
+ * guild.setOwner(guild.members.first())
+ * .then(g => console.log(`Updated the guild owner to ${g.owner.displayName}`))
+ * .catch(console.error);
+ */
+ setOwner(owner, reason) {
+ return this.edit({ owner }, reason);
+ }
+
+ /**
+ * Set a new guild splash screen.
+ * @param {BufferResolvable|Base64Resolvable} splash The new splash screen of the guild
+ * @param {string} [reason] Reason for changing the guild's splash screen
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild splash
+ * guild.setSplash('./splash.png')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setSplash(splash) {
+ return this.client.resolver.resolveImage(splash).then(data => this.edit({ splash: data }));
+ }
+
+ /**
+ * Sets the position of the guild in the guild listing.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {number} position Absolute or relative position
+ * @param {boolean} [relative=false] Whether to position relatively or absolutely
+ * @returns {Promise<Guild>}
+ * @deprecated
+ */
+ setPosition(position, relative) {
+ if (this.client.user.bot) {
+ return Promise.reject(new Error('Setting guild position is only available for user accounts'));
+ }
+ return this.client.user.settings.setGuildPosition(this, position, relative);
+ }
+
+ /**
+ * Marks all messages in this guild as read.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<Guild>}
+ * @deprecated
+ */
+ acknowledge() {
+ return this.client.rest.methods.ackGuild(this);
+ }
+
+ /**
+ * Allow direct messages from guild members.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {boolean} allow Whether to allow direct messages
+ * @returns {Promise<Guild>}
+ * @deprecated
+ */
+ allowDMs(allow) {
+ const settings = this.client.user.settings;
+ if (allow) return settings.removeRestrictedGuild(this);
+ else return settings.addRestrictedGuild(this);
+ }
+
+ /**
+ * Bans a user from the guild.
+ * @param {UserResolvable} user The user to ban
+ * @param {Object|number|string} [options] Ban options. If a number, the number of days to delete messages for, if a
+ * string, the ban reason. Supplying an object allows you to do both.
+ * @param {number} [options.days=0] Number of days of messages to delete
+ * @param {string} [options.reason] Reason for banning
+ * @returns {Promise<GuildMember|User|string>} Result object will be resolved as specifically as possible.
+ * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot
+ * be resolved, the user ID will be the result.
+ * @example
+ * // Ban a user by ID
+ * guild.ban('some user ID')
+ * .then(user => console.log(`Banned ${user.username || user.id || user} from ${guild}`))
+ * .catch(console.error);
+ * @example
+ * // Ban a user by object with reason and days
+ * guild.ban(user, { days: 7, reason: 'He needed to go' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ ban(user, options = {}) {
+ if (typeof options === 'number') {
+ options = { reason: null, 'delete-message-days': options };
+ } else if (typeof options === 'string') {
+ options = { reason: options, 'delete-message-days': 0 };
+ }
+ if (options.days) options['delete-message-days'] = options.days;
+ return this.client.rest.methods.banGuildMember(this, user, options);
+ }
+
+ /**
+ * Unbans a user from the guild.
+ * @param {UserResolvable} user The user to unban
+ * @param {string} [reason] Reason for unbanning the user
+ * @returns {Promise<User>}
+ * @example
+ * // Unban a user by ID (or with a user/guild member object)
+ * guild.unban('some user ID')
+ * .then(user => console.log(`Unbanned ${user.username} from ${guild}`))
+ * .catch(console.error);
+ */
+ unban(user, reason) {
+ return this.client.rest.methods.unbanGuildMember(this, user, reason);
+ }
+
+ /**
+ * Prunes members from the guild based on how long they have been inactive.
+ * @param {number} days Number of days of inactivity required to kick
+ * @param {boolean} [dry=false] If true, will return number of users that will be kicked, without actually doing it
+ * @param {string} [reason] Reason for this prune
+ * @returns {Promise<number>} The number of members that were/will be kicked
+ * @example
+ * // See how many members will be pruned
+ * guild.pruneMembers(12, true)
+ * .then(pruned => console.log(`This will prune ${pruned} people!`))
+ * .catch(console.error);
+ * @example
+ * // Actually prune the members
+ * guild.pruneMembers(12)
+ * .then(pruned => console.log(`I just pruned ${pruned} people!`))
+ * .catch(console.error);
+ */
+ pruneMembers(days, dry = false, reason) {
+ if (typeof days !== 'number') throw new TypeError('Days must be a number.');
+ return this.client.rest.methods.pruneGuildMembers(this, days, dry, reason);
+ }
+
+ /**
+ * Syncs this guild (already done automatically every 30 seconds).
+ * <warn>This is only available when using a user account.</warn>
+ * @deprecated
+ */
+ sync() {
+ if (!this.client.user.bot) this.client.syncGuilds([this]);
+ }
+
+ /**
+ * Overwrites to use when creating a channel or replacing overwrites
+ * @typedef {Object} ChannelCreationOverwrites
+ * @property {PermissionResolvable} [allow] The permissions to allow
+ * @property {PermissionResolvable} [allowed] The permissions to allow
+ * **(deprecated)**
+ * @property {PermissionResolvable} [deny] The permissions to deny
+ * @property {PermissionResolvable} [denied] The permissions to deny
+ * **(deprecated)**
+ * @property {GuildMemberResolvable|RoleResolvable} id Member or role this overwrite is for
+ */
+
+ /**
+ * Creates a new channel in the guild.
+ * @param {string} name The name of the new channel
+ * @param {string|ChannelData} [typeOrOptions='text']
+ * The type of the new channel, one of `text`, `voice`, `category`, `news`, or `store`. **(deprecated, use options)**
+ * Alternatively options for the new channel, overriding the following parameters.
+ * @param {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [permissionOverwrites]
+ * Permission overwrites **(deprecated, use options)**
+ * @param {string} [reason] Reason for creating this channel **(deprecated, use options)**
+ * @returns {Promise<CategoryChannel|TextChannel|VoiceChannel>}
+ * @example
+ * // Create a new text channel
+ * guild.createChannel('new-general', { type: 'text' })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Create a new category channel with permission overwrites
+ * guild.createChannel('new-category', {
+ * type: 'category',
+ * permissionOverwrites: [{
+ * id: guild.id,
+ * deny: ['MANAGE_MESSAGES'],
+ * allow: ['SEND_MESSAGES']
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ createChannel(name, typeOrOptions, permissionOverwrites, reason) {
+ if (!typeOrOptions || (typeof typeOrOptions === 'string')) {
+ if (typeOrOptions) {
+ process.emitWarning(
+ 'Guild#createChannel: Create channels with an options object instead of separate parameters',
+ 'DeprecationWarning'
+ );
+ }
+ typeOrOptions = {
+ type: typeOrOptions,
+ permissionOverwrites,
+ reason,
+ };
+ }
+ return this.client.rest.methods.createChannel(this, name, typeOrOptions);
+ }
+
+ /**
+ * The data needed for updating a channel's position.
+ * @typedef {Object} ChannelPosition
+ * @property {ChannelResolvable} channel Channel to update
+ * @property {number} position New position for the channel
+ */
+
+ /**
+ * Batch-updates the guild's channels' positions.
+ * @param {ChannelPosition[]} channelPositions Channel positions to update
+ * @returns {Promise<Guild>}
+ * @example
+ * guild.updateChannels([{ channel: channelID, position: newChannelIndex }])
+ * .then(g => console.log(`Updated channel positions for ${g}`))
+ * .catch(console.error);
+ */
+ setChannelPositions(channelPositions) {
+ channelPositions = channelPositions.map(({ channel, position }) => ({ id: channel.id || channel, position }));
+ return this.client.rest.methods.setChannelPositions(this.id, channelPositions);
+ }
+
+ /**
+ * The data needed for updating a role's position.
+ * @typedef {Object} RolePosition
+ * @property {RoleResolvable} role Role to update
+ * @property {number} position New position for the role
+ */
+
+ /**
+ * Batch-updates the guild's role's positions.
+ * @param {RolePosition[]} rolePositions Role positions to update
+ * @returns {Promise<Guild>}
+ */
+ setRolePositions(rolePositions) {
+ rolePositions = rolePositions.map(({ role, position }) => ({ id: role.id || role, position }));
+ return this.client.rest.methods.setRolePositions(this.id, rolePositions);
+ }
+
+ /**
+ * Edits the guild's embed.
+ * @param {GuildEmbedData} embed The embed for the guild
+ * @param {string} [reason] Reason for changing the guild's embed
+ * @returns {Promise<Guild>}
+ */
+ setEmbed(embed, reason) {
+ return this.client.rest.methods.updateEmbed(this.id, embed, reason)
+ .then(() => this);
+ }
+
+ /**
+ * Creates a new role in the guild with given information.
+ * @param {RoleData} [data] The data to update the role with
+ * @param {string} [reason] Reason for creating this role
+ * @returns {Promise<Role>}
+ * @example
+ * // Create a new role
+ * guild.createRole()
+ * .then(role => console.log(`Created new role with name ${role.name}`))
+ * .catch(console.error);
+ * @example
+ * // Create a new role with data
+ * guild.createRole({
+ * name: 'Super Cool People',
+ * color: 'BLUE',
+ * })
+ * .then(role => console.log(`Created new role with name ${role.name} and color ${role.color}`))
+ * .catch(console.error)
+ */
+ createRole(data = {}, reason) {
+ return this.client.rest.methods.createGuildRole(this, data, reason);
+ }
+
+ /**
+ * Creates a new custom emoji in the guild.
+ * @param {BufferResolvable|Base64Resolvable} attachment The image for the emoji
+ * @param {string} name The name for the emoji
+ * @param {Collection<Snowflake, Role>|Role[]} [roles] Roles to limit the emoji to
+ * @param {string} [reason] Reason for creating the emoji
+ * @returns {Promise<Emoji>} The created emoji
+ * @example
+ * // Create a new emoji from a url
+ * guild.createEmoji('https://i.imgur.com/w3duR07.png', 'rip')
+ * .then(emoji => console.log(`Created new emoji with name ${emoji.name}`))
+ * .catch(console.error);
+ * @example
+ * // Create a new emoji from a file on your computer
+ * guild.createEmoji('./memes/banana.png', 'banana')
+ * .then(emoji => console.log(`Created new emoji with name ${emoji.name}`))
+ * .catch(console.error);
+ */
+ createEmoji(attachment, name, roles, reason) {
+ if (typeof attachment === 'string' && attachment.startsWith('data:')) {
+ return this.client.rest.methods.createEmoji(this, attachment, name, roles, reason);
+ } else {
+ return this.client.resolver.resolveImage(attachment).then(data =>
+ this.client.rest.methods.createEmoji(this, data, name, roles, reason)
+ );
+ }
+ }
+
+ /**
+ * Delete an emoji.
+ * @param {Emoji|string} emoji The emoji to delete
+ * @param {string} [reason] Reason for deleting the emoji
+ * @returns {Promise}
+ * @deprecated
+ */
+ deleteEmoji(emoji, reason) {
+ if (typeof emoji === 'string') emoji = this.emojis.get(emoji);
+ if (!(emoji instanceof Emoji)) throw new TypeError('Emoji must be either an instance of Emoji or an ID');
+ return emoji.delete(reason);
+ }
+
+ /**
+ * Causes the client to leave the guild.
+ * @returns {Promise<Guild>}
+ * @example
+ * // Leave a guild
+ * guild.leave()
+ * .then(g => console.log(`Left the guild ${g}`))
+ * .catch(console.error);
+ */
+ leave() {
+ return this.client.rest.methods.leaveGuild(this);
+ }
+
+ /**
+ * Causes the client to delete the guild.
+ * @returns {Promise<Guild>}
+ * @example
+ * // Delete a guild
+ * guild.delete()
+ * .then(g => console.log(`Deleted the guild ${g}`))
+ * .catch(console.error);
+ */
+ delete() {
+ return this.client.rest.methods.deleteGuild(this);
+ }
+
+ /**
+ * Whether this guild equals another guild. It compares all properties, so for most operations
+ * it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often
+ * what most users need.
+ * @param {Guild} guild The guild to compare with
+ * @returns {boolean}
+ */
+ equals(guild) {
+ let equal =
+ guild &&
+ this.id === guild.id &&
+ this.available === !guild.unavailable &&
+ this.splash === guild.splash &&
+ this.region === guild.region &&
+ this.name === guild.name &&
+ this.memberCount === guild.member_count &&
+ this.large === guild.large &&
+ this.icon === guild.icon &&
+ Util.arraysEqual(this.features, guild.features) &&
+ this.ownerID === guild.owner_id &&
+ this.verificationLevel === guild.verification_level &&
+ this.embedEnabled === guild.embed_enabled;
+
+ if (equal) {
+ if (this.embedChannel) {
+ if (this.embedChannel.id !== guild.embed_channel_id) equal = false;
+ } else if (guild.embed_channel_id) {
+ equal = false;
+ }
+ }
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the guild's name instead of the guild object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from My Guild!
+ * console.log(`Hello from ${guild}!`);
+ * @example
+ * // Logs: Hello from My Guild!
+ * console.log('Hello from ' + guild + '!');
+ */
+ toString() {
+ return this.name;
+ }
+
+ _addMember(guildUser, emitEvent = true) {
+ const existing = this.members.has(guildUser.user.id);
+ if (!(guildUser.user instanceof User)) guildUser.user = this.client.dataManager.newUser(guildUser.user);
+
+ guildUser.joined_at = guildUser.joined_at || 0;
+ const member = new GuildMember(this, guildUser);
+ this.members.set(member.id, member);
+
+ if (this._rawVoiceStates && this._rawVoiceStates.has(member.user.id)) {
+ const voiceState = this._rawVoiceStates.get(member.user.id);
+ member.serverMute = voiceState.mute;
+ member.serverDeaf = voiceState.deaf;
+ member.selfMute = voiceState.self_mute;
+ member.selfDeaf = voiceState.self_deaf;
+ member.selfStream = voiceState.self_stream || false;
+ member.voiceSessionID = voiceState.session_id;
+ member.voiceChannelID = voiceState.channel_id;
+ if (this.client.channels.has(voiceState.channel_id)) {
+ this.client.channels.get(voiceState.channel_id).members.set(member.user.id, member);
+ } else {
+ this.client.emit('warn', `Member ${member.id} added in guild ${this.id} with an uncached voice channel`);
+ }
+ }
+
+ /**
+ * Emitted whenever a user joins a guild.
+ * @event Client#guildMemberAdd
+ * @param {GuildMember} member The member that has joined a guild
+ */
+ if (this.client.ws.connection.status === Constants.Status.READY && emitEvent && !existing) {
+ this.client.emit(Constants.Events.GUILD_MEMBER_ADD, member);
+ }
+
+ return member;
+ }
+
+ _updateMember(member, data) {
+ const oldMember = Util.cloneObject(member);
+
+ if (data.premium_since) member.premiumSinceTimestamp = new Date(data.premium_since).getTime();
+ if (data.roles) member._roles = data.roles;
+ if (typeof data.nick !== 'undefined') member.nickname = data.nick;
+
+ const notSame = member.nickname !== oldMember.nickname ||
+ member.premiumSinceTimestamp !== oldMember.premiumSinceTimestamp ||
+ !Util.arraysEqual(member._roles, oldMember._roles);
+
+ if (this.client.ws.connection.status === Constants.Status.READY && notSame) {
+ /**
+ * Emitted whenever a guild member changes - i.e. new role, removed role, nickname.
+ * @event Client#guildMemberUpdate
+ * @param {GuildMember} oldMember The member before the update
+ * @param {GuildMember} newMember The member after the update
+ */
+ this.client.emit(Constants.Events.GUILD_MEMBER_UPDATE, oldMember, member);
+ }
+
+ return {
+ old: oldMember,
+ mem: member,
+ };
+ }
+
+ _removeMember(guildMember) {
+ if (guildMember.voiceChannel) guildMember.voiceChannel.members.delete(guildMember.id);
+ this.members.delete(guildMember.id);
+ }
+
+ _memberSpeakUpdate(user, speaking) {
+ const member = this.members.get(user);
+ if (member && member.speaking !== speaking) {
+ member.speaking = speaking;
+ /**
+ * Emitted once a guild member starts/stops speaking.
+ * @event Client#guildMemberSpeaking
+ * @param {GuildMember} member The member that started/stopped speaking
+ * @param {boolean} speaking Whether or not the member is speaking
+ */
+ this.client.emit(Constants.Events.GUILD_MEMBER_SPEAKING, member, speaking);
+ }
+ }
+
+ _setPresence(id, presence) {
+ if (this.presences.get(id)) {
+ this.presences.get(id).update(presence);
+ return;
+ }
+ this.presences.set(id, new Presence(presence, this.client));
+ }
+
+ /**
+ * Set the position of a role in this guild.
+ * @param {string|Role} role The role to edit, can be a role object or a role ID
+ * @param {number} position The new position of the role
+ * @param {boolean} [relative=false] Position Moves the role relative to its current position
+ * @returns {Promise<Guild>}
+ */
+ setRolePosition(role, position, relative = false) {
+ if (typeof role === 'string') {
+ role = this.roles.get(role);
+ if (!role) return Promise.reject(new Error('Supplied role is not a role or snowflake.'));
+ }
+
+ position = Number(position);
+ if (isNaN(position)) return Promise.reject(new Error('Supplied position is not a number.'));
+
+ let updatedRoles = this._sortedRoles.array();
+
+ Util.moveElementInArray(updatedRoles, role, position, relative);
+
+ updatedRoles = updatedRoles.map((r, i) => ({ id: r.id, position: i }));
+ return this.client.rest.methods.setRolePositions(this.id, updatedRoles);
+ }
+
+ /**
+ * Set the position of a channel in this guild.
+ * @param {string|GuildChannel} channel The channel to edit, can be a channel object or a channel ID
+ * @param {number} position The new position of the channel
+ * @param {boolean} [relative=false] Position Moves the channel relative to its current position
+ * @returns {Promise<Guild>}
+ */
+ setChannelPosition(channel, position, relative = false) {
+ if (typeof channel === 'string') {
+ channel = this.channels.get(channel);
+ if (!channel) return Promise.reject(new Error('Supplied channel is not a channel or snowflake.'));
+ }
+
+ position = Number(position);
+ if (isNaN(position)) return Promise.reject(new Error('Supplied position is not a number.'));
+
+ let updatedChannels = this._sortedChannels(channel.type).array();
+
+ Util.moveElementInArray(updatedChannels, channel, position, relative);
+
+ updatedChannels = updatedChannels.map((c, i) => ({ id: c.id, position: i }));
+ return this.client.rest.methods.setChannelPositions(this.id, updatedChannels);
+ }
+
+ /**
+ * Fetches a collection of channels in the current guild sorted by position.
+ * @param {string} type The channel type
+ * @returns {Collection<Snowflake, GuildChannel>}
+ * @private
+ */
+ _sortedChannels(type) {
+ return this._sortPositionWithID(this.channels.filter(c => {
+ if (type === 'voice' && c.type === 'voice') return true;
+ else if (type !== 'voice' && c.type !== 'voice') return true;
+ else return type === c.type;
+ }));
+ }
+
+ /**
+ * Sorts a collection by object position or ID if the positions are equivalent.
+ * Intended to be identical to Discord's sorting method.
+ * @param {Collection} collection The collection to sort
+ * @returns {Collection}
+ * @private
+ */
+ _sortPositionWithID(collection) {
+ return collection.sort((a, b) =>
+ a.position !== b.position ?
+ a.position - b.position :
+ Long.fromString(b.id).sub(Long.fromString(a.id)).toNumber()
+ );
+ }
+}
+
+/**
+ * The `#general` TextChannel of the guild
+ * @name Guild#defaultChannel
+ * @type {TextChannel}
+ * @readonly
+ * @deprecated
+ */
+Object.defineProperty(Guild.prototype, 'defaultChannel', {
+ get: util.deprecate(function defaultChannel() {
+ return this.channels.get(this.id);
+ }, 'Guild#defaultChannel: This property is obsolete, will be removed in v12.0.0, and may not function as expected.'),
+});
+
+Guild.prototype.allowDMs =
+ util.deprecate(Guild.prototype.allowDMs, 'Guild#allowDMs: userbot methods will be removed');
+
+Guild.prototype.acknowledge =
+ util.deprecate(Guild.prototype.acknowledge, 'Guild#acknowledge: userbot methods will be removed');
+
+Guild.prototype.setPosition =
+ util.deprecate(Guild.prototype.setPosition, 'Guild#setPosition: userbot methods will be removed');
+
+Guild.prototype.search =
+ util.deprecate(Guild.prototype.search, 'Guild#search: userbot methods will be removed');
+
+Guild.prototype.sync =
+ util.deprecate(Guild.prototype.sync, 'Guild#sync:, userbot methods will be removed');
+
+Guild.prototype.deleteEmoji =
+ util.deprecate(Guild.prototype.deleteEmoji, 'Guild#deleteEmoji: use Emoji#delete instead');
+
+module.exports = Guild;
diff --git a/node_modules/discord.js/src/structures/GuildAuditLogs.js b/node_modules/discord.js/src/structures/GuildAuditLogs.js
new file mode 100644
index 0000000..5c98594
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildAuditLogs.js
@@ -0,0 +1,462 @@
+const Collection = require('../util/Collection');
+const Snowflake = require('../util/Snowflake');
+const Webhook = require('./Webhook');
+const Integration = require('./Integration');
+const Invite = require('./Invite');
+
+/**
+ * The target type of an entry, e.g. `GUILD`. Here are the available types:
+ * * GUILD
+ * * CHANNEL
+ * * USER
+ * * ROLE
+ * * INVITE
+ * * WEBHOOK
+ * * EMOJI
+ * * MESSAGE
+ * * INTEGRATION
+ * @typedef {string} AuditLogTargetType
+ */
+
+/**
+ * Key mirror of all available audit log targets.
+ * @name GuildAuditLogs.Targets
+ * @type {AuditLogTargetType}
+ */
+const Targets = {
+ ALL: 'ALL',
+ GUILD: 'GUILD',
+ CHANNEL: 'CHANNEL',
+ USER: 'USER',
+ ROLE: 'ROLE',
+ INVITE: 'INVITE',
+ WEBHOOK: 'WEBHOOK',
+ EMOJI: 'EMOJI',
+ MESSAGE: 'MESSAGE',
+ INTEGRATION: 'INTEGRATION',
+ UNKNOWN: 'UNKNOWN',
+};
+
+/**
+ * The action of an entry. Here are the available actions:
+ * * ALL: null
+ * * GUILD_UPDATE: 1
+ * * CHANNEL_CREATE: 10
+ * * CHANNEL_UPDATE: 11
+ * * CHANNEL_DELETE: 12
+ * * CHANNEL_OVERWRITE_CREATE: 13
+ * * CHANNEL_OVERWRITE_UPDATE: 14
+ * * CHANNEL_OVERWRITE_DELETE: 15
+ * * MEMBER_KICK: 20
+ * * MEMBER_PRUNE: 21
+ * * MEMBER_BAN_ADD: 22
+ * * MEMBER_BAN_REMOVE: 23
+ * * MEMBER_UPDATE: 24
+ * * MEMBER_ROLE_UPDATE: 25
+ * * MEMBER_MOVE: 26
+ * * MEMBER_DISCONNECT: 27
+ * * BOT_ADD: 28,
+ * * ROLE_CREATE: 30
+ * * ROLE_UPDATE: 31
+ * * ROLE_DELETE: 32
+ * * INVITE_CREATE: 40
+ * * INVITE_UPDATE: 41
+ * * INVITE_DELETE: 42
+ * * WEBHOOK_CREATE: 50
+ * * WEBHOOK_UPDATE: 51
+ * * WEBHOOK_DELETE: 52
+ * * EMOJI_CREATE: 60
+ * * EMOJI_UPDATE: 61
+ * * EMOJI_DELETE: 62
+ * * MESSAGE_DELETE: 72
+ * * MESSAGE_BULK_DELETE: 73
+ * * MESSAGE_PIN: 74
+ * * MESSAGE_UNPIN: 75
+ * * INTEGRATION_CREATE: 80
+ * * INTEGRATION_UPDATE: 81
+ * * INTEGRATION_DELETE: 82
+ * @typedef {?number|string} AuditLogAction
+ */
+
+/**
+ * All available actions keyed under their names to their numeric values.
+ * @name GuildAuditLogs.Actions
+ * @type {AuditLogAction}
+ */
+const Actions = {
+ ALL: null,
+ GUILD_UPDATE: 1,
+ CHANNEL_CREATE: 10,
+ CHANNEL_UPDATE: 11,
+ CHANNEL_DELETE: 12,
+ CHANNEL_OVERWRITE_CREATE: 13,
+ CHANNEL_OVERWRITE_UPDATE: 14,
+ CHANNEL_OVERWRITE_DELETE: 15,
+ MEMBER_KICK: 20,
+ MEMBER_PRUNE: 21,
+ MEMBER_BAN_ADD: 22,
+ MEMBER_BAN_REMOVE: 23,
+ MEMBER_UPDATE: 24,
+ MEMBER_ROLE_UPDATE: 25,
+ MEMBER_MOVE: 26,
+ MEMBER_DISCONNECT: 27,
+ BOT_ADD: 28,
+ ROLE_CREATE: 30,
+ ROLE_UPDATE: 31,
+ ROLE_DELETE: 32,
+ INVITE_CREATE: 40,
+ INVITE_UPDATE: 41,
+ INVITE_DELETE: 42,
+ WEBHOOK_CREATE: 50,
+ WEBHOOK_UPDATE: 51,
+ WEBHOOK_DELETE: 52,
+ EMOJI_CREATE: 60,
+ EMOJI_UPDATE: 61,
+ EMOJI_DELETE: 62,
+ MESSAGE_DELETE: 72,
+ MESSAGE_BULK_DELETE: 73,
+ MESSAGE_PIN: 74,
+ MESSAGE_UNPIN: 75,
+ INTEGRATION_CREATE: 80,
+ INTEGRATION_UPDATE: 81,
+ INTEGRATION_DELETE: 82,
+};
+
+
+/**
+ * Audit logs entries are held in this class.
+ */
+class GuildAuditLogs {
+ constructor(guild, data) {
+ if (data.users) for (const user of data.users) guild.client.dataManager.newUser(user);
+
+ /**
+ * Cached webhooks
+ * @type {Collection<Snowflake, Webhook>}
+ * @private
+ */
+ this.webhooks = new Collection();
+ if (data.webhooks) {
+ for (const hook of data.webhooks) {
+ this.webhooks.set(hook.id, new Webhook(guild.client, hook));
+ }
+ }
+
+ /**
+ * Cached integrations
+ * @type {Collection<Snowflake, Integration>}
+ * @private
+ */
+ this.integrations = new Collection();
+ if (data.integrations) {
+ for (const integration of data.integrations) {
+ this.integrations.set(integration.id, new Integration(guild.client, integration, guild));
+ }
+ }
+
+ /**
+ * The entries for this guild's audit logs
+ * @type {Collection<Snowflake, GuildAuditLogsEntry>}
+ */
+ this.entries = new Collection();
+ for (const item of data.audit_log_entries) {
+ const entry = new GuildAuditLogsEntry(this, guild, item);
+ this.entries.set(entry.id, entry);
+ }
+ }
+
+ /**
+ * Handles possible promises for entry targets.
+ * @returns {Promise<GuildAuditLogs>}
+ */
+ static build(...args) {
+ const logs = new GuildAuditLogs(...args);
+ return Promise.all(logs.entries.map(e => e.target)).then(() => logs);
+ }
+
+ /**
+ * The target of an entry. It can be one of:
+ * * A guild
+ * * A user
+ * * A role
+ * * An emoji
+ * * An invite
+ * * A webhook
+ * * An integration
+ * * An object with an id key if target was deleted
+ * * An object where the keys represent either the new value or the old value
+ * @typedef {?Object|Guild|User|Role|Emoji|Invite|Webhook|Integration} AuditLogEntryTarget
+ */
+
+ /**
+ * Find target type from entry action.
+ * @param {number} target The action target
+ * @returns {?string}
+ */
+ static targetType(target) {
+ if (target < 10) return Targets.GUILD;
+ if (target < 20) return Targets.CHANNEL;
+ if (target < 30) return Targets.USER;
+ if (target < 40) return Targets.ROLE;
+ if (target < 50) return Targets.INVITE;
+ if (target < 60) return Targets.WEBHOOK;
+ if (target < 70) return Targets.EMOJI;
+ if (target < 80) return Targets.MESSAGE;
+ if (target < 90) return Targets.INTEGRATION;
+ return null;
+ }
+
+ /**
+ * The action type of an entry, e.g. `CREATE`. Here are the available types:
+ * * CREATE
+ * * DELETE
+ * * UPDATE
+ * * ALL
+ * @typedef {string} AuditLogActionType
+ */
+
+
+ /**
+ * Finds the action type from the entry action.
+ * @param {AuditLogAction} action The action target
+ * @returns {AuditLogActionType}
+ */
+ static actionType(action) {
+ if ([
+ Actions.CHANNEL_CREATE,
+ Actions.CHANNEL_OVERWRITE_CREATE,
+ Actions.MEMBER_BAN_REMOVE,
+ Actions.BOT_ADD,
+ Actions.ROLE_CREATE,
+ Actions.INVITE_CREATE,
+ Actions.WEBHOOK_CREATE,
+ Actions.EMOJI_CREATE,
+ Actions.MESSAGE_PIN,
+ Actions.INTEGRATION_CREATE,
+ ].includes(action)) return 'CREATE';
+
+ if ([
+ Actions.CHANNEL_DELETE,
+ Actions.CHANNEL_OVERWRITE_DELETE,
+ Actions.MEMBER_KICK,
+ Actions.MEMBER_PRUNE,
+ Actions.MEMBER_BAN_ADD,
+ Actions.MEMBER_DISCONNECT,
+ Actions.ROLE_DELETE,
+ Actions.INVITE_DELETE,
+ Actions.WEBHOOK_DELETE,
+ Actions.EMOJI_DELETE,
+ Actions.MESSAGE_DELETE,
+ Actions.MESSAGE_BULK_DELETE,
+ Actions.MESSAGE_UNPIN,
+ Actions.INTEGRATION_DELETE,
+ ].includes(action)) return 'DELETE';
+
+ if ([
+ Actions.GUILD_UPDATE,
+ Actions.CHANNEL_UPDATE,
+ Actions.CHANNEL_OVERWRITE_UPDATE,
+ Actions.MEMBER_UPDATE,
+ Actions.MEMBER_ROLE_UPDATE,
+ Actions.MEMBER_MOVE,
+ Actions.ROLE_UPDATE,
+ Actions.INVITE_UPDATE,
+ Actions.WEBHOOK_UPDATE,
+ Actions.EMOJI_UPDATE,
+ Actions.INTEGRATION_UPDATE,
+ ].includes(action)) return 'UPDATE';
+
+ return 'ALL';
+ }
+}
+
+/**
+ * Audit logs entry.
+ */
+class GuildAuditLogsEntry {
+ // eslint-disable-next-line complexity
+ constructor(logs, guild, data) {
+ const targetType = GuildAuditLogs.targetType(data.action_type);
+ /**
+ * The target type of this entry
+ * @type {AuditLogTargetType}
+ */
+ this.targetType = targetType;
+
+ /**
+ * The action type of this entry
+ * @type {AuditLogActionType}
+ */
+ this.actionType = GuildAuditLogs.actionType(data.action_type);
+
+ /**
+ * Specific action type of this entry in its string representation
+ * @type {AuditLogAction}
+ */
+ this.action = Object.keys(Actions).find(k => Actions[k] === data.action_type);
+
+ /**
+ * The reason of this entry
+ * @type {?string}
+ */
+ this.reason = data.reason || null;
+
+ /**
+ * The user that executed this entry
+ * @type {User}
+ */
+ this.executor = guild.client.users.get(data.user_id);
+
+ /**
+ * An entry in the audit log representing a specific change.
+ * @typedef {object} AuditLogChange
+ * @property {string} key The property that was changed, e.g. `nick` for nickname changes
+ * @property {*} [old] The old value of the change, e.g. for nicknames, the old nickname
+ * @property {*} [new] The new value of the change, e.g. for nicknames, the new nickname
+ */
+
+ /**
+ * Specific property changes
+ * @type {AuditLogChange[]}
+ */
+ this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null;
+
+ /**
+ * The ID of this entry
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * Any extra data from the entry
+ * @type {?Object|Role|GuildMember}
+ */
+ this.extra = null;
+ switch (data.action_type) {
+ case Actions.MEMBER_PRUNE:
+ this.extra = {
+ removed: Number(data.options.members_removed),
+ days: Number(data.options.delete_member_days),
+ };
+ break;
+
+ case Actions.MEMBER_MOVE:
+ case Actions.MESSAGE_DELETE:
+ case Actions.MESSAGE_BULK_DELETE:
+ this.extra = {
+ channel: guild.channels.get(data.options.channel_id) || { id: data.options.channel_id },
+ count: Number(data.options.count),
+ };
+ break;
+
+ case Actions.MESSAGE_PIN:
+ case Actions.MESSAGE_UNPIN:
+ this.extra = {
+ channel: guild.client.channels.get(data.options.channel_id) || { id: data.options.channel_id },
+ messageID: data.options.message_id,
+ };
+ break;
+
+ case Actions.MEMBER_DISCONNECT:
+ this.extra = {
+ count: Number(data.options.count),
+ };
+ break;
+
+ case Actions.CHANNEL_OVERWRITE_CREATE:
+ case Actions.CHANNEL_OVERWRITE_UPDATE:
+ case Actions.CHANNEL_OVERWRITE_DELETE:
+ switch (data.options.type) {
+ case 'member':
+ this.extra = guild.members.get(data.options.id) ||
+ { id: data.options.id, type: 'member' };
+ break;
+ case 'role':
+ this.extra = guild.roles.get(data.options.id) ||
+ { id: data.options.id, name: data.options.role_name, type: 'role' };
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /**
+ * The target of this entry
+ * @type {AuditLogEntryTarget}
+ */
+ this.target = null;
+ if (targetType === Targets.UNKNOWN) {
+ this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {});
+ this.target.id = data.target_id;
+ // MEMBER_DISCONNECT and similar types do not provide a target_id.
+ } else if (targetType === Targets.USER && data.target_id) {
+ this.target = guild.client.users.get(data.target_id);
+ } else if (targetType === Targets.GUILD) {
+ this.target = guild.client.guilds.get(data.target_id);
+ } else if (targetType === Targets.WEBHOOK) {
+ this.target = logs.webhooks.get(data.target_id) ||
+ new Webhook(guild.client,
+ this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {
+ id: data.target_id,
+ guild_id: guild.id,
+ }));
+ } else if (targetType === Targets.INVITE) {
+ const changes = this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {
+ id: data.target_id,
+ guild,
+ });
+ changes.channel = { id: changes.channel_id };
+ this.target = new Invite(guild.client, changes);
+ } else if (targetType === Targets.MESSAGE) {
+ // Discord sends a channel id for the MESSAGE_BULK_DELETE action type.
+ this.target = data.action_type === Actions.MESSAGE_BULK_DELETE ?
+ guild.channels.get(data.target_id) || { id: data.target_id } :
+ guild.client.users.get(data.target_id);
+ } else if (targetType === Targets.INTEGRATION) {
+ this.target = logs.integrations.get(data.target_id) ||
+ new Integration(guild.client, this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, { id: data.target_id }), guild);
+ } else if (data.target_id) {
+ this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id) || { id: data.target_id };
+ }
+ }
+
+ /**
+ * The timestamp this entry was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time this entry was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+}
+
+GuildAuditLogs.Actions = Actions;
+GuildAuditLogs.Targets = Targets;
+GuildAuditLogs.Entry = GuildAuditLogsEntry;
+
+module.exports = GuildAuditLogs;
diff --git a/node_modules/discord.js/src/structures/GuildChannel.js b/node_modules/discord.js/src/structures/GuildChannel.js
new file mode 100644
index 0000000..0dbc0e3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildChannel.js
@@ -0,0 +1,601 @@
+const Channel = require('./Channel');
+const Role = require('./Role');
+const PermissionOverwrites = require('./PermissionOverwrites');
+const Permissions = require('../util/Permissions');
+const Collection = require('../util/Collection');
+const Constants = require('../util/Constants');
+const Invite = require('./Invite');
+const Util = require('../util/Util');
+
+/**
+ * Represents a guild channel (i.e. text channels and voice channels).
+ * @extends {Channel}
+ */
+class GuildChannel extends Channel {
+ constructor(guild, data) {
+ super(guild.client, data);
+
+ /**
+ * The guild the channel is in
+ * @type {Guild}
+ */
+ this.guild = guild;
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The name of the guild channel
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The position of the channel in the list
+ * @type {number}
+ */
+ this.position = data.position;
+
+ /**
+ * The ID of the category parent of this channel
+ * @type {?Snowflake}
+ */
+ this.parentID = data.parent_id;
+
+ /**
+ * A map of permission overwrites in this channel for roles and users
+ * @type {Collection<Snowflake, PermissionOverwrites>}
+ */
+ this.permissionOverwrites = new Collection();
+ if (data.permission_overwrites) {
+ for (const overwrite of data.permission_overwrites) {
+ this.permissionOverwrites.set(overwrite.id, new PermissionOverwrites(this, overwrite));
+ }
+ }
+ }
+
+ /**
+ * The position of the channel
+ * @type {number}
+ * @readonly
+ */
+ get calculatedPosition() {
+ const sorted = this.guild._sortedChannels(this.type);
+ return sorted.array().indexOf(sorted.get(this.id));
+ }
+
+ /**
+ * The category parent of this channel
+ * @type {?CategoryChannel}
+ * @readonly
+ */
+ get parent() {
+ return this.guild.channels.get(this.parentID) || null;
+ }
+
+ /**
+ * If the permissionOverwrites match the parent channel, null if no parent
+ * @type {?boolean}
+ * @readonly
+ */
+ get permissionsLocked() {
+ if (!this.parent) return null;
+ if (this.permissionOverwrites.size !== this.parent.permissionOverwrites.size) return false;
+ return this.permissionOverwrites.every((value, key) => {
+ const testVal = this.parent.permissionOverwrites.get(key);
+ return testVal !== undefined &&
+ testVal.deny === value.deny &&
+ testVal.allow === value.allow;
+ });
+ }
+
+ /**
+ * Gets the overall set of permissions for a user in this channel, taking into account channel overwrites.
+ * @param {GuildMemberResolvable} member The user that you want to obtain the overall permissions for
+ * @returns {?Permissions}
+ */
+ memberPermissions(member) {
+ member = this.client.resolver.resolveGuildMember(this.guild, member);
+ if (!member) return null;
+
+ if (member.id === this.guild.ownerID) return new Permissions(member, Permissions.ALL);
+
+ const roles = member.roles;
+ const permissions = new Permissions(roles.map(role => role.permissions));
+
+ if (permissions.has(Permissions.FLAGS.ADMINISTRATOR)) return new Permissions(Permissions.ALL).freeze();
+
+ const overwrites = this.overwritesFor(member, true, roles);
+
+ return permissions
+ .remove(overwrites.everyone ? overwrites.everyone.deny : 0)
+ .add(overwrites.everyone ? overwrites.everyone.allow : 0)
+ .remove(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.deny) : 0)
+ .add(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.allow) : 0)
+ .remove(overwrites.member ? overwrites.member.deny : 0)
+ .add(overwrites.member ? overwrites.member.allow : 0)
+ .freeze();
+ }
+
+ /**
+ * Gets the overall set of permissions for a role in this channel, taking into account channel overwrites.
+ * @param {RoleResolvable} role The role that you want to obtain the overall permissions for
+ * @returns {?Permissions}
+ */
+ rolePermissions(role) {
+ if (role.permissions & Permissions.FLAGS.ADMINISTRATOR) return new Permissions(Permissions.ALL).freeze();
+
+ const everyoneOverwrites = this.permissionOverwrites.get(this.guild.id);
+ const roleOverwrites = this.permissionOverwrites.get(role.id);
+
+ return new Permissions(role.permissions)
+ .remove(everyoneOverwrites ? everyoneOverwrites.deny : 0)
+ .add(everyoneOverwrites ? everyoneOverwrites.allow : 0)
+ .remove(roleOverwrites ? roleOverwrites.deny : 0)
+ .add(roleOverwrites ? roleOverwrites.allow : 0)
+ .freeze();
+ }
+
+ /**
+ * Get the overall set of permissions for a member or role in this channel, taking into account channel overwrites.
+ * @param {GuildMemberResolvable|RoleResolvable} memberOrRole The member or role to obtain the overall permissions for
+ * @returns {?Permissions}
+ */
+ permissionsFor(memberOrRole) {
+ const member = this.guild.member(memberOrRole);
+ if (member) return this.memberPermissions(member);
+ const role = this.client.resolver.resolveRole(this.guild, memberOrRole);
+ if (role) return this.rolePermissions(role);
+ return null;
+ }
+
+ overwritesFor(member, verified = false, roles = null) {
+ if (!verified) member = this.client.resolver.resolveGuildMember(this.guild, member);
+ if (!member) return [];
+
+ roles = roles || member.roles;
+ const roleOverwrites = [];
+ let memberOverwrites;
+ let everyoneOverwrites;
+
+ for (const overwrite of this.permissionOverwrites.values()) {
+ if (overwrite.id === this.guild.id) {
+ everyoneOverwrites = overwrite;
+ } else if (roles.has(overwrite.id)) {
+ roleOverwrites.push(overwrite);
+ } else if (overwrite.id === member.id) {
+ memberOverwrites = overwrite;
+ }
+ }
+
+ return {
+ everyone: everyoneOverwrites,
+ roles: roleOverwrites,
+ member: memberOverwrites,
+ };
+ }
+
+ /**
+ * Replaces the permission overwrites for a channel
+ * @param {Object} [options] Options
+ * @param {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [options.overwrites]
+ * Permission overwrites
+ * @param {string} [options.reason] Reason for updating the channel overwrites
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * channel.replacePermissionOverwrites({
+ * overwrites: [
+ * {
+ * id: message.author.id,
+ * denied: ['VIEW_CHANNEL'],
+ * },
+ * ],
+ * reason: 'Needed to change permissions'
+ * });
+ */
+ replacePermissionOverwrites({ overwrites, reason } = {}) {
+ return this.edit({ permissionOverwrites: overwrites, reason })
+ .then(() => this);
+ }
+
+ /**
+ * An object mapping permission flags to `true` (enabled), `null` (unset) or `false` (disabled).
+ * ```js
+ * {
+ * 'SEND_MESSAGES': true,
+ * 'EMBED_LINKS': null,
+ * 'ATTACH_FILES': false,
+ * }
+ * ```
+ * @typedef {Object} PermissionOverwriteOptions
+ */
+
+ /**
+ * Overwrites the permissions for a user or role in this channel.
+ * @param {Role|Snowflake|UserResolvable} userOrRole The user or role to update
+ * @param {PermissionOverwriteOptions} options The configuration for the update
+ * @param {string} [reason] Reason for creating/editing this overwrite
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Overwrite permissions for a message author
+ * message.channel.overwritePermissions(message.author, {
+ * SEND_MESSAGES: false
+ * })
+ * .then(updated => console.log(updated.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ * @example
+ * // Overwite permissions for a message author and reset some
+ * message.channel.overwritePermissions(message.author, {
+ * VIEW_CHANNEL: false,
+ * SEND_MESSAGES: null
+ * })
+ * .then(updated => console.log(updated.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ */
+ overwritePermissions(userOrRole, options, reason) {
+ const payload = {
+ allow: 0,
+ deny: 0,
+ };
+
+ if (userOrRole instanceof Role) {
+ payload.type = 'role';
+ } else if (this.guild.roles.has(userOrRole)) {
+ userOrRole = this.guild.roles.get(userOrRole);
+ payload.type = 'role';
+ } else {
+ userOrRole = this.client.resolver.resolveUser(userOrRole);
+ payload.type = 'member';
+ if (!userOrRole) return Promise.reject(new TypeError('Supplied parameter was neither a User nor a Role.'));
+ }
+
+ payload.id = userOrRole.id;
+
+ const prevOverwrite = this.permissionOverwrites.get(userOrRole.id);
+
+ if (prevOverwrite) {
+ payload.allow = prevOverwrite.allow;
+ payload.deny = prevOverwrite.deny;
+ }
+
+ for (const perm of Object.keys(options)) {
+ if (options[perm] === true) {
+ payload.allow |= Permissions.FLAGS[perm] || 0;
+ payload.deny &= ~(Permissions.FLAGS[perm] || 0);
+ } else if (options[perm] === false) {
+ payload.allow &= ~(Permissions.FLAGS[perm] || 0);
+ payload.deny |= Permissions.FLAGS[perm] || 0;
+ } else if (options[perm] === null) {
+ payload.allow &= ~(Permissions.FLAGS[perm] || 0);
+ payload.deny &= ~(Permissions.FLAGS[perm] || 0);
+ }
+ }
+
+ return this.client.rest.methods.setChannelOverwrite(this, payload, reason).then(() => this);
+ }
+
+ /**
+ * Locks in the permission overwrites from the parent channel.
+ * @returns {Promise<GuildChannel>}
+ */
+ lockPermissions() {
+ if (!this.parent) return Promise.reject(new TypeError('Could not find a parent to this guild channel.'));
+ const permissionOverwrites = this.parent.permissionOverwrites.map(overwrite => ({
+ deny: overwrite.deny,
+ allow: overwrite.allow,
+ id: overwrite.id,
+ type: overwrite.type,
+ }));
+ return this.edit({ permissionOverwrites });
+ }
+
+ /**
+ * The data for a guild channel.
+ * @typedef {Object} ChannelData
+ * @property {string} [type] The type of the channel (Only when creating)
+ * @property {string} [name] The name of the channel
+ * @property {number} [position] The position of the channel
+ * @property {string} [topic] The topic of the text channel
+ * @property {boolean} [nsfw] Whether the channel is NSFW
+ * @property {number} [bitrate] The bitrate of the voice channel
+ * @property {number} [userLimit] The user limit of the channel
+ * @property {CategoryChannel|Snowflake} [parent] The parent or parent ID of the channel
+ * @property {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [permissionOverwrites]
+ * Overwrites of the channel
+ * @property {number} [rateLimitPerUser] The rate limit per user of the channel in seconds
+ * @property {string} [reason] Reason for creating the channel (Only when creating)
+ */
+
+ /**
+ * Edits the channel.
+ * @param {ChannelData} data The new data for the channel
+ * @param {string} [reason] Reason for editing this channel
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Edit a channel
+ * channel.edit({ name: 'new-channel' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateChannel(this, data, reason).then(() => this);
+ }
+
+ /**
+ * Set a new name for the guild channel.
+ * @param {string} name The new name for the guild channel
+ * @param {string} [reason] Reason for changing the guild channel's name
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Set a new channel name
+ * channel.setName('not_general')
+ * .then(newChannel => console.log(`Channel's new name is ${newChannel.name}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Set a new position for the guild channel.
+ * @param {number} position The new position for the guild channel
+ * @param {boolean} [relative=false] Move the position relative to its current value
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Set a new channel position
+ * channel.setPosition(2)
+ * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`))
+ * .catch(console.error);
+ */
+ setPosition(position, relative) {
+ return this.guild.setChannelPosition(this, position, relative).then(() => this);
+ }
+
+ /**
+ * Set a new parent for the guild channel.
+ * @param {CategoryChannel|SnowFlake} parent The new parent for the guild channel
+ * @param {string} [reason] Reason for changing the guild channel's parent
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Sets the parent of a channel
+ * channel.setParent('174674066072928256')
+ * .then(updated => console.log(`Set the category of ${updated.name} to ${updated.parent.name}`))
+ * .catch(console.error);
+ */
+ setParent(parent, reason) {
+ parent = this.client.resolver.resolveChannelID(parent);
+ return this.edit({ parent }, reason);
+ }
+
+ /**
+ * Set a new topic for the guild channel.
+ * @param {string} topic The new topic for the guild channel
+ * @param {string} [reason] Reason for changing the guild channel's topic
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Set a new channel topic
+ * channel.setTopic('Needs more rate limiting')
+ * .then(updated => console.log(`Channel's new topic is ${updated.topic}`))
+ * .catch(console.error);
+ */
+ setTopic(topic, reason) {
+ return this.edit({ topic }, reason);
+ }
+
+ /**
+ * Create an invite to this guild channel.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {Object} [options={}] Options for the invite
+ * @param {boolean} [options.temporary=false] Whether members that joined via the invite should be automatically
+ * kicked after 24 hours if they have not yet received a role
+ * @param {number} [options.maxAge=86400] How long the invite should last (in seconds, 0 for forever)
+ * @param {number} [options.maxUses=0] Maximum number of uses
+ * @param {boolean} [options.unique=false] Create a unique invite, or use an existing one with similar settings
+ * @param {string} [reason] Reason for creating the invite
+ * @returns {Promise<Invite>}
+ * @example
+ * // Create an invite to a channel
+ * channel.createInvite()
+ * .then(invite => console.log(`Created an invite with a code of ${invite.code}`))
+ * .catch(console.error);
+ */
+ createInvite(options = {}, reason) {
+ return this.client.rest.methods.createChannelInvite(this, options, reason);
+ }
+
+ /* eslint-disable max-len */
+ /**
+ * Options to clone a guild channel.
+ * @typedef {Object} GuildChannelCloneOptions
+ * @property {string} [name=this.name] Name of the new channel
+ * @property {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [permissionOverwrites=this.permissionOverwrites]
+ * Permission overwrites of the new channel
+ * @property {string} [type=this.type] Type of the new channel
+ * @property {string} [topic=this.topic] Topic of the new channel (only text)
+ * @property {boolean} [nsfw=this.nsfw] Whether the new channel is nsfw (only text)
+ * @property {number} [bitrate=this.bitrate] Bitrate of the new channel in bits (only voice)
+ * @property {number} [userLimit=this.userLimit] Maximum amount of users allowed in the new channel (only voice)
+ * @property {number} [rateLimitPerUser=ThisType.rateLimitPerUser] Ratelimit per user for the new channel (only text)
+ * @property {ChannelResolvable} [parent=this.parent] Parent of the new channel
+ * @property {string} [reason] Reason for cloning this channel
+ */
+ /* eslint-enable max-len */
+
+ /**
+ * Clone this channel.
+ * @param {string|GuildChannelCloneOptions} [nameOrOptions={}] Name for the new channel.
+ * **(deprecated, use options)**
+ * Alternatively options for cloning the channel
+ * @param {boolean} [withPermissions=true] Whether to clone the channel with this channel's permission overwrites
+ * **(deprecated, use options)**
+ * @param {boolean} [withTopic=true] Whether to clone the channel with this channel's topic
+ * **(deprecated, use options)**
+ * @param {string} [reason] Reason for cloning this channel **(deprecated, user options)**
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Clone a channel
+ * channel.clone({ topic: null, reason: 'Needed a clone' })
+ * .then(clone => console.log(`Cloned ${channel.name} to make a channel called ${clone.name}`))
+ * .catch(console.error);
+ */
+ clone(nameOrOptions = {}, withPermissions = true, withTopic = true, reason) {
+ // If more than one parameter was specified or the first is a string,
+ // convert them to a compatible options object and issue a warning
+ if (arguments.length > 1 || typeof nameOrOptions === 'string') {
+ process.emitWarning(
+ 'GuildChannel#clone: Clone channels using an options object instead of separate parameters.',
+ 'Deprecation Warning'
+ );
+
+ nameOrOptions = {
+ name: nameOrOptions,
+ permissionOverwrites: withPermissions ? this.permissionOverwrites : null,
+ topic: withTopic ? this.topic : null,
+ reason: reason || null,
+ };
+ }
+
+ Util.mergeDefault({
+ name: this.name,
+ permissionOverwrites: this.permissionOverwrites,
+ topic: this.topic,
+ type: this.type,
+ nsfw: this.nsfw,
+ parent: this.parent,
+ bitrate: this.bitrate,
+ userLimit: this.userLimit,
+ rateLimitPerUser: this.rateLimitPerUser,
+ reason: null,
+ }, nameOrOptions);
+
+ return this.guild.createChannel(nameOrOptions.name, nameOrOptions);
+ }
+
+ /**
+ * Fetches a collection of invites to this guild channel.
+ * Resolves with a collection mapping invites by their codes.
+ * @returns {Promise<Collection<string, Invite>>}
+ */
+ fetchInvites() {
+ return this.client.rest.makeRequest('get', Constants.Endpoints.Channel(this.id).invites, true)
+ .then(data => {
+ const invites = new Collection();
+ for (let invite of data) {
+ invite = new Invite(this.client, invite);
+ invites.set(invite.code, invite);
+ }
+
+ return invites;
+ });
+ }
+
+ /**
+ * Deletes this channel.
+ * @param {string} [reason] Reason for deleting this channel
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Delete the channel
+ * channel.delete('Making room for new channels')
+ * .then(deleted => console.log(`Deleted ${deleted.name} to make room for new channels`))
+ * .catch(console.error);
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteChannel(this, reason);
+ }
+
+ /**
+ * Checks if this channel has the same type, topic, position, name, overwrites and ID as another channel.
+ * In most cases, a simple `channel.id === channel2.id` will do, and is much faster too.
+ * @param {GuildChannel} channel Channel to compare with
+ * @returns {boolean}
+ */
+ equals(channel) {
+ let equal = channel &&
+ this.id === channel.id &&
+ this.type === channel.type &&
+ this.topic === channel.topic &&
+ this.position === channel.position &&
+ this.name === channel.name;
+
+ if (equal) {
+ if (this.permissionOverwrites && channel.permissionOverwrites) {
+ equal = this.permissionOverwrites.equals(channel.permissionOverwrites);
+ } else {
+ equal = !this.permissionOverwrites && !channel.permissionOverwrites;
+ }
+ }
+
+ return equal;
+ }
+
+ /**
+ * Whether the channel is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return this.id !== this.guild.id &&
+ this.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS);
+ }
+
+ /**
+ * Whether the channel is manageable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get manageable() {
+ if (this.client.user.id === this.guild.ownerID) return true;
+ const permissions = this.permissionsFor(this.client.user);
+ if (!permissions) return false;
+ return permissions.has([Permissions.FLAGS.MANAGE_CHANNELS, Permissions.FLAGS.VIEW_CHANNEL]);
+ }
+
+ /**
+ * Whether the channel is muted
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get muted() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.guild.id).channelOverrides.get(this.id).muted;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ /**
+ * The type of message that should notify you
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?MessageNotificationType}
+ * @readonly
+ * @deprecated
+ */
+ get messageNotifications() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.guild.id).channelOverrides.get(this.id).messageNotifications;
+ } catch (err) {
+ return Constants.MessageNotificationTypes[3];
+ }
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the channel's mention instead of the Channel object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <#123456789012345678>
+ * console.log(`Hello from ${channel}`);
+ * @example
+ * // Logs: Hello from <#123456789012345678>
+ * console.log('Hello from ' + channel);
+ */
+ toString() {
+ return `<#${this.id}>`;
+ }
+}
+
+module.exports = GuildChannel;
diff --git a/node_modules/discord.js/src/structures/GuildMember.js b/node_modules/discord.js/src/structures/GuildMember.js
new file mode 100644
index 0000000..45b75b3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildMember.js
@@ -0,0 +1,636 @@
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Role = require('./Role');
+const Permissions = require('../util/Permissions');
+const Collection = require('../util/Collection');
+const { Presence } = require('./Presence');
+const util = require('util');
+
+/**
+ * Represents a member of a guild on Discord.
+ * @implements {TextBasedChannel}
+ */
+class GuildMember {
+ constructor(guild, data) {
+ /**
+ * The client that instantiated this GuildMember
+ * @name GuildMember#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: guild.client });
+
+ /**
+ * The guild that this member is part of
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * The user that this member instance Represents
+ * @type {User}
+ */
+ this.user = {};
+
+ /**
+ * The timestamp this member joined the guild at
+ * @type {number}
+ */
+ this.joinedTimestamp = null;
+
+ /**
+ * The timestamp of when the member used their Nitro boost on the guild, if it was used
+ * @type {?number}
+ */
+ this.premiumSinceTimestamp = null;
+
+ this._roles = [];
+ if (data) this.setup(data);
+
+ /**
+ * The ID of the last message sent by this member in their guild, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The Message object of the last message sent by this member in their guild, if one was sent
+ * @type {?Message}
+ */
+ this.lastMessage = null;
+
+ /**
+ * Whether the member has been removed from the guild
+ * @type {boolean}
+ */
+ this.deleted = false;
+ }
+
+ setup(data) {
+ /**
+ * Whether this member is deafened server-wide
+ * @type {boolean}
+ */
+ this.serverDeaf = data.deaf;
+
+ /**
+ * Whether this member is muted server-wide
+ * @type {boolean}
+ */
+ this.serverMute = data.mute;
+
+ /**
+ * Whether this member is self-muted
+ * @type {boolean}
+ */
+ this.selfMute = data.self_mute;
+
+ /**
+ * Whether this member is self-deafened
+ * @type {boolean}
+ */
+ this.selfDeaf = data.self_deaf;
+
+ /**
+ * Whether this member is streaming using "Go Live"
+ * @type {boolean}
+ */
+ this.selfStream = data.self_stream || false;
+
+ /**
+ * The voice session ID of this member, if any
+ * @type {?Snowflake}
+ */
+ this.voiceSessionID = data.session_id;
+
+ /**
+ * The voice channel ID of this member, if any
+ * @type {?Snowflake}
+ */
+ this.voiceChannelID = data.channel_id;
+
+ /**
+ * Whether this member is speaking and the client is in the same channel
+ * @type {boolean}
+ */
+ this.speaking = false;
+
+ /**
+ * The nickname of this member, if they have one
+ * @type {?string}
+ */
+ this.nickname = data.nick || null;
+
+ if (data.joined_at) this.joinedTimestamp = new Date(data.joined_at).getTime();
+ if (data.premium_since) this.premiumSinceTimestamp = new Date(data.premium_since).getTime();
+
+ this.user = data.user;
+ this._roles = data.roles;
+ }
+
+ /**
+ * The time this member joined the guild
+ * @type {?Date}
+ * @readonly
+ */
+ get joinedAt() {
+ return this.joinedTimestamp ? new Date(this.joinedTimestamp) : null;
+ }
+
+ /**
+ * The time of when the member used their Nitro boost on the guild, if it was used
+ * @type {?Date}
+ * @readonly
+ */
+ get premiumSince() {
+ return this.premiumSinceTimestamp ? new Date(this.premiumSinceTimestamp) : null;
+ }
+
+ /**
+ * The presence of this member
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ return this.frozenPresence || this.guild.presences.get(this.id) || new Presence(undefined, this.client);
+ }
+
+ /**
+ * A list of roles that are applied to this member, mapped by the role ID
+ * @type {Collection<Snowflake, Role>}
+ * @readonly
+ */
+ get roles() {
+ const list = new Collection();
+ const everyoneRole = this.guild.roles.get(this.guild.id);
+
+ if (everyoneRole) list.set(everyoneRole.id, everyoneRole);
+
+ for (const roleID of this._roles) {
+ const role = this.guild.roles.get(roleID);
+ if (role) list.set(role.id, role);
+ }
+
+ return list;
+ }
+
+ /**
+ * The role of this member with the highest position
+ * @type {Role}
+ * @readonly
+ */
+ get highestRole() {
+ return this.roles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
+ }
+
+ /**
+ * The role of this member used to set their color
+ * @type {?Role}
+ * @readonly
+ */
+ get colorRole() {
+ const coloredRoles = this.roles.filter(role => role.color);
+ if (!coloredRoles.size) return null;
+ return coloredRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
+ }
+
+ /**
+ * The displayed color of this member in base 10
+ * @type {number}
+ * @readonly
+ */
+ get displayColor() {
+ const role = this.colorRole;
+ return (role && role.color) || 0;
+ }
+
+ /**
+ * The displayed color of this member in hexadecimal
+ * @type {string}
+ * @readonly
+ */
+ get displayHexColor() {
+ const role = this.colorRole;
+ return (role && role.hexColor) || '#000000';
+ }
+
+ /**
+ * The role of this member used to hoist them in a separate category in the users list
+ * @type {?Role}
+ * @readonly
+ */
+ get hoistRole() {
+ const hoistedRoles = this.roles.filter(role => role.hoist);
+ if (!hoistedRoles.size) return null;
+ return hoistedRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
+ }
+
+ /**
+ * Whether this member is muted in any way
+ * @type {boolean}
+ * @readonly
+ */
+ get mute() {
+ return this.selfMute || this.serverMute;
+ }
+
+ /**
+ * Whether this member is deafened in any way
+ * @type {boolean}
+ * @readonly
+ */
+ get deaf() {
+ return this.selfDeaf || this.serverDeaf;
+ }
+
+ /**
+ * The voice channel this member is in, if any
+ * @type {?VoiceChannel}
+ * @readonly
+ */
+ get voiceChannel() {
+ return this.guild.channels.get(this.voiceChannelID);
+ }
+
+ /**
+ * The ID of this user
+ * @type {Snowflake}
+ * @readonly
+ */
+ get id() {
+ return this.user.id;
+ }
+
+ /**
+ * The nickname of this member, or their username if they don't have one
+ * @type {string}
+ * @readonly
+ */
+ get displayName() {
+ return this.nickname || this.user.username;
+ }
+
+ /**
+ * The overall set of permissions for this member, taking only roles into account
+ * @type {Permissions}
+ * @readonly
+ */
+ get permissions() {
+ if (this.user.id === this.guild.ownerID) return new Permissions(this, Permissions.ALL);
+
+ let permissions = 0;
+ const roles = this.roles;
+ for (const role of roles.values()) permissions |= role.permissions;
+
+ return new Permissions(this, permissions);
+ }
+
+ /**
+ * Whether this member is manageable in terms of role hierarchy by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get manageable() {
+ if (this.user.id === this.guild.ownerID) return false;
+ if (this.user.id === this.client.user.id) return false;
+ if (this.client.user.id === this.guild.ownerID) return true;
+ return this.guild.me.highestRole.comparePositionTo(this.highestRole) > 0;
+ }
+
+ /**
+ * Whether this member is kickable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get kickable() {
+ return this.manageable && this.guild.me.permissions.has(Permissions.FLAGS.KICK_MEMBERS);
+ }
+
+ /**
+ * Whether this member is bannable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get bannable() {
+ return this.manageable && this.guild.me.permissions.has(Permissions.FLAGS.BAN_MEMBERS);
+ }
+
+ /**
+ * Returns `channel.permissionsFor(guildMember)`. Returns permissions for this member in a guild channel,
+ * taking into account roles and permission overwrites.
+ * @param {ChannelResolvable} channel The guild channel to use as context
+ * @returns {?Permissions}
+ */
+ permissionsIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ if (!channel || !channel.guild) throw new Error('Could not resolve channel to a guild channel.');
+ return channel.permissionsFor(this);
+ }
+
+ /**
+ * Checks if any of this member's roles have a permission.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
+ * **(deprecated)**
+ * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override
+ * (takes priority over `explicit`)
+ * @param {boolean} [checkOwner] Whether to allow being the guild's owner to override
+ * (takes priority over `explicit`)
+ * @returns {boolean}
+ */
+ hasPermission(permission, explicit = false, checkAdmin, checkOwner) {
+ if (typeof checkAdmin === 'undefined') checkAdmin = !explicit;
+ if (typeof checkOwner === 'undefined') checkOwner = !explicit;
+ if (checkOwner && this.user.id === this.guild.ownerID) return true;
+ return this.roles.some(r => r.hasPermission(permission, undefined, checkAdmin));
+ }
+
+ /**
+ * Checks whether the roles of this member allows them to perform specific actions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
+ * @returns {boolean}
+ * @deprecated
+ */
+ hasPermissions(permissions, explicit = false) {
+ if (!explicit && this.user.id === this.guild.ownerID) return true;
+ return this.hasPermission(permissions, explicit);
+ }
+
+ /**
+ * Checks whether the roles of this member allows them to perform specific actions, and lists any missing permissions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
+ * @returns {PermissionResolvable}
+ */
+ missingPermissions(permissions, explicit = false) {
+ if (!(permissions instanceof Array)) permissions = [permissions];
+ return this.permissions.missing(permissions, explicit);
+ }
+
+ /**
+ * The data for editing this member.
+ * @typedef {Object} GuildMemberEditData
+ * @property {string} [nick] The nickname to set for the member
+ * @property {Collection<Snowflake, Role>|RoleResolvable[]} [roles] The roles or role IDs to apply
+ * @property {boolean} [mute] Whether or not the member should be muted
+ * @property {boolean} [deaf] Whether or not the member should be deafened
+ * @property {ChannelResolvable|null} [channel] Channel to move member to (if they are connected to voice), or `null`
+ * if you want to kick them from voice
+ */
+
+ /**
+ * Edits this member.
+ * @param {GuildMemberEditData} data The data to edit the member with
+ * @param {string} [reason] Reason for editing this user
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Set a member's nickname and clear their roles
+ * message.member.edit({
+ * nick: 'Cool Name',
+ * roles: []
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateGuildMember(this, data, reason);
+ }
+
+ /**
+ * Mute/unmute this member.
+ * @param {boolean} mute Whether or not the member should be muted
+ * @param {string} [reason] Reason for muting or unmuting
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Mute a member with a reason
+ * message.member.setMute(true, 'It needed to be done')
+ * .then(() => console.log(`Muted ${message.member.displayName}`)))
+ * .catch(console.error);
+ */
+ setMute(mute, reason) {
+ return this.edit({ mute }, reason);
+ }
+
+ /**
+ * Deafen/undeafen this member.
+ * @param {boolean} deaf Whether or not the member should be deafened
+ * @param {string} [reason] Reason for deafening or undeafening
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Deafen a member
+ * message.member.setDeaf(true)
+ * .then(() => console.log(`Deafened ${message.member.displayName}`))
+ * .catch(console.error);
+ */
+ setDeaf(deaf, reason) {
+ return this.edit({ deaf }, reason);
+ }
+
+ /**
+ * Moves this member to the given channel.
+ * @param {ChannelResolvable|null} channel Channel to move the member to, or `null` if you want to kick them from
+ * voice
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Moves a member to a voice channel
+ * member.setVoiceChannel('174674066072928256')
+ * .then(() => console.log(`Moved ${member.displayName}`))
+ * .catch(console.error);
+ */
+ setVoiceChannel(channel) {
+ return this.edit({ channel });
+ }
+
+ /**
+ * Sets the roles applied to this member.
+ * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to apply
+ * @param {string} [reason] Reason for applying the roles
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Set the member's roles to a single role
+ * guildMember.setRoles(['391156570408615936'])
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Remove all of the member's roles
+ * guildMember.setRoles([])
+ * .then(member => console.log(`${member.displayName} now has ${member.roles.size} roles`))
+ * .catch(console.error);
+ */
+ setRoles(roles, reason) {
+ return this.edit({ roles }, reason);
+ }
+
+ /**
+ * Adds a single role to this member.
+ * @param {RoleResolvable} role The role or ID of the role to add
+ * @param {string} [reason] Reason for adding the role
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Give a role to a member
+ * message.member.addRole('193654001089118208')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ addRole(role, reason) {
+ if (!(role instanceof Role)) role = this.guild.roles.get(role);
+ if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.'));
+ return this.client.rest.methods.addMemberRole(this, role, reason);
+ }
+
+ /**
+ * Adds multiple roles to this member.
+ * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to add
+ * @param {string} [reason] Reason for adding the roles
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Gives a member a few roles
+ * message.member.addRoles(['193654001089118208', '369308579892690945'])
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ addRoles(roles, reason) {
+ let allRoles;
+ if (roles instanceof Collection) {
+ allRoles = this._roles.slice();
+ for (const role of roles.values()) allRoles.push(role.id);
+ } else {
+ allRoles = this._roles.concat(roles);
+ }
+ return this.edit({ roles: allRoles }, reason);
+ }
+
+ /**
+ * Removes a single role from this member.
+ * @param {RoleResolvable} role The role or ID of the role to remove
+ * @param {string} [reason] Reason for removing the role
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Remove a role from a member
+ * message.member.removeRole('193654001089118208')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ removeRole(role, reason) {
+ if (!(role instanceof Role)) role = this.guild.roles.get(role);
+ if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.'));
+ return this.client.rest.methods.removeMemberRole(this, role, reason);
+ }
+
+ /**
+ * Removes multiple roles from this member.
+ * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to remove
+ * @param {string} [reason] Reason for removing the roles
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Removes a few roles from the member
+ * message.member.removeRoles(['193654001089118208', '369308579892690945'])
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ removeRoles(roles, reason) {
+ const allRoles = this._roles.slice();
+ if (roles instanceof Collection) {
+ for (const role of roles.values()) {
+ const index = allRoles.indexOf(role.id);
+ if (index >= 0) allRoles.splice(index, 1);
+ }
+ } else {
+ for (const role of roles) {
+ const index = allRoles.indexOf(role instanceof Role ? role.id : role);
+ if (index >= 0) allRoles.splice(index, 1);
+ }
+ }
+ return this.edit({ roles: allRoles }, reason);
+ }
+
+ /**
+ * Set the nickname for this member.
+ * @param {string} nick The nickname for the guild member
+ * @param {string} [reason] Reason for setting the nickname
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Update the member's nickname
+ * message.member.setNickname('Cool Name')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setNickname(nick, reason) {
+ return this.edit({ nick }, reason);
+ }
+
+ /**
+ * Creates a DM channel between the client and this member.
+ * @returns {Promise<DMChannel>}
+ */
+ createDM() {
+ return this.user.createDM();
+ }
+
+ /**
+ * Deletes any DMs with this guild member.
+ * @returns {Promise<DMChannel>}
+ */
+ deleteDM() {
+ return this.user.deleteDM();
+ }
+
+ /**
+ * Kick this member from the guild.
+ * @param {string} [reason] Reason for kicking user
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Kick a member
+ * member.kick()
+ * .then(() => console.log(`Kicked ${member.displayName}`))
+ * .catch(console.error);
+ */
+ kick(reason) {
+ return this.client.rest.methods.kickGuildMember(this.guild, this, reason);
+ }
+
+ /**
+ * Ban this member.
+ * @param {Object|number|string} [options] Ban options. If a number, the number of days to delete messages for, if a
+ * string, the ban reason. Supplying an object allows you to do both.
+ * @param {number} [options.days=0] Number of days of messages to delete
+ * @param {string} [options.reason] Reason for banning
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Ban a guild member
+ * member.ban(7)
+ * .then(() => console.log(`Banned ${member.displayName}`))
+ * .catch(console.error);
+ */
+ ban(options) {
+ return this.guild.ban(this, options);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the user's mention instead of the Member object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <@123456789>!
+ * console.log(`Hello from ${member}!`);
+ */
+ toString() {
+ return `<@${this.nickname ? '!' : ''}${this.user.id}>`;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendCode() {}
+}
+
+TextBasedChannel.applyToClass(GuildMember);
+
+GuildMember.prototype.hasPermissions = util.deprecate(GuildMember.prototype.hasPermissions,
+ 'GuildMember#hasPermissions is deprecated - use GuildMember#hasPermission, it now takes an array');
+GuildMember.prototype.missingPermissions = util.deprecate(GuildMember.prototype.missingPermissions,
+ 'GuildMember#missingPermissions is deprecated - use GuildMember#permissions.missing, it now takes an array');
+
+module.exports = GuildMember;
diff --git a/node_modules/discord.js/src/structures/Integration.js b/node_modules/discord.js/src/structures/Integration.js
new file mode 100644
index 0000000..96af017
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Integration.js
@@ -0,0 +1,151 @@
+/**
+ * The information account for an integration
+ * @typedef {Object} IntegrationAccount
+ * @property {string} id The id of the account
+ * @property {string} name The name of the account
+ */
+
+/**
+ * Represents a guild integration.
+ */
+class Integration {
+ constructor(client, data, guild) {
+ /**
+ * The client that created this integration
+ * @name Integration#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The guild this integration belongs to
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * The integration id
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The integration name
+ * @type {string}
+ */
+ this.name = data.name;
+ /**
+ * The integration type (twitch, youtube, etc)
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * Whether this integration is enabled
+ * @type {boolean}
+ */
+ this.enabled = data.enabled;
+
+ /**
+ * Whether this integration is syncing
+ * @type {boolean}
+ */
+ this.syncing = data.syncing;
+
+ /**
+ * The role that this integration uses for subscribers
+ * @type {Role}
+ */
+ this.role = this.guild.roles.get(data.role_id);
+
+ /**
+ * The user for this integration
+ * @type {User}
+ */
+ this.user = this.client.dataManager.newUser(data.user);
+
+ /**
+ * The account integration information
+ * @type {IntegrationAccount}
+ */
+ this.account = data.account;
+
+ /**
+ * The last time this integration was last synced
+ * @type {number}
+ */
+ this.syncedAt = data.synced_at;
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The behavior of expiring subscribers
+ * @type {number}
+ */
+ this.expireBehavior = data.expire_behavior;
+
+ /**
+ * The grace period before expiring subscribers
+ * @type {number}
+ */
+ this.expireGracePeriod = data.expire_grace_period;
+ }
+
+ /**
+ * Syncs this integration
+ * @returns {Promise<Integration>}
+ */
+ sync() {
+ this.syncing = true;
+ return this.client.rest.methods.syncIntegration(this)
+ .then(() => {
+ this.syncing = false;
+ this.syncedAt = Date.now();
+ return this;
+ });
+ }
+
+ /**
+ * The data for editing an integration.
+ * @typedef {Object} IntegrationEditData
+ * @property {number} [expireBehavior] The new behaviour of expiring subscribers
+ * @property {number} [expireGracePeriod] The new grace period before expiring subscribers
+ */
+
+ /**
+ * Edits this integration.
+ * @param {IntegrationEditData} data The data to edit this integration with
+ * @param {string} reason Reason for editing this integration
+ * @returns {Promise<Integration>}
+ */
+ edit(data, reason) {
+ if ('expireBehavior' in data) {
+ data.expire_behavior = data.expireBehavior;
+ data.expireBehavior = undefined;
+ }
+ if ('expireGracePeriod' in data) {
+ data.expire_grace_period = data.expireGracePeriod;
+ data.expireGracePeriod = undefined;
+ }
+ // The option enable_emoticons is only available for Twitch at this moment
+ return this.client.rest.methods.editIntegration(this, data, reason)
+ .then(() => {
+ this._patch(data);
+ return this;
+ });
+ }
+
+ /**
+ * Deletes this integration.
+ * @returns {Promise<Integration>}
+ * @param {string} [reason] Reason for deleting this integration
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteIntegration(this, reason)
+ .then(() => this);
+ }
+}
+
+module.exports = Integration;
diff --git a/node_modules/discord.js/src/structures/Invite.js b/node_modules/discord.js/src/structures/Invite.js
new file mode 100644
index 0000000..1d84cf5
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Invite.js
@@ -0,0 +1,164 @@
+const PartialGuild = require('./PartialGuild');
+const PartialGuildChannel = require('./PartialGuildChannel');
+const Constants = require('../util/Constants');
+
+/**
+ * Represents an invitation to a guild channel.
+ * <warn>The only guaranteed properties are `code`, `url`, `guild`, and `channel`.
+ * Other properties can be missing.</warn>
+ */
+class Invite {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the invite
+ * @name Invite#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The guild the invite is for. If this guild is already known, this will be a guild object. If the guild is
+ * unknown, this will be a PartialGuild object
+ * @type {Guild|PartialGuild}
+ */
+ this.guild = this.client.guilds.get(data.guild.id) || new PartialGuild(this.client, data.guild);
+
+ /**
+ * The code for this invite
+ * @type {string}
+ */
+ this.code = data.code;
+
+ /**
+ * The approximate number of online members of the guild this invite is for
+ * @type {number}
+ */
+ this.presenceCount = data.approximate_presence_count;
+
+ /**
+ * The approximate total number of members of the guild this invite is for
+ * @type {number}
+ */
+ this.memberCount = data.approximate_member_count;
+
+ /**
+ * The number of text channels the guild this invite goes to has
+ * @type {number}
+ */
+ this.textChannelCount = data.guild.text_channel_count;
+
+ /**
+ * The number of voice channels the guild this invite goes to has
+ * @type {number}
+ */
+ this.voiceChannelCount = data.guild.voice_channel_count;
+
+ /**
+ * Whether or not this invite is temporary
+ * @type {boolean}
+ */
+ this.temporary = data.temporary;
+
+ /**
+ * The maximum age of the invite, in seconds
+ * @type {?number}
+ */
+ this.maxAge = data.max_age;
+
+ /**
+ * How many times this invite has been used
+ * @type {number}
+ */
+ this.uses = data.uses;
+
+ /**
+ * The maximum uses of this invite
+ * @type {number}
+ */
+ this.maxUses = data.max_uses;
+
+ if (data.inviter) {
+ /**
+ * The user who created this invite
+ * @type {?User}
+ */
+ this.inviter = this.client.dataManager.newUser(data.inviter);
+ }
+
+ /**
+ * The channel the invite is for. If this channel is already known, this will be a GuildChannel object.
+ * If the channel is unknown, this will be a PartialGuildChannel object.
+ * @type {GuildChannel|PartialGuildChannel}
+ */
+ this.channel = this.client.channels.get(data.channel.id) || new PartialGuildChannel(this.client, data.channel);
+
+ /**
+ * The timestamp the invite was created at
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.created_at).getTime();
+ }
+
+ /**
+ * The time the invite was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The timestamp the invite will expire at
+ * @type {number}
+ * @readonly
+ */
+ get expiresTimestamp() {
+ return this.createdTimestamp + (this.maxAge * 1000);
+ }
+
+ /**
+ * The time the invite will expire
+ * @type {Date}
+ * @readonly
+ */
+ get expiresAt() {
+ return new Date(this.expiresTimestamp);
+ }
+
+ /**
+ * The URL to the invite
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return Constants.Endpoints.inviteLink(this.code);
+ }
+
+ /**
+ * Deletes this invite.
+ * @param {string} [reason] Reason for deleting this invite
+ * @returns {Promise<Invite>}
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteInvite(this, reason);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the invite's URL instead of the object.
+ * @returns {string}
+ * @example
+ * // Logs: Invite: https://discord.gg/A1b2C3
+ * console.log(`Invite: ${invite}`);
+ */
+ toString() {
+ return this.url;
+ }
+}
+
+module.exports = Invite;
diff --git a/node_modules/discord.js/src/structures/Message.js b/node_modules/discord.js/src/structures/Message.js
new file mode 100644
index 0000000..c71844f
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Message.js
@@ -0,0 +1,661 @@
+const Mentions = require('./MessageMentions');
+const Attachment = require('./MessageAttachment');
+const Embed = require('./MessageEmbed');
+const RichEmbed = require('./RichEmbed');
+const MessageReaction = require('./MessageReaction');
+const ReactionCollector = require('./ReactionCollector');
+const Util = require('../util/Util');
+const Collection = require('../util/Collection');
+const Constants = require('../util/Constants');
+const Permissions = require('../util/Permissions');
+const MessageFlags = require('../util/MessageFlags');
+let GuildMember;
+
+/**
+ * Represents a message on Discord.
+ */
+class Message {
+ constructor(channel, data, client) {
+ /**
+ * The client that instantiated the Message
+ * @name Message#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The channel that the message was sent in
+ * @type {TextChannel|DMChannel|GroupDMChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * Whether this message has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) { // eslint-disable-line complexity
+ /**
+ * The ID of the message
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of the message
+ * @type {MessageType}
+ */
+ this.type = Constants.MessageTypes[data.type];
+
+ /**
+ * The content of the message
+ * @type {string}
+ */
+ this.content = data.content;
+
+ /**
+ * The author of the message
+ * @type {User}
+ */
+ this.author = this.client.dataManager.newUser(data.author, !data.webhook_id);
+
+ /**
+ * Whether or not this message is pinned
+ * @type {boolean}
+ */
+ this.pinned = data.pinned;
+
+ /**
+ * Whether or not the message was Text-To-Speech
+ * @type {boolean}
+ */
+ this.tts = data.tts;
+
+ /**
+ * A random number or string used for checking message delivery
+ * <warn>This is only received after the message was sent successfully, and
+ * lost if re-fetched</warn>
+ * @type {?string}
+ */
+ this.nonce = data.nonce;
+
+ /**
+ * Whether or not this message was sent by Discord, not actually a user (e.g. pin notifications)
+ * @type {boolean}
+ */
+ this.system = data.type !== 0;
+
+ /**
+ * A list of embeds in the message - e.g. YouTube Player
+ * @type {MessageEmbed[]}
+ */
+ this.embeds = data.embeds.map(e => new Embed(this, e));
+
+ /**
+ * A collection of attachments in the message - e.g. Pictures - mapped by their ID
+ * @type {Collection<Snowflake, MessageAttachment>}
+ */
+ this.attachments = new Collection();
+ for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment));
+
+ /**
+ * The timestamp the message was sent at
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.timestamp).getTime();
+
+ /**
+ * The timestamp the message was last edited at (if applicable)
+ * @type {?number}
+ */
+ this.editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null;
+
+ /**
+ * A collection of reactions to this message, mapped by the reaction ID
+ * @type {Collection<Snowflake, MessageReaction>}
+ */
+ this.reactions = new Collection();
+ if (data.reactions && data.reactions.length > 0) {
+ for (const reaction of data.reactions) {
+ const id = reaction.emoji.id ? `${reaction.emoji.name}:${reaction.emoji.id}` : reaction.emoji.name;
+ this.reactions.set(id, new MessageReaction(this, reaction.emoji, reaction.count, reaction.me));
+ }
+ }
+
+ /**
+ * All valid mentions that the message contains
+ * @type {MessageMentions}
+ */
+ this.mentions = new Mentions(this, data.mentions, data.mention_roles, data.mention_everyone, data.mention_channels);
+
+ /**
+ * ID of the webhook that sent the message, if applicable
+ * @type {?Snowflake}
+ */
+ this.webhookID = data.webhook_id || null;
+
+ /**
+ * Whether this message is a hit in a search
+ * @type {?boolean}
+ */
+ this.hit = typeof data.hit === 'boolean' ? data.hit : null;
+
+ /**
+ * Flags that are applied to the message
+ * @type {Readonly<MessageFlags>}
+ */
+ this.flags = new MessageFlags(data.flags).freeze();
+
+ /**
+ * Reference data sent in a crossposted message.
+ * @typedef {Object} MessageReference
+ * @property {string} channelID ID of the channel the message was crossposted from
+ * @property {?string} guildID ID of the guild the message was crossposted from
+ * @property {?string} messageID ID of the message that was crossposted
+ */
+
+ /**
+ * Message reference data
+ * @type {?MessageReference}
+ */
+ this.reference = data.message_reference ? {
+ channelID: data.message_reference.channel_id,
+ guildID: data.message_reference.guild_id,
+ messageID: data.message_reference.message_id,
+ } : null;
+
+ /**
+ * The previous versions of the message, sorted with the most recent first
+ * @type {Message[]}
+ * @private
+ */
+ this._edits = [];
+
+ if (data.member && this.guild && this.author && !this.guild.members.has(this.author.id)) {
+ this.guild._addMember(Object.assign(data.member, { user: this.author }), false);
+ }
+
+ /**
+ * Represents the author of the message as a guild member
+ * Only available if the message comes from a guild where the author is still a member
+ * @type {?GuildMember}
+ */
+ this.member = this.guild ? this.guild.member(this.author) || null : null;
+ }
+
+ /**
+ * Updates the message.
+ * @param {Object} data Raw Discord message update data
+ * @private
+ */
+ patch(data) {
+ const clone = Util.cloneObject(this);
+ this._edits.unshift(clone);
+
+ if ('edited_timestamp' in data) this.editedTimestamp = new Date(data.edited_timestamp).getTime();
+ if ('content' in data) this.content = data.content;
+ if ('pinned' in data) this.pinned = data.pinned;
+ if ('tts' in data) this.tts = data.tts;
+ if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(this, e));
+ else this.embeds = this.embeds.slice();
+
+ if ('attachments' in data) {
+ this.attachments = new Collection();
+ for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment));
+ } else {
+ this.attachments = new Collection(this.attachments);
+ }
+
+ this.mentions = new Mentions(
+ this,
+ 'mentions' in data ? data.mentions : this.mentions.users,
+ 'mentions_roles' in data ? data.mentions_roles : this.mentions.roles,
+ 'mention_everyone' in data ? data.mention_everyone : this.mentions.everyone,
+ 'mention_channels' in data ? data.mention_channels : this.mentions.crosspostedChannels
+ );
+
+ this.flags = new MessageFlags('flags' in data ? data.flags : 0).freeze();
+ }
+
+ /**
+ * The time the message was sent
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The time the message was last edited at (if applicable)
+ * @type {?Date}
+ * @readonly
+ */
+ get editedAt() {
+ return this.editedTimestamp ? new Date(this.editedTimestamp) : null;
+ }
+
+ /**
+ * The guild the message was sent in (if in a guild channel)
+ * @type {?Guild}
+ * @readonly
+ */
+ get guild() {
+ return this.channel.guild || null;
+ }
+
+ /**
+ * The url to jump to the message
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return `https://discordapp.com/channels/${this.guild ? this.guild.id : '@me'}/${this.channel.id}/${this.id}`;
+ }
+
+ /**
+ * The message contents with all mentions replaced by the equivalent text.
+ * If mentions cannot be resolved to a name, the relevant mention in the message content will not be converted.
+ * @type {string}
+ * @readonly
+ */
+ get cleanContent() {
+ return this.content
+ .replace(/@(everyone|here)/g, '@\u200b$1')
+ .replace(/<@!?[0-9]+>/g, input => {
+ const id = input.replace(/<|!|>|@/g, '');
+ if (this.channel.type === 'dm' || this.channel.type === 'group') {
+ return this.client.users.has(id) ? `@${this.client.users.get(id).username}` : input;
+ }
+
+ const member = this.channel.guild.members.get(id);
+ if (member) {
+ if (member.nickname) return `@${member.nickname}`;
+ return `@${member.user.username}`;
+ } else {
+ const user = this.client.users.get(id);
+ if (user) return `@${user.username}`;
+ return input;
+ }
+ })
+ .replace(/<#[0-9]+>/g, input => {
+ const channel = this.client.channels.get(input.replace(/<|#|>/g, ''));
+ if (channel) return `#${channel.name}`;
+ return input;
+ })
+ .replace(/<@&[0-9]+>/g, input => {
+ if (this.channel.type === 'dm' || this.channel.type === 'group') return input;
+ const role = this.guild.roles.get(input.replace(/<|@|>|&/g, ''));
+ if (role) return `@${role.name}`;
+ return input;
+ });
+ }
+
+ /**
+ * Creates a reaction collector.
+ * @param {CollectorFilter} filter The filter to apply
+ * @param {ReactionCollectorOptions} [options={}] Options to send to the collector
+ * @returns {ReactionCollector}
+ * @example
+ * // Create a reaction collector
+ * const filter = (reaction, user) => reaction.emoji.name === '👌' && user.id === 'someID'
+ * const collector = message.createReactionCollector(filter, { time: 15000 });
+ * collector.on('collect', r => console.log(`Collected ${r.emoji.name}`));
+ * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
+ */
+ createReactionCollector(filter, options = {}) {
+ return new ReactionCollector(this, filter, options);
+ }
+
+ /**
+ * An object containing the same properties as CollectorOptions, but a few more:
+ * @typedef {ReactionCollectorOptions} AwaitReactionsOptions
+ * @property {string[]} [errors] Stop/end reasons that cause the promise to reject
+ */
+
+ /**
+ * Similar to createMessageCollector but in promise form.
+ * Resolves with a collection of reactions that pass the specified filter.
+ * @param {CollectorFilter} filter The filter function to use
+ * @param {AwaitReactionsOptions} [options={}] Optional options to pass to the internal collector
+ * @returns {Promise<Collection<string, MessageReaction>>}
+ * @example
+ * // Create a reaction collector
+ * const filter = (reaction, user) => reaction.emoji.name === '👌' && user.id === 'someID'
+ * message.awaitReactions(filter, { time: 15000 })
+ * .then(collected => console.log(`Collected ${collected.size} reactions`))
+ * .catch(console.error);
+ */
+ awaitReactions(filter, options = {}) {
+ return new Promise((resolve, reject) => {
+ const collector = this.createReactionCollector(filter, options);
+ collector.once('end', (reactions, reason) => {
+ if (options.errors && options.errors.includes(reason)) reject(reactions);
+ else resolve(reactions);
+ });
+ });
+ }
+
+ /**
+ * An array of cached versions of the message, including the current version
+ * Sorted from latest (first) to oldest (last)
+ * @type {Message[]}
+ * @readonly
+ */
+ get edits() {
+ const copy = this._edits.slice();
+ copy.unshift(this);
+ return copy;
+ }
+
+ /**
+ * Whether the message is editable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get editable() {
+ return this.author.id === this.client.user.id;
+ }
+
+ /**
+ * Whether the message is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return !this.deleted && (this.author.id === this.client.user.id || (this.guild &&
+ this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES)
+ ));
+ }
+
+ /**
+ * Whether the message is pinnable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get pinnable() {
+ return this.type === 'DEFAULT' && (!this.guild ||
+ this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES));
+ }
+
+ /**
+ * Whether or not a user, channel or role is mentioned in this message.
+ * @param {GuildChannel|User|Role|string} data Either a guild channel, user or a role object, or a string representing
+ * the ID of any of these
+ * @returns {boolean}
+ */
+ isMentioned(data) {
+ data = data && data.id ? data.id : data;
+ return this.mentions.users.has(data) || this.mentions.channels.has(data) || this.mentions.roles.has(data);
+ }
+
+ /**
+ * Whether or not a guild member is mentioned in this message. Takes into account
+ * user mentions, role mentions, and @everyone/@here mentions.
+ * @param {GuildMember|User} member The member/user to check for a mention of
+ * @returns {boolean}
+ */
+ isMemberMentioned(member) {
+ // Lazy-loading is used here to get around a circular dependency that breaks things
+ if (!GuildMember) GuildMember = require('./GuildMember');
+ if (this.mentions.everyone) return true;
+ if (this.mentions.users.has(member.id)) return true;
+ if (member instanceof GuildMember && member.roles.some(r => this.mentions.roles.has(r.id))) return true;
+ return false;
+ }
+
+ /**
+ * Options that can be passed into editMessage.
+ * @typedef {Object} MessageEditOptions
+ * @property {Object} [embed] An embed to be added/edited
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {MessageFlagsResolvable} [flags] Message flags to apply
+ */
+
+ /**
+ * Edit the content of the message.
+ * @param {StringResolvable} [content] The new content for the message
+ * @param {MessageEditOptions|RichEmbed} [options] The options to provide
+ * @returns {Promise<Message>}
+ * @example
+ * // Update the content of a message
+ * message.edit('This is my new content!')
+ * .then(msg => console.log(`New message content: ${msg}`))
+ * .catch(console.error);
+ */
+ edit(content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+ if (options instanceof RichEmbed) options = { embed: options };
+ return this.client.rest.methods.updateMessage(this, content, options);
+ }
+
+ /**
+ * Edit the content of the message, with a code block.
+ * @param {string} lang The language for the code block
+ * @param {StringResolvable} content The new content for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ editCode(lang, content) {
+ content = Util.escapeMarkdown(this.client.resolver.resolveString(content), true);
+ return this.edit(`\`\`\`${lang || ''}\n${content}\n\`\`\``);
+ }
+
+ /**
+ * Pins this message to the channel's pinned messages.
+ * @returns {Promise<Message>}
+ */
+ pin() {
+ return this.client.rest.methods.pinMessage(this);
+ }
+
+ /**
+ * Unpins this message from the channel's pinned messages.
+ * @returns {Promise<Message>}
+ */
+ unpin() {
+ return this.client.rest.methods.unpinMessage(this);
+ }
+
+ /**
+ * Add a reaction to the message.
+ * @param {string|Emoji|ReactionEmoji} emoji The emoji to react with
+ * @returns {Promise<MessageReaction>}
+ * @example
+ * // React to a message with a unicode emoji
+ * message.react('🤔')
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // React to a message with a custom emoji
+ * message.react(message.guild.emojis.get('123456789012345678'))
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ react(emoji) {
+ emoji = this.client.resolver.resolveEmojiIdentifier(emoji);
+ if (!emoji) throw new TypeError('Emoji must be a string or Emoji/ReactionEmoji');
+
+ return this.client.rest.methods.addMessageReaction(this, emoji);
+ }
+
+ /**
+ * Remove all reactions from a message.
+ * @returns {Promise<Message>}
+ */
+ clearReactions() {
+ return this.client.rest.methods.removeMessageReactions(this);
+ }
+
+ /**
+ * Deletes the message.
+ * @param {number} [timeout=0] How long to wait to delete the message in milliseconds
+ * @returns {Promise<Message>}
+ * @example
+ * // Delete a message
+ * message.delete()
+ * .then(msg => console.log(`Deleted message from ${msg.author.username}`))
+ * .catch(console.error);
+ */
+ delete(timeout = 0) {
+ if (timeout <= 0) {
+ return this.client.rest.methods.deleteMessage(this);
+ } else {
+ return new Promise(resolve => {
+ this.client.setTimeout(() => {
+ resolve(this.delete());
+ }, timeout);
+ });
+ }
+ }
+
+ /**
+ * Reply to the message.
+ * @param {StringResolvable} [content] The content for the message
+ * @param {MessageOptions} [options] The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @example
+ * // Reply to a message
+ * message.reply('Hey, I\'m a reply!')
+ * .then(sent => console.log(`Sent a reply to ${sent.author.username}`))
+ * .catch(console.error);
+ */
+ reply(content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+ return this.channel.send(content, Object.assign(options, { reply: this.member || this.author }));
+ }
+
+ /**
+ * Marks the message as read.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ acknowledge() {
+ return this.client.rest.methods.ackMessage(this);
+ }
+
+ /**
+ * Fetches the webhook used to create this message.
+ * @returns {Promise<?Webhook>}
+ */
+ fetchWebhook() {
+ if (!this.webhookID) return Promise.reject(new Error('The message was not sent by a webhook.'));
+ return this.client.fetchWebhook(this.webhookID);
+ }
+
+ /**
+ * Suppresses or unsuppresses embeds on a message
+ * @param {boolean} [suppress=true] If the embeds should be suppressed or not
+ * @returns {Promise<Message>}
+ */
+ suppressEmbeds(suppress = true) {
+ const flags = new MessageFlags(this.flags.bitfield);
+
+ if (suppress) {
+ flags.add(MessageFlags.FLAGS.SUPPRESS_EMBEDS);
+ } else {
+ flags.remove(MessageFlags.FLAGS.SUPPRESS_EMBEDS);
+ }
+
+ return this.edit(undefined, { flags });
+ }
+
+ /**
+ * Used mainly internally. Whether two messages are identical in properties. If you want to compare messages
+ * without checking all the properties, use `message.id === message2.id`, which is much more efficient. This
+ * method allows you to see if there are differences in content, embeds, attachments, nonce and tts properties.
+ * @param {Message} message The message to compare it to
+ * @param {Object} rawData Raw data passed through the WebSocket about this message
+ * @returns {boolean}
+ */
+ equals(message, rawData) {
+ if (!message) return false;
+ const embedUpdate = !message.author && !message.attachments;
+ if (embedUpdate) return this.id === message.id && this.embeds.length === message.embeds.length;
+
+ let equal = this.id === message.id &&
+ this.author.id === message.author.id &&
+ this.content === message.content &&
+ this.tts === message.tts &&
+ this.nonce === message.nonce &&
+ this.embeds.length === message.embeds.length &&
+ this.attachments.length === message.attachments.length;
+
+ if (equal && rawData) {
+ equal = this.mentions.everyone === message.mentions.everyone &&
+ this.createdTimestamp === new Date(rawData.timestamp).getTime() &&
+ this.editedTimestamp === new Date(rawData.edited_timestamp).getTime();
+ }
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the message's content instead of the object.
+ * @returns {string}
+ * @example
+ * // Logs: Message: This is a message!
+ * console.log(`Message: ${message}`);
+ */
+ toString() {
+ return this.content;
+ }
+
+ _addReaction(emoji, user) {
+ const emojiID = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name;
+ let reaction;
+ if (this.reactions.has(emojiID)) {
+ reaction = this.reactions.get(emojiID);
+ if (!reaction.me) reaction.me = user.id === this.client.user.id;
+ } else {
+ reaction = new MessageReaction(this, emoji, 0, user.id === this.client.user.id);
+ this.reactions.set(emojiID, reaction);
+ }
+ if (!reaction.users.has(user.id)) {
+ reaction.users.set(user.id, user);
+ reaction.count++;
+ }
+ return reaction;
+ }
+
+ _removeReaction(emoji, user) {
+ const emojiID = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name;
+ if (this.reactions.has(emojiID)) {
+ const reaction = this.reactions.get(emojiID);
+ if (!user) {
+ this.reactions.delete(emojiID);
+ return reaction;
+ }
+ if (reaction.users.has(user.id)) {
+ reaction.users.delete(user.id);
+ reaction.count--;
+ if (user.id === this.client.user.id) reaction.me = false;
+ if (reaction.count <= 0) this.reactions.delete(emojiID);
+ return reaction;
+ }
+ }
+ return null;
+ }
+
+ _clearReactions() {
+ this.reactions.clear();
+ }
+}
+
+module.exports = Message;
diff --git a/node_modules/discord.js/src/structures/MessageAttachment.js b/node_modules/discord.js/src/structures/MessageAttachment.js
new file mode 100644
index 0000000..6db15c8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageAttachment.js
@@ -0,0 +1,79 @@
+const { basename } = require('path');
+
+/**
+ * Represents an attachment in a message.
+ */
+class MessageAttachment {
+ constructor(message, data) {
+ /**
+ * The client that instantiated this MessageAttachment
+ * @name MessageAttachment#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: message.client });
+
+ /**
+ * The message this attachment is part of
+ * @type {Message}
+ */
+ this.message = message;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this attachment
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The file name of this attachment
+ * @type {string}
+ */
+ this.filename = data.filename;
+
+ /**
+ * The size of this attachment in bytes
+ * @type {number}
+ */
+ this.filesize = data.size;
+
+ /**
+ * The URL to this attachment
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The Proxy URL to this attachment
+ * @type {string}
+ */
+ this.proxyURL = data.proxy_url;
+
+ /**
+ * The height of this attachment (if an image)
+ * @type {?number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of this attachment (if an image)
+ * @type {?number}
+ */
+ this.width = data.width;
+ }
+
+ /**
+ * Whether or not this attachment has been marked as a spoiler
+ * @type {boolean}
+ * @readonly
+ */
+ get spoiler() {
+ return basename(this.url).startsWith('SPOILER_');
+ }
+}
+
+module.exports = MessageAttachment;
diff --git a/node_modules/discord.js/src/structures/MessageCollector.js b/node_modules/discord.js/src/structures/MessageCollector.js
new file mode 100644
index 0000000..acd82be
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageCollector.js
@@ -0,0 +1,97 @@
+const Collector = require('./interfaces/Collector');
+const util = require('util');
+
+/**
+ * @typedef {CollectorOptions} MessageCollectorOptions
+ * @property {number} max The maximum amount of messages to process
+ * @property {number} maxMatches The maximum amount of messages to collect
+ */
+
+/**
+ * Collects messages on a channel.
+ * @extends {Collector}
+ */
+class MessageCollector extends Collector {
+ /**
+ * @param {TextChannel|DMChannel|GroupDMChannel} channel The channel
+ * @param {CollectorFilter} filter The filter to be applied to this collector
+ * @param {MessageCollectorOptions} options The options to be applied to this collector
+ * @emits MessageCollector#message
+ */
+ constructor(channel, filter, options = {}) {
+ super(channel.client, filter, options);
+
+ /**
+ * The channel
+ * @type {TextBasedChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * Total number of messages that were received in the channel during message collection
+ * @type {number}
+ */
+ this.received = 0;
+
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() + 1);
+ this.client.on('message', this.listener);
+
+ this._reEmitter = message => {
+ /**
+ * Emitted when the collector receives a message.
+ * @event MessageCollector#message
+ * @param {Message} message The message
+ * @deprecated
+ */
+ this.emit('message', message);
+ };
+ this.on('collect', this._reEmitter);
+ }
+
+ // Remove in v12
+ on(eventName, listener) {
+ if (eventName === 'message') {
+ listener = util.deprecate(listener, 'MessageCollector will soon no longer emit "message", use "collect" instead');
+ }
+ super.on(eventName, listener);
+ }
+
+ /**
+ * Handle an incoming message for possible collection.
+ * @param {Message} message The message that could be collected
+ * @returns {?{key: Snowflake, value: Message}}
+ * @private
+ */
+ handle(message) {
+ if (message.channel.id !== this.channel.id) return null;
+ this.received++;
+ return {
+ key: message.id,
+ value: message,
+ };
+ }
+
+ /**
+ * Check after collection to see if the collector is done.
+ * @returns {?string} Reason to end the collector, if any
+ * @private
+ */
+ postCheck() {
+ // Consider changing the end reasons for v12
+ if (this.options.maxMatches && this.collected.size >= this.options.maxMatches) return 'matchesLimit';
+ if (this.options.max && this.received >= this.options.max) return 'limit';
+ return null;
+ }
+
+ /**
+ * Removes event listeners.
+ * @private
+ */
+ cleanup() {
+ this.removeListener('collect', this._reEmitter);
+ this.client.removeListener('message', this.listener);
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() - 1);
+ }
+}
+
+module.exports = MessageCollector;
diff --git a/node_modules/discord.js/src/structures/MessageEmbed.js b/node_modules/discord.js/src/structures/MessageEmbed.js
new file mode 100644
index 0000000..2a87679
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageEmbed.js
@@ -0,0 +1,386 @@
+/**
+ * Represents an embed in a message (image/video preview, rich embed, etc.)
+ * <info>This class is only used for *received* embeds. If you wish to send one, use the {@link RichEmbed} class.</info>
+ */
+class MessageEmbed {
+ constructor(message, data) {
+ /**
+ * The client that instantiated this embed
+ * @name MessageEmbed#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: message.client });
+
+ /**
+ * The message this embed is part of
+ * @type {Message}
+ */
+ this.message = message;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The type of this embed
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * The title of this embed
+ * @type {?string}
+ */
+ this.title = data.title;
+
+ /**
+ * The description of this embed
+ * @type {?string}
+ */
+ this.description = data.description;
+
+ /**
+ * The URL of this embed
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The color of the embed
+ * @type {number}
+ */
+ this.color = data.color;
+
+ /**
+ * The fields of this embed
+ * @type {MessageEmbedField[]}
+ */
+ this.fields = [];
+ if (data.fields) for (const field of data.fields) this.fields.push(new MessageEmbedField(this, field));
+
+ /**
+ * The timestamp of this embed
+ * @type {number}
+ */
+ this.timestamp = data.timestamp;
+
+ /**
+ * The thumbnail of this embed
+ * @type {?MessageEmbedThumbnail}
+ */
+ this.thumbnail = data.thumbnail ? new MessageEmbedThumbnail(this, data.thumbnail) : null;
+
+ /**
+ * The image of this embed
+ * @type {?MessageEmbedImage}
+ */
+ this.image = data.image ? new MessageEmbedImage(this, data.image) : null;
+
+ /**
+ * The video of this embed
+ * @type {?MessageEmbedVideo}
+ */
+ this.video = data.video ? new MessageEmbedVideo(this, data.video) : null;
+
+ /**
+ * The author of this embed
+ * @type {?MessageEmbedAuthor}
+ */
+ this.author = data.author ? new MessageEmbedAuthor(this, data.author) : null;
+
+ /**
+ * The provider of this embed
+ * @type {?MessageEmbedProvider}
+ */
+ this.provider = data.provider ? new MessageEmbedProvider(this, data.provider) : null;
+
+ /**
+ * The footer of this embed
+ * @type {?MessageEmbedFooter}
+ */
+ this.footer = data.footer ? new MessageEmbedFooter(this, data.footer) : null;
+ }
+
+ /**
+ * The date this embed was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The hexadecimal version of the embed color, with a leading hash
+ * @type {?string}
+ * @readonly
+ */
+ get hexColor() {
+ if (!this.color) return null;
+ let col = this.color.toString(16);
+ while (col.length < 6) col = `0${col}`;
+ return `#${col}`;
+ }
+}
+
+/**
+ * Represents a thumbnail for a message embed.
+ */
+class MessageEmbedThumbnail {
+ constructor(embed, data) {
+ /**
+ * The embed this thumbnail is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The URL for this thumbnail
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The Proxy URL for this thumbnail
+ * @type {string}
+ */
+ this.proxyURL = data.proxy_url;
+
+ /**
+ * The height of the thumbnail
+ * @type {number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of the thumbnail
+ * @type {number}
+ */
+ this.width = data.width;
+ }
+}
+
+/**
+ * Represents an image for a message embed.
+ */
+class MessageEmbedImage {
+ constructor(embed, data) {
+ /**
+ * The embed this image is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The URL for this image
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The Proxy URL for this image
+ * @type {string}
+ */
+ this.proxyURL = data.proxy_url;
+
+ /**
+ * The height of the image
+ * @type {number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of the image
+ * @type {number}
+ */
+ this.width = data.width;
+ }
+}
+
+/**
+ * Represents a video for a message embed.
+ */
+class MessageEmbedVideo {
+ constructor(embed, data) {
+ /**
+ * The embed this video is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The source URL for this video
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The height of the video
+ * @type {number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of the video
+ * @type {number}
+ */
+ this.width = data.width;
+ }
+}
+
+/**
+ * Represents a provider for a message embed.
+ */
+class MessageEmbedProvider {
+ constructor(embed, data) {
+ /**
+ * The embed this provider is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The name of this provider
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The URL of this provider
+ * @type {string}
+ */
+ this.url = data.url;
+ }
+}
+
+/**
+ * Represents an author for a message embed.
+ */
+class MessageEmbedAuthor {
+ constructor(embed, data) {
+ /**
+ * The embed this author is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The name of this author
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The URL of this author
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The icon URL of this author
+ * @type {string}
+ */
+ this.iconURL = data.icon_url;
+ }
+}
+
+/**
+ * Represents a field for a message embed.
+ */
+class MessageEmbedField {
+ constructor(embed, data) {
+ /**
+ * The embed this footer is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The name of this field
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The value of this field
+ * @type {string}
+ */
+ this.value = data.value;
+
+ /**
+ * If this field is displayed inline
+ * @type {boolean}
+ */
+ this.inline = data.inline;
+ }
+}
+
+/**
+ * Represents the footer of a message embed.
+ */
+class MessageEmbedFooter {
+ constructor(embed, data) {
+ /**
+ * The embed this footer is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The text in this footer
+ * @type {string}
+ */
+ this.text = data.text;
+
+ /**
+ * The icon URL of this footer
+ * @type {string}
+ */
+ this.iconURL = data.icon_url;
+
+ /**
+ * The proxy icon URL of this footer
+ * @type {string}
+ */
+ this.proxyIconUrl = data.proxy_icon_url;
+ }
+}
+
+MessageEmbed.Thumbnail = MessageEmbedThumbnail;
+MessageEmbed.Image = MessageEmbedImage;
+MessageEmbed.Video = MessageEmbedVideo;
+MessageEmbed.Provider = MessageEmbedProvider;
+MessageEmbed.Author = MessageEmbedAuthor;
+MessageEmbed.Field = MessageEmbedField;
+MessageEmbed.Footer = MessageEmbedFooter;
+
+module.exports = MessageEmbed;
diff --git a/node_modules/discord.js/src/structures/MessageMentions.js b/node_modules/discord.js/src/structures/MessageMentions.js
new file mode 100644
index 0000000..47aff55
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageMentions.js
@@ -0,0 +1,185 @@
+const Collection = require('../util/Collection');
+const { ChannelTypes } = require('../util/Constants');
+
+/**
+ * Keeps track of mentions in a {@link Message}.
+ */
+class MessageMentions {
+ constructor(message, users, roles, everyone, crosspostedChannels) {
+ /**
+ * Whether `@everyone` or `@here` were mentioned
+ * @type {boolean}
+ */
+ this.everyone = Boolean(everyone);
+
+ if (users) {
+ if (users instanceof Collection) {
+ /**
+ * Any users that were mentioned
+ * <info>Order as received from the API, not as they appear in the message content</info>
+ * @type {Collection<Snowflake, User>}
+ */
+ this.users = new Collection(users);
+ } else {
+ this.users = new Collection();
+ for (const mention of users) {
+ let user = message.client.users.get(mention.id);
+ if (!user) user = message.client.dataManager.newUser(mention);
+ this.users.set(user.id, user);
+ if (mention.member && message.guild && !message.guild.members.has(mention.id)) {
+ message.guild._addMember(Object.assign(mention.member, { user }), false);
+ }
+ }
+ }
+ } else {
+ this.users = new Collection();
+ }
+
+ if (roles) {
+ if (roles instanceof Collection) {
+ /**
+ * Any roles that were mentioned
+ * <info>Order as received from the API, not as they appear in the message content</
+ * @type {Collection<Snowflake, Role>}
+ */
+ this.roles = new Collection(roles);
+ } else {
+ this.roles = new Collection();
+ for (const mention of roles) {
+ const role = message.channel.guild.roles.get(mention);
+ if (role) this.roles.set(role.id, role);
+ }
+ }
+ } else {
+ this.roles = new Collection();
+ }
+
+ /**
+ * Content of the message
+ * @type {Message}
+ * @private
+ */
+ this._content = message.content;
+
+ /**
+ * The client the message is from
+ * @type {Client}
+ * @private
+ */
+ this._client = message.client;
+
+ /**
+ * The guild the message is in
+ * @type {?Guild}
+ * @private
+ */
+ this._guild = message.channel.guild;
+
+ /**
+ * Cached members for {@MessageMention#members}
+ * @type {?Collection<Snowflake, GuildMember>}
+ * @private
+ */
+ this._members = null;
+
+ /**
+ * Cached channels for {@MessageMention#channels}
+ * @type {?Collection<Snowflake, GuildChannel>}
+ * @private
+ */
+ this._channels = null;
+
+ /**
+ * Crossposted channel data.
+ * @typedef {Object} CrosspostedChannel
+ * @property {Snowflake} channelID ID of the mentioned channel
+ * @property {Snowflake} guildID ID of the guild that has the channel
+ * @property {string} type Type of the channel
+ * @property {string} name Name of the channel
+ */
+
+ if (crosspostedChannels) {
+ if (crosspostedChannels instanceof Collection) {
+ /**
+ * A collection of crossposted channels
+ * @type {Collection<Snowflake, CrosspostedChannel>}
+ */
+ this.crosspostedChannels = new Collection(crosspostedChannels);
+ } else {
+ this.crosspostedChannels = new Collection();
+ const channelTypes = Object.keys(ChannelTypes);
+ for (const d of crosspostedChannels) {
+ const type = channelTypes[d.type];
+ this.crosspostedChannels.set(d.id, {
+ channelID: d.id,
+ guildID: d.guild_id,
+ type: type ? type.toLowerCase() : 'unknown',
+ name: d.name,
+ });
+ }
+ }
+ } else {
+ this.crosspostedChannels = new Collection();
+ }
+ }
+
+ /**
+ * Any members that were mentioned (only in {@link TextChannel}s)
+ * <info>Order as received from the API, not as they appear in the message content</
+ * @type {?Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ if (this._members) return this._members;
+ if (!this._guild) return null;
+ this._members = new Collection();
+ this.users.forEach(user => {
+ const member = this._guild.member(user);
+ if (member) this._members.set(member.user.id, member);
+ });
+ return this._members;
+ }
+
+ /**
+ * Any channels that were mentioned
+ * <info>Order as they appear first in the message content</info>
+ * @type {Collection<Snowflake, GuildChannel>}
+ * @readonly
+ */
+ get channels() {
+ if (this._channels) return this._channels;
+ this._channels = new Collection();
+ let matches;
+ while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) {
+ const chan = this._client.channels.get(matches[1]);
+ if (chan) this._channels.set(chan.id, chan);
+ }
+ return this._channels;
+ }
+}
+
+/**
+ * Regular expression that globally matches `@everyone` and `@here`
+ * @type {RegExp}
+ */
+MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g;
+
+/**
+ * Regular expression that globally matches user mentions like `<@81440962496172032>`
+ * @type {RegExp}
+ */
+MessageMentions.USERS_PATTERN = /<@!?[0-9]+>/g;
+
+/**
+ * Regular expression that globally matches role mentions like `<@&297577916114403338>`
+ * @type {RegExp}
+ */
+MessageMentions.ROLES_PATTERN = /<@&[0-9]+>/g;
+
+/**
+ * Regular expression that globally matches channel mentions like `<#222079895583457280>`
+ * @type {RegExp}
+ */
+MessageMentions.CHANNELS_PATTERN = /<#([0-9]+)>/g;
+
+module.exports = MessageMentions;
diff --git a/node_modules/discord.js/src/structures/MessageReaction.js b/node_modules/discord.js/src/structures/MessageReaction.js
new file mode 100644
index 0000000..9109e18
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageReaction.js
@@ -0,0 +1,107 @@
+const Collection = require('../util/Collection');
+const Emoji = require('./Emoji');
+const ReactionEmoji = require('./ReactionEmoji');
+
+/**
+ * Represents a reaction to a message.
+ */
+class MessageReaction {
+ constructor(message, emoji, count, me) {
+ /**
+ * The message that this reaction refers to
+ * @type {Message}
+ */
+ this.message = message;
+
+ /**
+ * Whether the client has given this reaction
+ * @type {boolean}
+ */
+ this.me = me;
+
+ /**
+ * The number of people that have given the same reaction
+ * @type {number}
+ */
+ this.count = count || 0;
+
+ /**
+ * The users that have given this reaction, mapped by their ID
+ * @type {Collection<Snowflake, User>}
+ */
+ this.users = new Collection();
+
+ this._emoji = new ReactionEmoji(this, emoji);
+ }
+
+ /**
+ * The emoji of this reaction, either an Emoji object for known custom emojis, or a ReactionEmoji
+ * object which has fewer properties. Whatever the prototype of the emoji, it will still have
+ * `name`, `id`, `identifier` and `toString()`
+ * @type {Emoji|ReactionEmoji}
+ * @readonly
+ */
+ get emoji() {
+ if (this._emoji instanceof Emoji) return this._emoji;
+ // Check to see if the emoji has become known to the client
+ if (this._emoji.id) {
+ const emojis = this.message.client.emojis;
+ if (emojis.has(this._emoji.id)) {
+ const emoji = emojis.get(this._emoji.id);
+ this._emoji = emoji;
+ return emoji;
+ }
+ }
+ return this._emoji;
+ }
+
+ /**
+ * Removes a user from this reaction.
+ * @param {UserResolvable} [user=this.message.client.user] The user to remove the reaction of
+ * @returns {Promise<MessageReaction>}
+ */
+ remove(user = this.message.client.user) {
+ const message = this.message;
+ const userID = this.message.client.resolver.resolveUserID(user);
+ if (!userID) return Promise.reject(new Error('Couldn\'t resolve the user ID to remove from the reaction.'));
+ return message.client.rest.methods.removeMessageReaction(
+ message, this.emoji.identifier, userID
+ );
+ }
+
+ /**
+ * Removes this reaction from the message
+ * @returns {Promise<MessageReaction>}
+ */
+ removeAll() {
+ const message = this.message;
+ return message.client.rest.methods.removeMessageReactionEmoji(
+ message, this.emoji.identifier
+ );
+ }
+
+ /**
+ * Fetch all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs.
+ * @param {number} [limit=100] The maximum amount of users to fetch, defaults to 100
+ * @param {Object} [options] Options to fetch users
+ * @param {Snowflake} [options.before] Limit fetching users to those with an id lower than the supplied id
+ * @param {Snowflake} [options.after] Limit fetching users to those with an id greater than the supplied id
+ * @returns {Promise<Collection<Snowflake, User>>}
+ */
+ fetchUsers(limit = 100, { after, before } = {}) {
+ const message = this.message;
+ return message.client.rest.methods.getMessageReactionUsers(
+ message, this.emoji.identifier, { after, before, limit }
+ ).then(data => {
+ const users = new Collection();
+ for (const rawUser of data) {
+ const user = this.message.client.dataManager.newUser(rawUser);
+ this.users.set(user.id, user);
+ users.set(user.id, user);
+ }
+ return users;
+ });
+ }
+}
+
+module.exports = MessageReaction;
diff --git a/node_modules/discord.js/src/structures/NewsChannel.js b/node_modules/discord.js/src/structures/NewsChannel.js
new file mode 100644
index 0000000..bcbfbf3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/NewsChannel.js
@@ -0,0 +1,24 @@
+const TextChannel = require('./TextChannel');
+
+/**
+ * Represents a guild news channel on Discord.
+ * @extends {TextChannel}
+ */
+class NewsChannel extends TextChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'news';
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The ratelimit per user for this channel (always 0)
+ * @type {number}
+ */
+ this.rateLimitPerUser = 0;
+ }
+}
+
+module.exports = NewsChannel;
diff --git a/node_modules/discord.js/src/structures/OAuth2Application.js b/node_modules/discord.js/src/structures/OAuth2Application.js
new file mode 100644
index 0000000..710aae8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/OAuth2Application.js
@@ -0,0 +1,157 @@
+const Snowflake = require('../util/Snowflake');
+const Team = require('./Team');
+const util = require('util');
+
+/**
+ * Represents an OAuth2 Application.
+ */
+class OAuth2Application {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the application
+ * @name OAuth2Application#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the app
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the app
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The app's description
+ * @type {string}
+ */
+ this.description = data.description;
+
+ /**
+ * The app's icon hash
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The app's icon URL
+ * @type {string}
+ */
+ this.iconURL = `https://cdn.discordapp.com/app-icons/${this.id}/${this.icon}.jpg`;
+
+ /**
+ * The app's RPC origins
+ * @type {?string[]}
+ */
+ this.rpcOrigins = data.rpc_origins;
+
+ /**
+ * The app's redirect URIs
+ * @type {string[]}
+ */
+ this.redirectURIs = data.redirect_uris;
+
+ /**
+ * If this app's bot requires a code grant when using the OAuth2 flow
+ * @type {boolean}
+ */
+ this.botRequireCodeGrant = data.bot_require_code_grant;
+
+ /**
+ * If this app's bot is public
+ * @type {boolean}
+ */
+ this.botPublic = data.bot_public;
+
+ /**
+ * If this app can use rpc
+ * @type {boolean}
+ */
+ this.rpcApplicationState = data.rpc_application_state;
+
+ /**
+ * Object containing basic info about this app's bot
+ * @type {Object}
+ */
+ this.bot = data.bot;
+
+ /**
+ * The flags for the app
+ * @type {number}
+ */
+ this.flags = data.flags;
+
+ /**
+ * OAuth2 secret for the application
+ * @type {boolean}
+ */
+ this.secret = data.secret;
+
+ if (data.owner) {
+ /**
+ * The owner of this OAuth application
+ * @type {?User}
+ */
+ this.owner = this.client.dataManager.newUser(data.owner);
+ }
+
+ /**
+ * The owning team of this OAuth application
+ * <info>In v12.0.0 this property moves to `Team#owner`.</info>
+ * @type {?Team}
+ * @deprecated
+ */
+ this.team = data.team ? new Team(this.client, data.team) : null;
+ }
+
+ /**
+ * The timestamp the app was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the app was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Reset the app's secret and bot token.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {OAuth2Application}
+ * @deprecated
+ */
+ reset() {
+ return this.client.rest.methods.resetApplication(this.id);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the app name rather than the app object.
+ * @returns {string}
+ */
+ toString() {
+ return this.name;
+ }
+}
+
+OAuth2Application.prototype.reset =
+ util.deprecate(OAuth2Application.prototype.reset, 'OAuth2Application#reset: userbot methods will be removed');
+
+module.exports = OAuth2Application;
diff --git a/node_modules/discord.js/src/structures/PartialGuild.js b/node_modules/discord.js/src/structures/PartialGuild.js
new file mode 100644
index 0000000..3eb64f2
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PartialGuild.js
@@ -0,0 +1,51 @@
+/*
+{ splash: null,
+ id: '123123123',
+ icon: '123123123',
+ name: 'name' }
+*/
+
+/**
+ * Represents a guild that the client only has limited information for - e.g. from invites.
+ */
+class PartialGuild {
+ constructor(client, data) {
+ /**
+ * The client that instantiated this PartialGuild
+ * @name PartialGuild#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this guild
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of this guild
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The hash of this guild's icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The hash of the guild splash image (VIP only)
+ * @type {?string}
+ */
+ this.splash = data.splash;
+ }
+}
+
+module.exports = PartialGuild;
diff --git a/node_modules/discord.js/src/structures/PartialGuildChannel.js b/node_modules/discord.js/src/structures/PartialGuildChannel.js
new file mode 100644
index 0000000..c30c054
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PartialGuildChannel.js
@@ -0,0 +1,44 @@
+const Constants = require('../util/Constants');
+
+/*
+{ type: 0, id: '123123', name: 'heavy-testing' } }
+*/
+
+/**
+ * Represents a guild channel that the client only has limited information for - e.g. from invites.
+ */
+class PartialGuildChannel {
+ constructor(client, data) {
+ /**
+ * The client that instantiated this PartialGuildChannel
+ * @name PartialGuildChannel#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this guild channel
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of this guild channel
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The type of this guild channel - `text` or `voice`
+ * @type {string}
+ */
+ this.type = Constants.ChannelTypes.TEXT === data.type ? 'text' : 'voice';
+ }
+}
+
+module.exports = PartialGuildChannel;
diff --git a/node_modules/discord.js/src/structures/PermissionOverwrites.js b/node_modules/discord.js/src/structures/PermissionOverwrites.js
new file mode 100644
index 0000000..ebb78fb
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PermissionOverwrites.js
@@ -0,0 +1,69 @@
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a permission overwrite for a role or member in a guild channel.
+ */
+class PermissionOverwrites {
+ constructor(guildChannel, data) {
+ /**
+ * The GuildChannel this overwrite is for
+ * @name PermissionOverwrites#channel
+ * @type {GuildChannel}
+ * @readonly
+ */
+ Object.defineProperty(this, 'channel', { value: guildChannel });
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this overwrite, either a user ID or a role ID
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of this overwrite
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * The permissions that are denied for the user or role as a bitfield.
+ * @type {number}
+ */
+ this.deny = data.deny;
+
+ /**
+ * The permissions that are allowed for the user or role as a bitfield.
+ * @type {number}
+ */
+ this.allow = data.allow;
+
+ /**
+ * The permissions that are denied for the user or role.
+ * @type {Permissions}
+ * @deprecated
+ */
+ this.denied = new Permissions(data.deny).freeze();
+
+ /**
+ * The permissions that are allowed for the user or role.
+ * @type {Permissions}
+ * @deprecated
+ */
+ this.allowed = new Permissions(data.allow).freeze();
+ }
+
+ /**
+ * Delete this Permission Overwrite.
+ * @param {string} [reason] Reason for deleting this overwrite
+ * @returns {Promise<PermissionOverwrites>}
+ */
+ delete(reason) {
+ return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason);
+ }
+}
+
+module.exports = PermissionOverwrites;
diff --git a/node_modules/discord.js/src/structures/Presence.js b/node_modules/discord.js/src/structures/Presence.js
new file mode 100644
index 0000000..18cc3cc
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Presence.js
@@ -0,0 +1,301 @@
+const { ActivityFlags, Endpoints } = require('../util/Constants');
+const ReactionEmoji = require('./ReactionEmoji');
+
+/**
+ * The status of this presence:
+ * * **`online`** - user is online
+ * * **`idle`** - user is AFK
+ * * **`offline`** - user is offline or invisible
+ * * **`dnd`** - user is in Do Not Disturb
+ * @typedef {string} PresenceStatus
+ */
+
+/**
+ * The status of this presence:
+ * * **`online`** - user is online
+ * * **`idle`** - user is AFK
+ * * **`dnd`** - user is in Do Not Disturb
+ * @typedef {string} ClientPresenceStatus
+ */
+
+/**
+ * Represents a user's presence.
+ */
+class Presence {
+ constructor(data = {}, client) {
+ /**
+ * The client that instantiated this
+ * @name Presence#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.update(data);
+ }
+
+ update(data) {
+ /**
+ * The status of this presence:
+ * @type {PresenceStatus}
+ */
+ this.status = data.status || this.status || 'offline';
+
+ /**
+ * The game that the user is playing
+ * @type {?Game}
+ * @deprecated
+ */
+ this.game = data.game ? new Game(data.game, this) : null;
+
+ if (data.activities) {
+ /**
+ * The activities of this presence
+ * @type {Game[]}
+ */
+ this.activities = data.activities.map(activity => new Game(activity, this));
+ } else if (data.activity || data.game) {
+ this.activities = [new Game(data.activity || data.game, this)];
+ } else {
+ this.activities = [];
+ }
+
+ /**
+ * The devices this presence is on
+ * @type {?Object}
+ * @property {?ClientPresenceStatus} web The current presence in the web application
+ * @property {?ClientPresenceStatus} mobile The current presence in the mobile application
+ * @property {?ClientPresenceStatus} desktop The current presence in the desktop application
+ */
+ this.clientStatus = data.client_status || null;
+ }
+
+ /**
+ * Whether this presence is equal to another
+ * @param {Presence} presence The presence to compare with
+ * @returns {boolean}
+ */
+ equals(presence) {
+ return this === presence || (
+ presence &&
+ this.status === presence.status &&
+ this.activities.length === presence.activities.length &&
+ this.activities.every((activity, index) => activity.equals(presence.activities[index])) &&
+ this.clientStatus.web === presence.clientStatus.web &&
+ this.clientStatus.mobile === presence.clientStatus.mobile &&
+ this.clientStatus.desktop === presence.clientStatus.desktop
+ );
+ }
+}
+
+/**
+ * Represents a game that is part of a user's presence.
+ */
+class Game {
+ constructor(data, presence) {
+ Object.defineProperty(this, 'presence', { value: presence });
+
+ /**
+ * The name of the game being played
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The type of the game status, its possible values:
+ * - 0: Playing
+ * - 1: Streaming
+ * - 2: Listening
+ * - 3: Watching
+ * @type {number}
+ */
+ this.type = data.type;
+
+ /**
+ * If the game is being streamed, a link to the stream
+ * @type {?string}
+ */
+ this.url = data.url || null;
+
+ /**
+ * Details about the activity
+ * @type {?string}
+ */
+ this.details = data.details || null;
+
+ /**
+ * State of the activity
+ * @type {?string}
+ */
+ this.state = data.state || null;
+
+ /**
+ * Application ID associated with this activity
+ * @type {?Snowflake}
+ */
+ this.applicationID = data.application_id || null;
+
+ /**
+ * Timestamps for the activity
+ * @type {?Object}
+ * @prop {?Date} start When the activity started
+ * @prop {?Date} end When the activity will end
+ */
+ this.timestamps = data.timestamps ? {
+ start: data.timestamps.start ? new Date(Number(data.timestamps.start)) : null,
+ end: data.timestamps.end ? new Date(Number(data.timestamps.end)) : null,
+ } : null;
+
+ /**
+ * Party of the activity
+ * @type {?Object}
+ * @prop {?string} id ID of the party
+ * @prop {number[]} size Size of the party as `[current, max]`
+ */
+ this.party = data.party || null;
+
+ /**
+ * Assets for rich presence
+ * @type {?RichPresenceAssets}
+ */
+ this.assets = data.assets ? new RichPresenceAssets(this, data.assets) : null;
+
+ if (data.emoji) {
+ /**
+ * Emoji for a custom activity
+ * <warn>There is no `reaction` property for this emoji.</warn>
+ * @type {?ReactionEmoji}
+ */
+ this.emoji = new ReactionEmoji({ message: { client: this.presence.client } }, data.emoji);
+ this.emoji.reaction = null;
+ } else {
+ this.emoji = null;
+ }
+
+
+ /**
+ * Creation date of the activity
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.created_at).getTime();
+
+ this.syncID = data.sync_id;
+ this._flags = data.flags;
+ }
+
+ /**
+ * The time the activity was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Flags that describe the activity
+ * @type {ActivityFlags[]}
+ */
+ get flags() {
+ const flags = [];
+ for (const [name, flag] of Object.entries(ActivityFlags)) {
+ if ((this._flags & flag) === flag) flags.push(name);
+ }
+ return flags;
+ }
+
+ /**
+ * Whether or not the game is being streamed
+ * @type {boolean}
+ * @readonly
+ */
+ get streaming() {
+ return this.type === 1;
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the game's name instead of the Game object.
+ * @returns {string}
+ */
+ toString() {
+ return this.name;
+ }
+
+ /**
+ * Whether this game is equal to another game
+ * @param {Game} game The game to compare with
+ * @returns {boolean}
+ */
+ equals(game) {
+ return this === game || (
+ game &&
+ this.name === game.name &&
+ this.type === game.type &&
+ this.url === game.url
+ );
+ }
+}
+
+/**
+ * Assets for a rich presence
+ */
+class RichPresenceAssets {
+ constructor(game, assets) {
+ Object.defineProperty(this, 'game', { value: game });
+
+ /**
+ * Hover text for the large image
+ * @type {?string}
+ */
+ this.largeText = assets.large_text || null;
+
+ /**
+ * Hover text for the small image
+ * @type {?string}
+ */
+ this.smallText = assets.small_text || null;
+
+ /**
+ * ID of the large image asset
+ * @type {?Snowflake}
+ */
+ this.largeImage = assets.large_image || null;
+
+ /**
+ * ID of the small image asset
+ * @type {?Snowflake}
+ */
+ this.smallImage = assets.small_image || null;
+ }
+
+ /**
+ * The URL of the small image asset
+ * @type {?string}
+ * @readonly
+ */
+ get smallImageURL() {
+ if (!this.smallImage) return null;
+ return Endpoints.CDN(this.game.presence.client.options.http.cdn)
+ .AppAsset(this.game.applicationID, this.smallImage);
+ }
+
+ /**
+ * The URL of the large image asset
+ * @type {?string}
+ * @readonly
+ */
+ get largeImageURL() {
+ if (!this.largeImage) return null;
+ if (/^spotify:/.test(this.largeImage)) {
+ return `https://i.scdn.co/image/${this.largeImage.slice(8)}`;
+ } else if (/^twitch:/.test(this.largeImage)) {
+ return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${this.largeImage.slice(7)}.png`;
+ }
+ return Endpoints.CDN(this.game.presence.client.options.http.cdn)
+ .AppAsset(this.game.applicationID, this.largeImage);
+ }
+}
+
+exports.Presence = Presence;
+exports.Game = Game;
+exports.RichPresenceAssets = RichPresenceAssets;
diff --git a/node_modules/discord.js/src/structures/ReactionCollector.js b/node_modules/discord.js/src/structures/ReactionCollector.js
new file mode 100644
index 0000000..2b3235e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ReactionCollector.js
@@ -0,0 +1,89 @@
+const Collector = require('./interfaces/Collector');
+const Collection = require('../util/Collection');
+
+/**
+ * @typedef {CollectorOptions} ReactionCollectorOptions
+ * @property {number} max The maximum total amount of reactions to collect
+ * @property {number} maxEmojis The maximum number of emojis to collect
+ * @property {number} maxUsers The maximum number of users to react
+ */
+
+/**
+ * Collects reactions on messages.
+ * @extends {Collector}
+ */
+class ReactionCollector extends Collector {
+ /**
+ * @param {Message} message The message upon which to collect reactions
+ * @param {CollectorFilter} filter The filter to apply to this collector
+ * @param {ReactionCollectorOptions} [options={}] The options to apply to this collector
+ */
+ constructor(message, filter, options = {}) {
+ super(message.client, filter, options);
+
+ /**
+ * The message
+ * @type {Message}
+ */
+ this.message = message;
+
+ /**
+ * The users which have reacted
+ * @type {Collection}
+ */
+ this.users = new Collection();
+
+ /**
+ * The total number of reactions collected
+ * @type {number}
+ */
+ this.total = 0;
+
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() + 1);
+ this.client.on('messageReactionAdd', this.listener);
+
+ this.on('fullCollect', (reaction, user) => {
+ this.users.set(user.id, user);
+ this.total++;
+ });
+ }
+
+ /**
+ * Handle an incoming reaction for possible collection.
+ * @param {MessageReaction} reaction The reaction to possibly collect
+ * @returns {?{key: Snowflake, value: MessageReaction}}
+ * @private
+ */
+ handle(reaction) {
+ if (reaction.message.id !== this.message.id) return null;
+ return {
+ key: reaction.emoji.id || reaction.emoji.name,
+ value: reaction,
+ };
+ }
+
+ /**
+ * Check after collection to see if the collector is done.
+ * @param {MessageReaction} reaction The reaction that was collected
+ * @param {User} user The user that reacted
+ * @returns {?string} Reason to end the collector, if any
+ * @private
+ */
+ postCheck() {
+ if (this.options.max && this.total >= this.options.max) return 'limit';
+ if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit';
+ if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit';
+ return null;
+ }
+
+ /**
+ * Remove event listeners.
+ * @private
+ */
+ cleanup() {
+ this.client.removeListener('messageReactionAdd', this.listener);
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() - 1);
+ }
+}
+
+module.exports = ReactionCollector;
diff --git a/node_modules/discord.js/src/structures/ReactionEmoji.js b/node_modules/discord.js/src/structures/ReactionEmoji.js
new file mode 100644
index 0000000..9f7597e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ReactionEmoji.js
@@ -0,0 +1,98 @@
+const Constants = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a limited emoji set used for both custom and unicode emojis. Custom emojis
+ * will use this class opposed to the Emoji class when the client doesn't know enough
+ * information about them.
+ */
+class ReactionEmoji {
+ constructor(reaction, emoji) {
+ /**
+ * The client that instantiated this object
+ * @name ReactionEmoji#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: reaction.message.client });
+
+ /**
+ * The message reaction this emoji refers to
+ * @type {MessageReaction}
+ */
+ this.reaction = reaction;
+
+ /**
+ * The name of this reaction emoji
+ * @type {string}
+ */
+ this.name = emoji.name;
+
+ /**
+ * The ID of this reaction emoji
+ * @type {?Snowflake}
+ */
+ this.id = emoji.id;
+
+ /**
+ * Whether this reaction emoji is animated
+ * @type {boolean}
+ */
+ this.animated = emoji.animated || false;
+ }
+
+ /**
+ * The timestamp the reaction emoji was created at, or null if unicode
+ * @type {?number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ if (!this.id) return null;
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the reaction emoji was created, or null if unicode
+ * @type {?Date}
+ * @readonly
+ */
+ get createdAt() {
+ if (!this.id) return null;
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The URL to the reaction emoji file, or null if unicode
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ if (!this.id) return null;
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).Emoji(this.id, this.animated ? 'gif' : 'png');
+ }
+
+ /**
+ * The identifier of this emoji, used for message reactions
+ * @type {string}
+ * @readonly
+ */
+ get identifier() {
+ if (this.id) return `${this.name}:${this.id}`;
+ return encodeURIComponent(this.name);
+ }
+
+ /**
+ * Creates the text required to form a graphical emoji on Discord.
+ * @example
+ * // Send the emoji used in a reaction to the channel the reaction is part of
+ * reaction.message.channel.send(`The emoji used is ${reaction.emoji}`);
+ * @returns {string}
+ */
+ toString() {
+ if (!this.id) return this.name;
+
+ return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;
+ }
+}
+
+module.exports = ReactionEmoji;
diff --git a/node_modules/discord.js/src/structures/RichEmbed.js b/node_modules/discord.js/src/structures/RichEmbed.js
new file mode 100644
index 0000000..5ccb22d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/RichEmbed.js
@@ -0,0 +1,331 @@
+const Attachment = require('./Attachment');
+const MessageEmbed = require('./MessageEmbed');
+const util = require('../util/Util');
+let ClientDataResolver;
+
+/**
+ * A rich embed to be sent with a message with a fluent interface for creation.
+ * @param {Object} [data] Data to set in the rich embed
+ */
+class RichEmbed {
+ constructor(data = {}) {
+ /**
+ * Title for this Embed
+ * @type {string}
+ */
+ this.title = data.title;
+
+ /**
+ * Description for this Embed
+ * @type {string}
+ */
+ this.description = data.description;
+
+ /**
+ * URL for this Embed
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * Color for this Embed
+ * @type {number}
+ */
+ this.color = data.color;
+
+ /**
+ * Author for this Embed
+ * @type {Object}
+ */
+ this.author = data.author;
+
+ /**
+ * Timestamp for this Embed
+ * @type {Date}
+ */
+ this.timestamp = data.timestamp;
+
+ /**
+ * Fields for this Embed
+ * @type {Object[]}
+ */
+ this.fields = data.fields || [];
+
+ /**
+ * Thumbnail for this Embed
+ * @type {Object}
+ */
+ this.thumbnail = data.thumbnail;
+
+ /**
+ * Image for this Embed
+ * @type {Object}
+ */
+ this.image = data.image;
+
+ /**
+ * Footer for this Embed
+ * @type {Object}
+ */
+ this.footer = data.footer;
+
+ /**
+ * File to upload alongside this Embed
+ * @type {FileOptions|string|Attachment}
+ */
+ this.file = data.file;
+
+ /**
+ * The files to upload alongside this Embed
+ * @type {Array<FileOptions|string|Attachment>}
+ */
+ this.files = [];
+ }
+
+ /**
+ * Sets the title of this embed.
+ * @param {StringResolvable} title The title
+ * @returns {RichEmbed} This embed
+ */
+ setTitle(title) {
+ title = util.resolveString(title);
+ if (title.length > 256) throw new RangeError('RichEmbed titles may not exceed 256 characters.');
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * Sets the description of this embed.
+ * @param {StringResolvable} description The description
+ * @returns {RichEmbed} This embed
+ */
+ setDescription(description) {
+ description = util.resolveString(description);
+ if (description.length > 2048) throw new RangeError('RichEmbed descriptions may not exceed 2048 characters.');
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Sets the URL of this embed.
+ * @param {string} url The URL
+ * @returns {RichEmbed} This embed
+ */
+ setURL(url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Sets the color of this embed.
+ * @param {ColorResolvable} color The color of the embed
+ * @returns {RichEmbed} This embed
+ */
+ setColor(color) {
+ if (!ClientDataResolver) ClientDataResolver = require('../client/ClientDataResolver');
+ this.color = ClientDataResolver.resolveColor(color);
+ return this;
+ }
+
+ /**
+ * Sets the author of this embed.
+ * @param {StringResolvable} name The name of the author
+ * @param {string} [icon] The icon URL of the author
+ * @param {string} [url] The URL of the author
+ * @returns {RichEmbed} This embed
+ */
+ setAuthor(name, icon, url) {
+ this.author = { name: util.resolveString(name), icon_url: icon, url };
+ return this;
+ }
+
+ /**
+ * Sets the timestamp of this embed.
+ * @param {Date|number} [timestamp=Date.now()] The timestamp or date
+ * @returns {RichEmbed} This embed
+ */
+ setTimestamp(timestamp = Date.now()) {
+ if (timestamp instanceof Date) timestamp = timestamp.getTime();
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * Adds a field to the embed (max 25).
+ * @param {StringResolvable} name The name of the field
+ * @param {StringResolvable} value The value of the field
+ * @param {boolean} [inline=false] Set the field to display inline
+ * @returns {RichEmbed} This embed
+ */
+ addField(name, value, inline = false) {
+ if (this.fields.length >= 25) throw new RangeError('RichEmbeds may not exceed 25 fields.');
+ this.fields.push(this.constructor.normalizeField(name, value, inline));
+ return this;
+ }
+
+ /**
+ * Convenience function for `<RichEmbed>.addField('\u200B', '\u200B', inline)`.
+ * @param {boolean} [inline=false] Set the field to display inline
+ * @returns {RichEmbed} This embed
+ */
+ addBlankField(inline = false) {
+ return this.addField('\u200B', '\u200B', inline);
+ }
+
+ /**
+ * @typedef {Object} EmbedField
+ * @property {string} name The name of this field
+ * @property {string} value The value of this field
+ * @property {boolean} inline If this field will be displayed inline
+ */
+
+ /**
+ * @typedef {Object} EmbedFieldData
+ * @property {StringResolvable} name The name of this field
+ * @property {StringResolvable} value The value of this field
+ * @property {boolean} [inline=false] If this field will be displayed inline
+ */
+
+ /**
+ * Removes, replaces, and inserts fields in the embed (max 25).
+ * @param {number} index The index to start at
+ * @param {number} deleteCount The number of fields to remove
+ * @param {...EmbedFieldData} [fields] The replacing field objects
+ * @returns {RichEmbed}
+ */
+ spliceFields(index, deleteCount, ...fields) {
+ if (fields) {
+ const mapper = ({ name, value, inline }) => this.constructor.normalizeField(name, value, inline);
+ this.fields.splice(index, deleteCount, ...fields.map(mapper));
+ } else {
+ this.fields.splice(index, deleteCount);
+ }
+ return this;
+ }
+
+ /**
+ * Set the thumbnail of this embed.
+ * @param {string} url The URL of the thumbnail
+ * @returns {RichEmbed} This embed
+ */
+ setThumbnail(url) {
+ this.thumbnail = { url };
+ return this;
+ }
+
+ /**
+ * Set the image of this embed.
+ * @param {string} url The URL of the image
+ * @returns {RichEmbed} This embed
+ */
+ setImage(url) {
+ this.image = { url };
+ return this;
+ }
+
+ /**
+ * Sets the footer of this embed.
+ * @param {StringResolvable} text The text of the footer
+ * @param {string} [icon] The icon URL of the footer
+ * @returns {RichEmbed} This embed
+ */
+ setFooter(text, icon) {
+ text = util.resolveString(text);
+ if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.');
+ this.footer = { text, icon_url: icon };
+ return this;
+ }
+
+ /**
+ * Sets the file to upload alongside the embed. This file can be accessed via `attachment://fileName.extension` when
+ * setting an embed image or author/footer icons. Only one file may be attached.
+ * @param {FileOptions|string|Attachment} file Local path or URL to the file to attach,
+ * or valid FileOptions for a file to attach
+ * @returns {RichEmbed} This embed
+ */
+ attachFile(file) {
+ if (this.file) throw new RangeError('You may not upload more than one file at once.');
+ if (file instanceof Attachment) file = file.file;
+ this.file = file;
+ return this;
+ }
+
+ /**
+ * Sets the files to upload alongside the embed. A file can be accessed via `attachment://fileName.extension` when
+ * setting an embed image or author/footer icons. Multiple files can be attached.
+ * @param {Array<FileOptions|string|Attachment>} files Files to attach
+ * @returns {RichEmbed}
+ */
+ attachFiles(files) {
+ files = files.map(file => file instanceof Attachment ? file.file : file);
+ this.files = this.files.concat(files);
+ return this;
+ }
+
+ /**
+ * The accumulated length for the embed title, description, fields, author and footer text
+ * @type {number}
+ * @readonly
+ */
+ get length() {
+ return (
+ (this.title ? this.title.length : 0) +
+ (this.description ? this.description.length : 0) +
+ (this.fields.length >= 1 ? this.fields.reduce((prev, curr) =>
+ prev + curr.name.length + curr.value.length, 0) : 0) +
+ (this.footer ? this.footer.text.length : 0) +
+ (this.author ? this.author.name.length : 0));
+ }
+
+ /**
+ * Transforms the embed to a plain object.
+ * @returns {Object} The raw data of this embed
+ */
+ toJSON() {
+ return {
+ title: this.title,
+ type: 'rich',
+ description: this.description,
+ url: this.url,
+ timestamp: this.timestamp ? new Date(this.timestamp) : null,
+ color: this.color,
+ fields: this.fields ?
+ this.fields.map(field => ({ name: field.name, value: field.value, inline: field.inline })) :
+ null,
+ thumbnail: this.thumbnail ? {
+ url: this.thumbnail.url,
+ } : null,
+ image: this.image ? {
+ url: this.image.url,
+ } : null,
+ author: this.author ? {
+ name: this.author.name,
+ url: this.author.url,
+ icon_url: this.author instanceof MessageEmbed.Author ? this.author.iconURL : this.author.icon_url,
+ } : null,
+ footer: this.footer ? {
+ text: this.footer.text,
+ icon_url: this.footer instanceof MessageEmbed.Footer ? this.footer.iconURL : this.footer.icon_url,
+ } : null,
+ };
+ }
+
+ /**
+ * Normalizes field input and resolves strings.
+ * @param {StringResolvable} name The name of the field
+ * @param {StringResolvable} value The value of the field
+ * @param {boolean} [inline=false] Set the field to display inline
+ * @returns {EmbedField}
+ */
+ static normalizeField(name, value, inline = false) {
+ name = util.resolveString(name);
+ if (name.length > 256) throw new RangeError('RichEmbed field names may not exceed 256 characters.');
+ if (!/\S/.test(name)) throw new RangeError('RichEmbed field names may not be empty.');
+ value = util.resolveString(value);
+ if (value.length > 1024) throw new RangeError('RichEmbed field values may not exceed 1024 characters.');
+ if (!/\S/.test(value)) throw new RangeError('RichEmbed field values may not be empty.');
+ return { name, value, inline };
+ }
+}
+
+module.exports = RichEmbed;
diff --git a/node_modules/discord.js/src/structures/Role.js b/node_modules/discord.js/src/structures/Role.js
new file mode 100644
index 0000000..056cf98
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Role.js
@@ -0,0 +1,376 @@
+const Snowflake = require('../util/Snowflake');
+const Permissions = require('../util/Permissions');
+const util = require('util');
+
+/**
+ * Represents a role on Discord.
+ */
+class Role {
+ constructor(guild, data) {
+ /**
+ * The client that instantiated the role
+ * @name Role#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: guild.client });
+
+ /**
+ * The guild that the role belongs to
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * Whether the role has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the role (unique to the guild it is part of)
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the role
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The base 10 color of the role
+ * @type {number}
+ */
+ this.color = data.color;
+
+ /**
+ * If true, users that are part of this role will appear in a separate category in the users list
+ * @type {boolean}
+ */
+ this.hoist = data.hoist;
+
+ /**
+ * The position of the role from the API
+ * @type {number}
+ */
+ this.position = data.position;
+
+ /**
+ * The permissions bitfield of the role
+ * @type {number}
+ */
+ this.permissions = data.permissions;
+
+ /**
+ * Whether or not the role is managed by an external service
+ * @type {boolean}
+ */
+ this.managed = data.managed;
+
+ /**
+ * Whether or not the role can be mentioned by anyone
+ * @type {boolean}
+ */
+ this.mentionable = data.mentionable;
+ }
+
+ /**
+ * The timestamp the role was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the role was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The hexadecimal version of the role color, with a leading hashtag
+ * @type {string}
+ * @readonly
+ */
+ get hexColor() {
+ let col = this.color.toString(16);
+ while (col.length < 6) col = `0${col}`;
+ return `#${col}`;
+ }
+
+ /**
+ * The cached guild members that have this role
+ * @type {Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ return this.guild.members.filter(m => m.roles.has(this.id));
+ }
+
+ /**
+ * Whether the role is editable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get editable() {
+ if (this.managed) return false;
+ const clientMember = this.guild.member(this.client.user);
+ if (!clientMember.permissions.has(Permissions.FLAGS.MANAGE_ROLES_OR_PERMISSIONS)) return false;
+ return clientMember.highestRole.comparePositionTo(this) > 0;
+ }
+
+ /**
+ * The position of the role in the role manager
+ * @type {number}
+ * @readonly
+ */
+ get calculatedPosition() {
+ const sorted = this.guild._sortedRoles;
+ return sorted.array().indexOf(sorted.get(this.id));
+ }
+
+ /**
+ * Get an object mapping permission names to whether or not the role enables that permission.
+ * @returns {Object<string, boolean>}
+ * @example
+ * // Print the serialized role permissions
+ * console.log(role.serialize());
+ */
+ serialize() {
+ return new Permissions(this.permissions).serialize();
+ }
+
+ /**
+ * Checks if the role has a permission.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
+ * **(deprecated)**
+ * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override
+ * (takes priority over `explicit`)
+ * @returns {boolean}
+ * @example
+ * // See if a role can ban a member
+ * if (role.hasPermission('BAN_MEMBERS')) {
+ * console.log('This role can ban members');
+ * } else {
+ * console.log('This role can\'t ban members');
+ * }
+ */
+ hasPermission(permission, explicit = false, checkAdmin) {
+ return new Permissions(this.permissions).has(
+ permission, typeof checkAdmin !== 'undefined' ? checkAdmin : !explicit
+ );
+ }
+
+ /**
+ * Checks if the role has all specified permissions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permissions
+ * @returns {boolean}
+ * @deprecated
+ */
+ hasPermissions(permissions, explicit = false) {
+ return new Permissions(this.permissions).has(permissions, !explicit);
+ }
+
+ /**
+ * Compares this role's position to another role's.
+ * @param {Role} role Role to compare to this one
+ * @returns {number} Negative number if this role's position is lower (other role's is higher),
+ * positive number if this one is higher (other's is lower), 0 if equal
+ */
+ comparePositionTo(role) {
+ return this.constructor.comparePositions(this, role);
+ }
+
+ /**
+ * The data for a role.
+ * @typedef {Object} RoleData
+ * @property {string} [name] The name of the role
+ * @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number
+ * @property {boolean} [hoist] Whether or not the role should be hoisted
+ * @property {number} [position] The position of the role
+ * @property {PermissionResolvable|number} [permissions] The permissions of the role
+ * @property {boolean} [mentionable] Whether or not the role should be mentionable
+ */
+
+ /**
+ * Edits the role.
+ * @param {RoleData} data The new data for the role
+ * @param {string} [reason] The reason for editing this role
+ * @returns {Promise<Role>}
+ * @example
+ * // Edit name of a role
+ * role.edit({ name: 'New Name' })
+ * .then(updated => console.log(`Edited role name from ${role.name} to ${updated.name}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateGuildRole(this, data, reason);
+ }
+
+ /**
+ * Set a new name for the role.
+ * @param {string} name The new name of the role
+ * @param {string} [reason] Reason for changing the role's name
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the name of the role
+ * role.setName('New Name')
+ * .then(updated => console.log(`Edited role name from ${role.name} to ${updated.name}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Set a new color for the role.
+ * @param {ColorResolvable} color The color of the role
+ * @param {string} [reason] Reason for changing the role's color
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the color of a role
+ * role.setColor('#FF0000')
+ * .then(updated => console.log(`Set color of role to ${role.color}`))
+ * .catch(console.error);
+ */
+ setColor(color, reason) {
+ return this.edit({ color }, reason);
+ }
+
+ /**
+ * Set whether or not the role should be hoisted.
+ * @param {boolean} hoist Whether or not to hoist the role
+ * @param {string} [reason] Reason for setting whether or not the role should be hoisted
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the hoist of the role
+ * role.setHoist(true)
+ * .then(updated => console.log(`Role hoisted: ${updated.hoist}`))
+ * .catch(console.error);
+ */
+ setHoist(hoist, reason) {
+ return this.edit({ hoist }, reason);
+ }
+
+ /**
+ * Set the position of the role.
+ * @param {number} position The position of the role
+ * @param {boolean} [relative=false] Move the position relative to its current value
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the position of the role
+ * role.setPosition(1)
+ * .then(updated => console.log(`Role position: ${updated.position}`))
+ * .catch(console.error);
+ */
+ setPosition(position, relative) {
+ return this.guild.setRolePosition(this, position, relative).then(() => this);
+ }
+
+ /**
+ * Set the permissions of the role.
+ * @param {PermissionResolvable} permissions The permissions of the role
+ * @param {string} [reason] Reason for changing the role's permissions
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the permissions of the role
+ * role.setPermissions(['KICK_MEMBERS', 'BAN_MEMBERS'])
+ * .then(updated => console.log(`Updated permissions to ${updated.permissions.bitfield}`))
+ * .catch(console.error);
+ * @example
+ * // Remove all permissions from a role
+ * role.setPermissions(0)
+ * .then(updated => console.log(`Updated permissions to ${updated.permissions.bitfield}`))
+ * .catch(console.error);
+ */
+ setPermissions(permissions, reason) {
+ return this.edit({ permissions }, reason);
+ }
+
+ /**
+ * Set whether this role is mentionable.
+ * @param {boolean} mentionable Whether this role should be mentionable
+ * @param {string} [reason] Reason for setting whether or not this role should be mentionable
+ * @returns {Promise<Role>}
+ * @example
+ * // Make the role mentionable
+ * role.setMentionable(true, 'Role needs to be pinged')
+ * .then(updated => console.log(`Role mentionable: ${updated.mentionable}`))
+ * .catch(console.error);
+ */
+ setMentionable(mentionable, reason) {
+ return this.edit({ mentionable }, reason);
+ }
+
+ /**
+ * Deletes the role.
+ * @param {string} [reason] Reason for deleting the role
+ * @returns {Promise<Role>}
+ * @example
+ * // Delete a role
+ * role.delete('The role needed to go')
+ * .then(deleted => console.log(`Deleted role ${deleted.name}`))
+ * .catch(console.error);
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteGuildRole(this, reason);
+ }
+
+ /**
+ * Whether this role equals another role. It compares all properties, so for most operations
+ * it is advisable to just compare `role.id === role2.id` as it is much faster and is often
+ * what most users need.
+ * @param {Role} role Role to compare with
+ * @returns {boolean}
+ */
+ equals(role) {
+ return role &&
+ this.id === role.id &&
+ this.name === role.name &&
+ this.color === role.color &&
+ this.hoist === role.hoist &&
+ this.position === role.position &&
+ this.permissions === role.permissions &&
+ this.managed === role.managed;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the role mention rather than the Role object.
+ * @returns {string}
+ */
+ toString() {
+ if (this.id === this.guild.id) return '@everyone';
+ return `<@&${this.id}>`;
+ }
+
+ /**
+ * Compares the positions of two roles.
+ * @param {Role} role1 First role to compare
+ * @param {Role} role2 Second role to compare
+ * @returns {number} Negative number if the first role's position is lower (second role's is higher),
+ * positive number if the first's is higher (second's is lower), 0 if equal
+ */
+ static comparePositions(role1, role2) {
+ if (role1.position === role2.position) return role2.id - role1.id;
+ return role1.position - role2.position;
+ }
+}
+
+Role.prototype.hasPermissions = util
+ .deprecate(Role.prototype.hasPermissions,
+ 'Role#hasPermissions is deprecated - use Role#hasPermission instead, it now takes an array');
+
+module.exports = Role;
diff --git a/node_modules/discord.js/src/structures/StoreChannel.js b/node_modules/discord.js/src/structures/StoreChannel.js
new file mode 100644
index 0000000..8985c0f
--- /dev/null
+++ b/node_modules/discord.js/src/structures/StoreChannel.js
@@ -0,0 +1,25 @@
+const GuildChannel = require('./GuildChannel');
+
+/**
+ * Represents a guild store channel on Discord.
+ * @extends {GuildChannel}
+ */
+class StoreChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'store';
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * If the guild considers this channel NSFW
+ * @type {boolean}
+ * @readonly
+ */
+ this.nsfw = data.nsfw;
+ }
+}
+
+module.exports = StoreChannel;
diff --git a/node_modules/discord.js/src/structures/Team.js b/node_modules/discord.js/src/structures/Team.js
new file mode 100644
index 0000000..e0dc0e0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Team.js
@@ -0,0 +1,109 @@
+const Snowflake = require('../util/Snowflake');
+const Collection = require('../util/Collection');
+const TeamMember = require('./TeamMember');
+const Constants = require('../util/Constants');
+
+/**
+ * Represents a Client OAuth2 Application Team.
+ */
+class Team {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the team
+ * @name Team#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The ID of the Team
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the Team
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The Team's icon hash
+ * @type {?string}
+ */
+ this.icon = data.icon || null;
+
+ /**
+ * The Team's owner id
+ * @type {?string}
+ */
+ this.ownerID = data.owner_user_id || null;
+
+ /**
+ * The Team's members
+ * @type {Collection<Snowflake, TeamMember>}
+ */
+ this.members = new Collection();
+
+ for (const memberData of data.members) {
+ const member = new TeamMember(this.client, this, memberData);
+ this.members.set(member.id, member);
+ }
+ }
+
+ /**
+ * The owner of the team
+ * @type {?TeamMember}
+ * @readonly
+ */
+ get owner() {
+ return this.members.get(this.ownerID) || null;
+ }
+
+ /**
+ * The timestamp the team was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the team was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * A link to the teams's icon.
+ * @type {?string}
+ * @readonly
+ */
+ get iconURL() {
+ if (!this.icon) return null;
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).TeamIcon(this.id, this.icon);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the Team's name instead of the
+ * Team object.
+ * @returns {string}
+ * @example
+ * // Logs: Team name: My Team
+ * console.log(`Team name: ${team}`);
+ */
+ toString() {
+ return this.name;
+ }
+}
+
+module.exports = Team;
diff --git a/node_modules/discord.js/src/structures/TeamMember.js b/node_modules/discord.js/src/structures/TeamMember.js
new file mode 100644
index 0000000..5dbc2ce
--- /dev/null
+++ b/node_modules/discord.js/src/structures/TeamMember.js
@@ -0,0 +1,67 @@
+const { MembershipStates } = require('../util/Constants');
+
+/**
+ * Represents a Client OAuth2 Application Team Member.
+ */
+class TeamMember {
+ constructor(client, team, data) {
+ /**
+ * The client that instantiated the Team Member
+ * @name TeamMember#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The Team this member is part of
+ * @type {Team}
+ */
+ this.team = team;
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The permissions this Team Member has with regard to the team
+ * @type {string[]}
+ */
+ this.permissions = data.permissions;
+
+ /**
+ * The membership state this Team Member has with regard to the team
+ * @type {MembershipStates}
+ */
+ this.membershipState = MembershipStates[data.membership_state];
+
+ /**
+ * The user for this Team Member
+ * @type {User}
+ */
+ this.user = this.client.dataManager.newUser(data.user);
+ }
+
+ /**
+ * The ID of the Team Member
+ * @type {Snowflake}
+ * @readonly
+ */
+ get id() {
+ return this.user.id;
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the team members's mention instead of the
+ * TeamMember object.
+ * @returns {string}
+ * @example
+ * // Logs: Team Member's mention: <@123456789>
+ * console.log(`Team Member's mention: ${teamMember}`);
+ */
+ toString() {
+ return this.user.toString();
+ }
+}
+
+module.exports = TeamMember;
diff --git a/node_modules/discord.js/src/structures/TextChannel.js b/node_modules/discord.js/src/structures/TextChannel.js
new file mode 100644
index 0000000..d16dac8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/TextChannel.js
@@ -0,0 +1,154 @@
+const GuildChannel = require('./GuildChannel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Collection = require('../util/Collection');
+
+/**
+ * Represents a guild text channel on Discord.
+ * @extends {GuildChannel}
+ * @implements {TextBasedChannel}
+ */
+class TextChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'text';
+ /**
+ * A collection containing the messages sent to this channel
+ * @type {Collection<Snowflake, Message>}
+ */
+ this.messages = new Collection();
+ this._typing = new Map();
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The topic of the text channel
+ * @type {?string}
+ */
+ this.topic = data.topic;
+
+ /**
+ * If the Discord considers this channel NSFW
+ * @type {boolean}
+ * @readonly
+ */
+ this.nsfw = Boolean(data.nsfw);
+
+ /**
+ * The ID of the last message sent in this channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = data.last_message_id;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+
+ /**
+ * The ratelimit per user for this channel in seconds
+ * @type {number}
+ */
+ this.rateLimitPerUser = data.rate_limit_per_user || 0;
+ }
+
+ /**
+ * A collection of members that can see this channel, mapped by their ID
+ * @type {Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ const members = new Collection();
+ for (const member of this.guild.members.values()) {
+ if (this.permissionsFor(member).has('READ_MESSAGES')) {
+ members.set(member.id, member);
+ }
+ }
+ return members;
+ }
+
+ /**
+ * Fetch all webhooks for the channel.
+ * @returns {Promise<Collection<Snowflake, Webhook>>}
+ * @example
+ * // Fetch webhooks
+ * channel.fetchWebhooks()
+ * .then(hooks => console.log(`This channel has ${hooks.size} hooks`))
+ * .catch(console.error);
+ */
+ fetchWebhooks() {
+ return this.client.rest.methods.getChannelWebhooks(this);
+ }
+
+ /**
+ * Sets whether this channel is flagged as NSFW.
+ * @param {boolean} nsfw Whether the channel should be considered NSFW
+ * @param {string} [reason] Reason for changing the channel's NSFW flag
+ * @returns {Promise<TextChannel>}
+ */
+ setNSFW(nsfw, reason) {
+ return this.edit({ nsfw }, reason);
+ }
+
+ /**
+ * Create a webhook for the channel.
+ * @param {string} name The name of the webhook
+ * @param {BufferResolvable|Base64Resolvable} [avatar] The avatar for the webhook
+ * @param {string} [reason] Reason for creating this webhook
+ * @returns {Promise<Webhook>} webhook The created webhook
+ * @example
+ * channel.createWebhook('Snek', 'https://i.imgur.com/mI8XcpG.jpg')
+ * .then(webhook => console.log(`Created webhook ${webhook}`))
+ * .catch(console.error)
+ */
+ createWebhook(name, avatar, reason) {
+ if (typeof avatar === 'string' && avatar.startsWith('data:')) {
+ return this.client.rest.methods.createWebhook(this, name, avatar, reason);
+ } else {
+ return this.client.resolver.resolveImage(avatar).then(data =>
+ this.client.rest.methods.createWebhook(this, name, data, reason)
+ );
+ }
+ }
+
+ /**
+ * Sets the rate limit per user for this channel.
+ * @param {number} rateLimitPerUser The new ratelimit in seconds
+ * @param {string} [reason] Reason for changing the channel's ratelimits
+ * @returns {Promise<TextChannel>}
+ */
+ setRateLimitPerUser(rateLimitPerUser, reason) {
+ return this.edit({ rateLimitPerUser }, reason);
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastMessage() {}
+ get lastPinAt() {}
+ send() { }
+ sendMessage() { }
+ sendEmbed() { }
+ sendFile() { }
+ sendFiles() { }
+ sendCode() { }
+ fetchMessage() { }
+ fetchMessages() { }
+ fetchPinnedMessages() { }
+ search() { }
+ startTyping() { }
+ stopTyping() { }
+ get typing() { }
+ get typingCount() { }
+ createCollector() { }
+ createMessageCollector() { }
+ awaitMessages() { }
+ bulkDelete() { }
+ acknowledge() { }
+ _cacheMessage() { }
+}
+
+TextBasedChannel.applyToClass(TextChannel, true);
+
+module.exports = TextChannel;
diff --git a/node_modules/discord.js/src/structures/User.js b/node_modules/discord.js/src/structures/User.js
new file mode 100644
index 0000000..86c2bc0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/User.js
@@ -0,0 +1,336 @@
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Constants = require('../util/Constants');
+const Presence = require('./Presence').Presence;
+const Snowflake = require('../util/Snowflake');
+const util = require('util');
+
+/**
+ * Represents a user on Discord.
+ * @implements {TextBasedChannel}
+ */
+class User {
+ constructor(client, data) {
+ /**
+ * The client that created the instance of the user
+ * @name User#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the user
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The username of the user
+ * @type {string}
+ */
+ this.username = data.username;
+
+ /**
+ * A discriminator based on username for the user
+ * @type {string}
+ */
+ this.discriminator = data.discriminator;
+
+ /**
+ * The ID of the user's avatar
+ * @type {string}
+ */
+ this.avatar = data.avatar;
+
+ /**
+ * Whether or not the user is a bot
+ * @type {boolean}
+ */
+ this.bot = Boolean(data.bot);
+
+ /**
+ * Whether this is an Official Discord System user (part of the urgent message system)
+ * @type {?boolean}
+ * @name User#system
+ */
+ if (typeof data.system !== 'undefined') this.system = Boolean(data.system);
+
+ /**
+ * The ID of the last message sent by the user, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The Message object of the last message sent by the user, if one was sent
+ * @type {?Message}
+ */
+ this.lastMessage = null;
+ }
+
+ patch(data) {
+ for (const prop of ['id', 'username', 'discriminator', 'avatar', 'bot']) {
+ if (typeof data[prop] !== 'undefined') this[prop] = data[prop];
+ }
+ if (data.token) this.client.token = data.token;
+ }
+
+ /**
+ * The timestamp the user was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the user was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The presence of this user
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ if (this.client.presences.has(this.id)) return this.client.presences.get(this.id);
+ for (const guild of this.client.guilds.values()) {
+ if (guild.presences.has(this.id)) return guild.presences.get(this.id);
+ }
+ return new Presence(undefined, this.client);
+ }
+
+ /**
+ * A link to the user's avatar
+ * @type {?string}
+ * @readonly
+ */
+ get avatarURL() {
+ if (!this.avatar) return null;
+ return Constants.Endpoints.User(this).Avatar(this.client.options.http.cdn, this.avatar);
+ }
+
+ /**
+ * A link to the user's default avatar
+ * @type {string}
+ * @readonly
+ */
+ get defaultAvatarURL() {
+ const avatars = Object.keys(Constants.DefaultAvatars);
+ const avatar = avatars[this.discriminator % avatars.length];
+ return Constants.Endpoints.CDN(this.client.options.http.host).Asset(`${Constants.DefaultAvatars[avatar]}.png`);
+ }
+
+ /**
+ * A link to the user's avatar if they have one. Otherwise a link to their default avatar will be returned
+ * @type {string}
+ * @readonly
+ */
+ get displayAvatarURL() {
+ return this.avatarURL || this.defaultAvatarURL;
+ }
+
+ /**
+ * The Discord "tag" (e.g. `hydrabolt#0001`) for this user
+ * @type {string}
+ * @readonly
+ */
+ get tag() {
+ return `${this.username}#${this.discriminator}`;
+ }
+
+ /**
+ * The note that is set for the user
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?string}
+ * @readonly
+ * @deprecated
+ */
+ get note() {
+ return this.client.user.notes.get(this.id) || null;
+ }
+
+ /**
+ * Check whether the user is typing in a channel.
+ * @param {ChannelResolvable} channel The channel to check in
+ * @returns {boolean}
+ */
+ typingIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ return channel._typing.has(this.id);
+ }
+
+ /**
+ * Get the time that the user started typing.
+ * @param {ChannelResolvable} channel The channel to get the time in
+ * @returns {?Date}
+ */
+ typingSinceIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null;
+ }
+
+ /**
+ * Get the amount of time the user has been typing in a channel for (in milliseconds), or -1 if they're not typing.
+ * @param {ChannelResolvable} channel The channel to get the time in
+ * @returns {number}
+ */
+ typingDurationIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1;
+ }
+
+ /**
+ * The DM between the client's user and this user
+ * @type {?DMChannel}
+ * @readonly
+ */
+ get dmChannel() {
+ return this.client.channels.find(c => c.type === 'dm' && c.recipient.id === this.id);
+ }
+
+ /**
+ * Creates a DM channel between the client and the user.
+ * @returns {Promise<DMChannel>}
+ */
+ createDM() {
+ return this.client.rest.methods.createDM(this);
+ }
+
+ /**
+ * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
+ * @returns {Promise<DMChannel>}
+ */
+ deleteDM() {
+ return this.client.rest.methods.deleteChannel(this);
+ }
+
+ /**
+ * Sends a friend request to the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ addFriend() {
+ return this.client.rest.methods.addFriend(this);
+ }
+
+ /**
+ * Removes the user from your friends.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ removeFriend() {
+ return this.client.rest.methods.removeFriend(this);
+ }
+
+ /**
+ * Blocks the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ block() {
+ return this.client.rest.methods.blockUser(this);
+ }
+
+ /**
+ * Unblocks the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ unblock() {
+ return this.client.rest.methods.unblockUser(this);
+ }
+
+ /**
+ * Get the profile of the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<UserProfile>}
+ * @deprecated
+ */
+ fetchProfile() {
+ return this.client.rest.methods.fetchUserProfile(this);
+ }
+
+ /**
+ * Sets a note for the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {string} note The note to set for the user
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ setNote(note) {
+ return this.client.rest.methods.setNote(this, note);
+ }
+
+ /**
+ * Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags.
+ * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
+ * @param {User} user User to compare with
+ * @returns {boolean}
+ */
+ equals(user) {
+ let equal = user &&
+ this.id === user.id &&
+ this.username === user.username &&
+ this.discriminator === user.discriminator &&
+ this.avatar === user.avatar &&
+ this.bot === Boolean(user.bot);
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the user's mention instead of the User object.
+ * @returns {string}
+ * @example
+ * // logs: Hello from <@123456789>!
+ * console.log(`Hello from ${user}!`);
+ */
+ toString() {
+ return `<@${this.id}>`;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendCode() {}
+}
+
+TextBasedChannel.applyToClass(User);
+
+User.prototype.block =
+ util.deprecate(User.prototype.block, 'User#block: userbot methods will be removed');
+
+User.prototype.unblock =
+ util.deprecate(User.prototype.unblock, 'User#unblock: userbot methods will be removed');
+
+User.prototype.addFriend =
+ util.deprecate(User.prototype.addFriend, 'User#addFriend: userbot methods will be removed');
+
+User.prototype.removeFriend =
+ util.deprecate(User.prototype.removeFriend, 'User#removeFriend: userbot methods will be removed');
+
+User.prototype.setNote =
+ util.deprecate(User.prototype.setNote, 'User#setNote, userbot methods will be removed');
+
+User.prototype.fetchProfile =
+ util.deprecate(User.prototype.fetchProfile, 'User#fetchProfile: userbot methods will be removed');
+
+module.exports = User;
diff --git a/node_modules/discord.js/src/structures/UserConnection.js b/node_modules/discord.js/src/structures/UserConnection.js
new file mode 100644
index 0000000..ea6c65f
--- /dev/null
+++ b/node_modules/discord.js/src/structures/UserConnection.js
@@ -0,0 +1,48 @@
+/**
+ * Represents a user connection (or "platform identity").
+ */
+class UserConnection {
+ constructor(user, data) {
+ /**
+ * The user that owns the connection
+ * @type {User}
+ */
+ this.user = user;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The type of the connection
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * The username of the connection account
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The id of the connection account
+ * @type {string}
+ */
+ this.id = data.id;
+
+ /**
+ * Whether the connection is revoked
+ * @type {boolean}
+ */
+ this.revoked = data.revoked;
+
+ /**
+ * Partial server integrations (not yet implemented)
+ * @type {Object[]}
+ */
+ this.integrations = data.integrations;
+ }
+}
+
+module.exports = UserConnection;
diff --git a/node_modules/discord.js/src/structures/UserProfile.js b/node_modules/discord.js/src/structures/UserProfile.js
new file mode 100644
index 0000000..a27d4d0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/UserProfile.js
@@ -0,0 +1,62 @@
+const Collection = require('../util/Collection');
+const UserConnection = require('./UserConnection');
+
+/**
+ * Represents a user's profile on Discord.
+ */
+class UserProfile {
+ constructor(user, data) {
+ /**
+ * The owner of the profile
+ * @type {User}
+ */
+ this.user = user;
+
+ /**
+ * The client that created the instance of the UserProfile
+ * @name UserProfile#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: user.client });
+
+ /**
+ * The guilds that the client user and the user share
+ * @type {Collection<Snowflake, Guild>}
+ */
+ this.mutualGuilds = new Collection();
+
+ /**
+ * The user's connections
+ * @type {Collection<Snowflake, UserConnection>}
+ */
+ this.connections = new Collection();
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * If the user has Discord Premium
+ * @type {boolean}
+ */
+ this.premium = data.premium;
+
+ /**
+ * The date since which the user has had Discord Premium
+ * @type {?Date}
+ */
+ this.premiumSince = data.premium_since ? new Date(data.premium_since) : null;
+
+ for (const guild of data.mutual_guilds) {
+ if (this.client.guilds.has(guild.id)) {
+ this.mutualGuilds.set(guild.id, this.client.guilds.get(guild.id));
+ }
+ }
+ for (const connection of data.connected_accounts) {
+ this.connections.set(connection.id, new UserConnection(this.user, connection));
+ }
+ }
+}
+
+module.exports = UserProfile;
diff --git a/node_modules/discord.js/src/structures/VoiceChannel.js b/node_modules/discord.js/src/structures/VoiceChannel.js
new file mode 100644
index 0000000..a89dafa
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceChannel.js
@@ -0,0 +1,146 @@
+const GuildChannel = require('./GuildChannel');
+const Collection = require('../util/Collection');
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a guild voice channel on Discord.
+ * @extends {GuildChannel}
+ */
+class VoiceChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+
+ /**
+ * The members in this voice channel
+ * @type {Collection<Snowflake, GuildMember>}
+ */
+ this.members = new Collection();
+
+ this.type = 'voice';
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The bitrate of this voice channel
+ * @type {number}
+ */
+ this.bitrate = data.bitrate * 0.001;
+
+ /**
+ * The maximum amount of users allowed in this channel - 0 means unlimited.
+ * @type {number}
+ */
+ this.userLimit = data.user_limit;
+ }
+
+ /**
+ * The voice connection for this voice channel, if the client is connected
+ * @type {?VoiceConnection}
+ * @readonly
+ */
+ get connection() {
+ const connection = this.guild.voiceConnection;
+ if (connection && connection.channel.id === this.id) return connection;
+ return null;
+ }
+
+ /**
+ * Checks if the voice channel is full
+ * @type {boolean}
+ * @readonly
+ */
+ get full() {
+ return this.userLimit > 0 && this.members.size >= this.userLimit;
+ }
+
+ /**
+ * Whether the channel is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return super.deletable && this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT);
+ }
+
+ /**
+ * Checks if the client has permission join the voice channel
+ * @type {boolean}
+ * @readonly
+ */
+ get joinable() {
+ if (this.client.browser) return false;
+ if (!this.permissionsFor(this.client.user).has('CONNECT')) return false;
+ if (this.full && !this.permissionsFor(this.client.user).has('MOVE_MEMBERS')) return false;
+ return true;
+ }
+
+ /**
+ * Checks if the client has permission to send audio to the voice channel
+ * @type {boolean}
+ * @readonly
+ */
+ get speakable() {
+ return this.permissionsFor(this.client.user).has('SPEAK');
+ }
+
+ /**
+ * Sets the bitrate of the channel (in kbps).
+ * @param {number} bitrate The new bitrate
+ * @param {string} [reason] Reason for changing the channel's bitrate
+ * @returns {Promise<VoiceChannel>}
+ * @example
+ * // Set the bitrate of a voice channel
+ * voiceChannel.setBitrate(48)
+ * .then(vc => console.log(`Set bitrate to ${vc.bitrate}kbps for ${vc.name}`))
+ * .catch(console.error);
+ */
+ setBitrate(bitrate, reason) {
+ bitrate *= 1000;
+ return this.edit({ bitrate }, reason);
+ }
+
+ /**
+ * Sets the user limit of the channel.
+ * @param {number} userLimit The new user limit
+ * @param {string} [reason] Reason for changing the user limit
+ * @returns {Promise<VoiceChannel>}
+ * @example
+ * // Set the user limit of a voice channel
+ * voiceChannel.setUserLimit(42)
+ * .then(vc => console.log(`Set user limit to ${vc.userLimit} for ${vc.name}`))
+ * .catch(console.error);
+ */
+ setUserLimit(userLimit, reason) {
+ return this.edit({ userLimit }, reason);
+ }
+
+ /**
+ * Attempts to join this voice channel.
+ * @returns {Promise<VoiceConnection>}
+ * @example
+ * // Join a voice channel
+ * voiceChannel.join()
+ * .then(connection => console.log('Connected!'))
+ * .catch(console.error);
+ */
+ join() {
+ if (this.client.browser) return Promise.reject(new Error('Voice connections are not available in browsers.'));
+ return this.client.voice.joinChannel(this);
+ }
+
+ /**
+ * Leaves this voice channel.
+ * @example
+ * // Leave a voice channel
+ * voiceChannel.leave();
+ */
+ leave() {
+ if (this.client.browser) return;
+ const connection = this.client.voice.connections.get(this.guild.id);
+ if (connection && connection.channel.id === this.id) connection.disconnect();
+ }
+}
+
+module.exports = VoiceChannel;
diff --git a/node_modules/discord.js/src/structures/VoiceRegion.js b/node_modules/discord.js/src/structures/VoiceRegion.js
new file mode 100644
index 0000000..dc6b461
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceRegion.js
@@ -0,0 +1,50 @@
+/**
+ * Represents a Discord voice region for guilds.
+ */
+class VoiceRegion {
+ constructor(data) {
+ /**
+ * The ID of the region
+ * @type {string}
+ */
+ this.id = data.id;
+
+ /**
+ * Name of the region
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * Whether the region is VIP-only
+ * @type {boolean}
+ */
+ this.vip = data.vip;
+
+ /**
+ * Whether the region is deprecated
+ * @type {boolean}
+ */
+ this.deprecated = data.deprecated;
+
+ /**
+ * Whether the region is optimal
+ * @type {boolean}
+ */
+ this.optimal = data.optimal;
+
+ /**
+ * Whether the region is custom
+ * @type {boolean}
+ */
+ this.custom = data.custom;
+
+ /**
+ * A sample hostname for what a connection might look like
+ * @type {string}
+ */
+ this.sampleHostname = data.sample_hostname;
+ }
+}
+
+module.exports = VoiceRegion;
diff --git a/node_modules/discord.js/src/structures/Webhook.js b/node_modules/discord.js/src/structures/Webhook.js
new file mode 100644
index 0000000..5d82b0c
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Webhook.js
@@ -0,0 +1,377 @@
+const EventEmitter = require('events');
+const path = require('path');
+const Util = require('../util/Util');
+const Attachment = require('./Attachment');
+const RichEmbed = require('./RichEmbed');
+const Constants = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a webhook.
+ */
+class Webhook extends EventEmitter {
+ constructor(client, dataOrID, token) {
+ super();
+ if (client) {
+ /**
+ * The client that instantiated the webhook
+ * @name Webhook#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ if (dataOrID) this.setup(dataOrID);
+ } else {
+ this.id = dataOrID;
+ this.token = token;
+ Object.defineProperty(this, 'client', { value: this });
+ }
+ }
+
+ setup(data) {
+ /**
+ * The name of the webhook
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The token for the webhook
+ * @name Webhook#token
+ * @type {?string}
+ */
+ Object.defineProperty(this, 'token', { value: data.token || null, writable: true, configurable: true });
+
+ /**
+ * The avatar for the webhook
+ * @type {?string}
+ */
+ this.avatar = data.avatar;
+
+ /**
+ * The ID of the webhook
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of the webhook
+ * @type {WebhookTypes}
+ */
+ this.type = Constants.WebhookTypes[data.type];
+
+ /**
+ * The guild the webhook belongs to
+ * @type {Snowflake}
+ */
+ this.guildID = data.guild_id;
+
+ /**
+ * The channel the webhook belongs to
+ * @type {Snowflake}
+ */
+ this.channelID = data.channel_id;
+
+ if (data.user) {
+ /**
+ * The owner of the webhook
+ * @type {?User|Object}
+ */
+ this.owner = this.client.users ? this.client.users.get(data.user.id) : data.user;
+ } else {
+ this.owner = null;
+ }
+ }
+
+ /**
+ * The timestamp the webhook was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the webhook was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * A link to the webhook user's avatar
+ * @type {?stirng}
+ * @readonly
+ */
+ get avatarURL() {
+ if (!this.avatar) return null;
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).Avatar(this.id, this.avatar);
+ }
+
+ /**
+ * The url of this webhook
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ const API = `${this.client.options.http.host}/api/v${this.client.options.http.version}`;
+ return API + Constants.Endpoints.Webhook(this.id, this.token);
+ }
+
+ /**
+ * Options that can be passed into send, sendMessage, sendFile, sendEmbed, and sendCode.
+ * @typedef {Object} WebhookMessageOptions
+ * @property {string} [username=this.name] Username override for the message
+ * @property {string} [avatarURL] Avatar URL override for the message
+ * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
+ * @property {string} [nonce=''] The nonce for the message
+ * @property {Array<RichEmbed|Object>} [embeds] An array of embeds for the message
+ * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
+ * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here
+ * should be replaced with plain-text
+ * @property {FileOptions|BufferResolvable|Attachment} [file] A file to send with the message **(deprecated)**
+ * @property {FileOptions[]|BufferResolvable[]|Attachment[]} [files] Files to send with the message
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
+ * it exceeds the character limit. If an object is provided, these are the options for splitting the message.
+ */
+
+ /**
+ * Send a message with this webhook.
+ * @param {StringResolvable} content The content to send
+ * @param {WebhookMessageOptions|Attachment|RichEmbed} [options] The options to provide,
+ * can also be just a RichEmbed or Attachment
+ * @returns {Promise<Message|Message[]|Object|Object[]>}
+ * @example
+ * // Send a basic message
+ * webhook.send('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ * @example
+ * // Send a remote file
+ * webhook.send({
+ * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send a local file
+ * webhook.send({
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send an embed with a local image inside
+ * webhook.send('This is an embed', {
+ * embeds: [{
+ * thumbnail: {
+ * url: 'attachment://file.jpg'
+ * }
+ * }],
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ send(content, options) { // eslint-disable-line complexity
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+
+ if (options instanceof Attachment) options = { files: [options] };
+ if (options instanceof RichEmbed) options = { embeds: [options] };
+
+ if (content) {
+ content = this.client.resolver.resolveString(content);
+ let { split, code, disableEveryone } = options;
+ if (split && typeof split !== 'object') split = {};
+ if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
+ content = Util.escapeMarkdown(content, true);
+ content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
+ if (split) {
+ split.prepend = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n`;
+ split.append = '\n```';
+ }
+ }
+ if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) {
+ content = content.replace(/@(everyone|here)/g, '@\u200b$1');
+ }
+
+ if (split) content = Util.splitMessage(content, split);
+ }
+
+ if (options.file) {
+ if (options.files) options.files.push(options.file);
+ else options.files = [options.file];
+ }
+
+ if (options.embeds) {
+ const files = [];
+ for (const embed of options.embeds) {
+ if (embed.file) files.push(embed.file);
+ }
+ if (options.files) options.files.push(...files);
+ else options.files = files;
+ }
+
+ if (options.embeds) options.embeds = options.embeds.map(e => new RichEmbed(e).toJSON());
+
+ if (options.files) {
+ for (let i = 0; i < options.files.length; i++) {
+ let file = options.files[i];
+ if (typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file };
+ if (!file.name) {
+ if (typeof file.attachment === 'string') {
+ file.name = path.basename(file.attachment);
+ } else if (file.attachment && file.attachment.path) {
+ file.name = path.basename(file.attachment.path);
+ } else if (file instanceof Attachment) {
+ file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' };
+ } else {
+ file.name = 'file.jpg';
+ }
+ } else if (file instanceof Attachment) {
+ file = file.file;
+ }
+ options.files[i] = file;
+ }
+
+ return Promise.all(options.files.map(file =>
+ this.client.resolver.resolveFile(file.attachment).then(resource => {
+ file.file = resource;
+ return file;
+ })
+ )).then(files => this.client.rest.methods.sendWebhookMessage(this, content, options, files));
+ }
+
+ return this.client.rest.methods.sendWebhookMessage(this, content, options);
+ }
+
+ /**
+ * Send a message with this webhook
+ * @param {StringResolvable} content The content to send
+ * @param {WebhookMessageOptions} [options={}] The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ * @example
+ * // Send a message
+ * webhook.sendMessage('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ */
+ sendMessage(content, options = {}) {
+ return this.send(content, options);
+ }
+
+ /**
+ * Send a file with this webhook.
+ * @param {BufferResolvable} attachment The file to send
+ * @param {string} [name='file.jpg'] The name and extension of the file
+ * @param {StringResolvable} [content] Text message to send with the attachment
+ * @param {WebhookMessageOptions} [options] The options to provide
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendFile(attachment, name, content, options = {}) {
+ return this.send(content, Object.assign(options, { file: { attachment, name } }));
+ }
+
+ /**
+ * Send a code block with this webhook.
+ * @param {string} lang Language for the code block
+ * @param {StringResolvable} content Content of the code block
+ * @param {WebhookMessageOptions} options The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ */
+ sendCode(lang, content, options = {}) {
+ return this.send(content, Object.assign(options, { code: lang }));
+ }
+
+ /**
+ * Send a raw slack message with this webhook.
+ * @param {Object} body The raw body to send
+ * @returns {Promise}
+ * @example
+ * // Send a slack message
+ * webhook.sendSlackMessage({
+ * 'username': 'Wumpus',
+ * 'attachments': [{
+ * 'pretext': 'this looks pretty cool',
+ * 'color': '#F0F',
+ * 'footer_icon': 'http://snek.s3.amazonaws.com/topSnek.png',
+ * 'footer': 'Powered by sneks',
+ * 'ts': Date.now() / 1000
+ * }]
+ * }).catch(console.error);
+ */
+ sendSlackMessage(body) {
+ return this.client.rest.methods.sendSlackWebhookMessage(this, body);
+ }
+
+ /**
+ * Options provided to edit a webhook.
+ * @property {string} [name] The new name for the webhook
+ * @property {BufferResolvable} [avatar] The new avatar for the webhook
+ * @property {ChannelResolvable} [channel] The new channel for the webhook
+ * @typedef {Object} WebhookEditOptions
+ */
+
+ /**
+ * Edit the webhook.
+ * @param {string|WebhookEditOptions} nameOrOptions The new name for the webhook **(deprecated, use options)**
+ * Alternatively options for the webhook, overriding the avatar parameter.
+ * @param {BufferResolvable|string} [avatarOrReason] The new avatar for the webhook **(deprecated, use options)**
+ * Alternatively a reason to edit, if using options as first parameter.
+ * @returns {Promise<Webhook>}
+ */
+ edit(nameOrOptions = this.name, avatarOrReason) {
+ if (typeof nameOrOptions !== 'object') {
+ process.emitWarning('Webhook#edit: Use options object instead of separate parameters.');
+ nameOrOptions = {
+ name: nameOrOptions,
+ avatar: avatarOrReason,
+ };
+ // Parameter was an avatar here; Clear the now reason parameter
+ avatarOrReason = undefined;
+ }
+
+ if (nameOrOptions.channel) {
+ nameOrOptions.channel_id = this.client.resolver.resolveChannelID(nameOrOptions.channel);
+ nameOrOptions.channel = undefined;
+ }
+
+ if (nameOrOptions.avatar) {
+ return this.client.resolver.resolveImage(nameOrOptions.avatar).then(data => {
+ nameOrOptions.avatar = data;
+ return this.client.rest.methods.editWebhook(this, nameOrOptions, avatarOrReason);
+ });
+ }
+
+ return this.client.rest.methods.editWebhook(this, nameOrOptions, avatarOrReason);
+ }
+
+ /**
+ * Delete the webhook.
+ * @param {string} [reason] Reason for deleting the webhook
+ * @returns {Promise}
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteWebhook(this, reason);
+ }
+}
+
+module.exports = Webhook;
diff --git a/node_modules/discord.js/src/structures/interfaces/Collector.js b/node_modules/discord.js/src/structures/interfaces/Collector.js
new file mode 100644
index 0000000..6767b42
--- /dev/null
+++ b/node_modules/discord.js/src/structures/interfaces/Collector.js
@@ -0,0 +1,208 @@
+const Collection = require('../../util/Collection');
+const EventEmitter = require('events').EventEmitter;
+
+/**
+ * Filter to be applied to the collector.
+ * @typedef {Function} CollectorFilter
+ * @param {...*} args Any arguments received by the listener
+ * @param {Collection} collection The items collected by this collector
+ * @returns {boolean}
+ */
+
+/**
+ * Options to be applied to the collector.
+ * @typedef {Object} CollectorOptions
+ * @property {number} [time] How long to run the collector for
+ * @property {number} [idle] How long to stop the collector after inactivity in milliseconds
+ */
+
+/**
+ * Abstract class for defining a new Collector.
+ * @abstract
+ */
+class Collector extends EventEmitter {
+ constructor(client, filter, options = {}) {
+ super();
+
+ /**
+ * The client
+ * @name Collector#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The filter applied to this collector
+ * @type {CollectorFilter}
+ */
+ this.filter = filter;
+
+ /**
+ * The options of this collector
+ * @type {CollectorOptions}
+ */
+ this.options = options;
+
+ /**
+ * The items collected by this collector
+ * @type {Collection}
+ */
+ this.collected = new Collection();
+
+ /**
+ * Whether this collector has finished collecting
+ * @type {boolean}
+ */
+ this.ended = false;
+
+ /**
+ * Timeout for cleanup
+ * @type {?Timeout}
+ * @private
+ */
+ this._timeout = null;
+
+ /**
+ * Timeout for cleanup due to inactivity
+ * @type {?Timeout}
+ * @private
+ */
+ this._idletimeout = null;
+
+ /**
+ * Call this to handle an event as a collectable element
+ * Accepts any event data as parameters
+ * @type {Function}
+ * @private
+ */
+ this.listener = this._handle.bind(this);
+ if (options.time) this._timeout = this.client.setTimeout(() => this.stop('time'), options.time);
+ if (options.idle) this._idletimeout = this.client.setTimeout(() => this.stop('idle'), options.idle);
+ }
+
+ /**
+ * @param {...*} args The arguments emitted by the listener
+ * @emits Collector#collect
+ * @private
+ */
+ _handle(...args) {
+ const collect = this.handle(...args);
+ if (collect && this.filter(...args, this.collected)) {
+ this.collected.set(collect.key, collect.value);
+
+ /**
+ * Emitted whenever an element is collected.
+ * @event Collector#collect
+ * @param {*} element The element that got collected
+ * @param {Collector} collector The collector
+ */
+ this.emit('collect', collect.value, this);
+
+ /**
+ * Emitted whenever an element is collected.
+ * @event Collector#fullCollect
+ * @param {...*} args The arguments emitted by the listener
+ * @private
+ */
+ this.emit('fullCollect', ...args, this);
+
+ if (this._idletimeout) {
+ this.client.clearTimeout(this._idletimeout);
+ this._idletimeout = this.client.setTimeout(() => this.stop('idle'), this.options.idle);
+ }
+ }
+
+ const post = this.postCheck(...args);
+ if (post) this.stop(post);
+ }
+
+ /**
+ * Return a promise that resolves with the next collected element;
+ * rejects with collected elements if the collector finishes without receiving a next element
+ * @type {Promise}
+ * @readonly
+ */
+ get next() {
+ return new Promise((resolve, reject) => {
+ if (this.ended) {
+ reject(this.collected);
+ return;
+ }
+
+ const cleanup = () => {
+ this.removeListener('collect', onCollect);
+ this.removeListener('end', onEnd);
+ };
+
+ const onCollect = item => {
+ cleanup();
+ resolve(item);
+ };
+
+ const onEnd = () => {
+ cleanup();
+ reject(this.collected); // eslint-disable-line prefer-promise-reject-errors
+ };
+
+ this.on('collect', onCollect);
+ this.on('end', onEnd);
+ });
+ }
+
+ /**
+ * Stop this collector and emit the `end` event.
+ * @param {string} [reason='user'] The reason this collector is ending
+ * @emits Collector#end
+ */
+ stop(reason = 'user') {
+ if (this.ended) return;
+
+ if (this._timeout) {
+ this.client.clearTimeout(this._timeout);
+ this._timeout = null;
+ }
+ if (this._idletimeout) {
+ this.client.clearTimeout(this._idletimeout);
+ this._idletimeout = null;
+ }
+ this.ended = true;
+ this.cleanup();
+
+ /**
+ * Emitted when the collector is finished collecting.
+ * @event Collector#end
+ * @param {Collection} collected The elements collected by the collector
+ * @param {string} reason The reason the collector ended
+ */
+ this.emit('end', this.collected, reason);
+ }
+
+ /* eslint-disable no-empty-function, valid-jsdoc */
+ /**
+ * Handles incoming events from the `listener` function. Returns null if the event should not be collected,
+ * or returns an object describing the data that should be stored.
+ * @see Collector#listener
+ * @param {...*} args Any args the event listener emits
+ * @returns {?{key: string, value}} Data to insert into collection, if any
+ * @abstract
+ */
+ handle() {}
+
+ /**
+ * This method runs after collection to see if the collector should finish.
+ * @param {...*} args Any args the event listener emits
+ * @returns {?string} Reason to end the collector, if any
+ * @abstract
+ */
+ postCheck() {}
+
+ /**
+ * Called when the collector is ending.
+ * @abstract
+ */
+ cleanup() {}
+ /* eslint-enable no-empty-function, valid-jsdoc */
+}
+
+module.exports = Collector;
diff --git a/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js b/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js
new file mode 100644
index 0000000..ceaa73b
--- /dev/null
+++ b/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js
@@ -0,0 +1,635 @@
+const path = require('path');
+const Message = require('../Message');
+const MessageCollector = require('../MessageCollector');
+const Collection = require('../../util/Collection');
+const Attachment = require('../../structures/Attachment');
+const RichEmbed = require('../../structures/RichEmbed');
+const Snowflake = require('../../util/Snowflake');
+const util = require('util');
+
+/**
+ * Interface for classes that have text-channel-like features.
+ * @interface
+ */
+class TextBasedChannel {
+ constructor() {
+ /**
+ * A collection containing the messages sent to this channel
+ * @type {Collection<Snowflake, Message>}
+ */
+ this.messages = new Collection();
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The Message object of the last message in the channel, if one was sent
+ * @type {?Message}
+ */
+ this.lastMessage = null;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = null;
+ }
+
+ /**
+ * Options provided when sending or editing a message.
+ * @typedef {Object} MessageOptions
+ * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
+ * @property {string} [nonce=''] The nonce for the message
+ * @property {RichEmbed|Object} [embed] An embed for the message
+ * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
+ * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here
+ * should be replaced with plain-text
+ * @property {FileOptions|BufferResolvable|Attachment} [file] A file to send with the message **(deprecated)**
+ * @property {FileOptions[]|BufferResolvable[]|Attachment[]} [files] Files to send with the message
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
+ * it exceeds the character limit. If an object is provided, these are the options for splitting the message
+ * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs)
+ */
+
+ /**
+ * @typedef {Object} FileOptions
+ * @property {BufferResolvable} attachment File to attach
+ * @property {string} [name='file.jpg'] Filename of the attachment
+ */
+
+ /**
+ * Options for splitting a message.
+ * @typedef {Object} SplitOptions
+ * @property {number} [maxLength=1950] Maximum character length per message piece
+ * @property {string} [char='\n'] Character to split the message with
+ * @property {string} [prepend=''] Text to prepend to every piece except the first
+ * @property {string} [append=''] Text to append to every piece except the last
+ */
+
+ /**
+ * Send a message to this channel.
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions|Attachment|RichEmbed} [options] Options for the message,
+ * can also be just a RichEmbed or Attachment
+ * @returns {Promise<Message|Message[]>}
+ * @example
+ * // Send a basic message
+ * channel.send('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ * @example
+ * // Send a remote file
+ * channel.send({
+ * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send a local file
+ * channel.send({
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send an embed with a local image inside
+ * channel.send('This is an embed', {
+ * embed: {
+ * thumbnail: {
+ * url: 'attachment://file.jpg'
+ * }
+ * },
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ // eslint-disable-next-line complexity
+ send(content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+
+ const { reply } = options;
+ if (options instanceof Attachment) options = { files: [options.file] };
+ if (options instanceof RichEmbed) {
+ if (options.reply) options.reply = undefined;
+ options = { embed: options };
+ }
+ options.reply = reply;
+
+ if (options.embed) {
+ if (options.embed.file) {
+ if (options.files) options.files.push(options.embed.file);
+ else options.files = [options.embed.file];
+ }
+ if (options.embed.files) {
+ if (options.files) options.files = options.files.concat(options.embed.files);
+ else options.files = options.embed.files;
+ }
+ }
+
+ if (options.file) {
+ if (options.files) options.files.push(options.file);
+ else options.files = [options.file];
+ }
+
+ if (options.embed) options.embed = new RichEmbed(options.embed).toJSON();
+
+ if (options.files) {
+ for (let i = 0; i < options.files.length; i++) {
+ let file = options.files[i];
+ if (!file || typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file };
+ if (!file.name) {
+ if (typeof file.attachment === 'string') {
+ file.name = path.basename(file.attachment);
+ } else if (file.attachment && file.attachment.path) {
+ file.name = path.basename(file.attachment.path);
+ } else if (file instanceof Attachment) {
+ file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' };
+ } else {
+ file.name = 'file.jpg';
+ }
+ } else if (file instanceof Attachment) {
+ file = file.file;
+ }
+ options.files[i] = file;
+ }
+
+ return Promise.all(options.files.map(file =>
+ this.client.resolver.resolveFile(file.attachment).then(resource => {
+ file.file = resource;
+ return file;
+ })
+ )).then(files => this.client.rest.methods.sendMessage(this, content, options, files));
+ }
+
+ return this.client.rest.methods.sendMessage(this, content, options);
+ }
+
+ /**
+ * Gets a single message from this channel, regardless of it being cached or not.
+ * @param {Snowflake} messageID ID of the message to get
+ * @returns {Promise<Message>}
+ * @example
+ * // Get message
+ * channel.fetchMessage('99539446449315840')
+ * .then(message => console.log(message.content))
+ * .catch(console.error);
+ */
+ fetchMessage(messageID) {
+ if (!this.client.user.bot) {
+ return this.fetchMessages({ limit: 1, around: messageID }).then(messages => {
+ const msg = messages.get(messageID);
+ if (!msg) throw new Error('Message not found.');
+ return msg;
+ });
+ }
+ return this.client.rest.methods.getChannelMessage(this, messageID).then(data => {
+ const msg = data instanceof Message ? data : new Message(this, data, this.client);
+ this._cacheMessage(msg);
+ return msg;
+ });
+ }
+
+ /**
+ * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
+ * `after` are mutually exclusive. All the parameters are optional.
+ * @typedef {Object} ChannelLogsQueryOptions
+ * @property {number} [limit=50] Number of messages to acquire
+ * @property {Snowflake} [before] ID of a message to get the messages that were posted before it
+ * @property {Snowflake} [after] ID of a message to get the messages that were posted after it
+ * @property {Snowflake} [around] ID of a message to get the messages that were posted around it
+ */
+
+ /**
+ * Gets the past messages sent in this channel. Resolves with a collection mapping message ID's to Message objects.
+ * <info>The returned Collection does not contain reaction users of the messages if they were not cached.
+ * Those need to be fetched separately in such a case.</info>
+ * @param {ChannelLogsQueryOptions} [options={}] Query parameters to pass in
+ * @returns {Promise<Collection<Snowflake, Message>>}
+ * @example
+ * // Get messages
+ * channel.fetchMessages({ limit: 10 })
+ * .then(messages => console.log(`Received ${messages.size} messages`))
+ * .catch(console.error);
+ * @example
+ * // Get messages and filter by user ID
+ * channel.fetchMessages()
+ * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`))
+ * .catch(console.error);
+ */
+ fetchMessages(options = {}) {
+ return this.client.rest.methods.getChannelMessages(this, options).then(data => {
+ const messages = new Collection();
+ for (const message of data) {
+ const msg = new Message(this, message, this.client);
+ messages.set(message.id, msg);
+ this._cacheMessage(msg);
+ }
+ return messages;
+ });
+ }
+
+ /**
+ * Fetches the pinned messages of this channel and returns a collection of them.
+ * <info>The returned Collection does not contain any reaction data of the messages.
+ * Those need to be fetched separately.</info>
+ * @returns {Promise<Collection<Snowflake, Message>>}
+ * @example
+ * // Get pinned messages
+ * channel.fetchPinnedMessages()
+ * .then(messages => console.log(`Received ${messages.size} messages`))
+ * .catch(console.error);
+ */
+ fetchPinnedMessages() {
+ return this.client.rest.methods.getChannelPinnedMessages(this).then(data => {
+ const messages = new Collection();
+ for (const message of data) {
+ const msg = new Message(this, message, this.client);
+ messages.set(message.id, msg);
+ this._cacheMessage(msg);
+ }
+ return messages;
+ });
+ }
+
+ /**
+ * @typedef {Object} MessageSearchOptions
+ * @property {string} [content] Message content
+ * @property {Snowflake} [maxID] Maximum ID for the filter
+ * @property {Snowflake} [minID] Minimum ID for the filter
+ * @property {string} [has] One of `link`, `embed`, `file`, `video`, `image`, or `sound`,
+ * or add `-` to negate (e.g. `-file`)
+ * @property {ChannelResolvable} [channel] Channel to limit search to (only for guild search endpoint)
+ * @property {UserResolvable} [author] Author to limit search
+ * @property {string} [authorType] One of `user`, `bot`, `webhook`, or add `-` to negate (e.g. `-webhook`)
+ * @property {string} [sortBy='recent'] `recent` or `relevant`
+ * @property {string} [sortOrder='desc'] `asc` or `desc`
+ * @property {number} [contextSize=2] How many messages to get around the matched message (0 to 2)
+ * @property {number} [limit=25] Maximum number of results to get (1 to 25)
+ * @property {number} [offset=0] Offset the "pages" of results (since you can only see 25 at a time)
+ * @property {UserResolvable} [mentions] Mentioned user filter
+ * @property {boolean} [mentionsEveryone] If everyone is mentioned
+ * @property {string} [linkHostname] Filter links by hostname
+ * @property {string} [embedProvider] The name of an embed provider
+ * @property {string} [embedType] one of `image`, `video`, `url`, `rich`
+ * @property {string} [attachmentFilename] The name of an attachment
+ * @property {string} [attachmentExtension] The extension of an attachment
+ * @property {Date} [before] Date to find messages before
+ * @property {Date} [after] Date to find messages before
+ * @property {Date} [during] Date to find messages during (range of date to date + 24 hours)
+ * @property {boolean} [nsfw=false] Include results from NSFW channels
+ */
+
+ /**
+ * @typedef {Object} MessageSearchResult
+ * @property {number} totalResults Total result count
+ * @property {Message[][]} messages Array of message results
+ * The message which has triggered the result will have the `hit` property set to `true`
+ */
+
+ /**
+ * Performs a search within the channel.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {MessageSearchOptions} [options={}] Options to pass to the search
+ * @returns {Promise<MessageSearchResult>}
+ * @deprecated
+ * @example
+ * channel.search({
+ * content: 'discord.js',
+ * before: '2016-11-17'
+ * }).then(res => {
+ * const hit = res.messages[0].find(m => m.hit).content;
+ * console.log(`I found: **${hit}**, total results: ${res.totalResults}`);
+ * }).catch(console.error);
+ */
+ search(options = {}) {
+ return this.client.rest.methods.search(this, options);
+ }
+
+ /**
+ * Starts a typing indicator in the channel.
+ * @param {number} [count] The number of times startTyping should be considered to have been called
+ * @example
+ * // Start typing in a channel
+ * channel.startTyping();
+ */
+ startTyping(count) {
+ if (typeof count !== 'undefined' && count < 1) throw new RangeError('Count must be at least 1.');
+ if (this.client.user._typing.has(this.id)) {
+ const entry = this.client.user._typing.get(this.id);
+ entry.count = count || entry.count + 1;
+ return;
+ }
+
+ const entry = {
+ count: count || 1,
+ interval: this.client.setInterval(() => {
+ this.client.rest.methods.sendTyping(this.id).catch(() => {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ });
+ }, 9000),
+ };
+ this.client.rest.methods.sendTyping(this.id).catch(() => {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ });
+ this.client.user._typing.set(this.id, entry);
+ }
+
+ /**
+ * Stops the typing indicator in the channel.
+ * The indicator will only stop if this is called as many times as startTyping().
+ * <info>It can take a few seconds for the client user to stop typing.</info>
+ * @param {boolean} [force=false] Whether or not to reset the call count and force the indicator to stop
+ * @example
+ * // Reduce the typing count by one and stop typing if it reached 0
+ * channel.stopTyping();
+ * @example
+ * // Force typing to fully stop in a channel
+ * channel.stopTyping(true);
+ */
+ stopTyping(force = false) {
+ if (this.client.user._typing.has(this.id)) {
+ const entry = this.client.user._typing.get(this.id);
+ entry.count--;
+ if (entry.count <= 0 || force) {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ }
+ }
+ }
+
+ /**
+ * Whether or not the typing indicator is being shown in the channel
+ * @type {boolean}
+ * @readonly
+ */
+ get typing() {
+ return this.client.user._typing.has(this.id);
+ }
+
+ /**
+ * Number of times `startTyping` has been called
+ * @type {number}
+ * @readonly
+ */
+ get typingCount() {
+ if (this.client.user._typing.has(this.id)) return this.client.user._typing.get(this.id).count;
+ return 0;
+ }
+
+ /**
+ * The Message object of the last message in the channel, if one was sent
+ * @type {?Message}
+ * @readonly
+ */
+ get lastMessage() {
+ return this.messages.get(this.lastMessageID) || null;
+ }
+
+ /**
+ * The date when the last pinned message was pinned, if there was one
+ * @type {?Date}
+ * @readonly
+ */
+ get lastPinAt() {
+ return this.lastPinTimestamp ? new Date(this.lastPinTimestamp) : null;
+ }
+
+ /**
+ * Creates a Message Collector
+ * @param {CollectorFilter} filter The filter to create the collector with
+ * @param {MessageCollectorOptions} [options={}] The options to pass to the collector
+ * @returns {MessageCollector}
+ * @deprecated
+ */
+ createCollector(filter, options) {
+ return this.createMessageCollector(filter, options);
+ }
+
+ /**
+ * Creates a Message Collector.
+ * @param {CollectorFilter} filter The filter to create the collector with
+ * @param {MessageCollectorOptions} [options={}] The options to pass to the collector
+ * @returns {MessageCollector}
+ * @example
+ * // Create a message collector
+ * const filter = m => m.content.includes('discord');
+ * const collector = channel.createMessageCollector(filter, { time: 15000 });
+ * collector.on('collect', m => console.log(`Collected ${m.content}`));
+ * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
+ */
+ createMessageCollector(filter, options = {}) {
+ return new MessageCollector(this, filter, options);
+ }
+
+ /**
+ * An object containing the same properties as CollectorOptions, but a few more:
+ * @typedef {MessageCollectorOptions} AwaitMessagesOptions
+ * @property {string[]} [errors] Stop/end reasons that cause the promise to reject
+ */
+
+ /**
+ * Similar to createCollector but in promise form. Resolves with a collection of messages that pass the specified
+ * filter.
+ * @param {CollectorFilter} filter The filter function to use
+ * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector
+ * @returns {Promise<Collection<Snowflake, Message>>}
+ * @example
+ * // Await !vote messages
+ * const filter = m => m.content.startsWith('!vote');
+ * // Errors: ['time'] treats ending because of the time limit as an error
+ * channel.awaitMessages(filter, { max: 4, time: 60000, errors: ['time'] })
+ * .then(collected => console.log(collected.size))
+ * .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`));
+ */
+ awaitMessages(filter, options = {}) {
+ return new Promise((resolve, reject) => {
+ const collector = this.createCollector(filter, options);
+ collector.once('end', (collection, reason) => {
+ if (options.errors && options.errors.includes(reason)) {
+ reject(collection);
+ } else {
+ resolve(collection);
+ }
+ });
+ });
+ }
+
+ /**
+ * Bulk delete given messages that are newer than two weeks.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {Collection<Snowflake, Message>|Message[]|Snowflake[]|number} messages
+ * Messages or number of messages to delete
+ * @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically
+ * @returns {Promise<Collection<Snowflake, Message>>} Deleted messages
+ * @example
+ * // Bulk delete messages
+ * channel.bulkDelete(5)
+ * .then(messages => console.log(`Bulk deleted ${messages.size} messages`))
+ * .catch(console.error);
+ */
+ bulkDelete(messages, filterOld = false) {
+ if (messages instanceof Array || messages instanceof Collection) {
+ let messageIDs = messages instanceof Collection ? messages.keyArray() : messages.map(m => m.id || m);
+ if (filterOld) {
+ messageIDs = messageIDs.filter(id => Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000);
+ }
+ if (messageIDs.length === 0) return Promise.resolve(new Collection());
+ if (messageIDs.length === 1) {
+ return this.fetchMessage(messageIDs[0]).then(m => m.delete()).then(m => new Collection([[m.id, m]]));
+ }
+ return this.client.rest.methods.bulkDeleteMessages(this, messageIDs);
+ }
+ if (!isNaN(messages)) return this.fetchMessages({ limit: messages }).then(msgs => this.bulkDelete(msgs, filterOld));
+ throw new TypeError('The messages must be an Array, Collection, or number.');
+ }
+
+ /**
+ * Marks all messages in this channel as read.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<TextChannel|GroupDMChannel|DMChannel>}
+ * @deprecated
+ */
+ acknowledge() {
+ if (!this.lastMessageID) return Promise.resolve(this);
+ return this.client.rest.methods.ackTextChannel(this);
+ }
+
+ _cacheMessage(message) {
+ const maxSize = this.client.options.messageCacheMaxSize;
+ if (maxSize === 0) return null;
+ if (this.messages.size >= maxSize && maxSize > 0) this.messages.delete(this.messages.firstKey());
+ this.messages.set(message.id, message);
+ return message;
+ }
+}
+
+/** @lends TextBasedChannel.prototype */
+const Deprecated = {
+ /**
+ * Send a message to this channel.
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions} [options={}] Options for the message
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ * @example
+ * // Send a message
+ * channel.sendMessage('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ */
+ sendMessage(content, options) {
+ return this.send(content, options);
+ },
+
+ /**
+ * Send an embed to this channel.
+ * @param {RichEmbed|Object} embed Embed for the message
+ * @param {string} [content] Text for the message
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendEmbed(embed, content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+ return this.send(content, Object.assign(options, { embed }));
+ },
+
+ /**
+ * Send files to this channel.
+ * @param {FileOptions[]|string[]} files Files to send with the message
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendFiles(files, content, options = {}) {
+ return this.send(content, Object.assign(options, { files }));
+ },
+
+ /**
+ * Send a file to this channel.
+ * @param {BufferResolvable} attachment File to send
+ * @param {string} [name='file.jpg'] Name and extension of the file
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendFile(attachment, name, content, options = {}) {
+ return this.send({ files: [{ attachment, name }], content, options });
+ },
+
+ /**
+ * Send a code block to this channel.
+ * @param {string} lang Language for the code block
+ * @param {StringResolvable} content Content of the code block
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ */
+ sendCode(lang, content, options = {}) {
+ return this.send(content, Object.assign(options, { code: lang }));
+ },
+};
+
+for (const key of Object.keys(Deprecated)) {
+ TextBasedChannel.prototype[key] = util.deprecate(Deprecated[key], `TextChannel#${key}: use TextChannel#send instead`);
+}
+
+exports.applyToClass = (structure, full = false, ignore = []) => {
+ const props = ['send', 'sendMessage', 'sendEmbed', 'sendFile', 'sendFiles', 'sendCode'];
+ if (full) {
+ props.push(
+ '_cacheMessage',
+ 'acknowledge',
+ 'fetchMessages',
+ 'fetchMessage',
+ 'search',
+ 'lastMessage',
+ 'lastPinAt',
+ 'bulkDelete',
+ 'startTyping',
+ 'stopTyping',
+ 'typing',
+ 'typingCount',
+ 'fetchPinnedMessages',
+ 'createCollector',
+ 'createMessageCollector',
+ 'awaitMessages'
+ );
+ }
+ for (const prop of props) {
+ if (ignore.includes(prop)) continue;
+ Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop));
+ }
+};
+
+TextBasedChannel.prototype.acknowledge = util.deprecate(
+ TextBasedChannel.prototype.acknowledge, 'TextBasedChannel#acknowledge: userbot methods will be removed'
+);
+
+TextBasedChannel.prototype.search =
+ util.deprecate(TextBasedChannel.prototype.search, 'TextBasedChannel#search: userbot methods will be removed');
diff --git a/node_modules/discord.js/src/structures/shared/resolvePermissions.js b/node_modules/discord.js/src/structures/shared/resolvePermissions.js
new file mode 100644
index 0000000..c266d46
--- /dev/null
+++ b/node_modules/discord.js/src/structures/shared/resolvePermissions.js
@@ -0,0 +1,26 @@
+const Permissions = require('../../util/Permissions');
+const Collection = require('../../util/Collection');
+
+module.exports = function resolvePermissions(overwrites, guild) {
+ if (overwrites instanceof Collection || overwrites instanceof Array) {
+ overwrites = overwrites.map(overwrite => {
+ const role = this.client.resolver.resolveRole(guild, overwrite.id);
+ if (role) {
+ overwrite.id = role.id;
+ overwrite.type = 'role';
+ } else {
+ overwrite.id = this.client.resolver.resolveUserID(overwrite.id);
+ overwrite.type = 'member';
+ }
+
+ return {
+ allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0),
+ deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0),
+ type: overwrite.type,
+ id: overwrite.id,
+ };
+ });
+ }
+
+ return overwrites;
+};
diff --git a/node_modules/discord.js/src/util/BitField.js b/node_modules/discord.js/src/util/BitField.js
new file mode 100644
index 0000000..e0f86b1
--- /dev/null
+++ b/node_modules/discord.js/src/util/BitField.js
@@ -0,0 +1,160 @@
+/**
+ * Data structure that makes it easy to interact with a bitfield.
+ */
+class BitField {
+ /**
+ * @param {BitFieldResolvable} [bits=0] Bits(s) to read from
+ */
+ constructor(bits) {
+ /**
+ * Bitfield of the packed bits
+ * @type {number}
+ */
+ this.bitfield = this.constructor.resolve(bits);
+ }
+
+ /**
+ * Checks whether the bitfield has a bit, or any of multiple bits.
+ * @param {BitFieldResolvable} bit Bit(s) to check for
+ * @returns {boolean}
+ */
+ any(bit) {
+ return (this.bitfield & this.constructor.resolve(bit)) !== 0;
+ }
+
+ /**
+ * Checks if this bitfield equals another
+ * @param {BitFieldResolvable} bit Bit(s) to check for
+ * @returns {boolean}
+ */
+ equals(bit) {
+ return this.bitfield === this.constructor.resolve(bit);
+ }
+
+ /**
+ * Checks whether the bitfield has a bit, or multiple bits.
+ * @param {BitFieldResolvable} bit Bit(s) to check for
+ * @returns {boolean}
+ */
+ has(bit) {
+ if (Array.isArray(bit)) return bit.every(p => this.has(p));
+ bit = this.constructor.resolve(bit);
+ return (this.bitfield & bit) === bit;
+ }
+
+ /**
+ * Gets all given bits that are missing from the bitfield.
+ * @param {BitFieldResolvable} bits Bits(s) to check for
+ * @param {...*} hasParams Additional parameters for the has method, if any
+ * @returns {string[]}
+ */
+ missing(bits, ...hasParams) {
+ if (!Array.isArray(bits)) bits = new this.constructor(bits).toArray(false);
+ return bits.filter(p => !this.has(p, ...hasParams));
+ }
+
+ /**
+ * Freezes these bits, making them immutable.
+ * @returns {Readonly<BitField>} These bits
+ */
+ freeze() {
+ return Object.freeze(this);
+ }
+
+ /**
+ * Adds bits to these ones.
+ * @param {...BitFieldResolvable} [bits] Bits to add
+ * @returns {BitField} These bits or new BitField if the instance is frozen.
+ */
+ add(...bits) {
+ let total = 0;
+ for (const bit of bits) {
+ total |= this.constructor.resolve(bit);
+ }
+ if (Object.isFrozen(this)) return new this.constructor(this.bitfield | total);
+ this.bitfield |= total;
+ return this;
+ }
+
+ /**
+ * Removes bits from these.
+ * @param {...BitFieldResolvable} [bits] Bits to remove
+ * @returns {BitField} These bits or new BitField if the instance is frozen.
+ */
+ remove(...bits) {
+ let total = 0;
+ for (const bit of bits) {
+ total |= this.constructor.resolve(bit);
+ }
+ if (Object.isFrozen(this)) return new this.constructor(this.bitfield & ~total);
+ this.bitfield &= ~total;
+ return this;
+ }
+
+ /**
+ * Gets an object mapping field names to a {@link boolean} indicating whether the
+ * bit is available.
+ * @param {...*} hasParams Additional parameters for the has method, if any
+ * @returns {Object}
+ */
+ serialize(...hasParams) {
+ const serialized = {};
+ for (const flag of Object.keys(this.constructor.FLAGS)) {
+ serialized[flag] = this.has(this.constructor.FLAGS[flag], ...hasParams);
+ }
+ return serialized;
+ }
+
+ /**
+ * Gets an {@link Array} of bitfield names based on the bits available.
+ * @param {...*} hasParams Additional parameters for the has method, if any
+ * @returns {string[]}
+ */
+ toArray(...hasParams) {
+ return Object.keys(this.constructor.FLAGS).filter(bit => this.has(bit, ...hasParams));
+ }
+
+ toJSON() {
+ return this.bitfield;
+ }
+
+ valueOf() {
+ return this.bitfield;
+ }
+
+ *[Symbol.iterator]() {
+ yield* this.toArray();
+ }
+
+ /**
+ * Data that can be resolved to give a bitfield. This can be:
+ * * A string (see {@link BitField.FLAGS})
+ * * A bit number
+ * * An instance of BitField
+ * * An Array of BitFieldResolvable
+ * @typedef {string|number|BitField|BitFieldResolvable[]} BitFieldResolvable
+ */
+
+ /**
+ * Resolves bitfields to their numeric form.
+ * @param {BitFieldResolvable} [bit=0] - bit(s) to resolve
+ * @returns {number}
+ */
+ static resolve(bit = 0) {
+ if (typeof bit === 'number' && bit >= 0) return bit;
+ if (bit instanceof BitField) return bit.bitfield;
+ if (Array.isArray(bit)) return bit.map(p => this.resolve(p)).reduce((prev, p) => prev | p, 0);
+ if (typeof bit === 'string' && typeof this.FLAGS[bit] !== 'undefined') return this.FLAGS[bit];
+ throw new RangeError('Invalid bitfield flag or number.');
+ }
+}
+
+/**
+ * Numeric bitfield flags.
+ * <info>Defined in extension classes</info>
+ * @type {Object}
+ * @abstract
+ */
+BitField.FLAGS = {};
+
+module.exports = BitField;
diff --git a/node_modules/discord.js/src/util/Collection.js b/node_modules/discord.js/src/util/Collection.js
new file mode 100644
index 0000000..612b67c
--- /dev/null
+++ b/node_modules/discord.js/src/util/Collection.js
@@ -0,0 +1,532 @@
+const util = require('util');
+
+/**
+ * A Map with additional utility methods. This is used throughout discord.js rather than Arrays for anything that has
+ * an ID, for significantly improved performance and ease-of-use.
+ * @extends {Map}
+ */
+class Collection extends Map {
+ constructor(iterable) {
+ super(iterable);
+
+ /**
+ * Cached array for the `array()` method - will be reset to `null` whenever `set()` or `delete()` are called
+ * @name Collection#_array
+ * @type {?Array}
+ * @private
+ */
+ Object.defineProperty(this, '_array', { value: null, writable: true, configurable: true });
+
+ /**
+ * Cached array for the `keyArray()` method - will be reset to `null` whenever `set()` or `delete()` are called
+ * @name Collection#_keyArray
+ * @type {?Array}
+ * @private
+ */
+ Object.defineProperty(this, '_keyArray', { value: null, writable: true, configurable: true });
+ }
+
+ set(key, val) {
+ this._array = null;
+ this._keyArray = null;
+ return super.set(key, val);
+ }
+
+ delete(key) {
+ this._array = null;
+ this._keyArray = null;
+ return super.delete(key);
+ }
+
+ /**
+ * Creates an ordered array of the values of this collection, and caches it internally. The array will only be
+ * reconstructed if an item is added to or removed from the collection, or if you change the length of the array
+ * itself. If you don't want this caching behavior, use `[...collection.values()]` or
+ * `Array.from(collection.values())` instead.
+ * @returns {Array}
+ */
+ array() {
+ if (!this._array || this._array.length !== this.size) this._array = [...this.values()];
+ return this._array;
+ }
+
+ /**
+ * Creates an ordered array of the keys of this collection, and caches it internally. The array will only be
+ * reconstructed if an item is added to or removed from the collection, or if you change the length of the array
+ * itself. If you don't want this caching behavior, use `[...collection.keys()]` or
+ * `Array.from(collection.keys())` instead.
+ * @returns {Array}
+ */
+ keyArray() {
+ if (!this._keyArray || this._keyArray.length !== this.size) this._keyArray = [...this.keys()];
+ return this._keyArray;
+ }
+
+ /**
+ * Obtains the first value(s) in this collection.
+ * @param {number} [count] Number of values to obtain from the beginning
+ * @returns {*|Array<*>} The single value if `count` is undefined, or an array of values of `count` length
+ */
+ first(count) {
+ if (count === undefined) return this.values().next().value;
+ if (typeof count !== 'number') throw new TypeError('The count must be a number.');
+ if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.');
+ count = Math.min(this.size, count);
+ const arr = new Array(count);
+ const iter = this.values();
+ for (let i = 0; i < count; i++) arr[i] = iter.next().value;
+ return arr;
+ }
+
+ /**
+ * Obtains the first key(s) in this collection.
+ * @param {number} [count] Number of keys to obtain from the beginning
+ * @returns {*|Array<*>} The single key if `count` is undefined, or an array of keys of `count` length
+ */
+ firstKey(count) {
+ if (count === undefined) return this.keys().next().value;
+ if (typeof count !== 'number') throw new TypeError('The count must be a number.');
+ if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.');
+ count = Math.min(this.size, count);
+ const arr = new Array(count);
+ const iter = this.keys();
+ for (let i = 0; i < count; i++) arr[i] = iter.next().value;
+ return arr;
+ }
+
+ /**
+ * Obtains the last value(s) in this collection. This relies on {@link Collection#array}, and thus the caching
+ * mechanism applies here as well.
+ * @param {number} [count] Number of values to obtain from the end
+ * @returns {*|Array<*>} The single value if `count` is undefined, or an array of values of `count` length
+ */
+ last(count) {
+ const arr = this.array();
+ if (count === undefined) return arr[arr.length - 1];
+ if (typeof count !== 'number') throw new TypeError('The count must be a number.');
+ if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.');
+ return arr.slice(-count);
+ }
+
+ /**
+ * Obtains the last key(s) in this collection. This relies on {@link Collection#keyArray}, and thus the caching
+ * mechanism applies here as well.
+ * @param {number} [count] Number of keys to obtain from the end
+ * @returns {*|Array<*>} The single key if `count` is undefined, or an array of keys of `count` length
+ */
+ lastKey(count) {
+ const arr = this.keyArray();
+ if (count === undefined) return arr[arr.length - 1];
+ if (typeof count !== 'number') throw new TypeError('The count must be a number.');
+ if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.');
+ return arr.slice(-count);
+ }
+
+ /**
+ * Obtains random value(s) from this collection. This relies on {@link Collection#array}, and thus the caching
+ * mechanism applies here as well.
+ * @param {number} [count] Number of values to obtain randomly
+ * @returns {*|Array<*>} The single value if `count` is undefined, or an array of values of `count` length
+ */
+ random(count) {
+ let arr = this.array();
+ if (count === undefined) return arr[Math.floor(Math.random() * arr.length)];
+ if (typeof count !== 'number') throw new TypeError('The count must be a number.');
+ if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.');
+ if (arr.length === 0) return [];
+ const rand = new Array(count);
+ arr = arr.slice();
+ for (let i = 0; i < count; i++) rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0];
+ return rand;
+ }
+
+ /**
+ * Obtains random key(s) from this collection. This relies on {@link Collection#keyArray}, and thus the caching
+ * mechanism applies here as well.
+ * @param {number} [count] Number of keys to obtain randomly
+ * @returns {*|Array<*>} The single key if `count` is undefined, or an array of keys of `count` length
+ */
+ randomKey(count) {
+ let arr = this.keyArray();
+ if (count === undefined) return arr[Math.floor(Math.random() * arr.length)];
+ if (typeof count !== 'number') throw new TypeError('The count must be a number.');
+ if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.');
+ if (arr.length === 0) return [];
+ const rand = new Array(count);
+ arr = arr.slice();
+ for (let i = 0; i < count; i++) rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0];
+ return rand;
+ }
+
+ /**
+ * Searches for all items where their specified property's value is identical to the given value
+ * (`item[prop] === value`).
+ * @param {string} prop The property to test against
+ * @param {*} value The expected value
+ * @returns {Array}
+ * @deprecated
+ * @example
+ * collection.findAll('username', 'Bob');
+ */
+ findAll(prop, value) {
+ if (typeof prop !== 'string') throw new TypeError('Key must be a string.');
+ if (typeof value === 'undefined') throw new Error('Value must be specified.');
+ const results = [];
+ for (const item of this.values()) {
+ if (item[prop] === value) results.push(item);
+ }
+ return results;
+ }
+
+ /**
+ * Searches for a single item where its specified property's value is identical to the given value
+ * (`item[prop] === value`), or the given function returns a truthy value. In the latter case, this is identical to
+ * [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
+ * <warn>All collections used in Discord.js are mapped using their `id` property, and if you want to find by id you
+ * should use the `get` method. See
+ * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) for details.</warn>
+ * @param {string|Function} propOrFn The property to test against, or the function to test with
+ * @param {*} [value] The expected value - only applicable and required if using a property for the first argument
+ * @returns {*}
+ * @example
+ * collection.find('username', 'Bob');
+ * @example
+ * collection.find(val => val.username === 'Bob');
+ */
+ find(propOrFn, value) {
+ if (typeof propOrFn === 'string') {
+ if (typeof value === 'undefined') throw new Error('Value must be specified.');
+ for (const item of this.values()) {
+ if (item[propOrFn] === value) return item;
+ }
+ return null;
+ } else if (typeof propOrFn === 'function') {
+ for (const [key, val] of this) {
+ if (propOrFn(val, key, this)) return val;
+ }
+ return null;
+ } else {
+ throw new Error('First argument must be a property string or a function.');
+ }
+ }
+
+ /* eslint-disable max-len */
+ /**
+ * Searches for the key of a single item where its specified property's value is identical to the given value
+ * (`item[prop] === value`), or the given function returns a truthy value. In the latter case, this is identical to
+ * [Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex).
+ * @param {string|Function} propOrFn The property to test against, or the function to test with
+ * @param {*} [value] The expected value - only applicable and required if using a property for the first argument
+ * @returns {*}
+ * @example
+ * collection.findKey('username', 'Bob');
+ * @example
+ * collection.findKey(val => val.username === 'Bob');
+ */
+ findKey(propOrFn, value) {
+ /* eslint-enable max-len */
+ if (typeof propOrFn === 'string') {
+ if (typeof value === 'undefined') throw new Error('Value must be specified.');
+ for (const [key, val] of this) {
+ if (val[propOrFn] === value) return key;
+ }
+ return null;
+ } else if (typeof propOrFn === 'function') {
+ for (const [key, val] of this) {
+ if (propOrFn(val, key, this)) return key;
+ }
+ return null;
+ } else {
+ throw new Error('First argument must be a property string or a function.');
+ }
+ }
+
+ /**
+ * Searches for the existence of a single item where its specified property's value is identical to the given value
+ * (`item[prop] === value`).
+ * <warn>Do not use this to check for an item by its ID. Instead, use `collection.has(id)`. See
+ * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) for details.</warn>
+ * @param {string} prop The property to test against
+ * @param {*} value The expected value
+ * @returns {boolean}
+ * @deprecated
+ * @example
+ * if (collection.exists('username', 'Bob')) {
+ * console.log('user here!');
+ * }
+ */
+ exists(prop, value) {
+ return Boolean(this.find(prop, value));
+ }
+
+ /**
+ * Removes entries that satisfy the provided filter function.
+ * @param {Function} fn Function used to test (should return a boolean)
+ * @param {Object} [thisArg] Value to use as `this` when executing function
+ * @returns {number} The number of removed entries
+ */
+ sweep(fn, thisArg) {
+ if (thisArg) fn = fn.bind(thisArg);
+ const previousSize = this.size;
+ for (const [key, val] of this) {
+ if (fn(val, key, this)) this.delete(key);
+ }
+ return previousSize - this.size;
+ }
+
+ /**
+ * Identical to
+ * [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),
+ * but returns a Collection instead of an Array.
+ * @param {Function} fn Function used to test (should return a boolean)
+ * @param {Object} [thisArg] Value to use as `this` when executing function
+ * @returns {Collection}
+ */
+ filter(fn, thisArg) {
+ if (thisArg) fn = fn.bind(thisArg);
+ const results = new Collection();
+ for (const [key, val] of this) {
+ if (fn(val, key, this)) results.set(key, val);
+ }
+ return results;
+ }
+
+ /**
+ * Identical to
+ * [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).
+ * @param {Function} fn Function used to test (should return a boolean)
+ * @param {Object} [thisArg] Value to use as `this` when executing function
+ * @returns {Array}
+ * @deprecated
+ */
+ filterArray(fn, thisArg) {
+ if (thisArg) fn = fn.bind(thisArg);
+ const results = [];
+ for (const [key, val] of this) {
+ if (fn(val, key, this)) results.push(val);
+ }
+ return results;
+ }
+
+ /**
+ * Partitions the collection into two collections where the first collection
+ * contains the items that passed and the second contains the items that failed.
+ * @param {Function} fn Function used to test (should return a boolean)
+ * @param {*} [thisArg] Value to use as `this` when executing function
+ * @returns {Collection[]}
+ * @example const [big, small] = collection.partition(guild => guild.memberCount > 250);
+ */
+ partition(fn, thisArg) {
+ if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
+ const results = [new Collection(), new Collection()];
+ for (const [key, val] of this) {
+ if (fn(val, key, this)) {
+ results[0].set(key, val);
+ } else {
+ results[1].set(key, val);
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Identical to
+ * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
+ * @param {Function} fn Function that produces an element of the new array, taking three arguments
+ * @param {*} [thisArg] Value to use as `this` when executing function
+ * @returns {Array}
+ */
+ map(fn, thisArg) {
+ if (thisArg) fn = fn.bind(thisArg);
+ const arr = new Array(this.size);
+ let i = 0;
+ for (const [key, val] of this) arr[i++] = fn(val, key, this);
+ return arr;
+ }
+
+ /**
+ * Identical to
+ * [Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
+ * @param {Function} fn Function used to test (should return a boolean)
+ * @param {Object} [thisArg] Value to use as `this` when executing function
+ * @returns {boolean}
+ */
+ some(fn, thisArg) {
+ if (thisArg) fn = fn.bind(thisArg);
+ for (const [key, val] of this) {
+ if (fn(val, key, this)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Identical to
+ * [Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every).
+ * @param {Function} fn Function used to test (should return a boolean)
+ * @param {Object} [thisArg] Value to use as `this` when executing function
+ * @returns {boolean}
+ */
+ every(fn, thisArg) {
+ if (thisArg) fn = fn.bind(thisArg);
+ for (const [key, val] of this) {
+ if (!fn(val, key, this)) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Identical to
+ * [Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
+ * @param {Function} fn Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`,
+ * and `collection`
+ * @param {*} [initialValue] Starting value for the accumulator
+ * @returns {*}
+ */
+ reduce(fn, initialValue) {
+ let accumulator;
+ if (typeof initialValue !== 'undefined') {
+ accumulator = initialValue;
+ for (const [key, val] of this) accumulator = fn(accumulator, val, key, this);
+ } else {
+ let first = true;
+ for (const [key, val] of this) {
+ if (first) {
+ accumulator = val;
+ first = false;
+ continue;
+ }
+ accumulator = fn(accumulator, val, key, this);
+ }
+ }
+ return accumulator;
+ }
+
+ /**
+ * Identical to
+ * [Map.forEach()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach),
+ * but returns the collection instead of undefined.
+ * @param {Function} fn Function to execute for each element
+ * @param {*} [thisArg] Value to use as `this` when executing function
+ * @returns {Collection}
+ * @example
+ * collection
+ * .tap(user => console.log(user.username))
+ * .filter(user => user.bot)
+ * .tap(user => console.log(user.username));
+ */
+ tap(fn, thisArg) {
+ this.forEach(fn, thisArg);
+ return this;
+ }
+
+ /**
+ * Creates an identical shallow copy of this collection.
+ * @returns {Collection}
+ * @example const newColl = someColl.clone();
+ */
+ clone() {
+ return new this.constructor(this);
+ }
+
+ /**
+ * Combines this collection with others into a new collection. None of the source collections are modified.
+ * @param {...Collection} collections Collections to merge
+ * @returns {Collection}
+ * @example const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl);
+ */
+ concat(...collections) {
+ const newColl = this.clone();
+ for (const coll of collections) {
+ for (const [key, val] of coll) newColl.set(key, val);
+ }
+ return newColl;
+ }
+
+ /**
+ * Calls the `delete()` method on all items that have it.
+ * @returns {Promise[]}
+ */
+ deleteAll() {
+ const returns = [];
+ for (const item of this.values()) {
+ if (item.delete) returns.push(item.delete());
+ }
+ return returns;
+ }
+
+ /**
+ * Checks if this collection shares identical key-value pairings with another.
+ * This is different to checking for equality using equal-signs, because
+ * the collections may be different objects, but contain the same data.
+ * @param {Collection} collection Collection to compare with
+ * @returns {boolean} Whether the collections have identical contents
+ */
+ equals(collection) {
+ if (!collection) return false;
+ if (this === collection) return true;
+ if (this.size !== collection.size) return false;
+ return !this.find((value, key) => {
+ const testVal = collection.get(key);
+ return testVal !== value || (testVal === undefined && !collection.has(key));
+ });
+ }
+
+ /**
+ * The sort() method sorts the elements of a collection in place and returns the collection.
+ * The sort is not necessarily stable. The default sort order is according to string Unicode code points.
+ * @param {Function} [compareFunction] Specifies a function that defines the sort order.
+ * if omitted, the collection is sorted according to each character's Unicode code point value,
+ * according to the string conversion of each element.
+ * @returns {Collection}
+ */
+ sort(compareFunction = (x, y) => +(x > y) || +(x === y) - 1) {
+ return new Collection([...this.entries()].sort((a, b) => compareFunction(a[1], b[1], a[0], b[0])));
+ }
+}
+
+Collection.prototype.findAll =
+ util.deprecate(Collection.prototype.findAll, 'Collection#findAll: use Collection#filter instead');
+
+Collection.prototype.filterArray =
+ util.deprecate(Collection.prototype.filterArray, 'Collection#filterArray: use Collection#filter instead');
+
+Collection.prototype.exists =
+ util.deprecate(Collection.prototype.exists, 'Collection#exists: use Collection#some instead');
+
+Collection.prototype.find = function find(propOrFn, value) {
+ if (typeof propOrFn === 'string') {
+ process.emitWarning('Collection#find: pass a function instead', 'DeprecationWarning');
+ if (typeof value === 'undefined') throw new Error('Value must be specified.');
+ for (const item of this.values()) {
+ if (item[propOrFn] === value) return item;
+ }
+ return null;
+ } else if (typeof propOrFn === 'function') {
+ for (const [key, val] of this) {
+ if (propOrFn(val, key, this)) return val;
+ }
+ return null;
+ } else {
+ throw new Error('First argument must be a property string or a function.');
+ }
+};
+
+Collection.prototype.findKey = function findKey(propOrFn, value) {
+ if (typeof propOrFn === 'string') {
+ process.emitWarning('Collection#findKey: pass a function instead', 'DeprecationWarning');
+ if (typeof value === 'undefined') throw new Error('Value must be specified.');
+ for (const [key, val] of this) {
+ if (val[propOrFn] === value) return key;
+ }
+ return null;
+ } else if (typeof propOrFn === 'function') {
+ for (const [key, val] of this) {
+ if (propOrFn(val, key, this)) return key;
+ }
+ return null;
+ } else {
+ throw new Error('First argument must be a property string or a function.');
+ }
+};
+
+module.exports = Collection;
diff --git a/node_modules/discord.js/src/util/Constants.js b/node_modules/discord.js/src/util/Constants.js
new file mode 100644
index 0000000..3a2392b
--- /dev/null
+++ b/node_modules/discord.js/src/util/Constants.js
@@ -0,0 +1,943 @@
+exports.Package = require('../../package.json');
+
+/**
+ * Options for a client.
+ * @typedef {Object} ClientOptions
+ * @property {string} [apiRequestMethod='sequential'] One of `sequential` or `burst`. The sequential handler executes
+ * all requests in the order they are triggered, whereas the burst handler runs multiple in parallel, and doesn't
+ * provide the guarantee of any particular order. Burst mode is more likely to hit a 429 ratelimit error by its nature,
+ * and is therefore slightly riskier to use.
+ * @property {number} [shardId=0] ID of the shard to run
+ * @property {number} [shardCount=0] Total number of shards
+ * @property {number} [messageCacheMaxSize=200] Maximum number of messages to cache per channel
+ * (-1 or Infinity for unlimited - don't do this without message sweeping, otherwise memory usage will climb
+ * indefinitely)
+ * @property {number} [messageCacheLifetime=0] How long a message should stay in the cache until it is considered
+ * sweepable (in seconds, 0 for forever)
+ * @property {number} [messageSweepInterval=0] How frequently to remove messages from the cache that are older than
+ * the message cache lifetime (in seconds, 0 for never)
+ * @property {boolean} [fetchAllMembers=false] Whether to cache all guild members and users upon startup, as well as
+ * upon joining a guild (should be avoided whenever possible)
+ * @property {boolean} [disableEveryone=false] Default value for {@link MessageOptions#disableEveryone}
+ * @property {boolean} [sync=false] Whether to periodically sync guilds (for user accounts)
+ * @property {number} [restWsBridgeTimeout=5000] Maximum time permitted between REST responses and their
+ * corresponding websocket events
+ * @property {number} [restTimeOffset=500] Extra time in millseconds to wait before continuing to make REST
+ * requests (higher values will reduce rate-limiting errors on bad connections)
+ * @property {number} [retryLimit=Infinity] How many times to retry on 5XX errors
+ * (Infinity for indefinite amount of retries)
+ * @property {WSEventType[]} [disabledEvents] An array of disabled websocket events. Events in this array will not be
+ * processed, potentially resulting in performance improvements for larger bots. Only disable events you are
+ * 100% certain you don't need, as many are important, but not obviously so. The safest one to disable with the
+ * most impact is typically `TYPING_START`.
+ * @property {WebsocketOptions} [ws] Options for the WebSocket
+ * @property {HTTPOptions} [http] HTTP options
+ */
+exports.DefaultOptions = {
+ apiRequestMethod: 'sequential',
+ shardId: 0,
+ shardCount: 0,
+ messageCacheMaxSize: 200,
+ messageCacheLifetime: 0,
+ messageSweepInterval: 0,
+ fetchAllMembers: false,
+ disableEveryone: false,
+ sync: false,
+ restWsBridgeTimeout: 5000,
+ retryLimit: Infinity,
+ disabledEvents: [],
+ restTimeOffset: 500,
+
+ /**
+ * WebSocket options (these are left as snake_case to match the API)
+ * @typedef {Object} WebsocketOptions
+ * @property {number} [large_threshold=250] Number of members in a guild to be considered large
+ * @property {boolean} [compress=true] Whether to compress data sent on the connection
+ * (defaults to `false` for browsers)
+ */
+ ws: {
+ large_threshold: 250,
+ compress: require('os').platform() !== 'browser',
+ properties: {
+ $os: process ? process.platform : 'discord.js',
+ $browser: 'discord.js',
+ $device: 'discord.js',
+ $referrer: '',
+ $referring_domain: '',
+ },
+ version: 6,
+ },
+
+ /**
+ * HTTP options
+ * @typedef {Object} HTTPOptions
+ * @property {number} [version=7] API version to use
+ * @property {string} [api='https://discordapp.com/api'] Base url of the API
+ * @property {string} [cdn='https://cdn.discordapp.com'] Base url of the CDN
+ * @property {string} [invite='https://discord.gg'] Base url of invites
+ */
+ http: {
+ version: 7,
+ host: 'https://discordapp.com',
+ cdn: 'https://cdn.discordapp.com',
+ },
+};
+
+exports.WSCodes = {
+ 1000: 'Connection gracefully closed',
+ 4004: 'Tried to identify with an invalid token',
+ 4010: 'Sharding data provided was invalid',
+ 4011: 'Shard would be on too many guilds if connected',
+};
+
+exports.Errors = {
+ NO_TOKEN: 'Request to use token, but token was unavailable to the client.',
+ NO_BOT_ACCOUNT: 'Only bot accounts are able to make use of this feature.',
+ NO_USER_ACCOUNT: 'Only user accounts are able to make use of this feature.',
+ BAD_WS_MESSAGE: 'A bad message was received from the websocket; either bad compression, or not JSON.',
+ TOOK_TOO_LONG: 'Something took too long to do.',
+ NOT_A_PERMISSION: 'Invalid permission string or number.',
+ INVALID_RATE_LIMIT_METHOD: 'Unknown rate limiting method.',
+ BAD_LOGIN: 'Incorrect login details were provided.',
+ INVALID_SHARD: 'Invalid shard settings were provided.',
+ SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.',
+ INVALID_TOKEN: 'An invalid token was provided.',
+};
+
+const Endpoints = exports.Endpoints = {
+ User: userID => {
+ if (userID.id) userID = userID.id;
+ const base = `/users/${userID}`;
+ return {
+ toString: () => base,
+ channels: `${base}/channels`,
+ profile: `${base}/profile`,
+ relationships: `${base}/relationships`,
+ settings: `${base}/settings`,
+ Relationship: uID => `${base}/relationships/${uID}`,
+ Guild: guildID => ({
+ toString: () => `${base}/guilds/${guildID}`,
+ settings: `${base}/guilds/${guildID}/settings`,
+ }),
+ Note: id => `${base}/notes/${id}`,
+ Mentions: (limit, roles, everyone, guildID) =>
+ `${base}/mentions?limit=${limit}&roles=${roles}&everyone=${everyone}${guildID ? `&guild_id=${guildID}` : ''}`,
+ Avatar: (root, hash) => {
+ if (userID === '1') return hash;
+ return Endpoints.CDN(root).Avatar(userID, hash);
+ },
+ };
+ },
+ guilds: '/guilds',
+ Guild: guildID => {
+ if (guildID.id) guildID = guildID.id;
+ const base = `/guilds/${guildID}`;
+ return {
+ toString: () => base,
+ prune: `${base}/prune`,
+ embed: `${base}/embed`,
+ bans: `${base}/bans`,
+ integrations: `${base}/integrations`,
+ members: `${base}/members`,
+ channels: `${base}/channels`,
+ invites: `${base}/invites`,
+ roles: `${base}/roles`,
+ emojis: `${base}/emojis`,
+ search: `${base}/messages/search`,
+ vanityURL: `${base}/vanity-url`,
+ voiceRegions: `${base}/regions`,
+ webhooks: `${base}/webhooks`,
+ ack: `${base}/ack`,
+ settings: `${base}/settings`,
+ auditLogs: `${base}/audit-logs`,
+ Emoji: emojiID => `${base}/emojis/${emojiID}`,
+ Icon: (root, hash) => Endpoints.CDN(root).Icon(guildID, hash),
+ Banner: (root, hash) => Endpoints.CDN(root).Banner(guildID, hash),
+ Splash: (root, hash) => Endpoints.CDN(root).Splash(guildID, hash),
+ Role: roleID => `${base}/roles/${roleID}`,
+ Member: memberID => {
+ if (memberID.id) memberID = memberID.id;
+ const mbase = `${base}/members/${memberID}`;
+ return {
+ toString: () => mbase,
+ Role: roleID => `${mbase}/roles/${roleID}`,
+ nickname: `${base}/members/@me/nick`,
+ };
+ },
+ Integration: id => `${base}/integrations/${id}`,
+ };
+ },
+ channels: '/channels',
+ Channel: channelID => {
+ if (channelID.id) channelID = channelID.id;
+ const base = `/channels/${channelID}`;
+ return {
+ toString: () => base,
+ messages: {
+ toString: () => `${base}/messages`,
+ bulkDelete: `${base}/messages/bulk-delete`,
+ },
+ invites: `${base}/invites`,
+ typing: `${base}/typing`,
+ permissions: `${base}/permissions`,
+ webhooks: `${base}/webhooks`,
+ search: `${base}/messages/search`,
+ pins: `${base}/pins`,
+ Icon: (root, hash) => Endpoints.CDN(root).GDMIcon(channelID, hash),
+ Pin: messageID => `${base}/pins/${messageID}`,
+ Recipient: recipientID => `${base}/recipients/${recipientID}`,
+ Message: messageID => {
+ if (messageID.id) messageID = messageID.id;
+ const mbase = `${base}/messages/${messageID}`;
+ return {
+ toString: () => mbase,
+ reactions: `${mbase}/reactions`,
+ ack: `${mbase}/ack`,
+ Reaction: emoji => {
+ const rbase = `${mbase}/reactions/${emoji}`;
+ return {
+ toString: () => rbase,
+ User: userID => `${rbase}/${userID}`,
+ };
+ },
+ };
+ },
+ };
+ },
+ Message: m => exports.Endpoints.Channel(m.channel).Message(m),
+ Member: m => exports.Endpoints.Guild(m.guild).Member(m),
+ CDN(root) {
+ return {
+ Emoji: (emojiID, format = 'png') => `${root}/emojis/${emojiID}.${format}`,
+ Asset: name => `${root}/assets/${name}`,
+ Avatar: (userID, hash) => `${root}/avatars/${userID}/${hash}.${hash.startsWith('a_') ? 'gif' : 'png?size=2048'}`,
+ Icon: (guildID, hash) => `${root}/icons/${guildID}/${hash}.jpg`,
+ Banner: (guildID, hash) => `${root}/banners/${guildID}/${hash}.jpg`,
+ AppIcon: (clientID, hash) => `${root}/app-icons/${clientID}/${hash}.png`,
+ AppAsset: (clientID, hash) => `${root}/app-assets/${clientID}/${hash}.png`,
+ GDMIcon: (channelID, hash) => `${root}/channel-icons/${channelID}/${hash}.jpg?size=2048`,
+ Splash: (guildID, hash) => `${root}/splashes/${guildID}/${hash}.jpg`,
+ TeamIcon: (teamID, hash) => `${root}/team-icons/${teamID}/${hash}.jpg`,
+ };
+ },
+ OAUTH2: {
+ Application: appID => {
+ const base = `/oauth2/applications/${appID}`;
+ return {
+ toString: () => base,
+ resetSecret: `${base}/reset`,
+ resetToken: `${base}/bot/reset`,
+ };
+ },
+ App: appID => `/oauth2/authorize?client_id=${appID}`,
+ },
+ login: '/auth/login',
+ logout: '/auth/logout',
+ voiceRegions: '/voice/regions',
+ gateway: {
+ toString: () => '/gateway',
+ bot: '/gateway/bot',
+ },
+ Invite: inviteID => `/invite/${inviteID}?with_counts=true`,
+ inviteLink: id => `https://discord.gg/${id}`,
+ Webhook: (webhookID, token) => `/webhooks/${webhookID}${token ? `/${token}` : ''}`,
+};
+
+
+/**
+ * The current status of the client. Here are the available statuses:
+ * * READY: 0
+ * * CONNECTING: 1
+ * * RECONNECTING: 2
+ * * IDLE: 3
+ * * NEARLY: 4
+ * * DISCONNECTED: 5
+ * @typedef {number} Status
+ */
+exports.Status = {
+ READY: 0,
+ CONNECTING: 1,
+ RECONNECTING: 2,
+ IDLE: 3,
+ NEARLY: 4,
+ DISCONNECTED: 5,
+};
+
+/**
+ * The current status of a voice connection. Here are the available statuses:
+ * * CONNECTED: 0
+ * * CONNECTING: 1
+ * * AUTHENTICATING: 2
+ * * RECONNECTING: 3
+ * * DISCONNECTED: 4
+ * @typedef {number} VoiceStatus
+ */
+exports.VoiceStatus = {
+ CONNECTED: 0,
+ CONNECTING: 1,
+ AUTHENTICATING: 2,
+ RECONNECTING: 3,
+ DISCONNECTED: 4,
+};
+
+exports.ChannelTypes = {
+ TEXT: 0,
+ DM: 1,
+ VOICE: 2,
+ GROUP_DM: 3,
+ CATEGORY: 4,
+ NEWS: 5,
+ STORE: 6,
+};
+
+exports.OPCodes = {
+ DISPATCH: 0,
+ HEARTBEAT: 1,
+ IDENTIFY: 2,
+ STATUS_UPDATE: 3,
+ VOICE_STATE_UPDATE: 4,
+ VOICE_GUILD_PING: 5,
+ RESUME: 6,
+ RECONNECT: 7,
+ REQUEST_GUILD_MEMBERS: 8,
+ INVALID_SESSION: 9,
+ HELLO: 10,
+ HEARTBEAT_ACK: 11,
+};
+
+exports.VoiceOPCodes = {
+ IDENTIFY: 0,
+ SELECT_PROTOCOL: 1,
+ READY: 2,
+ HEARTBEAT: 3,
+ SESSION_DESCRIPTION: 4,
+ SPEAKING: 5,
+};
+
+exports.Events = {
+ RATE_LIMIT: 'rateLimit',
+ READY: 'ready',
+ RESUME: 'resume',
+ GUILD_CREATE: 'guildCreate',
+ GUILD_DELETE: 'guildDelete',
+ GUILD_UPDATE: 'guildUpdate',
+ GUILD_UNAVAILABLE: 'guildUnavailable',
+ GUILD_AVAILABLE: 'guildAvailable',
+ GUILD_MEMBER_ADD: 'guildMemberAdd',
+ GUILD_MEMBER_REMOVE: 'guildMemberRemove',
+ GUILD_MEMBER_UPDATE: 'guildMemberUpdate',
+ GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable',
+ GUILD_MEMBER_SPEAKING: 'guildMemberSpeaking',
+ GUILD_MEMBERS_CHUNK: 'guildMembersChunk',
+ GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate',
+ GUILD_ROLE_CREATE: 'roleCreate',
+ GUILD_ROLE_DELETE: 'roleDelete',
+ GUILD_ROLE_UPDATE: 'roleUpdate',
+ GUILD_EMOJI_CREATE: 'emojiCreate',
+ GUILD_EMOJI_DELETE: 'emojiDelete',
+ GUILD_EMOJI_UPDATE: 'emojiUpdate',
+ GUILD_BAN_ADD: 'guildBanAdd',
+ GUILD_BAN_REMOVE: 'guildBanRemove',
+ INVITE_CREATE: 'inviteCreate',
+ INVITE_DELETE: 'inviteDelete',
+ CHANNEL_CREATE: 'channelCreate',
+ CHANNEL_DELETE: 'channelDelete',
+ CHANNEL_UPDATE: 'channelUpdate',
+ CHANNEL_PINS_UPDATE: 'channelPinsUpdate',
+ MESSAGE_CREATE: 'message',
+ MESSAGE_DELETE: 'messageDelete',
+ MESSAGE_UPDATE: 'messageUpdate',
+ MESSAGE_BULK_DELETE: 'messageDeleteBulk',
+ MESSAGE_REACTION_ADD: 'messageReactionAdd',
+ MESSAGE_REACTION_REMOVE: 'messageReactionRemove',
+ MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji',
+ MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll',
+ USER_UPDATE: 'userUpdate',
+ USER_NOTE_UPDATE: 'userNoteUpdate',
+ USER_SETTINGS_UPDATE: 'clientUserSettingsUpdate',
+ USER_GUILD_SETTINGS_UPDATE: 'clientUserGuildSettingsUpdate',
+ PRESENCE_UPDATE: 'presenceUpdate',
+ VOICE_STATE_UPDATE: 'voiceStateUpdate',
+ TYPING_START: 'typingStart',
+ TYPING_STOP: 'typingStop',
+ WEBHOOKS_UPDATE: 'webhookUpdate',
+ DISCONNECT: 'disconnect',
+ RECONNECTING: 'reconnecting',
+ ERROR: 'error',
+ WARN: 'warn',
+ DEBUG: 'debug',
+};
+
+/**
+ * <info>Bots cannot set a `CUSTOM_STATUS`, it is only for custom statuses received from users</info>
+ * The type of an activity of a users presence, e.g. `PLAYING`. Here are the available types:
+ * * PLAYING
+ * * STREAMING
+ * * LISTENING
+ * * WATCHING
+ * * CUSTOM_STATUS
+ * @typedef {string} ActivityType
+ */
+exports.ActivityTypes = [
+ 'PLAYING',
+ 'STREAMING',
+ 'LISTENING',
+ 'WATCHING',
+ 'CUSTOM_STATUS',
+];
+
+/**
+ * Numeric activity flags. All available properties:
+ * * `INSTANCE`
+ * * `JOIN`
+ * * `SPECTATE`
+ * * `JOIN_REQUEST`
+ * * `SYNC`
+ * * `PLAY`
+ * @typedef {string} ActivityFlag
+ * @see {@link https://discordapp.com/developers/docs/topics/gateway#activity-object-activity-flags}
+ */
+exports.ActivityFlags = {
+ INSTANCE: 1 << 0,
+ JOIN: 1 << 1,
+ SPECTATE: 1 << 2,
+ JOIN_REQUEST: 1 << 3,
+ SYNC: 1 << 4,
+ PLAY: 1 << 5,
+};
+
+/**
+ * The type of a websocket message event, e.g. `MESSAGE_CREATE`. Here are the available events:
+ * * READY
+ * * RESUMED
+ * * GUILD_SYNC
+ * * GUILD_CREATE
+ * * GUILD_DELETE
+ * * GUILD_UPDATE
+ * * GUILD_MEMBER_ADD
+ * * GUILD_MEMBER_REMOVE
+ * * GUILD_MEMBER_UPDATE
+ * * GUILD_MEMBERS_CHUNK
+ * * GUILD_INTEGRATIONS_UPDATE
+ * * GUILD_ROLE_CREATE
+ * * GUILD_ROLE_DELETE
+ * * GUILD_ROLE_UPDATE
+ * * GUILD_BAN_ADD
+ * * GUILD_BAN_REMOVE
+ * * CHANNEL_CREATE
+ * * CHANNEL_DELETE
+ * * CHANNEL_UPDATE
+ * * CHANNEL_PINS_UPDATE
+ * * MESSAGE_CREATE
+ * * MESSAGE_DELETE
+ * * MESSAGE_UPDATE
+ * * MESSAGE_DELETE_BULK
+ * * MESSAGE_REACTION_ADD
+ * * MESSAGE_REACTION_REMOVE
+ * * MESSAGE_REACTION_REMOVE_EMOJI
+ * * MESSAGE_REACTION_REMOVE_ALL
+ * * USER_UPDATE
+ * * USER_NOTE_UPDATE
+ * * USER_SETTINGS_UPDATE
+ * * PRESENCE_UPDATE
+ * * VOICE_STATE_UPDATE
+ * * TYPING_START
+ * * VOICE_SERVER_UPDATE
+ * * RELATIONSHIP_ADD
+ * * RELATIONSHIP_REMOVE
+ * * WEBHOOKS_UPDATE
+ * @typedef {string} WSEventType
+ */
+exports.WSEvents = {
+ READY: 'READY',
+ RESUMED: 'RESUMED',
+ GUILD_SYNC: 'GUILD_SYNC',
+ GUILD_CREATE: 'GUILD_CREATE',
+ GUILD_DELETE: 'GUILD_DELETE',
+ GUILD_UPDATE: 'GUILD_UPDATE',
+ GUILD_MEMBER_ADD: 'GUILD_MEMBER_ADD',
+ GUILD_MEMBER_REMOVE: 'GUILD_MEMBER_REMOVE',
+ GUILD_MEMBER_UPDATE: 'GUILD_MEMBER_UPDATE',
+ GUILD_MEMBERS_CHUNK: 'GUILD_MEMBERS_CHUNK',
+ GUILD_INTEGRATIONS_UPDATE: 'GUILD_INTEGRATIONS_UPDATE',
+ GUILD_ROLE_CREATE: 'GUILD_ROLE_CREATE',
+ GUILD_ROLE_DELETE: 'GUILD_ROLE_DELETE',
+ GUILD_ROLE_UPDATE: 'GUILD_ROLE_UPDATE',
+ GUILD_BAN_ADD: 'GUILD_BAN_ADD',
+ GUILD_BAN_REMOVE: 'GUILD_BAN_REMOVE',
+ GUILD_EMOJIS_UPDATE: 'GUILD_EMOJIS_UPDATE',
+ INVITE_CREATE: 'INVITE_CREATE',
+ INVITE_DELETE: 'INVITE_DELETE',
+ CHANNEL_CREATE: 'CHANNEL_CREATE',
+ CHANNEL_DELETE: 'CHANNEL_DELETE',
+ CHANNEL_UPDATE: 'CHANNEL_UPDATE',
+ CHANNEL_PINS_UPDATE: 'CHANNEL_PINS_UPDATE',
+ MESSAGE_CREATE: 'MESSAGE_CREATE',
+ MESSAGE_DELETE: 'MESSAGE_DELETE',
+ MESSAGE_UPDATE: 'MESSAGE_UPDATE',
+ MESSAGE_DELETE_BULK: 'MESSAGE_DELETE_BULK',
+ MESSAGE_REACTION_ADD: 'MESSAGE_REACTION_ADD',
+ MESSAGE_REACTION_REMOVE: 'MESSAGE_REACTION_REMOVE',
+ MESSAGE_REACTION_REMOVE_EMOJI: 'MESSAGE_REACTION_REMOVE_EMOJI',
+ MESSAGE_REACTION_REMOVE_ALL: 'MESSAGE_REACTION_REMOVE_ALL',
+ USER_UPDATE: 'USER_UPDATE',
+ USER_NOTE_UPDATE: 'USER_NOTE_UPDATE',
+ USER_SETTINGS_UPDATE: 'USER_SETTINGS_UPDATE',
+ USER_GUILD_SETTINGS_UPDATE: 'USER_GUILD_SETTINGS_UPDATE',
+ PRESENCE_UPDATE: 'PRESENCE_UPDATE',
+ VOICE_STATE_UPDATE: 'VOICE_STATE_UPDATE',
+ TYPING_START: 'TYPING_START',
+ VOICE_SERVER_UPDATE: 'VOICE_SERVER_UPDATE',
+ RELATIONSHIP_ADD: 'RELATIONSHIP_ADD',
+ RELATIONSHIP_REMOVE: 'RELATIONSHIP_REMOVE',
+ WEBHOOKS_UPDATE: 'WEBHOOKS_UPDATE',
+};
+
+/**
+ * The type of a message, e.g. `DEFAULT`. Here are the available types:
+ * * DEFAULT
+ * * RECIPIENT_ADD
+ * * RECIPIENT_REMOVE
+ * * CALL
+ * * CHANNEL_NAME_CHANGE
+ * * CHANNEL_ICON_CHANGE
+ * * PINS_ADD
+ * * GUILD_MEMBER_JOIN
+ * * USER_PREMIUM_GUILD_SUBSCRIPTION
+ * * USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1
+ * * USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2
+ * * USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3
+ * * CHANNEL_FOLLOW_ADD
+ * * GUILD_DISCOVERY_DISQUALIFIED
+ * * GUILD_DISCOVERY_REQUALIFIED
+ * @typedef {string} MessageType
+ */
+exports.MessageTypes = [
+ 'DEFAULT',
+ 'RECIPIENT_ADD',
+ 'RECIPIENT_REMOVE',
+ 'CALL',
+ 'CHANNEL_NAME_CHANGE',
+ 'CHANNEL_ICON_CHANGE',
+ 'PINS_ADD',
+ 'GUILD_MEMBER_JOIN',
+ 'USER_PREMIUM_GUILD_SUBSCRIPTION',
+ 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1',
+ 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2',
+ 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3',
+ 'CHANNEL_FOLLOW_ADD',
+ // 13 isn't yet documented
+ null,
+ 'GUILD_DISCOVERY_DISQUALIFIED',
+ 'GUILD_DISCOVERY_REQUALIFIED',
+];
+
+/**
+ * The type of a message notification setting. Here are the available types:
+ * * EVERYTHING
+ * * MENTIONS
+ * * NOTHING
+ * * INHERIT (only for GuildChannel)
+ * @typedef {string} MessageNotificationType
+ */
+exports.MessageNotificationTypes = [
+ 'EVERYTHING',
+ 'MENTIONS',
+ 'NOTHING',
+ 'INHERIT',
+];
+
+exports.DefaultAvatars = {
+ BLURPLE: '6debd47ed13483642cf09e832ed0bc1b',
+ GREY: '322c936a8c8be1b803cd94861bdfa868',
+ GREEN: 'dd4dbc0016779df1378e7812eabaa04d',
+ ORANGE: '0e291f67c9274a1abdddeb3fd919cbaa',
+ RED: '1cbd08c76f8af6dddce02c5138971129',
+};
+
+exports.ExplicitContentFilterTypes = [
+ 'DISABLED',
+ 'NON_FRIENDS',
+ 'FRIENDS_AND_NON_FRIENDS',
+];
+
+exports.UserSettingsMap = {
+ /**
+ * Automatically convert emoticons in your messages to emoji
+ * For example, when you type `:-)` Discord will convert it to 😃
+ * @name ClientUserSettings#convertEmoticons
+ * @type {boolean}
+ */
+ convert_emoticons: 'convertEmoticons',
+
+ /**
+ * If new guilds should automatically disable DMs between you and its members
+ * @name ClientUserSettings#defaultGuildsRestricted
+ * @type {boolean}
+ */
+ default_guilds_restricted: 'defaultGuildsRestricted',
+
+ /**
+ * Automatically detect accounts from services like Steam and Blizzard when you open the Discord client
+ * @name ClientUserSettings#detectPlatformAccounts
+ * @type {boolean}
+ */
+ detect_platform_accounts: 'detectPlatformAccounts',
+
+ /**
+ * Developer Mode exposes context menu items helpful for people writing bots using the Discord API
+ * @name ClientUserSettings#developerMode
+ * @type {boolean}
+ */
+ developer_mode: 'developerMode',
+
+ /**
+ * Allow playback and usage of the `/tts` command
+ * @name ClientUserSettings#enableTTSCommand
+ * @type {boolean}
+ */
+ enable_tts_command: 'enableTTSCommand',
+
+ /**
+ * The theme of the client. Either `light` or `dark`
+ * @name ClientUserSettings#theme
+ * @type {string}
+ */
+ theme: 'theme',
+
+ /**
+ * Last status set in the client
+ * @name ClientUserSettings#status
+ * @type {PresenceStatus}
+ */
+ status: 'status',
+
+ /**
+ * Display currently running game as status message
+ * @name ClientUserSettings#showCurrentGame
+ * @type {boolean}
+ */
+ show_current_game: 'showCurrentGame',
+
+ /**
+ * Display images, videos, and lolcats when uploaded directly to Discord
+ * @name ClientUserSettings#inlineAttachmentMedia
+ * @type {boolean}
+ */
+ inline_attachment_media: 'inlineAttachmentMedia',
+
+ /**
+ * Display images, videos, and lolcats when uploaded posted as links in chat
+ * @name ClientUserSettings#inlineEmbedMedia
+ * @type {boolean}
+ */
+ inline_embed_media: 'inlineEmbedMedia',
+
+ /**
+ * Language the Discord client will use, as an RFC 3066 language identifier
+ * @name ClientUserSettings#locale
+ * @type {string}
+ */
+ locale: 'locale',
+
+ /**
+ * Display messages in compact mode
+ * @name ClientUserSettings#messageDisplayCompact
+ * @type {boolean}
+ */
+ message_display_compact: 'messageDisplayCompact',
+
+ /**
+ * Show emoji reactions on messages
+ * @name ClientUserSettings#renderReactions
+ * @type {boolean}
+ */
+ render_reactions: 'renderReactions',
+
+ /**
+ * Array of snowflake IDs for guilds, in the order they appear in the Discord client
+ * @name ClientUserSettings#guildPositions
+ * @type {Snowflake[]}
+ */
+ guild_positions: 'guildPositions',
+
+ /**
+ * Array of snowflake IDs for guilds which you will not recieve DMs from
+ * @name ClientUserSettings#restrictedGuilds
+ * @type {Snowflake[]}
+ */
+ restricted_guilds: 'restrictedGuilds',
+
+ explicit_content_filter: function explicitContentFilter(type) { // eslint-disable-line func-name-matching
+ /**
+ * Safe direct messaging; force people's messages with images to be scanned before they are sent to you.
+ * One of `DISABLED`, `NON_FRIENDS`, `FRIENDS_AND_NON_FRIENDS`
+ * @name ClientUserSettings#explicitContentFilter
+ * @type {string}
+ */
+ return exports.ExplicitContentFilterTypes[type];
+ },
+ friend_source_flags: function friendSources(flags) { // eslint-disable-line func-name-matching
+ /**
+ * Who can add you as a friend
+ * @name ClientUserSettings#friendSources
+ * @type {Object}
+ * @property {boolean} all Mutual friends and mutual guilds
+ * @property {boolean} mutualGuilds Only mutual guilds
+ * @property {boolean} mutualFriends Only mutual friends
+ */
+ return {
+ all: flags.all || false,
+ mutualGuilds: flags.all ? true : flags.mutual_guilds || false,
+ mutualFriends: flags.all ? true : flags.mutualFriends || false,
+ };
+ },
+};
+
+exports.UserGuildSettingsMap = {
+ message_notifications: function messageNotifications(type) { // eslint-disable-line func-name-matching
+ /**
+ * The type of message that should notify you
+ * @name ClientUserGuildSettings#messageNotifications
+ * @type {MessageNotificationType}
+ */
+ return exports.MessageNotificationTypes[type];
+ },
+ /**
+ * Whether to receive mobile push notifications
+ * @name ClientUserGuildSettings#mobilePush
+ * @type {boolean}
+ */
+ mobile_push: 'mobilePush',
+ /**
+ * Whether the guild is muted
+ * @name ClientUserGuildSettings#muted
+ * @type {boolean}
+ */
+ muted: 'muted',
+ /**
+ * Whether to suppress everyone mention
+ * @name ClientUserGuildSettings#suppressEveryone
+ * @type {boolean}
+ */
+ suppress_everyone: 'suppressEveryone',
+ /**
+ * A collection containing all the channel overrides
+ * @name ClientUserGuildSettings#channelOverrides
+ * @type {Collection<ClientUserChannelOverride>}
+ */
+ channel_overrides: 'channelOverrides',
+};
+
+exports.UserChannelOverrideMap = {
+ message_notifications: function messageNotifications(type) { // eslint-disable-line func-name-matching
+ /**
+ * The type of message that should notify you
+ * @name ClientUserChannelOverride#messageNotifications
+ * @type {MessageNotificationType}
+ */
+ return exports.MessageNotificationTypes[type];
+ },
+ /**
+ * Whether the channel is muted
+ * @name ClientUserChannelOverride#muted
+ * @type {boolean}
+ */
+ muted: 'muted',
+};
+
+exports.Colors = {
+ DEFAULT: 0x000000,
+ WHITE: 0xFFFFFF,
+ AQUA: 0x1ABC9C,
+ GREEN: 0x2ECC71,
+ BLUE: 0x3498DB,
+ PURPLE: 0x9B59B6,
+ LUMINOUS_VIVID_PINK: 0xE91E63,
+ GOLD: 0xF1C40F,
+ ORANGE: 0xE67E22,
+ RED: 0xE74C3C,
+ GREY: 0x95A5A6,
+ NAVY: 0x34495E,
+ DARK_AQUA: 0x11806A,
+ DARK_GREEN: 0x1F8B4C,
+ DARK_BLUE: 0x206694,
+ DARK_PURPLE: 0x71368A,
+ DARK_VIVID_PINK: 0xAD1457,
+ DARK_GOLD: 0xC27C0E,
+ DARK_ORANGE: 0xA84300,
+ DARK_RED: 0x992D22,
+ DARK_GREY: 0x979C9F,
+ DARKER_GREY: 0x7F8C8D,
+ LIGHT_GREY: 0xBCC0C0,
+ DARK_NAVY: 0x2C3E50,
+ BLURPLE: 0x7289DA,
+ GREYPLE: 0x99AAB5,
+ DARK_BUT_NOT_BLACK: 0x2C2F33,
+ NOT_QUITE_BLACK: 0x23272A,
+};
+
+/**
+ * The value set for the verification levels for a guild:
+ * * None
+ * * Low
+ * * Medium
+ * * (╯°□°)╯︵ ┻━┻
+ * * ┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻
+ * @typedef {string} VerificationLevel
+ */
+exports.VerificationLevels = [
+ 'None',
+ 'Low',
+ 'Medium',
+ '(╯°□°)╯︵ ┻━┻',
+ '┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻',
+];
+
+/**
+ * An error encountered while performing an API request. Here are the potential errors:
+ * * UNKNOWN_ACCOUNT
+ * * UNKNOWN_APPLICATION
+ * * UNKNOWN_CHANNEL
+ * * UNKNOWN_GUILD
+ * * UNKNOWN_INTEGRATION
+ * * UNKNOWN_INVITE
+ * * UNKNOWN_MEMBER
+ * * UNKNOWN_MESSAGE
+ * * UNKNOWN_OVERWRITE
+ * * UNKNOWN_PROVIDER
+ * * UNKNOWN_ROLE
+ * * UNKNOWN_TOKEN
+ * * UNKNOWN_USER
+ * * UNKNOWN_EMOJI
+ * * UNKNOWN_WEBHOOK
+ * * BOT_PROHIBITED_ENDPOINT
+ * * BOT_ONLY_ENDPOINT
+ * * MAXIMUM_GUILDS
+ * * MAXIMUM_FRIENDS
+ * * MAXIMUM_PINS
+ * * MAXIMUM_ROLES
+ * * MAXIMUM_REACTIONS
+ * * MAXIMUM_CHANNELS
+ * * MAXIMUM_INVITES
+ * * UNAUTHORIZED
+ * * USER_BANNED
+ * * MISSING_ACCESS
+ * * INVALID_ACCOUNT_TYPE
+ * * CANNOT_EXECUTE_ON_DM
+ * * EMBED_DISABLED
+ * * CANNOT_EDIT_MESSAGE_BY_OTHER
+ * * CANNOT_SEND_EMPTY_MESSAGE
+ * * CANNOT_MESSAGE_USER
+ * * CANNOT_SEND_MESSAGES_IN_VOICE_CHANNEL
+ * * CHANNEL_VERIFICATION_LEVEL_TOO_HIGH
+ * * OAUTH2_APPLICATION_BOT_ABSENT
+ * * MAXIMUM_OAUTH2_APPLICATIONS
+ * * INVALID_OAUTH_STATE
+ * * MISSING_PERMISSIONS
+ * * INVALID_AUTHENTICATION_TOKEN
+ * * NOTE_TOO_LONG
+ * * INVALID_BULK_DELETE_QUANTITY
+ * * CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL
+ * * INVALID_OR_TAKEN_INVITE_CODE
+ * * CANNOT_EXECUTE_ON_SYSTEM_MESSAGE
+ * * INVALID_OAUTH_TOKEN
+ * * BULK_DELETE_MESSAGE_TOO_OLD
+ * * INVALID_FORM_BODY
+ * * INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT
+ * * INVALID_API_VERSION
+ * * REACTION_BLOCKED
+ * * RESOURCE_OVERLOADED
+ * @typedef {string} APIError
+ */
+exports.APIErrors = {
+ UNKNOWN_ACCOUNT: 10001,
+ UNKNOWN_APPLICATION: 10002,
+ UNKNOWN_CHANNEL: 10003,
+ UNKNOWN_GUILD: 10004,
+ UNKNOWN_INTEGRATION: 10005,
+ UNKNOWN_INVITE: 10006,
+ UNKNOWN_MEMBER: 10007,
+ UNKNOWN_MESSAGE: 10008,
+ UNKNOWN_OVERWRITE: 10009,
+ UNKNOWN_PROVIDER: 10010,
+ UNKNOWN_ROLE: 10011,
+ UNKNOWN_TOKEN: 10012,
+ UNKNOWN_USER: 10013,
+ UNKNOWN_EMOJI: 10014,
+ UNKNOWN_WEBHOOK: 10015,
+ BOT_PROHIBITED_ENDPOINT: 20001,
+ BOT_ONLY_ENDPOINT: 20002,
+ MAXIMUM_GUILDS: 30001,
+ MAXIMUM_FRIENDS: 30002,
+ MAXIMUM_PINS: 30003,
+ MAXIMUM_ROLES: 30005,
+ MAXIMUM_REACTIONS: 30010,
+ MAXIMUM_CHANNELS: 30013,
+ MAXIMUM_INVITES: 30016,
+ UNAUTHORIZED: 40001,
+ USER_BANNED: 40007,
+ MISSING_ACCESS: 50001,
+ INVALID_ACCOUNT_TYPE: 50002,
+ CANNOT_EXECUTE_ON_DM: 50003,
+ EMBED_DISABLED: 50004,
+ CANNOT_EDIT_MESSAGE_BY_OTHER: 50005,
+ CANNOT_SEND_EMPTY_MESSAGE: 50006,
+ CANNOT_MESSAGE_USER: 50007,
+ CANNOT_SEND_MESSAGES_IN_VOICE_CHANNEL: 50008,
+ CHANNEL_VERIFICATION_LEVEL_TOO_HIGH: 50009,
+ OAUTH2_APPLICATION_BOT_ABSENT: 50010,
+ MAXIMUM_OAUTH2_APPLICATIONS: 50011,
+ INVALID_OAUTH_STATE: 50012,
+ MISSING_PERMISSIONS: 50013,
+ INVALID_AUTHENTICATION_TOKEN: 50014,
+ NOTE_TOO_LONG: 50015,
+ INVALID_BULK_DELETE_QUANTITY: 50016,
+ CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: 50019,
+ INVALID_OR_TAKEN_INVITE_CODE: 50020,
+ CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: 50021,
+ INVALID_OAUTH_TOKEN: 50025,
+ BULK_DELETE_MESSAGE_TOO_OLD: 50034,
+ INVALID_FORM_BODY: 50035,
+ INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT: 50036,
+ INVALID_API_VERSION: 50041,
+ REACTION_BLOCKED: 90001,
+ RESOURCE_OVERLOADED: 130000,
+};
+
+/**
+ * The value set for a guild's default message notifications, e.g. `ALL`. Here are the available types:
+ * * ALL
+ * * MENTIONS
+ * @typedef {string} DefaultMessageNotifications
+ */
+exports.DefaultMessageNotifications = [
+ 'ALL',
+ 'MENTIONS',
+];
+
+/**
+ * The value set for a team members's membership state:
+ * * INVITED
+ * * ACCEPTED
+ * @typedef {string} MembershipStates
+ */
+exports.MembershipStates = [
+ // They start at 1
+ null,
+ 'INVITED',
+ 'ACCEPTED',
+];
+
+/**
+ * The value set for a webhook's type:
+ * * Incoming
+ * * Channel Follower
+ * @typedef {string} WebhookTypes
+ */
+exports.WebhookTypes = [
+ // They start at 1
+ null,
+ 'Incoming',
+ 'Channel Follower',
+];
diff --git a/node_modules/discord.js/src/util/MessageFlags.js b/node_modules/discord.js/src/util/MessageFlags.js
new file mode 100644
index 0000000..88c8014
--- /dev/null
+++ b/node_modules/discord.js/src/util/MessageFlags.js
@@ -0,0 +1,36 @@
+const BitField = require('./BitField');
+
+/**
+ * Data structure that makes it easy to interact with an {@link Message#flags} bitfield.
+ * @extends {BitField}
+ */
+class MessageFlags extends BitField {}
+
+/**
+ * Data that can be resolved to give a permission number. This can be:
+ * * A string (see {@link MessageFlags.FLAGS})
+ * * A message flag
+ * * An instance of MessageFlags
+ * * An array of MessageFlagsResolvable
+ * @typedef {string|number|MessageFlags|MessageFlagsResolvable[]} MessageFlagsResolvable
+ */
+
+/**
+ * Numeric message flags. All available properties:
+ * * `CROSSPOSTED`
+ * * `IS_CROSSPOST`
+ * * `SUPPRESS_EMBEDS`
+ * * `SOURCE_MESSAGE_DELETED`
+ * * `URGENT`
+ * @type {Object}
+ * @see {@link https://discordapp.com/developers/docs/resources/channel#message-object-message-flags}
+ */
+MessageFlags.FLAGS = {
+ CROSSPOSTED: 1 << 0,
+ IS_CROSSPOST: 1 << 1,
+ SUPPRESS_EMBEDS: 1 << 2,
+ SOURCE_MESSAGE_DELETED: 1 << 3,
+ URGENT: 1 << 4,
+};
+
+module.exports = MessageFlags;
diff --git a/node_modules/discord.js/src/util/Permissions.js b/node_modules/discord.js/src/util/Permissions.js
new file mode 100644
index 0000000..fa1d0e0
--- /dev/null
+++ b/node_modules/discord.js/src/util/Permissions.js
@@ -0,0 +1,234 @@
+const BitField = require('./BitField');
+const util = require('util');
+
+/**
+ * Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of
+ * permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
+ * that override their default permissions.
+ * @extends {BitField}
+ */
+class Permissions extends BitField {
+ /**
+ * @param {GuildMember} [member] Member the permissions are for **(deprecated)**
+ * @param {number|PermissionResolvable} permissions Permissions or bitfield to read from
+ */
+ constructor(member, permissions) {
+ super(typeof member === 'object' && !(member instanceof Array) ? permissions : member);
+
+ Object.defineProperty(this, '_member', {
+ writable: true,
+ value: typeof member === 'object' && !(member instanceof Array) ? member : null,
+ });
+ }
+
+ /**
+ * Member the permissions are for
+ * @type {GuildMember}
+ * @deprecated
+ */
+ get member() {
+ return this._member;
+ }
+
+ set member(value) {
+ this._member = value;
+ }
+
+ /**
+ * Bitfield of the packed permissions
+ * @type {number}
+ * @see {@link Permissions#bitfield}
+ * @deprecated
+ * @readonly
+ */
+ get raw() {
+ return this.bitfield;
+ }
+
+ set raw(raw) {
+ this.bitfield = raw;
+ }
+
+ /**
+ * Checks whether the bitfield has a permission, or any of multiple permissions.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
+ * @returns {boolean}
+ */
+ any(permission, checkAdmin = true) {
+ return (checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) || super.any(permission);
+ }
+
+ /**
+ * Checks whether the bitfield has a permission, or multiple permissions.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
+ * @returns {boolean}
+ */
+ has(permission, checkAdmin = true) {
+ return (checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) || super.has(permission);
+ }
+
+ /**
+ * Checks whether the user has a certain permission, e.g. `READ_MESSAGES`.
+ * @param {PermissionResolvable} permission The permission to check for
+ * @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permission
+ * @returns {boolean}
+ * @see {@link Permissions#has}
+ * @deprecated
+ */
+ hasPermission(permission, explicit = false) {
+ return this.has(permission, !explicit);
+ }
+
+ /**
+ * Checks whether the user has all specified permissions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permissions
+ * @returns {boolean}
+ * @see {@link Permissions#has}
+ * @deprecated
+ */
+ hasPermissions(permissions, explicit = false) {
+ return this.has(permissions, !explicit);
+ }
+
+ /**
+ * Checks whether the user has all specified permissions, and lists any missing permissions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permissions
+ * @returns {PermissionResolvable}
+ * @see {@link Permissions#missing}
+ * @deprecated
+ */
+ missingPermissions(permissions, explicit = false) {
+ return this.missing(permissions, !explicit);
+ }
+}
+
+/**
+ * Data that can be resolved to give a permission number. This can be:
+ * * A string (see {@link Permissions.FLAGS})
+ * * A permission number
+ * @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable
+ */
+
+/**
+ * Numeric permission flags. All available properties:
+ * - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites)
+ * - `CREATE_INSTANT_INVITE` (create invitations to the guild)
+ * - `KICK_MEMBERS`
+ * - `BAN_MEMBERS`
+ * - `MANAGE_CHANNELS` (edit and reorder channels)
+ * - `MANAGE_GUILD` (edit the guild information, region, etc.)
+ * - `ADD_REACTIONS` (add new reactions to messages)
+ * - `VIEW_AUDIT_LOG`
+ * - `PRIORITY_SPEAKER`
+ * - `STREAM`
+ * - `VIEW_CHANNEL`
+ * - `READ_MESSAGES` **(deprecated)**
+ * - `SEND_MESSAGES`
+ * - `SEND_TTS_MESSAGES`
+ * - `MANAGE_MESSAGES` (delete messages and reactions)
+ * - `EMBED_LINKS` (links posted will have a preview embedded)
+ * - `ATTACH_FILES`
+ * - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord)
+ * - `MENTION_EVERYONE`
+ * - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds)
+ * - `EXTERNAL_EMOJIS` **(deprecated)**
+ * - `CONNECT` (connect to a voice channel)
+ * - `SPEAK` (speak in a voice channel)
+ * - `MUTE_MEMBERS` (mute members across all voice channels)
+ * - `DEAFEN_MEMBERS` (deafen members across all voice channels)
+ * - `MOVE_MEMBERS` (move members between voice channels)
+ * - `USE_VAD` (use voice activity detection)
+ * - `CHANGE_NICKNAME`
+ * - `MANAGE_NICKNAMES` (change other members' nicknames)
+ * - `MANAGE_ROLES`
+ * - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)**
+ * - `MANAGE_WEBHOOKS`
+ * - `MANAGE_EMOJIS`
+ * @type {Object}
+ * @see {@link https://discordapp.com/developers/docs/topics/permissions}
+ */
+Permissions.FLAGS = {
+ CREATE_INSTANT_INVITE: 1 << 0,
+ KICK_MEMBERS: 1 << 1,
+ BAN_MEMBERS: 1 << 2,
+ ADMINISTRATOR: 1 << 3,
+ MANAGE_CHANNELS: 1 << 4,
+ MANAGE_GUILD: 1 << 5,
+ ADD_REACTIONS: 1 << 6,
+ VIEW_AUDIT_LOG: 1 << 7,
+ PRIORITY_SPEAKER: 1 << 8,
+ STREAM: 1 << 9,
+
+ VIEW_CHANNEL: 1 << 10,
+ READ_MESSAGES: 1 << 10,
+ SEND_MESSAGES: 1 << 11,
+ SEND_TTS_MESSAGES: 1 << 12,
+ MANAGE_MESSAGES: 1 << 13,
+ EMBED_LINKS: 1 << 14,
+ ATTACH_FILES: 1 << 15,
+ READ_MESSAGE_HISTORY: 1 << 16,
+ MENTION_EVERYONE: 1 << 17,
+ EXTERNAL_EMOJIS: 1 << 18,
+ USE_EXTERNAL_EMOJIS: 1 << 18,
+
+ CONNECT: 1 << 20,
+ SPEAK: 1 << 21,
+ MUTE_MEMBERS: 1 << 22,
+ DEAFEN_MEMBERS: 1 << 23,
+ MOVE_MEMBERS: 1 << 24,
+ USE_VAD: 1 << 25,
+
+ CHANGE_NICKNAME: 1 << 26,
+ MANAGE_NICKNAMES: 1 << 27,
+ MANAGE_ROLES: 1 << 28,
+ MANAGE_ROLES_OR_PERMISSIONS: 1 << 28,
+ MANAGE_WEBHOOKS: 1 << 29,
+ MANAGE_EMOJIS: 1 << 30,
+};
+
+/**
+ * Bitfield representing every permission combined
+ * @type {number}
+ */
+Permissions.ALL = Object.keys(Permissions.FLAGS).reduce((all, p) => all | Permissions.FLAGS[p], 0);
+
+/**
+ * Bitfield representing the default permissions for users
+ * @type {number}
+ */
+Permissions.DEFAULT = 104324673;
+
+/**
+ * @class EvaluatedPermissions
+ * @classdesc The final evaluated permissions for a member in a channel
+ * @see {@link Permissions}
+ * @deprecated
+ */
+
+Permissions.prototype.hasPermission = util.deprecate(Permissions.prototype.hasPermission,
+ 'EvaluatedPermissions#hasPermission is deprecated, use Permissions#has instead');
+Permissions.prototype.hasPermissions = util.deprecate(Permissions.prototype.hasPermissions,
+ 'EvaluatedPermissions#hasPermissions is deprecated, use Permissions#has instead');
+Permissions.prototype.missingPermissions = util.deprecate(Permissions.prototype.missingPermissions,
+ 'EvaluatedPermissions#missingPermissions is deprecated, use Permissions#missing instead');
+Object.defineProperty(Permissions.prototype, 'raw', {
+ get: util
+ .deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'raw').get,
+ 'EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead'),
+ set: util.deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'raw').set,
+ 'EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead'),
+});
+Object.defineProperty(Permissions.prototype, 'member', {
+ get: util
+ .deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'member').get,
+ 'EvaluatedPermissions#member is deprecated'),
+ set: util
+ .deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'member').set,
+ 'EvaluatedPermissions#member is deprecated'),
+});
+
+module.exports = Permissions;
diff --git a/node_modules/discord.js/src/util/Snowflake.js b/node_modules/discord.js/src/util/Snowflake.js
new file mode 100644
index 0000000..2775266
--- /dev/null
+++ b/node_modules/discord.js/src/util/Snowflake.js
@@ -0,0 +1,82 @@
+const Long = require('long');
+
+// Discord epoch (2015-01-01T00:00:00.000Z)
+const EPOCH = 1420070400000;
+let INCREMENT = 0;
+
+/**
+ * A container for useful snowflake-related methods.
+ */
+class SnowflakeUtil {
+ constructor() {
+ throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
+ }
+
+ /**
+ * A Twitter snowflake, except the epoch is 2015-01-01T00:00:00.000Z
+ * ```
+ * If we have a snowflake '266241948824764416' we can represent it as binary:
+ *
+ * 64 22 17 12 0
+ * 000000111011000111100001101001000101000000 00001 00000 000000000000
+ * number of ms since Discord epoch worker pid increment
+ * ```
+ * @typedef {string} Snowflake
+ */
+
+ /**
+ * Generates a Discord snowflake.
+ * <info>This hardcodes the worker ID as 1 and the process ID as 0.</info>
+ * @param {number|Date} [timestamp=Date.now()] Timestamp or date of the snowflake to generate
+ * @returns {Snowflake} The generated snowflake
+ */
+ static generate(timestamp = Date.now()) {
+ if (timestamp instanceof Date) timestamp = timestamp.getTime();
+ if (typeof timestamp !== 'number' || isNaN(timestamp)) {
+ throw new TypeError(
+ `"timestamp" argument must be a number (received ${isNaN(timestamp) ? 'NaN' : typeof timestamp})`
+ );
+ }
+ if (INCREMENT >= 4095) INCREMENT = 0;
+ const BINARY = `${pad((timestamp - EPOCH).toString(2), 42)}0000100000${pad((INCREMENT++).toString(2), 12)}`;
+ return Long.fromString(BINARY, 2).toString();
+ }
+
+ /**
+ * A deconstructed snowflake.
+ * @typedef {Object} DeconstructedSnowflake
+ * @property {number} timestamp Timestamp the snowflake was created
+ * @property {Date} date Date the snowflake was created
+ * @property {number} workerID Worker ID in the snowflake
+ * @property {number} processID Process ID in the snowflake
+ * @property {number} increment Increment in the snowflake
+ * @property {string} binary Binary representation of the snowflake
+ */
+
+ /**
+ * Deconstructs a Discord snowflake.
+ * @param {Snowflake} snowflake Snowflake to deconstruct
+ * @returns {DeconstructedSnowflake} Deconstructed snowflake
+ */
+ static deconstruct(snowflake) {
+ const BINARY = pad(Long.fromString(snowflake).toString(2), 64);
+ const res = {
+ timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH,
+ workerID: parseInt(BINARY.substring(42, 47), 2),
+ processID: parseInt(BINARY.substring(47, 52), 2),
+ increment: parseInt(BINARY.substring(52, 64), 2),
+ binary: BINARY,
+ };
+ Object.defineProperty(res, 'date', {
+ get: function get() { return new Date(this.timestamp); },
+ enumerable: true,
+ });
+ return res;
+ }
+}
+
+function pad(v, n, c = '0') {
+ return String(v).length >= n ? String(v) : (String(c).repeat(n) + v).slice(-n);
+}
+
+module.exports = SnowflakeUtil;
diff --git a/node_modules/discord.js/src/util/SystemChannelFlags.js b/node_modules/discord.js/src/util/SystemChannelFlags.js
new file mode 100644
index 0000000..9f74984
--- /dev/null
+++ b/node_modules/discord.js/src/util/SystemChannelFlags.js
@@ -0,0 +1,31 @@
+const BitField = require('./BitField');
+
+/**
+ * Data structure that makes it easy to interact with a {@link Guild#systemChannelFlags} bitfield.
+ * <info>Note that all event message types are enabled by default,
+ * and by setting their corresponding flags you are disabling them</info>
+ * @extends {BitField}
+ */
+class SystemChannelFlags extends BitField {}
+
+/**
+ * Data that can be resolved to give a sytem channel flag bitfield. This can be:
+ * * A string (see {@link SystemChannelFlags.FLAGS})
+ * * A sytem channel flag
+ * * An instance of SystemChannelFlags
+ * * An Array of SystemChannelFlagsResolvable
+ * @typedef {string|number|SystemChannelFlags|SystemChannelFlagsResolvable[]} SystemChannelFlagsResolvable
+ */
+
+/**
+ * Numeric system channel flags. All available properties:
+ * * `WELCOME_MESSAGE_DISABLED`
+ * * `BOOST_MESSAGE_DISABLED`
+ * @type {Object}
+ */
+SystemChannelFlags.FLAGS = {
+ WELCOME_MESSAGE_DISABLED: 1 << 0,
+ BOOST_MESSAGE_DISABLED: 1 << 1,
+};
+
+module.exports = SystemChannelFlags;
diff --git a/node_modules/discord.js/src/util/Util.js b/node_modules/discord.js/src/util/Util.js
new file mode 100644
index 0000000..a286b07
--- /dev/null
+++ b/node_modules/discord.js/src/util/Util.js
@@ -0,0 +1,238 @@
+const snekfetch = require('snekfetch');
+const Constants = require('./Constants');
+const ConstantsHttp = Constants.DefaultOptions.http;
+
+/**
+ * Contains various general-purpose utility methods. These functions are also available on the base `Discord` object.
+ */
+class Util {
+ constructor() {
+ throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
+ }
+
+ /**
+ * Splits a string into multiple chunks at a designated character that do not exceed a specific length.
+ * @param {string} text Content to split
+ * @param {SplitOptions} [options] Options controlling the behaviour of the split
+ * @returns {string|string[]}
+ */
+ static splitMessage(text, { maxLength = 1950, char = '\n', prepend = '', append = '' } = {}) {
+ if (text.length <= maxLength) return text;
+ const splitText = text.split(char);
+ if (splitText.some(chunk => chunk.length > maxLength)) {
+ throw new Error('Message exceeds the max length and contains no split characters.');
+ }
+ const messages = [''];
+ let msg = 0;
+ for (let i = 0; i < splitText.length; i++) {
+ if (messages[msg].length + splitText[i].length + 1 > maxLength) {
+ messages[msg] += append;
+ messages.push(prepend);
+ msg++;
+ }
+ messages[msg] += (messages[msg].length > 0 && messages[msg] !== prepend ? char : '') + splitText[i];
+ }
+ return messages;
+ }
+
+ /**
+ * Data that can be resolved to give a string. This can be:
+ * * A string
+ * * An array (joined with a new line delimiter to give a string)
+ * * Any value
+ * @typedef {string|Array|*} StringResolvable
+ */
+
+ /**
+ * Resolves a StringResolvable to a string.
+ * @param {StringResolvable} data The string resolvable to resolve
+ * @returns {string}
+ */
+ static resolveString(data) {
+ if (typeof data === 'string') return data;
+ if (Array.isArray(data)) return data.join('\n');
+ return String(data);
+ }
+
+ /**
+ * Escapes any Discord-flavour markdown in a string.
+ * @param {string} text Content to escape
+ * @param {boolean} [onlyCodeBlock=false] Whether to only escape codeblocks (takes priority)
+ * @param {boolean} [onlyInlineCode=false] Whether to only escape inline code
+ * @returns {string}
+ */
+ static escapeMarkdown(text, onlyCodeBlock = false, onlyInlineCode = false) {
+ if (onlyCodeBlock) return text.replace(/```/g, '`\u200b``');
+ if (onlyInlineCode) return text.replace(/\\(`|\\)/g, '$1').replace(/(`|\\)/g, '\\$1');
+ return text.replace(/\\(\*|_|`|~|\\)/g, '$1').replace(/(\*|_|`|~|\\)/g, '\\$1');
+ }
+
+ /**
+ * Gets the recommended shard count from Discord.
+ * @param {string} token Discord auth token
+ * @param {number} [guildsPerShard=1000] Number of guilds per shard
+ * @returns {Promise<number>} The recommended number of shards
+ */
+ static fetchRecommendedShards(token, guildsPerShard = 1000) {
+ return new Promise((resolve, reject) => {
+ if (!token) throw new Error('A token must be provided.');
+ snekfetch.get(`${ConstantsHttp.host}/api/v${ConstantsHttp.version}${Constants.Endpoints.gateway.bot}`)
+ .set('Authorization', `Bot ${token.replace(/^Bot\s*/i, '')}`)
+ .end((err, res) => {
+ if (err) reject(err);
+ resolve(res.body.shards * (1000 / guildsPerShard));
+ });
+ });
+ }
+
+ /**
+ * Parses emoji info out of a string. The string must be one of:
+ * * A UTF-8 emoji (no ID)
+ * * A URL-encoded UTF-8 emoji (no ID)
+ * * A Discord custom emoji (`<:name:id>` or `<a:name:id>`)
+ * @param {string} text Emoji string to parse
+ * @returns {?Object} Object with `animated`, `name`, and `id` properties
+ * @private
+ */
+ static parseEmoji(text) {
+ if (text.includes('%')) text = decodeURIComponent(text);
+ if (!text.includes(':')) return { animated: false, name: text, id: null };
+ const m = text.match(/<?(a:)?(\w{2,32}):(\d{17,19})>?/);
+ if (!m) return null;
+ return { animated: Boolean(m[1]), name: m[2], id: m[3] };
+ }
+
+ /**
+ * Checks whether two arrays are equal or have the same elements.
+ * @param {Array<*>} a The first array.
+ * @param {Array<*>} b The second array.
+ * @returns {boolean} Whether the arrays are equal.
+ * @private
+ */
+ static arraysEqual(a, b) {
+ if (a === b) return true;
+ if (a.length !== b.length) return false;
+
+ const setA = new Set(a);
+ const setB = new Set(b);
+
+ return a.every(e => setB.has(e)) && b.every(e => setA.has(e));
+ }
+
+ /**
+ * Shallow-copies an object with its class/prototype intact.
+ * @param {Object} obj Object to clone
+ * @returns {Object}
+ * @private
+ */
+ static cloneObject(obj) {
+ return Object.assign(Object.create(obj), obj);
+ }
+
+ /**
+ * Sets default properties on an object that aren't already specified.
+ * @param {Object} def Default properties
+ * @param {Object} given Object to assign defaults to
+ * @returns {Object}
+ * @private
+ */
+ static mergeDefault(def, given) {
+ if (!given) return def;
+ for (const key in def) {
+ if (!{}.hasOwnProperty.call(given, key)) {
+ given[key] = def[key];
+ } else if (given[key] === Object(given[key])) {
+ given[key] = this.mergeDefault(def[key], given[key]);
+ }
+ }
+
+ return given;
+ }
+
+ /**
+ * Converts an ArrayBuffer or string to a Buffer.
+ * @param {ArrayBuffer|string} ab ArrayBuffer to convert
+ * @returns {Buffer}
+ * @private
+ */
+ static convertToBuffer(ab) {
+ if (typeof ab === 'string') ab = this.str2ab(ab);
+ return Buffer.from(ab);
+ }
+
+ /**
+ * Converts a string to an ArrayBuffer.
+ * @param {string} str String to convert
+ * @returns {ArrayBuffer}
+ * @private
+ */
+ static str2ab(str) {
+ const buffer = new ArrayBuffer(str.length * 2);
+ const view = new Uint16Array(buffer);
+ for (var i = 0, strLen = str.length; i < strLen; i++) view[i] = str.charCodeAt(i);
+ return buffer;
+ }
+
+ /**
+ * Makes an Error from a plain info object.
+ * @param {Object} obj Error info
+ * @param {string} obj.name Error type
+ * @param {string} obj.message Message for the error
+ * @param {string} obj.stack Stack for the error
+ * @returns {Error}
+ * @private
+ */
+ static makeError(obj) {
+ const err = new Error(obj.message);
+ err.name = obj.name;
+ err.stack = obj.stack;
+ return err;
+ }
+
+ /**
+ * Makes a plain error info object from an Error.
+ * @param {Error} err Error to get info from
+ * @returns {Object}
+ * @private
+ */
+ static makePlainError(err) {
+ const obj = {};
+ obj.name = err.name;
+ obj.message = err.message;
+ obj.stack = err.stack;
+ return obj;
+ }
+
+ /**
+ * Moves an element in an array *in place*.
+ * @param {Array<*>} array Array to modify
+ * @param {*} element Element to move
+ * @param {number} newIndex Index or offset to move the element to
+ * @param {boolean} [offset=false] Move the element by an offset amount rather than to a set index
+ * @returns {number}
+ * @private
+ */
+ static moveElementInArray(array, element, newIndex, offset = false) {
+ const index = array.indexOf(element);
+ newIndex = (offset ? index : 0) + newIndex;
+ if (newIndex > -1 && newIndex < array.length) {
+ const removedElement = array.splice(index, 1)[0];
+ array.splice(newIndex, 0, removedElement);
+ }
+ return array.indexOf(element);
+ }
+
+ /**
+ * Creates a Promise that resolves after a specified duration.
+ * @param {number} ms How long to wait before resolving (in milliseconds)
+ * @returns {Promise<void>}
+ * @private
+ */
+ static delayFor(ms) {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+ }
+}
+
+module.exports = Util;
diff --git a/node_modules/discord.js/typings/discord.js-test.ts b/node_modules/discord.js/typings/discord.js-test.ts
new file mode 100644
index 0000000..afa9573
--- /dev/null
+++ b/node_modules/discord.js/typings/discord.js-test.ts
@@ -0,0 +1,69 @@
+/// <reference path='index.d.ts' />
+
+import { Collector, Message, CollectorFilter, Client, CollectorHandler, MessageReaction, Collection, User, ReactionCollectorOptions, Snowflake } from 'discord.js';
+
+const client = new Client({
+ disableEveryone: false,
+ disabledEvents: ['GUILD_MEMBER_ADD']
+});
+
+client.on('message', (message) => {
+ if (message.content === 'hello') {
+ message.channel.sendMessage('o/');
+ }
+
+ const collector: ReactionCollector = new ReactionCollector(message,
+ (reaction: MessageReaction) => reaction.emoji.toString() === '👌',
+ { time: 30e3 });
+ collector.on('end', collected => console.log(collected));
+});
+
+client.login('dsfsd754.4fds4f68d4f6sd46f4s.7878easfdsgdfFDSIJIO');
+
+export class TestCollector extends Collector<Snowflake, Message> {
+ public filter: CollectorFilter;
+ public constructor(client: Client, filter: CollectorFilter) {
+ super(client, filter);
+ }
+
+ public handle(message: Message): CollectorHandler<Snowflake, Message> {
+ return { key: message.id, value: message };
+ }
+
+ public cleanup(): void {}
+ public postCheck(): null { return null; }
+}
+
+class ReactionCollector extends Collector<Snowflake, MessageReaction> {
+ public message: Message;
+ public users: Collection<Snowflake, User>;
+ public total: number;
+ public options: ReactionCollectorOptions;
+ public constructor(message: Message, filter: CollectorFilter, options?: ReactionCollectorOptions) {
+ super(message.client, filter, options || {});
+ this.message = message;
+ this.users = new Collection<Snowflake, User>();
+ this.total = 0;
+ this.client.on('messageReactionAdd', this.listener);
+ }
+
+ public handle(reaction: MessageReaction): CollectorHandler<Snowflake, MessageReaction> {
+ if (reaction.message.id !== this.message.id) { return null; }
+ return {
+ key: reaction.emoji.id || reaction.emoji.name,
+ value: reaction
+ };
+ }
+
+ public postCheck(reaction: MessageReaction, user: User): string {
+ this.users.set(user.id, user);
+ if (this.options.max && ++this.total >= this.options.max) { return 'limit'; }
+ if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) { return 'emojiLimit'; }
+ if (this.options.maxUsers && this.users.size >= this.options.maxUsers) { return 'userLimit'; }
+ return null;
+ }
+
+ public cleanup(): void {
+ this.client.removeListener('messageReactionAdd', this.listener as any);
+ }
+}
diff --git a/node_modules/discord.js/typings/index.d.ts b/node_modules/discord.js/typings/index.d.ts
new file mode 100644
index 0000000..f316f19
--- /dev/null
+++ b/node_modules/discord.js/typings/index.d.ts
@@ -0,0 +1,2440 @@
+// Type definitions for discord.js 11.6.2
+// Project: https://github.com/discordjs/discord.js
+// Definitions by:
+// acdenisSK <[email protected]> (https://github.com/acdenisSK)
+// Zack Campbell <[email protected]> (https://github.com/zajrik)
+// License: MIT
+
+declare module 'discord.js' {
+ import { EventEmitter } from 'events';
+ import { Stream, Readable as ReadableStream } from 'stream';
+ import { ChildProcess } from 'child_process';
+
+ export const version: string;
+
+//#region Classes
+
+ class Attachment {
+ constructor(file: BufferResolvable | Stream, name?: string);
+ private _attach(file: BufferResolvable | Stream, name: string): void;
+
+ public readonly attachment: BufferResolvable | Stream;
+ public readonly name: string;
+ public setAttachment(file: BufferResolvable | Stream, name: string): this;
+ public setFile(attachment: BufferResolvable | Stream): this;
+ public setName(name: string): this;
+ }
+
+ class AudioPlayer extends EventEmitter {
+ constructor(voiceConnection: VoiceConnection);
+ public readonly dispatcher: StreamDispatcher;
+ public opusEncoder: object;
+ public prism: object;
+ public readonly transcoder: object;
+ public voiceConnection: VoiceConnection;
+ public setBitrate(value: number | 'auto'): void;
+ }
+
+ class BaseOpus {
+ constructor(options?: { bitrate?: number, fec?: boolean, plp?: number });
+ public bitrate: number;
+ public options: object;
+ }
+
+ export class BitField<S extends string> {
+ constructor(bits?: BitFieldResolvable<S>);
+ public bitfield: number;
+ public add(...bits: BitFieldResolvable<S>[]): BitField<S>;
+ public any(bit: BitFieldResolvable<S>): boolean;
+ public equals(bit: BitFieldResolvable<S>): boolean;
+ public freeze(): Readonly<BitField<S>>;
+ public has(bit: BitFieldResolvable<S>): boolean;
+ public missing(bits: BitFieldResolvable<S>, ...hasParam: readonly unknown[]): S[];
+ public remove(...bits: BitFieldResolvable<S>[]): BitField<S>;
+ public serialize(...hasParam: readonly unknown[]): Record<S, boolean>;
+ public toArray(...hasParam: readonly unknown[]): S[];
+ public toJSON(): number;
+ public valueOf(): number;
+ public [Symbol.iterator](): IterableIterator<S>;
+ public static FLAGS: object;
+ public static resolve(bit?: BitFieldResolvable<any>): number;
+ }
+
+ export class CategoryChannel extends GuildChannel {
+ public readonly children: Collection<Snowflake, GuildChannel>;
+ }
+
+ export class Channel {
+ constructor(client: Client, data: object);
+ public readonly client: Client;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public deleted: boolean;
+ public id: Snowflake;
+ public type: 'dm' | 'group' | GuildChannelType;
+ public delete(): Promise<Channel>;
+ }
+
+ export class Client extends EventEmitter {
+ constructor(options?: ClientOptions);
+ private _intervals: Set<NodeJS.Timer>;
+ private _pingTimestamp: number;
+ private _timeouts: Set<NodeJS.Timer>;
+ private actions: object;
+ private dataManager: object;
+ private manager: ClientManager;
+ private resolver: ClientDataResolver;
+ private rest: object;
+ private voice: ClientVoiceManager;
+ private ws: object;
+ private _eval(script: string): any;
+ private _pong(startTime: number): void;
+ private _setPresence(id: Snowflake, presence: object): void;
+ private _validateOptions(options?: ClientOptions): void;
+
+ public broadcasts: VoiceBroadcast[];
+ public readonly browser: boolean;
+ public channels: Collection<Snowflake, Channel>;
+ public readonly emojis: Collection<Snowflake, Emoji>;
+ public guilds: Collection<Snowflake, Guild>;
+ public options: ClientOptions;
+ public readonly ping: number;
+ public pings: number[];
+ public presences: Collection<Snowflake, Presence>;
+ public readyAt: Date;
+ public readonly readyTimestamp: number;
+ public shard: ShardClientUtil;
+ public readonly status: number;
+ public token: string;
+ public readonly uptime: number;
+ public user: ClientUser;
+ public users: Collection<Snowflake, User>;
+ public readonly voiceConnections: Collection<Snowflake, VoiceConnection>;
+ public clearInterval(interval: NodeJS.Timer): void;
+ public clearTimeout(timeout: NodeJS.Timer): void;
+ public createVoiceBroadcast(): VoiceBroadcast;
+ public destroy(): Promise<void>;
+ public fetchApplication(id?: Snowflake): Promise<OAuth2Application>;
+ public fetchInvite(invite: InviteResolvable): Promise<Invite>;
+ public fetchUser(id: Snowflake, cache?: boolean): Promise<User>;
+ public fetchVoiceRegions(): Promise<Collection<string, VoiceRegion>>;
+ public fetchWebhook(id: Snowflake, token?: string): Promise<Webhook>;
+ public generateInvite(permissions?: PermissionResolvable): Promise<string>;
+ public login(token?: string): Promise<string>;
+ public setInterval(fn: Function, delay: number, ...args: any[]): NodeJS.Timer;
+ public setTimeout(fn: Function, delay: number, ...args: any[]): NodeJS.Timer;
+ public sweepMessages(lifetime?: number): number;
+ public syncGuilds(guilds?: Guild[] | Collection<Snowflake, Guild>): void;
+
+ public on(event: 'channelCreate', listener: (channel: Channel) => void): this;
+ public on(event: 'channelDelete', listener: (channel: Channel) => void): this;
+ public on(event: 'channelPinsUpdate', listener: (channel: Channel, time: Date) => void): this;
+ public on(event: 'channelUpdate', listener: (oldChannel: Channel, newChannel: Channel) => void): this;
+ public on(event: 'clientUserGuildSettingsUpdate', listener: (clientUserGuildSettings: ClientUserGuildSettings) => void): this;
+ public on(event: 'clientUserSettingsUpdate', listener: (clientUserSettings: ClientUserSettings) => void): this;
+ public on(event: 'debug', listener: (info: string) => void): this;
+ public on(event: 'disconnect', listener: (event: any) => void): this;
+ public on(event: 'emojiCreate', listener: (emoji: Emoji) => void): this;
+ public on(event: 'emojiDelete', listener: (emoji: Emoji) => void): this;
+ public on(event: 'emojiUpdate', listener: (oldEmoji: Emoji, newEmoji: Emoji) => void): this;
+ public on(event: 'error', listener: (error: Error) => void): this;
+ public on(event: 'guildBanAdd', listener: (guild: Guild, user: User) => void): this;
+ public on(event: 'guildBanRemove', listener: (guild: Guild, user: User) => void): this;
+ public on(event: 'guildCreate', listener: (guild: Guild) => void): this;
+ public on(event: 'guildDelete', listener: (guild: Guild) => void): this;
+ public on(event: 'guildMemberAdd', listener: (member: GuildMember) => void): this;
+ public on(event: 'guildMemberAvailable', listener: (member: GuildMember) => void): this;
+ public on(event: 'guildMemberRemove', listener: (member: GuildMember) => void): this;
+ public on(event: 'guildMembersChunk', listener: (members: GuildMember[], guild: Guild) => void): this;
+ public on(event: 'guildMemberSpeaking', listener: (member: GuildMember, speaking: boolean) => void): this;
+ public on(event: 'guildMemberUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
+ public on(event: 'guildUnavailable', listener: (guild: Guild) => void): this;
+ public on(event: 'guildUpdate', listener: (oldGuild: Guild, newGuild: Guild) => void): this;
+ public on(event: 'guildIntegrationsUpdate', listener: (guild: Guild) => void): this;
+ public on(event: 'inviteCreate' | 'inviteDelete', listener: (invite: Invite) => void): this;
+ public on(event: 'message', listener: (message: Message) => void): this;
+ public on(event: 'messageDelete', listener: (message: Message) => void): this;
+ public on(event: 'messageDeleteBulk', listener: (messages: Collection<Snowflake, Message>) => void): this;
+ public on(event: 'messageReactionAdd', listener: (messageReaction: MessageReaction, user: User) => void): this;
+ public on(event: 'messageReactionRemove', listener: (messageReaction: MessageReaction, user: User) => void): this;
+ public on(event: 'messageReactionRemoveEmoji', listener: (messageReaction: MessageReaction) => void): this;
+ public on(event: 'messageReactionRemoveAll', listener: (message: Message) => void): this;
+ public on(event: 'messageUpdate', listener: (oldMessage: Message, newMessage: Message) => void): this;
+ public on(event: 'presenceUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
+ public on(event: 'rateLimit', listener: (rateLimit: RateLimitInfo) => void): this;
+ public on(event: 'ready', listener: () => void): this;
+ public on(event: 'reconnecting', listener: () => void): this;
+ public on(event: 'resume', listener: (replayed: number) => void): this;
+ public on(event: 'roleCreate', listener: (role: Role) => void): this;
+ public on(event: 'roleDelete', listener: (role: Role) => void): this;
+ public on(event: 'roleUpdate', listener: (oldRole: Role, newRole: Role) => void): this;
+ public on(event: 'typingStart', listener: (channel: Channel, user: User) => void): this;
+ public on(event: 'typingStop', listener: (channel: Channel, user: User) => void): this;
+ public on(event: 'userNoteUpdate', listener: (user: UserResolvable, oldNote: string, newNote: string) => void): this;
+ public on(event: 'userUpdate', listener: (oldUser: User, newUser: User) => void): this;
+ public on(event: 'voiceStateUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
+ public on(event: 'warn', listener: (info: string) => void): this;
+ public on(event: 'webhookUpdate', listener: (channel: TextChannel) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'channelCreate', listener: (channel: Channel) => void): this;
+ public once(event: 'channelDelete', listener: (channel: Channel) => void): this;
+ public once(event: 'channelPinsUpdate', listener: (channel: Channel, time: Date) => void): this;
+ public once(event: 'channelUpdate', listener: (oldChannel: Channel, newChannel: Channel) => void): this;
+ public once(event: 'clientUserGuildSettingsUpdate', listener: (clientUserGuildSettings: ClientUserGuildSettings) => void): this;
+ public once(event: 'clientUserSettingsUpdate', listener: (clientUserSettings: ClientUserSettings) => void): this;
+ public once(event: 'debug', listener: (info: string) => void): this;
+ public once(event: 'disconnect', listener: (event: any) => void): this;
+ public once(event: 'emojiCreate', listener: (emoji: Emoji) => void): this;
+ public once(event: 'emojiDelete', listener: (emoji: Emoji) => void): this;
+ public once(event: 'emojiUpdate', listener: (oldEmoji: Emoji, newEmoji: Emoji) => void): this;
+ public once(event: 'error', listener: (error: Error) => void): this;
+ public once(event: 'guildBanAdd', listener: (guild: Guild, user: User) => void): this;
+ public once(event: 'guildBanRemove', listener: (guild: Guild, user: User) => void): this;
+ public once(event: 'guildCreate', listener: (guild: Guild) => void): this;
+ public once(event: 'guildDelete', listener: (guild: Guild) => void): this;
+ public once(event: 'guildMemberAdd', listener: (member: GuildMember) => void): this;
+ public once(event: 'guildMemberAvailable', listener: (member: GuildMember) => void): this;
+ public once(event: 'guildMemberRemove', listener: (member: GuildMember) => void): this;
+ public once(event: 'guildMembersChunk', listener: (members: GuildMember[], guild: Guild) => void): this;
+ public once(event: 'guildMemberSpeaking', listener: (member: GuildMember, speaking: boolean) => void): this;
+ public once(event: 'guildMemberUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
+ public once(event: 'guildUnavailable', listener: (guild: Guild) => void): this;
+ public once(event: 'guildUpdate', listener: (oldGuild: Guild, newGuild: Guild) => void): this;
+ public once(event: 'guildIntegrationsUpdate', listener: (guild: Guild) => void): this;
+ public once(event: 'message', listener: (message: Message) => void): this;
+ public once(event: 'messageDelete', listener: (message: Message) => void): this;
+ public once(event: 'messageDeleteBulk', listener: (messages: Collection<Snowflake, Message>) => void): this;
+ public once(event: 'messageReactionAdd', listener: (messageReaction: MessageReaction, user: User) => void): this;
+ public once(event: 'messageReactionRemove', listener: (messageReaction: MessageReaction, user: User) => void): this;
+ public once(event: 'messageReactionRemoveEmoji', listener: (messageReaction: MessageReaction) => void): this;
+ public once(event: 'messageReactionRemoveAll', listener: (message: Message) => void): this;
+ public once(event: 'messageUpdate', listener: (oldMessage: Message, newMessage: Message) => void): this;
+ public once(event: 'presenceUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
+ public once(event: 'rateLimit', listener: (rateLimit: RateLimitInfo) => void): this;
+ public once(event: 'ready', listener: () => void): this;
+ public once(event: 'reconnecting', listener: () => void): this;
+ public once(event: 'resume', listener: (replayed: number) => void): this;
+ public once(event: 'roleCreate', listener: (role: Role) => void): this;
+ public once(event: 'roleDelete', listener: (role: Role) => void): this;
+ public once(event: 'roleUpdate', listener: (oldRole: Role, newRole: Role) => void): this;
+ public once(event: 'typingStart', listener: (channel: Channel, user: User) => void): this;
+ public once(event: 'typingStop', listener: (channel: Channel, user: User) => void): this;
+ public once(event: 'userNoteUpdate', listener: (user: UserResolvable, oldNote: string, newNote: string) => void): this;
+ public once(event: 'userUpdate', listener: (oldUser: User, newUser: User) => void): this;
+ public once(event: 'voiceStateUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
+ public once(event: 'warn', listener: (info: string) => void): this;
+ public once(event: 'webhookUpdate', listener: (channel: TextChannel) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ class ClientDataResolver {
+ constructor(client: Client);
+ public resolveBase64(data: Base64Resolvable): string;
+ public resolveChannel(channel: ChannelResolvable): Channel;
+ public resolveChannelID(channel: ChannelResolvable): Snowflake;
+ public resolveColor(color: ColorResolvable): number;
+ public resolveString(data: StringResolvable): string;
+ public resolveEmojiIdentifier(emoji: EmojiIdentifierResolvable): string;
+ public resolveFile(resource: BufferResolvable | Stream): Promise<Buffer>;
+ public resolveGuild(guild: GuildResolvable): Guild;
+ public resolveGuildMember(guild: GuildResolvable, user: UserResolvable): GuildMember;
+ public resolveImage(imge: BufferResolvable | Base64Resolvable): Promise<string>;
+ public resolveInviteCode(data: InviteResolvable): string;
+ public resolveString(data: StringResolvable): string;
+ public resolveUser(user: UserResolvable): User;
+ public resolveUserID(user: UserResolvable): Snowflake;
+
+ public static resolveColor(color: ColorResolvable): number;
+ }
+
+ class ClientManager {
+ constructor(client: Client);
+ public client: Client;
+ public heartbeatInterval: number;
+ public status: number;
+ public connectToWebSocket(token: string, resolve: Function, reject: Function): void;
+ }
+
+ export class ClientUser extends User {
+ public blocked: Collection<Snowflake, User>;
+ public email: string;
+ public friends: Collection<Snowflake, User>;
+ public guildSettings: Collection<Snowflake, ClientUserGuildSettings>;
+ public mfaEnabled: boolean;
+ public mobile: boolean;
+ public notes: Collection<Snowflake, string>;
+ public premium: boolean;
+ public settings: ClientUserSettings;
+ public verified: boolean;
+ public acceptInvite(invite: Invite | string): Promise<Guild>;
+ public addFriend(user?: UserResolvable): Promise<User>;
+ public createGroupDM(recipients: GroupDMRecipientOptions[]): Promise<GroupDMChannel>;
+ public createGuild(name: string, region: string, icon?: BufferResolvable | Base64Resolvable): Promise<Guild>;
+ public fetchMentions(options?: { limit?: number; roles?: boolean, everyone?: boolean; guild?: Guild | Snowflake }): Promise<Message[]>;
+ public removeFriend(user?: UserResolvable): Promise<User>;
+ public setActivity(name: string | null, options?: { url?: string, type?: ActivityType | number }): Promise<Presence>;
+ public setAFK(afk: boolean): Promise<ClientUser>;
+ public setAvatar(avatar: BufferResolvable | Base64Resolvable): Promise<ClientUser>;
+ public setEmail(email: string, password: string): Promise<ClientUser>;
+ public setGame(game: string | null, streamingURL?: string): Promise<ClientUser>;
+ public setPassword(newPassword: string, oldPassword: string): Promise<ClientUser>;
+ public setPresence(data: PresenceData): Promise<ClientUser>;
+ public setStatus(status: PresenceStatus): Promise<ClientUser>;
+ public setUsername(username: string, password?: string): Promise<ClientUser>;
+ }
+
+ class ClientUserChannelOverride {
+ constructor(user: User, data: object);
+ private patch(data: object): void;
+
+ public messageNotifications: GuildChannelMessageNotifications;
+ public muted: boolean;
+ }
+
+ class ClientUserGuildSettings {
+ constructor(data: object, guild: Guild);
+ private patch(data: object): void;
+
+ public channelOverrides: Collection<Snowflake, ClientUserChannelOverride>;
+ public readonly client: Client;
+ public guildID: Snowflake;
+ public messageNotifications: GuildChannelMessageNotifications;
+ public mobilePush: boolean;
+ public muted: boolean;
+ public suppressEveryone: boolean;
+ public update(name: string, value: any): Promise<object>;
+ }
+
+ export class ClientUserSettings {
+ constructor(user: User, data: object);
+ private patch(data: object): void;
+
+ public convertEmoticons: boolean;
+ public defaultGuildsRestricted: boolean;
+ public detectPlatformAccounts: boolean;
+ public developerMode: boolean;
+ public enableTTSCommand: boolean;
+ public explicitContentFilter: 'DISABLED' | 'NON_FRIENDS' | 'FRIENDS_AND_NON_FRIENDS' | string;
+ public friendsSources: { all: boolean, mutualGuilds: boolean, mutualFriends: boolean };
+ public guildsPositions: Snowflake[];
+ public inlineAttachmentMedia: boolean;
+ public inlineEmbedMedia: boolean;
+ public locale: string;
+ public messageDisplayCompact: boolean;
+ public renderReactions: boolean;
+ public restrictedGuilds: Snowflake[];
+ public showCurrentGame: boolean;
+ public status: PresenceStatus;
+ public theme: string;
+ public addRestrictedGuild(guild: Guild): Promise<Guild>;
+ public removeRestrictedGuild(guild: Guild): Promise<Guild>;
+ public setGuildPosition(guild: Guild, position: number, relative?: boolean): Promise<Guild>;
+ public update(name: string, value: any): Promise<object>;
+ }
+
+ class ClientVoiceManager {
+ constructor(client: Client);
+ public client: Client;
+ public connections: Collection<Snowflake, VoiceConnection>;
+ public joinChannel(channel: VoiceChannel): Promise<VoiceConnection>;
+ }
+
+ export class Collection<K, V> extends Map<K, V> {
+ private _array: V[];
+ private _keyArray: K[];
+
+ public array(): V[];
+ public clone(): Collection<K, V>;
+ public concat(...collections: Collection<K, V>[]): Collection<K, V>;
+ public deleteAll(): Promise<V>[];
+ public equals(collection: Collection<any, any>): boolean;
+ public every(fn: (value: V, key: K, collection: Collection<K, V>) => boolean, thisArg?: any): boolean;
+ public exists(prop: keyof V, value: any): boolean;
+ public filter(fn: (value: V, key: K, collection: Collection<K, V>) => boolean, thisArg?: any): Collection<K, V>;
+ public filterArray(fn: (value: V, key: K, collection: Collection<K, V>) => boolean, thisArg?: any): V[];
+ public find(prop: keyof V, value: any): V;
+ public find(fn: (value: V, key: K, collection: Collection<K, V>) => boolean): V;
+ public findAll(prop: keyof V, value: any): V[];
+ public findKey(prop: keyof V, value: any): K;
+ public findKey(fn: (value: V, key: K, collection: Collection<K, V>) => boolean): K;
+ public first(): V;
+ public first(count: number): V[];
+ public firstKey(): K;
+ public firstKey(count: number): K[];
+ public keyArray(): K[];
+ public last(): V;
+ public last(count: number): V[];
+ public lastKey(): K;
+ public lastKey(count: number): K[];
+ public map<T>(fn: (value: V, key: K, collection: Collection<K, V>) => T, thisArg?: any): T[];
+ public partition(fn: (value: V, key: K, collection: Collection<K, V>) => boolean, thisArg?: any): [Collection<K, V>, Collection<K, V>];
+ public random(): V;
+ public random(count: number): V[];
+ public randomKey(): K;
+ public randomKey(count: number): K[];
+ public reduce<T>(fn: (accumulator: any, value: V, key: K, collection: Collection<K, V>) => T, initialValue?: any): T;
+ public some(fn: (value: V, key: K, collection: Collection<K, V>) => boolean, thisArg?: any): boolean;
+ public sort(compareFunction?: (a: V, b: V, c?: K, d?: K) => number): Collection<K, V>;
+ public sweep(fn: (value: V, key: K, collection: Collection<K, V>) => boolean, thisArg?: any): number;
+ public tap(fn: (value: V, key: K, map: Collection<K, V>) => void, thisArg?: any): Collection<K, V>;
+ }
+
+ abstract class Collector<K, V> extends EventEmitter {
+ constructor(client: Client, filter: CollectorFilter, options?: CollectorOptions);
+ private _timeout: NodeJS.Timer | null;
+ private _idletimeout: NodeJS.Timer | null;
+ private _handle(...args: any[]): void;
+
+ public readonly client: Client;
+ public collected: Collection<K, V>;
+ public ended: boolean;
+ public filter: CollectorFilter;
+ public readonly next: Promise<V>;
+ public options: CollectorOptions;
+ public stop(reason?: string): void;
+
+ protected listener: Function;
+ public abstract cleanup(): void;
+ public abstract handle(...args: any[]): CollectorHandler<K, V>;
+ public abstract postCheck(...args: any[]): string | null;
+
+ public on(event: 'collect', listener: (element: V, collector: Collector<K, V>) => void): this;
+ public on(event: 'end', listener: (collected: Collection<K, V>, reason: string) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'collect', listener: (element: V, collector: Collector<K, V>) => void): this;
+ public once(event: 'end', listener: (collected: Collection<K, V>, reason: string) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ class DiscordAPIError extends Error {
+ constructor(error: object);
+ private static flattenErrors(obj: object, key: string): string[];
+
+ public code: number;
+ public method: string;
+ public path: string;
+ }
+
+ export class DMChannel extends TextBasedChannel(Channel) {
+ constructor(client: Client, data: object);
+ public lastMessageID: Snowflake;
+ public messages: Collection<Snowflake, Message>;
+ public recipient: User;
+ public toString(): string;
+ }
+
+ export class Emoji {
+ constructor(guild: Guild, data: object);
+ public animated: boolean;
+ public available: boolean;
+ public readonly client: Client;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public readonly deletable: boolean;
+ public deleted: boolean;
+ public guild: Guild;
+ public id: Snowflake;
+ public readonly identifier: string;
+ public managed: boolean;
+ public name: string;
+ public requiresColons: boolean;
+ public readonly roles: Collection<Snowflake, Role>;
+ public readonly url: string;
+ public addRestrictedRole(role: Role): Promise<Emoji>;
+ public addRestrictedRoles(roles: Role[]): Promise<Emoji>;
+ public edit(data: EmojiEditData, reason?: string): Promise<Emoji>;
+ public equals(other: Emoji | object): boolean;
+ public delete(reason?: string): Promise<this>;
+ public fetchAuthor(): Promise<User>;
+ public removeRestrictedRole(role: Role): Promise<Emoji>;
+ public removeRestrictedRoles(roles: Role[]): Promise<Emoji>;
+ public setName(name: string, reason?: string): Promise<Emoji>;
+ public toString(): string;
+ }
+
+ export class Game {
+ constructor(data: object, presence: Presence);
+ private _flags: string[];
+ private syncID: string;
+
+ public applicationID: string;
+ public assets: RichPresenceAssets;
+ public details: string;
+ public emoji: Omit<ReactionEmoji, 'reaction'> | null;
+ public name: string;
+ public readonly streaming: boolean;
+ public party: {
+ id: string;
+ size: [number, number];
+ };
+ public state: string;
+ public timestamps: {
+ start: Date;
+ end: Date;
+ };
+ public readonly flags: string[];
+ public type: number;
+ public url: string;
+ public equals(game: Game): boolean;
+ public toString(): string;
+ }
+
+ export class GroupDMChannel extends TextBasedChannel(Channel) {
+ constructor(client: Client, data: object);
+ public applicationID: string;
+ public icon: string;
+ public lastMessageID: string;
+ public managed: boolean;
+ public messages: Collection<Snowflake, Message>;
+ public name: string;
+ public nicks: Collection<Snowflake, string>;
+ public readonly owner: User;
+ public ownerID: string;
+ public recipients: Collection<Snowflake, User>;
+ public addUser(accessTokenOrID: UserResolvable | string, nick?: string): Promise<GroupDMChannel>;
+ public equals(channel: GroupDMChannel): boolean;
+ public setIcon(icon: Base64Resolvable | BufferResolvable): Promise<GroupDMChannel>;
+ public toString(): string;
+ }
+
+ export class Guild {
+ constructor(client: Client, data: object);
+ private readonly _sortedRoles: Collection<Snowflake, Role>;
+ private _sortedChannels(type: string): Collection<Snowflake, GuildChannel>;
+ private _sortPositionWithID(collection: Collection<any, any>): Collection<any, any>;
+
+ protected setup(data: any): void;
+
+ public readonly afkChannel: VoiceChannel;
+ public afkChannelID: string;
+ public afkTimeout: number;
+ public applicationID: string;
+ public available: boolean;
+ public banner: string | null;
+ public readonly bannerURL: string | null;
+ public deleted: boolean;
+ public description: string | null;
+ public channels: Collection<Snowflake, GuildChannel>;
+ public defaultMessageNotifications: DefaultMessageNotifications | number;
+ public readonly client: Client;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public readonly defaultChannel: TextChannel;
+ public readonly defaultRole: Role;
+ public readonly embedChannel: TextChannel | null;
+ public embedChannelID: Snowflake | null;
+ public embedEnabled: boolean;
+ public emojis: Collection<Snowflake, Emoji>;
+ public explicitContentFilter: number;
+ public features: string[];
+ public icon: string;
+ public readonly iconURL: string;
+ public id: Snowflake;
+ public readonly joinedAt: Date;
+ public joinedTimestamp: number;
+ public large: boolean;
+ public maximumMembers?: number;
+ public maximumPresences?: number;
+ public readonly me: GuildMember;
+ public memberCount: number;
+ public members: Collection<Snowflake, GuildMember>;
+ public readonly messageNotifications: MessageNotifications;
+ public readonly mobilePush: boolean;
+ public readonly muted: boolean;
+ public name: string;
+ public readonly nameAcronym: string;
+ public readonly owner: GuildMember;
+ public ownerID: string;
+ public premiumSubscriptionCount: number | null;
+ public premiumTier: PremiumTier;
+ public readonly position: number;
+ public presences: Collection<Snowflake, Presence>;
+ public readonly publicUpdatesChannel: TextChannel | null;
+ public publicUpdatesChannelID: Snowflake | null;
+ public region: string;
+ public roles: Collection<Snowflake, Role>;
+ public readonly rulesChannel: TextChannel | null;
+ public rulesChannelID: Snowflake | null;
+ public splash: string;
+ public readonly splashURL: string;
+ public readonly suppressEveryone: boolean;
+ public readonly systemChannel: GuildChannel;
+ public systemChannelFlags: Readonly<SystemChannelFlags>;
+ public systemChannelID: Snowflake;
+ public vanityURLCode: string;
+ public readonly verified: boolean;
+ public verificationLevel: number;
+ public readonly voiceConnection: VoiceConnection;
+ public readonly widgetChannel: TextChannel | null;
+ public widgetChannelID?: Snowflake;
+ public widgetEnabled?: boolean;
+ public acknowledge(): Promise<Guild>;
+ public addMember(user: UserResolvable, options: AddGuildMemberOptions): Promise<GuildMember>;
+ public allowDMs(allow: boolean): Promise<Guild>;
+ public ban(user: UserResolvable, options?: BanOptions | number | string): Promise<GuildMember | User | string>;
+ public createChannel(name: string, options?: ChannelData): Promise<CategoryChannel | TextChannel | VoiceChannel>;
+ public createChannel(name: string, type?: GuildChannelType, permissionOverwrites?: PermissionOverwrites[] | ChannelCreationOverwrites[], reason?: string): Promise<CategoryChannel | TextChannel | VoiceChannel>;
+ public createEmoji(attachment: BufferResolvable | Base64Resolvable, name: string, roles?: Collection<Snowflake, Role> | Role[], reason?: string): Promise<Emoji>;
+ public createIntegration(data: IntegrationData, reason?: string): Promise<Guild>;
+ public createRole(data?: RoleData, reason?: string): Promise<Role>;
+ public delete(): Promise<Guild>;
+ public deleteEmoji(emoji: Emoji | string, reason?: string): Promise<void>;
+ public edit(data: GuildEditData, reason?: string): Promise<Guild>;
+ public equals(guild: Guild): boolean;
+ public fetch(): Promise<Guild>;
+ public fetchAuditLogs(options?: GuildAuditLogsFetchOptions): Promise<GuildAuditLogs>;
+ public fetchBan(user: UserResolvable): Promise<BanInfo>;
+ public fetchBans(withReasons?: false): Promise<Collection<Snowflake, User>>;
+ public fetchBans(withReasons: true): Promise<Collection<Snowflake, BanInfo>>;
+ public fetchBans(withReasons: boolean): Promise<Collection<Snowflake, BanInfo | User>>;
+ public fetchEmbed(): Promise<GuildEmbedData>;
+ public fetchIntegrations(): Promise<Collection<string, Integration>>;
+ public fetchInvites(): Promise<Collection<Snowflake, Invite>>;
+ public fetchMember(user: UserResolvable, cache?: boolean): Promise<GuildMember>;
+ public fetchMembers(query?: string, limit?: number): Promise<Guild>;
+ public fetchVanityCode(): Promise<string>;
+ public fetchVoiceRegions(): Promise<Collection<string, VoiceRegion>>;
+ public fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
+ public leave(): Promise<Guild>;
+ public member(user: UserResolvable): GuildMember;
+ public pruneMembers(days: number, dry?: boolean, reason?: string): Promise<number>;
+ public search(options?: MessageSearchOptions): Promise<MessageSearchResult>;
+ public setAFKChannel(afkChannel: ChannelResolvable, reason?: string): Promise<Guild>;
+ public setAFKTimeout(afkTimeout: number, reason?: string): Promise<Guild>;
+ public setBanner(banner: Base64Resolvable, reason?: string): Promise<Guild>;
+ public setChannelPosition(channel: string | GuildChannel, position: number, relative?: boolean): Promise<Guild>;
+ public setChannelPositions(channelPositions: ChannelPosition[]): Promise<Guild>;
+ public setDefaultMessageNotifications(defaultMessageNotifications: DefaultMessageNotifications, reason?: string): Promise<Guild>;
+ public setEmbed(embed: GuildEmbedData, reason?: string): Promise<Guild>;
+ public setExplicitContentFilter(explicitContentFilter: number, reason?: string): Promise<Guild>;
+ public setIcon(icon: Base64Resolvable, reason?: string): Promise<Guild>;
+ public setName(name: string, reason?: string): Promise<Guild>;
+ public setOwner(owner: GuildMemberResolvable, reason?: string): Promise<Guild>;
+ public setPosition(position: number, relative?: boolean): Promise<Guild>;
+ public setRegion(region: string, reason?: string): Promise<Guild>;
+ public setRolePosition(role: string | Role, position: number, relative?: boolean): Promise<Guild>;
+ public setRolePositions(rolePositions: RolePosition[]): Promise<Guild>;
+ public setSplash(splash: Base64Resolvable, reason?: string): Promise<Guild>;
+ public setSystemChannel(systemChannel: ChannelResolvable, reason?: string): Promise<Guild>;
+ public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise<Guild>;
+ public setVerificationLevel(verificationLevel: number, reason?: string): Promise<Guild>;
+ public sync(): void;
+ public toString(): string;
+ public unban(user: UserResolvable, reason?: string): Promise<User>;
+ }
+
+ export class GuildAuditLogs {
+ constructor(guild: Guild, data: object);
+ private webhooks: Collection<Snowflake, Webhook>;
+ private integrations: Collection<Snowflake, Integration>;
+
+ public entries: Collection<Snowflake, GuildAuditLogsEntry>;
+
+ public static Actions: GuildAuditLogsActions;
+ public static Targets: GuildAuditLogsTargets;
+ public static Entry: typeof GuildAuditLogsEntry;
+ public static actionType(action: number): GuildAuditLogsActionType;
+ public static build(...args: any[]): Promise<GuildAuditLogs>;
+ public static targetType(target: number): GuildAuditLogsTarget;
+ }
+
+ class GuildAuditLogsEntry {
+ constructor(logs: GuildAuditLogs, guild: Guild, data: object);
+ public action: GuildAuditLogsAction;
+ public actionType: GuildAuditLogsActionType;
+ public changes: AuditLogChange[];
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public executor: User;
+ public extra: object | Role | GuildMember;
+ public id: Snowflake;
+ public reason: string;
+ public target: Guild | User | Role | Emoji | Invite | Webhook | Integration | null;
+ public targetType: GuildAuditLogsTarget;
+ }
+
+ export class GuildChannel extends Channel {
+ constructor(guild: Guild, data: object);
+ public readonly calculatedPosition: number;
+ public readonly deletable: boolean;
+ public guild: Guild;
+ public readonly manageable: boolean;
+ public readonly messageNotifications: GuildChannelMessageNotifications;
+ public readonly muted: boolean;
+ public name: string;
+ public readonly parent: CategoryChannel | null;
+ public parentID: Snowflake | null;
+ public permissionOverwrites: Collection<Snowflake, PermissionOverwrites>;
+ public position: number;
+ public readonly permissionsLocked: boolean | null;
+ public clone(options: GuildChannelCloneOptions): Promise<GuildChannel>;
+ public clone(name?: string, withPermissions?: boolean, withTopic?: boolean, reason?: string): Promise<GuildChannel>;
+ public createInvite(options?: InviteOptions, reason?: string): Promise<Invite>;
+ public delete(reason?: string): Promise<GuildChannel>;
+ public edit(data: ChannelData, reason?: string): Promise<GuildChannel>;
+ public equals(channel: GuildChannel): boolean;
+ public fetchInvites(): Promise<Collection<string, Invite>>;
+ public lockPermissions(): Promise<GuildChannel>;
+ public memberPermissions(member: GuildMemberResolvable): Permissions | null;
+ public overwritePermissions(userOrRole: RoleResolvable | UserResolvable, options: PermissionOverwriteOptions, reason?: string): Promise<void>;
+ public permissionsFor(memberOrRole: GuildMemberResolvable | RoleResolvable): Permissions | null;
+ public replacePermissionOverwrites(options?: { overwrites?: (PermissionOverwrites | ChannelCreationOverwrites)[] | Collection<Snowflake, ChannelCreationOverwrites>, reason?: string }): Promise<GuildChannel>;
+ public rolePermissions(role: RoleResolvable): Permissions;
+ public setName(name: string, reason?: string): Promise<GuildChannel>;
+ public setParent(parent: ChannelResolvable, reason?: string): Promise<GuildChannel>;
+ public setPosition(position: number, relative?: boolean): Promise<GuildChannel>;
+ public setTopic(topic: string, reason?: string): Promise<GuildChannel>;
+ public toString(): string;
+ }
+
+ export class GuildMember extends PartialTextBasedChannel() {
+ constructor(guild: Guild, data: object);
+ public readonly bannable: boolean;
+ public readonly client: Client;
+ public readonly colorRole: Role;
+ public readonly deaf: boolean;
+ public deleted: boolean;
+ public readonly displayColor: number;
+ public readonly displayHexColor: string;
+ public readonly displayName: string;
+ public guild: Guild;
+ public readonly highestRole: Role;
+ public readonly hoistRole: Role;
+ public readonly id: Snowflake;
+ public readonly joinedAt: Date;
+ public joinedTimestamp: number;
+ public readonly kickable: boolean;
+ public lastMessageID: string;
+ public readonly mute: boolean;
+ public nickname: string | null;
+ public readonly manageable: boolean;
+ public readonly permissions: Permissions;
+ public readonly premiumSince: Date | null;
+ public premiumSinceTimestamp: number | null;
+ public readonly presence: Presence;
+ public readonly roles: Collection<Snowflake, Role>;
+ public selfDeaf: boolean;
+ public selfMute: boolean;
+ public selfStream: boolean;
+ public serverDeaf: boolean;
+ public serverMute: boolean;
+ public speaking: boolean;
+ public user: User;
+ public readonly voiceChannel: VoiceChannel;
+ public voiceChannelID: string;
+ public voiceSessionID: string;
+ public addRole(role: Role | Snowflake, reason?: string): Promise<GuildMember>;
+ public addRoles(roles: Collection<Snowflake, Role> | Role[] | Snowflake[], reason?: string): Promise<GuildMember>;
+ public ban(options?: BanOptions | number | string): Promise<GuildMember>;
+ public createDM(): Promise<DMChannel>;
+ public deleteDM(): Promise<DMChannel>;
+ public edit(data: GuildMemberEditData, reason?: string): Promise<GuildMember>;
+ public hasPermission(permission: PermissionResolvable, explicit?: boolean, checkAdmin?: boolean, checkOwner?: boolean): boolean;
+ public hasPermissions(permission: PermissionResolvable, explicit?: boolean): boolean;
+ public kick(reason?: string): Promise<GuildMember>;
+ public missingPermissions(permissions: PermissionResolvable, explicit?: boolean): PermissionResolvable;
+ public permissionsIn(channel: ChannelResolvable): Permissions;
+ public removeRole(role: Role | Snowflake, reason?: string): Promise<GuildMember>;
+ public removeRoles(roles: Collection<Snowflake, Role> | Role[] | Snowflake[], reason?: string): Promise<GuildMember>;
+ public setDeaf(deaf: boolean, reason?: string): Promise<GuildMember>;
+ public setMute(mute: boolean, reason?: string): Promise<GuildMember>;
+ public setNickname(nickname: string, reason?: string): Promise<GuildMember>;
+ public setRoles(roles: Collection<Snowflake, Role> | Role[] | Snowflake[], reason?: string): Promise<GuildMember>;
+ public setVoiceChannel(voiceChannel: ChannelResolvable | null): Promise<GuildMember>;
+ public toString(): string;
+ }
+
+ export class Integration {
+ constructor(client: Client, data: object, guild: Guild);
+ public account: IntegrationAccount;
+ public enabled: boolean;
+ public expireBehavior: number;
+ public expireGracePeriod: number;
+ public guild: Guild;
+ public id: Snowflake;
+ public name: string;
+ public role: Role;
+ public syncedAt: number;
+ public syncing: boolean;
+ public type: number;
+ public user: User;
+ public delete(reason?: string): Promise<Integration>;
+ public edit(data: IntegrationEditData, reason?: string): Promise<Integration>;
+ public sync(): Promise<Integration>;
+ }
+
+ export class Invite {
+ constructor(client: Client, data: object);
+ public channel: GuildChannel | PartialGuildChannel;
+ public readonly client: Client;
+ public code: string;
+ public readonly createdAt: Date;
+ public createdTimestamp: number;
+ public readonly expiresAt: Date;
+ public readonly expiresTimestamp: number;
+ public guild: Guild | PartialGuild;
+ public inviter: User;
+ public maxAge: number;
+ public maxUses: number;
+ public memberCount: number;
+ public presenceCount: number;
+ public temporary: boolean;
+ public textChannelCount: number;
+ public readonly url: string;
+ public uses: number;
+ public voiceChannelCount: number;
+ public delete(reason?: string): Promise<Invite>;
+ public toString(): string;
+ }
+
+ export class Message {
+ constructor(channel: TextChannel | DMChannel | GroupDMChannel, data: object, client: Client);
+ private _edits: Message[];
+ private patch(data: object): void;
+
+ public attachments: Collection<Snowflake, MessageAttachment>;
+ public author: User;
+ public channel: TextChannel | DMChannel | GroupDMChannel;
+ public readonly cleanContent: string;
+ public readonly client: Client;
+ public content: string;
+ public readonly createdAt: Date;
+ public createdTimestamp: number;
+ public readonly deletable: boolean;
+ public deleted: boolean;
+ public readonly editable: boolean;
+ public readonly editedAt: Date;
+ public editedTimestamp: number;
+ public readonly edits: Message[];
+ public embeds: MessageEmbed[];
+ public flags: Readonly<MessageFlags>;
+ public readonly guild: Guild;
+ public hit: boolean;
+ public id: Snowflake;
+ public member: GuildMember;
+ public mentions: MessageMentions;
+ public nonce: string;
+ public readonly pinnable: boolean;
+ public pinned: boolean;
+ public reactions: Collection<Snowflake, MessageReaction>;
+ public reference: MessageReference | null;
+ public system: boolean;
+ public tts: boolean;
+ public type: string;
+ public readonly url: string;
+ public webhookID: Snowflake;
+ public acknowledge(): Promise<Message>;
+ public awaitReactions(filter: CollectorFilter, options?: AwaitReactionsOptions): Promise<Collection<Snowflake, MessageReaction>>;
+ public clearReactions(): Promise<Message>;
+ public createReactionCollector(filter: CollectorFilter, options?: ReactionCollectorOptions): ReactionCollector;
+ public delete(timeout?: number): Promise<Message>;
+ public edit(content: StringResolvable, options?: MessageEditOptions | RichEmbed): Promise<Message>;
+ public editCode(lang: string, content: StringResolvable): Promise<Message>;
+ public equals(message: Message, rawData: object): boolean;
+ public fetchWebhook(): Promise<Webhook>;
+ public isMemberMentioned(member: GuildMember | User): boolean;
+ public isMentioned(data: GuildChannel | User | Role | Snowflake): boolean;
+ public pin(): Promise<Message>;
+ public react(emoji: string | Emoji | ReactionEmoji): Promise<MessageReaction>;
+ public reply(content?: StringResolvable, options?: MessageOptions & { split: false }): Promise<Message>;
+ public reply(content?: StringResolvable, options?: MessageOptions): Promise<Message | Message[]>;
+ public reply(options?: MessageOptions): Promise<Message | Message[]>;
+ public suppressEmbeds(suppress?: boolean): Promise<Message>;
+ public toString(): string;
+ public unpin(): Promise<Message>;
+ }
+
+ export class MessageAttachment {
+ constructor(message: Message, data: object);
+ public readonly client: Client;
+ public filename: string;
+ public filesize: number;
+ public height: number;
+ public id: Snowflake;
+ public message: Message;
+ public proxyURL: string;
+ public readonly spoiler: boolean;
+ public url: string;
+ public width: number;
+ }
+
+ export class MessageCollector extends Collector<Snowflake, Message> {
+ constructor(channel: TextChannel | DMChannel | GroupDMChannel, filter: CollectorFilter, options?: MessageCollectorOptions);
+ public channel: Channel;
+ public options: MessageCollectorOptions;
+ public received: number;
+
+ public cleanup(): void;
+ public handle(message: Message): CollectorHandler<Snowflake, Message>;
+ public postCheck(): string;
+ }
+
+ export class MessageEmbed {
+ constructor(message: Message, data: object);
+ public author: MessageEmbedAuthor;
+ public readonly client: Client;
+ public color: number;
+ public readonly createdAt: Date;
+ public timestamp: number;
+ public description: string;
+ public fields: MessageEmbedField[];
+ public footer: MessageEmbedFooter;
+ public readonly hexColor: string;
+ public image: MessageEmbedImage;
+ public message: Message;
+ public provider: MessageEmbedProvider;
+ public thumbnail: MessageEmbedThumbnail;
+ public title: string;
+ public type: string;
+ public url: string;
+ public video: MessageEmbedVideo;
+ }
+
+ export class MessageEmbedAuthor {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public iconURL: string;
+ public name: string;
+ public url: string;
+ }
+
+ export class MessageEmbedField {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public inline: boolean;
+ public name: string;
+ public value: string;
+ }
+
+ export class MessageEmbedFooter {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public iconURL: string;
+ public proxyIconURL: string;
+ public text: string;
+ }
+
+ export class MessageEmbedImage {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public height: number;
+ public proxyURL: string;
+ public url: string;
+ public width: number;
+ }
+
+ export class MessageEmbedProvider {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public name: string;
+ public url: string;
+ }
+
+ export class MessageEmbedThumbnail {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public height: number;
+ public proxyURL: string;
+ public url: string;
+ public width: number;
+ }
+
+ export class MessageEmbedVideo {
+ constructor(embed: MessageEmbed, data: object);
+ public embed: MessageEmbed;
+ public height: number;
+ public url: string;
+ public width: number;
+ }
+
+ export class MessageFlags extends BitField<MessageFlagsString> {
+ public static FLAGS: Record<MessageFlagsString, number>;
+ public static resolve(bit?: BitFieldResolvable<MessageFlagsString>): number;
+ }
+
+ export class MessageMentions {
+ private _channels: Collection<Snowflake, GuildChannel>;
+ private _client: Client;
+ private _content: Message;
+ private _guild: Guild;
+ private _members: Collection<Snowflake, GuildMember>;
+
+ public readonly channels: Collection<Snowflake, TextChannel>;
+ public crosspostedChannels: Collection<Snowflake, CrosspostedChannel>;
+ public everyone: boolean;
+ public readonly members: Collection<Snowflake, GuildMember>;
+ public roles: Collection<Snowflake, Role>;
+ public users: Collection<Snowflake, User>;
+
+ public static CHANNELS_PATTERN: RegExp;
+ public static EVERYONE_PATTERN: RegExp;
+ public static ROLES_PATTERN: RegExp;
+ public static USERS_PATTERN: RegExp;
+ }
+
+ export class MessageReaction {
+ constructor(message: Message, emoji: object, count: number, me: boolean);
+ public count: number;
+ public readonly emoji: Emoji | ReactionEmoji;
+ public me: boolean;
+ public message: Message;
+ public users: Collection<string, User>;
+ public fetchUsers(limit?: number, options?: { after?: number; before?: number }): Promise<Collection<Snowflake, User>>;
+ public remove(user?: UserResolvable): Promise<MessageReaction>;
+ public removeAll(): Promise<MessageReaction>;
+ }
+
+ export class NewsChannel extends TextChannel {
+ constructor(guild: Guild, data: object);
+ public rateLimitPerUser: 0;
+ }
+
+ export class OAuth2Application {
+ constructor(client: Client, data: object);
+ public bot: object;
+ public botPublic: boolean;
+ public botRequireCodeGrant: boolean;
+ public readonly client: Client;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public description: string;
+ public flags: number;
+ public icon: string;
+ public iconURL: string;
+ public id: Snowflake;
+ public name: string;
+ public owner: User;
+ public redirectURIs: string[];
+ public rpcApplicationState: boolean;
+ public rpcOrigins: string[];
+ public secret: string;
+ public team: Team | null;
+ public reset(): OAuth2Application;
+ public toString(): string;
+ }
+
+ export class PartialGuild {
+ constructor(client: Client, data: object);
+ public readonly client: Client;
+ public icon: string;
+ public id: Snowflake;
+ public name: string;
+ public splash: string;
+ }
+
+ export class PartialGuildChannel {
+ constructor(client: Client, data: object);
+ public readonly client: Client;
+ public id: Snowflake;
+ public name: string;
+ public type: string;
+ }
+
+ export class PermissionOverwrites {
+ constructor(guildChannel: GuildChannel, data: object);
+ public allow: number;
+ public allowed: Permissions;
+ public channel: GuildChannel;
+ public denied: Permissions;
+ public deny: number;
+ public id: Snowflake;
+ public type: string;
+ public delete(reason?: string): Promise<PermissionOverwrites>;
+ }
+
+ export class Permissions extends BitField<PermissionString> {
+ constructor(permissions: PermissionResolvable);
+ constructor(member: GuildMember, permissions: PermissionResolvable);
+
+ public bitfield: number;
+ public member: GuildMember;
+ public readonly raw: number;
+ public any(permissions: PermissionResolvable, checkAdmin?: boolean): boolean;
+ public has(permission: PermissionResolvable, checkAdmin?: boolean): boolean;
+ public hasPermission(permission: PermissionResolvable, explicit?: boolean): boolean;
+ public hasPermissions(permissions: PermissionResolvable, explicit?: boolean): boolean;
+ public missing(permissions: PermissionResolvable, checkAdmin?: boolean): PermissionString[];
+ public missingPermissions(permissions: PermissionResolvable, checkAdmin?: boolean): PermissionResolvable;
+ public serialize(checkAdmin?: boolean): Required<PermissionObject>;
+ public toArray(checkAdmin?: boolean): PermissionString[];
+ public valueOf(): number;
+
+ public static ALL: number;
+ public static DEFAULT: number;
+ public static FLAGS: PermissionFlags;
+ public static resolve(permission?: PermissionResolvable): number;
+ }
+
+ export class Presence {
+ constructor(data: object, client: Client);
+ public activities: Game[];
+ public readonly client: Client;
+ public game: Game;
+ public status: PresenceStatusData;
+ public clientStatus: ClientPresenceStatusData;
+ public equals(presence: Presence): boolean;
+ }
+
+ export class ReactionCollector extends Collector<Snowflake, MessageReaction> {
+ constructor(message: Message, filter: CollectorFilter, options?: ReactionCollectorOptions);
+ public message: Message;
+ public options: ReactionCollectorOptions;
+ public total: number;
+ public users: Collection<Snowflake, User>;
+
+ public cleanup(): void;
+ public handle(reaction: MessageReaction): CollectorHandler<Snowflake, MessageReaction>;
+ public postCheck(reaction: MessageReaction, user: User): string;
+ }
+
+ export class ReactionEmoji {
+ constructor(reaction: MessageReaction, emoji: object);
+ public animated: boolean;
+ public readonly client: Client;
+ public readonly createdAt: number | null;
+ public readonly createdTimestamp: number | null;
+ public id: Snowflake;
+ public readonly identifier: string;
+ public name: string;
+ public reaction: MessageReaction;
+ public readonly url: string | null;
+ public toString(): string;
+ }
+
+ class RequestHandler {
+ constructor(restManager: object);
+ public readonly globalLimit: boolean;
+ public queue: object[];
+ public restManager: object;
+ public handle(): void;
+ public push(request: object): void;
+ }
+
+ interface EmbedField {
+ name: string;
+ value: string;
+ inline: boolean;
+ }
+
+ export class RichEmbed {
+ constructor(data?: RichEmbedOptions | MessageEmbed);
+ public author?: { name: string; url?: string; icon_url?: string; };
+ public color?: number;
+ public description?: string;
+ public fields?: { name: string; value: string; inline?: boolean; }[];
+ public file?: Attachment | string | FileOptions;
+ public files?: Array<Attachment | string | FileOptions>;
+ public footer?: { text?: string; icon_url?: string; };
+ public image?: { url: string; proxy_url?: string; height?: number; width?: number; };
+ public readonly length: number;
+ public thumbnail?: { url: string; height?: number; width?: number; };
+ public timestamp?: Date;
+ public title?: string;
+ public url?: string;
+ public addBlankField(inline?: boolean): this;
+ public spliceFields(index: number, deleteCount: number, ...fields: EmbedFieldData[]): this;
+ public addField(name: StringResolvable, value: StringResolvable, inline?: boolean): this;
+ public attachFile(file: Attachment | FileOptions | string): this;
+ public attachFiles(file: Array<Attachment | FileOptions | string>): this;
+ public setAuthor(name: StringResolvable, icon?: string, url?: string): this;
+ public setColor(color: ColorResolvable): this;
+ public setDescription(description: StringResolvable): this;
+ public setFooter(text: StringResolvable, icon?: string): this;
+ public setImage(url: string): this;
+ public setThumbnail(url: string): this;
+ public setTimestamp(timestamp?: Date | number): this;
+ public setTitle(title: StringResolvable): this;
+ public setURL(url: string): this;
+ public static normalizeField(name: StringResolvable, value: StringResolvable, inline?: boolean): EmbedField;
+ }
+
+ export class RichPresenceAssets {
+ constructor(game: Game, assets: object);
+ public largeImage: Snowflake;
+ public largeText: string;
+ public smallImage: Snowflake;
+ public smallText: string;
+ public readonly smallImageURL: string;
+ public readonly largeImageURL: string;
+ }
+
+ export class Role {
+ constructor(guild: Guild, data: object);
+ public readonly calculatedPosition: number;
+ public readonly client: Client;
+ public color: number;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public deleted: boolean;
+ public readonly editable: boolean;
+ public guild: Guild;
+ public readonly hexColor: string;
+ public hoist: boolean;
+ public id: Snowflake;
+ public managed: boolean;
+ public readonly members: Collection<Snowflake, GuildMember>;
+ public mentionable: boolean;
+ public name: string;
+ public permissions: number;
+ public position: number;
+ public comparePositionTo(role: Role): number;
+ public delete(reason?: string): Promise<Role>;
+ public edit(data: RoleData, reason?: string): Promise<Role>;
+ public equals(role: Role): boolean;
+ public hasPermission(permission: PermissionResolvable, explicit?: boolean, checkAdmin?: boolean): boolean;
+ public hasPermissions(permissions: PermissionResolvable, explicit?: boolean): boolean;
+ public serialize(): PermissionObject;
+ public setColor(color: string | number, reason?: string): Promise<Role>;
+ public setHoist(hoist: boolean, reason?: string): Promise<Role>;
+ public setMentionable(mentionable: boolean, reason?: string): Promise<Role>;
+ public setName(name: string, reason?: string): Promise<Role>;
+ public setPermissions(permissions: PermissionResolvable, reason?: string): Promise<Role>;
+ public setPosition(position: number, relative?: boolean): Promise<Role>;
+ public toString(): string;
+
+ public static comparePositions(role1: Role, role2: Role): number;
+ }
+
+ class SecretKey {
+ constructor(key: Uint8Array);
+ public key: Uint8Array;
+ }
+
+ class SequentialRequestHandler extends RequestHandler {
+ constructor(restManager: object, endpoint: string);
+ public busy: boolean;
+ public endpoint: string;
+ public readonly globalLimit: boolean;
+ public queue: object[];
+ public restManager: object;
+ public timeDifference: number;
+ public execute(item: object): Promise<object | Error>;
+ public handle(): void;
+ public push(request: object): void;
+ }
+
+ export class Shard extends EventEmitter {
+ constructor(manager: ShardingManager, id: number, args?: string[]);
+ private _exitListener: Function;
+ private _handleExit(respawn?: boolean): void;
+ private _handleMessage(message: any): void;
+
+ public env: object;
+ public id: string;
+ public manager: ShardingManager;
+ public process: ChildProcess;
+ public readonly: boolean;
+ public eval(script: string): Promise<any>;
+ public fetchClientValue(prop: string): Promise<any>;
+ public kill(): void;
+ public respawn(delay?: number): Promise<ChildProcess>;
+ public spawn(args?: string[], execArgv?: string[]): Promise<ChildProcess>;
+ public send(message: any): Promise<Shard>;
+
+ public on(event: 'death', listener: () => void): this;
+ public on(event: 'disconnect', listener: () => void): this;
+ public on(event: 'message', listener: (message: any) => void): this;
+ public on(event: 'ready', listener: () => void): this;
+ public on(event: 'reconnecting', listener: () => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'death', listener: () => void): this;
+ public once(event: 'disconnect', listener: () => void): this;
+ public once(event: 'message', listener: (message: any) => void): this;
+ public once(event: 'ready', listener: () => void): this;
+ public once(event: 'reconnecting', listener: () => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ export class ShardClientUtil {
+ constructor(client: Client);
+ private _handleMessage(message: any): void;
+ private _respond(type: string, message: any): void;
+
+ public readonly count: number;
+ public readonly id: number;
+ public broadcastEval(script: string): Promise<any[]>;
+ public fetchClientValues(prop: string): Promise<any[]>;
+ public send(message: any): Promise<void>;
+
+ public static singleton(client: Client): ShardClientUtil;
+ }
+
+ export class ShardingManager extends EventEmitter {
+ constructor(file: string, options?: {
+ totalShards?: number | 'auto';
+ respawn?: boolean;
+ shardArgs?: string[];
+ token?: string;
+ });
+ private _spawn(amount: number, delay: number): Promise<Collection<number, Shard>>;
+
+ public execArgv: string[];
+ public file: string;
+ public respawn: boolean;
+ public shardArgs: string[];
+ public shards: Collection<number, Shard>;
+ public token: string;
+ public totalShards: number | string;
+ public broadcast(message: any): Promise<Shard[]>;
+ public broadcastEval(script: string): Promise<any[]>;
+ public createShard(id: number): Promise<Shard>;
+ public fetchClientValues(prop: string): Promise<any[]>;
+ public respawnAll(shardDelay?: number, respawnDelay?: number, waitForReady?: true, currentShardIndex?: number): Promise<Collection<number, Shard>>;
+ public spawn(amount?: number, delay?: number): Promise<Collection<number, Shard>>;
+
+ public on(event: 'launch', listener: (shard: Shard) => void): this;
+ public on(event: 'message', listener: (shard: Shard, message: any) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'launch', listener: (shard: Shard) => void): this;
+ public once(event: 'message', listener: (shard: Shard, message: any) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ export class SnowflakeUtil {
+ public static deconstruct(snowflake: Snowflake): DeconstructedSnowflake;
+ public static generate(timestamp?: number | Date): Snowflake;
+ }
+
+ export class StoreChannel extends GuildChannel {
+ constructor(guild: Guild, data: object);
+ public nsfw: boolean;
+ }
+
+ export class StreamDispatcher extends VolumeInterface {
+ constructor(player: AudioPlayer, stream: NodeJS.ReadableStream, streamOptions: StreamOptions);
+ public destroyed: boolean;
+ public readonly passes: number;
+ public paused: boolean;
+ public player: AudioPlayer;
+ public stream: ReadableStream | VoiceBroadcast;
+ public readonly time: number;
+ public readonly totalStreamTime: number;
+ public end(reason?: string): void;
+ public pause(): void;
+ public resume(): void;
+ public setBitrate(bitrate: number | 'auto'): void;
+ }
+
+ export class SystemChannelFlags extends BitField<SystemChannelFlagsString> {
+ public static FLAGS: Record<SystemChannelFlagsString, number>;
+ public static resolve(bit?: BitFieldResolvable<SystemChannelFlagsString>): number;
+ }
+
+ export class Team {
+ constructor(client: Client, data: object);
+ public readonly client: Client;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public icon: string | null;
+ public readonly iconURL: string;
+ public id: Snowflake;
+ public members: Collection<Snowflake, TeamMember>;
+ public name: string;
+ public readonly owner: TeamMember;
+ public ownerID: Snowflake | null;
+
+ public toString(): string;
+ }
+
+ export class TeamMember {
+ constructor(client: Client, team: Team, data: object);
+ public readonly client: Client;
+ public id: Snowflake;
+ public membershipState: MembershipStates;
+ public permissions: string[];
+ public team: Team;
+ public user: User;
+
+ public toString(): string;
+ }
+
+ export class TextChannel extends TextBasedChannel(GuildChannel) {
+ constructor(guild: Guild, data: object);
+ public lastMessageID: string;
+ public readonly members: Collection<Snowflake, GuildMember>;
+ public messages: Collection<Snowflake, Message>;
+ public nsfw: boolean;
+ public topic: string | null;
+ public rateLimitPerUser: number;
+ public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<TextChannel>;
+ public createWebhook(name: string, avatar: BufferResolvable, reason?: string): Promise<Webhook>;
+ public fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
+ public setNSFW(nsfw: boolean, reason?: string): Promise<this>;
+ }
+
+ export class User extends PartialTextBasedChannel() {
+ constructor(client: Client, data: object);
+ public avatar: string;
+ public readonly avatarURL: string;
+ public bot: boolean;
+ public readonly client: Client;
+ public readonly createdAt: Date;
+ public readonly createdTimestamp: number;
+ public readonly defaultAvatarURL: string;
+ public discriminator: string;
+ public readonly displayAvatarURL: string;
+ public readonly dmChannel: DMChannel;
+ public id: Snowflake;
+ public lastMessageID: string;
+ public readonly note: string;
+ public readonly presence: Presence;
+ public system?: boolean;
+ public readonly tag: string;
+ public username: string;
+ public addFriend(): Promise<User>;
+ public block(): Promise<User>;
+ public createDM(): Promise<DMChannel>;
+ public deleteDM(): Promise<DMChannel>;
+ public equals(user: User): boolean;
+ public fetchProfile(): Promise<UserProfile>;
+ public removeFriend(): Promise<User>;
+ public setNote(note: string): Promise<User>;
+ public toString(): string;
+ public typingDurationIn(channel: ChannelResolvable): number;
+ public typingIn(channel: ChannelResolvable): boolean;
+ public typingSinceIn(channel: ChannelResolvable): Date;
+ public unblock(): Promise<User>;
+ }
+
+ export class UserConnection {
+ constructor(user: User, data: object);
+ public id: string;
+ public integrations: object[];
+ public name: string;
+ public revoked: boolean;
+ public type: string;
+ public user: User;
+ }
+
+ export class UserProfile {
+ constructor(user: User, data: object);
+ public readonly client: Client;
+ public connections: Collection<string, UserConnection>;
+ public mutualGuilds: Collection<Snowflake, Guild>;
+ public premium: boolean;
+ public premiumSince: Date;
+ public user: User;
+ }
+
+ export class Util {
+ public static arraysEqual(a: any[], b: any[]): boolean;
+ public static cloneObject(obj: object): object;
+ public static convertToBuffer(ab: ArrayBuffer | string): Buffer;
+ public static delayFor(ms: number): Promise<void>;
+ public static escapeMarkdown(text: string, onlyCodeBlock?: boolean, onlyInlineCode?: boolean): string;
+ public static fetchRecommendedShards(token: string, guildsPerShard?: number): Promise<number>;
+ public static makeError(obj: { name: string, message: string, stack: string }): Error;
+ public static makePlainError(err: Error): object;
+ public static mergeDefault(def: object, given: object): object;
+ public static moveElementInArray(array: any[], element: any, newIndex: number, offset?: boolean): number;
+ public static parseEmoji(text: string): { animated: boolean; name: string; id: string; };
+ public static splitMessage(text: string, options?: SplitOptions): string | string[];
+ public static str2ab(str: string): ArrayBuffer;
+ }
+
+ export class VoiceBroadcast extends EventEmitter {
+ constructor(client: Client);
+ public readonly client: Client;
+ public currentTranscoder: object;
+ public readonly dispatchers: StreamDispatcher[];
+ public prism: object;
+ public destroy(): void;
+ public end(): void;
+ public pause(): void;
+ public playArbitraryInput(input: string, options?: StreamOptions): VoiceBroadcast;
+ public playConvertedStream(stream: ReadableStream, options?: StreamOptions): VoiceBroadcast;
+ public playFile(file: string, options?: StreamOptions): StreamDispatcher;
+ public playOpusStream(stream: ReadableStream, options?: StreamOptions): StreamDispatcher;
+ public playStream(stream: ReadableStream, options?: StreamOptions): VoiceBroadcast;
+ public resume(): void;
+
+ public on(event: string, listener: Function): this;
+ public on(event: 'end', listener: () => void): this;
+ public on(event: 'error', listener: (error: Error) => void): this;
+ public on(event: 'subscribe', listener: (dispatcher: StreamDispatcher) => void): this;
+ public on(event: 'unsubscribe', listener: (dispatcher: StreamDispatcher) => void): this;
+ public on(event: 'warn', listener: (warning: string | Error) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'end', listener: () => void): this;
+ public once(event: 'error', listener: (error: Error) => void): this;
+ public once(event: 'subscribe', listener: (dispatcher: StreamDispatcher) => void): this;
+ public once(event: 'unsubscribe', listener: (dispatcher: StreamDispatcher) => void): this;
+ public once(event: 'warn', listener: (warning: string | Error) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ export class VoiceChannel extends GuildChannel {
+ constructor(guild: Guild, data: object);
+ public bitrate: number;
+ public readonly connection: VoiceConnection;
+ public readonly full: boolean;
+ public readonly joinable: boolean;
+ public members: Collection<Snowflake, GuildMember>;
+ public readonly speakable: boolean;
+ public userLimit: number;
+ public join(): Promise<VoiceConnection>;
+ public leave(): void;
+ public setBitrate(bitrate: number, reason?: string): Promise<VoiceChannel>;
+ public setUserLimit(userLimit: number, reason?: string): Promise<VoiceChannel>;
+ }
+
+ export class VoiceConnection extends EventEmitter {
+ constructor(voiceManager: ClientVoiceManager, channel: VoiceChannel);
+ private authentication: object;
+ private sockets: object;
+ private ssrcMap: Map<number, Snowflake>;
+ private speakingTimeouts: Map<number, NodeJS.Timer>;
+ private authenticate(): void;
+ private authenticateFailed(reason: string): void;
+ private checkAuthenticated(): void;
+ private cleanup(): void;
+ private connect(): void;
+ private onReady(data: object): void;
+ private onSessionDescription(mode: string, secret: string): void;
+ private onSpeaking(data: object): void;
+ private reconnect(token: string, endpoint: string): void;
+ private setSpeaking(value: boolean): void;
+ private updateChannel(channel: VoiceChannel): void;
+
+ public channel: VoiceChannel;
+ public readonly client: Client;
+ public readonly dispatcher: StreamDispatcher;
+ public player: AudioPlayer;
+ public prism: object;
+ public receivers: VoiceReceiver[];
+ public speaking: boolean;
+ public status: number;
+ public voiceManager: ClientVoiceManager;
+ public createReceiver(): VoiceReceiver;
+ public disconnect(): void;
+ public playArbitraryInput(input: string, options?: StreamOptions): StreamDispatcher;
+ public playBroadcast(broadcast: VoiceBroadcast, options?: StreamOptions): StreamDispatcher;
+ public playConvertedStream(stream: ReadableStream, options?: StreamOptions): StreamDispatcher;
+ public playFile(file: string, options?: StreamOptions): StreamDispatcher;
+ public playOpusStream(steam: ReadableStream, options?: StreamOptions): StreamDispatcher;
+ public playStream(stream: ReadableStream, options?: StreamOptions): StreamDispatcher;
+ public sendVoiceStateUpdate(options: object): void;
+ public setSessionID(sessionID: string): void;
+ public setTokenAndEndpoint(token: string, endpoint: string): void;
+
+ public on(event: 'authenticated', listener: () => void): this;
+ public on(event: 'debug', listener: (message: string) => void): this;
+ public on(event: 'disconnect', listener: (error: Error) => void): this;
+ public on(event: 'error', listener: (error: Error) => void): this;
+ public on(event: 'failed', listener: (error: Error) => void): this;
+ public on(event: 'newSession', listener: () => void): this;
+ public on(event: 'ready', listener: () => void): this;
+ public on(event: 'reconnecting', listener: () => void): this;
+ public on(event: 'speaking', listener: (user: User, speaking: boolean) => void): this;
+ public on(event: 'warn', listener: (warning: string | Error) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'authenticated', listener: () => void): this;
+ public once(event: 'debug', listener: (message: string) => void): this;
+ public once(event: 'disconnect', listener: (error: Error) => void): this;
+ public once(event: 'error', listener: (error: Error) => void): this;
+ public once(event: 'failed', listener: (error: Error) => void): this;
+ public once(event: 'newSession', listener: () => void): this;
+ public once(event: 'ready', listener: () => void): this;
+ public once(event: 'reconnecting', listener: () => void): this;
+ public once(event: 'speaking', listener: (user: User, speaking: boolean) => void): this;
+ public once(event: 'warn', listener: (warning: string | Error) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ class VoiceConnectionUDPClient extends EventEmitter {
+ constructor(voiceConnection: VoiceConnection);
+ public discordAddress: string;
+ public readonly discordPort: number;
+ public localAddress: string;
+ public localPort: string;
+ public socket: any;
+ public voiceConnection: VoiceConnection;
+ public findEndpointAddress(): Promise<string>;
+ public send(packet: object): Promise<object>;
+ }
+
+ export class VoiceReceiver extends EventEmitter {
+ constructor(connection: VoiceConnection);
+ private stoppedSpeaking(user: User): void;
+
+ public destroyed: boolean;
+ public voiceConnection: VoiceConnection;
+ public createOpusStream(user: UserResolvable): ReadableStream;
+ public createPCMStream(user: UserResolvable): ReadableStream;
+ public destroy(): void;
+ public recreate(): void;
+
+ public on(event: 'opus', listener: (user: User, buffer: Buffer) => void): this;
+ public on(event: 'pcm', listener: (user: User, buffer: Buffer) => void): this;
+ public on(event: 'warn', listener: (reason: string, message: string) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'opus', listener: (user: User, buffer: Buffer) => void): this;
+ public once(event: 'pcm', listener: (user: User, buffer: Buffer) => void): this;
+ public once(event: 'warn', listener: (reason: string, message: string) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ export class VoiceRegion {
+ constructor(data: object);
+ public custom: boolean;
+ public deprecated: boolean;
+ public id: string;
+ public name: string;
+ public optimal: boolean;
+ public sampleHostname: string;
+ public vip: boolean;
+ }
+
+ class VoiceWebsocket extends EventEmitter {
+ constructor(voiceConnection: VoiceConnection);
+ public attempts: number;
+ public readonly client: Client;
+ public voiceConnection: VoiceConnection;
+ public ws: any;
+ public clearHeartbeat(): void;
+ public connect(): void;
+ public onClose(): void;
+ public onError(error: Error): void;
+ public onMessage(event: any): void;
+ public onOpen(): void;
+ public onPacket(packet: object): void;
+ public reset(): void;
+ public send(data: string): Promise<string>;
+ public sendHeartbeat(): void;
+ public sendPacket(packet: object): Promise<string>;
+ public setHeartbeat(interval: number): void;
+
+ public on(event: 'ready', listener: (packet: object) => void): this;
+ public on(event: 'sessionDescription', listener: (encryptionMode: string, secretKey: SecretKey) => void): this;
+ public on(event: 'startSpeaking', listener: (data: object) => void): this;
+ public on(event: 'unknownPacket', listener: (packet: object) => void): this;
+ public on(event: 'warn', listener: (warn: string) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'ready', listener: (packet: object) => void): this;
+ public once(event: 'sessionDescription', listener: (encryptionMode: string, secretKey: SecretKey) => void): this;
+ public once(event: 'startSpeaking', listener: (data: object) => void): this;
+ public once(event: 'unknownPacket', listener: (packet: object) => void): this;
+ public once(event: 'warn', listener: (warn: string) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ export class VolumeInterface extends EventEmitter {
+ constructor(object?: { volume: number })
+ public readonly volume: number;
+ public readonly volumeDecibels: number;
+ public readonly volumeLogarithmic: number;
+ public setVolume(volume: number): void;
+ public setVolumeDecibels(db: number): void;
+ public setVolumeLogarithmic(value: number): void;
+
+ public on(event: 'debug', listener: (information: string) => void): this;
+ public on(event: 'end', listener: (reason: string) => void): this;
+ public on(event: 'error', listener: (err: Error) => void): this;
+ public on(event: 'speaking', listener: (value: boolean) => void): this;
+ public on(event: 'start', listener: () => void): this;
+ public on(event: 'volumeChange', listener: (oldVolume: number, newVolume: number) => void): this;
+ public on(event: string, listener: Function): this;
+
+ public once(event: 'debug', listener: (information: string) => void): this;
+ public once(event: 'end', listener: (reason: string) => void): this;
+ public once(event: 'error', listener: (err: Error) => void): this;
+ public once(event: 'speaking', listener: (value: boolean) => void): this;
+ public once(event: 'start', listener: () => void): this;
+ public once(event: 'volumeChange', listener: (oldVolume: number, newVolume: number) => void): this;
+ public once(event: string, listener: Function): this;
+ }
+
+ export class Webhook {
+ constructor(client: Client, dataOrID: object | string, token: string);
+ public avatar: string | null;
+ public readonly avatarURL: string | null;
+ public channelID: string;
+ public readonly client: Client;
+ public guildID: string;
+ public id: Snowflake;
+ public name: string;
+ public owner: User | object;
+ public token: string | null;
+ public type: WebhookTypes;
+ public readonly url: string;
+ public delete(reason?: string): Promise<void>;
+ public edit(name?: string, avatar?: BufferResolvable): Promise<Webhook>;
+ public edit(options?: WebhookEditOptions, reason?: string): Promise<Webhook>;
+ public send(content?: StringResolvable, options?: WebhookMessageOptions & { split: false } | RichEmbed | Attachment): Promise<Message>;
+ public send(content?: StringResolvable, options?: WebhookMessageOptions | RichEmbed | Attachment): Promise<Message | Message[]>;
+ public send(options?: WebhookMessageOptions | RichEmbed | Attachment): Promise<Message | Message[]>;
+ public sendCode(lang: string, content: StringResolvable, options?: WebhookMessageOptions): Promise<Message | Message[]>;
+ public sendFile(attachment: BufferResolvable, name?: string, content?: StringResolvable, options?: WebhookMessageOptions): Promise<Message>;
+ public sendMessage(content?: StringResolvable, options?: WebhookMessageOptions): Promise<Message | Message[]>;
+ public sendMessage(options?: WebhookMessageOptions): Promise<Message | Message[]>;
+ public sendSlackMessage(body: object): Promise<void>;
+ }
+
+ export class WebhookClient extends Webhook {
+ constructor(id: string, token: string, options?: ClientOptions);
+ private _intervals: Set<NodeJS.Timer>;
+ private _timeouts: Set<NodeJS.Timer>;
+ private resolver: ClientDataResolver;
+ private rest: object;
+ public token: string;
+
+ public options: ClientOptions;
+ public clearInterval(interval: NodeJS.Timer): void;
+ public clearTimeout(timeout: NodeJS.Timer): void;
+ public destroy(): void;
+ public setInterval(fn: Function, delay: number, ...args: any[]): NodeJS.Timer;
+ public setTimeout(fn: Function, delay: number, ...args: any[]): NodeJS.Timer;
+ }
+
+//#endregion
+
+//#region Mixins
+
+ // Model the TextBasedChannel mixin system, allowing application of these fields
+ // to the classes that use these methods without having to manually add them
+ // to each of those classes
+
+ type Constructable<T> = new (...args: any[]) => T;
+ const PartialTextBasedChannel: <T>(Base?: Constructable<T>) => Constructable<T & PartialTextBasedChannelFields>;
+ const TextBasedChannel: <T>(Base?: Constructable<T>) => Constructable<T & TextBasedChannelFields>;
+
+ type PartialTextBasedChannelFields = {
+ lastMessage: Message;
+ acknowledge(): Promise<DMChannel | GroupDMChannel | TextChannel>;
+ send(content?: StringResolvable, options?: MessageOptions & { split: false } | RichEmbed | Attachment): Promise<Message>;
+ send(content?: StringResolvable, options?: MessageOptions | RichEmbed | Attachment): Promise<Message | Message[]>;
+ send(options?: MessageOptions | RichEmbed | Attachment): Promise<Message | Message[]>;
+ sendCode(lang: string, content: StringResolvable, options?: MessageOptions): Promise<Message | Message[]>;
+ sendEmbed(embed: RichEmbed | RichEmbedOptions, content?: string, options?: MessageOptions): Promise<Message>;
+ sendEmbed(embed: RichEmbed | RichEmbedOptions, options?: MessageOptions): Promise<Message>;
+ sendFile(attachment: BufferResolvable, name?: string, content?: StringResolvable, options?: MessageOptions): Promise<Message>;
+ sendMessage(content?: string, options?: MessageOptions): Promise<Message | Message[]>;
+ sendMessage(options?: MessageOptions): Promise<Message | Message[]>;
+ };
+
+ type TextBasedChannelFields = {
+ lastPinTimestamp: number;
+ readonly lastPinAt: Date;
+ typing: boolean;
+ typingCount: number;
+ awaitMessages(filter: CollectorFilter, options?: AwaitMessagesOptions): Promise<Collection<string, Message>>;
+ bulkDelete(messages: Collection<string, Message> | Message[] | Snowflake[] | number, filterOld?: boolean): Promise<Collection<string, Message>>;
+ createCollector(filter: CollectorFilter, options?: MessageCollectorOptions): MessageCollector;
+ createMessageCollector(filter: CollectorFilter, options?: MessageCollectorOptions): MessageCollector;
+ fetchMessage(messageID: string): Promise<Message>;
+ fetchMessages(options?: ChannelLogsQueryOptions): Promise<Collection<string, Message>>;
+ fetchPinnedMessages(): Promise<Collection<string, Message>>;
+ search(options?: MessageSearchOptions): Promise<MessageSearchResult>;
+ startTyping(count?: number): void;
+ stopTyping(force?: boolean): void;
+ } & PartialTextBasedChannelFields;
+
+//#endregion
+
+//#region Typedefs
+
+ type ActivityType = 'PLAYING'
+ | 'STREAMING'
+ | 'LISTENING'
+ | 'WATCHING'
+ | 'CUSTOM_STATUS';
+
+ type AddGuildMemberOptions = {
+ accessToken: String;
+ nick?: string;
+ roles?: Collection<string, Role> | Role[] | string[];
+ mute?: boolean;
+ deaf?: boolean;
+ };
+
+ type AuditLogChange = {
+ key: string;
+ old?: any;
+ new?: any;
+ };
+
+ type AwaitMessagesOptions = MessageCollectorOptions & { errors?: string[] };
+
+ type AwaitReactionsOptions = ReactionCollectorOptions & { errors?: string[] };
+
+ type BanInfo = {
+ user: User;
+ reason: string | null;
+ };
+
+ type BanOptions = {
+ days?: number;
+ reason?: string;
+ };
+
+ type Base64Resolvable = Buffer | Base64String;
+
+ type Base64String = string;
+
+ type BitFieldResolvable<T extends string> = RecursiveArray<T | number | Readonly<BitField<T>>> | T | number | Readonly<BitField<T>>;
+
+ type BufferResolvable = Buffer | string;
+
+ type ChannelCreationOverwrites = {
+ allow?: PermissionResolvable;
+ deny?: PermissionResolvable;
+ allowed?: PermissionResolvable;
+ denied?: PermissionResolvable;
+ id: RoleResolvable | UserResolvable;
+ };
+
+ type ChannelData = {
+ type?: GuildChannelType;
+ name?: string;
+ position?: number;
+ topic?: string;
+ nsfw?: boolean;
+ bitrate?: number;
+ userLimit?: number;
+ parent?: ChannelResolvable;
+ permissionOverwrites?: PermissionOverwrites[] | ChannelCreationOverwrites[];
+ rateLimitPerUser?: number;
+ reason?: string;
+ };
+
+ type ChannelLogsQueryOptions = {
+ limit?: number
+ before?: Snowflake
+ after?: Snowflake
+ around?: Snowflake
+ };
+
+ type ChannelPosition = {
+ channel: ChannelResolvable;
+ position: number;
+ };
+
+ type ChannelResolvable = Channel | Guild | Message | Snowflake;
+
+ type ClientOptions = {
+ apiRequestMethod?: string;
+ shardId?: number;
+ shardCount?: number;
+ messageCacheMaxSize?: number;
+ messageCacheLifetime?: number;
+ messageSweepInterval?: number;
+ fetchAllMembers?: boolean;
+ disableEveryone?: boolean;
+ sync?: boolean;
+ restWsBridgeTimeout?: number;
+ restTimeOffset?: number;
+ retryLimit?: number;
+ disabledEvents?: WSEventType[];
+ ws?: WebSocketOptions;
+ http?: HTTPOptions;
+ };
+
+ type CollectorHandler<K, V> = { key: K, value: V };
+ type CollectorFilter = (...args: any[]) => boolean;
+ type CollectorOptions = {
+ time?: number;
+ idle?: number;
+ };
+
+ type ColorResolvable = ('DEFAULT'
+ | 'WHITE'
+ | 'AQUA'
+ | 'GREEN'
+ | 'BLUE'
+ | 'PURPLE'
+ | 'LUMINOUS_VIVID_PINK'
+ | 'GOLD'
+ | 'ORANGE'
+ | 'RED'
+ | 'GREY'
+ | 'DARKER_GREY'
+ | 'NAVY'
+ | 'DARK_AQUA'
+ | 'DARK_GREEN'
+ | 'DARK_BLUE'
+ | 'DARK_PURPLE'
+ | ' DARK_VIVID_PINK'
+ | 'DARK_GOLD'
+ | 'DARK_ORANGE'
+ | 'DARK_RED'
+ | 'DARK_GREY'
+ | 'LIGHT_GREY'
+ | 'DARK_NAVY'
+ | 'RANDOM')
+ | [number, number, number]
+ | number
+ | string;
+
+ type CrosspostedChannel = {
+ channelID: Snowflake;
+ guildID: Snowflake;
+ type: Channel["type"] | 'unknown';
+ name: string;
+ };
+
+ type DeconstructedSnowflake = {
+ timestamp: number;
+ date: Date;
+ workerID: number;
+ processID: number;
+ increment: number;
+ binary: string;
+ };
+
+ type DefaultMessageNotifications = 'ALL'
+ | 'MENTIONS';
+
+ type EmojiEditData = {
+ name?: string;
+ roles?: Collection<Snowflake, Role> | Role[] | Snowflake[];
+ };
+
+ type EmojiIdentifierResolvable = string | Emoji | ReactionEmoji;
+
+ type FileOptions = {
+ attachment: BufferResolvable;
+ name?: string;
+ };
+
+ type GroupDMRecipientOptions = {
+ user?: UserResolvable | Snowflake;
+ accessToken?: string;
+ nick?: string;
+ };
+
+ type GuildAuditLogsAction = keyof GuildAuditLogsActions;
+
+ type GuildAuditLogsActions = {
+ ALL?: null;
+ GUILD_UPDATE?: number;
+ CHANNEL_CREATE?: number;
+ CHANNEL_UPDATE?: number;
+ CHANNEL_DELETE?: number;
+ CHANNEL_OVERWRITE_CREATE?: number;
+ CHANNEL_OVERWRITE_UPDATE?: number;
+ CHANNEL_OVERWRITE_DELETE?: number;
+ MEMBER_KICK?: number;
+ MEMBER_PRUNE?: number;
+ MEMBER_BAN_ADD?: number;
+ MEMBER_BAN_REMOVE?: number;
+ MEMBER_UPDATE?: number;
+ MEMBER_ROLE_UPDATE?: number;
+ MEMBER_MOVE?: number;
+ MEMBER_DISCONNECT?: number;
+ BOT_ADD?: number;
+ ROLE_CREATE?: number;
+ ROLE_UPDATE?: number;
+ ROLE_DELETE?: number;
+ INVITE_CREATE?: number;
+ INVITE_UPDATE?: number;
+ INVITE_DELETE?: number;
+ WEBHOOK_CREATE?: number;
+ WEBHOOK_UPDATE?: number;
+ WEBHOOK_DELETE?: number;
+ EMOJI_CREATE?: number;
+ EMOJI_UPDATE?: number;
+ EMOJI_DELETE?: number;
+ MESSAGE_DELETE?: number;
+ MESSAGE_BULK_DELETE?: number;
+ MESSAGE_PIN?: number;
+ MESSAGE_UNPIN?: number;
+ INTEGRATION_CREATE?: number;
+ INTEGRATION_UPDATE?: number;
+ INTEGRATION_DELETE?: number;
+ };
+
+ type GuildAuditLogsActionType = 'CREATE'
+ | 'DELETE'
+ | 'UPDATE'
+ | 'ALL';
+
+ type GuildAuditLogsFetchOptions = {
+ before?: Snowflake | GuildAuditLogsEntry;
+ after?: Snowflake | GuildAuditLogsEntry;
+ limit?: number;
+ user?: UserResolvable;
+ type?: string | number;
+ };
+
+ type GuildAuditLogsTarget = keyof GuildAuditLogsTargets;
+
+ type GuildAuditLogsTargets = {
+ ALL?: string;
+ GUILD?: string;
+ CHANNEL?: string;
+ USER?: string;
+ ROLE?: string;
+ INVITE?: string;
+ WEBHOOK?: string;
+ EMOJI?: string;
+ MESSAGE?: string;
+ INTEGRATION?: string;
+ UNKNOWN?: string;
+ };
+
+ type GuildChannelCloneOptions = {
+ name?: string;
+ permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>;
+ type?: GuildChannelType;
+ topic?: string;
+ nsfw?: boolean;
+ bitrate?: number;
+ userLimit?: number;
+ rateLimitPerUser?: number;
+ parent?: ChannelResolvable;
+ reason?: string;
+ }
+
+ type GuildChannelMessageNotifications = MessageNotifications
+ & 'INHERIT';
+
+ type GuildChannelType = 'category' | 'text' | 'voice' | 'news' | 'store';
+
+ type GuildEditData = {
+ name?: string;
+ region?: string;
+ verificationLevel?: number;
+ explicitContentFilter?: number;
+ afkChannel?: ChannelResolvable;
+ systemChannel?: ChannelResolvable;
+ systemChannelFlags?: SystemChannelFlagsResolvable;
+ afkTimeout?: number;
+ banner?: Base64Resolvable;
+ icon?: Base64Resolvable;
+ owner?: GuildMemberResolvable;
+ splash?: Base64Resolvable;
+ };
+
+ type GuildEmbedData = {
+ enabled: boolean;
+ channel: ChannelResolvable;
+ };
+
+ type GuildMemberEditData = {
+ nick?: string;
+ roles?: Collection<Snowflake, Role> | Role[] | Snowflake[];
+ mute?: boolean;
+ deaf?: boolean;
+ channel?: ChannelResolvable | null;
+ };
+
+ type GuildMemberResolvable = GuildMember | User;
+
+ type GuildResolvable = Guild | Snowflake;
+
+ type HTTPOptions = {
+ version?: number;
+ host?: string;
+ cdn?: string;
+ };
+
+ type IntegrationData = {
+ id: string;
+ type: string;
+ }
+
+ type IntegrationEditData = {
+ expireBehavior?: number;
+ expireGracePeriod?: number;
+ }
+
+ type IntegrationAccount = {
+ id: string;
+ number: string;
+ }
+
+ type InviteOptions = {
+ temporary?: boolean;
+ maxAge?: number;
+ maxUses?: number;
+ unique?: boolean;
+ };
+
+ type InviteResolvable = string;
+
+ type MembershipStates = 'INVITED'
+ | 'ACCEPTED';
+
+ type MessageCollectorOptions = CollectorOptions & {
+ max?: number;
+ maxMatches?: number;
+ };
+
+ type MessageEditOptions = {
+ embed?: RichEmbedOptions;
+ code?: string | boolean;
+ flags?: BitFieldResolvable<MessageFlagsString>;
+ };
+
+ type MessageFlagsString = 'CROSSPOSTED'
+ | 'IS_CROSSPOST'
+ | 'SUPRRESS_EMBEDS'
+ | 'SOURCE_MESSAGE_DELETED'
+ | 'URGENT';
+
+ type MessageNotifications = 'EVERYTHING'
+ | 'MENTIONS'
+ | 'NOTHING';
+
+ type MessageOptions = {
+ tts?: boolean;
+ nonce?: string;
+ embed?: RichEmbed | RichEmbedOptions,
+ disableEveryone?: boolean;
+ file?: FileOptions | string;
+ files?: (FileOptions | BufferResolvable | Attachment)[];
+ code?: string | boolean;
+ split?: boolean | SplitOptions;
+ reply?: UserResolvable;
+ };
+
+ type MessageSearchOptions = {
+ content?: string;
+ maxID?: Snowflake;
+ minID?: Snowflake;
+ has?: 'link'
+ | 'embed'
+ | 'file'
+ | 'video'
+ | 'image'
+ | 'sound'
+ | '-link'
+ | '-embed'
+ | '-file'
+ | '-video'
+ | '-image'
+ | '-sound';
+ channel?: ChannelResolvable;
+ author?: UserResolvable;
+ authorType?: 'user'
+ | 'bot'
+ | 'webhook'
+ | '-user'
+ | '-bot'
+ | '-webhook';
+ sortBy?: 'relevant' | 'recent';
+ sortOrder?: 'asc' | 'desc';
+ contextSize?: number;
+ limit?: number;
+ offset?: number;
+ mentions?: UserResolvable;
+ mentionsEveryone?: boolean;
+ linkHostname?: string;
+ embedProvider?: string;
+ embedType?: 'image' | 'video' | 'url' | 'rich';
+ attachmentFilename?: string;
+ attachmentExtension?: string;
+ before?: Date;
+ after?: Date;
+ during?: Date;
+ nsfw?: boolean;
+ };
+
+ type MessageReference = {
+ channelID: Snowflake;
+ guildID: Snowflake | null;
+ messageID: Snowflake | null;
+ };
+
+ type MessageSearchResult = {
+ totalResults: number;
+ messages: Message[][];
+ };
+
+ type ActivityFlags = {
+ INSTANCE?: number;
+ JOIN?: number;
+ SPECTATE?: number;
+ JOIN_REQUEST?: number;
+ SYNC?: number;
+ PLAY?: number;
+ };
+
+ type PermissionFlags = {
+ ADMINISTRATOR?: number;
+ CREATE_INSTANT_INVITE?: number;
+ KICK_MEMBERS?: number;
+ BAN_MEMBERS?: number;
+ MANAGE_CHANNELS?: number;
+ MANAGE_GUILD?: number;
+ ADD_REACTIONS?: number;
+ VIEW_AUDIT_LOG?: number;
+ PRIORITY_SPEAKER?: number;
+ STREAM?: number;
+ VIEW_CHANNEL?: number;
+ READ_MESSAGES?: number;
+ SEND_MESSAGES?: number;
+ SEND_TTS_MESSAGES?: number;
+ MANAGE_MESSAGES?: number;
+ EMBED_LINKS?: number;
+ ATTACH_FILES?: number;
+ READ_MESSAGE_HISTORY?: number;
+ MENTION_EVERYONE?: number;
+ USE_EXTERNAL_EMOJIS?: number;
+ EXTERNAL_EMOJIS?: number;
+ CONNECT?: number;
+ SPEAK?: number;
+ MUTE_MEMBERS?: number;
+ DEAFEN_MEMBERS?: number;
+ MOVE_MEMBERS?: number;
+ USE_VAD?: number;
+ CHANGE_NICKNAME?: number;
+ MANAGE_NICKNAMES?: number;
+ MANAGE_ROLES?: number;
+ MANAGE_ROLES_OR_PERMISSIONS?: number;
+ MANAGE_WEBHOOKS?: number;
+ MANAGE_EMOJIS?: number;
+ };
+
+ type PermissionObject = {
+ ADMINISTRATOR?: boolean;
+ CREATE_INSTANT_INVITE?: boolean;
+ KICK_MEMBERS?: boolean;
+ BAN_MEMBERS?: boolean;
+ MANAGE_CHANNELS?: boolean;
+ MANAGE_GUILD?: boolean;
+ ADD_REACTIONS?: boolean;
+ VIEW_AUDIT_LOG?: boolean;
+ PRIORITY_SPEAKER?: boolean;
+ STREAM?: boolean;
+ VIEW_CHANNEL?: boolean;
+ READ_MESSAGES?: boolean;
+ SEND_MESSAGES?: boolean;
+ SEND_TTS_MESSAGES?: boolean;
+ MANAGE_MESSAGES?: boolean;
+ EMBED_LINKS?: boolean;
+ ATTACH_FILES?: boolean;
+ READ_MESSAGE_HISTORY?: boolean;
+ MENTION_EVERYONE?: boolean;
+ USE_EXTERNAL_EMOJIS?: boolean;
+ EXTERNAL_EMOJIS?: boolean;
+ CONNECT?: boolean;
+ SPEAK?: boolean;
+ MUTE_MEMBERS?: boolean;
+ DEAFEN_MEMBERS?: boolean;
+ MOVE_MEMBERS?: boolean;
+ USE_VAD?: boolean;
+ CHANGE_NICKNAME?: boolean;
+ MANAGE_NICKNAMES?: boolean;
+ MANAGE_ROLES?: boolean;
+ MANAGE_ROLES_OR_PERMISSIONS?: boolean;
+ MANAGE_WEBHOOKS?: boolean;
+ MANAGE_EMOJIS?: boolean;
+ };
+
+ type PermissionString = 'ADMINISTRATOR'
+ | 'CREATE_INSTANT_INVITE'
+ | 'KICK_MEMBERS'
+ | 'BAN_MEMBERS'
+ | 'MANAGE_CHANNELS'
+ | 'MANAGE_GUILD'
+ | 'ADD_REACTIONS'
+ | 'VIEW_AUDIT_LOG'
+ | 'PRIORITY_SPEAKER'
+ | 'STREAM'
+ | 'VIEW_CHANNEL'
+ | 'READ_MESSAGES'
+ | 'SEND_MESSAGES'
+ | 'SEND_TTS_MESSAGES'
+ | 'MANAGE_MESSAGES'
+ | 'EMBED_LINKS'
+ | 'ATTACH_FILES'
+ | 'READ_MESSAGE_HISTORY'
+ | 'MENTION_EVERYONE'
+ | 'USE_EXTERNAL_EMOJIS'
+ | 'EXTERNAL_EMOJIS'
+ | 'CONNECT'
+ | 'SPEAK'
+ | 'MUTE_MEMBERS'
+ | 'DEAFEN_MEMBERS'
+ | 'MOVE_MEMBERS'
+ | 'USE_VAD'
+ | 'CHANGE_NICKNAME'
+ | 'MANAGE_NICKNAMES'
+ | 'MANAGE_ROLES'
+ | 'MANAGE_ROLES_OR_PERMISSIONS'
+ | 'MANAGE_WEBHOOKS'
+ | 'MANAGE_EMOJIS';
+
+ type PermissionOverwriteOptions = PermissionObject;
+
+ interface RecursiveArray<T> extends Array<T | RecursiveArray<T>> { }
+
+ interface EmbedFieldData {
+ name: StringResolvable;
+ value: StringResolvable;
+ inline?: boolean;
+ }
+
+ type PermissionResolvable = BitFieldResolvable<PermissionString>
+
+ type PremiumTier = number;
+
+ type PresenceData = {
+ status?: PresenceStatus;
+ afk?: boolean;
+ game?: {
+ name?: string;
+ url?: string;
+ type?: ActivityType;
+ } | null;
+ };
+
+ type ClientPresenceStatus = 'online' | 'idle' | 'dnd';
+
+ type PresenceStatus = ClientPresenceStatus | 'invisible';
+ type PresenceStatusData = ClientPresenceStatus | 'offline';
+
+ type ClientPresenceStatusData = {
+ web?: ClientPresenceStatus;
+ mobile?: ClientPresenceStatus;
+ desktop?: ClientPresenceStatus;
+ };
+
+ type RateLimitInfo = {
+ limit: number;
+ timeDifference: number;
+ method: string;
+ path: string;
+ };
+
+ type ReactionCollectorOptions = CollectorOptions & {
+ max?: number;
+ maxEmojis?: number;
+ maxUsers?: number;
+ };
+
+ type RichEmbedOptions = {
+ title?: string;
+ description?: string;
+ url?: string;
+ timestamp?: Date;
+ color?: number | string;
+ fields?: { name: string; value: string; inline?: boolean; }[];
+ file?: Attachment | string | FileOptions;
+ author?: { name: string; url?: string; icon_url?: string; };
+ thumbnail?: { url: string; height?: number; width?: number; };
+ image?: { url: string; proxy_url?: string; height?: number; width?: number; };
+ video?: { url: string; height: number; width: number; };
+ footer?: { text?: string; icon_url?: string; };
+ };
+
+ type RoleData = {
+ name?: string;
+ color?: ColorResolvable;
+ hoist?: boolean;
+ position?: number;
+ permissions?: PermissionResolvable;
+ mentionable?: boolean;
+ };
+
+ type RolePosition = {
+ role: RoleResolvable;
+ position: number;
+ };
+
+ type RoleResolvable = Role | string;
+
+ type Snowflake = string;
+
+ type SplitOptions = {
+ maxLength?: number;
+ char?: string;
+ prepend?: string;
+ append?: string;
+ };
+
+ type Status = number;
+
+ type StreamOptions = {
+ seek?: number;
+ volume?: number;
+ passes?: number;
+ bitrate?: number | 'auto';
+ };
+
+ type StringResolvable = string | string[] | any;
+
+ type SystemChannelFlagsString = 'WELCOME_MESSAGE_DISABLED' | 'BOOST_MESSAGE_DISABLED';
+
+ type SystemChannelFlagsResolvable = BitFieldResolvable<SystemChannelFlagsString>;
+
+ type UserResolvable = User | Snowflake | Message | Guild | GuildMember;
+
+ type VoiceStatus = number;
+
+ type WebhookEditOptions = {
+ name?: string;
+ avatar?: BufferResolvable;
+ channel?: ChannelResolvable;
+ };
+
+ type WebhookMessageOptions = {
+ username?: string;
+ avatarURL?: string;
+ tts?: boolean;
+ nonce?: string;
+ embeds?: (RichEmbed | object)[];
+ disableEveryone?: boolean;
+ file?: FileOptions | BufferResolvable | Attachment;
+ files?: (FileOptions | BufferResolvable | Attachment)[];
+ code?: string | boolean;
+ split?: boolean | SplitOptions;
+ };
+
+ type WebhookTypes = 'Incoming' | 'Channel Follower';
+
+ type WebSocketOptions = {
+ large_threshold?: number;
+ compress?: boolean;
+ };
+
+ type WSEventType = 'READY'
+ | 'RESUMED'
+ | 'GUILD_SYNC'
+ | 'GUILD_CREATE'
+ | 'GUILD_DELETE'
+ | 'GUILD_UPDATE'
+ | 'GUILD_MEMBER_ADD'
+ | 'GUILD_MEMBER_REMOVE'
+ | 'GUILD_MEMBER_UPDATE'
+ | 'GUILD_MEMBERS_CHUNK'
+ | 'GUILD_INTEGRATIONS_UPDATE'
+ | 'GUILD_ROLE_CREATE'
+ | 'GUILD_ROLE_DELETE'
+ | 'GUILD_ROLE_UPDATE'
+ | 'GUILD_BAN_ADD'
+ | 'GUILD_BAN_REMOVE'
+ | 'CHANNEL_CREATE'
+ | 'CHANNEL_DELETE'
+ | 'CHANNEL_UPDATE'
+ | 'CHANNEL_PINS_UPDATE'
+ | 'MESSAGE_CREATE'
+ | 'MESSAGE_DELETE'
+ | 'MESSAGE_UPDATE'
+ | 'MESSAGE_DELETE_BULK'
+ | 'MESSAGE_REACTION_ADD'
+ | 'MESSAGE_REACTION_REMOVE'
+ | 'MESSAGE_REACTION_REMOVE_ALL'
+ | 'USER_UPDATE'
+ | 'USER_NOTE_UPDATE'
+ | 'USER_SETTINGS_UPDATE'
+ | 'USER_GUILD_SETTINGS_UPDATE'
+ | 'PRESENCE_UPDATE'
+ | 'VOICE_STATE_UPDATE'
+ | 'TYPING_START'
+ | 'VOICE_SERVER_UPDATE'
+ | 'WEBHOOKS_UPDATE'
+ | 'RELATIONSHIP_ADD'
+ | 'RELATIONSHIP_REMOVE';
+
+//#endregion
+}