From a4429b7bef63a28d7650610dec483d57188b16a9 Mon Sep 17 00:00:00 2001 From: ZyLacx Date: Fri, 30 Oct 2020 22:52:39 +0100 Subject: [PATCH] first --- Procfile | 1 + main.js | 72 + node_modules/@discordjs/collection/LICENSE | 190 + node_modules/@discordjs/collection/README.md | 3 + .../@discordjs/collection/dist/index.d.ts | 319 + .../@discordjs/collection/dist/index.js | 392 + .../@discordjs/collection/package.json | 78 + node_modules/@discordjs/form-data/License | 19 + node_modules/@discordjs/form-data/Readme.md | 353 + node_modules/@discordjs/form-data/index.d.ts | 61 + .../@discordjs/form-data/lib/browser.js | 2 + .../@discordjs/form-data/lib/form_data.js | 497 + .../@discordjs/form-data/lib/populate.js | 10 + .../@discordjs/form-data/package.json | 98 + node_modules/abort-controller/LICENSE | 21 + node_modules/abort-controller/README.md | 98 + node_modules/abort-controller/browser.js | 13 + node_modules/abort-controller/browser.mjs | 11 + .../dist/abort-controller.d.ts | 43 + .../abort-controller/dist/abort-controller.js | 127 + .../dist/abort-controller.js.map | 1 + .../dist/abort-controller.mjs | 118 + .../dist/abort-controller.mjs.map | 1 + .../dist/abort-controller.umd.js | 5 + .../dist/abort-controller.umd.js.map | 1 + node_modules/abort-controller/package.json | 125 + node_modules/abort-controller/polyfill.js | 21 + node_modules/abort-controller/polyfill.mjs | 19 + node_modules/asynckit/LICENSE | 21 + node_modules/asynckit/README.md | 233 + node_modules/asynckit/bench.js | 76 + node_modules/asynckit/index.js | 6 + node_modules/asynckit/lib/abort.js | 29 + node_modules/asynckit/lib/async.js | 34 + node_modules/asynckit/lib/defer.js | 26 + node_modules/asynckit/lib/iterate.js | 75 + .../asynckit/lib/readable_asynckit.js | 91 + .../asynckit/lib/readable_parallel.js | 25 + node_modules/asynckit/lib/readable_serial.js | 25 + .../asynckit/lib/readable_serial_ordered.js | 29 + node_modules/asynckit/lib/state.js | 37 + node_modules/asynckit/lib/streamify.js | 141 + node_modules/asynckit/lib/terminator.js | 29 + node_modules/asynckit/package.json | 91 + node_modules/asynckit/parallel.js | 43 + node_modules/asynckit/serial.js | 17 + node_modules/asynckit/serialOrdered.js | 75 + node_modules/asynckit/stream.js | 21 + node_modules/combined-stream/License | 19 + node_modules/combined-stream/Readme.md | 138 + .../combined-stream/lib/combined_stream.js | 208 + node_modules/combined-stream/package.json | 57 + node_modules/combined-stream/yarn.lock | 17 + node_modules/delayed-stream/.npmignore | 1 + node_modules/delayed-stream/License | 19 + node_modules/delayed-stream/Makefile | 7 + node_modules/delayed-stream/Readme.md | 141 + .../delayed-stream/lib/delayed_stream.js | 107 + node_modules/delayed-stream/package.json | 62 + node_modules/discord.js/.tern-project | 17 + node_modules/discord.js/LICENSE | 190 + node_modules/discord.js/README.md | 111 + node_modules/discord.js/esm/discord.mjs | 94 + node_modules/discord.js/jsdoc.json | 3 + node_modules/discord.js/package.json | 192 + node_modules/discord.js/src/WebSocket.js | 49 + .../discord.js/src/client/BaseClient.js | 169 + node_modules/discord.js/src/client/Client.js | 486 + .../discord.js/src/client/WebhookClient.js | 31 + .../discord.js/src/client/actions/Action.js | 104 + .../src/client/actions/ActionsManager.js | 46 + .../src/client/actions/ChannelCreate.js | 23 + .../src/client/actions/ChannelDelete.js | 37 + .../src/client/actions/ChannelUpdate.js | 33 + .../src/client/actions/GuildBanRemove.js | 21 + .../actions/GuildChannelsPositionUpdate.js | 21 + .../src/client/actions/GuildDelete.js | 67 + .../src/client/actions/GuildEmojiCreate.js | 20 + .../src/client/actions/GuildEmojiDelete.js | 20 + .../src/client/actions/GuildEmojiUpdate.js | 20 + .../src/client/actions/GuildEmojisUpdate.js | 34 + .../client/actions/GuildIntegrationsUpdate.js | 19 + .../src/client/actions/GuildMemberRemove.js | 30 + .../src/client/actions/GuildRoleCreate.js | 25 + .../src/client/actions/GuildRoleDelete.js | 30 + .../src/client/actions/GuildRoleUpdate.js | 39 + .../actions/GuildRolesPositionUpdate.js | 21 + .../src/client/actions/GuildUpdate.js | 33 + .../src/client/actions/InviteCreate.js | 28 + .../src/client/actions/InviteDelete.js | 29 + .../src/client/actions/MessageCreate.js | 39 + .../src/client/actions/MessageDelete.js | 29 + .../src/client/actions/MessageDeleteBulk.js | 43 + .../src/client/actions/MessageReactionAdd.js | 52 + .../client/actions/MessageReactionRemove.js | 44 + .../actions/MessageReactionRemoveAll.js | 29 + .../actions/MessageReactionRemoveEmoji.js | 28 + .../src/client/actions/MessageUpdate.js | 24 + .../src/client/actions/PresenceUpdate.js | 44 + .../src/client/actions/TypingStart.js | 58 + .../src/client/actions/UserUpdate.js | 35 + .../src/client/actions/VoiceStateUpdate.js | 45 + .../src/client/actions/WebhooksUpdate.js | 19 + .../src/client/voice/ClientVoiceManager.js | 110 + .../src/client/voice/VoiceBroadcast.js | 111 + .../src/client/voice/VoiceConnection.js | 522 ++ .../voice/dispatcher/BroadcastDispatcher.js | 46 + .../voice/dispatcher/StreamDispatcher.js | 354 + .../client/voice/networking/VoiceUDPClient.js | 154 + .../client/voice/networking/VoiceWebSocket.js | 264 + .../src/client/voice/player/AudioPlayer.js | 27 + .../src/client/voice/player/BasePlayer.js | 92 + .../voice/player/BroadcastAudioPlayer.js | 28 + .../client/voice/receiver/PacketHandler.js | 121 + .../src/client/voice/receiver/Receiver.js | 58 + .../src/client/voice/util/PlayInterface.js | 94 + .../src/client/voice/util/Secretbox.js | 32 + .../src/client/voice/util/Silence.js | 13 + .../src/client/voice/util/VolumeInterface.js | 103 + .../src/client/websocket/WebSocketManager.js | 437 + .../src/client/websocket/WebSocketShard.js | 771 ++ .../websocket/handlers/CHANNEL_CREATE.js | 5 + .../websocket/handlers/CHANNEL_DELETE.js | 5 + .../websocket/handlers/CHANNEL_PINS_UPDATE.js | 22 + .../websocket/handlers/CHANNEL_UPDATE.js | 16 + .../websocket/handlers/GUILD_BAN_ADD.js | 16 + .../websocket/handlers/GUILD_BAN_REMOVE.js | 5 + .../client/websocket/handlers/GUILD_CREATE.js | 36 + .../client/websocket/handlers/GUILD_DELETE.js | 5 + .../websocket/handlers/GUILD_EMOJIS_UPDATE.js | 5 + .../handlers/GUILD_INTEGRATIONS_UPDATE.js | 5 + .../websocket/handlers/GUILD_MEMBERS_CHUNK.js | 30 + .../websocket/handlers/GUILD_MEMBER_ADD.js | 19 + .../websocket/handlers/GUILD_MEMBER_REMOVE.js | 5 + .../websocket/handlers/GUILD_MEMBER_UPDATE.js | 29 + .../websocket/handlers/GUILD_ROLE_CREATE.js | 5 + .../websocket/handlers/GUILD_ROLE_DELETE.js | 5 + .../websocket/handlers/GUILD_ROLE_UPDATE.js | 5 + .../client/websocket/handlers/GUILD_UPDATE.js | 5 + .../websocket/handlers/INVITE_CREATE.js | 5 + .../websocket/handlers/INVITE_DELETE.js | 5 + .../websocket/handlers/MESSAGE_CREATE.js | 5 + .../websocket/handlers/MESSAGE_DELETE.js | 5 + .../websocket/handlers/MESSAGE_DELETE_BULK.js | 5 + .../handlers/MESSAGE_REACTION_ADD.js | 5 + .../handlers/MESSAGE_REACTION_REMOVE.js | 5 + .../handlers/MESSAGE_REACTION_REMOVE_ALL.js | 5 + .../handlers/MESSAGE_REACTION_REMOVE_EMOJI.js | 5 + .../websocket/handlers/MESSAGE_UPDATE.js | 16 + .../websocket/handlers/PRESENCE_UPDATE.js | 5 + .../src/client/websocket/handlers/READY.js | 21 + .../src/client/websocket/handlers/RESUMED.js | 14 + .../client/websocket/handlers/TYPING_START.js | 5 + .../client/websocket/handlers/USER_UPDATE.js | 5 + .../websocket/handlers/VOICE_SERVER_UPDATE.js | 6 + .../websocket/handlers/VOICE_STATE_UPDATE.js | 5 + .../websocket/handlers/WEBHOOKS_UPDATE.js | 5 + .../src/client/websocket/handlers/index.js | 13 + .../discord.js/src/errors/DJSError.js | 61 + .../discord.js/src/errors/Messages.js | 108 + node_modules/discord.js/src/errors/index.js | 4 + node_modules/discord.js/src/index.js | 107 + .../discord.js/src/managers/BaseManager.js | 81 + .../discord.js/src/managers/ChannelManager.js | 96 + .../src/managers/GuildChannelManager.js | 131 + .../src/managers/GuildEmojiManager.js | 135 + .../src/managers/GuildEmojiRoleManager.js | 119 + .../discord.js/src/managers/GuildManager.js | 252 + .../src/managers/GuildMemberManager.js | 325 + .../src/managers/GuildMemberRoleManager.js | 161 + .../discord.js/src/managers/MessageManager.js | 147 + .../src/managers/PresenceManager.js | 59 + .../src/managers/ReactionManager.js | 69 + .../src/managers/ReactionUserManager.js | 66 + .../discord.js/src/managers/RoleManager.js | 146 + .../discord.js/src/managers/UserManager.js | 72 + .../src/managers/VoiceStateManager.js | 35 + .../discord.js/src/rest/APIRequest.js | 67 + node_modules/discord.js/src/rest/APIRouter.js | 53 + .../discord.js/src/rest/AsyncQueue.js | 95 + .../discord.js/src/rest/DiscordAPIError.js | 68 + node_modules/discord.js/src/rest/HTTPError.js | 37 + .../discord.js/src/rest/RESTManager.js | 59 + .../discord.js/src/rest/RequestHandler.js | 173 + node_modules/discord.js/src/sharding/Shard.js | 394 + .../src/sharding/ShardClientUtil.js | 229 + .../src/sharding/ShardingManager.js | 272 + .../discord.js/src/structures/APIMessage.js | 396 + .../discord.js/src/structures/Base.js | 43 + .../src/structures/BaseGuildEmoji.js | 66 + .../src/structures/CategoryChannel.js | 33 + .../discord.js/src/structures/Channel.js | 161 + .../src/structures/ClientApplication.js | 46 + .../src/structures/ClientPresence.js | 87 + .../discord.js/src/structures/ClientUser.js | 178 + .../discord.js/src/structures/DMChannel.js | 99 + .../discord.js/src/structures/Emoji.js | 104 + .../discord.js/src/structures/Guild.js | 1558 ++++ .../src/structures/GuildAuditLogs.js | 509 + .../discord.js/src/structures/GuildChannel.js | 624 ++ .../discord.js/src/structures/GuildEmoji.js | 168 + .../discord.js/src/structures/GuildMember.js | 414 + .../discord.js/src/structures/GuildPreview.js | 157 + .../src/structures/GuildPreviewEmoji.js | 26 + .../discord.js/src/structures/Integration.js | 186 + .../src/structures/IntegrationApplication.js | 25 + .../discord.js/src/structures/Invite.js | 194 + .../discord.js/src/structures/Message.js | 683 ++ .../src/structures/MessageAttachment.js | 98 + .../src/structures/MessageCollector.js | 129 + .../discord.js/src/structures/MessageEmbed.js | 461 + .../src/structures/MessageMentions.js | 225 + .../src/structures/MessageReaction.js | 136 + .../discord.js/src/structures/NewsChannel.js | 38 + .../src/structures/PartialGroupDMChannel.js | 46 + .../src/structures/PermissionOverwrites.js | 189 + .../discord.js/src/structures/Presence.js | 336 + .../src/structures/ReactionCollector.js | 188 + .../src/structures/ReactionEmoji.js | 31 + .../discord.js/src/structures/Role.js | 403 + .../discord.js/src/structures/StoreChannel.js | 32 + .../discord.js/src/structures/Team.js | 109 + .../discord.js/src/structures/TeamMember.js | 65 + .../discord.js/src/structures/TextChannel.js | 153 + .../discord.js/src/structures/User.js | 341 + .../discord.js/src/structures/VoiceChannel.js | 150 + .../discord.js/src/structures/VoiceRegion.js | 52 + .../discord.js/src/structures/VoiceState.js | 213 + .../discord.js/src/structures/Webhook.js | 273 + .../src/structures/interfaces/Application.js | 125 + .../src/structures/interfaces/Collector.js | 281 + .../structures/interfaces/TextBasedChannel.js | 393 + .../discord.js/src/util/ActivityFlags.js | 38 + node_modules/discord.js/src/util/BitField.js | 164 + .../discord.js/src/util/Collection.js | 17 + node_modules/discord.js/src/util/Constants.js | 654 ++ .../discord.js/src/util/DataResolver.js | 129 + node_modules/discord.js/src/util/Intents.js | 83 + .../discord.js/src/util/LimitedCollection.js | 34 + .../discord.js/src/util/MessageFlags.js | 36 + .../discord.js/src/util/Permissions.js | 131 + node_modules/discord.js/src/util/Snowflake.js | 84 + node_modules/discord.js/src/util/Speaking.js | 33 + .../discord.js/src/util/Structures.js | 112 + .../discord.js/src/util/SystemChannelFlags.js | 40 + node_modules/discord.js/src/util/UserFlags.js | 55 + node_modules/discord.js/src/util/Util.js | 625 ++ node_modules/discord.js/typings/index.d.ts | 3245 +++++++ node_modules/discord.js/typings/index.js | 40 + node_modules/discord.js/typings/index.ts | 53 + node_modules/discord.js/webpack/discord.js | 2 + .../discord.js/webpack/discord.js.LICENSE.txt | 837 ++ .../discord.js/webpack/discord.min.js | 2 + .../webpack/discord.min.js.LICENSE.txt | 6 + node_modules/event-target-shim/LICENSE | 22 + node_modules/event-target-shim/README.md | 293 + .../dist/event-target-shim.js | 871 ++ .../dist/event-target-shim.js.map | 1 + .../dist/event-target-shim.mjs | 862 ++ .../dist/event-target-shim.mjs.map | 1 + .../dist/event-target-shim.umd.js | 6 + .../dist/event-target-shim.umd.js.map | 1 + node_modules/event-target-shim/index.d.ts | 399 + node_modules/event-target-shim/package.json | 109 + node_modules/mime-db/HISTORY.md | 446 + node_modules/mime-db/LICENSE | 22 + node_modules/mime-db/README.md | 102 + node_modules/mime-db/db.json | 8176 +++++++++++++++++ node_modules/mime-db/index.js | 11 + node_modules/mime-db/package.json | 102 + node_modules/mime-types/HISTORY.md | 333 + node_modules/mime-types/LICENSE | 23 + node_modules/mime-types/README.md | 123 + node_modules/mime-types/index.js | 188 + node_modules/mime-types/package.json | 87 + node_modules/node-fetch/CHANGELOG.md | 272 + node_modules/node-fetch/LICENSE.md | 22 + node_modules/node-fetch/README.md | 590 ++ node_modules/node-fetch/browser.js | 25 + node_modules/node-fetch/lib/index.es.js | 1640 ++++ node_modules/node-fetch/lib/index.js | 1649 ++++ node_modules/node-fetch/lib/index.mjs | 1638 ++++ node_modules/node-fetch/package.json | 93 + node_modules/prism-media/LICENSE | 190 + node_modules/prism-media/README.md | 69 + node_modules/prism-media/package.json | 91 + node_modules/prism-media/src/core/FFmpeg.js | 147 + .../prism-media/src/core/VolumeTransformer.js | 129 + node_modules/prism-media/src/core/WebmBase.js | 199 + node_modules/prism-media/src/core/index.js | 9 + node_modules/prism-media/src/index.js | 5 + .../prism-media/src/opus/OggDemuxer.js | 118 + node_modules/prism-media/src/opus/Opus.js | 194 + .../prism-media/src/opus/WebmDemuxer.js | 24 + node_modules/prism-media/src/opus/index.js | 10 + .../prism-media/src/util/Constants.js | 0 node_modules/prism-media/src/util/loader.js | 13 + .../prism-media/src/vorbis/WebmDemuxer.js | 22 + node_modules/prism-media/src/vorbis/index.js | 8 + node_modules/prism-media/typings/index.d.ts | 41 + node_modules/prism-media/typings/opus.d.ts | 30 + node_modules/prism-media/typings/vorbis.d.ts | 5 + node_modules/setimmediate/LICENSE.txt | 20 + node_modules/setimmediate/package.json | 74 + node_modules/setimmediate/setImmediate.js | 186 + node_modules/tweetnacl/AUTHORS.md | 27 + node_modules/tweetnacl/CHANGELOG.md | 283 + node_modules/tweetnacl/LICENSE | 24 + .../tweetnacl/PULL_REQUEST_TEMPLATE.md | 20 + node_modules/tweetnacl/README.md | 494 + node_modules/tweetnacl/nacl-fast.js | 2391 +++++ node_modules/tweetnacl/nacl-fast.min.js | 1 + node_modules/tweetnacl/nacl.d.ts | 98 + node_modules/tweetnacl/nacl.js | 1178 +++ node_modules/tweetnacl/nacl.min.js | 1 + node_modules/tweetnacl/package.json | 83 + node_modules/ws/LICENSE | 21 + node_modules/ws/README.md | 496 + node_modules/ws/browser.js | 8 + node_modules/ws/index.js | 10 + node_modules/ws/lib/buffer-util.js | 129 + node_modules/ws/lib/constants.js | 10 + node_modules/ws/lib/event-target.js | 177 + node_modules/ws/lib/extension.js | 223 + node_modules/ws/lib/limiter.js | 54 + node_modules/ws/lib/permessage-deflate.js | 512 ++ node_modules/ws/lib/receiver.js | 507 + node_modules/ws/lib/sender.js | 392 + node_modules/ws/lib/stream.js | 164 + node_modules/ws/lib/validation.js | 30 + node_modules/ws/lib/websocket-server.js | 404 + node_modules/ws/lib/websocket.js | 905 ++ node_modules/ws/package.json | 91 + package-lock.json | 107 + package.json | 14 + 335 files changed, 59640 insertions(+) create mode 100644 Procfile create mode 100644 main.js create mode 100644 node_modules/@discordjs/collection/LICENSE create mode 100644 node_modules/@discordjs/collection/README.md create mode 100644 node_modules/@discordjs/collection/dist/index.d.ts create mode 100644 node_modules/@discordjs/collection/dist/index.js create mode 100644 node_modules/@discordjs/collection/package.json create mode 100644 node_modules/@discordjs/form-data/License create mode 100644 node_modules/@discordjs/form-data/Readme.md create mode 100644 node_modules/@discordjs/form-data/index.d.ts create mode 100644 node_modules/@discordjs/form-data/lib/browser.js create mode 100644 node_modules/@discordjs/form-data/lib/form_data.js create mode 100644 node_modules/@discordjs/form-data/lib/populate.js create mode 100644 node_modules/@discordjs/form-data/package.json create mode 100644 node_modules/abort-controller/LICENSE create mode 100644 node_modules/abort-controller/README.md create mode 100644 node_modules/abort-controller/browser.js create mode 100644 node_modules/abort-controller/browser.mjs create mode 100644 node_modules/abort-controller/dist/abort-controller.d.ts create mode 100644 node_modules/abort-controller/dist/abort-controller.js create mode 100644 node_modules/abort-controller/dist/abort-controller.js.map create mode 100644 node_modules/abort-controller/dist/abort-controller.mjs create mode 100644 node_modules/abort-controller/dist/abort-controller.mjs.map create mode 100644 node_modules/abort-controller/dist/abort-controller.umd.js create mode 100644 node_modules/abort-controller/dist/abort-controller.umd.js.map create mode 100644 node_modules/abort-controller/package.json create mode 100644 node_modules/abort-controller/polyfill.js create mode 100644 node_modules/abort-controller/polyfill.mjs create mode 100644 node_modules/asynckit/LICENSE create mode 100644 node_modules/asynckit/README.md create mode 100644 node_modules/asynckit/bench.js create mode 100644 node_modules/asynckit/index.js create mode 100644 node_modules/asynckit/lib/abort.js create mode 100644 node_modules/asynckit/lib/async.js create mode 100644 node_modules/asynckit/lib/defer.js create mode 100644 node_modules/asynckit/lib/iterate.js create mode 100644 node_modules/asynckit/lib/readable_asynckit.js create mode 100644 node_modules/asynckit/lib/readable_parallel.js create mode 100644 node_modules/asynckit/lib/readable_serial.js create mode 100644 node_modules/asynckit/lib/readable_serial_ordered.js create mode 100644 node_modules/asynckit/lib/state.js create mode 100644 node_modules/asynckit/lib/streamify.js create mode 100644 node_modules/asynckit/lib/terminator.js create mode 100644 node_modules/asynckit/package.json create mode 100644 node_modules/asynckit/parallel.js create mode 100644 node_modules/asynckit/serial.js create mode 100644 node_modules/asynckit/serialOrdered.js create mode 100644 node_modules/asynckit/stream.js create mode 100644 node_modules/combined-stream/License create mode 100644 node_modules/combined-stream/Readme.md create mode 100644 node_modules/combined-stream/lib/combined_stream.js create mode 100644 node_modules/combined-stream/package.json create mode 100644 node_modules/combined-stream/yarn.lock create mode 100644 node_modules/delayed-stream/.npmignore create mode 100644 node_modules/delayed-stream/License create mode 100644 node_modules/delayed-stream/Makefile create mode 100644 node_modules/delayed-stream/Readme.md create mode 100644 node_modules/delayed-stream/lib/delayed_stream.js create mode 100644 node_modules/delayed-stream/package.json create mode 100644 node_modules/discord.js/.tern-project create mode 100644 node_modules/discord.js/LICENSE create mode 100644 node_modules/discord.js/README.md create mode 100644 node_modules/discord.js/esm/discord.mjs create mode 100644 node_modules/discord.js/jsdoc.json create mode 100644 node_modules/discord.js/package.json create mode 100644 node_modules/discord.js/src/WebSocket.js create mode 100644 node_modules/discord.js/src/client/BaseClient.js create mode 100644 node_modules/discord.js/src/client/Client.js create mode 100644 node_modules/discord.js/src/client/WebhookClient.js create mode 100644 node_modules/discord.js/src/client/actions/Action.js create mode 100644 node_modules/discord.js/src/client/actions/ActionsManager.js create mode 100644 node_modules/discord.js/src/client/actions/ChannelCreate.js create mode 100644 node_modules/discord.js/src/client/actions/ChannelDelete.js create mode 100644 node_modules/discord.js/src/client/actions/ChannelUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildBanRemove.js create mode 100644 node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildDelete.js create mode 100644 node_modules/discord.js/src/client/actions/GuildEmojiCreate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildEmojiDelete.js create mode 100644 node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildIntegrationsUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildMemberRemove.js create mode 100644 node_modules/discord.js/src/client/actions/GuildRoleCreate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildRoleDelete.js create mode 100644 node_modules/discord.js/src/client/actions/GuildRoleUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/GuildUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/InviteCreate.js create mode 100644 node_modules/discord.js/src/client/actions/InviteDelete.js create mode 100644 node_modules/discord.js/src/client/actions/MessageCreate.js create mode 100644 node_modules/discord.js/src/client/actions/MessageDelete.js create mode 100644 node_modules/discord.js/src/client/actions/MessageDeleteBulk.js create mode 100644 node_modules/discord.js/src/client/actions/MessageReactionAdd.js create mode 100644 node_modules/discord.js/src/client/actions/MessageReactionRemove.js create mode 100644 node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js create mode 100644 node_modules/discord.js/src/client/actions/MessageReactionRemoveEmoji.js create mode 100644 node_modules/discord.js/src/client/actions/MessageUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/PresenceUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/TypingStart.js create mode 100644 node_modules/discord.js/src/client/actions/UserUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/VoiceStateUpdate.js create mode 100644 node_modules/discord.js/src/client/actions/WebhooksUpdate.js create mode 100644 node_modules/discord.js/src/client/voice/ClientVoiceManager.js create mode 100644 node_modules/discord.js/src/client/voice/VoiceBroadcast.js create mode 100644 node_modules/discord.js/src/client/voice/VoiceConnection.js create mode 100644 node_modules/discord.js/src/client/voice/dispatcher/BroadcastDispatcher.js create mode 100644 node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js create mode 100644 node_modules/discord.js/src/client/voice/networking/VoiceUDPClient.js create mode 100644 node_modules/discord.js/src/client/voice/networking/VoiceWebSocket.js create mode 100644 node_modules/discord.js/src/client/voice/player/AudioPlayer.js create mode 100644 node_modules/discord.js/src/client/voice/player/BasePlayer.js create mode 100644 node_modules/discord.js/src/client/voice/player/BroadcastAudioPlayer.js create mode 100644 node_modules/discord.js/src/client/voice/receiver/PacketHandler.js create mode 100644 node_modules/discord.js/src/client/voice/receiver/Receiver.js create mode 100644 node_modules/discord.js/src/client/voice/util/PlayInterface.js create mode 100644 node_modules/discord.js/src/client/voice/util/Secretbox.js create mode 100644 node_modules/discord.js/src/client/voice/util/Silence.js create mode 100644 node_modules/discord.js/src/client/voice/util/VolumeInterface.js create mode 100644 node_modules/discord.js/src/client/websocket/WebSocketManager.js create mode 100644 node_modules/discord.js/src/client/websocket/WebSocketShard.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/CHANNEL_CREATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/CHANNEL_DELETE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/CHANNEL_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_ADD.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_REMOVE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_CREATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_DELETE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_ADD.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_CREATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_DELETE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/GUILD_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/INVITE_CREATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/INVITE_DELETE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_CREATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/MESSAGE_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/PRESENCE_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/READY.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/RESUMED.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/TYPING_START.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/USER_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/VOICE_STATE_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/WEBHOOKS_UPDATE.js create mode 100644 node_modules/discord.js/src/client/websocket/handlers/index.js create mode 100644 node_modules/discord.js/src/errors/DJSError.js create mode 100644 node_modules/discord.js/src/errors/Messages.js create mode 100644 node_modules/discord.js/src/errors/index.js create mode 100644 node_modules/discord.js/src/index.js create mode 100644 node_modules/discord.js/src/managers/BaseManager.js create mode 100644 node_modules/discord.js/src/managers/ChannelManager.js create mode 100644 node_modules/discord.js/src/managers/GuildChannelManager.js create mode 100644 node_modules/discord.js/src/managers/GuildEmojiManager.js create mode 100644 node_modules/discord.js/src/managers/GuildEmojiRoleManager.js create mode 100644 node_modules/discord.js/src/managers/GuildManager.js create mode 100644 node_modules/discord.js/src/managers/GuildMemberManager.js create mode 100644 node_modules/discord.js/src/managers/GuildMemberRoleManager.js create mode 100644 node_modules/discord.js/src/managers/MessageManager.js create mode 100644 node_modules/discord.js/src/managers/PresenceManager.js create mode 100644 node_modules/discord.js/src/managers/ReactionManager.js create mode 100644 node_modules/discord.js/src/managers/ReactionUserManager.js create mode 100644 node_modules/discord.js/src/managers/RoleManager.js create mode 100644 node_modules/discord.js/src/managers/UserManager.js create mode 100644 node_modules/discord.js/src/managers/VoiceStateManager.js create mode 100644 node_modules/discord.js/src/rest/APIRequest.js create mode 100644 node_modules/discord.js/src/rest/APIRouter.js create mode 100644 node_modules/discord.js/src/rest/AsyncQueue.js create mode 100644 node_modules/discord.js/src/rest/DiscordAPIError.js create mode 100644 node_modules/discord.js/src/rest/HTTPError.js create mode 100644 node_modules/discord.js/src/rest/RESTManager.js create mode 100644 node_modules/discord.js/src/rest/RequestHandler.js create mode 100644 node_modules/discord.js/src/sharding/Shard.js create mode 100644 node_modules/discord.js/src/sharding/ShardClientUtil.js create mode 100644 node_modules/discord.js/src/sharding/ShardingManager.js create mode 100644 node_modules/discord.js/src/structures/APIMessage.js create mode 100644 node_modules/discord.js/src/structures/Base.js create mode 100644 node_modules/discord.js/src/structures/BaseGuildEmoji.js create mode 100644 node_modules/discord.js/src/structures/CategoryChannel.js create mode 100644 node_modules/discord.js/src/structures/Channel.js create mode 100644 node_modules/discord.js/src/structures/ClientApplication.js create mode 100644 node_modules/discord.js/src/structures/ClientPresence.js create mode 100644 node_modules/discord.js/src/structures/ClientUser.js create mode 100644 node_modules/discord.js/src/structures/DMChannel.js create mode 100644 node_modules/discord.js/src/structures/Emoji.js create mode 100644 node_modules/discord.js/src/structures/Guild.js create mode 100644 node_modules/discord.js/src/structures/GuildAuditLogs.js create mode 100644 node_modules/discord.js/src/structures/GuildChannel.js create mode 100644 node_modules/discord.js/src/structures/GuildEmoji.js create mode 100644 node_modules/discord.js/src/structures/GuildMember.js create mode 100644 node_modules/discord.js/src/structures/GuildPreview.js create mode 100644 node_modules/discord.js/src/structures/GuildPreviewEmoji.js create mode 100644 node_modules/discord.js/src/structures/Integration.js create mode 100644 node_modules/discord.js/src/structures/IntegrationApplication.js create mode 100644 node_modules/discord.js/src/structures/Invite.js create mode 100644 node_modules/discord.js/src/structures/Message.js create mode 100644 node_modules/discord.js/src/structures/MessageAttachment.js create mode 100644 node_modules/discord.js/src/structures/MessageCollector.js create mode 100644 node_modules/discord.js/src/structures/MessageEmbed.js create mode 100644 node_modules/discord.js/src/structures/MessageMentions.js create mode 100644 node_modules/discord.js/src/structures/MessageReaction.js create mode 100644 node_modules/discord.js/src/structures/NewsChannel.js create mode 100644 node_modules/discord.js/src/structures/PartialGroupDMChannel.js create mode 100644 node_modules/discord.js/src/structures/PermissionOverwrites.js create mode 100644 node_modules/discord.js/src/structures/Presence.js create mode 100644 node_modules/discord.js/src/structures/ReactionCollector.js create mode 100644 node_modules/discord.js/src/structures/ReactionEmoji.js create mode 100644 node_modules/discord.js/src/structures/Role.js create mode 100644 node_modules/discord.js/src/structures/StoreChannel.js create mode 100644 node_modules/discord.js/src/structures/Team.js create mode 100644 node_modules/discord.js/src/structures/TeamMember.js create mode 100644 node_modules/discord.js/src/structures/TextChannel.js create mode 100644 node_modules/discord.js/src/structures/User.js create mode 100644 node_modules/discord.js/src/structures/VoiceChannel.js create mode 100644 node_modules/discord.js/src/structures/VoiceRegion.js create mode 100644 node_modules/discord.js/src/structures/VoiceState.js create mode 100644 node_modules/discord.js/src/structures/Webhook.js create mode 100644 node_modules/discord.js/src/structures/interfaces/Application.js create mode 100644 node_modules/discord.js/src/structures/interfaces/Collector.js create mode 100644 node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js create mode 100644 node_modules/discord.js/src/util/ActivityFlags.js create mode 100644 node_modules/discord.js/src/util/BitField.js create mode 100644 node_modules/discord.js/src/util/Collection.js create mode 100644 node_modules/discord.js/src/util/Constants.js create mode 100644 node_modules/discord.js/src/util/DataResolver.js create mode 100644 node_modules/discord.js/src/util/Intents.js create mode 100644 node_modules/discord.js/src/util/LimitedCollection.js create mode 100644 node_modules/discord.js/src/util/MessageFlags.js create mode 100644 node_modules/discord.js/src/util/Permissions.js create mode 100644 node_modules/discord.js/src/util/Snowflake.js create mode 100644 node_modules/discord.js/src/util/Speaking.js create mode 100644 node_modules/discord.js/src/util/Structures.js create mode 100644 node_modules/discord.js/src/util/SystemChannelFlags.js create mode 100644 node_modules/discord.js/src/util/UserFlags.js create mode 100644 node_modules/discord.js/src/util/Util.js create mode 100644 node_modules/discord.js/typings/index.d.ts create mode 100644 node_modules/discord.js/typings/index.js create mode 100644 node_modules/discord.js/typings/index.ts create mode 100644 node_modules/discord.js/webpack/discord.js create mode 100644 node_modules/discord.js/webpack/discord.js.LICENSE.txt create mode 100644 node_modules/discord.js/webpack/discord.min.js create mode 100644 node_modules/discord.js/webpack/discord.min.js.LICENSE.txt create mode 100644 node_modules/event-target-shim/LICENSE create mode 100644 node_modules/event-target-shim/README.md create mode 100644 node_modules/event-target-shim/dist/event-target-shim.js create mode 100644 node_modules/event-target-shim/dist/event-target-shim.js.map create mode 100644 node_modules/event-target-shim/dist/event-target-shim.mjs create mode 100644 node_modules/event-target-shim/dist/event-target-shim.mjs.map create mode 100644 node_modules/event-target-shim/dist/event-target-shim.umd.js create mode 100644 node_modules/event-target-shim/dist/event-target-shim.umd.js.map create mode 100644 node_modules/event-target-shim/index.d.ts create mode 100644 node_modules/event-target-shim/package.json create mode 100644 node_modules/mime-db/HISTORY.md create mode 100644 node_modules/mime-db/LICENSE create mode 100644 node_modules/mime-db/README.md create mode 100644 node_modules/mime-db/db.json create mode 100644 node_modules/mime-db/index.js create mode 100644 node_modules/mime-db/package.json create mode 100644 node_modules/mime-types/HISTORY.md create mode 100644 node_modules/mime-types/LICENSE create mode 100644 node_modules/mime-types/README.md create mode 100644 node_modules/mime-types/index.js create mode 100644 node_modules/mime-types/package.json create mode 100644 node_modules/node-fetch/CHANGELOG.md create mode 100644 node_modules/node-fetch/LICENSE.md create mode 100644 node_modules/node-fetch/README.md create mode 100644 node_modules/node-fetch/browser.js create mode 100644 node_modules/node-fetch/lib/index.es.js create mode 100644 node_modules/node-fetch/lib/index.js create mode 100644 node_modules/node-fetch/lib/index.mjs create mode 100644 node_modules/node-fetch/package.json create mode 100644 node_modules/prism-media/LICENSE create mode 100644 node_modules/prism-media/README.md create mode 100644 node_modules/prism-media/package.json create mode 100644 node_modules/prism-media/src/core/FFmpeg.js create mode 100644 node_modules/prism-media/src/core/VolumeTransformer.js create mode 100644 node_modules/prism-media/src/core/WebmBase.js create mode 100644 node_modules/prism-media/src/core/index.js create mode 100644 node_modules/prism-media/src/index.js create mode 100644 node_modules/prism-media/src/opus/OggDemuxer.js create mode 100644 node_modules/prism-media/src/opus/Opus.js create mode 100644 node_modules/prism-media/src/opus/WebmDemuxer.js create mode 100644 node_modules/prism-media/src/opus/index.js create mode 100644 node_modules/prism-media/src/util/Constants.js create mode 100644 node_modules/prism-media/src/util/loader.js create mode 100644 node_modules/prism-media/src/vorbis/WebmDemuxer.js create mode 100644 node_modules/prism-media/src/vorbis/index.js create mode 100644 node_modules/prism-media/typings/index.d.ts create mode 100644 node_modules/prism-media/typings/opus.d.ts create mode 100644 node_modules/prism-media/typings/vorbis.d.ts create mode 100644 node_modules/setimmediate/LICENSE.txt create mode 100644 node_modules/setimmediate/package.json create mode 100644 node_modules/setimmediate/setImmediate.js create mode 100644 node_modules/tweetnacl/AUTHORS.md create mode 100644 node_modules/tweetnacl/CHANGELOG.md create mode 100644 node_modules/tweetnacl/LICENSE create mode 100644 node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md create mode 100644 node_modules/tweetnacl/README.md create mode 100644 node_modules/tweetnacl/nacl-fast.js create mode 100644 node_modules/tweetnacl/nacl-fast.min.js create mode 100644 node_modules/tweetnacl/nacl.d.ts create mode 100644 node_modules/tweetnacl/nacl.js create mode 100644 node_modules/tweetnacl/nacl.min.js create mode 100644 node_modules/tweetnacl/package.json create mode 100644 node_modules/ws/LICENSE create mode 100644 node_modules/ws/README.md create mode 100644 node_modules/ws/browser.js create mode 100644 node_modules/ws/index.js create mode 100644 node_modules/ws/lib/buffer-util.js create mode 100644 node_modules/ws/lib/constants.js create mode 100644 node_modules/ws/lib/event-target.js create mode 100644 node_modules/ws/lib/extension.js create mode 100644 node_modules/ws/lib/limiter.js create mode 100644 node_modules/ws/lib/permessage-deflate.js create mode 100644 node_modules/ws/lib/receiver.js create mode 100644 node_modules/ws/lib/sender.js create mode 100644 node_modules/ws/lib/stream.js create mode 100644 node_modules/ws/lib/validation.js create mode 100644 node_modules/ws/lib/websocket-server.js create mode 100644 node_modules/ws/lib/websocket.js create mode 100644 node_modules/ws/package.json create mode 100644 package-lock.json create mode 100644 package.json diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..7840110 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +Worker: node main.js \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..2f7b7ac --- /dev/null +++ b/main.js @@ -0,0 +1,72 @@ +const Discord = require('discord.js'); +const client = new Discord.Client(); +const prefix = '#'; +var author = " "; +var storedLink = " "; +var link = new Discord.MessageAttachment(); +var messageId; +var yes = false; + +client.once('ready', () => { + console.log('Im online!'); +}); + +client.on('message', message => { + + const args = message.content.split(' '); + + //storedMessages.push(message.content); + + + let msg = message.content.toLowerCase(); + + if(message.attachments.first() === undefined){ + } + + if (message.content.startsWith("http")){ + author = message.author.username; + storedLink = message.content; + messageId = message.id; + yes = true; + } + else if(message.attachments.first() != undefined){ + let storedMessage = message.attachments.first(); + link = new Discord.MessageAttachment(storedMessage.url); + messageId = message.id; + yes = true; + } + + if(msg.startsWith("henlo") && message.author.bot == false){ + message.channel.send("Henlooo " + message.author.username + " 🐄"); + } + else if (message.mentions.channels.first() && yes){ + if (args.length === 1){ + message.react('🐮'); + let channelId = ""; + let channelIdArray = message.content.split(''); + channelIdArray.splice(0, 2); + channelIdArray.pop(); + + + for (i = 0; i < channelIdArray.length; i++){ + channelId += channelIdArray[i]; + } + + message.delete({timeout: 5000}); + + var toDelete = message.channel.messages.cache.get(messageId); + toDelete.delete(); + + let getChannel = client.channels.cache.get(channelId); + getChannel.send(author + "\n" + storedLink); + } + + yes = false; + } + else if (msg.startsWith("how ye") && message.author.bot === false){ + message.channel.send("Not bad, how yee?"); + } + +}); + +client.login(process.env.token); \ No newline at end of file diff --git a/node_modules/@discordjs/collection/LICENSE b/node_modules/@discordjs/collection/LICENSE new file mode 100644 index 0000000..9997d13 --- /dev/null +++ b/node_modules/@discordjs/collection/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 2015 - 2020 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/@discordjs/collection/README.md b/node_modules/@discordjs/collection/README.md new file mode 100644 index 0000000..0e31ccd --- /dev/null +++ b/node_modules/@discordjs/collection/README.md @@ -0,0 +1,3 @@ +# Collection + +Utility data structure used in Discord.js. diff --git a/node_modules/@discordjs/collection/dist/index.d.ts b/node_modules/@discordjs/collection/dist/index.d.ts new file mode 100644 index 0000000..a332396 --- /dev/null +++ b/node_modules/@discordjs/collection/dist/index.d.ts @@ -0,0 +1,319 @@ +export interface CollectionConstructor { + new (): Collection; + new (entries?: ReadonlyArray | null): Collection; + new (iterable: Iterable): Collection; + readonly prototype: Collection; + readonly [Symbol.species]: CollectionConstructor; +} +/** + * 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} + * @property {number} size - The amount of elements in this collection. + */ +declare class Collection extends Map { + private _array; + private _keyArray; + static readonly default: typeof Collection; + ['constructor']: typeof Collection; + constructor(entries?: ReadonlyArray | null); + /** + * Identical to [Map.get()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get). + * Gets an element with the specified key, and returns its value, or `undefined` if the element does not exist. + * @param {*} key - The key to get from this collection + * @returns {* | undefined} + */ + get(key: K): V | undefined; + /** + * Identical to [Map.set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set). + * Sets a new element in the collection with the specified key and value. + * @param {*} key - The key of the element to add + * @param {*} value - The value of the element to add + * @returns {Collection} + */ + set(key: K, value: V): this; + /** + * Identical to [Map.has()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has). + * Checks if an element exists in the collection. + * @param {*} key - The key of the element to check for + * @returns {boolean} `true` if the element exists, `false` if it does not exist. + */ + has(key: K): boolean; + /** + * Identical to [Map.delete()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete). + * Deletes an element from the collection. + * @param {*} key - The key to delete from the collection + * @returns {boolean} `true` if the element was removed, `false` if the element does not exist. + */ + delete(key: K): boolean; + /** + * Identical to [Map.clear()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear). + * Removes all elements from the collection. + * @returns {undefined} + */ + clear(): void; + /** + * 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(): V[]; + /** + * 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(): K[]; + /** + * Obtains the first value(s) in this collection. + * @param {number} [amount] Amount of values to obtain from the beginning + * @returns {*|Array<*>} A single value if no amount is provided or an array of values, starting from the end if + * amount is negative + */ + first(): V | undefined; + first(amount: number): V[]; + /** + * Obtains the first key(s) in this collection. + * @param {number} [amount] Amount of keys to obtain from the beginning + * @returns {*|Array<*>} A single key if no amount is provided or an array of keys, starting from the end if + * amount is negative + */ + firstKey(): K | undefined; + firstKey(amount: number): K[]; + /** + * 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} [amount] Amount of values to obtain from the end + * @returns {*|Array<*>} A single value if no amount is provided or an array of values, starting from the start if + * amount is negative + */ + last(): V | undefined; + last(amount: number): V[]; + /** + * 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} [amount] Amount of keys to obtain from the end + * @returns {*|Array<*>} A single key if no amount is provided or an array of keys, starting from the start if + * amount is negative + */ + lastKey(): K | undefined; + lastKey(amount: number): K[]; + /** + * Obtains unique random value(s) from this collection. This relies on {@link Collection#array}, and thus the caching + * mechanism applies here as well. + * @param {number} [amount] Amount of values to obtain randomly + * @returns {*|Array<*>} A single value if no amount is provided or an array of values + */ + random(): V; + random(amount: number): V[]; + /** + * Obtains unique random key(s) from this collection. This relies on {@link Collection#keyArray}, and thus the caching + * mechanism applies here as well. + * @param {number} [amount] Amount of keys to obtain randomly + * @returns {*|Array<*>} A single key if no amount is provided or an array + */ + randomKey(): K; + randomKey(amount: number): K[]; + /** + * Searches for a single item where the given function returns a truthy value. This behaves like + * [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find). + * 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. + * @param {Function} fn The function to test with (should return boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {*} + * @example collection.find(user => user.username === 'Bob'); + */ + find(fn: (value: V, key: K, collection: this) => boolean): V | undefined; + find(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): V | undefined; + /** + * Searches for the key of a single item where the given function returns a truthy value. This behaves like + * [Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex), + * but returns the key rather than the positional index. + * @param {Function} fn The function to test with (should return boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {*} + * @example collection.findKey(user => user.username === 'Bob'); + */ + findKey(fn: (value: V, key: K, collection: this) => boolean): K | undefined; + findKey(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): K | undefined; + /** + * Removes items that satisfy the provided filter function. + * @param {Function} fn Function used to test (should return a boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {number} The number of removed entries + */ + sweep(fn: (value: V, key: K, collection: this) => boolean): number; + sweep(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): number; + /** + * 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 The function to test with (should return boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example collection.filter(user => user.username === 'Bob'); + */ + filter(fn: (value: V, key: K, collection: this) => boolean): this; + filter(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): this; + /** + * 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: (value: V, key: K, collection: this) => boolean): [this, this]; + partition(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): [this, this]; + /** + * Maps each item into a Collection, then joins the results into a single Collection. Identical in behavior to + * [Array.flatMap()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap). + * @param {Function} fn Function that produces a new Collection + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example collection.flatMap(guild => guild.members.cache); + */ + flatMap(fn: (value: V, key: K, collection: this) => Collection): Collection; + flatMap(fn: (this: This, value: V, key: K, collection: this) => Collection, thisArg: This): Collection; + /** + * Maps each item to another value into an array. Identical in behavior 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} + * @example collection.map(user => user.tag); + */ + map(fn: (value: V, key: K, collection: this) => T): T[]; + map(fn: (this: This, value: V, key: K, collection: this) => T, thisArg: This): T[]; + /** + * Maps each item to another value into a collection. Identical in behavior 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 collection, taking three arguments + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example collection.mapValues(user => user.tag); + */ + mapValues(fn: (value: V, key: K, collection: this) => T): Collection; + mapValues(fn: (this: This, value: V, key: K, collection: this) => T, thisArg: This): Collection; + /** + * Checks if there exists an item that passes a test. Identical in behavior 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 {*} [thisArg] Value to use as `this` when executing function + * @returns {boolean} + * @example collection.some(user => user.discriminator === '0000'); + */ + some(fn: (value: V, key: K, collection: this) => boolean): boolean; + some(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): boolean; + /** + * Checks if all items passes a test. Identical in behavior 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 {*} [thisArg] Value to use as `this` when executing function + * @returns {boolean} + * @example collection.every(user => !user.bot); + */ + every(fn: (value: V, key: K, collection: this) => boolean): boolean; + every(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): boolean; + /** + * Applies a function to produce a single value. Identical in behavior 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 {*} + * @example collection.reduce((acc, guild) => acc + guild.memberCount, 0); + */ + reduce(fn: (accumulator: T, value: V, key: K, collection: this) => T, initialValue?: T): T; + /** + * 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 + * .each(user => console.log(user.username)) + * .filter(user => user.bot) + * .each(user => console.log(user.username)); + */ + each(fn: (value: V, key: K, collection: this) => void): this; + each(fn: (this: T, value: V, key: K, collection: this) => void, thisArg: T): this; + /** + * Runs a function on the collection and returns the collection. + * @param {Function} fn Function to execute + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example + * collection + * .tap(coll => console.log(coll.size)) + * .filter(user => user.bot) + * .tap(coll => console.log(coll.size)) + */ + tap(fn: (collection: this) => void): this; + tap(fn: (this: T, collection: this) => void, thisArg: T): this; + /** + * Creates an identical shallow copy of this collection. + * @returns {Collection} + * @example const newColl = someColl.clone(); + */ + clone(): 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: Collection[]): this; + /** + * Checks if this collection shares identical items 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: Collection): boolean; + /** + * The sort method sorts the items of a collection in place and returns it. + * The sort is not necessarily stable in Node 10 or older. + * 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} + * @example collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + */ + sort(compareFunction?: (firstValue: V, secondValue: V, firstKey: K, secondKey: K) => number): this; + /** + * The intersect method returns a new structure containing items where the keys are present in both original structures. + * @param {Collection} other The other Collection to filter against + * @returns {Collection} + */ + intersect(other: Collection): Collection; + /** + * The difference method returns a new structure containing items where the key is present in one of the original structures but not the other. + * @param {Collection} other The other Collection to filter against + * @returns {Collection} + */ + difference(other: Collection): Collection; + /** + * The sorted method sorts the items of a collection and returns it. + * The sort is not necessarily stable in Node 10 or older. + * 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} + * @example collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + */ + sorted(compareFunction?: (firstValue: V, secondValue: V, firstKey: K, secondKey: K) => number): this; +} +export { Collection }; +export default Collection; diff --git a/node_modules/@discordjs/collection/dist/index.js b/node_modules/@discordjs/collection/dist/index.js new file mode 100644 index 0000000..8c47cfd --- /dev/null +++ b/node_modules/@discordjs/collection/dist/index.js @@ -0,0 +1,392 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Collection = void 0; +/** + * 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} + * @property {number} size - The amount of elements in this collection. + */ +class Collection extends Map { + constructor(entries) { + super(entries); + /** + * 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 }); + } + /** + * Identical to [Map.get()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get). + * Gets an element with the specified key, and returns its value, or `undefined` if the element does not exist. + * @param {*} key - The key to get from this collection + * @returns {* | undefined} + */ + get(key) { + return super.get(key); + } + /** + * Identical to [Map.set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set). + * Sets a new element in the collection with the specified key and value. + * @param {*} key - The key of the element to add + * @param {*} value - The value of the element to add + * @returns {Collection} + */ + set(key, value) { + this._array = null; + this._keyArray = null; + return super.set(key, value); + } + /** + * Identical to [Map.has()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has). + * Checks if an element exists in the collection. + * @param {*} key - The key of the element to check for + * @returns {boolean} `true` if the element exists, `false` if it does not exist. + */ + has(key) { + return super.has(key); + } + /** + * Identical to [Map.delete()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete). + * Deletes an element from the collection. + * @param {*} key - The key to delete from the collection + * @returns {boolean} `true` if the element was removed, `false` if the element does not exist. + */ + delete(key) { + this._array = null; + this._keyArray = null; + return super.delete(key); + } + /** + * Identical to [Map.clear()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear). + * Removes all elements from the collection. + * @returns {undefined} + */ + clear() { + return super.clear(); + } + /** + * 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; + } + first(amount) { + if (typeof amount === 'undefined') + return this.values().next().value; + if (amount < 0) + return this.last(amount * -1); + amount = Math.min(this.size, amount); + const iter = this.values(); + return Array.from({ length: amount }, () => iter.next().value); + } + firstKey(amount) { + if (typeof amount === 'undefined') + return this.keys().next().value; + if (amount < 0) + return this.lastKey(amount * -1); + amount = Math.min(this.size, amount); + const iter = this.keys(); + return Array.from({ length: amount }, () => iter.next().value); + } + last(amount) { + const arr = this.array(); + if (typeof amount === 'undefined') + return arr[arr.length - 1]; + if (amount < 0) + return this.first(amount * -1); + if (!amount) + return []; + return arr.slice(-amount); + } + lastKey(amount) { + const arr = this.keyArray(); + if (typeof amount === 'undefined') + return arr[arr.length - 1]; + if (amount < 0) + return this.firstKey(amount * -1); + if (!amount) + return []; + return arr.slice(-amount); + } + random(amount) { + let arr = this.array(); + if (typeof amount === 'undefined') + return arr[Math.floor(Math.random() * arr.length)]; + if (arr.length === 0 || !amount) + return []; + arr = arr.slice(); + return Array.from({ length: amount }, () => arr.splice(Math.floor(Math.random() * arr.length), 1)[0]); + } + randomKey(amount) { + let arr = this.keyArray(); + if (typeof amount === 'undefined') + return arr[Math.floor(Math.random() * arr.length)]; + if (arr.length === 0 || !amount) + return []; + arr = arr.slice(); + return Array.from({ length: amount }, () => arr.splice(Math.floor(Math.random() * arr.length), 1)[0]); + } + find(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) + return val; + } + return undefined; + } + findKey(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) + return key; + } + return undefined; + } + sweep(fn, thisArg) { + if (typeof thisArg !== 'undefined') + 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; + } + filter(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + const results = new this.constructor[Symbol.species](); + for (const [key, val] of this) { + if (fn(val, key, this)) + results.set(key, val); + } + return results; + } + partition(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + // TODO: consider removing the from the constructors after TS 3.7.0 is released, as it infers it + const results = [new this.constructor[Symbol.species](), new this.constructor[Symbol.species]()]; + for (const [key, val] of this) { + if (fn(val, key, this)) { + results[0].set(key, val); + } + else { + results[1].set(key, val); + } + } + return results; + } + flatMap(fn, thisArg) { + const collections = this.map(fn, thisArg); + return new this.constructor[Symbol.species]().concat(...collections); + } + map(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + const iter = this.entries(); + return Array.from({ length: this.size }, () => { + const [key, value] = iter.next().value; + return fn(value, key, this); + }); + } + mapValues(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + const coll = new this.constructor[Symbol.species](); + for (const [key, val] of this) + coll.set(key, fn(val, key, this)); + return coll; + } + some(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) + return true; + } + return false; + } + every(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (!fn(val, key, this)) + return false; + } + return true; + } + /** + * Applies a function to produce a single value. Identical in behavior 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 {*} + * @example collection.reduce((acc, guild) => acc + guild.memberCount, 0); + */ + reduce(fn, initialValue) { + let accumulator; + if (typeof initialValue !== 'undefined') { + accumulator = initialValue; + for (const [key, val] of this) + accumulator = fn(accumulator, val, key, this); + return accumulator; + } + let first = true; + for (const [key, val] of this) { + if (first) { + accumulator = val; + first = false; + continue; + } + accumulator = fn(accumulator, val, key, this); + } + // No items iterated. + if (first) { + throw new TypeError('Reduce of empty collection with no initial value'); + } + return accumulator; + } + each(fn, thisArg) { + this.forEach(fn, thisArg); + return this; + } + tap(fn, thisArg) { + if (typeof thisArg !== 'undefined') + fn = fn.bind(thisArg); + fn(this); + return this; + } + /** + * Creates an identical shallow copy of this collection. + * @returns {Collection} + * @example const newColl = someColl.clone(); + */ + clone() { + return new this.constructor[Symbol.species](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; + } + /** + * Checks if this collection shares identical items 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; + for (const [key, value] of this) { + if (!collection.has(key) || value !== collection.get(key)) { + return false; + } + } + return true; + } + /** + * The sort method sorts the items of a collection in place and returns it. + * The sort is not necessarily stable in Node 10 or older. + * 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} + * @example collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + */ + sort(compareFunction = (x, y) => Number(x > y) || Number(x === y) - 1) { + const entries = [...this.entries()]; + entries.sort((a, b) => compareFunction(a[1], b[1], a[0], b[0])); + // Perform clean-up + super.clear(); + this._array = null; + this._keyArray = null; + // Set the new entries + for (const [k, v] of entries) { + super.set(k, v); + } + return this; + } + /** + * The intersect method returns a new structure containing items where the keys are present in both original structures. + * @param {Collection} other The other Collection to filter against + * @returns {Collection} + */ + intersect(other) { + return other.filter((_, k) => this.has(k)); + } + /** + * The difference method returns a new structure containing items where the key is present in one of the original structures but not the other. + * @param {Collection} other The other Collection to filter against + * @returns {Collection} + */ + difference(other) { + return other.filter((_, k) => !this.has(k)).concat(this.filter((_, k) => !other.has(k))); + } + /** + * The sorted method sorts the items of a collection and returns it. + * The sort is not necessarily stable in Node 10 or older. + * 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} + * @example collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + */ + sorted(compareFunction = (x, y) => Number(x > y) || Number(x === y) - 1) { + return new this.constructor[Symbol.species]([...this.entries()]) + .sort((av, bv, ak, bk) => compareFunction(av, bv, ak, bk)); + } +} +exports.Collection = Collection; +Collection.default = Collection; +module.exports = Collection; +exports.default = Collection; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiLyIsInNvdXJjZXMiOlsiaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBUUE7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQWlCLFNBQVEsR0FBUztJQU12QyxZQUFtQixPQUErQztRQUNqRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFZjs7Ozs7V0FLRztRQUNILE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUUzRjs7Ozs7V0FLRztRQUNILE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLFdBQVcsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvRixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxHQUFHLENBQUMsR0FBTTtRQUNoQixPQUFPLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEdBQUcsQ0FBQyxHQUFNLEVBQUUsS0FBUTtRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztRQUN0QixPQUFPLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEdBQUcsQ0FBQyxHQUFNO1FBQ2hCLE9BQU8sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN2QixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxNQUFNLENBQUMsR0FBTTtRQUNuQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztRQUN0QixPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLO1FBQ1gsT0FBTyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEtBQUs7UUFDWCxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxJQUFJLENBQUMsSUFBSTtZQUFFLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZGLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNwQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksUUFBUTtRQUNkLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxJQUFJO1lBQUUsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDOUYsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3ZCLENBQUM7SUFVTSxLQUFLLENBQUMsTUFBZTtRQUMzQixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDckUsSUFBSSxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxNQUFNLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUMzQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFVTSxRQUFRLENBQUMsTUFBZTtRQUM5QixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDbkUsSUFBSSxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNqRCxNQUFNLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN6QixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFXTSxJQUFJLENBQUMsTUFBZTtRQUMxQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDekIsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXO1lBQUUsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztRQUM5RCxJQUFJLE1BQU0sR0FBRyxDQUFDO1lBQUUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9DLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDdkIsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDM0IsQ0FBQztJQVdNLE9BQU8sQ0FBQyxNQUFlO1FBQzdCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUM1QixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQzlELElBQUksTUFBTSxHQUFHLENBQUM7WUFBRSxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEVBQUUsQ0FBQztRQUN2QixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzQixDQUFDO0lBVU0sTUFBTSxDQUFDLE1BQWU7UUFDNUIsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3ZCLElBQUksT0FBTyxNQUFNLEtBQUssV0FBVztZQUFFLE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3RGLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDM0MsR0FBRyxHQUFHLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNsQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRyxDQUFDO0lBVU0sU0FBUyxDQUFDLE1BQWU7UUFDL0IsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzFCLElBQUksT0FBTyxNQUFNLEtBQUssV0FBVztZQUFFLE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3RGLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDM0MsR0FBRyxHQUFHLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNsQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRyxDQUFDO0lBZU0sSUFBSSxDQUFDLEVBQW1ELEVBQUUsT0FBaUI7UUFDakYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRTtZQUM5QixJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxPQUFPLEdBQUcsQ0FBQztTQUNuQztRQUNELE9BQU8sU0FBUyxDQUFDO0lBQ2xCLENBQUM7SUFhTSxPQUFPLENBQUMsRUFBbUQsRUFBRSxPQUFpQjtRQUNwRixJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVc7WUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMxRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQzlCLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDO2dCQUFFLE9BQU8sR0FBRyxDQUFDO1NBQ25DO1FBQ0QsT0FBTyxTQUFTLENBQUM7SUFDbEIsQ0FBQztJQVVNLEtBQUssQ0FBQyxFQUFtRCxFQUFFLE9BQWlCO1FBQ2xGLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDL0IsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRTtZQUM5QixJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3pDO1FBQ0QsT0FBTyxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQztJQUNqQyxDQUFDO0lBYU0sTUFBTSxDQUFDLEVBQW1ELEVBQUUsT0FBaUI7UUFDbkYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBZ0IsQ0FBQztRQUNyRSxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQzlCLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDO2dCQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1NBQzlDO1FBQ0QsT0FBTyxPQUFPLENBQUM7SUFDaEIsQ0FBQztJQVlNLFNBQVMsQ0FBQyxFQUFtRCxFQUFFLE9BQWlCO1FBQ3RGLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELHVHQUF1RztRQUN2RyxNQUFNLE9BQU8sR0FBaUIsQ0FBQyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFnQixFQUFFLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQWdCLENBQUMsQ0FBQztRQUMzSSxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQzlCLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLEVBQUU7Z0JBQ3ZCLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQ3pCO2lCQUFNO2dCQUNOLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQ3pCO1NBQ0Q7UUFDRCxPQUFPLE9BQU8sQ0FBQztJQUNoQixDQUFDO0lBWU0sT0FBTyxDQUFJLEVBQTRELEVBQUUsT0FBaUI7UUFDaEcsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDMUMsT0FBUSxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUE2QixDQUFDLE1BQU0sQ0FBQyxHQUFHLFdBQVcsQ0FBQyxDQUFDO0lBQ2xHLENBQUM7SUFZTSxHQUFHLENBQUksRUFBNkMsRUFBRSxPQUFpQjtRQUM3RSxJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVc7WUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMxRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDNUIsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFNLEVBQUU7WUFDaEQsTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDO1lBQ3ZDLE9BQU8sRUFBRSxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDN0IsQ0FBQyxDQUFDLENBQUM7SUFDSixDQUFDO0lBWU0sU0FBUyxDQUFJLEVBQTZDLEVBQUUsT0FBaUI7UUFDbkYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBNEIsQ0FBQztRQUM5RSxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSTtZQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDakUsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBWU0sSUFBSSxDQUFDLEVBQW1ELEVBQUUsT0FBaUI7UUFDakYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRTtZQUM5QixJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxPQUFPLElBQUksQ0FBQztTQUNwQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQVlNLEtBQUssQ0FBQyxFQUFtRCxFQUFFLE9BQWlCO1FBQ2xGLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUU7WUFDOUIsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxPQUFPLEtBQUssQ0FBQztTQUN0QztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksTUFBTSxDQUFJLEVBQTZELEVBQUUsWUFBZ0I7UUFDL0YsSUFBSSxXQUFlLENBQUM7UUFFcEIsSUFBSSxPQUFPLFlBQVksS0FBSyxXQUFXLEVBQUU7WUFDeEMsV0FBVyxHQUFHLFlBQVksQ0FBQztZQUMzQixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSTtnQkFBRSxXQUFXLEdBQUcsRUFBRSxDQUFDLFdBQVcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzdFLE9BQU8sV0FBVyxDQUFDO1NBQ25CO1FBQ0QsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUU7WUFDOUIsSUFBSSxLQUFLLEVBQUU7Z0JBQ1YsV0FBVyxHQUFHLEdBQW1CLENBQUM7Z0JBQ2xDLEtBQUssR0FBRyxLQUFLLENBQUM7Z0JBQ2QsU0FBUzthQUNUO1lBQ0QsV0FBVyxHQUFHLEVBQUUsQ0FBQyxXQUFXLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztTQUM5QztRQUVELHFCQUFxQjtRQUNyQixJQUFJLEtBQUssRUFBRTtZQUNWLE1BQU0sSUFBSSxTQUFTLENBQUMsa0RBQWtELENBQUMsQ0FBQztTQUN4RTtRQUVELE9BQU8sV0FBVyxDQUFDO0lBQ3BCLENBQUM7SUFpQk0sSUFBSSxDQUFDLEVBQWdELEVBQUUsT0FBaUI7UUFDOUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFnRCxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3hFLE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQWVNLEdBQUcsQ0FBQyxFQUE4QixFQUFFLE9BQWlCO1FBQzNELElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNULE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLO1FBQ1gsT0FBTyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBUyxDQUFDO0lBQzNELENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLE1BQU0sQ0FBQyxHQUFHLFdBQStCO1FBQy9DLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixLQUFLLE1BQU0sSUFBSSxJQUFJLFdBQVcsRUFBRTtZQUMvQixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSTtnQkFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztTQUNyRDtRQUNELE9BQU8sT0FBTyxDQUFDO0lBQ2hCLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxNQUFNLENBQUMsVUFBNEI7UUFDekMsSUFBSSxDQUFDLFVBQVU7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUM5QixJQUFJLElBQUksS0FBSyxVQUFVO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDckMsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFVBQVUsQ0FBQyxJQUFJO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFDaEQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLElBQUksRUFBRTtZQUNoQyxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLEtBQUssVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRTtnQkFDMUQsT0FBTyxLQUFLLENBQUM7YUFDYjtTQUNEO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQ7Ozs7Ozs7OztPQVNHO0lBQ0ksSUFBSSxDQUFDLGtCQUF3RixDQUFDLENBQUMsRUFBRSxDQUFDLEVBQVUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQ3pKLE1BQU0sT0FBTyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNwQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBVSxFQUFFLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFeEUsbUJBQW1CO1FBQ25CLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1FBQ25CLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBRXRCLHNCQUFzQjtRQUN0QixLQUFLLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksT0FBTyxFQUFFO1lBQzdCLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1NBQ2hCO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFNBQVMsQ0FBQyxLQUF1QjtRQUN2QyxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxVQUFVLENBQUMsS0FBdUI7UUFDeEMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFGLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSSxNQUFNLENBQUMsa0JBQXdGLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBVSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDM0osT0FBUSxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBVTthQUN4RSxJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzdELENBQUM7O0FBSU8sZ0NBQVU7QUFwakJLLGtCQUFPLEdBQXNCLFVBQVUsQ0FBQztBQW1qQmhFLE1BQU0sQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDO0FBRTVCLGtCQUFlLFVBQVUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBpbnRlcmZhY2UgQ29sbGVjdGlvbkNvbnN0cnVjdG9yIHtcblx0bmV3KCk6IENvbGxlY3Rpb248dW5rbm93biwgdW5rbm93bj47XG5cdG5ldzxLLCBWPihlbnRyaWVzPzogUmVhZG9ubHlBcnJheTxyZWFkb25seSBbSywgVl0+IHwgbnVsbCk6IENvbGxlY3Rpb248SywgVj47XG5cdG5ldzxLLCBWPihpdGVyYWJsZTogSXRlcmFibGU8cmVhZG9ubHkgW0ssIFZdPik6IENvbGxlY3Rpb248SywgVj47XG5cdHJlYWRvbmx5IHByb3RvdHlwZTogQ29sbGVjdGlvbjx1bmtub3duLCB1bmtub3duPjtcblx0cmVhZG9ubHkgW1N5bWJvbC5zcGVjaWVzXTogQ29sbGVjdGlvbkNvbnN0cnVjdG9yO1xufVxuXG4vKipcbiAqIEEgTWFwIHdpdGggYWRkaXRpb25hbCB1dGlsaXR5IG1ldGhvZHMuIFRoaXMgaXMgdXNlZCB0aHJvdWdob3V0IGRpc2NvcmQuanMgcmF0aGVyIHRoYW4gQXJyYXlzIGZvciBhbnl0aGluZyB0aGF0IGhhc1xuICogYW4gSUQsIGZvciBzaWduaWZpY2FudGx5IGltcHJvdmVkIHBlcmZvcm1hbmNlIGFuZCBlYXNlLW9mLXVzZS5cbiAqIEBleHRlbmRzIHtNYXB9XG4gKiBAcHJvcGVydHkge251bWJlcn0gc2l6ZSAtIFRoZSBhbW91bnQgb2YgZWxlbWVudHMgaW4gdGhpcyBjb2xsZWN0aW9uLlxuICovXG5jbGFzcyBDb2xsZWN0aW9uPEssIFY+IGV4dGVuZHMgTWFwPEssIFY+IHtcblx0cHJpdmF0ZSBfYXJyYXkhOiBWW10gfCBudWxsO1xuXHRwcml2YXRlIF9rZXlBcnJheSE6IEtbXSB8IG51bGw7XG5cdHB1YmxpYyBzdGF0aWMgcmVhZG9ubHkgZGVmYXVsdDogdHlwZW9mIENvbGxlY3Rpb24gPSBDb2xsZWN0aW9uO1xuXHRwdWJsaWMgWydjb25zdHJ1Y3RvciddOiB0eXBlb2YgQ29sbGVjdGlvbjtcblxuXHRwdWJsaWMgY29uc3RydWN0b3IoZW50cmllcz86IFJlYWRvbmx5QXJyYXk8cmVhZG9ubHkgW0ssIFZdPiB8IG51bGwpIHtcblx0XHRzdXBlcihlbnRyaWVzKTtcblxuXHRcdC8qKlxuXHRcdCAqIENhY2hlZCBhcnJheSBmb3IgdGhlIGBhcnJheSgpYCBtZXRob2QgLSB3aWxsIGJlIHJlc2V0IHRvIGBudWxsYCB3aGVuZXZlciBgc2V0KClgIG9yIGBkZWxldGUoKWAgYXJlIGNhbGxlZFxuXHRcdCAqIEBuYW1lIENvbGxlY3Rpb24jX2FycmF5XG5cdFx0ICogQHR5cGUgez9BcnJheX1cblx0XHQgKiBAcHJpdmF0ZVxuXHRcdCAqL1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnX2FycmF5JywgeyB2YWx1ZTogbnVsbCwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9KTtcblxuXHRcdC8qKlxuXHRcdCAqIENhY2hlZCBhcnJheSBmb3IgdGhlIGBrZXlBcnJheSgpYCBtZXRob2QgLSB3aWxsIGJlIHJlc2V0IHRvIGBudWxsYCB3aGVuZXZlciBgc2V0KClgIG9yIGBkZWxldGUoKWAgYXJlIGNhbGxlZFxuXHRcdCAqIEBuYW1lIENvbGxlY3Rpb24jX2tleUFycmF5XG5cdFx0ICogQHR5cGUgez9BcnJheX1cblx0XHQgKiBAcHJpdmF0ZVxuXHRcdCAqL1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnX2tleUFycmF5JywgeyB2YWx1ZTogbnVsbCwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBJZGVudGljYWwgdG8gW01hcC5nZXQoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL2dldCkuXG5cdCAqIEdldHMgYW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQga2V5LCBhbmQgcmV0dXJucyBpdHMgdmFsdWUsIG9yIGB1bmRlZmluZWRgIGlmIHRoZSBlbGVtZW50IGRvZXMgbm90IGV4aXN0LlxuXHQgKiBAcGFyYW0geyp9IGtleSAtIFRoZSBrZXkgdG8gZ2V0IGZyb20gdGhpcyBjb2xsZWN0aW9uXG5cdCAqIEByZXR1cm5zIHsqIHwgdW5kZWZpbmVkfVxuXHQgKi9cblx0cHVibGljIGdldChrZXk6IEspOiBWIHwgdW5kZWZpbmVkIHtcblx0XHRyZXR1cm4gc3VwZXIuZ2V0KGtleSk7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvIFtNYXAuc2V0KCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcC9zZXQpLlxuXHQgKiBTZXRzIGEgbmV3IGVsZW1lbnQgaW4gdGhlIGNvbGxlY3Rpb24gd2l0aCB0aGUgc3BlY2lmaWVkIGtleSBhbmQgdmFsdWUuXG5cdCAqIEBwYXJhbSB7Kn0ga2V5IC0gVGhlIGtleSBvZiB0aGUgZWxlbWVudCB0byBhZGRcblx0ICogQHBhcmFtIHsqfSB2YWx1ZSAtIFRoZSB2YWx1ZSBvZiB0aGUgZWxlbWVudCB0byBhZGRcblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqL1xuXHRwdWJsaWMgc2V0KGtleTogSywgdmFsdWU6IFYpOiB0aGlzIHtcblx0XHR0aGlzLl9hcnJheSA9IG51bGw7XG5cdFx0dGhpcy5fa2V5QXJyYXkgPSBudWxsO1xuXHRcdHJldHVybiBzdXBlci5zZXQoa2V5LCB2YWx1ZSk7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvIFtNYXAuaGFzKCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcC9oYXMpLlxuXHQgKiBDaGVja3MgaWYgYW4gZWxlbWVudCBleGlzdHMgaW4gdGhlIGNvbGxlY3Rpb24uXG5cdCAqIEBwYXJhbSB7Kn0ga2V5IC0gVGhlIGtleSBvZiB0aGUgZWxlbWVudCB0byBjaGVjayBmb3Jcblx0ICogQHJldHVybnMge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgZWxlbWVudCBleGlzdHMsIGBmYWxzZWAgaWYgaXQgZG9lcyBub3QgZXhpc3QuXG5cdCAqL1xuXHRwdWJsaWMgaGFzKGtleTogSyk6IGJvb2xlYW4ge1xuXHRcdHJldHVybiBzdXBlci5oYXMoa2V5KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBJZGVudGljYWwgdG8gW01hcC5kZWxldGUoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL2RlbGV0ZSkuXG5cdCAqIERlbGV0ZXMgYW4gZWxlbWVudCBmcm9tIHRoZSBjb2xsZWN0aW9uLlxuXHQgKiBAcGFyYW0geyp9IGtleSAtIFRoZSBrZXkgdG8gZGVsZXRlIGZyb20gdGhlIGNvbGxlY3Rpb25cblx0ICogQHJldHVybnMge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgZWxlbWVudCB3YXMgcmVtb3ZlZCwgYGZhbHNlYCBpZiB0aGUgZWxlbWVudCBkb2VzIG5vdCBleGlzdC5cblx0ICovXG5cdHB1YmxpYyBkZWxldGUoa2V5OiBLKTogYm9vbGVhbiB7XG5cdFx0dGhpcy5fYXJyYXkgPSBudWxsO1xuXHRcdHRoaXMuX2tleUFycmF5ID0gbnVsbDtcblx0XHRyZXR1cm4gc3VwZXIuZGVsZXRlKGtleSk7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvIFtNYXAuY2xlYXIoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL2NsZWFyKS5cblx0ICogUmVtb3ZlcyBhbGwgZWxlbWVudHMgZnJvbSB0aGUgY29sbGVjdGlvbi5cblx0ICogQHJldHVybnMge3VuZGVmaW5lZH1cblx0ICovXG5cdHB1YmxpYyBjbGVhcigpOiB2b2lkIHtcblx0XHRyZXR1cm4gc3VwZXIuY2xlYXIoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIG9yZGVyZWQgYXJyYXkgb2YgdGhlIHZhbHVlcyBvZiB0aGlzIGNvbGxlY3Rpb24sIGFuZCBjYWNoZXMgaXQgaW50ZXJuYWxseS4gVGhlIGFycmF5IHdpbGwgb25seSBiZVxuXHQgKiByZWNvbnN0cnVjdGVkIGlmIGFuIGl0ZW0gaXMgYWRkZWQgdG8gb3IgcmVtb3ZlZCBmcm9tIHRoZSBjb2xsZWN0aW9uLCBvciBpZiB5b3UgY2hhbmdlIHRoZSBsZW5ndGggb2YgdGhlIGFycmF5XG5cdCAqIGl0c2VsZi4gSWYgeW91IGRvbid0IHdhbnQgdGhpcyBjYWNoaW5nIGJlaGF2aW9yLCB1c2UgYFsuLi5jb2xsZWN0aW9uLnZhbHVlcygpXWAgb3Jcblx0ICogYEFycmF5LmZyb20oY29sbGVjdGlvbi52YWx1ZXMoKSlgIGluc3RlYWQuXG5cdCAqIEByZXR1cm5zIHtBcnJheX1cblx0ICovXG5cdHB1YmxpYyBhcnJheSgpOiBWW10ge1xuXHRcdGlmICghdGhpcy5fYXJyYXkgfHwgdGhpcy5fYXJyYXkubGVuZ3RoICE9PSB0aGlzLnNpemUpIHRoaXMuX2FycmF5ID0gWy4uLnRoaXMudmFsdWVzKCldO1xuXHRcdHJldHVybiB0aGlzLl9hcnJheTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIG9yZGVyZWQgYXJyYXkgb2YgdGhlIGtleXMgb2YgdGhpcyBjb2xsZWN0aW9uLCBhbmQgY2FjaGVzIGl0IGludGVybmFsbHkuIFRoZSBhcnJheSB3aWxsIG9ubHkgYmVcblx0ICogcmVjb25zdHJ1Y3RlZCBpZiBhbiBpdGVtIGlzIGFkZGVkIHRvIG9yIHJlbW92ZWQgZnJvbSB0aGUgY29sbGVjdGlvbiwgb3IgaWYgeW91IGNoYW5nZSB0aGUgbGVuZ3RoIG9mIHRoZSBhcnJheVxuXHQgKiBpdHNlbGYuIElmIHlvdSBkb24ndCB3YW50IHRoaXMgY2FjaGluZyBiZWhhdmlvciwgdXNlIGBbLi4uY29sbGVjdGlvbi5rZXlzKCldYCBvclxuXHQgKiBgQXJyYXkuZnJvbShjb2xsZWN0aW9uLmtleXMoKSlgIGluc3RlYWQuXG5cdCAqIEByZXR1cm5zIHtBcnJheX1cblx0ICovXG5cdHB1YmxpYyBrZXlBcnJheSgpOiBLW10ge1xuXHRcdGlmICghdGhpcy5fa2V5QXJyYXkgfHwgdGhpcy5fa2V5QXJyYXkubGVuZ3RoICE9PSB0aGlzLnNpemUpIHRoaXMuX2tleUFycmF5ID0gWy4uLnRoaXMua2V5cygpXTtcblx0XHRyZXR1cm4gdGhpcy5fa2V5QXJyYXk7XG5cdH1cblxuXHQvKipcblx0ICogT2J0YWlucyB0aGUgZmlyc3QgdmFsdWUocykgaW4gdGhpcyBjb2xsZWN0aW9uLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gW2Ftb3VudF0gQW1vdW50IG9mIHZhbHVlcyB0byBvYnRhaW4gZnJvbSB0aGUgYmVnaW5uaW5nXG5cdCAqIEByZXR1cm5zIHsqfEFycmF5PCo+fSBBIHNpbmdsZSB2YWx1ZSBpZiBubyBhbW91bnQgaXMgcHJvdmlkZWQgb3IgYW4gYXJyYXkgb2YgdmFsdWVzLCBzdGFydGluZyBmcm9tIHRoZSBlbmQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgZmlyc3QoKTogViB8IHVuZGVmaW5lZDtcblx0cHVibGljIGZpcnN0KGFtb3VudDogbnVtYmVyKTogVltdO1xuXHRwdWJsaWMgZmlyc3QoYW1vdW50PzogbnVtYmVyKTogViB8IFZbXSB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiBhbW91bnQgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdGhpcy52YWx1ZXMoKS5uZXh0KCkudmFsdWU7XG5cdFx0aWYgKGFtb3VudCA8IDApIHJldHVybiB0aGlzLmxhc3QoYW1vdW50ICogLTEpO1xuXHRcdGFtb3VudCA9IE1hdGgubWluKHRoaXMuc2l6ZSwgYW1vdW50KTtcblx0XHRjb25zdCBpdGVyID0gdGhpcy52YWx1ZXMoKTtcblx0XHRyZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogYW1vdW50IH0sICgpOiBWID0+IGl0ZXIubmV4dCgpLnZhbHVlKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBPYnRhaW5zIHRoZSBmaXJzdCBrZXkocykgaW4gdGhpcyBjb2xsZWN0aW9uLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gW2Ftb3VudF0gQW1vdW50IG9mIGtleXMgdG8gb2J0YWluIGZyb20gdGhlIGJlZ2lubmluZ1xuXHQgKiBAcmV0dXJucyB7KnxBcnJheTwqPn0gQSBzaW5nbGUga2V5IGlmIG5vIGFtb3VudCBpcyBwcm92aWRlZCBvciBhbiBhcnJheSBvZiBrZXlzLCBzdGFydGluZyBmcm9tIHRoZSBlbmQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgZmlyc3RLZXkoKTogSyB8IHVuZGVmaW5lZDtcblx0cHVibGljIGZpcnN0S2V5KGFtb3VudDogbnVtYmVyKTogS1tdO1xuXHRwdWJsaWMgZmlyc3RLZXkoYW1vdW50PzogbnVtYmVyKTogSyB8IEtbXSB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiBhbW91bnQgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdGhpcy5rZXlzKCkubmV4dCgpLnZhbHVlO1xuXHRcdGlmIChhbW91bnQgPCAwKSByZXR1cm4gdGhpcy5sYXN0S2V5KGFtb3VudCAqIC0xKTtcblx0XHRhbW91bnQgPSBNYXRoLm1pbih0aGlzLnNpemUsIGFtb3VudCk7XG5cdFx0Y29uc3QgaXRlciA9IHRoaXMua2V5cygpO1xuXHRcdHJldHVybiBBcnJheS5mcm9tKHsgbGVuZ3RoOiBhbW91bnQgfSwgKCk6IEsgPT4gaXRlci5uZXh0KCkudmFsdWUpO1xuXHR9XG5cblx0LyoqXG5cdCAqIE9idGFpbnMgdGhlIGxhc3QgdmFsdWUocykgaW4gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNhcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiB2YWx1ZXMgdG8gb2J0YWluIGZyb20gdGhlIGVuZFxuXHQgKiBAcmV0dXJucyB7KnxBcnJheTwqPn0gQSBzaW5nbGUgdmFsdWUgaWYgbm8gYW1vdW50IGlzIHByb3ZpZGVkIG9yIGFuIGFycmF5IG9mIHZhbHVlcywgc3RhcnRpbmcgZnJvbSB0aGUgc3RhcnQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgbGFzdCgpOiBWIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgbGFzdChhbW91bnQ6IG51bWJlcik6IFZbXTtcblx0cHVibGljIGxhc3QoYW1vdW50PzogbnVtYmVyKTogViB8IFZbXSB8IHVuZGVmaW5lZCB7XG5cdFx0Y29uc3QgYXJyID0gdGhpcy5hcnJheSgpO1xuXHRcdGlmICh0eXBlb2YgYW1vdW50ID09PSAndW5kZWZpbmVkJykgcmV0dXJuIGFyclthcnIubGVuZ3RoIC0gMV07XG5cdFx0aWYgKGFtb3VudCA8IDApIHJldHVybiB0aGlzLmZpcnN0KGFtb3VudCAqIC0xKTtcblx0XHRpZiAoIWFtb3VudCkgcmV0dXJuIFtdO1xuXHRcdHJldHVybiBhcnIuc2xpY2UoLWFtb3VudCk7XG5cdH1cblxuXHQvKipcblx0ICogT2J0YWlucyB0aGUgbGFzdCBrZXkocykgaW4gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNrZXlBcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiBrZXlzIHRvIG9idGFpbiBmcm9tIHRoZSBlbmRcblx0ICogQHJldHVybnMgeyp8QXJyYXk8Kj59IEEgc2luZ2xlIGtleSBpZiBubyBhbW91bnQgaXMgcHJvdmlkZWQgb3IgYW4gYXJyYXkgb2Yga2V5cywgc3RhcnRpbmcgZnJvbSB0aGUgc3RhcnQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgbGFzdEtleSgpOiBLIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgbGFzdEtleShhbW91bnQ6IG51bWJlcik6IEtbXTtcblx0cHVibGljIGxhc3RLZXkoYW1vdW50PzogbnVtYmVyKTogSyB8IEtbXSB8IHVuZGVmaW5lZCB7XG5cdFx0Y29uc3QgYXJyID0gdGhpcy5rZXlBcnJheSgpO1xuXHRcdGlmICh0eXBlb2YgYW1vdW50ID09PSAndW5kZWZpbmVkJykgcmV0dXJuIGFyclthcnIubGVuZ3RoIC0gMV07XG5cdFx0aWYgKGFtb3VudCA8IDApIHJldHVybiB0aGlzLmZpcnN0S2V5KGFtb3VudCAqIC0xKTtcblx0XHRpZiAoIWFtb3VudCkgcmV0dXJuIFtdO1xuXHRcdHJldHVybiBhcnIuc2xpY2UoLWFtb3VudCk7XG5cdH1cblxuXHQvKipcblx0ICogT2J0YWlucyB1bmlxdWUgcmFuZG9tIHZhbHVlKHMpIGZyb20gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNhcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiB2YWx1ZXMgdG8gb2J0YWluIHJhbmRvbWx5XG5cdCAqIEByZXR1cm5zIHsqfEFycmF5PCo+fSBBIHNpbmdsZSB2YWx1ZSBpZiBubyBhbW91bnQgaXMgcHJvdmlkZWQgb3IgYW4gYXJyYXkgb2YgdmFsdWVzXG5cdCAqL1xuXHRwdWJsaWMgcmFuZG9tKCk6IFY7XG5cdHB1YmxpYyByYW5kb20oYW1vdW50OiBudW1iZXIpOiBWW107XG5cdHB1YmxpYyByYW5kb20oYW1vdW50PzogbnVtYmVyKTogViB8IFZbXSB7XG5cdFx0bGV0IGFyciA9IHRoaXMuYXJyYXkoKTtcblx0XHRpZiAodHlwZW9mIGFtb3VudCA9PT0gJ3VuZGVmaW5lZCcpIHJldHVybiBhcnJbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYXJyLmxlbmd0aCldO1xuXHRcdGlmIChhcnIubGVuZ3RoID09PSAwIHx8ICFhbW91bnQpIHJldHVybiBbXTtcblx0XHRhcnIgPSBhcnIuc2xpY2UoKTtcblx0XHRyZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogYW1vdW50IH0sICgpOiBWID0+IGFyci5zcGxpY2UoTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYXJyLmxlbmd0aCksIDEpWzBdKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBPYnRhaW5zIHVuaXF1ZSByYW5kb20ga2V5KHMpIGZyb20gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNrZXlBcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiBrZXlzIHRvIG9idGFpbiByYW5kb21seVxuXHQgKiBAcmV0dXJucyB7KnxBcnJheTwqPn0gQSBzaW5nbGUga2V5IGlmIG5vIGFtb3VudCBpcyBwcm92aWRlZCBvciBhbiBhcnJheVxuXHQgKi9cblx0cHVibGljIHJhbmRvbUtleSgpOiBLO1xuXHRwdWJsaWMgcmFuZG9tS2V5KGFtb3VudDogbnVtYmVyKTogS1tdO1xuXHRwdWJsaWMgcmFuZG9tS2V5KGFtb3VudD86IG51bWJlcik6IEsgfCBLW10ge1xuXHRcdGxldCBhcnIgPSB0aGlzLmtleUFycmF5KCk7XG5cdFx0aWYgKHR5cGVvZiBhbW91bnQgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gYXJyW01hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFyci5sZW5ndGgpXTtcblx0XHRpZiAoYXJyLmxlbmd0aCA9PT0gMCB8fCAhYW1vdW50KSByZXR1cm4gW107XG5cdFx0YXJyID0gYXJyLnNsaWNlKCk7XG5cdFx0cmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGFtb3VudCB9LCAoKTogSyA9PiBhcnIuc3BsaWNlKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFyci5sZW5ndGgpLCAxKVswXSk7XG5cdH1cblxuXHQvKipcblx0ICogU2VhcmNoZXMgZm9yIGEgc2luZ2xlIGl0ZW0gd2hlcmUgdGhlIGdpdmVuIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUuIFRoaXMgYmVoYXZlcyBsaWtlXG5cdCAqIFtBcnJheS5maW5kKCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2ZpbmQpLlxuXHQgKiA8d2Fybj5BbGwgY29sbGVjdGlvbnMgdXNlZCBpbiBEaXNjb3JkLmpzIGFyZSBtYXBwZWQgdXNpbmcgdGhlaXIgYGlkYCBwcm9wZXJ0eSwgYW5kIGlmIHlvdSB3YW50IHRvIGZpbmQgYnkgaWQgeW91XG5cdCAqIHNob3VsZCB1c2UgdGhlIGBnZXRgIG1ldGhvZC4gU2VlXG5cdCAqIFtNRE5dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcC9nZXQpIGZvciBkZXRhaWxzLjwvd2Fybj5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGZ1bmN0aW9uIHRvIHRlc3Qgd2l0aCAoc2hvdWxkIHJldHVybiBib29sZWFuKVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHsqfVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmZpbmQodXNlciA9PiB1c2VyLnVzZXJuYW1lID09PSAnQm9iJyk7XG5cdCAqL1xuXHRwdWJsaWMgZmluZChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBWIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZDxUPihmbjogKHRoaXM6IFQsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc6IFQpOiBWIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc/OiB1bmtub3duKTogViB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiB0aGlzQXJnICE9PSAndW5kZWZpbmVkJykgZm4gPSBmbi5iaW5kKHRoaXNBcmcpO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSByZXR1cm4gdmFsO1xuXHRcdH1cblx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNlYXJjaGVzIGZvciB0aGUga2V5IG9mIGEgc2luZ2xlIGl0ZW0gd2hlcmUgdGhlIGdpdmVuIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUuIFRoaXMgYmVoYXZlcyBsaWtlXG5cdCAqIFtBcnJheS5maW5kSW5kZXgoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvZmluZEluZGV4KSxcblx0ICogYnV0IHJldHVybnMgdGhlIGtleSByYXRoZXIgdGhhbiB0aGUgcG9zaXRpb25hbCBpbmRleC5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGZ1bmN0aW9uIHRvIHRlc3Qgd2l0aCAoc2hvdWxkIHJldHVybiBib29sZWFuKVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHsqfVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmZpbmRLZXkodXNlciA9PiB1c2VyLnVzZXJuYW1lID09PSAnQm9iJyk7XG5cdCAqL1xuXHRwdWJsaWMgZmluZEtleShmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBLIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZEtleTxUPihmbjogKHRoaXM6IFQsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc6IFQpOiBLIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZEtleShmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc/OiB1bmtub3duKTogSyB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiB0aGlzQXJnICE9PSAndW5kZWZpbmVkJykgZm4gPSBmbi5iaW5kKHRoaXNBcmcpO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSByZXR1cm4ga2V5O1xuXHRcdH1cblx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJlbW92ZXMgaXRlbXMgdGhhdCBzYXRpc2Z5IHRoZSBwcm92aWRlZCBmaWx0ZXIgZnVuY3Rpb24uXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIEZ1bmN0aW9uIHVzZWQgdG8gdGVzdCAoc2hvdWxkIHJldHVybiBhIGJvb2xlYW4pXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge251bWJlcn0gVGhlIG51bWJlciBvZiByZW1vdmVkIGVudHJpZXNcblx0ICovXG5cdHB1YmxpYyBzd2VlcChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBudW1iZXI7XG5cdHB1YmxpYyBzd2VlcDxUPihmbjogKHRoaXM6IFQsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc6IFQpOiBudW1iZXI7XG5cdHB1YmxpYyBzd2VlcChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc/OiB1bmtub3duKTogbnVtYmVyIHtcblx0XHRpZiAodHlwZW9mIHRoaXNBcmcgIT09ICd1bmRlZmluZWQnKSBmbiA9IGZuLmJpbmQodGhpc0FyZyk7XG5cdFx0Y29uc3QgcHJldmlvdXNTaXplID0gdGhpcy5zaXplO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSB0aGlzLmRlbGV0ZShrZXkpO1xuXHRcdH1cblx0XHRyZXR1cm4gcHJldmlvdXNTaXplIC0gdGhpcy5zaXplO1xuXHR9XG5cblx0LyoqXG5cdCAqIElkZW50aWNhbCB0b1xuXHQgKiBbQXJyYXkuZmlsdGVyKCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2ZpbHRlciksXG5cdCAqIGJ1dCByZXR1cm5zIGEgQ29sbGVjdGlvbiBpbnN0ZWFkIG9mIGFuIEFycmF5LlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBUaGUgZnVuY3Rpb24gdG8gdGVzdCB3aXRoIChzaG91bGQgcmV0dXJuIGJvb2xlYW4pXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqIEBleGFtcGxlIGNvbGxlY3Rpb24uZmlsdGVyKHVzZXIgPT4gdXNlci51c2VybmFtZSA9PT0gJ0JvYicpO1xuXHQgKi9cblx0cHVibGljIGZpbHRlcihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiB0aGlzO1xuXHRwdWJsaWMgZmlsdGVyPFQ+KGZuOiAodGhpczogVCwgdmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gYm9vbGVhbiwgdGhpc0FyZzogVCk6IHRoaXM7XG5cdHB1YmxpYyBmaWx0ZXIoZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnPzogdW5rbm93bik6IHRoaXMge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRjb25zdCByZXN1bHRzID0gbmV3IHRoaXMuY29uc3RydWN0b3JbU3ltYm9sLnNwZWNpZXNdPEssIFY+KCkgYXMgdGhpcztcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykge1xuXHRcdFx0aWYgKGZuKHZhbCwga2V5LCB0aGlzKSkgcmVzdWx0cy5zZXQoa2V5LCB2YWwpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVzdWx0cztcblx0fVxuXG5cdC8qKlxuXHQgKiBQYXJ0aXRpb25zIHRoZSBjb2xsZWN0aW9uIGludG8gdHdvIGNvbGxlY3Rpb25zIHdoZXJlIHRoZSBmaXJzdCBjb2xsZWN0aW9uXG5cdCAqIGNvbnRhaW5zIHRoZSBpdGVtcyB0aGF0IHBhc3NlZCBhbmQgdGhlIHNlY29uZCBjb250YWlucyB0aGUgaXRlbXMgdGhhdCBmYWlsZWQuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIEZ1bmN0aW9uIHVzZWQgdG8gdGVzdCAoc2hvdWxkIHJldHVybiBhIGJvb2xlYW4pXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge0NvbGxlY3Rpb25bXX1cblx0ICogQGV4YW1wbGUgY29uc3QgW2JpZywgc21hbGxdID0gY29sbGVjdGlvbi5wYXJ0aXRpb24oZ3VpbGQgPT4gZ3VpbGQubWVtYmVyQ291bnQgPiAyNTApO1xuXHQgKi9cblx0cHVibGljIHBhcnRpdGlvbihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBbdGhpcywgdGhpc107XG5cdHB1YmxpYyBwYXJ0aXRpb248VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnOiBUKTogW3RoaXMsIHRoaXNdO1xuXHRwdWJsaWMgcGFydGl0aW9uKGZuOiAodmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gYm9vbGVhbiwgdGhpc0FyZz86IHVua25vd24pOiBbdGhpcywgdGhpc10ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHQvLyBUT0RPOiBjb25zaWRlciByZW1vdmluZyB0aGUgPEssIFY+IGZyb20gdGhlIGNvbnN0cnVjdG9ycyBhZnRlciBUUyAzLjcuMCBpcyByZWxlYXNlZCwgYXMgaXQgaW5mZXJzIGl0XG5cdFx0Y29uc3QgcmVzdWx0czogW3RoaXMsIHRoaXNdID0gW25ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXTxLLCBWPigpIGFzIHRoaXMsIG5ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXTxLLCBWPigpIGFzIHRoaXNdO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSB7XG5cdFx0XHRcdHJlc3VsdHNbMF0uc2V0KGtleSwgdmFsKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJlc3VsdHNbMV0uc2V0KGtleSwgdmFsKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHJlc3VsdHM7XG5cdH1cblxuXHQvKipcblx0ICogTWFwcyBlYWNoIGl0ZW0gaW50byBhIENvbGxlY3Rpb24sIHRoZW4gam9pbnMgdGhlIHJlc3VsdHMgaW50byBhIHNpbmdsZSBDb2xsZWN0aW9uLiBJZGVudGljYWwgaW4gYmVoYXZpb3IgdG9cblx0ICogW0FycmF5LmZsYXRNYXAoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvZmxhdE1hcCkuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIEZ1bmN0aW9uIHRoYXQgcHJvZHVjZXMgYSBuZXcgQ29sbGVjdGlvblxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmZsYXRNYXAoZ3VpbGQgPT4gZ3VpbGQubWVtYmVycy5jYWNoZSk7XG5cdCAqL1xuXHRwdWJsaWMgZmxhdE1hcDxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IENvbGxlY3Rpb248SywgVD4pOiBDb2xsZWN0aW9uPEssIFQ+O1xuXHRwdWJsaWMgZmxhdE1hcDxULCBUaGlzPihmbjogKHRoaXM6IFRoaXMsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IENvbGxlY3Rpb248SywgVD4sIHRoaXNBcmc6IFRoaXMpOiBDb2xsZWN0aW9uPEssIFQ+O1xuXHRwdWJsaWMgZmxhdE1hcDxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IENvbGxlY3Rpb248SywgVD4sIHRoaXNBcmc/OiB1bmtub3duKTogQ29sbGVjdGlvbjxLLCBUPiB7XG5cdFx0Y29uc3QgY29sbGVjdGlvbnMgPSB0aGlzLm1hcChmbiwgdGhpc0FyZyk7XG5cdFx0cmV0dXJuIChuZXcgdGhpcy5jb25zdHJ1Y3RvcltTeW1ib2wuc3BlY2llc108SywgVD4oKSBhcyBDb2xsZWN0aW9uPEssIFQ+KS5jb25jYXQoLi4uY29sbGVjdGlvbnMpO1xuXHR9XG5cblx0LyoqXG5cdCAqIE1hcHMgZWFjaCBpdGVtIHRvIGFub3RoZXIgdmFsdWUgaW50byBhbiBhcnJheS4gSWRlbnRpY2FsIGluIGJlaGF2aW9yIHRvXG5cdCAqIFtBcnJheS5tYXAoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvbWFwKS5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdGhhdCBwcm9kdWNlcyBhbiBlbGVtZW50IG9mIHRoZSBuZXcgYXJyYXksIHRha2luZyB0aHJlZSBhcmd1bWVudHNcblx0ICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVmFsdWUgdG8gdXNlIGFzIGB0aGlzYCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvblxuXHQgKiBAcmV0dXJucyB7QXJyYXl9XG5cdCAqIEBleGFtcGxlIGNvbGxlY3Rpb24ubWFwKHVzZXIgPT4gdXNlci50YWcpO1xuXHQgKi9cblx0cHVibGljIG1hcDxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IFQpOiBUW107XG5cdHB1YmxpYyBtYXA8VGhpcywgVD4oZm46ICh0aGlzOiBUaGlzLCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBULCB0aGlzQXJnOiBUaGlzKTogVFtdO1xuXHRwdWJsaWMgbWFwPFQ+KGZuOiAodmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gVCwgdGhpc0FyZz86IHVua25vd24pOiBUW10ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRjb25zdCBpdGVyID0gdGhpcy5lbnRyaWVzKCk7XG5cdFx0cmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IHRoaXMuc2l6ZSB9LCAoKTogVCA9PiB7XG5cdFx0XHRjb25zdCBba2V5LCB2YWx1ZV0gPSBpdGVyLm5leHQoKS52YWx1ZTtcblx0XHRcdHJldHVybiBmbih2YWx1ZSwga2V5LCB0aGlzKTtcblx0XHR9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBNYXBzIGVhY2ggaXRlbSB0byBhbm90aGVyIHZhbHVlIGludG8gYSBjb2xsZWN0aW9uLiBJZGVudGljYWwgaW4gYmVoYXZpb3IgdG9cblx0ICogW0FycmF5Lm1hcCgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9tYXApLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB0aGF0IHByb2R1Y2VzIGFuIGVsZW1lbnQgb2YgdGhlIG5ldyBjb2xsZWN0aW9uLCB0YWtpbmcgdGhyZWUgYXJndW1lbnRzXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqIEBleGFtcGxlIGNvbGxlY3Rpb24ubWFwVmFsdWVzKHVzZXIgPT4gdXNlci50YWcpO1xuXHQgKi9cblx0cHVibGljIG1hcFZhbHVlczxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IFQpOiBDb2xsZWN0aW9uPEssIFQ+O1xuXHRwdWJsaWMgbWFwVmFsdWVzPFRoaXMsIFQ+KGZuOiAodGhpczogVGhpcywgdmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gVCwgdGhpc0FyZzogVGhpcyk6IENvbGxlY3Rpb248SywgVD47XG5cdHB1YmxpYyBtYXBWYWx1ZXM8VD4oZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBULCB0aGlzQXJnPzogdW5rbm93bik6IENvbGxlY3Rpb248SywgVD4ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRjb25zdCBjb2xsID0gbmV3IHRoaXMuY29uc3RydWN0b3JbU3ltYm9sLnNwZWNpZXNdPEssIFQ+KCkgYXMgQ29sbGVjdGlvbjxLLCBUPjtcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykgY29sbC5zZXQoa2V5LCBmbih2YWwsIGtleSwgdGhpcykpO1xuXHRcdHJldHVybiBjb2xsO1xuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGVyZSBleGlzdHMgYW4gaXRlbSB0aGF0IHBhc3NlcyBhIHRlc3QuIElkZW50aWNhbCBpbiBiZWhhdmlvciB0b1xuXHQgKiBbQXJyYXkuc29tZSgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9zb21lKS5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdXNlZCB0byB0ZXN0IChzaG91bGQgcmV0dXJuIGEgYm9vbGVhbilcblx0ICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVmFsdWUgdG8gdXNlIGFzIGB0aGlzYCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvblxuXHQgKiBAcmV0dXJucyB7Ym9vbGVhbn1cblx0ICogQGV4YW1wbGUgY29sbGVjdGlvbi5zb21lKHVzZXIgPT4gdXNlci5kaXNjcmltaW5hdG9yID09PSAnMDAwMCcpO1xuXHQgKi9cblx0cHVibGljIHNvbWUoZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuKTogYm9vbGVhbjtcblx0cHVibGljIHNvbWU8VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnOiBUKTogYm9vbGVhbjtcblx0cHVibGljIHNvbWUoZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnPzogdW5rbm93bik6IGJvb2xlYW4ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykge1xuXHRcdFx0aWYgKGZuKHZhbCwga2V5LCB0aGlzKSkgcmV0dXJuIHRydWU7XG5cdFx0fVxuXHRcdHJldHVybiBmYWxzZTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgYWxsIGl0ZW1zIHBhc3NlcyBhIHRlc3QuIElkZW50aWNhbCBpbiBiZWhhdmlvciB0b1xuXHQgKiBbQXJyYXkuZXZlcnkoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvZXZlcnkpLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB1c2VkIHRvIHRlc3QgKHNob3VsZCByZXR1cm4gYSBib29sZWFuKVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHtib29sZWFufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmV2ZXJ5KHVzZXIgPT4gIXVzZXIuYm90KTtcblx0ICovXG5cdHB1YmxpYyBldmVyeShmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBib29sZWFuO1xuXHRwdWJsaWMgZXZlcnk8VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnOiBUKTogYm9vbGVhbjtcblx0cHVibGljIGV2ZXJ5KGZuOiAodmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gYm9vbGVhbiwgdGhpc0FyZz86IHVua25vd24pOiBib29sZWFuIHtcblx0XHRpZiAodHlwZW9mIHRoaXNBcmcgIT09ICd1bmRlZmluZWQnKSBmbiA9IGZuLmJpbmQodGhpc0FyZyk7XG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWxdIG9mIHRoaXMpIHtcblx0XHRcdGlmICghZm4odmFsLCBrZXksIHRoaXMpKSByZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdHJldHVybiB0cnVlO1xuXHR9XG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgYSBmdW5jdGlvbiB0byBwcm9kdWNlIGEgc2luZ2xlIHZhbHVlLiBJZGVudGljYWwgaW4gYmVoYXZpb3IgdG9cblx0ICogW0FycmF5LnJlZHVjZSgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9yZWR1Y2UpLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB1c2VkIHRvIHJlZHVjZSwgdGFraW5nIGZvdXIgYXJndW1lbnRzOyBgYWNjdW11bGF0b3JgLCBgY3VycmVudFZhbHVlYCwgYGN1cnJlbnRLZXlgLFxuXHQgKiBhbmQgYGNvbGxlY3Rpb25gXG5cdCAqIEBwYXJhbSB7Kn0gW2luaXRpYWxWYWx1ZV0gU3RhcnRpbmcgdmFsdWUgZm9yIHRoZSBhY2N1bXVsYXRvclxuXHQgKiBAcmV0dXJucyB7Kn1cblx0ICogQGV4YW1wbGUgY29sbGVjdGlvbi5yZWR1Y2UoKGFjYywgZ3VpbGQpID0+IGFjYyArIGd1aWxkLm1lbWJlckNvdW50LCAwKTtcblx0ICovXG5cdHB1YmxpYyByZWR1Y2U8VD4oZm46IChhY2N1bXVsYXRvcjogVCwgdmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gVCwgaW5pdGlhbFZhbHVlPzogVCk6IFQge1xuXHRcdGxldCBhY2N1bXVsYXRvciE6IFQ7XG5cblx0XHRpZiAodHlwZW9mIGluaXRpYWxWYWx1ZSAhPT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdGFjY3VtdWxhdG9yID0gaW5pdGlhbFZhbHVlO1xuXHRcdFx0Zm9yIChjb25zdCBba2V5LCB2YWxdIG9mIHRoaXMpIGFjY3VtdWxhdG9yID0gZm4oYWNjdW11bGF0b3IsIHZhbCwga2V5LCB0aGlzKTtcblx0XHRcdHJldHVybiBhY2N1bXVsYXRvcjtcblx0XHR9XG5cdFx0bGV0IGZpcnN0ID0gdHJ1ZTtcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykge1xuXHRcdFx0aWYgKGZpcnN0KSB7XG5cdFx0XHRcdGFjY3VtdWxhdG9yID0gdmFsIGFzIHVua25vd24gYXMgVDtcblx0XHRcdFx0Zmlyc3QgPSBmYWxzZTtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cdFx0XHRhY2N1bXVsYXRvciA9IGZuKGFjY3VtdWxhdG9yLCB2YWwsIGtleSwgdGhpcyk7XG5cdFx0fVxuXG5cdFx0Ly8gTm8gaXRlbXMgaXRlcmF0ZWQuXG5cdFx0aWYgKGZpcnN0KSB7XG5cdFx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCdSZWR1Y2Ugb2YgZW1wdHkgY29sbGVjdGlvbiB3aXRoIG5vIGluaXRpYWwgdmFsdWUnKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gYWNjdW11bGF0b3I7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvXG5cdCAqIFtNYXAuZm9yRWFjaCgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9NYXAvZm9yRWFjaCksXG5cdCAqIGJ1dCByZXR1cm5zIHRoZSBjb2xsZWN0aW9uIGluc3RlYWQgb2YgdW5kZWZpbmVkLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB0byBleGVjdXRlIGZvciBlYWNoIGVsZW1lbnRcblx0ICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVmFsdWUgdG8gdXNlIGFzIGB0aGlzYCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvblxuXHQgKiBAcmV0dXJucyB7Q29sbGVjdGlvbn1cblx0ICogQGV4YW1wbGVcblx0ICogY29sbGVjdGlvblxuXHQgKiAgLmVhY2godXNlciA9PiBjb25zb2xlLmxvZyh1c2VyLnVzZXJuYW1lKSlcblx0ICogIC5maWx0ZXIodXNlciA9PiB1c2VyLmJvdClcblx0ICogIC5lYWNoKHVzZXIgPT4gY29uc29sZS5sb2codXNlci51c2VybmFtZSkpO1xuXHQgKi9cblx0cHVibGljIGVhY2goZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkKTogdGhpcztcblx0cHVibGljIGVhY2g8VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkLCB0aGlzQXJnOiBUKTogdGhpcztcblx0cHVibGljIGVhY2goZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkLCB0aGlzQXJnPzogdW5rbm93bik6IHRoaXMge1xuXHRcdHRoaXMuZm9yRWFjaChmbiBhcyAodmFsdWU6IFYsIGtleTogSywgbWFwOiBNYXA8SywgVj4pID0+IHZvaWQsIHRoaXNBcmcpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJ1bnMgYSBmdW5jdGlvbiBvbiB0aGUgY29sbGVjdGlvbiBhbmQgcmV0dXJucyB0aGUgY29sbGVjdGlvbi5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdG8gZXhlY3V0ZVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZVxuXHQgKiBjb2xsZWN0aW9uXG5cdCAqICAudGFwKGNvbGwgPT4gY29uc29sZS5sb2coY29sbC5zaXplKSlcblx0ICogIC5maWx0ZXIodXNlciA9PiB1c2VyLmJvdClcblx0ICogIC50YXAoY29sbCA9PiBjb25zb2xlLmxvZyhjb2xsLnNpemUpKVxuXHQgKi9cblx0cHVibGljIHRhcChmbjogKGNvbGxlY3Rpb246IHRoaXMpID0+IHZvaWQpOiB0aGlzO1xuXHRwdWJsaWMgdGFwPFQ+KGZuOiAodGhpczogVCwgY29sbGVjdGlvbjogdGhpcykgPT4gdm9pZCwgdGhpc0FyZzogVCk6IHRoaXM7XG5cdHB1YmxpYyB0YXAoZm46IChjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkLCB0aGlzQXJnPzogdW5rbm93bik6IHRoaXMge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRmbih0aGlzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIGlkZW50aWNhbCBzaGFsbG93IGNvcHkgb2YgdGhpcyBjb2xsZWN0aW9uLlxuXHQgKiBAcmV0dXJucyB7Q29sbGVjdGlvbn1cblx0ICogQGV4YW1wbGUgY29uc3QgbmV3Q29sbCA9IHNvbWVDb2xsLmNsb25lKCk7XG5cdCAqL1xuXHRwdWJsaWMgY2xvbmUoKTogdGhpcyB7XG5cdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXSh0aGlzKSBhcyB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbWJpbmVzIHRoaXMgY29sbGVjdGlvbiB3aXRoIG90aGVycyBpbnRvIGEgbmV3IGNvbGxlY3Rpb24uIE5vbmUgb2YgdGhlIHNvdXJjZSBjb2xsZWN0aW9ucyBhcmUgbW9kaWZpZWQuXG5cdCAqIEBwYXJhbSB7Li4uQ29sbGVjdGlvbn0gY29sbGVjdGlvbnMgQ29sbGVjdGlvbnMgdG8gbWVyZ2Vcblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqIEBleGFtcGxlIGNvbnN0IG5ld0NvbGwgPSBzb21lQ29sbC5jb25jYXQoc29tZU90aGVyQ29sbCwgYW5vdGhlckNvbGwsIG9oQm95QUNvbGwpO1xuXHQgKi9cblx0cHVibGljIGNvbmNhdCguLi5jb2xsZWN0aW9uczogQ29sbGVjdGlvbjxLLCBWPltdKTogdGhpcyB7XG5cdFx0Y29uc3QgbmV3Q29sbCA9IHRoaXMuY2xvbmUoKTtcblx0XHRmb3IgKGNvbnN0IGNvbGwgb2YgY29sbGVjdGlvbnMpIHtcblx0XHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiBjb2xsKSBuZXdDb2xsLnNldChrZXksIHZhbCk7XG5cdFx0fVxuXHRcdHJldHVybiBuZXdDb2xsO1xuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGlzIGNvbGxlY3Rpb24gc2hhcmVzIGlkZW50aWNhbCBpdGVtcyB3aXRoIGFub3RoZXIuXG5cdCAqIFRoaXMgaXMgZGlmZmVyZW50IHRvIGNoZWNraW5nIGZvciBlcXVhbGl0eSB1c2luZyBlcXVhbC1zaWducywgYmVjYXVzZVxuXHQgKiB0aGUgY29sbGVjdGlvbnMgbWF5IGJlIGRpZmZlcmVudCBvYmplY3RzLCBidXQgY29udGFpbiB0aGUgc2FtZSBkYXRhLlxuXHQgKiBAcGFyYW0ge0NvbGxlY3Rpb259IGNvbGxlY3Rpb24gQ29sbGVjdGlvbiB0byBjb21wYXJlIHdpdGhcblx0ICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIgdGhlIGNvbGxlY3Rpb25zIGhhdmUgaWRlbnRpY2FsIGNvbnRlbnRzXG5cdCAqL1xuXHRwdWJsaWMgZXF1YWxzKGNvbGxlY3Rpb246IENvbGxlY3Rpb248SywgVj4pOiBib29sZWFuIHtcblx0XHRpZiAoIWNvbGxlY3Rpb24pIHJldHVybiBmYWxzZTtcblx0XHRpZiAodGhpcyA9PT0gY29sbGVjdGlvbikgcmV0dXJuIHRydWU7XG5cdFx0aWYgKHRoaXMuc2l6ZSAhPT0gY29sbGVjdGlvbi5zaXplKSByZXR1cm4gZmFsc2U7XG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgdGhpcykge1xuXHRcdFx0aWYgKCFjb2xsZWN0aW9uLmhhcyhrZXkpIHx8IHZhbHVlICE9PSBjb2xsZWN0aW9uLmdldChrZXkpKSB7XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIHNvcnQgbWV0aG9kIHNvcnRzIHRoZSBpdGVtcyBvZiBhIGNvbGxlY3Rpb24gaW4gcGxhY2UgYW5kIHJldHVybnMgaXQuXG5cdCAqIFRoZSBzb3J0IGlzIG5vdCBuZWNlc3NhcmlseSBzdGFibGUgaW4gTm9kZSAxMCBvciBvbGRlci5cblx0ICogVGhlIGRlZmF1bHQgc29ydCBvcmRlciBpcyBhY2NvcmRpbmcgdG8gc3RyaW5nIFVuaWNvZGUgY29kZSBwb2ludHMuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IFtjb21wYXJlRnVuY3Rpb25dIFNwZWNpZmllcyBhIGZ1bmN0aW9uIHRoYXQgZGVmaW5lcyB0aGUgc29ydCBvcmRlci5cblx0ICogSWYgb21pdHRlZCwgdGhlIGNvbGxlY3Rpb24gaXMgc29ydGVkIGFjY29yZGluZyB0byBlYWNoIGNoYXJhY3RlcidzIFVuaWNvZGUgY29kZSBwb2ludCB2YWx1ZSxcblx0ICogYWNjb3JkaW5nIHRvIHRoZSBzdHJpbmcgY29udmVyc2lvbiBvZiBlYWNoIGVsZW1lbnQuXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLnNvcnQoKHVzZXJBLCB1c2VyQikgPT4gdXNlckEuY3JlYXRlZFRpbWVzdGFtcCAtIHVzZXJCLmNyZWF0ZWRUaW1lc3RhbXApO1xuXHQgKi9cblx0cHVibGljIHNvcnQoY29tcGFyZUZ1bmN0aW9uOiAoZmlyc3RWYWx1ZTogViwgc2Vjb25kVmFsdWU6IFYsIGZpcnN0S2V5OiBLLCBzZWNvbmRLZXk6IEspID0+IG51bWJlciA9ICh4LCB5KTogbnVtYmVyID0+IE51bWJlcih4ID4geSkgfHwgTnVtYmVyKHggPT09IHkpIC0gMSk6IHRoaXMge1xuXHRcdGNvbnN0IGVudHJpZXMgPSBbLi4udGhpcy5lbnRyaWVzKCldO1xuXHRcdGVudHJpZXMuc29ydCgoYSwgYik6IG51bWJlciA9PiBjb21wYXJlRnVuY3Rpb24oYVsxXSwgYlsxXSwgYVswXSwgYlswXSkpO1xuXG5cdFx0Ly8gUGVyZm9ybSBjbGVhbi11cFxuXHRcdHN1cGVyLmNsZWFyKCk7XG5cdFx0dGhpcy5fYXJyYXkgPSBudWxsO1xuXHRcdHRoaXMuX2tleUFycmF5ID0gbnVsbDtcblxuXHRcdC8vIFNldCB0aGUgbmV3IGVudHJpZXNcblx0XHRmb3IgKGNvbnN0IFtrLCB2XSBvZiBlbnRyaWVzKSB7XG5cdFx0XHRzdXBlci5zZXQoaywgdik7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBpbnRlcnNlY3QgbWV0aG9kIHJldHVybnMgYSBuZXcgc3RydWN0dXJlIGNvbnRhaW5pbmcgaXRlbXMgd2hlcmUgdGhlIGtleXMgYXJlIHByZXNlbnQgaW4gYm90aCBvcmlnaW5hbCBzdHJ1Y3R1cmVzLlxuXHQgKiBAcGFyYW0ge0NvbGxlY3Rpb259IG90aGVyIFRoZSBvdGhlciBDb2xsZWN0aW9uIHRvIGZpbHRlciBhZ2FpbnN0XG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKi9cblx0cHVibGljIGludGVyc2VjdChvdGhlcjogQ29sbGVjdGlvbjxLLCBWPik6IENvbGxlY3Rpb248SywgVj4ge1xuXHRcdHJldHVybiBvdGhlci5maWx0ZXIoKF8sIGspID0+IHRoaXMuaGFzKGspKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUaGUgZGlmZmVyZW5jZSBtZXRob2QgcmV0dXJucyBhIG5ldyBzdHJ1Y3R1cmUgY29udGFpbmluZyBpdGVtcyB3aGVyZSB0aGUga2V5IGlzIHByZXNlbnQgaW4gb25lIG9mIHRoZSBvcmlnaW5hbCBzdHJ1Y3R1cmVzIGJ1dCBub3QgdGhlIG90aGVyLlxuXHQgKiBAcGFyYW0ge0NvbGxlY3Rpb259IG90aGVyIFRoZSBvdGhlciBDb2xsZWN0aW9uIHRvIGZpbHRlciBhZ2FpbnN0XG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKi9cblx0cHVibGljIGRpZmZlcmVuY2Uob3RoZXI6IENvbGxlY3Rpb248SywgVj4pOiBDb2xsZWN0aW9uPEssIFY+IHtcblx0XHRyZXR1cm4gb3RoZXIuZmlsdGVyKChfLCBrKSA9PiAhdGhpcy5oYXMoaykpLmNvbmNhdCh0aGlzLmZpbHRlcigoXywgaykgPT4gIW90aGVyLmhhcyhrKSkpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBzb3J0ZWQgbWV0aG9kIHNvcnRzIHRoZSBpdGVtcyBvZiBhIGNvbGxlY3Rpb24gYW5kIHJldHVybnMgaXQuXG5cdCAqIFRoZSBzb3J0IGlzIG5vdCBuZWNlc3NhcmlseSBzdGFibGUgaW4gTm9kZSAxMCBvciBvbGRlci5cblx0ICogVGhlIGRlZmF1bHQgc29ydCBvcmRlciBpcyBhY2NvcmRpbmcgdG8gc3RyaW5nIFVuaWNvZGUgY29kZSBwb2ludHMuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IFtjb21wYXJlRnVuY3Rpb25dIFNwZWNpZmllcyBhIGZ1bmN0aW9uIHRoYXQgZGVmaW5lcyB0aGUgc29ydCBvcmRlci5cblx0ICogSWYgb21pdHRlZCwgdGhlIGNvbGxlY3Rpb24gaXMgc29ydGVkIGFjY29yZGluZyB0byBlYWNoIGNoYXJhY3RlcidzIFVuaWNvZGUgY29kZSBwb2ludCB2YWx1ZSxcblx0ICogYWNjb3JkaW5nIHRvIHRoZSBzdHJpbmcgY29udmVyc2lvbiBvZiBlYWNoIGVsZW1lbnQuXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLnNvcnRlZCgodXNlckEsIHVzZXJCKSA9PiB1c2VyQS5jcmVhdGVkVGltZXN0YW1wIC0gdXNlckIuY3JlYXRlZFRpbWVzdGFtcCk7XG5cdCAqL1xuXHRwdWJsaWMgc29ydGVkKGNvbXBhcmVGdW5jdGlvbjogKGZpcnN0VmFsdWU6IFYsIHNlY29uZFZhbHVlOiBWLCBmaXJzdEtleTogSywgc2Vjb25kS2V5OiBLKSA9PiBudW1iZXIgPSAoeCwgeSk6IG51bWJlciA9PiBOdW1iZXIoeCA+IHkpIHx8IE51bWJlcih4ID09PSB5KSAtIDEpOiB0aGlzIHtcblx0XHRyZXR1cm4gKG5ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXShbLi4udGhpcy5lbnRyaWVzKCldKSBhcyB0aGlzKVxuXHRcdFx0LnNvcnQoKGF2LCBidiwgYWssIGJrKSA9PiBjb21wYXJlRnVuY3Rpb24oYXYsIGJ2LCBhaywgYmspKTtcblx0fVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IENvbGxlY3Rpb247XG5leHBvcnQgeyBDb2xsZWN0aW9uIH07XG5leHBvcnQgZGVmYXVsdCBDb2xsZWN0aW9uO1xuIl19 \ No newline at end of file diff --git a/node_modules/@discordjs/collection/package.json b/node_modules/@discordjs/collection/package.json new file mode 100644 index 0000000..ed4ba86 --- /dev/null +++ b/node_modules/@discordjs/collection/package.json @@ -0,0 +1,78 @@ +{ + "_from": "@discordjs/collection@^0.1.6", + "_id": "@discordjs/collection@0.1.6", + "_inBundle": false, + "_integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==", + "_location": "/@discordjs/collection", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "@discordjs/collection@^0.1.6", + "name": "@discordjs/collection", + "escapedName": "@discordjs%2fcollection", + "scope": "@discordjs", + "rawSpec": "^0.1.6", + "saveSpec": null, + "fetchSpec": "^0.1.6" + }, + "_requiredBy": [ + "/discord.js" + ], + "_resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", + "_shasum": "9e9a7637f4e4e0688fd8b2b5c63133c91607682c", + "_spec": "@discordjs/collection@^0.1.6", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\discord.js", + "author": { + "name": "Amish Shah", + "email": "amishshah.2k@gmail.com" + }, + "bugs": { + "url": "https://github.com/discordjs/collection/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Utility data structure used in Discord.js", + "devDependencies": { + "@babel/cli": "^7.8.4", + "@babel/core": "^7.8.4", + "@babel/preset-env": "^7.8.4", + "@babel/preset-typescript": "^7.8.3", + "@types/node": "^13.7.4", + "@typescript-eslint/eslint-plugin": "^2.21.0", + "@typescript-eslint/parser": "^2.21.0", + "discord.js-docgen": "github:discordjs/docgen#ts-patch", + "eslint": "^6.8.0", + "eslint-config-marine": "^6.0.0", + "jsdoc-babel": "^0.5.0", + "rimraf": "^3.0.2", + "typescript": "^3.8.2" + }, + "eslintConfig": { + "extends": "marine/node" + }, + "homepage": "https://github.com/discordjs/collection#readme", + "keywords": [ + "map", + "collection", + "utility" + ], + "license": "Apache-2.0", + "main": "dist/index.js", + "name": "@discordjs/collection", + "repository": { + "type": "git", + "url": "git+https://github.com/discordjs/collection.git" + }, + "scripts": { + "build": "rimraf dist/ && tsc", + "docs": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml --output docs/docs.json", + "docs:test": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml", + "lint": "eslint src --ext .ts", + "prebuild": "npm run lint", + "pretest": "npm run build", + "test": "node test/index.js" + }, + "types": "dist/index.d.ts", + "version": "0.1.6" +} diff --git a/node_modules/@discordjs/form-data/License b/node_modules/@discordjs/form-data/License new file mode 100644 index 0000000..c7ff12a --- /dev/null +++ b/node_modules/@discordjs/form-data/License @@ -0,0 +1,19 @@ +Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/node_modules/@discordjs/form-data/Readme.md b/node_modules/@discordjs/form-data/Readme.md new file mode 100644 index 0000000..3d8c93a --- /dev/null +++ b/node_modules/@discordjs/form-data/Readme.md @@ -0,0 +1,353 @@ +# Form-Data [![NPM Module](https://img.shields.io/npm/v/form-data.svg)](https://www.npmjs.com/package/form-data) [![Join the chat at https://gitter.im/form-data/form-data](http://form-data.github.io/images/gitterbadge.svg)](https://gitter.im/form-data/form-data) + +A library to create readable ```"multipart/form-data"``` streams. Can be used to submit forms and file uploads to other web applications. + +The API of this library is inspired by the [XMLHttpRequest-2 FormData Interface][xhr2-fd]. + +[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface + +[![Linux Build](https://img.shields.io/travis/form-data/form-data/master.svg?label=linux:6.x-12.x)](https://travis-ci.org/form-data/form-data) +[![MacOS Build](https://img.shields.io/travis/form-data/form-data/master.svg?label=macos:6.x-12.x)](https://travis-ci.org/form-data/form-data) +[![Windows Build](https://img.shields.io/travis/form-data/form-data/master.svg?label=windows:6.x-12.x)](https://travis-ci.org/form-data/form-data) + +[![Coverage Status](https://img.shields.io/coveralls/form-data/form-data/master.svg?label=code+coverage)](https://coveralls.io/github/form-data/form-data?branch=master) +[![Dependency Status](https://img.shields.io/david/form-data/form-data.svg)](https://david-dm.org/form-data/form-data) + +## Install + +``` +npm install --save form-data +``` + +## Usage + +In this example we are constructing a form with 3 fields that contain a string, +a buffer and a file stream. + +``` javascript +var FormData = require('form-data'); +var fs = require('fs'); + +var form = new FormData(); +form.append('my_field', 'my value'); +form.append('my_buffer', new Buffer(10)); +form.append('my_file', fs.createReadStream('/foo/bar.jpg')); +``` + +Also you can use http-response stream: + +``` javascript +var FormData = require('form-data'); +var http = require('http'); + +var form = new FormData(); + +http.request('http://nodejs.org/images/logo.png', function(response) { + form.append('my_field', 'my value'); + form.append('my_buffer', new Buffer(10)); + form.append('my_logo', response); +}); +``` + +Or @mikeal's [request](https://github.com/request/request) stream: + +``` javascript +var FormData = require('form-data'); +var request = require('request'); + +var form = new FormData(); + +form.append('my_field', 'my value'); +form.append('my_buffer', new Buffer(10)); +form.append('my_logo', request('http://nodejs.org/images/logo.png')); +``` + +In order to submit this form to a web application, call ```submit(url, [callback])``` method: + +``` javascript +form.submit('http://example.org/', function(err, res) { + // res – response object (http.IncomingMessage) // + res.resume(); +}); + +``` + +For more advanced request manipulations ```submit()``` method returns ```http.ClientRequest``` object, or you can choose from one of the alternative submission methods. + +### Custom options + +You can provide custom options, such as `maxDataSize`: + +``` javascript +var FormData = require('form-data'); + +var form = new FormData({ maxDataSize: 20971520 }); +form.append('my_field', 'my value'); +form.append('my_buffer', /* something big */); +``` + +List of available options could be found in [combined-stream](https://github.com/felixge/node-combined-stream/blob/master/lib/combined_stream.js#L7-L15) + +### Alternative submission methods + +You can use node's http client interface: + +``` javascript +var http = require('http'); + +var request = http.request({ + method: 'post', + host: 'example.org', + path: '/upload', + headers: form.getHeaders() +}); + +form.pipe(request); + +request.on('response', function(res) { + console.log(res.statusCode); +}); +``` + +Or if you would prefer the `'Content-Length'` header to be set for you: + +``` javascript +form.submit('example.org/upload', function(err, res) { + console.log(res.statusCode); +}); +``` + +To use custom headers and pre-known length in parts: + +``` javascript +var CRLF = '\r\n'; +var form = new FormData(); + +var options = { + header: CRLF + '--' + form.getBoundary() + CRLF + 'X-Custom-Header: 123' + CRLF + CRLF, + knownLength: 1 +}; + +form.append('my_buffer', buffer, options); + +form.submit('http://example.com/', function(err, res) { + if (err) throw err; + console.log('Done'); +}); +``` + +Form-Data can recognize and fetch all the required information from common types of streams (```fs.readStream```, ```http.response``` and ```mikeal's request```), for some other types of streams you'd need to provide "file"-related information manually: + +``` javascript +someModule.stream(function(err, stdout, stderr) { + if (err) throw err; + + var form = new FormData(); + + form.append('file', stdout, { + filename: 'unicycle.jpg', // ... or: + filepath: 'photos/toys/unicycle.jpg', + contentType: 'image/jpeg', + knownLength: 19806 + }); + + form.submit('http://example.com/', function(err, res) { + if (err) throw err; + console.log('Done'); + }); +}); +``` + +The `filepath` property overrides `filename` and may contain a relative path. This is typically used when uploading [multiple files from a directory](https://wicg.github.io/entries-api/#dom-htmlinputelement-webkitdirectory). + +For edge cases, like POST request to URL with query string or to pass HTTP auth credentials, object can be passed to `form.submit()` as first parameter: + +``` javascript +form.submit({ + host: 'example.com', + path: '/probably.php?extra=params', + auth: 'username:password' +}, function(err, res) { + console.log(res.statusCode); +}); +``` + +In case you need to also send custom HTTP headers with the POST request, you can use the `headers` key in first parameter of `form.submit()`: + +``` javascript +form.submit({ + host: 'example.com', + path: '/surelynot.php', + headers: {'x-test-header': 'test-header-value'} +}, function(err, res) { + console.log(res.statusCode); +}); +``` + +### Methods + +- [_Void_ append( **String** _field_, **Mixed** _value_ [, **Mixed** _options_] )](https://github.com/form-data/form-data#void-append-string-field-mixed-value--mixed-options-). +- [_Headers_ getHeaders( [**Headers** _userHeaders_] )](https://github.com/form-data/form-data#array-getheaders-array-userheaders-) +- [_String_ getBoundary()](https://github.com/form-data/form-data#string-getboundary) +- [_Buffer_ getBuffer()](https://github.com/form-data/form-data#buffer-getbuffer) +- [_Integer_ getLengthSync()](https://github.com/form-data/form-data#integer-getlengthsync) +- [_Integer_ getLength( **function** _callback_ )](https://github.com/form-data/form-data#integer-getlength-function-callback-) +- [_Boolean_ hasKnownLength()](https://github.com/form-data/form-data#boolean-hasknownlength) +- [_Request_ submit( _params_, **function** _callback_ )](https://github.com/form-data/form-data#request-submit-params-function-callback-) +- [_String_ toString()](https://github.com/form-data/form-data#string-tostring) + +#### _Void_ append( **String** _field_, **Mixed** _value_ [, **Mixed** _options_] ) +Append data to the form. You can submit about any format (string, integer, boolean, buffer, etc.). However, Arrays are not supported and need to be turned into strings by the user. +```javascript +var form = new FormData(); +form.append( 'my_string', 'my value' ); +form.append( 'my_integer', 1 ); +form.append( 'my_boolean', true ); +form.append( 'my_buffer', new Buffer(10) ); +form.append( 'my_array_as_json', JSON.stringify( ['bird','cute'] ) ) +``` + +You may provide a string for options, or an object. +```javascript +// Set filename by providing a string for options +form.append( 'my_file', fs.createReadStream('/foo/bar.jpg'), 'bar.jpg' ); + +// provide an object. +form.append( 'my_file', fs.createReadStream('/foo/bar.jpg'), {filename: 'bar.jpg', contentType: 'image/jpeg', knownLength: 19806} ); +``` + +#### _Headers_ getHeaders( [**Headers** _userHeaders_] ) +This method adds the correct `content-type` header to the provided array of `userHeaders`. + +#### _String_ getBoundary() +Return the boundary of the formData. A boundary consists of 26 `-` followed by 24 numbers +for example: +```javascript +--------------------------515890814546601021194782 +``` +_Note: The boundary must be unique and may not appear in the data._ + +#### _Buffer_ getBuffer() +Return the full formdata request package, as a Buffer. You can insert this Buffer in e.g. Axios to send multipart data. +```javascript +var form = new FormData(); +form.append( 'my_buffer', Buffer.from([0x4a,0x42,0x20,0x52,0x6f,0x63,0x6b,0x73]) ); +form.append( 'my_file', fs.readFileSync('/foo/bar.jpg') ); + +axios.post( 'https://example.com/path/to/api', + form.getBuffer(), + form.getHeaders() + ) +``` +**Note:** Because the output is of type Buffer, you can only append types that are accepted by Buffer: *string, Buffer, ArrayBuffer, Array, or Array-like Object*. A ReadStream for example will result in an error. + +#### _Integer_ getLengthSync() +Same as `getLength` but synchronous. + +_Note: getLengthSync __doesn't__ calculate streams length._ + +#### _Integer_ getLength( **function** _callback_ ) +Returns the `Content-Length` async. The callback is used to handle errors and continue once the length has been calculated +```javascript +this.getLength(function(err, length) { + if (err) { + this._error(err); + return; + } + + // add content length + request.setHeader('Content-Length', length); + + ... +}.bind(this)); +``` + +#### _Boolean_ hasKnownLength() +Checks if the length of added values is known. + +#### _Request_ submit( _params_, **function** _callback_ ) +Submit the form to a web application. +```javascript +var form = new FormData(); +form.append( 'my_string', 'Hello World' ); + +form.submit( 'http://example.com/', function(err, res) { + // res – response object (http.IncomingMessage) // + res.resume(); +} ); +``` + +#### _String_ toString() +Returns the form data as a string. Don't use this if you are sending files or buffers, use `getBuffer()` instead. + +### Integration with other libraries + +#### Request + +Form submission using [request](https://github.com/request/request): + +```javascript +var formData = { + my_field: 'my_value', + my_file: fs.createReadStream(__dirname + '/unicycle.jpg'), +}; + +request.post({url:'http://service.com/upload', formData: formData}, function(err, httpResponse, body) { + if (err) { + return console.error('upload failed:', err); + } + console.log('Upload successful! Server responded with:', body); +}); +``` + +For more details see [request readme](https://github.com/request/request#multipartform-data-multipart-form-uploads). + +#### node-fetch + +You can also submit a form using [node-fetch](https://github.com/bitinn/node-fetch): + +```javascript +var form = new FormData(); + +form.append('a', 1); + +fetch('http://example.com', { method: 'POST', body: form }) + .then(function(res) { + return res.json(); + }).then(function(json) { + console.log(json); + }); +``` + +#### axios + +In Node.js you can post a file using [axios](https://github.com/axios/axios): +```javascript +const form = new FormData(); +const stream = fs.createReadStream(PATH_TO_FILE); + +form.append('image', stream); + +// In Node.js environment you need to set boundary in the header field 'Content-Type' by calling method `getHeaders` +const formHeaders = form.getHeaders(); + +axios.post('http://example.com', form, { + headers: { + ...formHeaders, + }, +}) +.then(response => response) +.catch(error => error) +``` + +## Notes + +- ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround. +- ```getLength(cb)``` will send an error as first parameter of callback if stream length cannot be calculated (e.g. send in custom streams w/o using ```knownLength```). +- ```sbumit``` will not add `content-length` if form length is unknown or not calculable. +- Starting version `2.x` FormData has dropped support for `node@0.10.x`. +- Starting version `3.x` FormData has dropped support for `node@4.x`. + +## License + +Form-Data is released under the [MIT](License) license. diff --git a/node_modules/@discordjs/form-data/index.d.ts b/node_modules/@discordjs/form-data/index.d.ts new file mode 100644 index 0000000..6e52045 --- /dev/null +++ b/node_modules/@discordjs/form-data/index.d.ts @@ -0,0 +1,61 @@ +// Definitions by: Carlos Ballesteros Velasco +// Leon Yu +// BendingBender +// Maple Miao + +/// +import * as stream from 'stream'; +import * as http from 'http'; + +export = FormData; + +// Extracted because @types/node doesn't export interfaces. +interface ReadableOptions { + highWaterMark?: number; + encoding?: string; + objectMode?: boolean; + read?(this: stream.Readable, size: number): void; + destroy?(this: stream.Readable, error: Error | null, callback: (error: Error | null) => void): void; + autoDestroy?: boolean; +} + +interface Options extends ReadableOptions { + writable?: boolean; + readable?: boolean; + dataSize?: number; + maxDataSize?: number; + pauseStreams?: boolean; +} + +declare class FormData extends stream.Readable { + constructor(options?: Options); + append(key: string, value: any, options?: FormData.AppendOptions | string): void; + getHeaders(userHeaders?: FormData.Headers): FormData.Headers; + submit( + params: string | FormData.SubmitOptions, + callback?: (error: Error | null, response: http.IncomingMessage) => void + ): http.ClientRequest; + getBuffer(): Buffer; + getBoundary(): string; + getLength(callback: (err: Error | null, length: number) => void): void; + getLengthSync(): number; + hasKnownLength(): boolean; +} + +declare namespace FormData { + interface Headers { + [key: string]: any; + } + + interface AppendOptions { + header?: string | Headers; + knownLength?: number; + filename?: string; + filepath?: string; + contentType?: string; + } + + interface SubmitOptions extends http.RequestOptions { + protocol?: 'https:' | 'http:'; + } +} diff --git a/node_modules/@discordjs/form-data/lib/browser.js b/node_modules/@discordjs/form-data/lib/browser.js new file mode 100644 index 0000000..09e7c70 --- /dev/null +++ b/node_modules/@discordjs/form-data/lib/browser.js @@ -0,0 +1,2 @@ +/* eslint-env browser */ +module.exports = typeof self == 'object' ? self.FormData : window.FormData; diff --git a/node_modules/@discordjs/form-data/lib/form_data.js b/node_modules/@discordjs/form-data/lib/form_data.js new file mode 100644 index 0000000..0b9d8c8 --- /dev/null +++ b/node_modules/@discordjs/form-data/lib/form_data.js @@ -0,0 +1,497 @@ +var CombinedStream = require('combined-stream'); +var util = require('util'); +var path = require('path'); +var http = require('http'); +var https = require('https'); +var parseUrl = require('url').parse; +var fs = require('fs'); +var Stream = require('stream').Stream; +var mime = require('mime-types'); +var asynckit = require('asynckit'); +var populate = require('./populate.js'); + +// Public API +module.exports = FormData; + +// make it a Stream +util.inherits(FormData, CombinedStream); + +/** + * Create readable "multipart/form-data" streams. + * Can be used to submit forms + * and file uploads to other web applications. + * + * @constructor + * @param {Object} options - Properties to be added/overriden for FormData and CombinedStream + */ +function FormData(options) { + if (!(this instanceof FormData)) { + return new FormData(options); + } + + this._overheadLength = 0; + this._valueLength = 0; + this._valuesToMeasure = []; + + CombinedStream.call(this); + + options = options || {}; + for (var option in options) { + this[option] = options[option]; + } +} + +FormData.LINE_BREAK = '\r\n'; +FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; + +FormData.prototype.append = function(field, value, options) { + + options = options || {}; + + // allow filename as single option + if (typeof options == 'string') { + options = {filename: options}; + } + + var append = CombinedStream.prototype.append.bind(this); + + // all that streamy business can't handle numbers + if (typeof value == 'number') { + value = '' + value; + } + + // https://github.com/felixge/node-form-data/issues/38 + if (util.isArray(value)) { + // Please convert your array into string + // the way web server expects it + this._error(new Error('Arrays are not supported.')); + return; + } + + var header = this._multiPartHeader(field, value, options); + var footer = this._multiPartFooter(); + + append(header); + append(value); + append(footer); + + // pass along options.knownLength + this._trackLength(header, value, options); +}; + +FormData.prototype._trackLength = function(header, value, options) { + var valueLength = 0; + + // used w/ getLengthSync(), when length is known. + // e.g. for streaming directly from a remote server, + // w/ a known file a size, and not wanting to wait for + // incoming file to finish to get its size. + if (options.knownLength != null) { + valueLength += +options.knownLength; + } else if (Buffer.isBuffer(value)) { + valueLength = value.length; + } else if (typeof value === 'string') { + valueLength = Buffer.byteLength(value); + } + + this._valueLength += valueLength; + + // @check why add CRLF? does this account for custom/multiple CRLFs? + this._overheadLength += + Buffer.byteLength(header) + + FormData.LINE_BREAK.length; + + // empty or either doesn't have path or not an http response or not a stream + if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) && !(value instanceof Stream))) { + return; + } + + // no need to bother with the length + if (!options.knownLength) { + this._valuesToMeasure.push(value); + } +}; + +FormData.prototype._lengthRetriever = function(value, callback) { + + if (value.hasOwnProperty('fd')) { + + // take read range into a account + // `end` = Infinity –> read file till the end + // + // TODO: Looks like there is bug in Node fs.createReadStream + // it doesn't respect `end` options without `start` options + // Fix it when node fixes it. + // https://github.com/joyent/node/issues/7819 + if (value.end != undefined && value.end != Infinity && value.start != undefined) { + + // when end specified + // no need to calculate range + // inclusive, starts with 0 + callback(null, value.end + 1 - (value.start ? value.start : 0)); + + // not that fast snoopy + } else { + // still need to fetch file size from fs + fs.stat(value.path, function(err, stat) { + + var fileSize; + + if (err) { + callback(err); + return; + } + + // update final size based on the range options + fileSize = stat.size - (value.start ? value.start : 0); + callback(null, fileSize); + }); + } + + // or http response + } else if (value.hasOwnProperty('httpVersion')) { + callback(null, +value.headers['content-length']); + + // or request stream http://github.com/mikeal/request + } else if (value.hasOwnProperty('httpModule')) { + // wait till response come back + value.on('response', function(response) { + value.pause(); + callback(null, +response.headers['content-length']); + }); + value.resume(); + + // something else + } else { + callback('Unknown stream'); + } +}; + +FormData.prototype._multiPartHeader = function(field, value, options) { + // custom header specified (as string)? + // it becomes responsible for boundary + // (e.g. to handle extra CRLFs on .NET servers) + if (typeof options.header == 'string') { + return options.header; + } + + var contentDisposition = this._getContentDisposition(value, options); + var contentType = this._getContentType(value, options); + + var contents = ''; + var headers = { + // add custom disposition as third element or keep it two elements if not + 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + // if no content type. allow it to be empty array + 'Content-Type': [].concat(contentType || []) + }; + + // allow custom headers. + if (typeof options.header == 'object') { + populate(headers, options.header); + } + + var header; + for (var prop in headers) { + if (!headers.hasOwnProperty(prop)) continue; + header = headers[prop]; + + // skip nullish headers. + if (header == null) { + continue; + } + + // convert all headers to arrays. + if (!Array.isArray(header)) { + header = [header]; + } + + // add non-empty headers. + if (header.length) { + contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK; + } + } + + return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; +}; + +FormData.prototype._getContentDisposition = function(value, options) { + + var filename + , contentDisposition + ; + + if (typeof options.filepath === 'string') { + // custom filepath for relative paths + filename = path.normalize(options.filepath).replace(/\\/g, '/'); + } else if (options.filename || value.name || value.path) { + // custom filename take precedence + // formidable and the browser add a name property + // fs- and request- streams have path property + filename = path.basename(options.filename || value.name || value.path); + } else if (value.readable && value.hasOwnProperty('httpVersion')) { + // or try http response + filename = path.basename(value.client._httpMessage.path || ''); + } + + if (filename) { + contentDisposition = 'filename="' + filename + '"'; + } + + return contentDisposition; +}; + +FormData.prototype._getContentType = function(value, options) { + + // use custom content-type above all + var contentType = options.contentType; + + // or try `name` from formidable, browser + if (!contentType && value.name) { + contentType = mime.lookup(value.name); + } + + // or try `path` from fs-, request- streams + if (!contentType && value.path) { + contentType = mime.lookup(value.path); + } + + // or if it's http-reponse + if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) { + contentType = value.headers['content-type']; + } + + // or guess it from the filepath or filename + if (!contentType && (options.filepath || options.filename)) { + contentType = mime.lookup(options.filepath || options.filename); + } + + // fallback to the default content type if `value` is not simple value + if (!contentType && typeof value == 'object') { + contentType = FormData.DEFAULT_CONTENT_TYPE; + } + + return contentType; +}; + +FormData.prototype._multiPartFooter = function() { + return function(next) { + var footer = FormData.LINE_BREAK; + + var lastPart = (this._streams.length === 0); + if (lastPart) { + footer += this._lastBoundary(); + } + + next(footer); + }.bind(this); +}; + +FormData.prototype._lastBoundary = function() { + return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; +}; + +FormData.prototype.getHeaders = function(userHeaders) { + var header; + var formHeaders = { + 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() + }; + + for (header in userHeaders) { + if (userHeaders.hasOwnProperty(header)) { + formHeaders[header.toLowerCase()] = userHeaders[header]; + } + } + + return formHeaders; +}; + +FormData.prototype.getBoundary = function() { + if (!this._boundary) { + this._generateBoundary(); + } + + return this._boundary; +}; + +FormData.prototype.getBuffer = function() { + var dataBuffer = new Buffer.alloc( 0 ); + var boundary = this.getBoundary(); + + // Create the form content. Add Line breaks to the end of data. + for (var i = 0, len = this._streams.length; i < len; i++) { + if (typeof this._streams[i] !== 'function') { + + // Add content to the buffer. + if(Buffer.isBuffer(this._streams[i])) { + dataBuffer = Buffer.concat( [dataBuffer, this._streams[i]]); + }else { + dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(this._streams[i])]); + } + + // Add break after content. + if (typeof this._streams[i] !== 'string' || this._streams[i].substring( 2, boundary.length + 2 ) !== boundary) { + dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(FormData.LINE_BREAK)] ); + } + } + } + + // Add the footer and return the Buffer object. + return Buffer.concat( [dataBuffer, Buffer.from(this._lastBoundary())] ); +}; + +FormData.prototype._generateBoundary = function() { + // This generates a 50 character boundary similar to those used by Firefox. + // They are optimized for boyer-moore parsing. + var boundary = '--------------------------'; + for (var i = 0; i < 24; i++) { + boundary += Math.floor(Math.random() * 10).toString(16); + } + + this._boundary = boundary; +}; + +// Note: getLengthSync DOESN'T calculate streams length +// As workaround one can calculate file size manually +// and add it as knownLength option +FormData.prototype.getLengthSync = function() { + var knownLength = this._overheadLength + this._valueLength; + + // Don't get confused, there are 3 "internal" streams for each keyval pair + // so it basically checks if there is any value added to the form + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + // https://github.com/form-data/form-data/issues/40 + if (!this.hasKnownLength()) { + // Some async length retrievers are present + // therefore synchronous length calculation is false. + // Please use getLength(callback) to get proper length + this._error(new Error('Cannot calculate proper length in synchronous way.')); + } + + return knownLength; +}; + +// Public API to check if length of added values is known +// https://github.com/form-data/form-data/issues/196 +// https://github.com/form-data/form-data/issues/262 +FormData.prototype.hasKnownLength = function() { + var hasKnownLength = true; + + if (this._valuesToMeasure.length) { + hasKnownLength = false; + } + + return hasKnownLength; +}; + +FormData.prototype.getLength = function(cb) { + var knownLength = this._overheadLength + this._valueLength; + + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + if (!this._valuesToMeasure.length) { + process.nextTick(cb.bind(this, null, knownLength)); + return; + } + + asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { + if (err) { + cb(err); + return; + } + + values.forEach(function(length) { + knownLength += length; + }); + + cb(null, knownLength); + }); +}; + +FormData.prototype.submit = function(params, cb) { + var request + , options + , defaults = {method: 'post'} + ; + + // parse provided url if it's string + // or treat it as options object + if (typeof params == 'string') { + + params = parseUrl(params); + options = populate({ + port: params.port, + path: params.pathname, + host: params.hostname, + protocol: params.protocol + }, defaults); + + // use custom params + } else { + + options = populate(params, defaults); + // if no port provided use default one + if (!options.port) { + options.port = options.protocol == 'https:' ? 443 : 80; + } + } + + // put that good code in getHeaders to some use + options.headers = this.getHeaders(params.headers); + + // https if specified, fallback to http in any other case + if (options.protocol == 'https:') { + request = https.request(options); + } else { + request = http.request(options); + } + + // get content length and fire away + this.getLength(function(err, length) { + if (err && err !== 'Unknown stream') { + this._error(err); + return; + } + + // add content length + if (length) { + request.setHeader('Content-Length', length); + } + + this.pipe(request); + if (cb) { + var onResponse; + + var callback = function (error, responce) { + request.removeListener('error', callback); + request.removeListener('response', onResponse); + + return cb.call(this, error, responce); + }; + + onResponse = callback.bind(this, null); + + request.on('error', callback); + request.on('response', onResponse); + } + }.bind(this)); + + return request; +}; + +FormData.prototype._error = function(err) { + if (!this.error) { + this.error = err; + this.pause(); + this.emit('error', err); + } +}; + +FormData.prototype.toString = function () { + return '[object FormData]'; +}; diff --git a/node_modules/@discordjs/form-data/lib/populate.js b/node_modules/@discordjs/form-data/lib/populate.js new file mode 100644 index 0000000..4d35738 --- /dev/null +++ b/node_modules/@discordjs/form-data/lib/populate.js @@ -0,0 +1,10 @@ +// populates missing values +module.exports = function(dst, src) { + + Object.keys(src).forEach(function(prop) + { + dst[prop] = dst[prop] || src[prop]; + }); + + return dst; +}; diff --git a/node_modules/@discordjs/form-data/package.json b/node_modules/@discordjs/form-data/package.json new file mode 100644 index 0000000..d230714 --- /dev/null +++ b/node_modules/@discordjs/form-data/package.json @@ -0,0 +1,98 @@ +{ + "_from": "@discordjs/form-data@^3.0.1", + "_id": "@discordjs/form-data@3.0.1", + "_inBundle": false, + "_integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", + "_location": "/@discordjs/form-data", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "@discordjs/form-data@^3.0.1", + "name": "@discordjs/form-data", + "escapedName": "@discordjs%2fform-data", + "scope": "@discordjs", + "rawSpec": "^3.0.1", + "saveSpec": null, + "fetchSpec": "^3.0.1" + }, + "_requiredBy": [ + "/discord.js" + ], + "_resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", + "_shasum": "5c9e6be992e2e57d0dfa0e39979a850225fb4697", + "_spec": "@discordjs/form-data@^3.0.1", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\discord.js", + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "browser": "./lib/browser", + "bugs": { + "url": "https://github.com/form-data/form-data/issues" + }, + "bundleDependencies": false, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "deprecated": false, + "description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", + "devDependencies": { + "@types/node": "^12.0.10", + "browserify": "^13.1.1", + "browserify-istanbul": "^2.0.0", + "coveralls": "^3.0.4", + "cross-spawn": "^6.0.5", + "eslint": "^6.0.1", + "fake": "^0.2.2", + "far": "^0.0.7", + "formidable": "^1.0.17", + "in-publish": "^2.0.0", + "is-node-modern": "^1.0.0", + "istanbul": "^0.4.5", + "obake": "^0.1.2", + "pkgfiles": "^2.3.0", + "pre-commit": "^1.1.3", + "puppeteer": "^1.19.0", + "request": "^2.88.0", + "rimraf": "^2.7.1", + "tape": "^4.6.2", + "typescript": "^3.5.2" + }, + "engines": { + "node": ">= 6" + }, + "homepage": "https://github.com/form-data/form-data#readme", + "license": "MIT", + "main": "./lib/form_data", + "name": "@discordjs/form-data", + "pre-commit": [ + "lint", + "ci-test", + "check" + ], + "repository": { + "type": "git", + "url": "git://github.com/form-data/form-data.git" + }, + "scripts": { + "browser": "browserify -t browserify-istanbul test/run-browser.js | obake --coverage", + "check": "istanbul check-coverage coverage/coverage*.json", + "ci-lint": "is-node-modern 8 && npm run lint || is-node-not-modern 8", + "ci-test": "npm run test && npm run browser && npm run report", + "debug": "verbose=1 ./test/run.js", + "files": "pkgfiles --sort=name", + "get-version": "node -e \"console.log(require('./package.json').version)\"", + "lint": "eslint lib/*.js test/*.js test/integration/*.js", + "posttest": "istanbul report lcov text", + "predebug": "rimraf coverage test/tmp", + "pretest": "rimraf coverage test/tmp", + "report": "istanbul report lcov text", + "test": "istanbul cover test/run.js" + }, + "typings": "./index.d.ts", + "version": "3.0.1" +} diff --git a/node_modules/abort-controller/LICENSE b/node_modules/abort-controller/LICENSE new file mode 100644 index 0000000..c914149 --- /dev/null +++ b/node_modules/abort-controller/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Toru Nagashima + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/abort-controller/README.md b/node_modules/abort-controller/README.md new file mode 100644 index 0000000..9de3e45 --- /dev/null +++ b/node_modules/abort-controller/README.md @@ -0,0 +1,98 @@ +# abort-controller + +[![npm version](https://img.shields.io/npm/v/abort-controller.svg)](https://www.npmjs.com/package/abort-controller) +[![Downloads/month](https://img.shields.io/npm/dm/abort-controller.svg)](http://www.npmtrends.com/abort-controller) +[![Build Status](https://travis-ci.org/mysticatea/abort-controller.svg?branch=master)](https://travis-ci.org/mysticatea/abort-controller) +[![Coverage Status](https://codecov.io/gh/mysticatea/abort-controller/branch/master/graph/badge.svg)](https://codecov.io/gh/mysticatea/abort-controller) +[![Dependency Status](https://david-dm.org/mysticatea/abort-controller.svg)](https://david-dm.org/mysticatea/abort-controller) + +An implementation of [WHATWG AbortController interface](https://dom.spec.whatwg.org/#interface-abortcontroller). + +```js +import AbortController from "abort-controller" + +const controller = new AbortController() +const signal = controller.signal + +signal.addEventListener("abort", () => { + console.log("aborted!") +}) + +controller.abort() +``` + +> https://jsfiddle.net/1r2994qp/1/ + +## 💿 Installation + +Use [npm](https://www.npmjs.com/) to install then use a bundler. + +``` +npm install abort-controller +``` + +Or download from [`dist` directory](./dist). + +- [dist/abort-controller.mjs](dist/abort-controller.mjs) ... ES modules version. +- [dist/abort-controller.js](dist/abort-controller.js) ... Common JS version. +- [dist/abort-controller.umd.js](dist/abort-controller.umd.js) ... UMD (Universal Module Definition) version. This is transpiled by [Babel](https://babeljs.io/) for IE 11. + +## 📖 Usage + +### Basic + +```js +import AbortController from "abort-controller" +// or +const AbortController = require("abort-controller") + +// or UMD version defines a global variable: +const AbortController = window.AbortControllerShim +``` + +If your bundler recognizes `browser` field of `package.json`, the imported `AbortController` is the native one and it doesn't contain shim (even if the native implementation was nothing). +If you wanted to polyfill `AbortController` for IE, use `abort-controller/polyfill`. + +### Polyfilling + +Importing `abort-controller/polyfill` assigns the `AbortController` shim to the `AbortController` global variable if the native implementation was nothing. + +```js +import "abort-controller/polyfill" +// or +require("abort-controller/polyfill") +``` + +### API + +#### AbortController + +> https://dom.spec.whatwg.org/#interface-abortcontroller + +##### controller.signal + +The [AbortSignal](https://dom.spec.whatwg.org/#interface-AbortSignal) object which is associated to this controller. + +##### controller.abort() + +Notify `abort` event to listeners that the `signal` has. + +## 📰 Changelog + +- See [GitHub releases](https://github.com/mysticatea/abort-controller/releases). + +## 🍻 Contributing + +Contributing is welcome ❤️ + +Please use GitHub issues/PRs. + +### Development tools + +- `npm install` installs dependencies for development. +- `npm test` runs tests and measures code coverage. +- `npm run clean` removes temporary files of tests. +- `npm run coverage` opens code coverage of the previous test with your default browser. +- `npm run lint` runs ESLint. +- `npm run build` generates `dist` codes. +- `npm run watch` runs tests on each file change. diff --git a/node_modules/abort-controller/browser.js b/node_modules/abort-controller/browser.js new file mode 100644 index 0000000..b0c5ec3 --- /dev/null +++ b/node_modules/abort-controller/browser.js @@ -0,0 +1,13 @@ +/*globals self, window */ +"use strict" + +/*eslint-disable @mysticatea/prettier */ +const { AbortController, AbortSignal } = + typeof self !== "undefined" ? self : + typeof window !== "undefined" ? window : + /* otherwise */ undefined +/*eslint-enable @mysticatea/prettier */ + +module.exports = AbortController +module.exports.AbortSignal = AbortSignal +module.exports.default = AbortController diff --git a/node_modules/abort-controller/browser.mjs b/node_modules/abort-controller/browser.mjs new file mode 100644 index 0000000..a8f321a --- /dev/null +++ b/node_modules/abort-controller/browser.mjs @@ -0,0 +1,11 @@ +/*globals self, window */ + +/*eslint-disable @mysticatea/prettier */ +const { AbortController, AbortSignal } = + typeof self !== "undefined" ? self : + typeof window !== "undefined" ? window : + /* otherwise */ undefined +/*eslint-enable @mysticatea/prettier */ + +export default AbortController +export { AbortController, AbortSignal } diff --git a/node_modules/abort-controller/dist/abort-controller.d.ts b/node_modules/abort-controller/dist/abort-controller.d.ts new file mode 100644 index 0000000..75852fb --- /dev/null +++ b/node_modules/abort-controller/dist/abort-controller.d.ts @@ -0,0 +1,43 @@ +import { EventTarget } from "event-target-shim" + +type Events = { + abort: any +} +type EventAttributes = { + onabort: any +} +/** + * The signal class. + * @see https://dom.spec.whatwg.org/#abortsignal + */ +declare class AbortSignal extends EventTarget { + /** + * AbortSignal cannot be constructed directly. + */ + constructor() + /** + * Returns `true` if this `AbortSignal`"s `AbortController` has signaled to abort, and `false` otherwise. + */ + readonly aborted: boolean +} +/** + * The AbortController. + * @see https://dom.spec.whatwg.org/#abortcontroller + */ +declare class AbortController { + /** + * Initialize this controller. + */ + constructor() + /** + * Returns the `AbortSignal` object associated with this object. + */ + readonly signal: AbortSignal + /** + * Abort and signal to any observers that the associated activity is to be aborted. + */ + abort(): void +} + +export default AbortController +export { AbortController, AbortSignal } diff --git a/node_modules/abort-controller/dist/abort-controller.js b/node_modules/abort-controller/dist/abort-controller.js new file mode 100644 index 0000000..49af739 --- /dev/null +++ b/node_modules/abort-controller/dist/abort-controller.js @@ -0,0 +1,127 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var eventTargetShim = require('event-target-shim'); + +/** + * The signal class. + * @see https://dom.spec.whatwg.org/#abortsignal + */ +class AbortSignal extends eventTargetShim.EventTarget { + /** + * AbortSignal cannot be constructed directly. + */ + constructor() { + super(); + throw new TypeError("AbortSignal cannot be constructed directly"); + } + /** + * Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise. + */ + get aborted() { + const aborted = abortedFlags.get(this); + if (typeof aborted !== "boolean") { + throw new TypeError(`Expected 'this' to be an 'AbortSignal' object, but got ${this === null ? "null" : typeof this}`); + } + return aborted; + } +} +eventTargetShim.defineEventAttribute(AbortSignal.prototype, "abort"); +/** + * Create an AbortSignal object. + */ +function createAbortSignal() { + const signal = Object.create(AbortSignal.prototype); + eventTargetShim.EventTarget.call(signal); + abortedFlags.set(signal, false); + return signal; +} +/** + * Abort a given signal. + */ +function abortSignal(signal) { + if (abortedFlags.get(signal) !== false) { + return; + } + abortedFlags.set(signal, true); + signal.dispatchEvent({ type: "abort" }); +} +/** + * Aborted flag for each instances. + */ +const abortedFlags = new WeakMap(); +// Properties should be enumerable. +Object.defineProperties(AbortSignal.prototype, { + aborted: { enumerable: true }, +}); +// `toString()` should return `"[object AbortSignal]"` +if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") { + Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, { + configurable: true, + value: "AbortSignal", + }); +} + +/** + * The AbortController. + * @see https://dom.spec.whatwg.org/#abortcontroller + */ +class AbortController { + /** + * Initialize this controller. + */ + constructor() { + signals.set(this, createAbortSignal()); + } + /** + * Returns the `AbortSignal` object associated with this object. + */ + get signal() { + return getSignal(this); + } + /** + * Abort and signal to any observers that the associated activity is to be aborted. + */ + abort() { + abortSignal(getSignal(this)); + } +} +/** + * Associated signals. + */ +const signals = new WeakMap(); +/** + * Get the associated signal of a given controller. + */ +function getSignal(controller) { + const signal = signals.get(controller); + if (signal == null) { + throw new TypeError(`Expected 'this' to be an 'AbortController' object, but got ${controller === null ? "null" : typeof controller}`); + } + return signal; +} +// Properties should be enumerable. +Object.defineProperties(AbortController.prototype, { + signal: { enumerable: true }, + abort: { enumerable: true }, +}); +if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") { + Object.defineProperty(AbortController.prototype, Symbol.toStringTag, { + configurable: true, + value: "AbortController", + }); +} + +exports.AbortController = AbortController; +exports.AbortSignal = AbortSignal; +exports.default = AbortController; + +module.exports = AbortController +module.exports.AbortController = module.exports["default"] = AbortController +module.exports.AbortSignal = AbortSignal +//# sourceMappingURL=abort-controller.js.map diff --git a/node_modules/abort-controller/dist/abort-controller.js.map b/node_modules/abort-controller/dist/abort-controller.js.map new file mode 100644 index 0000000..cfdcafd --- /dev/null +++ b/node_modules/abort-controller/dist/abort-controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"abort-controller.js","sources":["../src/abort-signal.ts","../src/abort-controller.ts"],"sourcesContent":["import {\n // Event,\n EventTarget,\n // Type,\n defineEventAttribute,\n} from \"event-target-shim\"\n\n// Known Limitation\n// Use `any` because the type of `AbortSignal` in `lib.dom.d.ts` is wrong and\n// to make assignable our `AbortSignal` into that.\n// https://github.com/Microsoft/TSJS-lib-generator/pull/623\ntype Events = {\n abort: any // Event & Type<\"abort\">\n}\ntype EventAttributes = {\n onabort: any // Event & Type<\"abort\">\n}\n\n/**\n * The signal class.\n * @see https://dom.spec.whatwg.org/#abortsignal\n */\nexport default class AbortSignal extends EventTarget {\n /**\n * AbortSignal cannot be constructed directly.\n */\n public constructor() {\n super()\n throw new TypeError(\"AbortSignal cannot be constructed directly\")\n }\n\n /**\n * Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise.\n */\n public get aborted(): boolean {\n const aborted = abortedFlags.get(this)\n if (typeof aborted !== \"boolean\") {\n throw new TypeError(\n `Expected 'this' to be an 'AbortSignal' object, but got ${\n this === null ? \"null\" : typeof this\n }`,\n )\n }\n return aborted\n }\n}\ndefineEventAttribute(AbortSignal.prototype, \"abort\")\n\n/**\n * Create an AbortSignal object.\n */\nexport function createAbortSignal(): AbortSignal {\n const signal = Object.create(AbortSignal.prototype)\n EventTarget.call(signal)\n abortedFlags.set(signal, false)\n return signal\n}\n\n/**\n * Abort a given signal.\n */\nexport function abortSignal(signal: AbortSignal): void {\n if (abortedFlags.get(signal) !== false) {\n return\n }\n\n abortedFlags.set(signal, true)\n signal.dispatchEvent<\"abort\">({ type: \"abort\" })\n}\n\n/**\n * Aborted flag for each instances.\n */\nconst abortedFlags = new WeakMap()\n\n// Properties should be enumerable.\nObject.defineProperties(AbortSignal.prototype, {\n aborted: { enumerable: true },\n})\n\n// `toString()` should return `\"[object AbortSignal]\"`\nif (typeof Symbol === \"function\" && typeof Symbol.toStringTag === \"symbol\") {\n Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {\n configurable: true,\n value: \"AbortSignal\",\n })\n}\n","import AbortSignal, { abortSignal, createAbortSignal } from \"./abort-signal\"\n\n/**\n * The AbortController.\n * @see https://dom.spec.whatwg.org/#abortcontroller\n */\nexport default class AbortController {\n /**\n * Initialize this controller.\n */\n public constructor() {\n signals.set(this, createAbortSignal())\n }\n\n /**\n * Returns the `AbortSignal` object associated with this object.\n */\n public get signal(): AbortSignal {\n return getSignal(this)\n }\n\n /**\n * Abort and signal to any observers that the associated activity is to be aborted.\n */\n public abort(): void {\n abortSignal(getSignal(this))\n }\n}\n\n/**\n * Associated signals.\n */\nconst signals = new WeakMap()\n\n/**\n * Get the associated signal of a given controller.\n */\nfunction getSignal(controller: AbortController): AbortSignal {\n const signal = signals.get(controller)\n if (signal == null) {\n throw new TypeError(\n `Expected 'this' to be an 'AbortController' object, but got ${\n controller === null ? \"null\" : typeof controller\n }`,\n )\n }\n return signal\n}\n\n// Properties should be enumerable.\nObject.defineProperties(AbortController.prototype, {\n signal: { enumerable: true },\n abort: { enumerable: true },\n})\n\nif (typeof Symbol === \"function\" && typeof Symbol.toStringTag === \"symbol\") {\n Object.defineProperty(AbortController.prototype, Symbol.toStringTag, {\n configurable: true,\n value: \"AbortController\",\n })\n}\n\nexport { AbortController, AbortSignal }\n"],"names":["EventTarget","defineEventAttribute"],"mappings":";;;;;;;;;;AAkBA;;;;AAIA,MAAqB,WAAY,SAAQA,2BAAoC;;;;IAIzE;QACI,KAAK,EAAE,CAAA;QACP,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAA;KACpE;;;;IAKD,IAAW,OAAO;QACd,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACtC,IAAI,OAAO,OAAO,KAAK,SAAS,EAAE;YAC9B,MAAM,IAAI,SAAS,CACf,0DACI,IAAI,KAAK,IAAI,GAAG,MAAM,GAAG,OAAO,IACpC,EAAE,CACL,CAAA;SACJ;QACD,OAAO,OAAO,CAAA;KACjB;CACJ;AACDC,oCAAoB,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;;;;AAKpD,SAAgB,iBAAiB;IAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;IACnDD,2BAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACxB,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC/B,OAAO,MAAM,CAAA;CAChB;;;;AAKD,SAAgB,WAAW,CAAC,MAAmB;IAC3C,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE;QACpC,OAAM;KACT;IAED,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC9B,MAAM,CAAC,aAAa,CAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;CACnD;;;;AAKD,MAAM,YAAY,GAAG,IAAI,OAAO,EAAwB,CAAA;;AAGxD,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,SAAS,EAAE;IAC3C,OAAO,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;CAChC,CAAC,CAAA;;AAGF,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;IACxE,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,EAAE;QAC7D,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,aAAa;KACvB,CAAC,CAAA;CACL;;ACpFD;;;;AAIA,MAAqB,eAAe;;;;IAIhC;QACI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAA;KACzC;;;;IAKD,IAAW,MAAM;QACb,OAAO,SAAS,CAAC,IAAI,CAAC,CAAA;KACzB;;;;IAKM,KAAK;QACR,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;KAC/B;CACJ;;;;AAKD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAgC,CAAA;;;;AAK3D,SAAS,SAAS,CAAC,UAA2B;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACtC,IAAI,MAAM,IAAI,IAAI,EAAE;QAChB,MAAM,IAAI,SAAS,CACf,8DACI,UAAU,KAAK,IAAI,GAAG,MAAM,GAAG,OAAO,UAC1C,EAAE,CACL,CAAA;KACJ;IACD,OAAO,MAAM,CAAA;CAChB;;AAGD,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,SAAS,EAAE;IAC/C,MAAM,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;IAC5B,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;CAC9B,CAAC,CAAA;AAEF,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;IACxE,MAAM,CAAC,cAAc,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,EAAE;QACjE,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,iBAAiB;KAC3B,CAAC,CAAA;CACL;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/node_modules/abort-controller/dist/abort-controller.mjs b/node_modules/abort-controller/dist/abort-controller.mjs new file mode 100644 index 0000000..88ba22d --- /dev/null +++ b/node_modules/abort-controller/dist/abort-controller.mjs @@ -0,0 +1,118 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */ +import { EventTarget, defineEventAttribute } from 'event-target-shim'; + +/** + * The signal class. + * @see https://dom.spec.whatwg.org/#abortsignal + */ +class AbortSignal extends EventTarget { + /** + * AbortSignal cannot be constructed directly. + */ + constructor() { + super(); + throw new TypeError("AbortSignal cannot be constructed directly"); + } + /** + * Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise. + */ + get aborted() { + const aborted = abortedFlags.get(this); + if (typeof aborted !== "boolean") { + throw new TypeError(`Expected 'this' to be an 'AbortSignal' object, but got ${this === null ? "null" : typeof this}`); + } + return aborted; + } +} +defineEventAttribute(AbortSignal.prototype, "abort"); +/** + * Create an AbortSignal object. + */ +function createAbortSignal() { + const signal = Object.create(AbortSignal.prototype); + EventTarget.call(signal); + abortedFlags.set(signal, false); + return signal; +} +/** + * Abort a given signal. + */ +function abortSignal(signal) { + if (abortedFlags.get(signal) !== false) { + return; + } + abortedFlags.set(signal, true); + signal.dispatchEvent({ type: "abort" }); +} +/** + * Aborted flag for each instances. + */ +const abortedFlags = new WeakMap(); +// Properties should be enumerable. +Object.defineProperties(AbortSignal.prototype, { + aborted: { enumerable: true }, +}); +// `toString()` should return `"[object AbortSignal]"` +if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") { + Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, { + configurable: true, + value: "AbortSignal", + }); +} + +/** + * The AbortController. + * @see https://dom.spec.whatwg.org/#abortcontroller + */ +class AbortController { + /** + * Initialize this controller. + */ + constructor() { + signals.set(this, createAbortSignal()); + } + /** + * Returns the `AbortSignal` object associated with this object. + */ + get signal() { + return getSignal(this); + } + /** + * Abort and signal to any observers that the associated activity is to be aborted. + */ + abort() { + abortSignal(getSignal(this)); + } +} +/** + * Associated signals. + */ +const signals = new WeakMap(); +/** + * Get the associated signal of a given controller. + */ +function getSignal(controller) { + const signal = signals.get(controller); + if (signal == null) { + throw new TypeError(`Expected 'this' to be an 'AbortController' object, but got ${controller === null ? "null" : typeof controller}`); + } + return signal; +} +// Properties should be enumerable. +Object.defineProperties(AbortController.prototype, { + signal: { enumerable: true }, + abort: { enumerable: true }, +}); +if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") { + Object.defineProperty(AbortController.prototype, Symbol.toStringTag, { + configurable: true, + value: "AbortController", + }); +} + +export default AbortController; +export { AbortController, AbortSignal }; +//# sourceMappingURL=abort-controller.mjs.map diff --git a/node_modules/abort-controller/dist/abort-controller.mjs.map b/node_modules/abort-controller/dist/abort-controller.mjs.map new file mode 100644 index 0000000..1e8fa6b --- /dev/null +++ b/node_modules/abort-controller/dist/abort-controller.mjs.map @@ -0,0 +1 @@ +{"version":3,"file":"abort-controller.mjs","sources":["../src/abort-signal.ts","../src/abort-controller.ts"],"sourcesContent":["import {\n // Event,\n EventTarget,\n // Type,\n defineEventAttribute,\n} from \"event-target-shim\"\n\n// Known Limitation\n// Use `any` because the type of `AbortSignal` in `lib.dom.d.ts` is wrong and\n// to make assignable our `AbortSignal` into that.\n// https://github.com/Microsoft/TSJS-lib-generator/pull/623\ntype Events = {\n abort: any // Event & Type<\"abort\">\n}\ntype EventAttributes = {\n onabort: any // Event & Type<\"abort\">\n}\n\n/**\n * The signal class.\n * @see https://dom.spec.whatwg.org/#abortsignal\n */\nexport default class AbortSignal extends EventTarget {\n /**\n * AbortSignal cannot be constructed directly.\n */\n public constructor() {\n super()\n throw new TypeError(\"AbortSignal cannot be constructed directly\")\n }\n\n /**\n * Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise.\n */\n public get aborted(): boolean {\n const aborted = abortedFlags.get(this)\n if (typeof aborted !== \"boolean\") {\n throw new TypeError(\n `Expected 'this' to be an 'AbortSignal' object, but got ${\n this === null ? \"null\" : typeof this\n }`,\n )\n }\n return aborted\n }\n}\ndefineEventAttribute(AbortSignal.prototype, \"abort\")\n\n/**\n * Create an AbortSignal object.\n */\nexport function createAbortSignal(): AbortSignal {\n const signal = Object.create(AbortSignal.prototype)\n EventTarget.call(signal)\n abortedFlags.set(signal, false)\n return signal\n}\n\n/**\n * Abort a given signal.\n */\nexport function abortSignal(signal: AbortSignal): void {\n if (abortedFlags.get(signal) !== false) {\n return\n }\n\n abortedFlags.set(signal, true)\n signal.dispatchEvent<\"abort\">({ type: \"abort\" })\n}\n\n/**\n * Aborted flag for each instances.\n */\nconst abortedFlags = new WeakMap()\n\n// Properties should be enumerable.\nObject.defineProperties(AbortSignal.prototype, {\n aborted: { enumerable: true },\n})\n\n// `toString()` should return `\"[object AbortSignal]\"`\nif (typeof Symbol === \"function\" && typeof Symbol.toStringTag === \"symbol\") {\n Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {\n configurable: true,\n value: \"AbortSignal\",\n })\n}\n","import AbortSignal, { abortSignal, createAbortSignal } from \"./abort-signal\"\n\n/**\n * The AbortController.\n * @see https://dom.spec.whatwg.org/#abortcontroller\n */\nexport default class AbortController {\n /**\n * Initialize this controller.\n */\n public constructor() {\n signals.set(this, createAbortSignal())\n }\n\n /**\n * Returns the `AbortSignal` object associated with this object.\n */\n public get signal(): AbortSignal {\n return getSignal(this)\n }\n\n /**\n * Abort and signal to any observers that the associated activity is to be aborted.\n */\n public abort(): void {\n abortSignal(getSignal(this))\n }\n}\n\n/**\n * Associated signals.\n */\nconst signals = new WeakMap()\n\n/**\n * Get the associated signal of a given controller.\n */\nfunction getSignal(controller: AbortController): AbortSignal {\n const signal = signals.get(controller)\n if (signal == null) {\n throw new TypeError(\n `Expected 'this' to be an 'AbortController' object, but got ${\n controller === null ? \"null\" : typeof controller\n }`,\n )\n }\n return signal\n}\n\n// Properties should be enumerable.\nObject.defineProperties(AbortController.prototype, {\n signal: { enumerable: true },\n abort: { enumerable: true },\n})\n\nif (typeof Symbol === \"function\" && typeof Symbol.toStringTag === \"symbol\") {\n Object.defineProperty(AbortController.prototype, Symbol.toStringTag, {\n configurable: true,\n value: \"AbortController\",\n })\n}\n\nexport { AbortController, AbortSignal }\n"],"names":[],"mappings":";;;;;;AAkBA;;;;AAIA,MAAqB,WAAY,SAAQ,WAAoC;;;;IAIzE;QACI,KAAK,EAAE,CAAA;QACP,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAA;KACpE;;;;IAKD,IAAW,OAAO;QACd,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACtC,IAAI,OAAO,OAAO,KAAK,SAAS,EAAE;YAC9B,MAAM,IAAI,SAAS,CACf,0DACI,IAAI,KAAK,IAAI,GAAG,MAAM,GAAG,OAAO,IACpC,EAAE,CACL,CAAA;SACJ;QACD,OAAO,OAAO,CAAA;KACjB;CACJ;AACD,oBAAoB,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;;;;AAKpD,SAAgB,iBAAiB;IAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;IACnD,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACxB,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC/B,OAAO,MAAM,CAAA;CAChB;;;;AAKD,SAAgB,WAAW,CAAC,MAAmB;IAC3C,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE;QACpC,OAAM;KACT;IAED,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC9B,MAAM,CAAC,aAAa,CAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;CACnD;;;;AAKD,MAAM,YAAY,GAAG,IAAI,OAAO,EAAwB,CAAA;;AAGxD,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,SAAS,EAAE;IAC3C,OAAO,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;CAChC,CAAC,CAAA;;AAGF,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;IACxE,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,EAAE;QAC7D,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,aAAa;KACvB,CAAC,CAAA;CACL;;ACpFD;;;;AAIA,MAAqB,eAAe;;;;IAIhC;QACI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAA;KACzC;;;;IAKD,IAAW,MAAM;QACb,OAAO,SAAS,CAAC,IAAI,CAAC,CAAA;KACzB;;;;IAKM,KAAK;QACR,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;KAC/B;CACJ;;;;AAKD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAgC,CAAA;;;;AAK3D,SAAS,SAAS,CAAC,UAA2B;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACtC,IAAI,MAAM,IAAI,IAAI,EAAE;QAChB,MAAM,IAAI,SAAS,CACf,8DACI,UAAU,KAAK,IAAI,GAAG,MAAM,GAAG,OAAO,UAC1C,EAAE,CACL,CAAA;KACJ;IACD,OAAO,MAAM,CAAA;CAChB;;AAGD,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,SAAS,EAAE;IAC/C,MAAM,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;IAC5B,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;CAC9B,CAAC,CAAA;AAEF,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;IACxE,MAAM,CAAC,cAAc,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,EAAE;QACjE,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,iBAAiB;KAC3B,CAAC,CAAA;CACL;;;;;"} \ No newline at end of file diff --git a/node_modules/abort-controller/dist/abort-controller.umd.js b/node_modules/abort-controller/dist/abort-controller.umd.js new file mode 100644 index 0000000..f643cfd --- /dev/null +++ b/node_modules/abort-controller/dist/abort-controller.umd.js @@ -0,0 +1,5 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */(function(a,b){"object"==typeof exports&&"undefined"!=typeof module?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a=a||self,b(a.AbortControllerShim={}))})(this,function(a){'use strict';function b(a){return b="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},b(a)}function c(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function d(a,b){for(var c,d=0;d\n * @copyright 2015 Toru Nagashima. All rights reserved.\n * See LICENSE file in root directory for full license.\n */\n/**\n * @typedef {object} PrivateData\n * @property {EventTarget} eventTarget The event target.\n * @property {{type:string}} event The original event object.\n * @property {number} eventPhase The current event phase.\n * @property {EventTarget|null} currentTarget The current event target.\n * @property {boolean} canceled The flag to prevent default.\n * @property {boolean} stopped The flag to stop propagation.\n * @property {boolean} immediateStopped The flag to stop propagation immediately.\n * @property {Function|null} passiveListener The listener if the current listener is passive. Otherwise this is null.\n * @property {number} timeStamp The unix time.\n * @private\n */\n\n/**\n * Private data for event wrappers.\n * @type {WeakMap}\n * @private\n */\nconst privateData = new WeakMap();\n\n/**\n * Cache for wrapper classes.\n * @type {WeakMap}\n * @private\n */\nconst wrappers = new WeakMap();\n\n/**\n * Get private data.\n * @param {Event} event The event object to get private data.\n * @returns {PrivateData} The private data of the event.\n * @private\n */\nfunction pd(event) {\n const retv = privateData.get(event);\n console.assert(\n retv != null,\n \"'this' is expected an Event object, but got\",\n event\n );\n return retv\n}\n\n/**\n * https://dom.spec.whatwg.org/#set-the-canceled-flag\n * @param data {PrivateData} private data.\n */\nfunction setCancelFlag(data) {\n if (data.passiveListener != null) {\n if (\n typeof console !== \"undefined\" &&\n typeof console.error === \"function\"\n ) {\n console.error(\n \"Unable to preventDefault inside passive event listener invocation.\",\n data.passiveListener\n );\n }\n return\n }\n if (!data.event.cancelable) {\n return\n }\n\n data.canceled = true;\n if (typeof data.event.preventDefault === \"function\") {\n data.event.preventDefault();\n }\n}\n\n/**\n * @see https://dom.spec.whatwg.org/#interface-event\n * @private\n */\n/**\n * The event wrapper.\n * @constructor\n * @param {EventTarget} eventTarget The event target of this dispatching.\n * @param {Event|{type:string}} event The original event to wrap.\n */\nfunction Event(eventTarget, event) {\n privateData.set(this, {\n eventTarget,\n event,\n eventPhase: 2,\n currentTarget: eventTarget,\n canceled: false,\n stopped: false,\n immediateStopped: false,\n passiveListener: null,\n timeStamp: event.timeStamp || Date.now(),\n });\n\n // https://heycam.github.io/webidl/#Unforgeable\n Object.defineProperty(this, \"isTrusted\", { value: false, enumerable: true });\n\n // Define accessors\n const keys = Object.keys(event);\n for (let i = 0; i < keys.length; ++i) {\n const key = keys[i];\n if (!(key in this)) {\n Object.defineProperty(this, key, defineRedirectDescriptor(key));\n }\n }\n}\n\n// Should be enumerable, but class methods are not enumerable.\nEvent.prototype = {\n /**\n * The type of this event.\n * @type {string}\n */\n get type() {\n return pd(this).event.type\n },\n\n /**\n * The target of this event.\n * @type {EventTarget}\n */\n get target() {\n return pd(this).eventTarget\n },\n\n /**\n * The target of this event.\n * @type {EventTarget}\n */\n get currentTarget() {\n return pd(this).currentTarget\n },\n\n /**\n * @returns {EventTarget[]} The composed path of this event.\n */\n composedPath() {\n const currentTarget = pd(this).currentTarget;\n if (currentTarget == null) {\n return []\n }\n return [currentTarget]\n },\n\n /**\n * Constant of NONE.\n * @type {number}\n */\n get NONE() {\n return 0\n },\n\n /**\n * Constant of CAPTURING_PHASE.\n * @type {number}\n */\n get CAPTURING_PHASE() {\n return 1\n },\n\n /**\n * Constant of AT_TARGET.\n * @type {number}\n */\n get AT_TARGET() {\n return 2\n },\n\n /**\n * Constant of BUBBLING_PHASE.\n * @type {number}\n */\n get BUBBLING_PHASE() {\n return 3\n },\n\n /**\n * The target of this event.\n * @type {number}\n */\n get eventPhase() {\n return pd(this).eventPhase\n },\n\n /**\n * Stop event bubbling.\n * @returns {void}\n */\n stopPropagation() {\n const data = pd(this);\n\n data.stopped = true;\n if (typeof data.event.stopPropagation === \"function\") {\n data.event.stopPropagation();\n }\n },\n\n /**\n * Stop event bubbling.\n * @returns {void}\n */\n stopImmediatePropagation() {\n const data = pd(this);\n\n data.stopped = true;\n data.immediateStopped = true;\n if (typeof data.event.stopImmediatePropagation === \"function\") {\n data.event.stopImmediatePropagation();\n }\n },\n\n /**\n * The flag to be bubbling.\n * @type {boolean}\n */\n get bubbles() {\n return Boolean(pd(this).event.bubbles)\n },\n\n /**\n * The flag to be cancelable.\n * @type {boolean}\n */\n get cancelable() {\n return Boolean(pd(this).event.cancelable)\n },\n\n /**\n * Cancel this event.\n * @returns {void}\n */\n preventDefault() {\n setCancelFlag(pd(this));\n },\n\n /**\n * The flag to indicate cancellation state.\n * @type {boolean}\n */\n get defaultPrevented() {\n return pd(this).canceled\n },\n\n /**\n * The flag to be composed.\n * @type {boolean}\n */\n get composed() {\n return Boolean(pd(this).event.composed)\n },\n\n /**\n * The unix time of this event.\n * @type {number}\n */\n get timeStamp() {\n return pd(this).timeStamp\n },\n\n /**\n * The target of this event.\n * @type {EventTarget}\n * @deprecated\n */\n get srcElement() {\n return pd(this).eventTarget\n },\n\n /**\n * The flag to stop event bubbling.\n * @type {boolean}\n * @deprecated\n */\n get cancelBubble() {\n return pd(this).stopped\n },\n set cancelBubble(value) {\n if (!value) {\n return\n }\n const data = pd(this);\n\n data.stopped = true;\n if (typeof data.event.cancelBubble === \"boolean\") {\n data.event.cancelBubble = true;\n }\n },\n\n /**\n * The flag to indicate cancellation state.\n * @type {boolean}\n * @deprecated\n */\n get returnValue() {\n return !pd(this).canceled\n },\n set returnValue(value) {\n if (!value) {\n setCancelFlag(pd(this));\n }\n },\n\n /**\n * Initialize this event object. But do nothing under event dispatching.\n * @param {string} type The event type.\n * @param {boolean} [bubbles=false] The flag to be possible to bubble up.\n * @param {boolean} [cancelable=false] The flag to be possible to cancel.\n * @deprecated\n */\n initEvent() {\n // Do nothing.\n },\n};\n\n// `constructor` is not enumerable.\nObject.defineProperty(Event.prototype, \"constructor\", {\n value: Event,\n configurable: true,\n writable: true,\n});\n\n// Ensure `event instanceof window.Event` is `true`.\nif (typeof window !== \"undefined\" && typeof window.Event !== \"undefined\") {\n Object.setPrototypeOf(Event.prototype, window.Event.prototype);\n\n // Make association for wrappers.\n wrappers.set(window.Event.prototype, Event);\n}\n\n/**\n * Get the property descriptor to redirect a given property.\n * @param {string} key Property name to define property descriptor.\n * @returns {PropertyDescriptor} The property descriptor to redirect the property.\n * @private\n */\nfunction defineRedirectDescriptor(key) {\n return {\n get() {\n return pd(this).event[key]\n },\n set(value) {\n pd(this).event[key] = value;\n },\n configurable: true,\n enumerable: true,\n }\n}\n\n/**\n * Get the property descriptor to call a given method property.\n * @param {string} key Property name to define property descriptor.\n * @returns {PropertyDescriptor} The property descriptor to call the method property.\n * @private\n */\nfunction defineCallDescriptor(key) {\n return {\n value() {\n const event = pd(this).event;\n return event[key].apply(event, arguments)\n },\n configurable: true,\n enumerable: true,\n }\n}\n\n/**\n * Define new wrapper class.\n * @param {Function} BaseEvent The base wrapper class.\n * @param {Object} proto The prototype of the original event.\n * @returns {Function} The defined wrapper class.\n * @private\n */\nfunction defineWrapper(BaseEvent, proto) {\n const keys = Object.keys(proto);\n if (keys.length === 0) {\n return BaseEvent\n }\n\n /** CustomEvent */\n function CustomEvent(eventTarget, event) {\n BaseEvent.call(this, eventTarget, event);\n }\n\n CustomEvent.prototype = Object.create(BaseEvent.prototype, {\n constructor: { value: CustomEvent, configurable: true, writable: true },\n });\n\n // Define accessors.\n for (let i = 0; i < keys.length; ++i) {\n const key = keys[i];\n if (!(key in BaseEvent.prototype)) {\n const descriptor = Object.getOwnPropertyDescriptor(proto, key);\n const isFunc = typeof descriptor.value === \"function\";\n Object.defineProperty(\n CustomEvent.prototype,\n key,\n isFunc\n ? defineCallDescriptor(key)\n : defineRedirectDescriptor(key)\n );\n }\n }\n\n return CustomEvent\n}\n\n/**\n * Get the wrapper class of a given prototype.\n * @param {Object} proto The prototype of the original event to get its wrapper.\n * @returns {Function} The wrapper class.\n * @private\n */\nfunction getWrapper(proto) {\n if (proto == null || proto === Object.prototype) {\n return Event\n }\n\n let wrapper = wrappers.get(proto);\n if (wrapper == null) {\n wrapper = defineWrapper(getWrapper(Object.getPrototypeOf(proto)), proto);\n wrappers.set(proto, wrapper);\n }\n return wrapper\n}\n\n/**\n * Wrap a given event to management a dispatching.\n * @param {EventTarget} eventTarget The event target of this dispatching.\n * @param {Object} event The event to wrap.\n * @returns {Event} The wrapper instance.\n * @private\n */\nfunction wrapEvent(eventTarget, event) {\n const Wrapper = getWrapper(Object.getPrototypeOf(event));\n return new Wrapper(eventTarget, event)\n}\n\n/**\n * Get the immediateStopped flag of a given event.\n * @param {Event} event The event to get.\n * @returns {boolean} The flag to stop propagation immediately.\n * @private\n */\nfunction isStopped(event) {\n return pd(event).immediateStopped\n}\n\n/**\n * Set the current event phase of a given event.\n * @param {Event} event The event to set current target.\n * @param {number} eventPhase New event phase.\n * @returns {void}\n * @private\n */\nfunction setEventPhase(event, eventPhase) {\n pd(event).eventPhase = eventPhase;\n}\n\n/**\n * Set the current target of a given event.\n * @param {Event} event The event to set current target.\n * @param {EventTarget|null} currentTarget New current target.\n * @returns {void}\n * @private\n */\nfunction setCurrentTarget(event, currentTarget) {\n pd(event).currentTarget = currentTarget;\n}\n\n/**\n * Set a passive listener of a given event.\n * @param {Event} event The event to set current target.\n * @param {Function|null} passiveListener New passive listener.\n * @returns {void}\n * @private\n */\nfunction setPassiveListener(event, passiveListener) {\n pd(event).passiveListener = passiveListener;\n}\n\n/**\n * @typedef {object} ListenerNode\n * @property {Function} listener\n * @property {1|2|3} listenerType\n * @property {boolean} passive\n * @property {boolean} once\n * @property {ListenerNode|null} next\n * @private\n */\n\n/**\n * @type {WeakMap>}\n * @private\n */\nconst listenersMap = new WeakMap();\n\n// Listener types\nconst CAPTURE = 1;\nconst BUBBLE = 2;\nconst ATTRIBUTE = 3;\n\n/**\n * Check whether a given value is an object or not.\n * @param {any} x The value to check.\n * @returns {boolean} `true` if the value is an object.\n */\nfunction isObject(x) {\n return x !== null && typeof x === \"object\" //eslint-disable-line no-restricted-syntax\n}\n\n/**\n * Get listeners.\n * @param {EventTarget} eventTarget The event target to get.\n * @returns {Map} The listeners.\n * @private\n */\nfunction getListeners(eventTarget) {\n const listeners = listenersMap.get(eventTarget);\n if (listeners == null) {\n throw new TypeError(\n \"'this' is expected an EventTarget object, but got another value.\"\n )\n }\n return listeners\n}\n\n/**\n * Get the property descriptor for the event attribute of a given event.\n * @param {string} eventName The event name to get property descriptor.\n * @returns {PropertyDescriptor} The property descriptor.\n * @private\n */\nfunction defineEventAttributeDescriptor(eventName) {\n return {\n get() {\n const listeners = getListeners(this);\n let node = listeners.get(eventName);\n while (node != null) {\n if (node.listenerType === ATTRIBUTE) {\n return node.listener\n }\n node = node.next;\n }\n return null\n },\n\n set(listener) {\n if (typeof listener !== \"function\" && !isObject(listener)) {\n listener = null; // eslint-disable-line no-param-reassign\n }\n const listeners = getListeners(this);\n\n // Traverse to the tail while removing old value.\n let prev = null;\n let node = listeners.get(eventName);\n while (node != null) {\n if (node.listenerType === ATTRIBUTE) {\n // Remove old value.\n if (prev !== null) {\n prev.next = node.next;\n } else if (node.next !== null) {\n listeners.set(eventName, node.next);\n } else {\n listeners.delete(eventName);\n }\n } else {\n prev = node;\n }\n\n node = node.next;\n }\n\n // Add new value.\n if (listener !== null) {\n const newNode = {\n listener,\n listenerType: ATTRIBUTE,\n passive: false,\n once: false,\n next: null,\n };\n if (prev === null) {\n listeners.set(eventName, newNode);\n } else {\n prev.next = newNode;\n }\n }\n },\n configurable: true,\n enumerable: true,\n }\n}\n\n/**\n * Define an event attribute (e.g. `eventTarget.onclick`).\n * @param {Object} eventTargetPrototype The event target prototype to define an event attrbite.\n * @param {string} eventName The event name to define.\n * @returns {void}\n */\nfunction defineEventAttribute(eventTargetPrototype, eventName) {\n Object.defineProperty(\n eventTargetPrototype,\n `on${eventName}`,\n defineEventAttributeDescriptor(eventName)\n );\n}\n\n/**\n * Define a custom EventTarget with event attributes.\n * @param {string[]} eventNames Event names for event attributes.\n * @returns {EventTarget} The custom EventTarget.\n * @private\n */\nfunction defineCustomEventTarget(eventNames) {\n /** CustomEventTarget */\n function CustomEventTarget() {\n EventTarget.call(this);\n }\n\n CustomEventTarget.prototype = Object.create(EventTarget.prototype, {\n constructor: {\n value: CustomEventTarget,\n configurable: true,\n writable: true,\n },\n });\n\n for (let i = 0; i < eventNames.length; ++i) {\n defineEventAttribute(CustomEventTarget.prototype, eventNames[i]);\n }\n\n return CustomEventTarget\n}\n\n/**\n * EventTarget.\n *\n * - This is constructor if no arguments.\n * - This is a function which returns a CustomEventTarget constructor if there are arguments.\n *\n * For example:\n *\n * class A extends EventTarget {}\n * class B extends EventTarget(\"message\") {}\n * class C extends EventTarget(\"message\", \"error\") {}\n * class D extends EventTarget([\"message\", \"error\"]) {}\n */\nfunction EventTarget() {\n /*eslint-disable consistent-return */\n if (this instanceof EventTarget) {\n listenersMap.set(this, new Map());\n return\n }\n if (arguments.length === 1 && Array.isArray(arguments[0])) {\n return defineCustomEventTarget(arguments[0])\n }\n if (arguments.length > 0) {\n const types = new Array(arguments.length);\n for (let i = 0; i < arguments.length; ++i) {\n types[i] = arguments[i];\n }\n return defineCustomEventTarget(types)\n }\n throw new TypeError(\"Cannot call a class as a function\")\n /*eslint-enable consistent-return */\n}\n\n// Should be enumerable, but class methods are not enumerable.\nEventTarget.prototype = {\n /**\n * Add a given listener to this event target.\n * @param {string} eventName The event name to add.\n * @param {Function} listener The listener to add.\n * @param {boolean|{capture?:boolean,passive?:boolean,once?:boolean}} [options] The options for this listener.\n * @returns {void}\n */\n addEventListener(eventName, listener, options) {\n if (listener == null) {\n return\n }\n if (typeof listener !== \"function\" && !isObject(listener)) {\n throw new TypeError(\"'listener' should be a function or an object.\")\n }\n\n const listeners = getListeners(this);\n const optionsIsObj = isObject(options);\n const capture = optionsIsObj\n ? Boolean(options.capture)\n : Boolean(options);\n const listenerType = capture ? CAPTURE : BUBBLE;\n const newNode = {\n listener,\n listenerType,\n passive: optionsIsObj && Boolean(options.passive),\n once: optionsIsObj && Boolean(options.once),\n next: null,\n };\n\n // Set it as the first node if the first node is null.\n let node = listeners.get(eventName);\n if (node === undefined) {\n listeners.set(eventName, newNode);\n return\n }\n\n // Traverse to the tail while checking duplication..\n let prev = null;\n while (node != null) {\n if (\n node.listener === listener &&\n node.listenerType === listenerType\n ) {\n // Should ignore duplication.\n return\n }\n prev = node;\n node = node.next;\n }\n\n // Add it.\n prev.next = newNode;\n },\n\n /**\n * Remove a given listener from this event target.\n * @param {string} eventName The event name to remove.\n * @param {Function} listener The listener to remove.\n * @param {boolean|{capture?:boolean,passive?:boolean,once?:boolean}} [options] The options for this listener.\n * @returns {void}\n */\n removeEventListener(eventName, listener, options) {\n if (listener == null) {\n return\n }\n\n const listeners = getListeners(this);\n const capture = isObject(options)\n ? Boolean(options.capture)\n : Boolean(options);\n const listenerType = capture ? CAPTURE : BUBBLE;\n\n let prev = null;\n let node = listeners.get(eventName);\n while (node != null) {\n if (\n node.listener === listener &&\n node.listenerType === listenerType\n ) {\n if (prev !== null) {\n prev.next = node.next;\n } else if (node.next !== null) {\n listeners.set(eventName, node.next);\n } else {\n listeners.delete(eventName);\n }\n return\n }\n\n prev = node;\n node = node.next;\n }\n },\n\n /**\n * Dispatch a given event.\n * @param {Event|{type:string}} event The event to dispatch.\n * @returns {boolean} `false` if canceled.\n */\n dispatchEvent(event) {\n if (event == null || typeof event.type !== \"string\") {\n throw new TypeError('\"event.type\" should be a string.')\n }\n\n // If listeners aren't registered, terminate.\n const listeners = getListeners(this);\n const eventName = event.type;\n let node = listeners.get(eventName);\n if (node == null) {\n return true\n }\n\n // Since we cannot rewrite several properties, so wrap object.\n const wrappedEvent = wrapEvent(this, event);\n\n // This doesn't process capturing phase and bubbling phase.\n // This isn't participating in a tree.\n let prev = null;\n while (node != null) {\n // Remove this listener if it's once\n if (node.once) {\n if (prev !== null) {\n prev.next = node.next;\n } else if (node.next !== null) {\n listeners.set(eventName, node.next);\n } else {\n listeners.delete(eventName);\n }\n } else {\n prev = node;\n }\n\n // Call this listener\n setPassiveListener(\n wrappedEvent,\n node.passive ? node.listener : null\n );\n if (typeof node.listener === \"function\") {\n try {\n node.listener.call(this, wrappedEvent);\n } catch (err) {\n if (\n typeof console !== \"undefined\" &&\n typeof console.error === \"function\"\n ) {\n console.error(err);\n }\n }\n } else if (\n node.listenerType !== ATTRIBUTE &&\n typeof node.listener.handleEvent === \"function\"\n ) {\n node.listener.handleEvent(wrappedEvent);\n }\n\n // Break if `event.stopImmediatePropagation` was called.\n if (isStopped(wrappedEvent)) {\n break\n }\n\n node = node.next;\n }\n setPassiveListener(wrappedEvent, null);\n setEventPhase(wrappedEvent, 0);\n setCurrentTarget(wrappedEvent, null);\n\n return !wrappedEvent.defaultPrevented\n },\n};\n\n// `constructor` is not enumerable.\nObject.defineProperty(EventTarget.prototype, \"constructor\", {\n value: EventTarget,\n configurable: true,\n writable: true,\n});\n\n// Ensure `eventTarget instanceof window.EventTarget` is `true`.\nif (\n typeof window !== \"undefined\" &&\n typeof window.EventTarget !== \"undefined\"\n) {\n Object.setPrototypeOf(EventTarget.prototype, window.EventTarget.prototype);\n}\n\nexport default EventTarget;\nexport { defineEventAttribute, EventTarget };\n//# sourceMappingURL=event-target-shim.mjs.map\n","import {\n // Event,\n EventTarget,\n // Type,\n defineEventAttribute,\n} from \"event-target-shim\"\n\n// Known Limitation\n// Use `any` because the type of `AbortSignal` in `lib.dom.d.ts` is wrong and\n// to make assignable our `AbortSignal` into that.\n// https://github.com/Microsoft/TSJS-lib-generator/pull/623\ntype Events = {\n abort: any // Event & Type<\"abort\">\n}\ntype EventAttributes = {\n onabort: any // Event & Type<\"abort\">\n}\n\n/**\n * The signal class.\n * @see https://dom.spec.whatwg.org/#abortsignal\n */\nexport default class AbortSignal extends EventTarget {\n /**\n * AbortSignal cannot be constructed directly.\n */\n public constructor() {\n super()\n throw new TypeError(\"AbortSignal cannot be constructed directly\")\n }\n\n /**\n * Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise.\n */\n public get aborted(): boolean {\n const aborted = abortedFlags.get(this)\n if (typeof aborted !== \"boolean\") {\n throw new TypeError(\n `Expected 'this' to be an 'AbortSignal' object, but got ${\n this === null ? \"null\" : typeof this\n }`,\n )\n }\n return aborted\n }\n}\ndefineEventAttribute(AbortSignal.prototype, \"abort\")\n\n/**\n * Create an AbortSignal object.\n */\nexport function createAbortSignal(): AbortSignal {\n const signal = Object.create(AbortSignal.prototype)\n EventTarget.call(signal)\n abortedFlags.set(signal, false)\n return signal\n}\n\n/**\n * Abort a given signal.\n */\nexport function abortSignal(signal: AbortSignal): void {\n if (abortedFlags.get(signal) !== false) {\n return\n }\n\n abortedFlags.set(signal, true)\n signal.dispatchEvent<\"abort\">({ type: \"abort\" })\n}\n\n/**\n * Aborted flag for each instances.\n */\nconst abortedFlags = new WeakMap()\n\n// Properties should be enumerable.\nObject.defineProperties(AbortSignal.prototype, {\n aborted: { enumerable: true },\n})\n\n// `toString()` should return `\"[object AbortSignal]\"`\nif (typeof Symbol === \"function\" && typeof Symbol.toStringTag === \"symbol\") {\n Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {\n configurable: true,\n value: \"AbortSignal\",\n })\n}\n","import AbortSignal, { abortSignal, createAbortSignal } from \"./abort-signal\"\n\n/**\n * The AbortController.\n * @see https://dom.spec.whatwg.org/#abortcontroller\n */\nexport default class AbortController {\n /**\n * Initialize this controller.\n */\n public constructor() {\n signals.set(this, createAbortSignal())\n }\n\n /**\n * Returns the `AbortSignal` object associated with this object.\n */\n public get signal(): AbortSignal {\n return getSignal(this)\n }\n\n /**\n * Abort and signal to any observers that the associated activity is to be aborted.\n */\n public abort(): void {\n abortSignal(getSignal(this))\n }\n}\n\n/**\n * Associated signals.\n */\nconst signals = new WeakMap()\n\n/**\n * Get the associated signal of a given controller.\n */\nfunction getSignal(controller: AbortController): AbortSignal {\n const signal = signals.get(controller)\n if (signal == null) {\n throw new TypeError(\n `Expected 'this' to be an 'AbortController' object, but got ${\n controller === null ? \"null\" : typeof controller\n }`,\n )\n }\n return signal\n}\n\n// Properties should be enumerable.\nObject.defineProperties(AbortController.prototype, {\n signal: { enumerable: true },\n abort: { enumerable: true },\n})\n\nif (typeof Symbol === \"function\" && typeof Symbol.toStringTag === \"symbol\") {\n Object.defineProperty(AbortController.prototype, Symbol.toStringTag, {\n configurable: true,\n value: \"AbortController\",\n })\n}\n\nexport { AbortController, AbortSignal }\n"],"names":["pd","event","retv","privateData","get","console","assert","setCancelFlag","data","passiveListener","cancelable","canceled","preventDefault","error","Event","eventTarget","set","eventPhase","currentTarget","stopped","immediateStopped","timeStamp","Date","now","Object","defineProperty","value","enumerable","key","keys","i","length","defineRedirectDescriptor","configurable","defineCallDescriptor","apply","arguments","defineWrapper","BaseEvent","proto","CustomEvent","call","prototype","create","constructor","writable","descriptor","getOwnPropertyDescriptor","isFunc","getWrapper","wrapper","wrappers","getPrototypeOf","wrapEvent","Wrapper","isStopped","setEventPhase","setCurrentTarget","setPassiveListener","createAbortSignal","signal","AbortSignal","EventTarget","abortedFlags","abortSignal","dispatchEvent","type","getSignal","controller","signals","TypeError","WeakMap","target","composedPath","NONE","CAPTURING_PHASE","AT_TARGET","BUBBLING_PHASE","stopPropagation","stopImmediatePropagation","bubbles","defaultPrevented","composed","srcElement","cancelBubble","returnValue","initEvent","window","setPrototypeOf","aborted","defineEventAttribute","defineProperties","Symbol","_typeof","toStringTag","AbortController","abort"],"mappings":";;;+3CAkCA,QAASA,CAAAA,CAAT,CAAYC,CAAZ,CAAmB,IACTC,CAAAA,CAAI,CAAGC,CAAW,CAACC,GAAZ,CAAgBH,CAAhB,QACbI,CAAAA,OAAO,CAACC,MAAR,CACY,IAAR,EAAAJ,CADJ,CAEI,6CAFJ,CAGID,CAHJ,EAKOC,EAOX,QAASK,CAAAA,CAAT,CAAuBC,CAAvB,CAA6B,OACG,KAAxB,EAAAA,CAAI,CAACC,eADgB,MAarB,CAACD,CAAI,CAACP,KAAL,CAAWS,UAbS,GAiBzBF,CAAI,CAACG,QAAL,GAjByB,CAkBgB,UAArC,QAAOH,CAAAA,CAAI,CAACP,KAAL,CAAWW,cAlBG,EAmBrBJ,CAAI,CAACP,KAAL,CAAWW,cAAX,EAnBqB,QAGE,WAAnB,QAAOP,CAAAA,OAAP,EACyB,UAAzB,QAAOA,CAAAA,OAAO,CAACQ,KAJE,EAMjBR,OAAO,CAACQ,KAAR,CACI,oEADJ,CAEIL,CAAI,CAACC,eAFT,CANiB,EAiC7B,QAASK,CAAAA,CAAT,CAAeC,CAAf,CAA4Bd,CAA5B,CAAmC,CAC/BE,CAAW,CAACa,GAAZ,CAAgB,IAAhB,CAAsB,CAClBD,WAAW,CAAXA,CADkB,CAElBd,KAAK,CAALA,CAFkB,CAGlBgB,UAAU,CAAE,CAHM,CAIlBC,aAAa,CAAEH,CAJG,CAKlBJ,QAAQ,GALU,CAMlBQ,OAAO,GANW,CAOlBC,gBAAgB,GAPE,CAQlBX,eAAe,CAAE,IARC,CASlBY,SAAS,CAAEpB,CAAK,CAACoB,SAAN,EAAmBC,IAAI,CAACC,GAAL,EATZ,CAAtB,CAD+B,CAc/BC,MAAM,CAACC,cAAP,CAAsB,IAAtB,CAA4B,WAA5B,CAAyC,CAAEC,KAAK,GAAP,CAAgBC,UAAU,GAA1B,CAAzC,CAd+B,QAmBrBC,CAAAA,EAFJC,CAAI,CAAGL,MAAM,CAACK,IAAP,CAAY5B,CAAZ,EACJ6B,CAAC,CAAG,EAAGA,CAAC,CAAGD,CAAI,CAACE,OAAQ,EAAED,EACzBF,EAAMC,CAAI,CAACC,CAAD,EACVF,CAAG,GAAI,OACTJ,MAAM,CAACC,cAAP,CAAsB,IAAtB,CAA4BG,CAA5B,CAAiCI,CAAwB,CAACJ,CAAD,CAAzD,EAyOZ,QAASI,CAAAA,CAAT,CAAkCJ,CAAlC,CAAuC,OAC5B,CACHxB,GADG,WACG,OACKJ,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASC,KAAT,CAAe2B,CAAf,CAFR,CAAA,CAIHZ,GAJG,UAICU,EAAO,CACP1B,CAAE,CAAC,IAAD,CAAF,CAASC,KAAT,CAAe2B,CAAf,EAAsBF,CALvB,CAAA,CAOHO,YAAY,GAPT,CAQHN,UAAU,GARP,EAkBX,QAASO,CAAAA,CAAT,CAA8BN,CAA9B,CAAmC,OACxB,CACHF,KADG,WACK,IACEzB,CAAAA,CAAK,CAAGD,CAAE,CAAC,IAAD,CAAF,CAASC,YAChBA,CAAAA,CAAK,CAAC2B,CAAD,CAAL,CAAWO,KAAX,CAAiBlC,CAAjB,CAAwBmC,SAAxB,CAHR,CAAA,CAKHH,YAAY,GALT,CAMHN,UAAU,GANP,EAiBX,QAASU,CAAAA,CAAT,CAAuBC,CAAvB,CAAkCC,CAAlC,CAAyC,SAO5BC,CAAAA,EAAYzB,EAAad,EAAO,CACrCqC,CAAS,CAACG,IAAV,CAAe,IAAf,CAAqB1B,CAArB,CAAkCd,CAAlC,KAPE4B,CAAAA,CAAI,CAAGL,MAAM,CAACK,IAAP,CAAYU,CAAZ,KACO,CAAhB,GAAAV,CAAI,CAACE,aACEO,CAAAA,EAQXE,CAAW,CAACE,SAAZ,CAAwBlB,MAAM,CAACmB,MAAP,CAAcL,CAAS,CAACI,SAAxB,CAAmC,CACvDE,WAAW,CAAE,CAAElB,KAAK,CAAEc,CAAT,CAAsBP,YAAY,GAAlC,CAA0CY,QAAQ,GAAlD,CAD0C,CAAnC,CAXa,KAgBhC,GACKjB,CAAAA,CADL,CAAIE,CAAC,CAAG,EAAGA,CAAC,CAAGD,CAAI,CAACE,OAAQ,EAAED,KACzBF,EAAMC,CAAI,CAACC,CAAD,EACZ,EAAEF,CAAG,GAAIU,CAAAA,CAAS,CAACI,SAAnB,EAA+B,IACzBI,CAAAA,CAAU,CAAGtB,MAAM,CAACuB,wBAAP,CAAgCR,CAAhC,CAAuCX,CAAvC,CADY,CAEzBoB,CAAM,CAA+B,UAA5B,QAAOF,CAAAA,CAAU,CAACpB,KAFF,CAG/BF,MAAM,CAACC,cAAP,CACIe,CAAW,CAACE,SADhB,CAEId,CAFJ,CAGIoB,CAAM,CACAd,CAAoB,CAACN,CAAD,CADpB,CAEAI,CAAwB,CAACJ,CAAD,CALlC,QAUDY,CAAAA,EASX,QAASS,CAAAA,CAAT,CAAoBV,CAApB,CAA2B,IACV,IAAT,EAAAA,CAAK,EAAYA,CAAK,GAAKf,MAAM,CAACkB,gBAC3B5B,CAAAA,KAGPoC,CAAAA,CAAO,CAAGC,CAAQ,CAAC/C,GAAT,CAAamC,CAAb,QACC,KAAX,EAAAW,IACAA,CAAO,CAAGb,CAAa,CAACY,CAAU,CAACzB,MAAM,CAAC4B,cAAP,CAAsBb,CAAtB,CAAD,CAAX,CAA2CA,CAA3C,EACvBY,CAAQ,CAACnC,GAAT,CAAauB,CAAb,CAAoBW,CAApB,GAEGA,EAUJ,QAASG,CAAAA,CAAT,CAAmBtC,CAAnB,CAAgCd,CAAhC,CAAuC,IACpCqD,CAAAA,CAAO,CAAGL,CAAU,CAACzB,MAAM,CAAC4B,cAAP,CAAsBnD,CAAtB,CAAD,QACnB,IAAIqD,CAAAA,CAAJ,CAAYvC,CAAZ,CAAyBd,CAAzB,EASJ,QAASsD,CAAAA,CAAT,CAAmBtD,CAAnB,CAA0B,OACtBD,CAAAA,CAAE,CAACC,CAAD,CAAF,CAAUmB,iBAUd,QAASoC,CAAAA,CAAT,CAAuBvD,CAAvB,CAA8BgB,CAA9B,CAA0C,CAC7CjB,CAAE,CAACC,CAAD,CAAF,CAAUgB,UAAV,CAAuBA,EAUpB,QAASwC,CAAAA,CAAT,CAA0BxD,CAA1B,CAAiCiB,CAAjC,CAAgD,CACnDlB,CAAE,CAACC,CAAD,CAAF,CAAUiB,aAAV,CAA0BA,EAUvB,QAASwC,CAAAA,CAAT,CAA4BzD,CAA5B,CAAmCQ,CAAnC,CAAoD,CACvDT,CAAE,CAACC,CAAD,CAAF,CAAUQ,eAAV,CAA4BA,ysCC1ahBkD,CAAAA,OACNC,CAAAA,CAAM,CAAGpC,MAAM,CAACmB,MAAPnB,CAAcqC,CAAW,CAACnB,SAA1BlB,QACfsC,CAAAA,CAAW,CAACrB,IAAZqB,CAAiBF,CAAjBE,EACAC,CAAY,CAAC/C,GAAb+C,CAAiBH,CAAjBG,KACOH,UAMKI,CAAAA,EAAYJ,GACpBG,KAAAA,CAAY,CAAC3D,GAAb2D,CAAiBH,CAAjBG,IAIJA,CAAY,CAAC/C,GAAb+C,CAAiBH,CAAjBG,KACAH,CAAM,CAACK,aAAPL,CAA8B,CAAEM,IAAI,CAAE,OAAR,CAA9BN,GC9BJ,QAASO,CAAAA,CAAT,CAAmBC,CAAnB,KACUR,CAAAA,CAAM,CAAGS,CAAO,CAACjE,GAARiE,CAAYD,CAAZC,KACD,IAAVT,EAAAA,OACM,IAAIU,CAAAA,SAAJ,sEAEiB,IAAfF,GAAAA,CAAU,CAAY,MAAZ,GAA4BA,GAFxC,QAMHR,CAAAA,KF3BLzD,CAAAA,CAAW,CAAG,GAAIoE,CAAAA,QAOlBpB,CAAQ,CAAG,GAAIoB,CAAAA,QAkFrBzD,CAAK,CAAC4B,SAAN,CAAkB,IAKVwB,CAAAA,MAAO,OACAlE,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASC,KAAT,CAAeiE,IANZ,CAAA,IAaVM,CAAAA,QAAS,OACFxE,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASe,WAdN,CAAA,IAqBVG,CAAAA,eAAgB,OACTlB,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASkB,aAtBN,CAAA,CA4BduD,YA5Bc,WA4BC,IACLvD,CAAAA,CAAa,CAAGlB,CAAE,CAAC,IAAD,CAAF,CAASkB,cADpB,MAEU,KAAjB,EAAAA,CAFO,CAGA,EAHA,CAKJ,CAACA,CAAD,CAjCG,CAAA,IAwCVwD,CAAAA,MAAO,OACA,EAzCG,CAAA,IAgDVC,CAAAA,iBAAkB,OACX,EAjDG,CAAA,IAwDVC,CAAAA,WAAY,OACL,EAzDG,CAAA,IAgEVC,CAAAA,gBAAiB,OACV,EAjEG,CAAA,IAwEV5D,CAAAA,YAAa,OACNjB,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASiB,UAzEN,CAAA,CAgFd6D,eAhFc,WAgFI,IACRtE,CAAAA,CAAI,CAAGR,CAAE,CAAC,IAAD,EAEfQ,CAAI,CAACW,OAAL,GAHc,CAI4B,UAAtC,QAAOX,CAAAA,CAAI,CAACP,KAAL,CAAW6E,eAJR,EAKVtE,CAAI,CAACP,KAAL,CAAW6E,eAAX,EArFM,CAAA,CA6FdC,wBA7Fc,WA6Fa,IACjBvE,CAAAA,CAAI,CAAGR,CAAE,CAAC,IAAD,EAEfQ,CAAI,CAACW,OAAL,GAHuB,CAIvBX,CAAI,CAACY,gBAAL,GAJuB,CAK4B,UAA/C,QAAOZ,CAAAA,CAAI,CAACP,KAAL,CAAW8E,wBALC,EAMnBvE,CAAI,CAACP,KAAL,CAAW8E,wBAAX,EAnGM,CAAA,IA2GVC,CAAAA,SAAU,SACKhF,CAAE,CAAC,IAAD,CAAF,CAASC,KAAT,CAAe+E,OA5GpB,CAAA,IAmHVtE,CAAAA,YAAa,SACEV,CAAE,CAAC,IAAD,CAAF,CAASC,KAAT,CAAeS,UApHpB,CAAA,CA2HdE,cA3Hc,WA2HG,CACbL,CAAa,CAACP,CAAE,CAAC,IAAD,CAAH,CA5HH,CAAA,IAmIViF,CAAAA,kBAAmB,OACZjF,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASW,QApIN,CAAA,IA2IVuE,CAAAA,UAAW,SACIlF,CAAE,CAAC,IAAD,CAAF,CAASC,KAAT,CAAeiF,QA5IpB,CAAA,IAmJV7D,CAAAA,WAAY,OACLrB,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASqB,SApJN,CAAA,IA4JV8D,CAAAA,YAAa,OACNnF,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASe,WA7JN,CAAA,IAqKVqE,CAAAA,cAAe,OACRpF,CAAAA,CAAE,CAAC,IAAD,CAAF,CAASmB,OAtKN,CAAA,IAwKViE,CAAAA,aAAa1D,EAAO,IACfA,MAGClB,CAAAA,CAAI,CAAGR,CAAE,CAAC,IAAD,EAEfQ,CAAI,CAACW,OAAL,IACuC,SAAnC,QAAOX,CAAAA,CAAI,CAACP,KAAL,CAAWmF,eAClB5E,CAAI,CAACP,KAAL,CAAWmF,YAAX,KAhLM,CAAA,IAyLVC,CAAAA,aAAc,OACP,CAACrF,CAAE,CAAC,IAAD,CAAF,CAASW,QA1LP,CAAA,IA4LV0E,CAAAA,YAAY3D,EAAO,CACdA,CADc,EAEfnB,CAAa,CAACP,CAAE,CAAC,IAAD,CAAH,CA9LP,CAAA,CAyMdsF,SAzMc,WAyMF,EAzME,EA+MlB9D,MAAM,CAACC,cAAP,CAAsBX,CAAK,CAAC4B,SAA5B,CAAuC,aAAvC,CAAsD,CAClDhB,KAAK,CAAEZ,CAD2C,CAElDmB,YAAY,GAFsC,CAGlDY,QAAQ,GAH0C,CAAtD,EAOsB,WAAlB,QAAO0C,CAAAA,MAAP,EAAyD,WAAxB,QAAOA,CAAAA,MAAM,CAACzE,QAC/CU,MAAM,CAACgE,cAAP,CAAsB1E,CAAK,CAAC4B,SAA5B,CAAuC6C,MAAM,CAACzE,KAAP,CAAa4B,SAApD,EAGAS,CAAQ,CAACnC,GAAT,CAAauE,MAAM,CAACzE,KAAP,CAAa4B,SAA1B,CAAqC5B,CAArC,wiDChTiB+C,CAAAA,2EAMP,GAAIS,CAAAA,SAAJ,CAAc,4CAAd,sDAOAmB,CAAAA,CAAO,CAAG1B,CAAY,CAAC3D,GAAb2D,CAAiB,IAAjBA,KACO,SAAnB,QAAO0B,CAAAA,OACD,IAAInB,CAAAA,SAAJ,kEAEW,IAAT,QAAgB,MAAhB,GAAgC,MAFlC,QAMHmB,CAAAA,SArB0B3B,GAwBzC4B,CAAoB,CAAC7B,CAAW,CAACnB,SAAb,CAAwB,OAAxB,EA2BpB,GAAMqB,CAAAA,CAAY,CAAG,GAAIQ,CAAAA,OAAzB,CAGA/C,MAAM,CAACmE,gBAAPnE,CAAwBqC,CAAW,CAACnB,SAApClB,CAA+C,CAC3CiE,OAAO,CAAE,CAAE9D,UAAU,GAAZ,CADkC,CAA/CH,EAKsB,UAAlB,QAAOoE,CAAAA,MAAP,EAA8D,QAA9B,GAAAC,EAAOD,MAAM,CAACE,cAC9CtE,MAAM,CAACC,cAAPD,CAAsBqC,CAAW,CAACnB,SAAlClB,CAA6CoE,MAAM,CAACE,WAApDtE,CAAiE,CAC7DS,YAAY,GADiD,CAE7DP,KAAK,CAAE,aAFsD,CAAjEF,KC5EiBuE,CAAAA,oCAKb1B,CAAO,CAACrD,GAARqD,CAAY,IAAZA,CAAkBV,CAAiB,EAAnCU,4CAcAL,CAAW,CAACG,CAAS,CAAC,IAAD,CAAV,uCAPJA,CAAAA,CAAS,CAAC,IAAD,WAclBE,CAAO,CAAG,GAAIE,CAAAA,WAkBpB/C,MAAM,CAACmE,gBAAPnE,CAAwBuE,CAAe,CAACrD,SAAxClB,CAAmD,CAC/CoC,MAAM,CAAE,CAAEjC,UAAU,GAAZ,CADuC,CAE/CqE,KAAK,CAAE,CAAErE,UAAU,GAAZ,CAFwC,CAAnDH,EAKsB,UAAlB,QAAOoE,CAAAA,MAAP,EAA8D,QAA9B,GAAAC,EAAOD,MAAM,CAACE,cAC9CtE,MAAM,CAACC,cAAPD,CAAsBuE,CAAe,CAACrD,SAAtClB,CAAiDoE,MAAM,CAACE,WAAxDtE,CAAqE,CACjES,YAAY,GADqD,CAEjEP,KAAK,CAAE,iBAF0D,CAArEF"} \ No newline at end of file diff --git a/node_modules/abort-controller/package.json b/node_modules/abort-controller/package.json new file mode 100644 index 0000000..18350c5 --- /dev/null +++ b/node_modules/abort-controller/package.json @@ -0,0 +1,125 @@ +{ + "_from": "abort-controller@^3.0.0", + "_id": "abort-controller@3.0.0", + "_inBundle": false, + "_integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "_location": "/abort-controller", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "abort-controller@^3.0.0", + "name": "abort-controller", + "escapedName": "abort-controller", + "rawSpec": "^3.0.0", + "saveSpec": null, + "fetchSpec": "^3.0.0" + }, + "_requiredBy": [ + "/discord.js" + ], + "_resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "_shasum": "eaf54d53b62bae4138e809ca225c8439a6efb392", + "_spec": "abort-controller@^3.0.0", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\discord.js", + "author": { + "name": "Toru Nagashima", + "url": "https://github.com/mysticatea" + }, + "browser": "./browser.js", + "bugs": { + "url": "https://github.com/mysticatea/abort-controller/issues" + }, + "bundleDependencies": false, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "deprecated": false, + "description": "An implementation of WHATWG AbortController interface.", + "devDependencies": { + "@babel/core": "^7.2.2", + "@babel/plugin-transform-modules-commonjs": "^7.2.0", + "@babel/preset-env": "^7.3.0", + "@babel/register": "^7.0.0", + "@mysticatea/eslint-plugin": "^8.0.1", + "@mysticatea/spy": "^0.1.2", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.18", + "assert": "^1.4.1", + "codecov": "^3.1.0", + "dts-bundle-generator": "^2.0.0", + "eslint": "^5.12.1", + "karma": "^3.1.4", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "^1.1.2", + "karma-firefox-launcher": "^1.1.0", + "karma-growl-reporter": "^1.0.0", + "karma-ie-launcher": "^1.0.0", + "karma-mocha": "^1.3.0", + "karma-rollup-preprocessor": "^7.0.0-rc.2", + "mocha": "^5.2.0", + "npm-run-all": "^4.1.5", + "nyc": "^13.1.0", + "opener": "^1.5.1", + "rimraf": "^2.6.3", + "rollup": "^1.1.2", + "rollup-plugin-babel": "^4.3.2", + "rollup-plugin-babel-minify": "^7.0.0", + "rollup-plugin-commonjs": "^9.2.0", + "rollup-plugin-node-resolve": "^4.0.0", + "rollup-plugin-sourcemaps": "^0.4.2", + "rollup-plugin-typescript": "^1.0.0", + "rollup-watch": "^4.3.1", + "ts-node": "^8.0.1", + "type-tester": "^1.0.0", + "typescript": "^3.2.4" + }, + "engines": { + "node": ">=6.5" + }, + "files": [ + "dist", + "polyfill.*", + "browser.*" + ], + "homepage": "https://github.com/mysticatea/abort-controller#readme", + "keywords": [ + "w3c", + "whatwg", + "event", + "events", + "abort", + "cancel", + "abortcontroller", + "abortsignal", + "controller", + "signal", + "shim" + ], + "license": "MIT", + "main": "dist/abort-controller", + "name": "abort-controller", + "repository": { + "type": "git", + "url": "git+https://github.com/mysticatea/abort-controller.git" + }, + "scripts": { + "build": "run-s -s build:*", + "build:dts": "dts-bundle-generator -o dist/abort-controller.d.ts src/abort-controller.ts && ts-node scripts/fix-dts", + "build:rollup": "rollup -c", + "clean": "rimraf .nyc_output coverage", + "codecov": "codecov", + "coverage": "opener coverage/lcov-report/index.html", + "lint": "eslint . --ext .ts", + "postversion": "git push && git push --tags", + "preversion": "npm test", + "test": "run-s -s lint test:*", + "test:karma": "karma start --single-run", + "test:mocha": "nyc mocha test/*.ts", + "version": "npm run -s build && git add dist/*", + "watch": "run-p -s watch:*", + "watch:karma": "karma start --watch", + "watch:mocha": "mocha test/*.ts --require ts-node/register --watch-extensions ts --watch --growl" + }, + "version": "3.0.0" +} diff --git a/node_modules/abort-controller/polyfill.js b/node_modules/abort-controller/polyfill.js new file mode 100644 index 0000000..3ca8923 --- /dev/null +++ b/node_modules/abort-controller/polyfill.js @@ -0,0 +1,21 @@ +/*globals require, self, window */ +"use strict" + +const ac = require("./dist/abort-controller") + +/*eslint-disable @mysticatea/prettier */ +const g = + typeof self !== "undefined" ? self : + typeof window !== "undefined" ? window : + typeof global !== "undefined" ? global : + /* otherwise */ undefined +/*eslint-enable @mysticatea/prettier */ + +if (g) { + if (typeof g.AbortController === "undefined") { + g.AbortController = ac.AbortController + } + if (typeof g.AbortSignal === "undefined") { + g.AbortSignal = ac.AbortSignal + } +} diff --git a/node_modules/abort-controller/polyfill.mjs b/node_modules/abort-controller/polyfill.mjs new file mode 100644 index 0000000..0602a64 --- /dev/null +++ b/node_modules/abort-controller/polyfill.mjs @@ -0,0 +1,19 @@ +/*globals self, window */ +import * as ac from "./dist/abort-controller" + +/*eslint-disable @mysticatea/prettier */ +const g = + typeof self !== "undefined" ? self : + typeof window !== "undefined" ? window : + typeof global !== "undefined" ? global : + /* otherwise */ undefined +/*eslint-enable @mysticatea/prettier */ + +if (g) { + if (typeof g.AbortController === "undefined") { + g.AbortController = ac.AbortController + } + if (typeof g.AbortSignal === "undefined") { + g.AbortSignal = ac.AbortSignal + } +} diff --git a/node_modules/asynckit/LICENSE b/node_modules/asynckit/LICENSE new file mode 100644 index 0000000..c9eca5d --- /dev/null +++ b/node_modules/asynckit/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Alex Indigo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/asynckit/README.md b/node_modules/asynckit/README.md new file mode 100644 index 0000000..ddcc7e6 --- /dev/null +++ b/node_modules/asynckit/README.md @@ -0,0 +1,233 @@ +# asynckit [![NPM Module](https://img.shields.io/npm/v/asynckit.svg?style=flat)](https://www.npmjs.com/package/asynckit) + +Minimal async jobs utility library, with streams support. + +[![PhantomJS Build](https://img.shields.io/travis/alexindigo/asynckit/v0.4.0.svg?label=browser&style=flat)](https://travis-ci.org/alexindigo/asynckit) +[![Linux Build](https://img.shields.io/travis/alexindigo/asynckit/v0.4.0.svg?label=linux:0.12-6.x&style=flat)](https://travis-ci.org/alexindigo/asynckit) +[![Windows Build](https://img.shields.io/appveyor/ci/alexindigo/asynckit/v0.4.0.svg?label=windows:0.12-6.x&style=flat)](https://ci.appveyor.com/project/alexindigo/asynckit) + +[![Coverage Status](https://img.shields.io/coveralls/alexindigo/asynckit/v0.4.0.svg?label=code+coverage&style=flat)](https://coveralls.io/github/alexindigo/asynckit?branch=master) +[![Dependency Status](https://img.shields.io/david/alexindigo/asynckit/v0.4.0.svg?style=flat)](https://david-dm.org/alexindigo/asynckit) +[![bitHound Overall Score](https://www.bithound.io/github/alexindigo/asynckit/badges/score.svg)](https://www.bithound.io/github/alexindigo/asynckit) + + + +AsyncKit provides harness for `parallel` and `serial` iterators over list of items represented by arrays or objects. +Optionally it accepts abort function (should be synchronously return by iterator for each item), and terminates left over jobs upon an error event. For specific iteration order built-in (`ascending` and `descending`) and custom sort helpers also supported, via `asynckit.serialOrdered` method. + +It ensures async operations to keep behavior more stable and prevent `Maximum call stack size exceeded` errors, from sync iterators. + +| compression | size | +| :----------------- | -------: | +| asynckit.js | 12.34 kB | +| asynckit.min.js | 4.11 kB | +| asynckit.min.js.gz | 1.47 kB | + + +## Install + +```sh +$ npm install --save asynckit +``` + +## Examples + +### Parallel Jobs + +Runs iterator over provided array in parallel. Stores output in the `result` array, +on the matching positions. In unlikely event of an error from one of the jobs, +will terminate rest of the active jobs (if abort function is provided) +and return error along with salvaged data to the main callback function. + +#### Input Array + +```javascript +var parallel = require('asynckit').parallel + , assert = require('assert') + ; + +var source = [ 1, 1, 4, 16, 64, 32, 8, 2 ] + , expectedResult = [ 2, 2, 8, 32, 128, 64, 16, 4 ] + , expectedTarget = [ 1, 1, 2, 4, 8, 16, 32, 64 ] + , target = [] + ; + +parallel(source, asyncJob, function(err, result) +{ + assert.deepEqual(result, expectedResult); + assert.deepEqual(target, expectedTarget); +}); + +// async job accepts one element from the array +// and a callback function +function asyncJob(item, cb) +{ + // different delays (in ms) per item + var delay = item * 25; + + // pretend different jobs take different time to finish + // and not in consequential order + var timeoutId = setTimeout(function() { + target.push(item); + cb(null, item * 2); + }, delay); + + // allow to cancel "leftover" jobs upon error + // return function, invoking of which will abort this job + return clearTimeout.bind(null, timeoutId); +} +``` + +More examples could be found in [test/test-parallel-array.js](test/test-parallel-array.js). + +#### Input Object + +Also it supports named jobs, listed via object. + +```javascript +var parallel = require('asynckit/parallel') + , assert = require('assert') + ; + +var source = { first: 1, one: 1, four: 4, sixteen: 16, sixtyFour: 64, thirtyTwo: 32, eight: 8, two: 2 } + , expectedResult = { first: 2, one: 2, four: 8, sixteen: 32, sixtyFour: 128, thirtyTwo: 64, eight: 16, two: 4 } + , expectedTarget = [ 1, 1, 2, 4, 8, 16, 32, 64 ] + , expectedKeys = [ 'first', 'one', 'two', 'four', 'eight', 'sixteen', 'thirtyTwo', 'sixtyFour' ] + , target = [] + , keys = [] + ; + +parallel(source, asyncJob, function(err, result) +{ + assert.deepEqual(result, expectedResult); + assert.deepEqual(target, expectedTarget); + assert.deepEqual(keys, expectedKeys); +}); + +// supports full value, key, callback (shortcut) interface +function asyncJob(item, key, cb) +{ + // different delays (in ms) per item + var delay = item * 25; + + // pretend different jobs take different time to finish + // and not in consequential order + var timeoutId = setTimeout(function() { + keys.push(key); + target.push(item); + cb(null, item * 2); + }, delay); + + // allow to cancel "leftover" jobs upon error + // return function, invoking of which will abort this job + return clearTimeout.bind(null, timeoutId); +} +``` + +More examples could be found in [test/test-parallel-object.js](test/test-parallel-object.js). + +### Serial Jobs + +Runs iterator over provided array sequentially. Stores output in the `result` array, +on the matching positions. In unlikely event of an error from one of the jobs, +will not proceed to the rest of the items in the list +and return error along with salvaged data to the main callback function. + +#### Input Array + +```javascript +var serial = require('asynckit/serial') + , assert = require('assert') + ; + +var source = [ 1, 1, 4, 16, 64, 32, 8, 2 ] + , expectedResult = [ 2, 2, 8, 32, 128, 64, 16, 4 ] + , expectedTarget = [ 0, 1, 2, 3, 4, 5, 6, 7 ] + , target = [] + ; + +serial(source, asyncJob, function(err, result) +{ + assert.deepEqual(result, expectedResult); + assert.deepEqual(target, expectedTarget); +}); + +// extended interface (item, key, callback) +// also supported for arrays +function asyncJob(item, key, cb) +{ + target.push(key); + + // it will be automatically made async + // even it iterator "returns" in the same event loop + cb(null, item * 2); +} +``` + +More examples could be found in [test/test-serial-array.js](test/test-serial-array.js). + +#### Input Object + +Also it supports named jobs, listed via object. + +```javascript +var serial = require('asynckit').serial + , assert = require('assert') + ; + +var source = [ 1, 1, 4, 16, 64, 32, 8, 2 ] + , expectedResult = [ 2, 2, 8, 32, 128, 64, 16, 4 ] + , expectedTarget = [ 0, 1, 2, 3, 4, 5, 6, 7 ] + , target = [] + ; + +var source = { first: 1, one: 1, four: 4, sixteen: 16, sixtyFour: 64, thirtyTwo: 32, eight: 8, two: 2 } + , expectedResult = { first: 2, one: 2, four: 8, sixteen: 32, sixtyFour: 128, thirtyTwo: 64, eight: 16, two: 4 } + , expectedTarget = [ 1, 1, 4, 16, 64, 32, 8, 2 ] + , target = [] + ; + + +serial(source, asyncJob, function(err, result) +{ + assert.deepEqual(result, expectedResult); + assert.deepEqual(target, expectedTarget); +}); + +// shortcut interface (item, callback) +// works for object as well as for the arrays +function asyncJob(item, cb) +{ + target.push(item); + + // it will be automatically made async + // even it iterator "returns" in the same event loop + cb(null, item * 2); +} +``` + +More examples could be found in [test/test-serial-object.js](test/test-serial-object.js). + +_Note: Since _object_ is an _unordered_ collection of properties, +it may produce unexpected results with sequential iterations. +Whenever order of the jobs' execution is important please use `serialOrdered` method._ + +### Ordered Serial Iterations + +TBD + +For example [compare-property](compare-property) package. + +### Streaming interface + +TBD + +## Want to Know More? + +More examples can be found in [test folder](test/). + +Or open an [issue](https://github.com/alexindigo/asynckit/issues) with questions and/or suggestions. + +## License + +AsyncKit is licensed under the MIT license. diff --git a/node_modules/asynckit/bench.js b/node_modules/asynckit/bench.js new file mode 100644 index 0000000..c612f1a --- /dev/null +++ b/node_modules/asynckit/bench.js @@ -0,0 +1,76 @@ +/* eslint no-console: "off" */ + +var asynckit = require('./') + , async = require('async') + , assert = require('assert') + , expected = 0 + ; + +var Benchmark = require('benchmark'); +var suite = new Benchmark.Suite; + +var source = []; +for (var z = 1; z < 100; z++) +{ + source.push(z); + expected += z; +} + +suite +// add tests + +.add('async.map', function(deferred) +{ + var total = 0; + + async.map(source, + function(i, cb) + { + setImmediate(function() + { + total += i; + cb(null, total); + }); + }, + function(err, result) + { + assert.ifError(err); + assert.equal(result[result.length - 1], expected); + deferred.resolve(); + }); +}, {'defer': true}) + + +.add('asynckit.parallel', function(deferred) +{ + var total = 0; + + asynckit.parallel(source, + function(i, cb) + { + setImmediate(function() + { + total += i; + cb(null, total); + }); + }, + function(err, result) + { + assert.ifError(err); + assert.equal(result[result.length - 1], expected); + deferred.resolve(); + }); +}, {'defer': true}) + + +// add listeners +.on('cycle', function(ev) +{ + console.log(String(ev.target)); +}) +.on('complete', function() +{ + console.log('Fastest is ' + this.filter('fastest').map('name')); +}) +// run async +.run({ 'async': true }); diff --git a/node_modules/asynckit/index.js b/node_modules/asynckit/index.js new file mode 100644 index 0000000..455f945 --- /dev/null +++ b/node_modules/asynckit/index.js @@ -0,0 +1,6 @@ +module.exports = +{ + parallel : require('./parallel.js'), + serial : require('./serial.js'), + serialOrdered : require('./serialOrdered.js') +}; diff --git a/node_modules/asynckit/lib/abort.js b/node_modules/asynckit/lib/abort.js new file mode 100644 index 0000000..114367e --- /dev/null +++ b/node_modules/asynckit/lib/abort.js @@ -0,0 +1,29 @@ +// API +module.exports = abort; + +/** + * Aborts leftover active jobs + * + * @param {object} state - current state object + */ +function abort(state) +{ + Object.keys(state.jobs).forEach(clean.bind(state)); + + // reset leftover jobs + state.jobs = {}; +} + +/** + * Cleans up leftover job by invoking abort function for the provided job id + * + * @this state + * @param {string|number} key - job id to abort + */ +function clean(key) +{ + if (typeof this.jobs[key] == 'function') + { + this.jobs[key](); + } +} diff --git a/node_modules/asynckit/lib/async.js b/node_modules/asynckit/lib/async.js new file mode 100644 index 0000000..7f1288a --- /dev/null +++ b/node_modules/asynckit/lib/async.js @@ -0,0 +1,34 @@ +var defer = require('./defer.js'); + +// API +module.exports = async; + +/** + * Runs provided callback asynchronously + * even if callback itself is not + * + * @param {function} callback - callback to invoke + * @returns {function} - augmented callback + */ +function async(callback) +{ + var isAsync = false; + + // check if async happened + defer(function() { isAsync = true; }); + + return function async_callback(err, result) + { + if (isAsync) + { + callback(err, result); + } + else + { + defer(function nextTick_callback() + { + callback(err, result); + }); + } + }; +} diff --git a/node_modules/asynckit/lib/defer.js b/node_modules/asynckit/lib/defer.js new file mode 100644 index 0000000..b67110c --- /dev/null +++ b/node_modules/asynckit/lib/defer.js @@ -0,0 +1,26 @@ +module.exports = defer; + +/** + * Runs provided function on next iteration of the event loop + * + * @param {function} fn - function to run + */ +function defer(fn) +{ + var nextTick = typeof setImmediate == 'function' + ? setImmediate + : ( + typeof process == 'object' && typeof process.nextTick == 'function' + ? process.nextTick + : null + ); + + if (nextTick) + { + nextTick(fn); + } + else + { + setTimeout(fn, 0); + } +} diff --git a/node_modules/asynckit/lib/iterate.js b/node_modules/asynckit/lib/iterate.js new file mode 100644 index 0000000..5d2839a --- /dev/null +++ b/node_modules/asynckit/lib/iterate.js @@ -0,0 +1,75 @@ +var async = require('./async.js') + , abort = require('./abort.js') + ; + +// API +module.exports = iterate; + +/** + * Iterates over each job object + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {object} state - current job status + * @param {function} callback - invoked when all elements processed + */ +function iterate(list, iterator, state, callback) +{ + // store current index + var key = state['keyedList'] ? state['keyedList'][state.index] : state.index; + + state.jobs[key] = runJob(iterator, key, list[key], function(error, output) + { + // don't repeat yourself + // skip secondary callbacks + if (!(key in state.jobs)) + { + return; + } + + // clean up jobs + delete state.jobs[key]; + + if (error) + { + // don't process rest of the results + // stop still active jobs + // and reset the list + abort(state); + } + else + { + state.results[key] = output; + } + + // return salvaged results + callback(error, state.results); + }); +} + +/** + * Runs iterator over provided job element + * + * @param {function} iterator - iterator to invoke + * @param {string|number} key - key/index of the element in the list of jobs + * @param {mixed} item - job description + * @param {function} callback - invoked after iterator is done with the job + * @returns {function|mixed} - job abort function or something else + */ +function runJob(iterator, key, item, callback) +{ + var aborter; + + // allow shortcut if iterator expects only two arguments + if (iterator.length == 2) + { + aborter = iterator(item, async(callback)); + } + // otherwise go with full three arguments + else + { + aborter = iterator(item, key, async(callback)); + } + + return aborter; +} diff --git a/node_modules/asynckit/lib/readable_asynckit.js b/node_modules/asynckit/lib/readable_asynckit.js new file mode 100644 index 0000000..78ad240 --- /dev/null +++ b/node_modules/asynckit/lib/readable_asynckit.js @@ -0,0 +1,91 @@ +var streamify = require('./streamify.js') + , defer = require('./defer.js') + ; + +// API +module.exports = ReadableAsyncKit; + +/** + * Base constructor for all streams + * used to hold properties/methods + */ +function ReadableAsyncKit() +{ + ReadableAsyncKit.super_.apply(this, arguments); + + // list of active jobs + this.jobs = {}; + + // add stream methods + this.destroy = destroy; + this._start = _start; + this._read = _read; +} + +/** + * Destroys readable stream, + * by aborting outstanding jobs + * + * @returns {void} + */ +function destroy() +{ + if (this.destroyed) + { + return; + } + + this.destroyed = true; + + if (typeof this.terminator == 'function') + { + this.terminator(); + } +} + +/** + * Starts provided jobs in async manner + * + * @private + */ +function _start() +{ + // first argument – runner function + var runner = arguments[0] + // take away first argument + , args = Array.prototype.slice.call(arguments, 1) + // second argument - input data + , input = args[0] + // last argument - result callback + , endCb = streamify.callback.call(this, args[args.length - 1]) + ; + + args[args.length - 1] = endCb; + // third argument - iterator + args[1] = streamify.iterator.call(this, args[1]); + + // allow time for proper setup + defer(function() + { + if (!this.destroyed) + { + this.terminator = runner.apply(null, args); + } + else + { + endCb(null, Array.isArray(input) ? [] : {}); + } + }.bind(this)); +} + + +/** + * Implement _read to comply with Readable streams + * Doesn't really make sense for flowing object mode + * + * @private + */ +function _read() +{ + +} diff --git a/node_modules/asynckit/lib/readable_parallel.js b/node_modules/asynckit/lib/readable_parallel.js new file mode 100644 index 0000000..5d2929f --- /dev/null +++ b/node_modules/asynckit/lib/readable_parallel.js @@ -0,0 +1,25 @@ +var parallel = require('../parallel.js'); + +// API +module.exports = ReadableParallel; + +/** + * Streaming wrapper to `asynckit.parallel` + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {stream.Readable#} + */ +function ReadableParallel(list, iterator, callback) +{ + if (!(this instanceof ReadableParallel)) + { + return new ReadableParallel(list, iterator, callback); + } + + // turn on object mode + ReadableParallel.super_.call(this, {objectMode: true}); + + this._start(parallel, list, iterator, callback); +} diff --git a/node_modules/asynckit/lib/readable_serial.js b/node_modules/asynckit/lib/readable_serial.js new file mode 100644 index 0000000..7822698 --- /dev/null +++ b/node_modules/asynckit/lib/readable_serial.js @@ -0,0 +1,25 @@ +var serial = require('../serial.js'); + +// API +module.exports = ReadableSerial; + +/** + * Streaming wrapper to `asynckit.serial` + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {stream.Readable#} + */ +function ReadableSerial(list, iterator, callback) +{ + if (!(this instanceof ReadableSerial)) + { + return new ReadableSerial(list, iterator, callback); + } + + // turn on object mode + ReadableSerial.super_.call(this, {objectMode: true}); + + this._start(serial, list, iterator, callback); +} diff --git a/node_modules/asynckit/lib/readable_serial_ordered.js b/node_modules/asynckit/lib/readable_serial_ordered.js new file mode 100644 index 0000000..3de89c4 --- /dev/null +++ b/node_modules/asynckit/lib/readable_serial_ordered.js @@ -0,0 +1,29 @@ +var serialOrdered = require('../serialOrdered.js'); + +// API +module.exports = ReadableSerialOrdered; +// expose sort helpers +module.exports.ascending = serialOrdered.ascending; +module.exports.descending = serialOrdered.descending; + +/** + * Streaming wrapper to `asynckit.serialOrdered` + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} sortMethod - custom sort function + * @param {function} callback - invoked when all elements processed + * @returns {stream.Readable#} + */ +function ReadableSerialOrdered(list, iterator, sortMethod, callback) +{ + if (!(this instanceof ReadableSerialOrdered)) + { + return new ReadableSerialOrdered(list, iterator, sortMethod, callback); + } + + // turn on object mode + ReadableSerialOrdered.super_.call(this, {objectMode: true}); + + this._start(serialOrdered, list, iterator, sortMethod, callback); +} diff --git a/node_modules/asynckit/lib/state.js b/node_modules/asynckit/lib/state.js new file mode 100644 index 0000000..cbea7ad --- /dev/null +++ b/node_modules/asynckit/lib/state.js @@ -0,0 +1,37 @@ +// API +module.exports = state; + +/** + * Creates initial state object + * for iteration over list + * + * @param {array|object} list - list to iterate over + * @param {function|null} sortMethod - function to use for keys sort, + * or `null` to keep them as is + * @returns {object} - initial state object + */ +function state(list, sortMethod) +{ + var isNamedList = !Array.isArray(list) + , initState = + { + index : 0, + keyedList: isNamedList || sortMethod ? Object.keys(list) : null, + jobs : {}, + results : isNamedList ? {} : [], + size : isNamedList ? Object.keys(list).length : list.length + } + ; + + if (sortMethod) + { + // sort array keys based on it's values + // sort object's keys just on own merit + initState.keyedList.sort(isNamedList ? sortMethod : function(a, b) + { + return sortMethod(list[a], list[b]); + }); + } + + return initState; +} diff --git a/node_modules/asynckit/lib/streamify.js b/node_modules/asynckit/lib/streamify.js new file mode 100644 index 0000000..f56a1c9 --- /dev/null +++ b/node_modules/asynckit/lib/streamify.js @@ -0,0 +1,141 @@ +var async = require('./async.js'); + +// API +module.exports = { + iterator: wrapIterator, + callback: wrapCallback +}; + +/** + * Wraps iterators with long signature + * + * @this ReadableAsyncKit# + * @param {function} iterator - function to wrap + * @returns {function} - wrapped function + */ +function wrapIterator(iterator) +{ + var stream = this; + + return function(item, key, cb) + { + var aborter + , wrappedCb = async(wrapIteratorCallback.call(stream, cb, key)) + ; + + stream.jobs[key] = wrappedCb; + + // it's either shortcut (item, cb) + if (iterator.length == 2) + { + aborter = iterator(item, wrappedCb); + } + // or long format (item, key, cb) + else + { + aborter = iterator(item, key, wrappedCb); + } + + return aborter; + }; +} + +/** + * Wraps provided callback function + * allowing to execute snitch function before + * real callback + * + * @this ReadableAsyncKit# + * @param {function} callback - function to wrap + * @returns {function} - wrapped function + */ +function wrapCallback(callback) +{ + var stream = this; + + var wrapped = function(error, result) + { + return finisher.call(stream, error, result, callback); + }; + + return wrapped; +} + +/** + * Wraps provided iterator callback function + * makes sure snitch only called once, + * but passes secondary calls to the original callback + * + * @this ReadableAsyncKit# + * @param {function} callback - callback to wrap + * @param {number|string} key - iteration key + * @returns {function} wrapped callback + */ +function wrapIteratorCallback(callback, key) +{ + var stream = this; + + return function(error, output) + { + // don't repeat yourself + if (!(key in stream.jobs)) + { + callback(error, output); + return; + } + + // clean up jobs + delete stream.jobs[key]; + + return streamer.call(stream, error, {key: key, value: output}, callback); + }; +} + +/** + * Stream wrapper for iterator callback + * + * @this ReadableAsyncKit# + * @param {mixed} error - error response + * @param {mixed} output - iterator output + * @param {function} callback - callback that expects iterator results + */ +function streamer(error, output, callback) +{ + if (error && !this.error) + { + this.error = error; + this.pause(); + this.emit('error', error); + // send back value only, as expected + callback(error, output && output.value); + return; + } + + // stream stuff + this.push(output); + + // back to original track + // send back value only, as expected + callback(error, output && output.value); +} + +/** + * Stream wrapper for finishing callback + * + * @this ReadableAsyncKit# + * @param {mixed} error - error response + * @param {mixed} output - iterator output + * @param {function} callback - callback that expects final results + */ +function finisher(error, output, callback) +{ + // signal end of the stream + // only for successfully finished streams + if (!error) + { + this.push(null); + } + + // back to original track + callback(error, output); +} diff --git a/node_modules/asynckit/lib/terminator.js b/node_modules/asynckit/lib/terminator.js new file mode 100644 index 0000000..d6eb992 --- /dev/null +++ b/node_modules/asynckit/lib/terminator.js @@ -0,0 +1,29 @@ +var abort = require('./abort.js') + , async = require('./async.js') + ; + +// API +module.exports = terminator; + +/** + * Terminates jobs in the attached state context + * + * @this AsyncKitState# + * @param {function} callback - final callback to invoke after termination + */ +function terminator(callback) +{ + if (!Object.keys(this.jobs).length) + { + return; + } + + // fast forward iteration index + this.index = this.size; + + // abort jobs + abort(this); + + // send back results we have so far + async(callback)(null, this.results); +} diff --git a/node_modules/asynckit/package.json b/node_modules/asynckit/package.json new file mode 100644 index 0000000..d903307 --- /dev/null +++ b/node_modules/asynckit/package.json @@ -0,0 +1,91 @@ +{ + "_from": "asynckit@^0.4.0", + "_id": "asynckit@0.4.0", + "_inBundle": false, + "_integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "_location": "/asynckit", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "asynckit@^0.4.0", + "name": "asynckit", + "escapedName": "asynckit", + "rawSpec": "^0.4.0", + "saveSpec": null, + "fetchSpec": "^0.4.0" + }, + "_requiredBy": [ + "/@discordjs/form-data" + ], + "_resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "_shasum": "c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79", + "_spec": "asynckit@^0.4.0", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\@discordjs\\form-data", + "author": { + "name": "Alex Indigo", + "email": "iam@alexindigo.com" + }, + "bugs": { + "url": "https://github.com/alexindigo/asynckit/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Minimal async jobs utility library, with streams support", + "devDependencies": { + "browserify": "^13.0.0", + "browserify-istanbul": "^2.0.0", + "coveralls": "^2.11.9", + "eslint": "^2.9.0", + "istanbul": "^0.4.3", + "obake": "^0.1.2", + "phantomjs-prebuilt": "^2.1.7", + "pre-commit": "^1.1.3", + "reamde": "^1.1.0", + "rimraf": "^2.5.2", + "size-table": "^0.2.0", + "tap-spec": "^4.1.1", + "tape": "^4.5.1" + }, + "homepage": "https://github.com/alexindigo/asynckit#readme", + "keywords": [ + "async", + "jobs", + "parallel", + "serial", + "iterator", + "array", + "object", + "stream", + "destroy", + "terminate", + "abort" + ], + "license": "MIT", + "main": "index.js", + "name": "asynckit", + "pre-commit": [ + "clean", + "lint", + "test", + "browser", + "report", + "size" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/alexindigo/asynckit.git" + }, + "scripts": { + "browser": "browserify -t browserify-istanbul test/lib/browserify_adjustment.js test/test-*.js | obake --coverage | tap-spec", + "clean": "rimraf coverage", + "debug": "tape test/test-*.js", + "lint": "eslint *.js lib/*.js test/*.js", + "report": "istanbul report", + "size": "browserify index.js | size-table asynckit", + "test": "istanbul cover --reporter=json tape -- 'test/test-*.js' | tap-spec", + "win-test": "tape test/test-*.js" + }, + "version": "0.4.0" +} diff --git a/node_modules/asynckit/parallel.js b/node_modules/asynckit/parallel.js new file mode 100644 index 0000000..3c50344 --- /dev/null +++ b/node_modules/asynckit/parallel.js @@ -0,0 +1,43 @@ +var iterate = require('./lib/iterate.js') + , initState = require('./lib/state.js') + , terminator = require('./lib/terminator.js') + ; + +// Public API +module.exports = parallel; + +/** + * Runs iterator over provided array elements in parallel + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function parallel(list, iterator, callback) +{ + var state = initState(list); + + while (state.index < (state['keyedList'] || list).length) + { + iterate(list, iterator, state, function(error, result) + { + if (error) + { + callback(error, result); + return; + } + + // looks like it's the last one + if (Object.keys(state.jobs).length === 0) + { + callback(null, state.results); + return; + } + }); + + state.index++; + } + + return terminator.bind(state, callback); +} diff --git a/node_modules/asynckit/serial.js b/node_modules/asynckit/serial.js new file mode 100644 index 0000000..6cd949a --- /dev/null +++ b/node_modules/asynckit/serial.js @@ -0,0 +1,17 @@ +var serialOrdered = require('./serialOrdered.js'); + +// Public API +module.exports = serial; + +/** + * Runs iterator over provided array elements in series + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function serial(list, iterator, callback) +{ + return serialOrdered(list, iterator, null, callback); +} diff --git a/node_modules/asynckit/serialOrdered.js b/node_modules/asynckit/serialOrdered.js new file mode 100644 index 0000000..607eafe --- /dev/null +++ b/node_modules/asynckit/serialOrdered.js @@ -0,0 +1,75 @@ +var iterate = require('./lib/iterate.js') + , initState = require('./lib/state.js') + , terminator = require('./lib/terminator.js') + ; + +// Public API +module.exports = serialOrdered; +// sorting helpers +module.exports.ascending = ascending; +module.exports.descending = descending; + +/** + * Runs iterator over provided sorted array elements in series + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} sortMethod - custom sort function + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function serialOrdered(list, iterator, sortMethod, callback) +{ + var state = initState(list, sortMethod); + + iterate(list, iterator, state, function iteratorHandler(error, result) + { + if (error) + { + callback(error, result); + return; + } + + state.index++; + + // are we there yet? + if (state.index < (state['keyedList'] || list).length) + { + iterate(list, iterator, state, iteratorHandler); + return; + } + + // done here + callback(null, state.results); + }); + + return terminator.bind(state, callback); +} + +/* + * -- Sort methods + */ + +/** + * sort helper to sort array elements in ascending order + * + * @param {mixed} a - an item to compare + * @param {mixed} b - an item to compare + * @returns {number} - comparison result + */ +function ascending(a, b) +{ + return a < b ? -1 : a > b ? 1 : 0; +} + +/** + * sort helper to sort array elements in descending order + * + * @param {mixed} a - an item to compare + * @param {mixed} b - an item to compare + * @returns {number} - comparison result + */ +function descending(a, b) +{ + return -1 * ascending(a, b); +} diff --git a/node_modules/asynckit/stream.js b/node_modules/asynckit/stream.js new file mode 100644 index 0000000..d43465f --- /dev/null +++ b/node_modules/asynckit/stream.js @@ -0,0 +1,21 @@ +var inherits = require('util').inherits + , Readable = require('stream').Readable + , ReadableAsyncKit = require('./lib/readable_asynckit.js') + , ReadableParallel = require('./lib/readable_parallel.js') + , ReadableSerial = require('./lib/readable_serial.js') + , ReadableSerialOrdered = require('./lib/readable_serial_ordered.js') + ; + +// API +module.exports = +{ + parallel : ReadableParallel, + serial : ReadableSerial, + serialOrdered : ReadableSerialOrdered, +}; + +inherits(ReadableAsyncKit, Readable); + +inherits(ReadableParallel, ReadableAsyncKit); +inherits(ReadableSerial, ReadableAsyncKit); +inherits(ReadableSerialOrdered, ReadableAsyncKit); diff --git a/node_modules/combined-stream/License b/node_modules/combined-stream/License new file mode 100644 index 0000000..4804b7a --- /dev/null +++ b/node_modules/combined-stream/License @@ -0,0 +1,19 @@ +Copyright (c) 2011 Debuggable Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/combined-stream/Readme.md b/node_modules/combined-stream/Readme.md new file mode 100644 index 0000000..9e367b5 --- /dev/null +++ b/node_modules/combined-stream/Readme.md @@ -0,0 +1,138 @@ +# combined-stream + +A stream that emits multiple other streams one after another. + +**NB** Currently `combined-stream` works with streams version 1 only. There is ongoing effort to switch this library to streams version 2. Any help is welcome. :) Meanwhile you can explore other libraries that provide streams2 support with more or less compatibility with `combined-stream`. + +- [combined-stream2](https://www.npmjs.com/package/combined-stream2): A drop-in streams2-compatible replacement for the combined-stream module. + +- [multistream](https://www.npmjs.com/package/multistream): A stream that emits multiple other streams one after another. + +## Installation + +``` bash +npm install combined-stream +``` + +## Usage + +Here is a simple example that shows how you can use combined-stream to combine +two files into one: + +``` javascript +var CombinedStream = require('combined-stream'); +var fs = require('fs'); + +var combinedStream = CombinedStream.create(); +combinedStream.append(fs.createReadStream('file1.txt')); +combinedStream.append(fs.createReadStream('file2.txt')); + +combinedStream.pipe(fs.createWriteStream('combined.txt')); +``` + +While the example above works great, it will pause all source streams until +they are needed. If you don't want that to happen, you can set `pauseStreams` +to `false`: + +``` javascript +var CombinedStream = require('combined-stream'); +var fs = require('fs'); + +var combinedStream = CombinedStream.create({pauseStreams: false}); +combinedStream.append(fs.createReadStream('file1.txt')); +combinedStream.append(fs.createReadStream('file2.txt')); + +combinedStream.pipe(fs.createWriteStream('combined.txt')); +``` + +However, what if you don't have all the source streams yet, or you don't want +to allocate the resources (file descriptors, memory, etc.) for them right away? +Well, in that case you can simply provide a callback that supplies the stream +by calling a `next()` function: + +``` javascript +var CombinedStream = require('combined-stream'); +var fs = require('fs'); + +var combinedStream = CombinedStream.create(); +combinedStream.append(function(next) { + next(fs.createReadStream('file1.txt')); +}); +combinedStream.append(function(next) { + next(fs.createReadStream('file2.txt')); +}); + +combinedStream.pipe(fs.createWriteStream('combined.txt')); +``` + +## API + +### CombinedStream.create([options]) + +Returns a new combined stream object. Available options are: + +* `maxDataSize` +* `pauseStreams` + +The effect of those options is described below. + +### combinedStream.pauseStreams = `true` + +Whether to apply back pressure to the underlaying streams. If set to `false`, +the underlaying streams will never be paused. If set to `true`, the +underlaying streams will be paused right after being appended, as well as when +`delayedStream.pipe()` wants to throttle. + +### combinedStream.maxDataSize = `2 * 1024 * 1024` + +The maximum amount of bytes (or characters) to buffer for all source streams. +If this value is exceeded, `combinedStream` emits an `'error'` event. + +### combinedStream.dataSize = `0` + +The amount of bytes (or characters) currently buffered by `combinedStream`. + +### combinedStream.append(stream) + +Appends the given `stream` to the combinedStream object. If `pauseStreams` is +set to `true, this stream will also be paused right away. + +`streams` can also be a function that takes one parameter called `next`. `next` +is a function that must be invoked in order to provide the `next` stream, see +example above. + +Regardless of how the `stream` is appended, combined-stream always attaches an +`'error'` listener to it, so you don't have to do that manually. + +Special case: `stream` can also be a String or Buffer. + +### combinedStream.write(data) + +You should not call this, `combinedStream` takes care of piping the appended +streams into itself for you. + +### combinedStream.resume() + +Causes `combinedStream` to start drain the streams it manages. The function is +idempotent, and also emits a `'resume'` event each time which usually goes to +the stream that is currently being drained. + +### combinedStream.pause(); + +If `combinedStream.pauseStreams` is set to `false`, this does nothing. +Otherwise a `'pause'` event is emitted, this goes to the stream that is +currently being drained, so you can use it to apply back pressure. + +### combinedStream.end(); + +Sets `combinedStream.writable` to false, emits an `'end'` event, and removes +all streams from the queue. + +### combinedStream.destroy(); + +Same as `combinedStream.end()`, except it emits a `'close'` event instead of +`'end'`. + +## License + +combined-stream is licensed under the MIT license. diff --git a/node_modules/combined-stream/lib/combined_stream.js b/node_modules/combined-stream/lib/combined_stream.js new file mode 100644 index 0000000..125f097 --- /dev/null +++ b/node_modules/combined-stream/lib/combined_stream.js @@ -0,0 +1,208 @@ +var util = require('util'); +var Stream = require('stream').Stream; +var DelayedStream = require('delayed-stream'); + +module.exports = CombinedStream; +function CombinedStream() { + this.writable = false; + this.readable = true; + this.dataSize = 0; + this.maxDataSize = 2 * 1024 * 1024; + this.pauseStreams = true; + + this._released = false; + this._streams = []; + this._currentStream = null; + this._insideLoop = false; + this._pendingNext = false; +} +util.inherits(CombinedStream, Stream); + +CombinedStream.create = function(options) { + var combinedStream = new this(); + + options = options || {}; + for (var option in options) { + combinedStream[option] = options[option]; + } + + return combinedStream; +}; + +CombinedStream.isStreamLike = function(stream) { + return (typeof stream !== 'function') + && (typeof stream !== 'string') + && (typeof stream !== 'boolean') + && (typeof stream !== 'number') + && (!Buffer.isBuffer(stream)); +}; + +CombinedStream.prototype.append = function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + + if (isStreamLike) { + if (!(stream instanceof DelayedStream)) { + var newStream = DelayedStream.create(stream, { + maxDataSize: Infinity, + pauseStream: this.pauseStreams, + }); + stream.on('data', this._checkDataSize.bind(this)); + stream = newStream; + } + + this._handleErrors(stream); + + if (this.pauseStreams) { + stream.pause(); + } + } + + this._streams.push(stream); + return this; +}; + +CombinedStream.prototype.pipe = function(dest, options) { + Stream.prototype.pipe.call(this, dest, options); + this.resume(); + return dest; +}; + +CombinedStream.prototype._getNext = function() { + this._currentStream = null; + + if (this._insideLoop) { + this._pendingNext = true; + return; // defer call + } + + this._insideLoop = true; + try { + do { + this._pendingNext = false; + this._realGetNext(); + } while (this._pendingNext); + } finally { + this._insideLoop = false; + } +}; + +CombinedStream.prototype._realGetNext = function() { + var stream = this._streams.shift(); + + + if (typeof stream == 'undefined') { + this.end(); + return; + } + + if (typeof stream !== 'function') { + this._pipeNext(stream); + return; + } + + var getStream = stream; + getStream(function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('data', this._checkDataSize.bind(this)); + this._handleErrors(stream); + } + + this._pipeNext(stream); + }.bind(this)); +}; + +CombinedStream.prototype._pipeNext = function(stream) { + this._currentStream = stream; + + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('end', this._getNext.bind(this)); + stream.pipe(this, {end: false}); + return; + } + + var value = stream; + this.write(value); + this._getNext(); +}; + +CombinedStream.prototype._handleErrors = function(stream) { + var self = this; + stream.on('error', function(err) { + self._emitError(err); + }); +}; + +CombinedStream.prototype.write = function(data) { + this.emit('data', data); +}; + +CombinedStream.prototype.pause = function() { + if (!this.pauseStreams) { + return; + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.pause) == 'function') this._currentStream.pause(); + this.emit('pause'); +}; + +CombinedStream.prototype.resume = function() { + if (!this._released) { + this._released = true; + this.writable = true; + this._getNext(); + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.resume) == 'function') this._currentStream.resume(); + this.emit('resume'); +}; + +CombinedStream.prototype.end = function() { + this._reset(); + this.emit('end'); +}; + +CombinedStream.prototype.destroy = function() { + this._reset(); + this.emit('close'); +}; + +CombinedStream.prototype._reset = function() { + this.writable = false; + this._streams = []; + this._currentStream = null; +}; + +CombinedStream.prototype._checkDataSize = function() { + this._updateDataSize(); + if (this.dataSize <= this.maxDataSize) { + return; + } + + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'; + this._emitError(new Error(message)); +}; + +CombinedStream.prototype._updateDataSize = function() { + this.dataSize = 0; + + var self = this; + this._streams.forEach(function(stream) { + if (!stream.dataSize) { + return; + } + + self.dataSize += stream.dataSize; + }); + + if (this._currentStream && this._currentStream.dataSize) { + this.dataSize += this._currentStream.dataSize; + } +}; + +CombinedStream.prototype._emitError = function(err) { + this._reset(); + this.emit('error', err); +}; diff --git a/node_modules/combined-stream/package.json b/node_modules/combined-stream/package.json new file mode 100644 index 0000000..8b594ee --- /dev/null +++ b/node_modules/combined-stream/package.json @@ -0,0 +1,57 @@ +{ + "_from": "combined-stream@^1.0.8", + "_id": "combined-stream@1.0.8", + "_inBundle": false, + "_integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "_location": "/combined-stream", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "combined-stream@^1.0.8", + "name": "combined-stream", + "escapedName": "combined-stream", + "rawSpec": "^1.0.8", + "saveSpec": null, + "fetchSpec": "^1.0.8" + }, + "_requiredBy": [ + "/@discordjs/form-data" + ], + "_resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "_shasum": "c3d45a8b34fd730631a110a8a2520682b31d5a7f", + "_spec": "combined-stream@^1.0.8", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\@discordjs\\form-data", + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "bugs": { + "url": "https://github.com/felixge/node-combined-stream/issues" + }, + "bundleDependencies": false, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "deprecated": false, + "description": "A stream that emits multiple other streams one after another.", + "devDependencies": { + "far": "~0.0.7" + }, + "engines": { + "node": ">= 0.8" + }, + "homepage": "https://github.com/felixge/node-combined-stream", + "license": "MIT", + "main": "./lib/combined_stream", + "name": "combined-stream", + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-combined-stream.git" + }, + "scripts": { + "test": "node test/run.js" + }, + "version": "1.0.8" +} diff --git a/node_modules/combined-stream/yarn.lock b/node_modules/combined-stream/yarn.lock new file mode 100644 index 0000000..7edf418 --- /dev/null +++ b/node_modules/combined-stream/yarn.lock @@ -0,0 +1,17 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +far@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/far/-/far-0.0.7.tgz#01c1fd362bcd26ce9cf161af3938aa34619f79a7" + dependencies: + oop "0.0.3" + +oop@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/oop/-/oop-0.0.3.tgz#70fa405a5650891a194fdc82ca68dad6dabf4401" diff --git a/node_modules/delayed-stream/.npmignore b/node_modules/delayed-stream/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/node_modules/delayed-stream/.npmignore @@ -0,0 +1 @@ +test diff --git a/node_modules/delayed-stream/License b/node_modules/delayed-stream/License new file mode 100644 index 0000000..4804b7a --- /dev/null +++ b/node_modules/delayed-stream/License @@ -0,0 +1,19 @@ +Copyright (c) 2011 Debuggable Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/delayed-stream/Makefile b/node_modules/delayed-stream/Makefile new file mode 100644 index 0000000..b4ff85a --- /dev/null +++ b/node_modules/delayed-stream/Makefile @@ -0,0 +1,7 @@ +SHELL := /bin/bash + +test: + @./test/run.js + +.PHONY: test + diff --git a/node_modules/delayed-stream/Readme.md b/node_modules/delayed-stream/Readme.md new file mode 100644 index 0000000..aca36f9 --- /dev/null +++ b/node_modules/delayed-stream/Readme.md @@ -0,0 +1,141 @@ +# delayed-stream + +Buffers events from a stream until you are ready to handle them. + +## Installation + +``` bash +npm install delayed-stream +``` + +## Usage + +The following example shows how to write a http echo server that delays its +response by 1000 ms. + +``` javascript +var DelayedStream = require('delayed-stream'); +var http = require('http'); + +http.createServer(function(req, res) { + var delayed = DelayedStream.create(req); + + setTimeout(function() { + res.writeHead(200); + delayed.pipe(res); + }, 1000); +}); +``` + +If you are not using `Stream#pipe`, you can also manually release the buffered +events by calling `delayedStream.resume()`: + +``` javascript +var delayed = DelayedStream.create(req); + +setTimeout(function() { + // Emit all buffered events and resume underlaying source + delayed.resume(); +}, 1000); +``` + +## Implementation + +In order to use this meta stream properly, here are a few things you should +know about the implementation. + +### Event Buffering / Proxying + +All events of the `source` stream are hijacked by overwriting the `source.emit` +method. Until node implements a catch-all event listener, this is the only way. + +However, delayed-stream still continues to emit all events it captures on the +`source`, regardless of whether you have released the delayed stream yet or +not. + +Upon creation, delayed-stream captures all `source` events and stores them in +an internal event buffer. Once `delayedStream.release()` is called, all +buffered events are emitted on the `delayedStream`, and the event buffer is +cleared. After that, delayed-stream merely acts as a proxy for the underlaying +source. + +### Error handling + +Error events on `source` are buffered / proxied just like any other events. +However, `delayedStream.create` attaches a no-op `'error'` listener to the +`source`. This way you only have to handle errors on the `delayedStream` +object, rather than in two places. + +### Buffer limits + +delayed-stream provides a `maxDataSize` property that can be used to limit +the amount of data being buffered. In order to protect you from bad `source` +streams that don't react to `source.pause()`, this feature is enabled by +default. + +## API + +### DelayedStream.create(source, [options]) + +Returns a new `delayedStream`. Available options are: + +* `pauseStream` +* `maxDataSize` + +The description for those properties can be found below. + +### delayedStream.source + +The `source` stream managed by this object. This is useful if you are +passing your `delayedStream` around, and you still want to access properties +on the `source` object. + +### delayedStream.pauseStream = true + +Whether to pause the underlaying `source` when calling +`DelayedStream.create()`. Modifying this property afterwards has no effect. + +### delayedStream.maxDataSize = 1024 * 1024 + +The amount of data to buffer before emitting an `error`. + +If the underlaying source is emitting `Buffer` objects, the `maxDataSize` +refers to bytes. + +If the underlaying source is emitting JavaScript strings, the size refers to +characters. + +If you know what you are doing, you can set this property to `Infinity` to +disable this feature. You can also modify this property during runtime. + +### delayedStream.dataSize = 0 + +The amount of data buffered so far. + +### delayedStream.readable + +An ECMA5 getter that returns the value of `source.readable`. + +### delayedStream.resume() + +If the `delayedStream` has not been released so far, `delayedStream.release()` +is called. + +In either case, `source.resume()` is called. + +### delayedStream.pause() + +Calls `source.pause()`. + +### delayedStream.pipe(dest) + +Calls `delayedStream.resume()` and then proxies the arguments to `source.pipe`. + +### delayedStream.release() + +Emits and clears all events that have been buffered up so far. This does not +resume the underlaying source, use `delayedStream.resume()` instead. + +## License + +delayed-stream is licensed under the MIT license. diff --git a/node_modules/delayed-stream/lib/delayed_stream.js b/node_modules/delayed-stream/lib/delayed_stream.js new file mode 100644 index 0000000..b38fc85 --- /dev/null +++ b/node_modules/delayed-stream/lib/delayed_stream.js @@ -0,0 +1,107 @@ +var Stream = require('stream').Stream; +var util = require('util'); + +module.exports = DelayedStream; +function DelayedStream() { + this.source = null; + this.dataSize = 0; + this.maxDataSize = 1024 * 1024; + this.pauseStream = true; + + this._maxDataSizeExceeded = false; + this._released = false; + this._bufferedEvents = []; +} +util.inherits(DelayedStream, Stream); + +DelayedStream.create = function(source, options) { + var delayedStream = new this(); + + options = options || {}; + for (var option in options) { + delayedStream[option] = options[option]; + } + + delayedStream.source = source; + + var realEmit = source.emit; + source.emit = function() { + delayedStream._handleEmit(arguments); + return realEmit.apply(source, arguments); + }; + + source.on('error', function() {}); + if (delayedStream.pauseStream) { + source.pause(); + } + + return delayedStream; +}; + +Object.defineProperty(DelayedStream.prototype, 'readable', { + configurable: true, + enumerable: true, + get: function() { + return this.source.readable; + } +}); + +DelayedStream.prototype.setEncoding = function() { + return this.source.setEncoding.apply(this.source, arguments); +}; + +DelayedStream.prototype.resume = function() { + if (!this._released) { + this.release(); + } + + this.source.resume(); +}; + +DelayedStream.prototype.pause = function() { + this.source.pause(); +}; + +DelayedStream.prototype.release = function() { + this._released = true; + + this._bufferedEvents.forEach(function(args) { + this.emit.apply(this, args); + }.bind(this)); + this._bufferedEvents = []; +}; + +DelayedStream.prototype.pipe = function() { + var r = Stream.prototype.pipe.apply(this, arguments); + this.resume(); + return r; +}; + +DelayedStream.prototype._handleEmit = function(args) { + if (this._released) { + this.emit.apply(this, args); + return; + } + + if (args[0] === 'data') { + this.dataSize += args[1].length; + this._checkIfMaxDataSizeExceeded(); + } + + this._bufferedEvents.push(args); +}; + +DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() { + if (this._maxDataSizeExceeded) { + return; + } + + if (this.dataSize <= this.maxDataSize) { + return; + } + + this._maxDataSizeExceeded = true; + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.' + this.emit('error', new Error(message)); +}; diff --git a/node_modules/delayed-stream/package.json b/node_modules/delayed-stream/package.json new file mode 100644 index 0000000..e9f457d --- /dev/null +++ b/node_modules/delayed-stream/package.json @@ -0,0 +1,62 @@ +{ + "_from": "delayed-stream@~1.0.0", + "_id": "delayed-stream@1.0.0", + "_inBundle": false, + "_integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "_location": "/delayed-stream", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "delayed-stream@~1.0.0", + "name": "delayed-stream", + "escapedName": "delayed-stream", + "rawSpec": "~1.0.0", + "saveSpec": null, + "fetchSpec": "~1.0.0" + }, + "_requiredBy": [ + "/combined-stream" + ], + "_resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "_shasum": "df3ae199acadfb7d440aaae0b29e2272b24ec619", + "_spec": "delayed-stream@~1.0.0", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\combined-stream", + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "bugs": { + "url": "https://github.com/felixge/node-delayed-stream/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Mike Atkins", + "email": "apeherder@gmail.com" + } + ], + "dependencies": {}, + "deprecated": false, + "description": "Buffers events from a stream until you are ready to handle them.", + "devDependencies": { + "fake": "0.2.0", + "far": "0.0.1" + }, + "engines": { + "node": ">=0.4.0" + }, + "homepage": "https://github.com/felixge/node-delayed-stream", + "license": "MIT", + "main": "./lib/delayed_stream", + "name": "delayed-stream", + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-delayed-stream.git" + }, + "scripts": { + "test": "make test" + }, + "version": "1.0.0" +} diff --git a/node_modules/discord.js/.tern-project b/node_modules/discord.js/.tern-project new file mode 100644 index 0000000..f5ce948 --- /dev/null +++ b/node_modules/discord.js/.tern-project @@ -0,0 +1,17 @@ +{ + "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..9997d13 --- /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 2015 - 2020 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..cbc2bb0 --- /dev/null +++ b/node_modules/discord.js/README.md @@ -0,0 +1,111 @@ +
+
+

+ discord.js +

+
+

+ Discord server + NPM version + NPM downloads + Build status + Dependencies + Patreon +

+

+ npm installnfo +

+
+ +## Table of contents + +- [About](#about) +- [Installation](#installation) + - [Audio engines](#audio-engines) + - [Optional packages](#optional-packages) +- [Example Usage](#example-usage) +- [Links](#links) + - [Extensions](#extensions) +- [Contributing](#contributing) +- [Help](#help) + +## About + +discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the +[Discord API](https://discord.com/developers/docs/intro). + +- Object-oriented +- Predictable abstractions +- Performant +- 100% coverage of the Discord API + +## Installation + +**Node.js 12.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 + +- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`) +- [erlpack](https://github.com/discord/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install discord/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`) +- [bufferutil](https://www.npmjs.com/package/bufferutil) for a much faster WebSocket connection (`npm install bufferutil`) +- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate) in combination with `bufferutil` for much faster WebSocket processing (`npm install utf-8-validate`) + +## 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/main/master/general/welcome) +- [Guide](https://discordjs.guide/) ([source](https://github.com/discordjs/guide)) - this is still for stable + See also the [Update Guide](https://discordjs.guide/additional-info/changes-in-v12.html), including updated and removed items in the library. +- [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/esm/discord.mjs b/node_modules/discord.js/esm/discord.mjs new file mode 100644 index 0000000..d8a451f --- /dev/null +++ b/node_modules/discord.js/esm/discord.mjs @@ -0,0 +1,94 @@ +import Discord from '../src/index.js'; + +export default Discord; + +export const { + BaseClient, + Client, + Shard, + ShardClientUtil, + ShardingManager, + WebhookClient, + ActivityFlags, + BitField, + Collection, + Constants, + DataResolver, + BaseManager, + DiscordAPIError, + HTTPError, + MessageFlags, + Intents, + Permissions, + Speaking, + Snowflake, + SnowflakeUtil, + Structures, + SystemChannelFlags, + UserFlags, + Util, + version, + ChannelManager, + GuildChannelManager, + GuildEmojiManager, + GuildEmojiRoleManager, + GuildMemberManager, + GuildMemberRoleManager, + GuildManager, + ReactionManager, + ReactionUserManager, + MessageManager, + PresenceManager, + RoleManager, + UserManager, + discordSort, + escapeMarkdown, + fetchRecommendedShards, + resolveColor, + resolveString, + splitMessage, + Application, + Base, + Activity, + APIMessage, + BaseGuildEmoji, + CategoryChannel, + Channel, + ClientApplication, + ClientUser, + Collector, + DMChannel, + Emoji, + Guild, + GuildAuditLogs, + GuildChannel, + GuildEmoji, + GuildMember, + GuildPreview, + Integration, + Invite, + Message, + MessageAttachment, + MessageCollector, + MessageEmbed, + MessageMentions, + MessageReaction, + NewsChannel, + PermissionOverwrites, + Presence, + ClientPresence, + ReactionCollector, + ReactionEmoji, + RichPresenceAssets, + Role, + StoreChannel, + Team, + TeamMember, + TextChannel, + User, + VoiceChannel, + VoiceRegion, + VoiceState, + Webhook, + WebSocket +} = Discord; diff --git a/node_modules/discord.js/jsdoc.json b/node_modules/discord.js/jsdoc.json new file mode 100644 index 0000000..3bf2d09 --- /dev/null +++ b/node_modules/discord.js/jsdoc.json @@ -0,0 +1,3 @@ +{ + "plugins": ["node_modules/jsdoc-strip-async-await"] +} diff --git a/node_modules/discord.js/package.json b/node_modules/discord.js/package.json new file mode 100644 index 0000000..75c9985 --- /dev/null +++ b/node_modules/discord.js/package.json @@ -0,0 +1,192 @@ +{ + "_from": "discord.js", + "_id": "discord.js@12.4.1", + "_inBundle": false, + "_integrity": "sha512-KxOB8LOAN3GmrvkD6a6Fr1nlfArIFZ+q7Uqg4T/5duB90GZy9a0/Py2E+Y+eHKP6ZUCR2mbNMLCcHGjahiaNqA==", + "_location": "/discord.js", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "discord.js", + "name": "discord.js", + "escapedName": "discord.js", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.4.1.tgz", + "_shasum": "be88bb32caa9e41eae67749a4e2387585745ac3b", + "_spec": "discord.js", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot", + "author": { + "name": "Amish Shah", + "email": "amishshah.2k@gmail.com" + }, + "browser": { + "@discordjs/opus": false, + "https": false, + "ws": false, + "erlpack": false, + "prism-media": false, + "opusscript": false, + "node-opus": false, + "tweetnacl": false, + "sodium": false, + "worker_threads": false, + "zlib-sync": false, + "src/sharding/Shard.js": false, + "src/sharding/ShardClientUtil.js": false, + "src/sharding/ShardingManager.js": false, + "src/client/voice/ClientVoiceManager.js": false, + "src/client/voice/VoiceBroadcast.js": false, + "src/client/voice/VoiceConnection.js": false, + "src/client/voice/dispatcher/BroadcastDispatcher.js": false, + "src/client/voice/dispatcher/StreamDispatcher.js": false, + "src/client/voice/networking/VoiceUDPClient.js": false, + "src/client/voice/networking/VoiceWebSocket.js": false, + "src/client/voice/player/AudioPlayer.js": false, + "src/client/voice/player/BasePlayer.js": false, + "src/client/voice/player/BroadcastAudioPlayer.js": false, + "src/client/voice/receiver/PacketHandler.js": false, + "src/client/voice/receiver/Receiver.js": false, + "src/client/voice/util/PlayInterface.js": false, + "src/client/voice/util/Secretbox.js": false, + "src/client/voice/util/Silence.js": false, + "src/client/voice/util/VolumeInterface.js": false + }, + "bugs": { + "url": "https://github.com/discordjs/discord.js/issues" + }, + "bundleDependencies": false, + "commitlint": { + "extends": [ + "@commitlint/config-angular" + ], + "rules": { + "scope-case": [ + 2, + "always", + "pascal-case" + ], + "type-enum": [ + 2, + "always", + [ + "chore", + "build", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "revert", + "style", + "test" + ] + ] + } + }, + "dependencies": { + "@discordjs/collection": "^0.1.6", + "@discordjs/form-data": "^3.0.1", + "abort-controller": "^3.0.0", + "node-fetch": "^2.6.1", + "prism-media": "^1.2.2", + "setimmediate": "^1.0.5", + "tweetnacl": "^1.0.3", + "ws": "^7.3.1" + }, + "deprecated": false, + "description": "A powerful library for interacting with the Discord API", + "devDependencies": { + "@commitlint/cli": "^11.0.0", + "@commitlint/config-angular": "^11.0.0", + "@types/node": "^12.12.6", + "@types/ws": "^7.2.7", + "cross-env": "^7.0.2", + "discord.js-docgen": "github:discordjs/docgen", + "dtslint": "^4.0.4", + "eslint": "^7.11.0", + "eslint-config-prettier": "^6.13.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-prettier": "^3.1.4", + "husky": "^4.3.0", + "jest": "^26.6.0", + "json-filter-loader": "^1.0.0", + "lint-staged": "^10.4.2", + "prettier": "^2.1.2", + "terser-webpack-plugin": "^4.2.3", + "tslint": "^6.1.3", + "typescript": "^4.0.3", + "webpack": "^4.44.2", + "webpack-cli": "^3.3.12" + }, + "engines": { + "node": ">=12.0.0" + }, + "exports": { + ".": [ + { + "require": "./src/index.js", + "import": "./esm/discord.mjs" + }, + "./src/index.js" + ], + "./esm": "./esm/discord.mjs" + }, + "homepage": "https://github.com/discordjs/discord.js#readme", + "husky": { + "hooks": { + "pre-commit": "lint-staged", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "keywords": [ + "discord", + "api", + "bot", + "client", + "node", + "discordapp" + ], + "license": "Apache-2.0", + "lint-staged": { + "*.js": "eslint --fix", + "*.ts": "prettier --write" + }, + "main": "./src/index", + "name": "discord.js", + "prettier": { + "singleQuote": true, + "printWidth": 120, + "trailingComma": "all", + "endOfLine": "lf", + "arrowParens": "avoid" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/discordjs/discord.js.git" + }, + "runkitExampleFilename": "./docs/examples/ping.js", + "scripts": { + "build:browser": "webpack", + "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 src --fix", + "lint:typings": "tslint typings/index.d.ts", + "prepublishOnly": "npm run test && cross-env NODE_ENV=production npm run build:browser", + "prettier": "prettier --write src/**/*.js typings/**/*.ts", + "test": "npm run lint && npm run docs:test && npm run lint:typings", + "test:typescript": "tsc" + }, + "types": "./typings/index.d.ts", + "unpkg": "./webpack/discord.min.js", + "version": "12.4.1" +} diff --git a/node_modules/discord.js/src/WebSocket.js b/node_modules/discord.js/src/WebSocket.js new file mode 100644 index 0000000..90dd51b --- /dev/null +++ b/node_modules/discord.js/src/WebSocket.js @@ -0,0 +1,49 @@ +'use strict'; + +const { browser } = require('./util/Constants'); + +let erlpack; + +try { + erlpack = require('erlpack'); + if (!erlpack.pack) erlpack = null; +} catch {} // eslint-disable-line no-empty + +let TextDecoder; + +if (browser) { + TextDecoder = window.TextDecoder; // eslint-disable-line no-undef + exports.WebSocket = window.WebSocket; // eslint-disable-line no-undef +} else { + TextDecoder = require('util').TextDecoder; + exports.WebSocket = require('ws'); +} + +const ab = new TextDecoder(); + +exports.encoding = erlpack ? 'etf' : 'json'; + +exports.pack = erlpack ? erlpack.pack : JSON.stringify; + +exports.unpack = (data, type) => { + if (exports.encoding === 'json' || type === 'json') { + if (typeof data !== 'string') { + data = ab.decode(data); + } + return JSON.parse(data); + } + if (!Buffer.isBuffer(data)) data = Buffer.from(new Uint8Array(data)); + return erlpack.unpack(data); +}; + +exports.create = (gateway, query = {}, ...args) => { + const [g, q] = gateway.split('?'); + query.encoding = exports.encoding; + query = new URLSearchParams(query); + if (q) new URLSearchParams(q).forEach((v, k) => query.set(k, v)); + const ws = new exports.WebSocket(`${g}?${query}`, ...args); + if (browser) ws.binaryType = 'arraybuffer'; + return ws; +}; + +for (const state of ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']) exports[state] = exports.WebSocket[state]; diff --git a/node_modules/discord.js/src/client/BaseClient.js b/node_modules/discord.js/src/client/BaseClient.js new file mode 100644 index 0000000..29abeca --- /dev/null +++ b/node_modules/discord.js/src/client/BaseClient.js @@ -0,0 +1,169 @@ +'use strict'; + +require('setimmediate'); +const EventEmitter = require('events'); +const RESTManager = require('../rest/RESTManager'); +const { DefaultOptions } = require('../util/Constants'); +const Util = require('../util/Util'); + +/** + * The base class for all clients. + * @extends {EventEmitter} + */ +class BaseClient extends EventEmitter { + constructor(options = {}) { + super(); + + /** + * Timeouts set by {@link BaseClient#setTimeout} that are still active + * @type {Set} + * @private + */ + this._timeouts = new Set(); + + /** + * Intervals set by {@link BaseClient#setInterval} that are still active + * @type {Set} + * @private + */ + this._intervals = new Set(); + + /** + * Intervals set by {@link BaseClient#setImmediate} that are still active + * @type {Set} + * @private + */ + this._immediates = new Set(); + + /** + * The options the client was instantiated with + * @type {ClientOptions} + */ + this.options = Util.mergeDefault(DefaultOptions, options); + + /** + * The REST manager of the client + * @type {RESTManager} + * @private + */ + this.rest = new RESTManager(this, options._tokenType); + } + + /** + * API shortcut + * @type {Object} + * @readonly + * @private + */ + get api() { + return this.rest.api; + } + + /** + * Destroys all assets used by the base client. + */ + destroy() { + for (const t of this._timeouts) this.clearTimeout(t); + for (const i of this._intervals) this.clearInterval(i); + for (const i of this._immediates) this.clearImmediate(i); + this._timeouts.clear(); + this._intervals.clear(); + this._immediates.clear(); + } + + /** + * 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 between executions (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); + } + + /** + * Sets an immediate that will be automatically cancelled if the client is destroyed. + * @param {Function} fn Function to execute + * @param {...*} args Arguments for the function + * @returns {Immediate} + */ + setImmediate(fn, ...args) { + const immediate = setImmediate(fn, ...args); + this._immediates.add(immediate); + return immediate; + } + + /** + * Clears an immediate. + * @param {Immediate} immediate Immediate to cancel + */ + clearImmediate(immediate) { + clearImmediate(immediate); + this._immediates.delete(immediate); + } + + /** + * Increments max listeners by one, if they are not zero. + * @private + */ + incrementMaxListeners() { + const maxListeners = this.getMaxListeners(); + if (maxListeners !== 0) { + this.setMaxListeners(maxListeners + 1); + } + } + + /** + * Decrements max listeners by one, if they are not zero. + * @private + */ + decrementMaxListeners() { + const maxListeners = this.getMaxListeners(); + if (maxListeners !== 0) { + this.setMaxListeners(maxListeners - 1); + } + } + + toJSON(...props) { + return Util.flatten(this, { domain: false }, ...props); + } +} + +module.exports = BaseClient; 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..fd02197 --- /dev/null +++ b/node_modules/discord.js/src/client/Client.js @@ -0,0 +1,486 @@ +'use strict'; + +const BaseClient = require('./BaseClient'); +const ActionsManager = require('./actions/ActionsManager'); +const ClientVoiceManager = require('./voice/ClientVoiceManager'); +const WebSocketManager = require('./websocket/WebSocketManager'); +const { Error, TypeError, RangeError } = require('../errors'); +const ChannelManager = require('../managers/ChannelManager'); +const GuildEmojiManager = require('../managers/GuildEmojiManager'); +const GuildManager = require('../managers/GuildManager'); +const UserManager = require('../managers/UserManager'); +const ShardClientUtil = require('../sharding/ShardClientUtil'); +const ClientApplication = require('../structures/ClientApplication'); +const GuildPreview = require('../structures/GuildPreview'); +const Invite = require('../structures/Invite'); +const VoiceRegion = require('../structures/VoiceRegion'); +const Webhook = require('../structures/Webhook'); +const Collection = require('../util/Collection'); +const { Events, browser, DefaultOptions } = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const Intents = require('../util/Intents'); +const Permissions = require('../util/Permissions'); +const Structures = require('../util/Structures'); + +/** + * The main hub for interacting with the Discord API, and the starting point for any bot. + * @extends {BaseClient} + */ +class Client extends BaseClient { + /** + * @param {ClientOptions} [options] Options for the client + */ + constructor(options = {}) { + super(Object.assign({ _tokenType: 'Bot' }, options)); + + // Obtain shard details from environment or if present, worker threads + let data = process.env; + try { + // Test if worker threads module is present and used + data = require('worker_threads').workerData || data; + } catch { + // Do nothing + } + + if (this.options.shards === DefaultOptions.shards) { + if ('SHARDS' in data) { + this.options.shards = JSON.parse(data.SHARDS); + } + } + + if (this.options.shardCount === DefaultOptions.shardCount) { + if ('SHARD_COUNT' in data) { + this.options.shardCount = Number(data.SHARD_COUNT); + } else if (Array.isArray(this.options.shards)) { + this.options.shardCount = this.options.shards.length; + } + } + + const typeofShards = typeof this.options.shards; + + if (typeofShards === 'undefined' && typeof this.options.shardCount === 'number') { + this.options.shards = Array.from({ length: this.options.shardCount }, (_, i) => i); + } + + if (typeofShards === 'number') this.options.shards = [this.options.shards]; + + if (Array.isArray(this.options.shards)) { + this.options.shards = [ + ...new Set( + this.options.shards.filter(item => !isNaN(item) && item >= 0 && item < Infinity && item === (item | 0)), + ), + ]; + } + + this._validateOptions(); + + /** + * The WebSocket manager of the client + * @type {WebSocketManager} + */ + this.ws = new WebSocketManager(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} + */ + this.voice = !browser ? new ClientVoiceManager(this) : null; + + /** + * Shard helpers for the client (only if the process was spawned from a {@link ShardingManager}) + * @type {?ShardClientUtil} + */ + this.shard = + !browser && process.env.SHARDING_MANAGER + ? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE) + : null; + + /** + * All of the {@link User} objects that have been cached at any point, mapped by their IDs + * @type {UserManager} + */ + this.users = new UserManager(this); + + /** + * 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 {GuildManager} + */ + this.guilds = new GuildManager(this); + + /** + * 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 the bot + * is a member of. Note that DM channels will not be initially cached, and thus not be present + * in the Manager without their explicit fetching or use. + * @type {ChannelManager} + */ + this.channels = new ChannelManager(this); + + const ClientPresence = Structures.get('ClientPresence'); + /** + * The presence of the Client + * @private + * @type {ClientPresence} + */ + this.presence = new ClientPresence(this); + + Object.defineProperty(this, 'token', { writable: true }); + if (!browser && !this.token && 'DISCORD_TOKEN' in process.env) { + /** + * Authorization token for the logged in bot. + * If present, this defaults to `process.env.DISCORD_TOKEN` when instantiating the client + * This should be kept private at all times. + * @type {?string} + */ + this.token = process.env.DISCORD_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; + + if (this.options.messageSweepInterval > 0) { + this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000); + } + } + + /** + * All custom emojis that the client has access to, mapped by their IDs + * @type {GuildEmojiManager} + * @readonly + */ + get emojis() { + const emojis = new GuildEmojiManager({ client: this }); + for (const guild of this.guilds.cache.values()) { + if (guild.available) for (const emoji of guild.emojis.cache.values()) emojis.cache.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; + } + + /** + * 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; + } + + /** + * Logs the client in, establishing a websocket connection to Discord. + * @param {string} [token=this.token] Token of the account to log in with + * @returns {Promise} Token of the account used + * @example + * client.login('my token'); + */ + async login(token = this.token) { + if (!token || typeof token !== 'string') throw new Error('TOKEN_INVALID'); + this.token = token = token.replace(/^(Bot|Bearer)\s*/i, ''); + this.emit( + Events.DEBUG, + `Provided token: ${token + .split('.') + .map((val, i) => (i > 1 ? val.replace(/./g, '*') : val)) + .join('.')}`, + ); + + if (this.options.presence) { + this.options.ws.presence = await this.presence._parse(this.options.presence); + } + + this.emit(Events.DEBUG, 'Preparing to connect to the gateway...'); + + try { + await this.ws.connect(); + return this.token; + } catch (error) { + this.destroy(); + throw error; + } + } + + /** + * Logs out, terminates the connection to Discord, and destroys the client. + * @returns {void} + */ + destroy() { + super.destroy(); + this.ws.destroy(); + this.token = null; + } + + /** + * Obtains an invite from Discord. + * @param {InviteResolvable} invite Invite code or URL + * @returns {Promise} + * @example + * client.fetchInvite('https://discord.gg/bRCvFy9') + * .then(invite => console.log(`Obtained invite with code: ${invite.code}`)) + * .catch(console.error); + */ + fetchInvite(invite) { + const code = DataResolver.resolveInviteCode(invite); + return this.api + .invites(code) + .get({ query: { with_counts: true } }) + .then(data => new Invite(this, data)); + } + + /** + * Obtains a webhook from Discord. + * @param {Snowflake} id ID of the webhook + * @param {string} [token] Token for the webhook + * @returns {Promise} + * @example + * client.fetchWebhook('id', 'token') + * .then(webhook => console.log(`Obtained webhook with name: ${webhook.name}`)) + * .catch(console.error); + */ + fetchWebhook(id, token) { + return this.api + .webhooks(id, token) + .get() + .then(data => new Webhook(this, data)); + } + + /** + * Obtains the available voice regions from Discord. + * @returns {Promise>} + * @example + * client.fetchVoiceRegions() + * .then(regions => console.log(`Available regions are: ${regions.map(region => region.name).join(', ')}`)) + * .catch(console.error); + */ + fetchVoiceRegions() { + return this.api.voice.regions.get().then(res => { + const regions = new Collection(); + for (const region of res) regions.set(region.id, new VoiceRegion(region)); + return regions; + }); + } + + /** + * 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 + * @example + * // Remove all messages older than 1800 seconds from the messages cache + * const amount = client.sweepMessages(1800); + * console.log(`Successfully removed ${amount} messages from the cache.`); + */ + sweepMessages(lifetime = this.options.messageCacheLifetime) { + if (typeof lifetime !== 'number' || isNaN(lifetime)) { + throw new TypeError('INVALID_TYPE', 'lifetime', 'number'); + } + if (lifetime <= 0) { + this.emit(Events.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.cache.values()) { + if (!channel.messages) continue; + channels++; + + messages += channel.messages.cache.sweep( + message => now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs, + ); + } + + this.emit( + Events.DEBUG, + `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`, + ); + return messages; + } + + /** + * Obtains the OAuth Application of this bot from Discord. + * @returns {Promise} + */ + fetchApplication() { + return this.api.oauth2 + .applications('@me') + .get() + .then(app => new ClientApplication(this, app)); + } + + /** + * Obtains a guild preview from Discord, available for all guilds the bot is in and all Discoverable guilds. + * @param {GuildResolvable} guild The guild to fetch the preview for + * @returns {Promise} + */ + fetchGuildPreview(guild) { + const id = this.guilds.resolveID(guild); + if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable'); + return this.api + .guilds(id) + .preview.get() + .then(data => new GuildPreview(this, data)); + } + + /** + * Generates a link that can be used to invite the bot to a guild. + * @param {InviteGenerationOptions|PermissionResolvable} [options] Permissions to request + * @returns {Promise} + * @example + * client.generateInvite({ + * permissions: ['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'], + * }) + * .then(link => console.log(`Generated bot invite link: ${link}`)) + * .catch(console.error); + */ + async generateInvite(options = {}) { + if (Array.isArray(options) || ['string', 'number'].includes(typeof options) || options instanceof Permissions) { + process.emitWarning( + 'Client#generateInvite: Generate invite with an options object instead of a PermissionResolvable', + 'DeprecationWarning', + ); + options = { permissions: options }; + } + const application = await this.fetchApplication(); + const query = new URLSearchParams({ + client_id: application.id, + permissions: Permissions.resolve(options.permissions), + scope: 'bot', + }); + if (typeof options.disableGuildSelect === 'boolean') { + query.set('disable_guild_select', options.disableGuildSelect.toString()); + } + if (typeof options.guild !== 'undefined') { + const guildID = this.guilds.resolveID(options.guild); + if (!guildID) throw new TypeError('INVALID_TYPE', 'options.guild', 'GuildResolvable'); + query.set('guild_id', guildID); + } + return `${this.options.http.api}${this.api.oauth2.authorize}?${query}`; + } + + toJSON() { + return super.toJSON({ + readyAt: false, + }); + } + + /** + * 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) { + if (typeof options.ws.intents !== 'undefined') { + options.ws.intents = Intents.resolve(options.ws.intents); + } + if (typeof options.shardCount !== 'number' || isNaN(options.shardCount) || options.shardCount < 1) { + throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number greater than or equal to 1'); + } + if (options.shards && !(options.shards === 'auto' || Array.isArray(options.shards))) { + throw new TypeError('CLIENT_INVALID_OPTION', 'shards', "'auto', a number or array of numbers"); + } + if (options.shards && !options.shards.length) throw new RangeError('CLIENT_INVALID_PROVIDED_SHARDS'); + if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'messageCacheMaxSize', 'a number'); + } + if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number'); + } + if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'messageSweepInterval', 'a number'); + } + if ( + typeof options.messageEditHistoryMaxSize !== 'number' || + isNaN(options.messageEditHistoryMaxSize) || + options.messageEditHistoryMaxSize < -1 + ) { + throw new TypeError('CLIENT_INVALID_OPTION', 'messageEditHistoryMaxSize', 'a number greater than or equal to -1'); + } + if (typeof options.fetchAllMembers !== 'boolean') { + throw new TypeError('CLIENT_INVALID_OPTION', 'fetchAllMembers', 'a boolean'); + } + if (typeof options.disableMentions !== 'string') { + throw new TypeError('CLIENT_INVALID_OPTION', 'disableMentions', 'a string'); + } + if (!Array.isArray(options.partials)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array'); + } + if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number'); + } + if (typeof options.restRequestTimeout !== 'number' || isNaN(options.restRequestTimeout)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'restRequestTimeout', 'a number'); + } + if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number'); + } + if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number'); + } + } +} + +module.exports = Client; + +/** + * Options for {@link Client#generateInvite}. + * @typedef {Object} InviteGenerationOptions + * @property {PermissionResolvable} [permissions] Permissions to request + * @property {GuildResolvable} [guild] Guild to preselect + * @property {boolean} [disableGuildSelect] Whether to disable the guild selection + */ + +/** + * 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/WebhookClient.js b/node_modules/discord.js/src/client/WebhookClient.js new file mode 100644 index 0000000..cea6631 --- /dev/null +++ b/node_modules/discord.js/src/client/WebhookClient.js @@ -0,0 +1,31 @@ +'use strict'; + +const BaseClient = require('./BaseClient'); +const Webhook = require('../structures/Webhook'); + +/** + * The webhook client. + * @implements {Webhook} + * @extends {BaseClient} + */ +class WebhookClient extends BaseClient { + /** + * @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.send('This will send a message').catch(console.error); + */ + constructor(id, token, options) { + super(options); + Object.defineProperty(this, 'client', { value: this }); + this.id = id; + Object.defineProperty(this, 'token', { value: token, writable: true, configurable: true }); + } +} + +Webhook.applyToClass(WebhookClient); + +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..07ce7ed --- /dev/null +++ b/node_modules/discord.js/src/client/actions/Action.js @@ -0,0 +1,104 @@ +'use strict'; + +const { PartialTypes } = require('../../util/Constants'); + +/* + +ABOUT ACTIONS + +Actions are similar to WebSocket Packet Handlers, but since introducing +the REST API methods, in order to prevent rewriting code to handle data, +"actions" have been introduced. They're basically what Packet Handlers +used to be but they're strictly for manipulating data and making sure +that WebSocket events don't clash with REST methods. + +*/ + +class GenericAction { + constructor(client) { + this.client = client; + } + + handle(data) { + return data; + } + + getPayload(data, manager, id, partialType, cache) { + const existing = manager.cache.get(id); + if (!existing && this.client.options.partials.includes(partialType)) { + return manager.add(data, cache); + } + return existing; + } + + getChannel(data) { + const id = data.channel_id || data.id; + return ( + data.channel || + this.getPayload( + { + id, + guild_id: data.guild_id, + recipients: [data.author || { id: data.user_id }], + }, + this.client.channels, + id, + PartialTypes.CHANNEL, + ) + ); + } + + getMessage(data, channel, cache) { + const id = data.message_id || data.id; + return ( + data.message || + this.getPayload( + { + id, + channel_id: channel.id, + guild_id: data.guild_id || (channel.guild ? channel.guild.id : null), + }, + channel.messages, + id, + PartialTypes.MESSAGE, + cache, + ) + ); + } + + getReaction(data, message, user) { + const id = data.emoji.id || decodeURIComponent(data.emoji.name); + return this.getPayload( + { + emoji: data.emoji, + count: message.partial ? null : 0, + me: user ? user.id === this.client.user.id : false, + }, + message.reactions, + id, + PartialTypes.REACTION, + ); + } + + getMember(data, guild) { + return this.getPayload(data, guild.members, data.user.id, PartialTypes.GUILD_MEMBER); + } + + getUser(data) { + const id = data.user_id; + return data.user || this.getPayload({ id }, this.client.users, id, PartialTypes.USER); + } + + getUserFromMember(data) { + if (data.guild_id && data.member && data.member.user) { + const guild = this.client.guilds.cache.get(data.guild_id); + if (guild) { + const member = this.getMember(data.member, guild); + return member ? member.user : this.getUser(data.member.user); + } + } + return this.getUser(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..a5193c9 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/ActionsManager.js @@ -0,0 +1,46 @@ +'use strict'; + +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('./MessageReactionRemoveAll')); + this.register(require('./MessageReactionRemoveEmoji')); + this.register(require('./ChannelCreate')); + this.register(require('./ChannelDelete')); + this.register(require('./ChannelUpdate')); + this.register(require('./GuildDelete')); + this.register(require('./GuildUpdate')); + this.register(require('./InviteCreate')); + this.register(require('./InviteDelete')); + this.register(require('./GuildMemberRemove')); + this.register(require('./GuildBanRemove')); + this.register(require('./GuildRoleCreate')); + this.register(require('./GuildRoleDelete')); + this.register(require('./GuildRoleUpdate')); + this.register(require('./PresenceUpdate')); + this.register(require('./UserUpdate')); + this.register(require('./VoiceStateUpdate')); + this.register(require('./GuildEmojiCreate')); + this.register(require('./GuildEmojiDelete')); + this.register(require('./GuildEmojiUpdate')); + this.register(require('./GuildEmojisUpdate')); + this.register(require('./GuildRolesPositionUpdate')); + this.register(require('./GuildChannelsPositionUpdate')); + this.register(require('./GuildIntegrationsUpdate')); + this.register(require('./WebhooksUpdate')); + this.register(require('./TypingStart')); + } + + 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..fa60a0b --- /dev/null +++ b/node_modules/discord.js/src/client/actions/ChannelCreate.js @@ -0,0 +1,23 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class ChannelCreateAction extends Action { + handle(data) { + const client = this.client; + const existing = client.channels.cache.has(data.id); + const channel = client.channels.add(data); + if (!existing && channel) { + /** + * Emitted whenever a channel is created. + * @event Client#channelCreate + * @param {DMChannel|GuildChannel} channel The channel that was created + */ + client.emit(Events.CHANNEL_CREATE, channel); + } + 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..2956d72 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/ChannelDelete.js @@ -0,0 +1,37 @@ +'use strict'; + +const Action = require('./Action'); +const DMChannel = require('../../structures/DMChannel'); +const { Events } = require('../../util/Constants'); + +class ChannelDeleteAction extends Action { + constructor(client) { + super(client); + this.deleted = new Map(); + } + + handle(data) { + const client = this.client; + let channel = client.channels.cache.get(data.id); + + if (channel) { + client.channels.remove(channel.id); + channel.deleted = true; + if (channel.messages && !(channel instanceof DMChannel)) { + for (const message of channel.messages.cache.values()) { + message.deleted = true; + } + } + /** + * Emitted whenever a channel is deleted. + * @event Client#channelDelete + * @param {DMChannel|GuildChannel} channel The channel that was deleted + */ + client.emit(Events.CHANNEL_DELETE, channel); + } + + return { channel }; + } +} + +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..06bb71b --- /dev/null +++ b/node_modules/discord.js/src/client/actions/ChannelUpdate.js @@ -0,0 +1,33 @@ +'use strict'; + +const Action = require('./Action'); +const Channel = require('../../structures/Channel'); +const { ChannelTypes } = require('../../util/Constants'); + +class ChannelUpdateAction extends Action { + handle(data) { + const client = this.client; + + let channel = client.channels.cache.get(data.id); + if (channel) { + const old = channel._update(data); + + if (ChannelTypes[channel.type.toUpperCase()] !== data.type) { + const newChannel = Channel.create(this.client, data, channel.guild); + for (const [id, message] of channel.messages.cache) newChannel.messages.cache.set(id, message); + newChannel._typing = new Map(channel._typing); + channel = newChannel; + this.client.channels.cache.set(channel.id, channel); + } + + return { + old, + updated: channel, + }; + } + + return {}; + } +} + +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..fc28e11 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildBanRemove.js @@ -0,0 +1,21 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildBanRemove extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + const user = client.users.add(data.user); + /** + * 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 + */ + if (guild && user) client.emit(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..a393167 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js @@ -0,0 +1,21 @@ +'use strict'; + +const Action = require('./Action'); + +class GuildChannelsPositionUpdate extends Action { + handle(data) { + const client = this.client; + + const guild = client.guilds.cache.get(data.guild_id); + if (guild) { + for (const partialChannel of data.channels) { + const channel = guild.channels.cache.get(partialChannel.id); + if (channel) channel.rawPosition = partialChannel.position; + } + } + + return { guild }; + } +} + +module.exports = GuildChannelsPositionUpdate; 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..8bb630f --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildDelete.js @@ -0,0 +1,67 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = 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.cache.get(data.id); + if (guild) { + for (const channel of guild.channels.cache.values()) { + if (channel.type === 'text') channel.stopTyping(true); + } + + if (data.unavailable) { + // Guild is unavailable + guild.available = false; + + /** + * Emitted whenever a guild becomes unavailable, likely due to a server outage. + * @event Client#guildUnavailable + * @param {Guild} guild The guild that has become unavailable + */ + client.emit(Events.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.cache.values()) this.client.channels.remove(channel.id); + if (guild.voice && guild.voice.connection) guild.voice.connection.disconnect(); + + // Delete guild + client.guilds.cache.delete(guild.id); + guild.deleted = true; + + /** + * Emitted whenever a guild kicks the client or the guild is deleted/left. + * @event Client#guildDelete + * @param {Guild} guild The guild that was deleted + */ + client.emit(Events.GUILD_DELETE, guild); + + this.deleted.set(guild.id, guild); + this.scheduleForDeletion(guild.id); + } else { + guild = this.deleted.get(data.id) || null; + } + + return { guild }; + } + + scheduleForDeletion(id) { + this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout); + } +} + +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..f47ddd5 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js @@ -0,0 +1,20 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildEmojiCreateAction extends Action { + handle(guild, createdEmoji) { + const already = guild.emojis.cache.has(createdEmoji.id); + const emoji = guild.emojis.add(createdEmoji); + /** + * Emitted whenever a custom emoji is created in a guild. + * @event Client#emojiCreate + * @param {GuildEmoji} emoji The emoji that was created + */ + if (!already) this.client.emit(Events.GUILD_EMOJI_CREATE, emoji); + return { emoji }; + } +} + +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..42af70c --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js @@ -0,0 +1,20 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildEmojiDeleteAction extends Action { + handle(emoji) { + emoji.guild.emojis.cache.delete(emoji.id); + emoji.deleted = true; + /** + * Emitted whenever a custom emoji is deleted in a guild. + * @event Client#emojiDelete + * @param {GuildEmoji} emoji The emoji that was deleted + */ + this.client.emit(Events.GUILD_EMOJI_DELETE, emoji); + return { emoji }; + } +} + +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..9fa59c9 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js @@ -0,0 +1,20 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildEmojiUpdateAction extends Action { + handle(current, data) { + const old = current._update(data); + /** + * Emitted whenever a custom emoji is updated in a guild. + * @event Client#emojiUpdate + * @param {GuildEmoji} oldEmoji The old emoji + * @param {GuildEmoji} newEmoji The new emoji + */ + this.client.emit(Events.GUILD_EMOJI_UPDATE, old, current); + return { emoji: current }; + } +} + +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..7711933 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js @@ -0,0 +1,34 @@ +'use strict'; + +const Action = require('./Action'); + +class GuildEmojisUpdateAction extends Action { + handle(data) { + const guild = this.client.guilds.cache.get(data.guild_id); + if (!guild || !guild.emojis) return; + + const deletions = new Map(guild.emojis.cache); + + for (const emoji of data.emojis) { + // Determine type of emoji event + const cachedEmoji = guild.emojis.cache.get(emoji.id); + if (cachedEmoji) { + deletions.delete(emoji.id); + if (!cachedEmoji.equals(emoji)) { + // Emoji updated + this.client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji); + } + } else { + // Emoji added + this.client.actions.GuildEmojiCreate.handle(guild, emoji); + } + } + + for (const emoji of deletions.values()) { + // Emoji deleted + this.client.actions.GuildEmojiDelete.handle(emoji); + } + } +} + +module.exports = GuildEmojisUpdateAction; diff --git a/node_modules/discord.js/src/client/actions/GuildIntegrationsUpdate.js b/node_modules/discord.js/src/client/actions/GuildIntegrationsUpdate.js new file mode 100644 index 0000000..bbce076 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildIntegrationsUpdate.js @@ -0,0 +1,19 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildIntegrationsUpdate extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + /** + * Emitted whenever a guild integration is updated + * @event Client#guildIntegrationsUpdate + * @param {Guild} guild The guild whose integrations were updated + */ + if (guild) client.emit(Events.GUILD_INTEGRATIONS_UPDATE, guild); + } +} + +module.exports = GuildIntegrationsUpdate; 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..b690446 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildMemberRemove.js @@ -0,0 +1,30 @@ +'use strict'; + +const Action = require('./Action'); +const { Events, Status } = require('../../util/Constants'); + +class GuildMemberRemoveAction extends Action { + handle(data, shard) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + let member = null; + if (guild) { + member = this.getMember({ user: data.user }, guild); + guild.memberCount--; + if (member) { + member.deleted = true; + guild.members.cache.delete(member.id); + /** + * Emitted whenever a member leaves a guild, or is kicked. + * @event Client#guildMemberRemove + * @param {GuildMember} member The member that has left/been kicked from the guild + */ + if (shard.status === Status.READY) client.emit(Events.GUILD_MEMBER_REMOVE, member); + } + guild.voiceStates.cache.delete(data.user.id); + } + return { guild, member }; + } +} + +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..d7d5214 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildRoleCreate.js @@ -0,0 +1,25 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildRoleCreate extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + let role; + if (guild) { + const already = guild.roles.cache.has(data.role.id); + role = guild.roles.add(data.role); + /** + * Emitted whenever a role is created. + * @event Client#roleCreate + * @param {Role} role The role that was created + */ + if (!already) client.emit(Events.GUILD_ROLE_CREATE, role); + } + return { role }; + } +} + +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..51754b3 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildRoleDelete.js @@ -0,0 +1,30 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildRoleDeleteAction extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + let role; + + if (guild) { + role = guild.roles.cache.get(data.role_id); + if (role) { + guild.roles.cache.delete(data.role_id); + role.deleted = true; + /** + * Emitted whenever a guild role is deleted. + * @event Client#roleDelete + * @param {Role} role The role that was deleted + */ + client.emit(Events.GUILD_ROLE_DELETE, role); + } + } + + return { role }; + } +} + +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..faea120 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js @@ -0,0 +1,39 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildRoleUpdateAction extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + + if (guild) { + let old = null; + + const role = guild.roles.cache.get(data.role.id); + if (role) { + old = role._update(data.role); + /** + * Emitted whenever a guild role is updated. + * @event Client#roleUpdate + * @param {Role} oldRole The role before the update + * @param {Role} newRole The role after the update + */ + client.emit(Events.GUILD_ROLE_UPDATE, old, role); + } + + return { + old, + updated: role, + }; + } + + return { + old: null, + updated: null, + }; + } +} + +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..d7abca9 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js @@ -0,0 +1,21 @@ +'use strict'; + +const Action = require('./Action'); + +class GuildRolesPositionUpdate extends Action { + handle(data) { + const client = this.client; + + const guild = client.guilds.cache.get(data.guild_id); + if (guild) { + for (const partialRole of data.roles) { + const role = guild.roles.cache.get(partialRole.id); + if (role) role.rawPosition = partialRole.position; + } + } + + return { guild }; + } +} + +module.exports = GuildRolesPositionUpdate; 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..78a8afb --- /dev/null +++ b/node_modules/discord.js/src/client/actions/GuildUpdate.js @@ -0,0 +1,33 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class GuildUpdateAction extends Action { + handle(data) { + const client = this.client; + + const guild = client.guilds.cache.get(data.id); + if (guild) { + const old = guild._update(data); + /** + * Emitted whenever a guild is updated - e.g. name change. + * @event Client#guildUpdate + * @param {Guild} oldGuild The guild before the update + * @param {Guild} newGuild The guild after the update + */ + client.emit(Events.GUILD_UPDATE, old, guild); + return { + old, + updated: guild, + }; + } + + return { + old: null, + updated: null, + }; + } +} + +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..34cfa84 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/InviteCreate.js @@ -0,0 +1,28 @@ +'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 channel = client.channels.cache.get(data.channel_id); + const guild = client.guilds.cache.get(data.guild_id); + if (!channel) return false; + + const inviteData = Object.assign(data, { channel, guild }); + const invite = new Invite(client, inviteData); + /** + * Emitted when an invite is created. + * This event only triggers if the client has `MANAGE_GUILD` permissions for the guild, + * or `MANAGE_CHANNEL` permissions for the channel. + * @event Client#inviteCreate + * @param {Invite} invite The invite that was created + */ + client.emit(Events.INVITE_CREATE, invite); + return { invite }; + } +} + +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..92692c3 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/InviteDelete.js @@ -0,0 +1,29 @@ +'use strict'; + +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 channel = client.channels.cache.get(data.channel_id); + const guild = client.guilds.cache.get(data.guild_id); + if (!channel && !guild) return false; + + const inviteData = Object.assign(data, { channel, guild }); + const invite = new Invite(client, inviteData); + + /** + * Emitted when an invite is deleted. + * This event only triggers if the client has `MANAGE_GUILD` permissions for the guild, + * or `MANAGE_CHANNEL` permissions for the channel. + * @event Client#inviteDelete + * @param {Invite} invite The invite that was deleted + */ + client.emit(Events.INVITE_DELETE, invite); + return { 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..7138707 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageCreate.js @@ -0,0 +1,39 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class MessageCreateAction extends Action { + handle(data) { + const client = this.client; + const channel = client.channels.cache.get(data.channel_id); + if (channel) { + const existing = channel.messages.cache.get(data.id); + if (existing) return { message: existing }; + const message = channel.messages.add(data); + const user = message.author; + let member = message.member; + channel.lastMessageID = data.id; + if (user) { + user.lastMessageID = data.id; + user.lastMessageChannelID = channel.id; + } + if (member) { + member.lastMessageID = data.id; + member.lastMessageChannelID = channel.id; + } + + /** + * Emitted whenever a message is created. + * @event Client#message + * @param {Message} message The created message + */ + client.emit(Events.MESSAGE_CREATE, message); + return { message }; + } + + return {}; + } +} + +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..0210625 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageDelete.js @@ -0,0 +1,29 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class MessageDeleteAction extends Action { + handle(data) { + const client = this.client; + const channel = this.getChannel(data); + let message; + if (channel) { + message = this.getMessage(data, channel); + if (message) { + channel.messages.cache.delete(message.id); + message.deleted = true; + /** + * Emitted whenever a message is deleted. + * @event Client#messageDelete + * @param {Message} message The deleted message + */ + client.emit(Events.MESSAGE_DELETE, message); + } + } + + return { message }; + } +} + +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..8686b1d --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js @@ -0,0 +1,43 @@ +'use strict'; + +const Action = require('./Action'); +const Collection = require('../../util/Collection'); +const { Events } = require('../../util/Constants'); + +class MessageDeleteBulkAction extends Action { + handle(data) { + const client = this.client; + const channel = client.channels.cache.get(data.channel_id); + + if (channel) { + const ids = data.ids; + const messages = new Collection(); + for (const id of ids) { + const message = this.getMessage( + { + id, + guild_id: data.guild_id, + }, + channel, + false, + ); + if (message) { + message.deleted = true; + messages.set(message.id, message); + channel.messages.cache.delete(id); + } + } + + /** + * Emitted whenever messages are deleted in bulk. + * @event Client#messageDeleteBulk + * @param {Collection} messages The deleted messages, mapped by their ID + */ + if (messages.size > 0) client.emit(Events.MESSAGE_BULK_DELETE, messages); + return { messages }; + } + return {}; + } +} + +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..54929f6 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageReactionAdd.js @@ -0,0 +1,52 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); +const { PartialTypes } = require('../../util/Constants'); + +/* +{ user_id: 'id', + message_id: 'id', + emoji: { name: '�', id: null }, + channel_id: 'id' } } +*/ + +class MessageReactionAdd extends Action { + handle(data) { + if (!data.emoji) return false; + + const user = this.getUserFromMember(data); + if (!user) return false; + + // Verify channel + const channel = this.getChannel(data); + if (!channel || channel.type === 'voice') return false; + + // Verify message + const message = this.getMessage(data, channel); + if (!message) return false; + + // Verify reaction + if (message.partial && !this.client.options.partials.includes(PartialTypes.REACTION)) return false; + const existing = message.reactions.cache.get(data.emoji.id || data.emoji.name); + if (existing && existing.users.cache.has(user.id)) return { message, reaction: existing, user }; + const reaction = message.reactions.add({ + emoji: data.emoji, + count: message.partial ? null : 0, + me: user.id === this.client.user.id, + }); + if (!reaction) return false; + reaction._add(user); + /** + * Emitted whenever a reaction is added to a cached message. + * @event Client#messageReactionAdd + * @param {MessageReaction} messageReaction The reaction object + * @param {User} user The user that applied the guild or reaction emoji + */ + this.client.emit(Events.MESSAGE_REACTION_ADD, reaction, user); + + return { message, reaction, user }; + } +} + +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..a40aa75 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageReactionRemove.js @@ -0,0 +1,44 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +/* +{ user_id: 'id', + message_id: 'id', + emoji: { name: '�', id: null }, + channel_id: 'id' } } +*/ + +class MessageReactionRemove extends Action { + handle(data) { + if (!data.emoji) return false; + + const user = this.getUser(data); + if (!user) return false; + + // Verify channel + const channel = this.getChannel(data); + if (!channel || channel.type === 'voice') return false; + + // Verify message + const message = this.getMessage(data, channel); + if (!message) return false; + + // Verify reaction + const reaction = this.getReaction(data, message, user); + if (!reaction) return false; + reaction._remove(user); + /** + * 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 + */ + this.client.emit(Events.MESSAGE_REACTION_REMOVE, reaction, user); + + return { message, reaction, user }; + } +} + +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..14b79bf --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js @@ -0,0 +1,29 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class MessageReactionRemoveAll extends Action { + handle(data) { + // Verify channel + const channel = this.getChannel(data); + if (!channel || channel.type === 'voice') return false; + + // Verify message + const message = this.getMessage(data, channel); + if (!message) return false; + + message.reactions.cache.clear(); + this.client.emit(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..143702c --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageReactionRemoveEmoji.js @@ -0,0 +1,28 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class MessageReactionRemoveEmoji extends Action { + handle(data) { + const channel = this.getChannel(data); + if (!channel || channel.type === 'voice') return false; + + const message = this.getMessage(data, channel); + if (!message) return false; + + const reaction = this.getReaction(data, message); + if (!reaction) return false; + if (!message.partial) message.reactions.cache.delete(reaction.emoji.id || reaction.emoji.name); + + /** + * Emitted when a bot removes an emoji reaction from a cached message. + * @event Client#messageReactionRemoveEmoji + * @param {MessageReaction} reaction The reaction that was removed + */ + this.client.emit(Events.MESSAGE_REACTION_REMOVE_EMOJI, reaction); + return { reaction }; + } +} + +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..7667dea --- /dev/null +++ b/node_modules/discord.js/src/client/actions/MessageUpdate.js @@ -0,0 +1,24 @@ +'use strict'; + +const Action = require('./Action'); + +class MessageUpdateAction extends Action { + handle(data) { + const channel = this.getChannel(data); + if (channel) { + const { id, channel_id, guild_id, author, timestamp, type } = data; + const message = this.getMessage({ id, channel_id, guild_id, author, timestamp, type }, channel); + if (message) { + const old = message.patch(data); + return { + old, + updated: message, + }; + } + } + + return {}; + } +} + +module.exports = MessageUpdateAction; diff --git a/node_modules/discord.js/src/client/actions/PresenceUpdate.js b/node_modules/discord.js/src/client/actions/PresenceUpdate.js new file mode 100644 index 0000000..f74fbeb --- /dev/null +++ b/node_modules/discord.js/src/client/actions/PresenceUpdate.js @@ -0,0 +1,44 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class PresenceUpdateAction extends Action { + handle(data) { + let user = this.client.users.cache.get(data.user.id); + if (!user && data.user.username) user = this.client.users.add(data.user); + if (!user) return; + + if (data.user && data.user.username) { + if (!user.equals(data.user)) this.client.actions.UserUpdate.handle(data.user); + } + + const guild = this.client.guilds.cache.get(data.guild_id); + if (!guild) return; + + let oldPresence = guild.presences.cache.get(user.id); + if (oldPresence) oldPresence = oldPresence._clone(); + let member = guild.members.cache.get(user.id); + if (!member && data.status !== 'offline') { + member = guild.members.add({ + user, + roles: data.roles, + deaf: false, + mute: false, + }); + this.client.emit(Events.GUILD_MEMBER_AVAILABLE, member); + } + guild.presences.add(Object.assign(data, { guild })); + if (member && this.client.listenerCount(Events.PRESENCE_UPDATE)) { + /** + * Emitted whenever a guild member's presence (e.g. status, activity) is changed. + * @event Client#presenceUpdate + * @param {?Presence} oldPresence The presence before the update, if one at all + * @param {Presence} newPresence The presence after the update + */ + this.client.emit(Events.PRESENCE_UPDATE, oldPresence, member.presence); + } + } +} + +module.exports = PresenceUpdateAction; diff --git a/node_modules/discord.js/src/client/actions/TypingStart.js b/node_modules/discord.js/src/client/actions/TypingStart.js new file mode 100644 index 0000000..7a35bc3 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/TypingStart.js @@ -0,0 +1,58 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); +const textBasedChannelTypes = ['dm', 'text', 'news']; + +class TypingStart extends Action { + handle(data) { + const channel = this.getChannel(data); + if (!channel) { + return; + } + if (!textBasedChannelTypes.includes(channel.type)) { + this.client.emit(Events.WARN, `Discord sent a typing packet to a ${channel.type} channel ${channel.id}`); + return; + } + + const user = this.getUserFromMember(data); + const timestamp = new Date(data.timestamp * 1000); + + if (channel && user) { + if (channel._typing.has(user.id)) { + const typing = channel._typing.get(user.id); + + typing.lastTimestamp = timestamp; + typing.elapsedTime = Date.now() - typing.since; + this.client.clearTimeout(typing.timeout); + typing.timeout = this.tooLate(channel, user); + } else { + const since = new Date(); + const lastTimestamp = new Date(); + channel._typing.set(user.id, { + user, + since, + lastTimestamp, + elapsedTime: Date.now() - since, + timeout: this.tooLate(channel, user), + }); + + /** + * 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 + */ + this.client.emit(Events.TYPING_START, channel, user); + } + } + } + + tooLate(channel, user) { + return channel.client.setTimeout(() => { + channel._typing.delete(user.id); + }, 10000); + } +} + +module.exports = TypingStart; 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..0033aaa --- /dev/null +++ b/node_modules/discord.js/src/client/actions/UserUpdate.js @@ -0,0 +1,35 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class UserUpdateAction extends Action { + handle(data) { + const client = this.client; + + const newUser = client.users.cache.get(data.id); + const oldUser = newUser._update(data); + + if (!oldUser.equals(newUser)) { + /** + * Emitted whenever a user's details (e.g. username) are changed. + * Triggered by the Discord gateway events USER_UPDATE, GUILD_MEMBER_UPDATE, and PRESENCE_UPDATE. + * @event Client#userUpdate + * @param {User} oldUser The user before the update + * @param {User} newUser The user after the update + */ + client.emit(Events.USER_UPDATE, oldUser, newUser); + return { + old: oldUser, + updated: newUser, + }; + } + + return { + old: null, + updated: null, + }; + } +} + +module.exports = UserUpdateAction; diff --git a/node_modules/discord.js/src/client/actions/VoiceStateUpdate.js b/node_modules/discord.js/src/client/actions/VoiceStateUpdate.js new file mode 100644 index 0000000..406bd2f --- /dev/null +++ b/node_modules/discord.js/src/client/actions/VoiceStateUpdate.js @@ -0,0 +1,45 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); +const Structures = require('../../util/Structures'); + +class VoiceStateUpdate extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + if (guild) { + const VoiceState = Structures.get('VoiceState'); + // Update the state + const oldState = guild.voiceStates.cache.has(data.user_id) + ? guild.voiceStates.cache.get(data.user_id)._clone() + : new VoiceState(guild, { user_id: data.user_id }); + + const newState = guild.voiceStates.add(data); + + // Get the member + let member = guild.members.cache.get(data.user_id); + if (member && data.member) { + member._patch(data.member); + } else if (data.member && data.member.user && data.member.joined_at) { + member = guild.members.add(data.member); + } + + // Emit event + if (member && member.user.id === client.user.id) { + client.emit('debug', `[VOICE] received voice state update: ${JSON.stringify(data)}`); + client.voice.onVoiceStateUpdate(data); + } + + /** + * Emitted whenever a member changes voice state - e.g. joins/leaves a channel, mutes/unmutes. + * @event Client#voiceStateUpdate + * @param {VoiceState} oldState The voice state before the update + * @param {VoiceState} newState The voice state after the update + */ + client.emit(Events.VOICE_STATE_UPDATE, oldState, newState); + } + } +} + +module.exports = VoiceStateUpdate; diff --git a/node_modules/discord.js/src/client/actions/WebhooksUpdate.js b/node_modules/discord.js/src/client/actions/WebhooksUpdate.js new file mode 100644 index 0000000..fdf9c56 --- /dev/null +++ b/node_modules/discord.js/src/client/actions/WebhooksUpdate.js @@ -0,0 +1,19 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class WebhooksUpdate extends Action { + handle(data) { + const client = this.client; + const channel = client.channels.cache.get(data.channel_id); + /** + * Emitted whenever a guild text channel has its webhooks changed. + * @event Client#webhookUpdate + * @param {TextChannel} channel The channel that had a webhook update + */ + if (channel) client.emit(Events.WEBHOOKS_UPDATE, channel); + } +} + +module.exports = WebhooksUpdate; 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..1eab808 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/ClientVoiceManager.js @@ -0,0 +1,110 @@ +'use strict'; + +const VoiceBroadcast = require('./VoiceBroadcast'); +const VoiceConnection = require('./VoiceConnection'); +const { Error } = require('../../errors'); +const Collection = require('../../util/Collection'); + +/** + * Manages voice connections for the client + */ +class ClientVoiceManager { + constructor(client) { + /** + * The client that instantiated this voice manager + * @type {Client} + * @readonly + * @name ClientVoiceManager#client + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * A collection mapping connection IDs to the Connection objects + * @type {Collection} + */ + this.connections = new Collection(); + + /** + * Active voice broadcasts that have been created + * @type {VoiceBroadcast[]} + */ + this.broadcasts = []; + } + + /** + * Creates a voice broadcast. + * @returns {VoiceBroadcast} + */ + createBroadcast() { + const broadcast = new VoiceBroadcast(this.client); + this.broadcasts.push(broadcast); + return broadcast; + } + + onVoiceServer({ guild_id, token, endpoint }) { + this.client.emit('debug', `[VOICE] voiceServer guild: ${guild_id} token: ${token} endpoint: ${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); + this.client.emit('debug', `[VOICE] connection? ${!!connection}, ${guild_id} ${session_id} ${channel_id}`); + if (!connection) return; + if (!channel_id) { + connection._disconnect(); + this.connections.delete(guild_id); + return; + } + connection.channel = this.client.channels.cache.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} + * @private + */ + joinChannel(channel) { + return new Promise((resolve, reject) => { + if (!channel.joinable) { + throw new Error('VOICE_JOIN_CHANNEL', channel.full); + } + + 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); + connection.on('debug', msg => + this.client.emit('debug', `[VOICE (${channel.guild.id}:${connection.status})]: ${msg}`), + ); + connection.authenticate(); + this.connections.set(channel.guild.id, connection); + } + + connection.once('failed', reason => { + this.connections.delete(channel.guild.id); + reject(reason); + }); + + connection.on('error', reject); + + connection.once('authenticated', () => { + connection.once('ready', () => { + resolve(connection); + connection.removeListener('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..5755b52 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/VoiceBroadcast.js @@ -0,0 +1,111 @@ +'use strict'; + +const EventEmitter = require('events'); +const BroadcastAudioPlayer = require('./player/BroadcastAudioPlayer'); +const PlayInterface = require('./util/PlayInterface'); +const { Events } = require('../../util/Constants'); + +/** + * A voice broadcast can be played across multiple voice connections for improved shared-stream efficiency. + * + * Example usage: + * ```js + * const broadcast = client.voice.createBroadcast(); + * broadcast.play('./music.mp3'); + * // Play "music.mp3" in all voice connections that the client is in + * for (const connection of client.voice.connections.values()) { + * connection.play(broadcast); + * } + * ``` + * @implements {PlayInterface} + * @extends {EventEmitter} + */ +class VoiceBroadcast extends EventEmitter { + constructor(client) { + super(); + /** + * The client that created the broadcast + * @type {Client} + */ + this.client = client; + /** + * The subscribed StreamDispatchers of this broadcast + * @type {StreamDispatcher[]} + */ + this.subscribers = []; + this.player = new BroadcastAudioPlayer(this); + } + + /** + * The current master dispatcher, if any. This dispatcher controls all that is played by subscribed dispatchers. + * @type {?BroadcastDispatcher} + * @readonly + */ + get dispatcher() { + return this.player.dispatcher; + } + + /** + * Play an audio resource. + * @param {ReadableStream|string} resource The resource to play. + * @param {StreamOptions} [options] The options to play. + * @example + * // Play a local audio file + * broadcast.play('/home/hydrabolt/audio.mp3', { volume: 0.5 }); + * @example + * // Play a ReadableStream + * broadcast.play(ytdl('https://www.youtube.com/watch?v=ZlAU_w7-Xp8', { filter: 'audioonly' })); + * @example + * // Using different protocols: https://ffmpeg.org/ffmpeg-protocols.html + * broadcast.play('http://www.sample-videos.com/audio/mp3/wave.mp3'); + * @returns {BroadcastDispatcher} + */ + play() { + return null; + } + + /** + * Ends the broadcast, unsubscribing all subscribed channels and deleting the broadcast + */ + end() { + for (const dispatcher of this.subscribers) this.delete(dispatcher); + const index = this.client.voice.broadcasts.indexOf(this); + if (index !== -1) this.client.voice.broadcasts.splice(index, 1); + } + + add(dispatcher) { + const index = this.subscribers.indexOf(dispatcher); + if (index === -1) { + this.subscribers.push(dispatcher); + /** + * Emitted whenever a stream dispatcher subscribes to the broadcast. + * @event VoiceBroadcast#subscribe + * @param {StreamDispatcher} subscriber The subscribed dispatcher + */ + this.emit(Events.VOICE_BROADCAST_SUBSCRIBE, dispatcher); + return true; + } else { + return false; + } + } + + delete(dispatcher) { + const index = this.subscribers.indexOf(dispatcher); + if (index !== -1) { + this.subscribers.splice(index, 1); + dispatcher.destroy(); + /** + * Emitted whenever a stream dispatcher unsubscribes to the broadcast. + * @event VoiceBroadcast#unsubscribe + * @param {StreamDispatcher} dispatcher The unsubscribed dispatcher + */ + this.emit(Events.VOICE_BROADCAST_UNSUBSCRIBE, dispatcher); + return true; + } + return false; + } +} + +PlayInterface.applyToClass(VoiceBroadcast); + +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..8c1057f --- /dev/null +++ b/node_modules/discord.js/src/client/voice/VoiceConnection.js @@ -0,0 +1,522 @@ +'use strict'; + +const EventEmitter = require('events'); +const VoiceUDP = require('./networking/VoiceUDPClient'); +const VoiceWebSocket = require('./networking/VoiceWebSocket'); +const AudioPlayer = require('./player/AudioPlayer'); +const VoiceReceiver = require('./receiver/Receiver'); +const PlayInterface = require('./util/PlayInterface'); +const Silence = require('./util/Silence'); +const { Error } = require('../../errors'); +const { OPCodes, VoiceOPCodes, VoiceStatus, Events } = require('../../util/Constants'); +const Speaking = require('../../util/Speaking'); +const Util = require('../../util/Util'); + +// Workaround for Discord now requiring silence to be sent before being able to receive audio +class SingleSilence extends Silence { + _read() { + super._read(); + this.push(null); + } +} + +const SUPPORTED_MODES = ['xsalsa20_poly1305_lite', 'xsalsa20_poly1305_suffix', 'xsalsa20_poly1305']; + +/** + * Represents a connection to a guild's voice server. + * ```js + * // Obtained using: + * voiceChannel.join() + * .then(connection => { + * + * }); + * ``` + * @extends {EventEmitter} + * @implements {PlayInterface} + */ +class VoiceConnection extends EventEmitter { + constructor(voiceManager, channel) { + super(); + + /** + * The voice manager that instantiated this connection + * @type {ClientVoiceManager} + */ + this.voiceManager = voiceManager; + + /** + * The voice channel this connection is currently serving + * @type {VoiceChannel} + */ + this.channel = channel; + + /** + * The current status of the voice connection + * @type {VoiceStatus} + */ + this.status = VoiceStatus.AUTHENTICATING; + + /** + * Our current speaking state + * @type {Readonly} + */ + this.speaking = new Speaking().freeze(); + + /** + * 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); + }); + + this.once('closing', () => this.player.destroy()); + + /** + * Map SSRC values to user IDs + * @type {Map} + * @private + */ + this.ssrcMap = new Map(); + + /** + * Tracks which users are talking + * @type {Map>} + * @private + */ + this._speaking = new Map(); + + /** + * Object that wraps contains the `ws` and `udp` sockets of this voice connection + * @type {Object} + * @private + */ + this.sockets = {}; + + /** + * The voice receiver of this connection + * @type {VoiceReceiver} + */ + this.receiver = new VoiceReceiver(this); + } + + /** + * The client that instantiated this connection + * @type {Client} + * @readonly + */ + get client() { + return this.voiceManager.client; + } + + /** + * The current stream dispatcher (if any) + * @type {?StreamDispatcher} + * @readonly + */ + get dispatcher() { + return this.player.dispatcher; + } + + /** + * Sets whether the voice connection should display as "speaking", "soundshare" or "none". + * @param {BitFieldResolvable} value The new speaking state + */ + setSpeaking(value) { + if (this.speaking.equals(value)) return; + if (this.status !== VoiceStatus.CONNECTED) return; + this.speaking = new Speaking(value).freeze(); + this.sockets.ws + .sendPacket({ + op: VoiceOPCodes.SPEAKING, + d: { + speaking: this.speaking.bitfield, + delay: 0, + ssrc: this.authentication.ssrc, + }, + }) + .catch(e => { + this.emit('debug', e); + }); + } + + /** + * The voice state of this connection + * @type {?VoiceState} + */ + get voice() { + return this.channel.guild.voice; + } + + /** + * Sends a request to the main gateway to join a voice channel. + * @param {Object} [options] The options to provide + * @returns {Promise} + * @private + */ + sendVoiceStateUpdate(options = {}) { + options = Util.mergeDefault( + { + guild_id: this.channel.guild.id, + channel_id: this.channel.id, + self_mute: this.voice ? this.voice.selfMute : false, + self_deaf: this.voice ? this.voice.selfDeaf : false, + }, + options, + ); + + this.emit('debug', `Sending voice state update: ${JSON.stringify(options)}`); + + return this.channel.guild.shard.send( + { + op: OPCodes.VOICE_STATE_UPDATE, + d: options, + }, + true, + ); + } + + /** + * 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} + * @private + */ + setTokenAndEndpoint(token, endpoint) { + this.emit('debug', `Token "${token}" and endpoint "${endpoint}"`); + if (!endpoint) { + // Signifies awaiting endpoint stage + return; + } + + if (!token) { + this.authenticateFailed('VOICE_TOKEN_ABSENT'); + return; + } + + endpoint = endpoint.match(/([^:]*)/)[0]; + this.emit('debug', `Endpoint resolved as ${endpoint}`); + + if (!endpoint) { + this.authenticateFailed('VOICE_INVALID_ENDPOINT'); + return; + } + + if (this.status === 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 + * @private + */ + setSessionID(sessionID) { + this.emit('debug', `Setting sessionID ${sessionID} (stored as "${this.authentication.sessionID}")`); + if (!sessionID) { + this.authenticateFailed('VOICE_SESSION_ABSENT'); + return; + } + + if (this.status === 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; + this.emit('debug', `Authenticated with sessionID ${sessionID}`); + if (token && endpoint && sessionID) { + this.status = 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); + this.emit('debug', `Authenticate failed - ${reason}`); + if (this.status === 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 = 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('VOICE_CONNECTION_TIMEOUT'), 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.speaking = new Speaking().freeze(); + this.status = VoiceStatus.RECONNECTING; + this.emit('debug', `Reconnecting to ${endpoint}`); + /** + * Emitted when the voice connection is reconnecting (typically after a region change). + * @event VoiceConnection#reconnecting + */ + this.emit('reconnecting'); + this.connect(); + } + + /** + * Disconnects the voice connection, causing a disconnect and closing event to be emitted. + */ + disconnect() { + this.emit('closing'); + this.emit('debug', 'disconnect() triggered'); + this.client.clearTimeout(this.connectTimeout); + const conn = this.voiceManager.connections.get(this.channel.guild.id); + if (conn === this) this.voiceManager.connections.delete(this.channel.guild.id); + this.sendVoiceStateUpdate({ + channel_id: null, + }); + this._disconnect(); + } + + /** + * Internally disconnects (doesn't send disconnect packet). + * @private + */ + _disconnect() { + this.cleanup(); + this.status = VoiceStatus.DISCONNECTED; + /** + * Emitted when the voice connection disconnects. + * @event VoiceConnection#disconnect + */ + this.emit('disconnect'); + } + + /** + * Cleans up after disconnect. + * @private + */ + cleanup() { + this.player.destroy(); + this.speaking = new Speaking().freeze(); + const { ws, udp } = this.sockets; + + this.emit('debug', 'Connection clean up'); + + if (ws) { + ws.removeAllListeners('error'); + ws.removeAllListeners('ready'); + ws.removeAllListeners('sessionDescription'); + ws.removeAllListeners('speaking'); + ws.shutdown(); + } + + if (udp) udp.removeAllListeners('error'); + + this.sockets.ws = null; + this.sockets.udp = null; + } + + /** + * Connect the voice connection. + * @private + */ + connect() { + this.emit('debug', `Connect triggered`); + if (this.status !== VoiceStatus.RECONNECTING) { + if (this.sockets.ws) throw new Error('WS_CONNECTION_EXISTS'); + if (this.sockets.udp) throw new Error('UDP_CONNECTION_EXISTS'); + } + + 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('debug', msg => this.emit('debug', msg)); + udp.on('debug', msg => this.emit('debug', msg)); + 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)); + + this.sockets.ws.connect(); + } + + /** + * Invoked when the voice websocket is ready. + * @param {Object} data The received data + * @private + */ + onReady(data) { + Object.assign(this.authentication, data); + for (let mode of data.modes) { + if (SUPPORTED_MODES.includes(mode)) { + this.authentication.mode = mode; + this.emit('debug', `Selecting the ${mode} mode`); + break; + } + } + this.sockets.udp.createUDPSocket(data.ip); + } + + /** + * Invoked when a session description is received. + * @param {Object} data The received data + * @private + */ + onSessionDescription(data) { + Object.assign(this.authentication, data); + this.status = VoiceStatus.CONNECTED; + const ready = () => { + this.client.clearTimeout(this.connectTimeout); + this.emit('debug', `Ready with authentication details: ${JSON.stringify(this.authentication)}`); + /** + * 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. + const dispatcher = this.play(new SingleSilence(), { type: 'opus', volume: false }); + dispatcher.once('finish', ready); + } + } + + onStartSpeaking({ user_id, ssrc, speaking }) { + this.ssrcMap.set(+ssrc, { userID: user_id, speaking: speaking }); + } + + /** + * Invoked when a speaking event is received. + * @param {Object} data The received data + * @private + */ + onSpeaking({ user_id, speaking }) { + speaking = new Speaking(speaking).freeze(); + const guild = this.channel.guild; + const user = this.client.users.cache.get(user_id); + const old = this._speaking.get(user_id); + this._speaking.set(user_id, speaking); + /** + * Emitted whenever a user changes speaking state. + * @event VoiceConnection#speaking + * @param {User} user The user that has changed speaking state + * @param {Readonly} speaking The speaking state of the user + */ + if (this.status === VoiceStatus.CONNECTED) { + this.emit('speaking', user, speaking); + if (!speaking.has(Speaking.FLAGS.SPEAKING)) { + this.receiver.packets._stoppedSpeaking(user_id); + } + } + + if (guild && user && !speaking.equals(old)) { + const member = guild.member(user); + if (member) { + /** + * Emitted once a guild member changes speaking state. + * @event Client#guildMemberSpeaking + * @param {GuildMember} member The member that started/stopped speaking + * @param {Readonly} speaking The speaking state of the member + */ + this.client.emit(Events.GUILD_MEMBER_SPEAKING, member, speaking); + } + } + } + + play() {} // eslint-disable-line no-empty-function +} + +PlayInterface.applyToClass(VoiceConnection); + +module.exports = VoiceConnection; diff --git a/node_modules/discord.js/src/client/voice/dispatcher/BroadcastDispatcher.js b/node_modules/discord.js/src/client/voice/dispatcher/BroadcastDispatcher.js new file mode 100644 index 0000000..ae8d412 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/dispatcher/BroadcastDispatcher.js @@ -0,0 +1,46 @@ +'use strict'; + +const StreamDispatcher = require('./StreamDispatcher'); + +/** + * The class that sends voice packet data to the voice connection. + * @implements {VolumeInterface} + * @extends {StreamDispatcher} + */ +class BroadcastDispatcher extends StreamDispatcher { + constructor(player, options, streams) { + super(player, options, streams); + this.broadcast = player.broadcast; + } + + _write(chunk, enc, done) { + if (!this.startTime) this.startTime = Date.now(); + for (const dispatcher of this.broadcast.subscribers) { + dispatcher._write(chunk, enc); + } + this._step(done); + } + + _destroy(err, cb) { + if (this.player.dispatcher === this) this.player.dispatcher = null; + const { streams } = this; + if (streams.opus) streams.opus.unpipe(this); + if (streams.ffmpeg) streams.ffmpeg.destroy(); + super._destroy(err, cb); + } + + /** + * Set the bitrate of the current Opus encoder if using a compatible Opus stream. + * @param {number} value New bitrate, in kbps + * If set to 'auto', 48kbps will be used + * @returns {boolean} true if the bitrate has been successfully changed. + */ + setBitrate(value) { + if (!value || !this.streams.opus || !this.streams.opus.setBitrate) return false; + const bitrate = value === 'auto' ? 48 : value; + this.streams.opus.setBitrate(bitrate * 1000); + return true; + } +} + +module.exports = BroadcastDispatcher; 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..bb1c7bb --- /dev/null +++ b/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js @@ -0,0 +1,354 @@ +'use strict'; + +const { Writable } = require('stream'); +const secretbox = require('../util/Secretbox'); +const Silence = require('../util/Silence'); +const VolumeInterface = require('../util/VolumeInterface'); + +const FRAME_LENGTH = 20; +const CHANNELS = 2; +const TIMESTAMP_INC = (48000 / 100) * CHANNELS; + +const MAX_NONCE_SIZE = 2 ** 32 - 1; +const nonce = Buffer.alloc(24); + +/** + * @external WritableStream + * @see {@link https://nodejs.org/api/stream.html#stream_class_stream_writable} + */ + +/** + * 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.play('/home/hydrabolt/audio.mp3'); + * }); + * ``` + * @implements {VolumeInterface} + * @extends {WritableStream} + */ +class StreamDispatcher extends Writable { + constructor(player, { seek = 0, volume = 1, fec, plp, bitrate = 96, highWaterMark = 12 } = {}, streams) { + const streamOptions = { seek, volume, fec, plp, bitrate, highWaterMark }; + super(streamOptions); + /** + * The Audio Player that controls this dispatcher + * @type {AudioPlayer} + */ + this.player = player; + this.streamOptions = streamOptions; + this.streams = streams; + this.streams.silence = new Silence(); + + this._nonce = 0; + this._nonceBuffer = Buffer.alloc(24); + + /** + * The time that the stream was paused at (null if not paused) + * @type {?number} + */ + this.pausedSince = null; + this._writeCallback = null; + + /** + * The broadcast controlling this dispatcher, if any + * @type {?VoiceBroadcast} + */ + this.broadcast = this.streams.broadcast || null; + + this._pausedTime = 0; + this._silentPausedTime = 0; + this.count = 0; + + this.on('finish', () => { + this._cleanup(); + this._setSpeaking(0); + }); + + this.setVolume(volume); + this.setBitrate(bitrate); + if (typeof fec !== 'undefined') this.setFEC(fec); + if (typeof plp !== 'undefined') this.setPLP(plp); + + const streamError = (type, err) => { + /** + * Emitted when the dispatcher encounters an error. + * @event StreamDispatcher#error + */ + if (type && err) { + err.message = `${type} stream: ${err.message}`; + this.emit(this.player.dispatcher === this ? 'error' : 'debug', err); + } + this.destroy(); + }; + + this.on('error', () => streamError()); + if (this.streams.input) this.streams.input.on('error', err => streamError('input', err)); + if (this.streams.ffmpeg) this.streams.ffmpeg.on('error', err => streamError('ffmpeg', err)); + if (this.streams.opus) this.streams.opus.on('error', err => streamError('opus', err)); + if (this.streams.volume) this.streams.volume.on('error', err => streamError('volume', err)); + } + + get _sdata() { + return this.player.streamingData; + } + + _write(chunk, enc, done) { + if (!this.startTime) { + /** + * Emitted once the stream has started to play. + * @event StreamDispatcher#start + */ + this.emit('start'); + this.startTime = Date.now(); + } + this._playChunk(chunk); + this._step(done); + } + + _destroy(err, cb) { + this._cleanup(); + super._destroy(err, cb); + } + + _cleanup() { + if (this.player.dispatcher === this) this.player.dispatcher = null; + const { streams } = this; + if (streams.broadcast) streams.broadcast.delete(this); + if (streams.opus) streams.opus.destroy(); + if (streams.ffmpeg) streams.ffmpeg.destroy(); + } + + /** + * Pauses playback + * @param {boolean} [silence=false] Whether to play silence while paused to prevent audio glitches + */ + pause(silence = false) { + if (this.paused) return; + if (this.streams.opus) this.streams.opus.unpipe(this); + if (silence) { + this.streams.silence.pipe(this); + this._silence = true; + } else { + this._setSpeaking(0); + } + this.pausedSince = Date.now(); + } + + /** + * Whether or not playback is paused + * @type {boolean} + * @readonly + */ + get paused() { + return Boolean(this.pausedSince); + } + + /** + * Total time that this dispatcher has been paused in milliseconds + * @type {number} + * @readonly + */ + get pausedTime() { + return this._silentPausedTime + this._pausedTime + (this.paused ? Date.now() - this.pausedSince : 0); + } + + /** + * Resumes playback + */ + resume() { + if (!this.pausedSince) return; + this.streams.silence.unpipe(this); + if (this.streams.opus) this.streams.opus.pipe(this); + if (this._silence) { + this._silentPausedTime += Date.now() - this.pausedSince; + this._silence = false; + } else { + this._pausedTime += Date.now() - this.pausedSince; + } + this.pausedSince = null; + if (typeof this._writeCallback === 'function') this._writeCallback(); + } + + /** + * The time (in milliseconds) that the dispatcher has actually been playing audio for + * @type {number} + * @readonly + */ + get streamTime() { + return this.count * FRAME_LENGTH; + } + + /** + * The time (in milliseconds) that the dispatcher has been playing audio for, taking into account skips and pauses + * @type {number} + * @readonly + */ + get totalStreamTime() { + return Date.now() - this.startTime; + } + + /** + * Set the bitrate of the current Opus encoder if using a compatible Opus stream. + * @param {number} value New bitrate, in kbps + * If set to 'auto', the voice channel's bitrate will be used + * @returns {boolean} true if the bitrate has been successfully changed. + */ + setBitrate(value) { + if (!value || !this.bitrateEditable) return false; + const bitrate = value === 'auto' ? this.player.voiceConnection.channel.bitrate : value; + this.streams.opus.setBitrate(bitrate * 1000); + return true; + } + + /** + * Sets the expected packet loss percentage if using a compatible Opus stream. + * @param {number} value between 0 and 1 + * @returns {boolean} Returns true if it was successfully set. + */ + setPLP(value) { + if (!this.bitrateEditable) return false; + this.streams.opus.setPLP(value); + return true; + } + + /** + * Enables or disables forward error correction if using a compatible Opus stream. + * @param {boolean} enabled true to enable + * @returns {boolean} Returns true if it was successfully set. + */ + setFEC(enabled) { + if (!this.bitrateEditable) return false; + this.streams.opus.setFEC(enabled); + return true; + } + + _step(done) { + this._writeCallback = () => { + this._writeCallback = null; + done(); + }; + if (!this.streams.broadcast) { + const next = FRAME_LENGTH + this.count * FRAME_LENGTH - (Date.now() - this.startTime - this._pausedTime); + setTimeout(() => { + if ((!this.pausedSince || this._silence) && this._writeCallback) this._writeCallback(); + }, next); + } + this._sdata.sequence++; + this._sdata.timestamp += TIMESTAMP_INC; + if (this._sdata.sequence >= 2 ** 16) this._sdata.sequence = 0; + if (this._sdata.timestamp >= 2 ** 32) this._sdata.timestamp = 0; + this.count++; + } + + _final(callback) { + this._writeCallback = null; + callback(); + } + + _playChunk(chunk) { + if (this.player.dispatcher !== this || !this.player.voiceConnection.authentication.secret_key) return; + this._sendPacket(this._createPacket(this._sdata.sequence, this._sdata.timestamp, chunk)); + } + + _encrypt(buffer) { + const { secret_key, mode } = this.player.voiceConnection.authentication; + if (mode === 'xsalsa20_poly1305_lite') { + this._nonce++; + if (this._nonce > MAX_NONCE_SIZE) this._nonce = 0; + this._nonceBuffer.writeUInt32BE(this._nonce, 0); + return [secretbox.methods.close(buffer, this._nonceBuffer, secret_key), this._nonceBuffer.slice(0, 4)]; + } else if (mode === 'xsalsa20_poly1305_suffix') { + const random = secretbox.methods.random(24); + return [secretbox.methods.close(buffer, random, secret_key), random]; + } else { + return [secretbox.methods.close(buffer, nonce, secret_key)]; + } + } + + _createPacket(sequence, timestamp, buffer) { + const packetBuffer = Buffer.alloc(12); + 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); + return Buffer.concat([packetBuffer, ...this._encrypt(buffer)]); + } + + _sendPacket(packet) { + /** + * Emitted whenever the dispatcher has debug information. + * @event StreamDispatcher#debug + * @param {string} info The debug info + */ + this._setSpeaking(1); + if (!this.player.voiceConnection.sockets.udp) { + this.emit('debug', 'Failed to send a packet - no UDP socket'); + return; + } + this.player.voiceConnection.sockets.udp.send(packet).catch(e => { + this._setSpeaking(0); + this.emit('debug', `Failed to send a packet - ${e}`); + }); + } + + _setSpeaking(value) { + if (typeof this.player.voiceConnection !== 'undefined') { + this.player.voiceConnection.setSpeaking(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); + } + + get volumeEditable() { + return Boolean(this.streams.volume); + } + + /** + * Whether or not the Opus bitrate of this stream is editable + * @type {boolean} + * @readonly + */ + get bitrateEditable() { + return this.streams.opus && this.streams.opus.setBitrate; + } + + // Volume + get volume() { + return this.streams.volume ? this.streams.volume.volume : 1; + } + + setVolume(value) { + if (!this.streams.volume) return false; + /** + * Emitted when the volume of this dispatcher changes. + * @event StreamDispatcher#volumeChange + * @param {number} oldVolume The old volume of this dispatcher + * @param {number} newVolume The new volume of this dispatcher + */ + this.emit('volumeChange', this.volume, value); + this.streams.volume.setVolume(value); + return true; + } + + // Volume stubs for docs + /* eslint-disable no-empty-function*/ + get volumeDecibels() {} + get volumeLogarithmic() {} + setVolumeDecibels() {} + setVolumeLogarithmic() {} +} + +VolumeInterface.applyToClass(StreamDispatcher); + +module.exports = StreamDispatcher; diff --git a/node_modules/discord.js/src/client/voice/networking/VoiceUDPClient.js b/node_modules/discord.js/src/client/voice/networking/VoiceUDPClient.js new file mode 100644 index 0000000..b86428a --- /dev/null +++ b/node_modules/discord.js/src/client/voice/networking/VoiceUDPClient.js @@ -0,0 +1,154 @@ +'use strict'; + +const udp = require('dgram'); +const EventEmitter = require('events'); +const { Error } = require('../../../errors'); +const { VoiceOPCodes } = require('../../../util/Constants'); + +/** + * 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() { + this.emit('debug', `[UDP] shutdown requested`); + 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} + */ + send(packet) { + return new Promise((resolve, reject) => { + if (!this.socket) throw new Error('UDP_SEND_FAIL'); + if (!this.discordAddress || !this.discordPort) throw new Error('UDP_ADDRESS_MALFORMED'); + this.socket.send(packet, 0, packet.length, this.discordPort, this.discordAddress, error => { + if (error) { + this.emit('debug', `[UDP] >> ERROR: ${error}`); + reject(error); + } else { + resolve(packet); + } + }); + }); + } + + async createUDPSocket(address) { + this.discordAddress = address; + const socket = (this.socket = udp.createSocket('udp4')); + socket.on('error', e => { + this.emit('debug', `[UDP] Error: ${e}`); + this.emit('error', e); + }); + socket.on('close', () => { + this.emit('debug', '[UDP] socket closed'); + }); + this.emit('debug', `[UDP] created socket`); + socket.once('message', message => { + this.emit('debug', `[UDP] message: [${[...message]}] (${message})`); + // Stop if the sockets have been deleted because the connection has been closed already + if (!this.voiceConnection.sockets.ws) return; + + const packet = parseLocalPacket(message); + if (packet.error) { + this.emit('debug', `[UDP] ERROR: ${packet.error}`); + this.emit('error', packet.error); + return; + } + + this.localAddress = packet.address; + this.localPort = packet.port; + + this.voiceConnection.sockets.ws.sendPacket({ + op: VoiceOPCodes.SELECT_PROTOCOL, + d: { + protocol: 'udp', + data: { + address: packet.address, + port: packet.port, + mode: this.voiceConnection.authentication.mode, + }, + }, + }); + + this.emit('debug', `[UDP] << ${JSON.stringify(packet)}`); + + socket.on('message', buffer => this.voiceConnection.receiver.packets.push(buffer)); + }); + + const blankMessage = Buffer.alloc(70); + blankMessage.writeUIntBE(this.voiceConnection.authentication.ssrc, 0, 4); + this.emit('debug', `Sending IP discovery packet: [${[...blankMessage]}]`); + await this.send(blankMessage); + this.emit('debug', `Successfully sent IP discovery packet`); + } +} + +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/networking/VoiceWebSocket.js b/node_modules/discord.js/src/client/voice/networking/VoiceWebSocket.js new file mode 100644 index 0000000..fefd241 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/networking/VoiceWebSocket.js @@ -0,0 +1,264 @@ +'use strict'; + +const EventEmitter = require('events'); +const WebSocket = require('../../../WebSocket'); +const { Error } = require('../../../errors'); +const { OPCodes, VoiceOPCodes } = require('../../../util/Constants'); + +/** + * Represents a Voice Connection's WebSocket. + * @extends {EventEmitter} + * @private + */ +class VoiceWebSocket extends EventEmitter { + constructor(connection) { + super(); + /** + * The Voice Connection that this WebSocket serves + * @type {VoiceConnection} + */ + this.connection = connection; + + /** + * How many connection attempts have been made + * @type {number} + */ + this.attempts = 0; + + this.dead = false; + this.connection.on('closing', this.shutdown.bind(this)); + } + + /** + * The client of this voice WebSocket + * @type {Client} + * @readonly + */ + get client() { + return this.connection.client; + } + + shutdown() { + this.emit('debug', `[WS] shutdown requested`); + this.dead = true; + this.reset(); + } + + /** + * Resets the current WebSocket. + */ + reset() { + this.emit('debug', `[WS] reset requested`); + 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() { + this.emit('debug', `[WS] connect requested`); + if (this.dead) return; + if (this.ws) this.reset(); + if (this.attempts >= 5) { + this.emit('debug', new Error('VOICE_CONNECTION_ATTEMPTS_EXCEEDED', this.attempts)); + return; + } + + this.attempts++; + + /** + * The actual WebSocket used to connect to the Voice WebSocket Server. + * @type {WebSocket} + */ + this.ws = WebSocket.create(`wss://${this.connection.authentication.endpoint}/`, { v: 4 }); + this.emit('debug', `[WS] connecting, ${this.attempts} attempts, ${this.ws.url}`); + 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} + */ + send(data) { + this.emit('debug', `[WS] >> ${data}`); + return new Promise((resolve, reject) => { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) throw new Error('WS_NOT_OPEN', 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} + */ + sendPacket(packet) { + try { + packet = JSON.stringify(packet); + } catch (error) { + return Promise.reject(error); + } + return this.send(packet); + } + + /** + * Called whenever the WebSocket opens. + */ + onOpen() { + this.emit('debug', `[WS] opened at gateway ${this.connection.authentication.endpoint}`); + this.sendPacket({ + op: OPCodes.DISPATCH, + d: { + server_id: this.connection.channel.guild.id, + user_id: this.client.user.id, + token: this.connection.authentication.token, + session_id: this.connection.authentication.sessionID, + }, + }).catch(() => { + this.emit('error', new Error('VOICE_JOIN_SOCKET_CLOSED')); + }); + } + + /** + * 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(WebSocket.unpack(event.data, 'json')); + } catch (error) { + return this.onError(error); + } + } + + /** + * Called whenever the connection to the WebSocket server is lost. + */ + onClose() { + this.emit('debug', `[WS] closed`); + 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('debug', `[WS] Error: ${error}`); + this.emit('error', error); + } + + /** + * Called whenever a valid packet is received from the WebSocket. + * @param {Object} packet The received packet + */ + onPacket(packet) { + this.emit('debug', `[WS] << ${JSON.stringify(packet)}`); + switch (packet.op) { + case VoiceOPCodes.HELLO: + this.setHeartbeat(packet.d.heartbeat_interval); + break; + case VoiceOPCodes.READY: + /** + * Emitted once the voice WebSocket receives the ready packet. + * @param {Object} packet The received packet + * @event VoiceWebSocket#ready + */ + this.emit('ready', packet.d); + break; + /* eslint-disable no-case-declarations */ + case VoiceOPCodes.SESSION_DESCRIPTION: + packet.d.secret_key = new Uint8Array(packet.d.secret_key); + /** + * Emitted once the Voice Websocket receives a description of this voice session. + * @param {Object} packet The received packet + * @event VoiceWebSocket#sessionDescription + */ + this.emit('sessionDescription', packet.d); + break; + case VoiceOPCodes.CLIENT_CONNECT: + this.connection.ssrcMap.set(+packet.d.audio_ssrc, { userID: packet.d.user_id, speaking: 0 }); + break; + case VoiceOPCodes.CLIENT_DISCONNECT: + const streamInfo = this.connection.receiver && this.connection.receiver.packets.streams.get(packet.d.user_id); + if (streamInfo) { + this.connection.receiver.packets.streams.delete(packet.d.user_id); + streamInfo.stream.push(null); + } + break; + case 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('VOICE_INVALID_HEARTBEAT')); + return; + } + if (this.heartbeatInterval) { + /** + * Emitted whenever 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'); + this.client.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; + } + this.client.clearInterval(this.heartbeatInterval); + this.heartbeatInterval = null; + } + + /** + * Sends a heartbeat packet. + */ + sendHeartbeat() { + this.sendPacket({ op: VoiceOPCodes.HEARTBEAT, d: Math.floor(Math.random() * 10e10) }).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/player/AudioPlayer.js b/node_modules/discord.js/src/client/voice/player/AudioPlayer.js new file mode 100644 index 0000000..6f719a7 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/player/AudioPlayer.js @@ -0,0 +1,27 @@ +'use strict'; + +const BasePlayer = require('./BasePlayer'); + +/** + * An Audio Player for a Voice Connection. + * @private + * @extends {BasePlayer} + */ +class AudioPlayer extends BasePlayer { + constructor(voiceConnection) { + super(); + /** + * The voice connection that the player serves + * @type {VoiceConnection} + */ + this.voiceConnection = voiceConnection; + } + + playBroadcast(broadcast, options) { + const dispatcher = this.createDispatcher(options, { broadcast }); + broadcast.add(dispatcher); + return dispatcher; + } +} + +module.exports = AudioPlayer; diff --git a/node_modules/discord.js/src/client/voice/player/BasePlayer.js b/node_modules/discord.js/src/client/voice/player/BasePlayer.js new file mode 100644 index 0000000..b968f82 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/player/BasePlayer.js @@ -0,0 +1,92 @@ +'use strict'; + +const EventEmitter = require('events'); +const { Readable: ReadableStream } = require('stream'); +const prism = require('prism-media'); +const StreamDispatcher = require('../dispatcher/StreamDispatcher'); + +const FFMPEG_ARGUMENTS = ['-analyzeduration', '0', '-loglevel', '0', '-f', 's16le', '-ar', '48000', '-ac', '2']; + +/** + * An Audio Player for a Voice Connection. + * @private + * @extends {EventEmitter} + */ +class BasePlayer extends EventEmitter { + constructor() { + super(); + + this.dispatcher = null; + + this.streamingData = { + channels: 2, + sequence: 0, + timestamp: 0, + }; + } + + destroy() { + this.destroyDispatcher(); + } + + destroyDispatcher() { + if (this.dispatcher) { + this.dispatcher.destroy(); + this.dispatcher = null; + } + } + + playUnknown(input, options) { + this.destroyDispatcher(); + + const isStream = input instanceof ReadableStream; + + const args = isStream ? FFMPEG_ARGUMENTS.slice() : ['-i', input, ...FFMPEG_ARGUMENTS]; + if (options.seek) args.unshift('-ss', String(options.seek)); + + const ffmpeg = new prism.FFmpeg({ args }); + const streams = { ffmpeg }; + if (isStream) { + streams.input = input; + input.pipe(ffmpeg); + } + return this.playPCMStream(ffmpeg, options, streams); + } + + playPCMStream(stream, options, streams = {}) { + this.destroyDispatcher(); + const opus = (streams.opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 })); + if (options && options.volume === false) { + stream.pipe(opus); + return this.playOpusStream(opus, options, streams); + } + streams.volume = new prism.VolumeTransformer({ type: 's16le', volume: options ? options.volume : 1 }); + stream.pipe(streams.volume).pipe(opus); + return this.playOpusStream(opus, options, streams); + } + + playOpusStream(stream, options, streams = {}) { + this.destroyDispatcher(); + streams.opus = stream; + if (options.volume !== false && !streams.input) { + streams.input = stream; + const decoder = new prism.opus.Decoder({ channels: 2, rate: 48000, frameSize: 960 }); + streams.volume = new prism.VolumeTransformer({ type: 's16le', volume: options ? options.volume : 1 }); + streams.opus = stream + .pipe(decoder) + .pipe(streams.volume) + .pipe(new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 })); + } + const dispatcher = this.createDispatcher(options, streams); + streams.opus.pipe(dispatcher); + return dispatcher; + } + + createDispatcher(options, streams, broadcast) { + this.destroyDispatcher(); + const dispatcher = (this.dispatcher = new StreamDispatcher(this, options, streams, broadcast)); + return dispatcher; + } +} + +module.exports = BasePlayer; diff --git a/node_modules/discord.js/src/client/voice/player/BroadcastAudioPlayer.js b/node_modules/discord.js/src/client/voice/player/BroadcastAudioPlayer.js new file mode 100644 index 0000000..05197a4 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/player/BroadcastAudioPlayer.js @@ -0,0 +1,28 @@ +'use strict'; + +const BasePlayer = require('./BasePlayer'); +const BroadcastDispatcher = require('../dispatcher/BroadcastDispatcher'); + +/** + * An Audio Player for a Voice Connection. + * @private + * @extends {BasePlayer} + */ +class AudioPlayer extends BasePlayer { + constructor(broadcast) { + super(); + /** + * The broadcast that the player serves + * @type {VoiceBroadcast} + */ + this.broadcast = broadcast; + } + + createDispatcher(options, streams) { + this.destroyDispatcher(); + const dispatcher = (this.dispatcher = new BroadcastDispatcher(this, options, streams)); + return dispatcher; + } +} + +module.exports = AudioPlayer; diff --git a/node_modules/discord.js/src/client/voice/receiver/PacketHandler.js b/node_modules/discord.js/src/client/voice/receiver/PacketHandler.js new file mode 100644 index 0000000..ebcd673 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/receiver/PacketHandler.js @@ -0,0 +1,121 @@ +'use strict'; + +const EventEmitter = require('events'); +const secretbox = require('../util/Secretbox'); + +// 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; + +class Readable extends require('stream').Readable { + _read() {} // eslint-disable-line no-empty-function +} + +class PacketHandler extends EventEmitter { + constructor(receiver) { + super(); + this.nonce = Buffer.alloc(24); + this.receiver = receiver; + this.streams = new Map(); + this.speakingTimeouts = new Map(); + } + + get connection() { + return this.receiver.connection; + } + + _stoppedSpeaking(userID) { + const streamInfo = this.streams.get(userID); + if (streamInfo && streamInfo.end === 'silence') { + this.streams.delete(userID); + streamInfo.stream.push(null); + } + } + + makeStream(user, end) { + if (this.streams.has(user)) return this.streams.get(user).stream; + const stream = new Readable(); + stream.on('end', () => this.streams.delete(user)); + this.streams.set(user, { stream, end }); + return stream; + } + + parseBuffer(buffer) { + const { secret_key, mode } = this.receiver.connection.authentication; + + // Choose correct nonce depending on encryption + let end; + if (mode === 'xsalsa20_poly1305_lite') { + buffer.copy(this.nonce, 0, buffer.length - 4); + end = buffer.length - 4; + } else if (mode === 'xsalsa20_poly1305_suffix') { + buffer.copy(this.nonce, 0, buffer.length - 24); + end = buffer.length - 24; + } else { + buffer.copy(this.nonce, 0, 0, 12); + } + + // Open packet + let packet = secretbox.methods.open(buffer.slice(12, end), this.nonce, secret_key); + if (!packet) return new Error('Failed to decrypt voice packet'); + packet = Buffer.from(packet); + + // Strip RTP Header Extensions (one-byte only) + if (packet[0] === 0xbe && packet[1] === 0xde && packet.length > 4) { + const headerExtensionLength = packet.readUInt16BE(2); + let offset = 4; + for (let i = 0; i < headerExtensionLength; i++) { + const byte = packet[offset]; + offset++; + if (byte === 0) continue; + offset += 1 + (0b1111 & (byte >> 4)); + } + // Skip over undocumented Discord byte + offset++; + + packet = packet.slice(offset); + } + + return packet; + } + + push(buffer) { + const ssrc = buffer.readUInt32BE(8); + const userStat = this.connection.ssrcMap.get(ssrc); + if (!userStat) return; + + let speakingTimeout = this.speakingTimeouts.get(ssrc); + if (typeof speakingTimeout === 'undefined') { + // Ensure at least the speaking bit is set. + // As the object is by reference, it's only needed once per client re-connect. + if (userStat.speaking === 0) { + userStat.speaking = 1; + } + this.connection.onSpeaking({ user_id: userStat.userID, ssrc: ssrc, speaking: userStat.speaking }); + speakingTimeout = this.receiver.connection.client.setTimeout(() => { + try { + this.connection.onSpeaking({ user_id: userStat.userID, ssrc: ssrc, speaking: 0 }); + this.receiver.connection.client.clearTimeout(speakingTimeout); + this.speakingTimeouts.delete(ssrc); + } catch { + // Connection already closed, ignore + } + }, DISCORD_SPEAKING_DELAY); + this.speakingTimeouts.set(ssrc, speakingTimeout); + } else { + speakingTimeout.refresh(); + } + + let stream = this.streams.get(userStat.userID); + if (!stream) return; + stream = stream.stream; + const opusPacket = this.parseBuffer(buffer); + if (opusPacket instanceof Error) { + this.emit('error', opusPacket); + return; + } + stream.push(opusPacket); + } +} + +module.exports = PacketHandler; diff --git a/node_modules/discord.js/src/client/voice/receiver/Receiver.js b/node_modules/discord.js/src/client/voice/receiver/Receiver.js new file mode 100644 index 0000000..605d992 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/receiver/Receiver.js @@ -0,0 +1,58 @@ +'use strict'; + +const EventEmitter = require('events'); +const prism = require('prism-media'); +const PacketHandler = require('./PacketHandler'); +const { Error } = require('../../../errors'); + +/** + * Receives audio packets from a voice connection. + * @example + * const receiver = connection.createReceiver(); + * // opusStream is a ReadableStream - that means you could play it back to a voice channel if you wanted to! + * const opusStream = receiver.createStream(user); + */ +class VoiceReceiver extends EventEmitter { + constructor(connection) { + super(); + this.connection = connection; + this.packets = new PacketHandler(this); + /** + * Emitted whenever there is a warning + * @event VoiceReceiver#debug + * @param {Error|string} error The error or message to debug + */ + this.packets.on('error', err => this.emit('debug', err)); + } + + /** + * Options passed to `VoiceReceiver#createStream`. + * @typedef {Object} ReceiveStreamOptions + * @property {string} [mode='opus'] The mode for audio output. This defaults to opus, meaning discord.js won't decode + * the packets for you. You can set this to 'pcm' so that the stream's output will be 16-bit little-endian stereo + * audio + * @property {string} [end='silence'] When the stream should be destroyed. If `silence`, this will be when the user + * stops talking. Otherwise, if `manual`, this should be handled by you. + */ + + /** + * Creates a new audio receiving stream. If a stream already exists for a user, then that stream will be returned + * rather than generating a new one. + * @param {UserResolvable} user The user to start listening to. + * @param {ReceiveStreamOptions} options Options. + * @returns {ReadableStream} + */ + createStream(user, { mode = 'opus', end = 'silence' } = {}) { + user = this.connection.client.users.resolve(user); + if (!user) throw new Error('VOICE_USER_MISSING'); + const stream = this.packets.makeStream(user.id, end); + if (mode === 'pcm') { + const decoder = new prism.opus.Decoder({ channels: 2, rate: 48000, frameSize: 960 }); + stream.pipe(decoder); + return decoder; + } + return stream; + } +} + +module.exports = VoiceReceiver; diff --git a/node_modules/discord.js/src/client/voice/util/PlayInterface.js b/node_modules/discord.js/src/client/voice/util/PlayInterface.js new file mode 100644 index 0000000..9478ee8 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/util/PlayInterface.js @@ -0,0 +1,94 @@ +'use strict'; + +const { Readable } = require('stream'); +const prism = require('prism-media'); +const { Error } = require('../../../errors'); + +/** + * Options that can be passed to stream-playing methods: + * @typedef {Object} StreamOptions + * @property {StreamType} [type='unknown'] The type of stream. + * @property {number} [seek=0] The time to seek to, will be ignored when playing `ogg/opus` or `webm/opus` streams + * @property {number|boolean} [volume=1] The volume to play at. Set this to false to disable volume transforms for + * this stream to improve performance. + * @property {number} [plp] Expected packet loss percentage + * @property {boolean} [fec] Enabled forward error correction + * @property {number|string} [bitrate=96] The bitrate (quality) of the audio in kbps. + * If set to 'auto', the voice channel's bitrate will be used + * @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are + * actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to + * 1 means that changes in volume will be more instant. + */ + +/** + * An option passed as part of `StreamOptions` specifying the type of the stream. + * * `unknown`: The default type, streams/input will be passed through to ffmpeg before encoding. + * Will play most streams. + * * `converted`: Play a stream of 16bit signed stereo PCM data, skipping ffmpeg. + * * `opus`: Play a stream of opus packets, skipping ffmpeg. You lose the ability to alter volume. + * * `ogg/opus`: Play an ogg file with the opus encoding, skipping ffmpeg. You lose the ability to alter volume. + * * `webm/opus`: Play a webm file with opus audio, skipping ffmpeg. You lose the ability to alter volume. + * @typedef {string} StreamType + */ + +/** + * An interface class to allow you to play audio over VoiceConnections and VoiceBroadcasts. + */ +class PlayInterface { + constructor(player) { + this.player = player; + } + + /** + * Play an audio resource. + * @param {VoiceBroadcast|ReadableStream|string} resource The resource to play. + * @param {StreamOptions} [options] The options to play. + * @example + * // Play a local audio file + * connection.play('/home/hydrabolt/audio.mp3', { volume: 0.5 }); + * @example + * // Play a ReadableStream + * connection.play(ytdl('https://www.youtube.com/watch?v=ZlAU_w7-Xp8', { quality: 'highestaudio' })); + * @example + * // Play a voice broadcast + * const broadcast = client.voice.createBroadcast(); + * broadcast.play('/home/hydrabolt/audio.mp3'); + * connection.play(broadcast); + * @example + * // Using different protocols: https://ffmpeg.org/ffmpeg-protocols.html + * connection.play('http://www.sample-videos.com/audio/mp3/wave.mp3'); + * @returns {StreamDispatcher} + */ + play(resource, options = {}) { + const VoiceBroadcast = require('../VoiceBroadcast'); + if (resource instanceof VoiceBroadcast) { + if (!this.player.playBroadcast) throw new Error('VOICE_PLAY_INTERFACE_NO_BROADCAST'); + return this.player.playBroadcast(resource, options); + } + if (resource instanceof Readable || typeof resource === 'string') { + const type = options.type || 'unknown'; + if (type === 'unknown') { + return this.player.playUnknown(resource, options); + } else if (type === 'converted') { + return this.player.playPCMStream(resource, options); + } else if (type === 'opus') { + return this.player.playOpusStream(resource, options); + } else if (type === 'ogg/opus') { + if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM'); + return this.player.playOpusStream(resource.pipe(new prism.opus.OggDemuxer()), options); + } else if (type === 'webm/opus') { + if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM'); + return this.player.playOpusStream(resource.pipe(new prism.opus.WebmDemuxer()), options); + } + } + throw new Error('VOICE_PLAY_INTERFACE_BAD_TYPE'); + } + + static applyToClass(structure) { + for (const prop of ['play']) { + Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop)); + } + } +} + +module.exports = PlayInterface; 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..c16a435 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/util/Secretbox.js @@ -0,0 +1,32 @@ +'use strict'; + +const libs = { + sodium: sodium => ({ + open: sodium.api.crypto_secretbox_open_easy, + close: sodium.api.crypto_secretbox_easy, + random: n => sodium.randombytes_buf(n), + }), + 'libsodium-wrappers': sodium => ({ + open: sodium.crypto_secretbox_open_easy, + close: sodium.crypto_secretbox_easy, + random: n => sodium.randombytes_buf(n), + }), + tweetnacl: tweetnacl => ({ + open: tweetnacl.secretbox.open, + close: tweetnacl.secretbox, + random: n => tweetnacl.randomBytes(n), + }), +}; + +exports.methods = {}; + +(async () => { + for (const libName of Object.keys(libs)) { + try { + const lib = require(libName); + if (libName === 'libsodium-wrappers' && lib.ready) await lib.ready; // eslint-disable-line no-await-in-loop + exports.methods = libs[libName](lib); + break; + } catch {} // 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..9bea3d0 --- /dev/null +++ b/node_modules/discord.js/src/client/voice/util/Silence.js @@ -0,0 +1,13 @@ +'use strict'; + +const { Readable } = require('stream'); + +const SILENCE_FRAME = Buffer.from([0xf8, 0xff, 0xfe]); + +class Silence extends Readable { + _read() { + this.push(SILENCE_FRAME); + } +} + +module.exports = Silence; 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..0dca04f --- /dev/null +++ b/node_modules/discord.js/src/client/voice/util/VolumeInterface.js @@ -0,0 +1,103 @@ +'use strict'; + +const EventEmitter = require('events'); + +/** + * An interface class for volume transformation. + * @extends {EventEmitter} + */ +class VolumeInterface extends EventEmitter { + constructor({ volume = 1 } = {}) { + super(); + this.setVolume(volume); + } + + /** + * Whether or not the volume of this stream is editable + * @type {boolean} + * @readonly + */ + get volumeEditable() { + return true; + } + + /** + * The current volume of the stream + * @type {number} + * @readonly + */ + get volume() { + return this._volume; + } + + /** + * The current volume of the stream in decibels + * @type {number} + * @readonly + */ + get volumeDecibels() { + return Math.log10(this.volume) * 20; + } + + /** + * The current volume of the stream from a logarithmic scale + * @type {number} + * @readonly + */ + 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; + } + + /** + * Sets the volume in decibels. + * @param {number} db The decibels + */ + setVolumeDecibels(db) { + this.setVolume(Math.pow(10, db / 20)); + } + + /** + * Sets 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)); + } +} + +const props = ['volumeDecibels', 'volumeLogarithmic', 'setVolumeDecibels', 'setVolumeLogarithmic']; + +exports.applyToClass = function applyToClass(structure) { + for (const prop of props) { + Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(VolumeInterface.prototype, prop)); + } +}; 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..f892985 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/WebSocketManager.js @@ -0,0 +1,437 @@ +'use strict'; + +const EventEmitter = require('events'); +const WebSocketShard = require('./WebSocketShard'); +const PacketHandlers = require('./handlers'); +const { Error: DJSError } = require('../../errors'); +const Collection = require('../../util/Collection'); +const { Events, ShardEvents, Status, WSCodes, WSEvents } = require('../../util/Constants'); +const Util = require('../../util/Util'); + +const BeforeReadyWhitelist = [ + WSEvents.READY, + WSEvents.RESUMED, + WSEvents.GUILD_CREATE, + WSEvents.GUILD_DELETE, + WSEvents.GUILD_MEMBERS_CHUNK, + WSEvents.GUILD_MEMBER_ADD, + WSEvents.GUILD_MEMBER_REMOVE, +]; + +const UNRECOVERABLE_CLOSE_CODES = Object.keys(WSCodes).slice(1).map(Number); +const UNRESUMABLE_CLOSE_CODES = [1000, 4006, 4007]; + +/** + * The WebSocket manager for this client. + * This class forwards raw dispatch events, + * read more about it here {@link https://discord.com/developers/docs/topics/gateway} + * @extends EventEmitter + */ +class WebSocketManager extends EventEmitter { + constructor(client) { + super(); + + /** + * The client that instantiated this WebSocketManager + * @type {Client} + * @readonly + * @name WebSocketManager#client + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The gateway this manager uses + * @type {?string} + */ + this.gateway = null; + + /** + * The amount of shards this manager handles + * @private + * @type {number} + */ + this.totalShards = this.client.options.shards.length; + + /** + * A collection of all shards this manager handles + * @type {Collection} + */ + this.shards = new Collection(); + + /** + * An array of shards to be connected or that need to reconnect + * @type {Set} + * @private + * @name WebSocketManager#shardQueue + */ + Object.defineProperty(this, 'shardQueue', { value: new Set(), writable: true }); + + /** + * An array of queued events before this WebSocketManager became ready + * @type {object[]} + * @private + * @name WebSocketManager#packetQueue + */ + Object.defineProperty(this, 'packetQueue', { value: [] }); + + /** + * The current status of this WebSocketManager + * @type {number} + */ + this.status = Status.IDLE; + + /** + * If this manager was destroyed. It will prevent shards from reconnecting + * @type {boolean} + * @private + */ + this.destroyed = false; + + /** + * If this manager is currently reconnecting one or multiple shards + * @type {boolean} + * @private + */ + this.reconnecting = false; + + /** + * The current session limit of the client + * @private + * @type {?Object} + * @property {number} total Total number of identifies available + * @property {number} remaining Number of identifies remaining + * @property {number} reset_after Number of milliseconds after which the limit resets + */ + this.sessionStartLimit = null; + } + + /** + * The average ping of all WebSocketShards + * @type {number} + * @readonly + */ + get ping() { + const sum = this.shards.reduce((a, b) => a + b.ping, 0); + return sum / this.shards.size; + } + + /** + * Emits a debug message. + * @param {string} message The debug message + * @param {?WebSocketShard} [shard] The shard that emitted this message, if any + * @private + */ + debug(message, shard) { + this.client.emit(Events.DEBUG, `[WS => ${shard ? `Shard ${shard.id}` : 'Manager'}] ${message}`); + } + + /** + * Connects this manager to the gateway. + * @private + */ + async connect() { + const invalidToken = new DJSError(WSCodes[4004]); + const { + url: gatewayURL, + shards: recommendedShards, + session_start_limit: sessionStartLimit, + } = await this.client.api.gateway.bot.get().catch(error => { + throw error.httpStatus === 401 ? invalidToken : error; + }); + + this.sessionStartLimit = sessionStartLimit; + + const { total, remaining, reset_after } = sessionStartLimit; + + this.debug(`Fetched Gateway Information + URL: ${gatewayURL} + Recommended Shards: ${recommendedShards}`); + + this.debug(`Session Limit Information + Total: ${total} + Remaining: ${remaining}`); + + this.gateway = `${gatewayURL}/`; + + let { shards } = this.client.options; + + if (shards === 'auto') { + this.debug(`Using the recommended shard count provided by Discord: ${recommendedShards}`); + this.totalShards = this.client.options.shardCount = recommendedShards; + shards = this.client.options.shards = Array.from({ length: recommendedShards }, (_, i) => i); + } + + this.totalShards = shards.length; + this.debug(`Spawning shards: ${shards.join(', ')}`); + this.shardQueue = new Set(shards.map(id => new WebSocketShard(this, id))); + + await this._handleSessionLimit(remaining, reset_after); + + return this.createShards(); + } + + /** + * Handles the creation of a shard. + * @returns {Promise} + * @private + */ + async createShards() { + // If we don't have any shards to handle, return + if (!this.shardQueue.size) return false; + + const [shard] = this.shardQueue; + + this.shardQueue.delete(shard); + + if (!shard.eventsAttached) { + shard.on(ShardEvents.ALL_READY, unavailableGuilds => { + /** + * Emitted when a shard turns ready. + * @event Client#shardReady + * @param {number} id The shard ID that turned ready + * @param {?Set} unavailableGuilds Set of unavailable guild IDs, if any + */ + this.client.emit(Events.SHARD_READY, shard.id, unavailableGuilds); + + if (!this.shardQueue.size) this.reconnecting = false; + this.checkShardsReady(); + }); + + shard.on(ShardEvents.CLOSE, event => { + if (event.code === 1000 ? this.destroyed : UNRECOVERABLE_CLOSE_CODES.includes(event.code)) { + /** + * Emitted when a shard's WebSocket disconnects and will no longer reconnect. + * @event Client#shardDisconnect + * @param {CloseEvent} event The WebSocket close event + * @param {number} id The shard ID that disconnected + */ + this.client.emit(Events.SHARD_DISCONNECT, event, shard.id); + this.debug(WSCodes[event.code], shard); + return; + } + + if (UNRESUMABLE_CLOSE_CODES.includes(event.code)) { + // These event codes cannot be resumed + shard.sessionID = null; + } + + /** + * Emitted when a shard is attempting to reconnect or re-identify. + * @event Client#shardReconnecting + * @param {number} id The shard ID that is attempting to reconnect + */ + this.client.emit(Events.SHARD_RECONNECTING, shard.id); + + this.shardQueue.add(shard); + + if (shard.sessionID) { + this.debug(`Session ID is present, attempting an immediate reconnect...`, shard); + this.reconnect(true); + } else { + shard.destroy({ reset: true, emit: false, log: false }); + this.reconnect(); + } + }); + + shard.on(ShardEvents.INVALID_SESSION, () => { + this.client.emit(Events.SHARD_RECONNECTING, shard.id); + }); + + shard.on(ShardEvents.DESTROYED, () => { + this.debug('Shard was destroyed but no WebSocket connection was present! Reconnecting...', shard); + + this.client.emit(Events.SHARD_RECONNECTING, shard.id); + + this.shardQueue.add(shard); + this.reconnect(); + }); + + shard.eventsAttached = true; + } + + this.shards.set(shard.id, shard); + + try { + await shard.connect(); + } catch (error) { + if (error && error.code && UNRECOVERABLE_CLOSE_CODES.includes(error.code)) { + throw new DJSError(WSCodes[error.code]); + // Undefined if session is invalid, error event for regular closes + } else if (!error || error.code) { + this.debug('Failed to connect to the gateway, requeueing...', shard); + this.shardQueue.add(shard); + } else { + throw error; + } + } + // If we have more shards, add a 5s delay + if (this.shardQueue.size) { + this.debug(`Shard Queue Size: ${this.shardQueue.size}; continuing in 5 seconds...`); + await Util.delayFor(5000); + await this._handleSessionLimit(); + return this.createShards(); + } + + return true; + } + + /** + * Handles reconnects for this manager. + * @param {boolean} [skipLimit=false] IF this reconnect should skip checking the session limit + * @private + * @returns {Promise} + */ + async reconnect(skipLimit = false) { + if (this.reconnecting || this.status !== Status.READY) return false; + this.reconnecting = true; + try { + if (!skipLimit) await this._handleSessionLimit(); + await this.createShards(); + } catch (error) { + this.debug(`Couldn't reconnect or fetch information about the gateway. ${error}`); + if (error.httpStatus !== 401) { + this.debug(`Possible network error occurred. Retrying in 5s...`); + await Util.delayFor(5000); + this.reconnecting = false; + return this.reconnect(); + } + // If we get an error at this point, it means we cannot reconnect anymore + if (this.client.listenerCount(Events.INVALIDATED)) { + /** + * Emitted when the client's session becomes invalidated. + * You are expected to handle closing the process gracefully and preventing a boot loop + * if you are listening to this event. + * @event Client#invalidated + */ + this.client.emit(Events.INVALIDATED); + // Destroy just the shards. This means you have to handle the cleanup yourself + this.destroy(); + } else { + this.client.destroy(); + } + } finally { + this.reconnecting = false; + } + return true; + } + + /** + * Broadcasts a packet to every shard this manager handles. + * @param {Object} packet The packet to send + * @private + */ + broadcast(packet) { + for (const shard of this.shards.values()) shard.send(packet); + } + + /** + * Destroys this manager and all its shards. + * @private + */ + destroy() { + if (this.destroyed) return; + this.debug(`Manager was destroyed. Called by:\n${new Error('MANAGER_DESTROYED').stack}`); + this.destroyed = true; + this.shardQueue.clear(); + for (const shard of this.shards.values()) shard.destroy({ closeCode: 1000, reset: true, emit: false, log: false }); + } + + /** + * Handles the timeout required if we cannot identify anymore. + * @param {number} [remaining] The amount of remaining identify sessions that can be done today + * @param {number} [resetAfter] The amount of time in which the identify counter resets + * @private + */ + async _handleSessionLimit(remaining, resetAfter) { + if (typeof remaining === 'undefined' && typeof resetAfter === 'undefined') { + const { session_start_limit } = await this.client.api.gateway.bot.get(); + this.sessionStartLimit = session_start_limit; + remaining = session_start_limit.remaining; + resetAfter = session_start_limit.reset_after; + this.debug(`Session Limit Information + Total: ${session_start_limit.total} + Remaining: ${remaining}`); + } + if (!remaining) { + this.debug(`Exceeded identify threshold. Will attempt a connection in ${resetAfter}ms`); + await Util.delayFor(resetAfter); + } + } + + /** + * Processes a packet and queues it if this WebSocketManager is not ready. + * @param {Object} [packet] The packet to be handled + * @param {WebSocketShard} [shard] The shard that will handle this packet + * @returns {boolean} + * @private + */ + handlePacket(packet, shard) { + if (packet && this.status !== Status.READY) { + if (!BeforeReadyWhitelist.includes(packet.t)) { + this.packetQueue.push({ packet, shard }); + return false; + } + } + + if (this.packetQueue.length) { + const item = this.packetQueue.shift(); + this.client.setImmediate(() => { + this.handlePacket(item.packet, item.shard); + }); + } + + if (packet && PacketHandlers[packet.t]) { + PacketHandlers[packet.t](this.client, packet, shard); + } + + return true; + } + + /** + * Checks whether the client is ready to be marked as ready. + * @private + */ + async checkShardsReady() { + if (this.status === Status.READY) return; + if (this.shards.size !== this.totalShards || this.shards.some(s => s.status !== Status.READY)) { + return; + } + + this.status = Status.NEARLY; + + if (this.client.options.fetchAllMembers) { + try { + const promises = this.client.guilds.cache.map(guild => { + if (guild.available) return guild.members.fetch(); + // Return empty promise if guild is unavailable + return Promise.resolve(); + }); + await Promise.all(promises); + } catch (err) { + this.debug(`Failed to fetch all members before ready! ${err}\n${err.stack}`); + } + } + + this.triggerClientReady(); + } + + /** + * Causes the client to be marked as ready and emits the ready event. + * @private + */ + triggerClientReady() { + this.status = Status.READY; + + this.client.readyAt = new Date(); + + /** + * Emitted when the client becomes ready to start working. + * @event Client#ready + */ + this.client.emit(Events.CLIENT_READY); + + this.handlePacket(); + } +} + +module.exports = WebSocketManager; diff --git a/node_modules/discord.js/src/client/websocket/WebSocketShard.js b/node_modules/discord.js/src/client/websocket/WebSocketShard.js new file mode 100644 index 0000000..f99c83f --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/WebSocketShard.js @@ -0,0 +1,771 @@ +'use strict'; + +const EventEmitter = require('events'); +const WebSocket = require('../../WebSocket'); +const { browser, Status, Events, ShardEvents, OPCodes, WSEvents } = require('../../util/Constants'); + +const STATUS_KEYS = Object.keys(Status); +const CONNECTION_STATE = Object.keys(WebSocket.WebSocket); + +let zlib; + +if (!browser) { + try { + zlib = require('zlib-sync'); + } catch {} // eslint-disable-line no-empty +} + +/** + * Represents a Shard's WebSocket connection + */ +class WebSocketShard extends EventEmitter { + constructor(manager, id) { + super(); + + /** + * The WebSocketManager of the shard + * @type {WebSocketManager} + */ + this.manager = manager; + + /** + * The ID of the shard + * @type {number} + */ + this.id = id; + + /** + * The current status of the shard + * @type {Status} + */ + this.status = Status.IDLE; + + /** + * The current sequence of the shard + * @type {number} + * @private + */ + this.sequence = -1; + + /** + * The sequence of the shard after close + * @type {number} + * @private + */ + this.closeSequence = 0; + + /** + * The current session ID of the shard + * @type {?string} + * @private + */ + this.sessionID = null; + + /** + * The previous heartbeat ping of the shard + * @type {number} + */ + this.ping = -1; + + /** + * The last time a ping was sent (a timestamp) + * @type {number} + * @private + */ + this.lastPingTimestamp = -1; + + /** + * If we received a heartbeat ack back. Used to identify zombie connections + * @type {boolean} + * @private + */ + this.lastHeartbeatAcked = true; + + /** + * Contains the rate limit queue and metadata + * @name WebSocketShard#ratelimit + * @type {Object} + * @private + */ + Object.defineProperty(this, 'ratelimit', { + value: { + queue: [], + total: 120, + remaining: 120, + time: 60e3, + timer: null, + }, + }); + + /** + * The WebSocket connection for the current shard + * @name WebSocketShard#connection + * @type {?WebSocket} + * @private + */ + Object.defineProperty(this, 'connection', { value: null, writable: true }); + + /** + * @external Inflate + * @see {@link https://www.npmjs.com/package/zlib-sync} + */ + + /** + * The compression to use + * @name WebSocketShard#inflate + * @type {?Inflate} + * @private + */ + Object.defineProperty(this, 'inflate', { value: null, writable: true }); + + /** + * The HELLO timeout + * @name WebSocketShard#helloTimeout + * @type {?NodeJS.Timeout} + * @private + */ + Object.defineProperty(this, 'helloTimeout', { value: null, writable: true }); + + /** + * If the manager attached its event handlers on the shard + * @name WebSocketShard#eventsAttached + * @type {boolean} + * @private + */ + Object.defineProperty(this, 'eventsAttached', { value: false, writable: true }); + + /** + * A set of guild IDs this shard expects to receive + * @name WebSocketShard#expectedGuilds + * @type {?Set} + * @private + */ + Object.defineProperty(this, 'expectedGuilds', { value: null, writable: true }); + + /** + * The ready timeout + * @name WebSocketShard#readyTimeout + * @type {?NodeJS.Timeout} + * @private + */ + Object.defineProperty(this, 'readyTimeout', { value: null, writable: true }); + + /** + * Time when the WebSocket connection was opened + * @name WebSocketShard#connectedAt + * @type {number} + * @private + */ + Object.defineProperty(this, 'connectedAt', { value: 0, writable: true }); + } + + /** + * Emits a debug event. + * @param {string} message The debug message + * @private + */ + debug(message) { + this.manager.debug(message, this); + } + + /** + * Connects the shard to the gateway. + * @private + * @returns {Promise} A promise that will resolve if the shard turns ready successfully, + * or reject if we couldn't connect + */ + connect() { + const { gateway, client } = this.manager; + + if (this.connection && this.connection.readyState === WebSocket.OPEN && this.status === Status.READY) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + const cleanup = () => { + this.removeListener(ShardEvents.CLOSE, onClose); + this.removeListener(ShardEvents.READY, onReady); + this.removeListener(ShardEvents.RESUMED, onResumed); + this.removeListener(ShardEvents.INVALID_SESSION, onInvalidOrDestroyed); + this.removeListener(ShardEvents.DESTROYED, onInvalidOrDestroyed); + }; + + const onReady = () => { + cleanup(); + resolve(); + }; + + const onResumed = () => { + cleanup(); + resolve(); + }; + + const onClose = event => { + cleanup(); + reject(event); + }; + + const onInvalidOrDestroyed = () => { + cleanup(); + // eslint-disable-next-line prefer-promise-reject-errors + reject(); + }; + + this.once(ShardEvents.READY, onReady); + this.once(ShardEvents.RESUMED, onResumed); + this.once(ShardEvents.CLOSE, onClose); + this.once(ShardEvents.INVALID_SESSION, onInvalidOrDestroyed); + this.once(ShardEvents.DESTROYED, onInvalidOrDestroyed); + + if (this.connection && this.connection.readyState === WebSocket.OPEN) { + this.debug('An open connection was found, attempting an immediate identify.'); + this.identify(); + return; + } + + if (this.connection) { + this.debug(`A connection object was found. Cleaning up before continuing. + State: ${CONNECTION_STATE[this.connection.readyState]}`); + this.destroy({ emit: false }); + } + + const wsQuery = { v: client.options.ws.version }; + + if (zlib) { + this.inflate = new zlib.Inflate({ + chunkSize: 65535, + flush: zlib.Z_SYNC_FLUSH, + to: WebSocket.encoding === 'json' ? 'string' : '', + }); + wsQuery.compress = 'zlib-stream'; + } + + this.debug( + `[CONNECT] + Gateway : ${gateway} + Version : ${client.options.ws.version} + Encoding : ${WebSocket.encoding} + Compression: ${zlib ? 'zlib-stream' : 'none'}`, + ); + + this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING; + this.setHelloTimeout(); + + this.connectedAt = Date.now(); + + const ws = (this.connection = WebSocket.create(gateway, wsQuery)); + ws.onopen = this.onOpen.bind(this); + ws.onmessage = this.onMessage.bind(this); + ws.onerror = this.onError.bind(this); + ws.onclose = this.onClose.bind(this); + }); + } + + /** + * Called whenever a connection is opened to the gateway. + * @private + */ + onOpen() { + this.debug(`[CONNECTED] ${this.connection.url} in ${Date.now() - this.connectedAt}ms`); + this.status = Status.NEARLY; + } + + /** + * Called whenever a message is received. + * @param {MessageEvent} event Event received + * @private + */ + onMessage({ data }) { + let raw; + if (data instanceof ArrayBuffer) data = new Uint8Array(data); + if (zlib) { + const l = data.length; + const flush = + l >= 4 && data[l - 4] === 0x00 && data[l - 3] === 0x00 && data[l - 2] === 0xff && data[l - 1] === 0xff; + + this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH); + if (!flush) return; + raw = this.inflate.result; + } else { + raw = data; + } + let packet; + try { + packet = WebSocket.unpack(raw); + this.manager.client.emit(Events.RAW, packet, this.id); + if (packet.op === OPCodes.DISPATCH) this.manager.emit(packet.t, packet.d, this.id); + } catch (err) { + this.manager.client.emit(Events.SHARD_ERROR, err, this.id); + return; + } + this.onPacket(packet); + } + + /** + * Called whenever an error occurs with the WebSocket. + * @param {ErrorEvent} event The error that occurred + * @private + */ + onError(event) { + const error = event && event.error ? event.error : event; + if (!error) return; + + /** + * Emitted whenever a shard's WebSocket encounters a connection error. + * @event Client#shardError + * @param {Error} error The encountered error + * @param {number} shardID The shard that encountered this error + */ + this.manager.client.emit(Events.SHARD_ERROR, error, this.id); + } + + /** + * @external CloseEvent + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent} + */ + + /** + * @external ErrorEvent + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent} + */ + + /** + * @external MessageEvent + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent} + */ + + /** + * Called whenever a connection to the gateway is closed. + * @param {CloseEvent} event Close event that was received + * @private + */ + onClose(event) { + if (this.sequence !== -1) this.closeSequence = this.sequence; + this.sequence = -1; + + this.debug(`[CLOSE] + Event Code: ${event.code} + Clean : ${event.wasClean} + Reason : ${event.reason || 'No reason received'}`); + + this.setHeartbeatTimer(-1); + this.setHelloTimeout(-1); + // If we still have a connection object, clean up its listeners + if (this.connection) this._cleanupConnection(); + + this.status = Status.DISCONNECTED; + + /** + * Emitted when a shard's WebSocket closes. + * @private + * @event WebSocketShard#close + * @param {CloseEvent} event The received event + */ + this.emit(ShardEvents.CLOSE, event); + } + + /** + * Called whenever a packet is received. + * @param {Object} packet The received packet + * @private + */ + onPacket(packet) { + if (!packet) { + this.debug(`Received broken packet: '${packet}'.`); + return; + } + + switch (packet.t) { + case WSEvents.READY: + /** + * Emitted when the shard receives the READY payload and is now waiting for guilds + * @event WebSocketShard#ready + */ + this.emit(ShardEvents.READY); + + this.sessionID = packet.d.session_id; + this.expectedGuilds = new Set(packet.d.guilds.map(d => d.id)); + this.status = Status.WAITING_FOR_GUILDS; + this.debug(`[READY] Session ${this.sessionID}.`); + this.lastHeartbeatAcked = true; + this.sendHeartbeat('ReadyHeartbeat'); + break; + case WSEvents.RESUMED: { + /** + * Emitted when the shard resumes successfully + * @event WebSocketShard#resumed + */ + this.emit(ShardEvents.RESUMED); + + this.status = Status.READY; + const replayed = packet.s - this.closeSequence; + this.debug(`[RESUMED] Session ${this.sessionID} | Replayed ${replayed} events.`); + this.lastHeartbeatAcked = true; + this.sendHeartbeat('ResumeHeartbeat'); + break; + } + } + + if (packet.s > this.sequence) this.sequence = packet.s; + + switch (packet.op) { + case OPCodes.HELLO: + this.setHelloTimeout(-1); + this.setHeartbeatTimer(packet.d.heartbeat_interval); + this.identify(); + break; + case OPCodes.RECONNECT: + this.debug('[RECONNECT] Discord asked us to reconnect'); + this.destroy({ closeCode: 4000 }); + break; + case OPCodes.INVALID_SESSION: + this.debug(`[INVALID SESSION] Resumable: ${packet.d}.`); + // If we can resume the session, do so immediately + if (packet.d) { + this.identifyResume(); + return; + } + // Reset the sequence + this.sequence = -1; + // Reset the session ID as it's invalid + this.sessionID = null; + // Set the status to reconnecting + this.status = Status.RECONNECTING; + // Finally, emit the INVALID_SESSION event + this.emit(ShardEvents.INVALID_SESSION); + break; + case OPCodes.HEARTBEAT_ACK: + this.ackHeartbeat(); + break; + case OPCodes.HEARTBEAT: + this.sendHeartbeat('HeartbeatRequest', true); + break; + default: + this.manager.handlePacket(packet, this); + if (this.status === Status.WAITING_FOR_GUILDS && packet.t === WSEvents.GUILD_CREATE) { + this.expectedGuilds.delete(packet.d.id); + this.checkReady(); + } + } + } + + /** + * Checks if the shard can be marked as ready + * @private + */ + checkReady() { + // Step 0. Clear the ready timeout, if it exists + if (this.readyTimeout) { + this.manager.client.clearTimeout(this.readyTimeout); + this.readyTimeout = null; + } + // Step 1. If we don't have any other guilds pending, we are ready + if (!this.expectedGuilds.size) { + this.debug('Shard received all its guilds. Marking as fully ready.'); + this.status = Status.READY; + + /** + * Emitted when the shard is fully ready. + * This event is emitted if: + * * all guilds were received by this shard + * * the ready timeout expired, and some guilds are unavailable + * @event WebSocketShard#allReady + * @param {?Set} unavailableGuilds Set of unavailable guilds, if any + */ + this.emit(ShardEvents.ALL_READY); + return; + } + // Step 2. Create a 15s timeout that will mark the shard as ready if there are still unavailable guilds + this.readyTimeout = this.manager.client.setTimeout(() => { + this.debug(`Shard did not receive any more guild packets in 15 seconds. + Unavailable guild count: ${this.expectedGuilds.size}`); + + this.readyTimeout = null; + + this.status = Status.READY; + + this.emit(ShardEvents.ALL_READY, this.expectedGuilds); + }, 15000); + } + + /** + * Sets the HELLO packet timeout. + * @param {number} [time] If set to -1, it will clear the hello timeout timeout + * @private + */ + setHelloTimeout(time) { + if (time === -1) { + if (this.helloTimeout) { + this.debug('Clearing the HELLO timeout.'); + this.manager.client.clearTimeout(this.helloTimeout); + this.helloTimeout = null; + } + return; + } + this.debug('Setting a HELLO timeout for 20s.'); + this.helloTimeout = this.manager.client.setTimeout(() => { + this.debug('Did not receive HELLO in time. Destroying and connecting again.'); + this.destroy({ reset: true, closeCode: 4009 }); + }, 20000); + } + + /** + * Sets the heartbeat timer for this shard. + * @param {number} time If -1, clears the interval, any other number sets an interval + * @private + */ + setHeartbeatTimer(time) { + if (time === -1) { + if (this.heartbeatInterval) { + this.debug('Clearing the heartbeat interval.'); + this.manager.client.clearInterval(this.heartbeatInterval); + this.heartbeatInterval = null; + } + return; + } + this.debug(`Setting a heartbeat interval for ${time}ms.`); + // Sanity checks + if (this.heartbeatInterval) this.manager.client.clearInterval(this.heartbeatInterval); + this.heartbeatInterval = this.manager.client.setInterval(() => this.sendHeartbeat(), time); + } + + /** + * Sends a heartbeat to the WebSocket. + * If this shard didn't receive a heartbeat last time, it will destroy it and reconnect + * @param {string} [tag='HeartbeatTimer'] What caused this heartbeat to be sent + * @param {boolean} [ignoreHeartbeatAck] If we should send the heartbeat forcefully. + * @private + */ + sendHeartbeat( + tag = 'HeartbeatTimer', + ignoreHeartbeatAck = [Status.WAITING_FOR_GUILDS, Status.IDENTIFYING, Status.RESUMING].includes(this.status), + ) { + if (ignoreHeartbeatAck && !this.lastHeartbeatAcked) { + this.debug(`[${tag}] Didn't process heartbeat ack yet but we are still connected. Sending one now.`); + } else if (!this.lastHeartbeatAcked) { + this.debug( + `[${tag}] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting. + Status : ${STATUS_KEYS[this.status]} + Sequence : ${this.sequence} + Connection State: ${this.connection ? CONNECTION_STATE[this.connection.readyState] : 'No Connection??'}`, + ); + + this.destroy({ closeCode: 4009, reset: true }); + return; + } + + this.debug(`[${tag}] Sending a heartbeat.`); + this.lastHeartbeatAcked = false; + this.lastPingTimestamp = Date.now(); + this.send({ op: OPCodes.HEARTBEAT, d: this.sequence }, true); + } + + /** + * Acknowledges a heartbeat. + * @private + */ + ackHeartbeat() { + this.lastHeartbeatAcked = true; + const latency = Date.now() - this.lastPingTimestamp; + this.debug(`Heartbeat acknowledged, latency of ${latency}ms.`); + this.ping = latency; + } + + /** + * Identifies the client on the connection. + * @private + * @returns {void} + */ + identify() { + return this.sessionID ? this.identifyResume() : this.identifyNew(); + } + + /** + * Identifies as a new connection on the gateway. + * @private + */ + identifyNew() { + const { client } = this.manager; + if (!client.token) { + this.debug('[IDENTIFY] No token available to identify a new session.'); + return; + } + + this.status = Status.IDENTIFYING; + + // Clone the identify payload and assign the token and shard info + const d = { + ...client.options.ws, + token: client.token, + shard: [this.id, Number(client.options.shardCount)], + }; + + this.debug(`[IDENTIFY] Shard ${this.id}/${client.options.shardCount}`); + this.send({ op: OPCodes.IDENTIFY, d }, true); + } + + /** + * Resumes a session on the gateway. + * @private + */ + identifyResume() { + if (!this.sessionID) { + this.debug('[RESUME] No session ID was present; identifying as a new session.'); + this.identifyNew(); + return; + } + + this.status = Status.RESUMING; + + this.debug(`[RESUME] Session ${this.sessionID}, sequence ${this.closeSequence}`); + + const d = { + token: this.manager.client.token, + session_id: this.sessionID, + seq: this.closeSequence, + }; + + this.send({ op: OPCodes.RESUME, d }, true); + } + + /** + * Adds a packet to the queue to be sent to the gateway. + * If you use this method, make sure you understand that you need to provide + * a full [Payload](https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-commands). + * Do not use this method if you don't know what you're doing. + * @param {Object} data The full packet to send + * @param {boolean} [important=false] If this packet should be added first in queue + */ + send(data, important = false) { + this.ratelimit.queue[important ? 'unshift' : 'push'](data); + this.processQueue(); + } + + /** + * Sends data, bypassing the queue. + * @param {Object} data Packet to send + * @returns {void} + * @private + */ + _send(data) { + if (!this.connection || this.connection.readyState !== WebSocket.OPEN) { + this.debug(`Tried to send packet '${JSON.stringify(data)}' but no WebSocket is available!`); + this.destroy({ close: 4000 }); + return; + } + + this.connection.send(WebSocket.pack(data), err => { + if (err) this.manager.client.emit(Events.SHARD_ERROR, err, this.id); + }); + } + + /** + * Processes the current WebSocket queue. + * @returns {void} + * @private + */ + processQueue() { + if (this.ratelimit.remaining === 0) return; + if (this.ratelimit.queue.length === 0) return; + if (this.ratelimit.remaining === this.ratelimit.total) { + this.ratelimit.timer = this.manager.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--; + } + } + + /** + * Destroys this shard and closes its WebSocket connection. + * @param {Object} [options={ closeCode: 1000, reset: false, emit: true, log: true }] Options for destroying the shard + * @private + */ + destroy({ closeCode = 1000, reset = false, emit = true, log = true } = {}) { + if (log) { + this.debug(`[DESTROY] + Close Code : ${closeCode} + Reset : ${reset} + Emit DESTROYED: ${emit}`); + } + + // Step 0: Remove all timers + this.setHeartbeatTimer(-1); + this.setHelloTimeout(-1); + + // Step 1: Close the WebSocket connection, if any, otherwise, emit DESTROYED + if (this.connection) { + // If the connection is currently opened, we will (hopefully) receive close + if (this.connection.readyState === WebSocket.OPEN) { + this.connection.close(closeCode); + } else { + // Connection is not OPEN + this.debug(`WS State: ${CONNECTION_STATE[this.connection.readyState]}`); + // Remove listeners from the connection + this._cleanupConnection(); + // Attempt to close the connection just in case + try { + this.connection.close(closeCode); + } catch { + // No-op + } + // Emit the destroyed event if needed + if (emit) this._emitDestroyed(); + } + } else if (emit) { + // We requested a destroy, but we had no connection. Emit destroyed + this._emitDestroyed(); + } + + // Step 2: Null the connection object + this.connection = null; + + // Step 3: Set the shard status to DISCONNECTED + this.status = Status.DISCONNECTED; + + // Step 4: Cache the old sequence (use to attempt a resume) + if (this.sequence !== -1) this.closeSequence = this.sequence; + + // Step 5: Reset the sequence and session ID if requested + if (reset) { + this.sequence = -1; + this.sessionID = null; + } + + // Step 6: reset the ratelimit data + this.ratelimit.remaining = this.ratelimit.total; + this.ratelimit.queue.length = 0; + if (this.ratelimit.timer) { + this.manager.client.clearTimeout(this.ratelimit.timer); + this.ratelimit.timer = null; + } + } + + /** + * Cleans up the WebSocket connection listeners. + * @private + */ + _cleanupConnection() { + this.connection.onopen = this.connection.onclose = this.connection.onerror = this.connection.onmessage = null; + } + + /** + * Emits the DESTROYED event on the shard + * @private + */ + _emitDestroyed() { + /** + * Emitted when a shard is destroyed, but no WebSocket connection was present. + * @private + * @event WebSocketShard#destroyed + */ + this.emit(ShardEvents.DESTROYED); + } +} + +module.exports = WebSocketShard; diff --git a/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_CREATE.js b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_CREATE.js new file mode 100644 index 0000000..d6d560d --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_CREATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.ChannelCreate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_DELETE.js b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_DELETE.js new file mode 100644 index 0000000..cb9f3d8 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_DELETE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.ChannelDelete.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js new file mode 100644 index 0000000..13e6f0f --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js @@ -0,0 +1,22 @@ +'use strict'; + +const { Events } = require('../../../util/Constants'); + +module.exports = (client, { d: data }) => { + const channel = client.channels.cache.get(data.channel_id); + const time = new Date(data.last_pin_timestamp); + + if (channel && !Number.isNaN(time.getTime())) { + // Discord sends null for last_pin_timestamp if the last pinned message was removed + channel.lastPinTimestamp = time.getTime() || null; + + /** + * 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. + * @event Client#channelPinsUpdate + * @param {DMChannel|TextChannel} channel The channel that the pins update occurred in + * @param {Date} time The time of the pins update + */ + client.emit(Events.CHANNEL_PINS_UPDATE, channel, time); + } +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_UPDATE.js new file mode 100644 index 0000000..d441478 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/CHANNEL_UPDATE.js @@ -0,0 +1,16 @@ +'use strict'; + +const { Events } = require('../../../util/Constants'); + +module.exports = (client, packet) => { + const { old, updated } = client.actions.ChannelUpdate.handle(packet.d); + if (old && updated) { + /** + * Emitted whenever a channel is updated - e.g. name change, topic change, channel type change. + * @event Client#channelUpdate + * @param {DMChannel|GuildChannel} oldChannel The channel before the update + * @param {DMChannel|GuildChannel} newChannel The channel after the update + */ + client.emit(Events.CHANNEL_UPDATE, old, updated); + } +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_ADD.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_ADD.js new file mode 100644 index 0000000..5d4a096 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_ADD.js @@ -0,0 +1,16 @@ +'use strict'; + +const { Events } = require('../../../util/Constants'); + +module.exports = (client, { d: data }) => { + const guild = client.guilds.cache.get(data.guild_id); + const user = client.users.add(data.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 + */ + if (guild && user) client.emit(Events.GUILD_BAN_ADD, guild, user); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_REMOVE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_REMOVE.js new file mode 100644 index 0000000..8389e46 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_BAN_REMOVE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildBanRemove.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_CREATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_CREATE.js new file mode 100644 index 0000000..6743204 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_CREATE.js @@ -0,0 +1,36 @@ +'use strict'; + +const { Events, Status } = require('../../../util/Constants'); + +module.exports = async (client, { d: data }, shard) => { + let guild = client.guilds.cache.get(data.id); + if (guild) { + if (!guild.available && !data.unavailable) { + // A newly available guild + guild._patch(data); + // If the client was ready before and we had unavailable guilds, fetch them + if (client.ws.status === Status.READY && client.options.fetchAllMembers) { + await guild.members + .fetch() + .catch(err => client.emit(Events.DEBUG, `Failed to fetch all members: ${err}\n${err.stack}`)); + } + } + } else { + // A new guild + data.shardID = shard.id; + guild = client.guilds.add(data); + if (client.ws.status === Status.READY) { + /** + * Emitted whenever the client joins a guild. + * @event Client#guildCreate + * @param {Guild} guild The created guild + */ + if (client.options.fetchAllMembers) { + await guild.members + .fetch() + .catch(err => client.emit(Events.DEBUG, `Failed to fetch all members: ${err}\n${err.stack}`)); + } + client.emit(Events.GUILD_CREATE, guild); + } + } +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_DELETE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_DELETE.js new file mode 100644 index 0000000..27a3256 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_DELETE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildDelete.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js new file mode 100644 index 0000000..e23b671 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildEmojisUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js new file mode 100644 index 0000000..e90a72c --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildIntegrationsUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js new file mode 100644 index 0000000..2a04c68 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js @@ -0,0 +1,30 @@ +'use strict'; + +const Collection = require('../../../util/Collection'); +const { Events } = require('../../../util/Constants'); + +module.exports = (client, { d: data }) => { + const guild = client.guilds.cache.get(data.guild_id); + if (!guild) return; + const members = new Collection(); + + for (const member of data.members) members.set(member.user.id, guild.members.add(member)); + if (data.presences) { + for (const presence of data.presences) guild.presences.add(Object.assign(presence, { guild })); + } + /** + * Emitted whenever a chunk of guild members is received (all members come from the same guild). + * @event Client#guildMembersChunk + * @param {Collection} members The members in the chunk + * @param {Guild} guild The guild related to the member chunk + * @param {Object} chunk Properties of the received chunk + * @param {number} chunk.index Index of the received chunk + * @param {number} chunk.count Number of chunks the client should receive + * @param {?string} chunk.nonce Nonce for this chunk + */ + client.emit(Events.GUILD_MEMBERS_CHUNK, members, guild, { + count: data.chunk_count, + index: data.chunk_index, + nonce: data.nonce, + }); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_ADD.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_ADD.js new file mode 100644 index 0000000..5128756 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_ADD.js @@ -0,0 +1,19 @@ +'use strict'; + +const { Events, Status } = require('../../../util/Constants'); + +module.exports = (client, { d: data }, shard) => { + const guild = client.guilds.cache.get(data.guild_id); + if (guild) { + guild.memberCount++; + const member = guild.members.add(data); + if (shard.status === Status.READY) { + /** + * Emitted whenever a user joins a guild. + * @event Client#guildMemberAdd + * @param {GuildMember} member The member that has joined a guild + */ + client.emit(Events.GUILD_MEMBER_ADD, member); + } + } +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js new file mode 100644 index 0000000..72432af --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet, shard) => { + client.actions.GuildMemberRemove.handle(packet.d, shard); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js new file mode 100644 index 0000000..4c8d259 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js @@ -0,0 +1,29 @@ +'use strict'; + +const { Status, Events } = require('../../../util/Constants'); + +module.exports = (client, { d: data }, shard) => { + let user = client.users.cache.get(data.user.id); + if (!user && data.user.username) user = client.users.add(data.user); + if (user && data.user && data.user.username) { + if (!user.equals(data.user)) client.actions.UserUpdate.handle(data.user); + } + + const guild = client.guilds.cache.get(data.guild_id); + if (guild) { + const member = guild.members.cache.get(data.user.id); + if (member) { + const old = member._update(data); + if (shard.status === Status.READY) { + /** + * Emitted whenever a guild member changes - i.e. new role, removed role, nickname. + * Also emitted when the user's details (e.g. username) change. + * @event Client#guildMemberUpdate + * @param {GuildMember} oldMember The member before the update + * @param {GuildMember} newMember The member after the update + */ + client.emit(Events.GUILD_MEMBER_UPDATE, old, member); + } + } + } +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_CREATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_CREATE.js new file mode 100644 index 0000000..da9e7bc --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_CREATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildRoleCreate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_DELETE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_DELETE.js new file mode 100644 index 0000000..cdc6353 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_DELETE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildRoleDelete.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js new file mode 100644 index 0000000..3a9b62e --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildRoleUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/GUILD_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/GUILD_UPDATE.js new file mode 100644 index 0000000..fd0012a --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/GUILD_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/INVITE_CREATE.js b/node_modules/discord.js/src/client/websocket/handlers/INVITE_CREATE.js new file mode 100644 index 0000000..50a2e72 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/INVITE_CREATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.InviteCreate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/INVITE_DELETE.js b/node_modules/discord.js/src/client/websocket/handlers/INVITE_DELETE.js new file mode 100644 index 0000000..5971852 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/INVITE_DELETE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.InviteDelete.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_CREATE.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_CREATE.js new file mode 100644 index 0000000..c9b79a8 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_CREATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageCreate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE.js new file mode 100644 index 0000000..85ae2bc --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageDelete.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js new file mode 100644 index 0000000..fbcf80f --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageDeleteBulk.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js new file mode 100644 index 0000000..e219b4a --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageReactionAdd.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js new file mode 100644 index 0000000..2980e69 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageReactionRemove.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js new file mode 100644 index 0000000..ead80f7 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageReactionRemoveAll.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js new file mode 100644 index 0000000..579444c --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.MessageReactionRemoveEmoji.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_UPDATE.js new file mode 100644 index 0000000..7428e90 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/MESSAGE_UPDATE.js @@ -0,0 +1,16 @@ +'use strict'; + +const { Events } = require('../../../util/Constants'); + +module.exports = (client, packet) => { + const { old, updated } = client.actions.MessageUpdate.handle(packet.d); + if (old && updated) { + /** + * 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 + */ + client.emit(Events.MESSAGE_UPDATE, old, updated); + } +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/PRESENCE_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/PRESENCE_UPDATE.js new file mode 100644 index 0000000..bde3629 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/PRESENCE_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.PresenceUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/READY.js b/node_modules/discord.js/src/client/websocket/handlers/READY.js new file mode 100644 index 0000000..c38b681 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/READY.js @@ -0,0 +1,21 @@ +'use strict'; + +let ClientUser; + +module.exports = (client, { d: data }, shard) => { + if (client.user) { + client.user._patch(data.user); + } else { + if (!ClientUser) ClientUser = require('../../../structures/ClientUser'); + const clientUser = new ClientUser(client, data.user); + client.user = clientUser; + client.users.cache.set(clientUser.id, clientUser); + } + + for (const guild of data.guilds) { + guild.shardID = shard.id; + client.guilds.add(guild); + } + + shard.checkReady(); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/RESUMED.js b/node_modules/discord.js/src/client/websocket/handlers/RESUMED.js new file mode 100644 index 0000000..5e5f403 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/RESUMED.js @@ -0,0 +1,14 @@ +'use strict'; + +const { Events } = require('../../../util/Constants'); + +module.exports = (client, packet, shard) => { + const replayed = shard.sequence - shard.closeSequence; + /** + * Emitted when a shard resumes successfully. + * @event Client#shardResume + * @param {number} id The shard ID that resumed + * @param {number} replayedEvents The amount of replayed events + */ + client.emit(Events.SHARD_RESUME, shard.id, replayed); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/TYPING_START.js b/node_modules/discord.js/src/client/websocket/handlers/TYPING_START.js new file mode 100644 index 0000000..9a56a54 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/TYPING_START.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.TypingStart.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/USER_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/USER_UPDATE.js new file mode 100644 index 0000000..a02bf58 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/USER_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.UserUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js new file mode 100644 index 0000000..f9cf534 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = (client, packet) => { + client.emit('debug', `[VOICE] received voice server: ${JSON.stringify(packet)}`); + client.voice.onVoiceServer(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/VOICE_STATE_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/VOICE_STATE_UPDATE.js new file mode 100644 index 0000000..dbff6ea --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/VOICE_STATE_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.VoiceStateUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/WEBHOOKS_UPDATE.js b/node_modules/discord.js/src/client/websocket/handlers/WEBHOOKS_UPDATE.js new file mode 100644 index 0000000..46cacee --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/WEBHOOKS_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.WebhooksUpdate.handle(packet.d); +}; diff --git a/node_modules/discord.js/src/client/websocket/handlers/index.js b/node_modules/discord.js/src/client/websocket/handlers/index.js new file mode 100644 index 0000000..d69c105 --- /dev/null +++ b/node_modules/discord.js/src/client/websocket/handlers/index.js @@ -0,0 +1,13 @@ +'use strict'; + +const { WSEvents } = require('../../../util/Constants'); + +const handlers = {}; + +for (const name of Object.keys(WSEvents)) { + try { + handlers[name] = require(`./${name}.js`); + } catch {} // eslint-disable-line no-empty +} + +module.exports = handlers; diff --git a/node_modules/discord.js/src/errors/DJSError.js b/node_modules/discord.js/src/errors/DJSError.js new file mode 100644 index 0000000..157ca66 --- /dev/null +++ b/node_modules/discord.js/src/errors/DJSError.js @@ -0,0 +1,61 @@ +'use strict'; + +// Heavily inspired by node's `internal/errors` module + +const kCode = Symbol('code'); +const messages = new Map(); + +/** + * Extend an error of some sort into a DiscordjsError. + * @param {Error} Base Base error to extend + * @returns {DiscordjsError} + */ +function makeDiscordjsError(Base) { + return class DiscordjsError extends Base { + constructor(key, ...args) { + super(message(key, args)); + this[kCode] = key; + if (Error.captureStackTrace) Error.captureStackTrace(this, DiscordjsError); + } + + get name() { + return `${super.name} [${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } + }; +} + +/** + * Format the message for an error. + * @param {string} key Error key + * @param {Array<*>} args Arguments to pass for util format or as function args + * @returns {string} Formatted string + */ +function message(key, args) { + if (typeof key !== 'string') throw new Error('Error message key must be a string'); + const msg = messages.get(key); + if (!msg) throw new Error(`An invalid error message key was used: ${key}.`); + if (typeof msg === 'function') return msg(...args); + if (args === undefined || args.length === 0) return msg; + args.unshift(msg); + return String(...args); +} + +/** + * Register an error code and message. + * @param {string} sym Unique name for the error + * @param {*} val Value of the error + */ +function register(sym, val) { + messages.set(sym, typeof val === 'function' ? val : String(val)); +} + +module.exports = { + register, + Error: makeDiscordjsError(Error), + TypeError: makeDiscordjsError(TypeError), + RangeError: makeDiscordjsError(RangeError), +}; diff --git a/node_modules/discord.js/src/errors/Messages.js b/node_modules/discord.js/src/errors/Messages.js new file mode 100644 index 0000000..9f0f6e4 --- /dev/null +++ b/node_modules/discord.js/src/errors/Messages.js @@ -0,0 +1,108 @@ +'use strict'; + +const { register } = require('./DJSError'); + +const Messages = { + CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`, + CLIENT_INVALID_PROVIDED_SHARDS: 'None of the provided shards were valid.', + + TOKEN_INVALID: 'An invalid token was provided.', + TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.', + + WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.', + WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.', + WS_NOT_OPEN: (data = 'data') => `Websocket not open to send ${data}`, + + BITFIELD_INVALID: 'Invalid bitfield flag or number.', + + SHARDING_INVALID: 'Invalid shard settings were provided.', + SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.', + INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.', + DISALLOWED_INTENTS: 'Privileged intent provided is not enabled or whitelisted.', + SHARDING_NO_SHARDS: 'No shards have been spawned.', + SHARDING_IN_PROCESS: 'Shards are still being spawned.', + SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards.`, + SHARDING_PROCESS_EXISTS: id => `Shard ${id} already has an active process.`, + SHARDING_WORKER_EXISTS: id => `Shard ${id} already has an active worker.`, + SHARDING_READY_TIMEOUT: id => `Shard ${id}'s Client took too long to become ready.`, + SHARDING_READY_DISCONNECTED: id => `Shard ${id}'s Client disconnected before becoming ready.`, + SHARDING_READY_DIED: id => `Shard ${id}'s process exited before its Client became ready.`, + SHARDING_NO_CHILD_EXISTS: id => `Shard ${id} has no active process or worker.`, + + COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).', + COLOR_CONVERT: 'Unable to convert color to a number.', + + EMBED_FIELD_NAME: 'MessageEmbed field names may not be empty.', + EMBED_FIELD_VALUE: 'MessageEmbed field values may not be empty.', + + FILE_NOT_FOUND: file => `File could not be found: ${file}`, + + USER_NO_DMCHANNEL: 'No DM Channel exists!', + + VOICE_INVALID_HEARTBEAT: 'Tried to set voice heartbeat but no valid interval was specified.', + VOICE_USER_MISSING: "Couldn't resolve the user to create stream.", + VOICE_JOIN_CHANNEL: (full = false) => + `You do not have permission to join this voice channel${full ? '; it is full.' : '.'}`, + VOICE_CONNECTION_TIMEOUT: 'Connection not established within 15 seconds.', + VOICE_TOKEN_ABSENT: 'Token not provided from voice server packet.', + VOICE_SESSION_ABSENT: 'Session ID not supplied.', + VOICE_INVALID_ENDPOINT: 'Invalid endpoint received.', + VOICE_NO_BROWSER: 'Voice connections are not available in browsers.', + VOICE_CONNECTION_ATTEMPTS_EXCEEDED: attempts => `Too many connection attempts (${attempts}).`, + VOICE_JOIN_SOCKET_CLOSED: 'Tried to send join packet, but the WebSocket is not open.', + VOICE_PLAY_INTERFACE_NO_BROADCAST: 'A broadcast cannot be played in this context.', + VOICE_PLAY_INTERFACE_BAD_TYPE: 'Unknown stream type', + VOICE_PRISM_DEMUXERS_NEED_STREAM: 'To play a webm/ogg stream, you need to pass a ReadableStream.', + + VOICE_STATE_UNCACHED_MEMBER: 'The member of this voice state is uncached.', + VOICE_STATE_NOT_OWN: 'You cannot self-deafen/mute on VoiceStates that do not belong to the ClientUser.', + VOICE_STATE_INVALID_TYPE: name => `${name} must be a boolean.`, + + UDP_SEND_FAIL: 'Tried to send a UDP packet, but there is no socket available.', + UDP_ADDRESS_MALFORMED: 'Malformed UDP address or port.', + UDP_CONNECTION_EXISTS: 'There is already an existing UDP connection.', + + REQ_RESOURCE_TYPE: 'The resource must be a string, Buffer or a valid file stream.', + + IMAGE_FORMAT: format => `Invalid image format: ${format}`, + IMAGE_SIZE: size => `Invalid image size: ${size}`, + + MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.', + MESSAGE_NONCE_TYPE: 'Message nonce must fit in an unsigned 64-bit integer.', + + TYPING_COUNT: 'Count must be at least 1', + + SPLIT_MAX_LEN: 'Chunk exceeds the max length and contains no split characters.', + + BAN_RESOLVE_ID: (ban = false) => `Couldn't resolve the user ID to ${ban ? 'ban' : 'unban'}.`, + FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user ID to fetch the ban.", + + PRUNE_DAYS_TYPE: 'Days must be a number', + + GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.', + GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.', + GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.', + GUILD_OWNED: 'Guild is owned by the client.', + GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.", + GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.', + + INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`, + + WEBHOOK_MESSAGE: 'The message was not sent by a webhook.', + + EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji', + EMOJI_MANAGED: 'Emoji is managed and has no Author.', + MISSING_MANAGE_EMOJIS_PERMISSION: guild => + `Client must have Manage Emoji permission in guild ${guild} to see emoji authors.`, + + REACTION_RESOLVE_USER: "Couldn't resolve the user ID to remove from the reaction.", + + VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.', + + DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete them", + FETCH_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot fetch them", + + MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', +}; + +for (const [name, message] of Object.entries(Messages)) register(name, message); diff --git a/node_modules/discord.js/src/errors/index.js b/node_modules/discord.js/src/errors/index.js new file mode 100644 index 0000000..c94ddc7 --- /dev/null +++ b/node_modules/discord.js/src/errors/index.js @@ -0,0 +1,4 @@ +'use strict'; + +module.exports = require('./DJSError'); +module.exports.Messages = require('./Messages'); diff --git a/node_modules/discord.js/src/index.js b/node_modules/discord.js/src/index.js new file mode 100644 index 0000000..32b98a0 --- /dev/null +++ b/node_modules/discord.js/src/index.js @@ -0,0 +1,107 @@ +'use strict'; + +const Util = require('./util/Util'); + +module.exports = { + // "Root" classes (starting points) + BaseClient: require('./client/BaseClient'), + Client: require('./client/Client'), + Shard: require('./sharding/Shard'), + ShardClientUtil: require('./sharding/ShardClientUtil'), + ShardingManager: require('./sharding/ShardingManager'), + WebhookClient: require('./client/WebhookClient'), + + // Utilities + ActivityFlags: require('./util/ActivityFlags'), + BitField: require('./util/BitField'), + Collection: require('./util/Collection'), + Constants: require('./util/Constants'), + DataResolver: require('./util/DataResolver'), + BaseManager: require('./managers/BaseManager'), + DiscordAPIError: require('./rest/DiscordAPIError'), + HTTPError: require('./rest/HTTPError'), + MessageFlags: require('./util/MessageFlags'), + Intents: require('./util/Intents'), + Permissions: require('./util/Permissions'), + Speaking: require('./util/Speaking'), + Snowflake: require('./util/Snowflake'), + SnowflakeUtil: require('./util/Snowflake'), + Structures: require('./util/Structures'), + SystemChannelFlags: require('./util/SystemChannelFlags'), + UserFlags: require('./util/UserFlags'), + Util: Util, + version: require('../package.json').version, + + // Managers + ChannelManager: require('./managers/ChannelManager'), + GuildChannelManager: require('./managers/GuildChannelManager'), + GuildEmojiManager: require('./managers/GuildEmojiManager'), + GuildEmojiRoleManager: require('./managers/GuildEmojiRoleManager'), + GuildMemberManager: require('./managers/GuildMemberManager'), + GuildMemberRoleManager: require('./managers/GuildMemberRoleManager'), + GuildManager: require('./managers/GuildManager'), + ReactionManager: require('./managers/ReactionManager'), + ReactionUserManager: require('./managers/ReactionUserManager'), + MessageManager: require('./managers/MessageManager'), + PresenceManager: require('./managers/PresenceManager'), + RoleManager: require('./managers/RoleManager'), + UserManager: require('./managers/UserManager'), + + // Shortcuts to Util methods + discordSort: Util.discordSort, + escapeMarkdown: Util.escapeMarkdown, + fetchRecommendedShards: Util.fetchRecommendedShards, + resolveColor: Util.resolveColor, + resolveString: Util.resolveString, + splitMessage: Util.splitMessage, + + // Structures + Application: require('./structures/interfaces/Application'), + Base: require('./structures/Base'), + Activity: require('./structures/Presence').Activity, + APIMessage: require('./structures/APIMessage'), + BaseGuildEmoji: require('./structures/BaseGuildEmoji'), + CategoryChannel: require('./structures/CategoryChannel'), + Channel: require('./structures/Channel'), + ClientApplication: require('./structures/ClientApplication'), + get ClientUser() { + // This is a getter so that it properly extends any custom User class + return require('./structures/ClientUser'); + }, + Collector: require('./structures/interfaces/Collector'), + DMChannel: require('./structures/DMChannel'), + Emoji: require('./structures/Emoji'), + Guild: require('./structures/Guild'), + GuildAuditLogs: require('./structures/GuildAuditLogs'), + GuildChannel: require('./structures/GuildChannel'), + GuildEmoji: require('./structures/GuildEmoji'), + GuildMember: require('./structures/GuildMember'), + GuildPreview: require('./structures/GuildPreview'), + 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'), + PermissionOverwrites: require('./structures/PermissionOverwrites'), + Presence: require('./structures/Presence').Presence, + ClientPresence: require('./structures/ClientPresence'), + ReactionCollector: require('./structures/ReactionCollector'), + ReactionEmoji: require('./structures/ReactionEmoji'), + RichPresenceAssets: require('./structures/Presence').RichPresenceAssets, + Role: require('./structures/Role'), + StoreChannel: require('./structures/StoreChannel'), + Team: require('./structures/Team'), + TeamMember: require('./structures/TeamMember'), + TextChannel: require('./structures/TextChannel'), + User: require('./structures/User'), + VoiceChannel: require('./structures/VoiceChannel'), + VoiceRegion: require('./structures/VoiceRegion'), + VoiceState: require('./structures/VoiceState'), + Webhook: require('./structures/Webhook'), + + WebSocket: require('./WebSocket'), +}; diff --git a/node_modules/discord.js/src/managers/BaseManager.js b/node_modules/discord.js/src/managers/BaseManager.js new file mode 100644 index 0000000..c11d191 --- /dev/null +++ b/node_modules/discord.js/src/managers/BaseManager.js @@ -0,0 +1,81 @@ +'use strict'; + +const Collection = require('../util/Collection'); +let Structures; + +/** + * Manages the API methods of a data model and holds its cache. + * @abstract + */ +class BaseManager { + constructor(client, iterable, holds, cacheType = Collection, ...cacheOptions) { + if (!Structures) Structures = require('../util/Structures'); + /** + * The data structure belonging to this manager + * @name BaseManager#holds + * @type {Function} + * @private + * @readonly + */ + Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) || holds }); + + /** + * The client that instantiated this Manager + * @name BaseManager#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The type of Collection of the Manager + * @type {Collection} + */ + this.cacheType = cacheType; + + /** + * Holds the cache for the data model + * @type {Collection} + */ + this.cache = new cacheType(...cacheOptions); + if (iterable) for (const i of iterable) this.add(i); + } + + add(data, cache = true, { id, extras = [] } = {}) { + const existing = this.cache.get(id || data.id); + if (existing && existing._patch && cache) existing._patch(data); + if (existing) return existing; + + const entry = this.holds ? new this.holds(this.client, data, ...extras) : data; + if (cache) this.cache.set(id || entry.id, entry); + return entry; + } + + /** + * Resolves a data entry to a data Object. + * @param {string|Object} idOrInstance The id or instance of something in this Manager + * @returns {?Object} An instance from this Manager + */ + resolve(idOrInstance) { + if (idOrInstance instanceof this.holds) return idOrInstance; + if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) || null; + return null; + } + + /** + * Resolves a data entry to a instance ID. + * @param {string|Object} idOrInstance The id or instance of something in this Manager + * @returns {?Snowflake} + */ + resolveID(idOrInstance) { + if (idOrInstance instanceof this.holds) return idOrInstance.id; + if (typeof idOrInstance === 'string') return idOrInstance; + return null; + } + + valueOf() { + return this.cache; + } +} + +module.exports = BaseManager; diff --git a/node_modules/discord.js/src/managers/ChannelManager.js b/node_modules/discord.js/src/managers/ChannelManager.js new file mode 100644 index 0000000..8d7d71e --- /dev/null +++ b/node_modules/discord.js/src/managers/ChannelManager.js @@ -0,0 +1,96 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Channel = require('../structures/Channel'); +const { Events } = require('../util/Constants'); + +/** + * A manager of channels belonging to a client + * @extends {BaseManager} + */ +class ChannelManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Channel); + } + + /** + * The cache of Channels + * @type {Collection} + * @name ChannelManager#cache + */ + + add(data, guild, cache = true) { + const existing = this.cache.get(data.id); + if (existing) { + if (existing._patch && cache) existing._patch(data); + if (guild) guild.channels.add(existing); + return existing; + } + + const channel = Channel.create(this.client, data, guild); + + if (!channel) { + this.client.emit(Events.DEBUG, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`); + return null; + } + + if (cache) this.cache.set(channel.id, channel); + + return channel; + } + + remove(id) { + const channel = this.cache.get(id); + if (channel.guild) channel.guild.channels.cache.delete(id); + this.cache.delete(id); + } + + /** + * Data that can be resolved to give a Channel object. This can be: + * * A Channel object + * * A Snowflake + * @typedef {Channel|Snowflake} ChannelResolvable + */ + + /** + * Resolves a ChannelResolvable to a Channel object. + * @method resolve + * @memberof ChannelManager + * @instance + * @param {ChannelResolvable} channel The channel resolvable to resolve + * @returns {?Channel} + */ + + /** + * Resolves a ChannelResolvable to a channel ID string. + * @method resolveID + * @memberof ChannelManager + * @instance + * @param {ChannelResolvable} channel The channel resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Obtains a channel from Discord, or the channel cache if it's already available. + * @param {Snowflake} id ID of the channel + * @param {boolean} [cache=true] Whether to cache the new channel object if it isn't already + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + * @example + * // Fetch a channel by its id + * client.channels.fetch('222109930545610754') + * .then(channel => console.log(channel.name)) + * .catch(console.error); + */ + async fetch(id, cache = true, force = false) { + if (!force) { + const existing = this.cache.get(id); + if (existing && !existing.partial) return existing; + } + + const data = await this.client.api.channels(id).get(); + return this.add(data, null, cache); + } +} + +module.exports = ChannelManager; diff --git a/node_modules/discord.js/src/managers/GuildChannelManager.js b/node_modules/discord.js/src/managers/GuildChannelManager.js new file mode 100644 index 0000000..ab4f1f2 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildChannelManager.js @@ -0,0 +1,131 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const GuildChannel = require('../structures/GuildChannel'); +const PermissionOverwrites = require('../structures/PermissionOverwrites'); +const { ChannelTypes } = require('../util/Constants'); + +/** + * Manages API methods for GuildChannels and stores their cache. + * @extends {BaseManager} + */ +class GuildChannelManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, GuildChannel); + + /** + * The guild this Manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this Manager + * @type {Collection} + * @name GuildChannelManager#cache + */ + + add(channel) { + const existing = this.cache.get(channel.id); + if (existing) return existing; + this.cache.set(channel.id, channel); + return channel; + } + + /** + * Data that can be resolved to give a Guild Channel object. This can be: + * * A GuildChannel object + * * A Snowflake + * @typedef {GuildChannel|Snowflake} GuildChannelResolvable + */ + + /** + * Resolves a GuildChannelResolvable to a Channel object. + * @method resolve + * @memberof GuildChannelManager + * @instance + * @param {GuildChannelResolvable} channel The GuildChannel resolvable to resolve + * @returns {?GuildChannel} + */ + + /** + * Resolves a GuildChannelResolvable to a channel ID string. + * @method resolveID + * @memberof GuildChannelManager + * @instance + * @param {GuildChannelResolvable} channel The GuildChannel resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Creates a new channel in the guild. + * @param {string} name The name of the new channel + * @param {Object} [options] Options + * @param {string} [options.type='text'] The type of the new channel, either `text`, `voice`, or `category` + * @param {string} [options.topic] The topic for the new channel + * @param {boolean} [options.nsfw] Whether the new channel is nsfw + * @param {number} [options.bitrate] Bitrate of the new channel in bits (only voice) + * @param {number} [options.userLimit] Maximum amount of users allowed in the new channel (only voice) + * @param {ChannelResolvable} [options.parent] Parent of the new channel + * @param {OverwriteResolvable[]|Collection} [options.permissionOverwrites] + * Permission overwrites of the new channel + * @param {number} [options.position] Position of the new channel + * @param {number} [options.rateLimitPerUser] The ratelimit per user for the channel + * @param {string} [options.reason] Reason for creating the channel + * @returns {Promise} + * @example + * // Create a new text channel + * guild.channels.create('new-general', { reason: 'Needed a cool new channel' }) + * .then(console.log) + * .catch(console.error); + * @example + * // Create a new channel with permission overwrites + * guild.channels.create('new-voice', { + * type: 'voice', + * permissionOverwrites: [ + * { + * id: message.author.id, + * deny: ['VIEW_CHANNEL'], + * }, + * ], + * }) + */ + async create(name, options = {}) { + let { + type, + topic, + nsfw, + bitrate, + userLimit, + parent, + permissionOverwrites, + position, + rateLimitPerUser, + reason, + } = options; + if (parent) parent = this.client.channels.resolveID(parent); + if (permissionOverwrites) { + permissionOverwrites = permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); + } + + const data = await this.client.api.guilds(this.guild.id).channels.post({ + data: { + name, + topic, + type: type ? ChannelTypes[type.toUpperCase()] : ChannelTypes.TEXT, + nsfw, + bitrate, + user_limit: userLimit, + parent_id: parent, + position, + permission_overwrites: permissionOverwrites, + rate_limit_per_user: rateLimitPerUser, + }, + reason, + }); + return this.client.actions.ChannelCreate.handle(data).channel; + } +} + +module.exports = GuildChannelManager; diff --git a/node_modules/discord.js/src/managers/GuildEmojiManager.js b/node_modules/discord.js/src/managers/GuildEmojiManager.js new file mode 100644 index 0000000..acf3576 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildEmojiManager.js @@ -0,0 +1,135 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { TypeError } = require('../errors'); +const GuildEmoji = require('../structures/GuildEmoji'); +const ReactionEmoji = require('../structures/ReactionEmoji'); +const Collection = require('../util/Collection'); +const DataResolver = require('../util/DataResolver'); +const { parseEmoji } = require('../util/Util'); + +/** + * Manages API methods for GuildEmojis and stores their cache. + * @extends {BaseManager} + */ +class GuildEmojiManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, GuildEmoji); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of GuildEmojis + * @type {Collection} + * @name GuildEmojiManager#cache + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.guild] }); + } + + /** + * 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 {Object} [options] Options + * @param {Collection|RoleResolvable[]} [options.roles] Roles to limit the emoji to + * @param {string} [options.reason] Reason for creating the emoji + * @returns {Promise} The created emoji + * @example + * // Create a new emoji from a url + * guild.emojis.create('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.emojis.create('./memes/banana.png', 'banana') + * .then(emoji => console.log(`Created new emoji with name ${emoji.name}!`)) + * .catch(console.error); + */ + async create(attachment, name, { roles, reason } = {}) { + attachment = await DataResolver.resolveImage(attachment); + if (!attachment) throw new TypeError('REQ_RESOURCE_TYPE'); + + const data = { image: attachment, name }; + if (roles) { + data.roles = []; + for (let role of roles instanceof Collection ? roles.values() : roles) { + role = this.guild.roles.resolve(role); + if (!role) { + return Promise.reject( + new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true), + ); + } + data.roles.push(role.id); + } + } + + return this.client.api + .guilds(this.guild.id) + .emojis.post({ data, reason }) + .then(emoji => this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji); + } + + /** + * Data that can be resolved into an GuildEmoji object. This can be: + * * A custom emoji ID + * * A GuildEmoji object + * * A ReactionEmoji object + * @typedef {Snowflake|GuildEmoji|ReactionEmoji} EmojiResolvable + */ + + /** + * Resolves an EmojiResolvable to an Emoji object. + * @param {EmojiResolvable} emoji The Emoji resolvable to identify + * @returns {?GuildEmoji} + */ + resolve(emoji) { + if (emoji instanceof ReactionEmoji) return super.resolve(emoji.id); + return super.resolve(emoji); + } + + /** + * Resolves an EmojiResolvable to an Emoji ID string. + * @param {EmojiResolvable} emoji The Emoji resolvable to identify + * @returns {?Snowflake} + */ + resolveID(emoji) { + if (emoji instanceof ReactionEmoji) return emoji.id; + return super.resolveID(emoji); + } + + /** + * Data that can be resolved to give an emoji identifier. This can be: + * * The unicode representation of an emoji + * * The ``, `<:name:id>`, `:name:id` or `a:name:id` emoji identifier string of an emoji + * * An EmojiResolvable + * @typedef {string|EmojiResolvable} EmojiIdentifierResolvable + */ + + /** + * Resolves an EmojiResolvable to an emoji identifier. + * @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve + * @returns {?string} + */ + resolveIdentifier(emoji) { + const emojiResolvable = this.resolve(emoji); + if (emojiResolvable) return emojiResolvable.identifier; + if (emoji instanceof ReactionEmoji) return emoji.identifier; + if (typeof emoji === 'string') { + const res = parseEmoji(emoji); + if (res && res.name.length) { + emoji = `${res.animated ? 'a:' : ''}${res.name}${res.id ? `:${res.id}` : ''}`; + } + if (!emoji.includes('%')) return encodeURIComponent(emoji); + else return emoji; + } + return null; + } +} + +module.exports = GuildEmojiManager; diff --git a/node_modules/discord.js/src/managers/GuildEmojiRoleManager.js b/node_modules/discord.js/src/managers/GuildEmojiRoleManager.js new file mode 100644 index 0000000..038571f --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildEmojiRoleManager.js @@ -0,0 +1,119 @@ +'use strict'; + +const { TypeError } = require('../errors'); +const Collection = require('../util/Collection'); + +/** + * Manages API methods for roles belonging to emojis and stores their cache. + */ +class GuildEmojiRoleManager { + constructor(emoji) { + /** + * The emoji belonging to this manager + * @type {GuildEmoji} + */ + this.emoji = emoji; + /** + * The guild belonging to this manager + * @type {Guild} + */ + this.guild = emoji.guild; + /** + * The client belonging to this manager + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: emoji.client }); + } + + /** + * The filtered collection of roles of the guild emoji + * @type {Collection} + * @private + * @readonly + */ + get _roles() { + return this.guild.roles.cache.filter(role => this.emoji._roles.includes(role.id)); + } + + /** + * The cache of roles belonging to this emoji + * @type {Collection} + * @readonly + */ + get cache() { + return this._roles; + } + + /** + * Adds a role (or multiple roles) to the list of roles that can use this emoji. + * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to add + * @returns {Promise} + */ + add(roleOrRoles) { + if (roleOrRoles instanceof Collection) return this.add(roleOrRoles.keyArray()); + if (!Array.isArray(roleOrRoles)) return this.add([roleOrRoles]); + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r)); + + if (roleOrRoles.includes(null)) { + return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true)); + } + + const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))]; + return this.set(newRoles); + } + + /** + * Removes a role (or multiple roles) from the list of roles that can use this emoji. + * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to remove + * @returns {Promise} + */ + remove(roleOrRoles) { + if (roleOrRoles instanceof Collection) return this.remove(roleOrRoles.keyArray()); + if (!Array.isArray(roleOrRoles)) return this.remove([roleOrRoles]); + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolveID(r)); + + if (roleOrRoles.includes(null)) { + return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true)); + } + + const newRoles = this._roles.keyArray().filter(role => !roleOrRoles.includes(role)); + return this.set(newRoles); + } + + /** + * Sets the role(s) that can use this emoji. + * @param {Collection|RoleResolvable[]} roles The roles or role IDs to apply + * @returns {Promise} + * @example + * // Set the emoji's roles to a single role + * guildEmoji.roles.set(['391156570408615936']) + * .then(console.log) + * .catch(console.error); + * @example + * // Remove all roles from an emoji + * guildEmoji.roles.set([]) + * .then(console.log) + * .catch(console.error); + */ + set(roles) { + return this.emoji.edit({ roles }); + } + + clone() { + const clone = new this.constructor(this.emoji); + clone._patch(this._roles.keyArray().slice()); + return clone; + } + + /** + * Patches the roles for this manager's cache + * @param {Snowflake[]} roles The new roles + * @private + */ + _patch(roles) { + this.emoji._roles = roles; + } +} + +module.exports = GuildEmojiRoleManager; diff --git a/node_modules/discord.js/src/managers/GuildManager.js b/node_modules/discord.js/src/managers/GuildManager.js new file mode 100644 index 0000000..b04a52c --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildManager.js @@ -0,0 +1,252 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Guild = require('../structures/Guild'); +const GuildChannel = require('../structures/GuildChannel'); +const GuildEmoji = require('../structures/GuildEmoji'); +const GuildMember = require('../structures/GuildMember'); +const Invite = require('../structures/Invite'); +const Role = require('../structures/Role'); +const { + ChannelTypes, + Events, + VerificationLevels, + DefaultMessageNotifications, + ExplicitContentFilterLevels, +} = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const Permissions = require('../util/Permissions'); +const { resolveColor } = require('../util/Util'); + +/** + * Manages API methods for Guilds and stores their cache. + * @extends {BaseManager} + */ +class GuildManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Guild); + } + + /** + * The cache of this Manager + * @type {Collection} + * @name GuildManager#cache + */ + + /** + * Data that resolves to give a Guild object. This can be: + * * A Guild object + * * A GuildChannel object + * * A GuildEmoji object + * * A Role object + * * A Snowflake + * * An Invite object + * @typedef {Guild|GuildChannel|GuildMember|GuildEmoji|Role|Snowflake|Invite} GuildResolvable + */ + + /** + * Partial data for a Role. + * @typedef {Object} PartialRoleData + * @property {number} [id] The ID for this role, used to set channel overrides, + * this is a placeholder and will be replaced by the API after consumption + * @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 + */ + + /** + * Partial overwrite data. + * @typedef {Object} PartialOverwriteData + * @property {number|Snowflake} id The Role or User ID for this overwrite + * @property {string} [type] The type of this overwrite + * @property {PermissionResolvable} [allow] The permissions to allow + * @property {PermissionResolvable} [deny] The permissions to deny + */ + + /** + * Partial data for a Channel. + * @typedef {Object} PartialChannelData + * @property {number} [id] The ID for this channel, used to set its parent, + * this is a placeholder and will be replaced by the API after consumption + * @property {number} [parentID] The parent ID for this channel + * @property {string} [type] The type of the channel + * @property {string} name The name 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 {PartialOverwriteData} [permissionOverwrites] + * Overwrites of the channel + * @property {number} [rateLimitPerUser] The rate limit per user of the channel in seconds + */ + + /** + * Resolves a GuildResolvable to a Guild object. + * @method resolve + * @memberof GuildManager + * @instance + * @param {GuildResolvable} guild The guild resolvable to identify + * @returns {?Guild} + */ + resolve(guild) { + if ( + guild instanceof GuildChannel || + guild instanceof GuildMember || + guild instanceof GuildEmoji || + guild instanceof Role || + (guild instanceof Invite && guild.guild) + ) { + return super.resolve(guild.guild); + } + return super.resolve(guild); + } + + /** + * Resolves a GuildResolvable to a Guild ID string. + * @method resolveID + * @memberof GuildManager + * @instance + * @param {GuildResolvable} guild The guild resolvable to identify + * @returns {?Snowflake} + */ + resolveID(guild) { + if ( + guild instanceof GuildChannel || + guild instanceof GuildMember || + guild instanceof GuildEmoji || + guild instanceof Role || + (guild instanceof Invite && guild.guild) + ) { + return super.resolveID(guild.guild.id); + } + return super.resolveID(guild); + } + + /** + * Creates a guild. + * This is only available to bots in fewer than 10 guilds. + * @param {string} name The name of the guild + * @param {Object} [options] Options for the creating + * @param {number} [options.afkChannelID] The ID of the AFK channel + * @param {number} [options.afkTimeout] The AFK timeout in seconds + * @param {PartialChannelData[]} [options.channels] The channels for this guild + * @param {DefaultMessageNotifications} [options.defaultMessageNotifications] The default message notifications + * for the guild + * @param {ExplicitContentFilterLevel} [options.explicitContentFilter] The explicit content filter level for the guild + * @param {BufferResolvable|Base64Resolvable} [options.icon=null] The icon for the guild + * @param {string} [options.region] The region for the server, defaults to the closest one available + * @param {PartialRoleData[]} [options.roles] The roles for this guild, + * the first element of this array is used to change properties of the guild's everyone role. + * @param {number} [options.systemChannelID] The ID of the system channel + * @param {VerificationLevel} [options.verificationLevel] The verification level for the guild + * @returns {Promise} The guild that was created + */ + async create( + name, + { + afkChannelID, + afkTimeout, + channels = [], + defaultMessageNotifications, + explicitContentFilter, + icon = null, + region, + roles = [], + systemChannelID, + verificationLevel, + } = {}, + ) { + icon = await DataResolver.resolveImage(icon); + if (typeof verificationLevel !== 'undefined' && typeof verificationLevel !== 'number') { + verificationLevel = VerificationLevels.indexOf(verificationLevel); + } + if (typeof defaultMessageNotifications !== 'undefined' && typeof defaultMessageNotifications !== 'number') { + defaultMessageNotifications = DefaultMessageNotifications.indexOf(defaultMessageNotifications); + } + if (typeof explicitContentFilter !== 'undefined' && typeof explicitContentFilter !== 'number') { + explicitContentFilter = ExplicitContentFilterLevels.indexOf(explicitContentFilter); + } + for (const channel of channels) { + if (channel.type) channel.type = ChannelTypes[channel.type.toUpperCase()]; + channel.parent_id = channel.parentID; + delete channel.parentID; + if (!channel.permissionOverwrites) continue; + for (const overwrite of channel.permissionOverwrites) { + if (overwrite.allow) overwrite.allow = Permissions.resolve(overwrite.allow); + if (overwrite.deny) overwrite.deny = Permissions.resolve(overwrite.deny); + } + channel.permission_overwrites = channel.permissionOverwrites; + delete channel.permissionOverwrites; + } + for (const role of roles) { + if (role.color) role.color = resolveColor(role.color); + if (role.permissions) role.permissions = Permissions.resolve(role.permissions); + } + return new Promise((resolve, reject) => + this.client.api.guilds + .post({ + data: { + name, + region, + icon, + verification_level: verificationLevel, + default_message_notifications: defaultMessageNotifications, + explicit_content_filter: explicitContentFilter, + roles, + channels, + afk_channel_id: afkChannelID, + afk_timeout: afkTimeout, + system_channel_id: systemChannelID, + }, + }) + .then(data => { + if (this.client.guilds.cache.has(data.id)) return resolve(this.client.guilds.cache.get(data.id)); + + const handleGuild = guild => { + if (guild.id === data.id) { + this.client.clearTimeout(timeout); + this.client.removeListener(Events.GUILD_CREATE, handleGuild); + this.client.decrementMaxListeners(); + resolve(guild); + } + }; + this.client.incrementMaxListeners(); + this.client.on(Events.GUILD_CREATE, handleGuild); + + const timeout = this.client.setTimeout(() => { + this.client.removeListener(Events.GUILD_CREATE, handleGuild); + this.client.decrementMaxListeners(); + resolve(this.client.guilds.add(data)); + }, 10000); + return undefined; + }, reject), + ); + } + + /** + * Obtains a guild from Discord, or the guild cache if it's already available. + * @param {Snowflake} id ID of the guild + * @param {boolean} [cache=true] Whether to cache the new guild object if it isn't already + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + * @example + * // Fetch a guild by its id + * client.guilds.fetch('222078108977594368') + * .then(guild => console.log(guild.name)) + * .catch(console.error); + */ + async fetch(id, cache = true, force = false) { + if (!force) { + const existing = this.cache.get(id); + if (existing) return existing; + } + + const data = await this.client.api.guilds(id).get({ query: { with_counts: true } }); + return this.add(data, cache); + } +} + +module.exports = GuildManager; diff --git a/node_modules/discord.js/src/managers/GuildMemberManager.js b/node_modules/discord.js/src/managers/GuildMemberManager.js new file mode 100644 index 0000000..8dfa2d8 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildMemberManager.js @@ -0,0 +1,325 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Error, TypeError, RangeError } = require('../errors'); +const GuildMember = require('../structures/GuildMember'); +const Collection = require('../util/Collection'); +const { Events, OPCodes } = require('../util/Constants'); +const SnowflakeUtil = require('../util/Snowflake'); + +/** + * Manages API methods for GuildMembers and stores their cache. + * @extends {BaseManager} + */ +class GuildMemberManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, GuildMember); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this Manager + * @type {Collection} + * @name GuildMemberManager#cache + */ + + add(data, cache = true) { + return super.add(data, cache, { id: data.user.id, extras: [this.guild] }); + } + + /** + * Data that resolves to give a GuildMember object. This can be: + * * A GuildMember object + * * A User resolvable + * @typedef {GuildMember|UserResolvable} GuildMemberResolvable + */ + + /** + * Resolves a GuildMemberResolvable to a GuildMember object. + * @param {GuildMemberResolvable} member The user that is part of the guild + * @returns {?GuildMember} + */ + resolve(member) { + const memberResolvable = super.resolve(member); + if (memberResolvable) return memberResolvable; + const userResolvable = this.client.users.resolveID(member); + if (userResolvable) return super.resolve(userResolvable); + return null; + } + + /** + * Resolves a GuildMemberResolvable to a member ID string. + * @param {GuildMemberResolvable} member The user that is part of the guild + * @returns {?Snowflake} + */ + resolveID(member) { + const memberResolvable = super.resolveID(member); + if (memberResolvable) return memberResolvable; + const userResolvable = this.client.users.resolveID(member); + return this.cache.has(userResolvable) ? userResolvable : null; + } + + /** + * Options used to fetch a single member from a guild. + * @typedef {Object} FetchMemberOptions + * @property {UserResolvable} user The user to fetch + * @property {boolean} [cache=true] Whether or not to cache the fetched member + * @property {boolean} [force=false] Whether to skip the cache check and request the API + */ + + /** + * Options used to fetch multiple members from a guild. + * @typedef {Object} FetchMembersOptions + * @property {UserResolvable|UserResolvable[]} user The user(s) to fetch + * @property {?string} query Limit fetch to members with similar usernames + * @property {number} [limit=0] Maximum number of members to request + * @property {boolean} [withPresences=false] Whether or not to include the presences + * @property {number} [time=120e3] Timeout for receipt of members + * @property {?string} nonce Nonce for this request (32 characters max - default to base 16 now timestamp) + * @property {boolean} [force=false] Whether to skip the cache check and request the API + */ + + /** + * Fetches member(s) from Discord, even if they're offline. + * @param {UserResolvable|FetchMemberOptions|FetchMembersOptions} [options] If a UserResolvable, the user to fetch. + * If undefined, fetches all members. + * If a query, it limits the results to users with similar usernames. + * @returns {Promise|Promise>} + * @example + * // Fetch all members from a guild + * guild.members.fetch() + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch a single member + * guild.members.fetch('66564597481480192') + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch a single member without checking cache + * guild.members.fetch({ user, force: true }) + * .then(console.log) + * .catch(console.error) + * @example + * // Fetch a single member without caching + * guild.members.fetch({ user, cache: false }) + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch by an array of users including their presences + * guild.members.fetch({ user: ['66564597481480192', '191615925336670208'], withPresences: true }) + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch by query + * guild.members.fetch({ query: 'hydra', limit: 1 }) + * .then(console.log) + * .catch(console.error); + */ + fetch(options) { + if (!options) return this._fetchMany(); + const user = this.client.users.resolveID(options); + if (user) return this._fetchSingle({ user, cache: true }); + if (options.user) { + if (Array.isArray(options.user)) { + options.user = options.user.map(u => this.client.users.resolveID(u)); + return this._fetchMany(options); + } else { + options.user = this.client.users.resolveID(options.user); + } + if (!options.limit && !options.withPresences) return this._fetchSingle(options); + } + return this._fetchMany(options); + } + + /** + * Prunes members from the guild based on how long they have been inactive. + * It's recommended to set options.count to `false` for large guilds. + * @param {Object} [options] Prune options + * @param {number} [options.days=7] Number of days of inactivity required to kick + * @param {boolean} [options.dry=false] Get number of users that will be kicked, without actually kicking them + * @param {boolean} [options.count=true] Whether or not to return the number of users that have been kicked. + * @param {RoleResolvable[]} [options.roles=[]] Array of roles to bypass the "...and no roles" constraint when pruning + * @param {string} [options.reason] Reason for this prune + * @returns {Promise} The number of members that were/will be kicked + * @example + * // See how many members will be pruned + * guild.members.prune({ dry: true }) + * .then(pruned => console.log(`This will prune ${pruned} people!`)) + * .catch(console.error); + * @example + * // Actually prune the members + * guild.members.prune({ days: 1, reason: 'too many people!' }) + * .then(pruned => console.log(`I just pruned ${pruned} people!`)) + * .catch(console.error); + * @example + * // Include members with a specified role + * guild.members.prune({ days: 7, roles: ['657259391652855808'] }) + * .then(pruned => console.log(`I just pruned ${pruned} people!`)) + * .catch(console.error); + */ + prune({ days = 7, dry = false, count: compute_prune_count = true, roles = [], reason } = {}) { + if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE'); + + const query = { days }; + const resolvedRoles = []; + + for (const role of roles) { + const resolvedRole = this.guild.roles.resolveID(role); + if (!resolvedRole) { + return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array of Roles or Snowflakes', true)); + } + resolvedRoles.push(resolvedRole); + } + + if (resolvedRoles.length) { + query.include_roles = dry ? resolvedRoles.join(',') : resolvedRoles; + } + + const endpoint = this.client.api.guilds(this.guild.id).prune; + + if (dry) { + return endpoint.get({ query, reason }).then(data => data.pruned); + } + + return endpoint + .post({ + data: { ...query, compute_prune_count }, + reason, + }) + .then(data => data.pruned); + } + + /** + * Bans a user from the guild. + * @param {UserResolvable} user The user to ban + * @param {Object} [options] Options for the ban + * @param {number} [options.days=0] Number of days of messages to delete, must be between 0 and 7 + * @param {string} [options.reason] Reason for banning + * @returns {Promise} 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 (or with a user/guild member object) + * guild.members.ban('84484653687267328') + * .then(user => console.log(`Banned ${user.username || user.id || user} from ${guild.name}`)) + * .catch(console.error); + */ + ban(user, options = { days: 0 }) { + if (options.days) options.delete_message_days = options.days; + const id = this.client.users.resolveID(user); + if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID', true)); + return this.client.api + .guilds(this.guild.id) + .bans[id].put({ data: options }) + .then(() => { + if (user instanceof GuildMember) return user; + const _user = this.client.users.resolve(id); + if (_user) { + const member = this.resolve(_user); + return member || _user; + } + return id; + }); + } + + /** + * Unbans a user from the guild. + * @param {UserResolvable} user The user to unban + * @param {string} [reason] Reason for unbanning user + * @returns {Promise} + * @example + * // Unban a user by ID (or with a user/guild member object) + * guild.members.unban('84484653687267328') + * .then(user => console.log(`Unbanned ${user.username} from ${guild.name}`)) + * .catch(console.error); + */ + unban(user, reason) { + const id = this.client.users.resolveID(user); + if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID')); + return this.client.api + .guilds(this.guild.id) + .bans[id].delete({ reason }) + .then(() => this.client.users.resolve(user)); + } + + _fetchSingle({ user, cache, force = false }) { + if (!force) { + const existing = this.cache.get(user); + if (existing && !existing.partial) return Promise.resolve(existing); + } + + return this.client.api + .guilds(this.guild.id) + .members(user) + .get() + .then(data => this.add(data, cache)); + } + + _fetchMany({ + limit = 0, + withPresences: presences = false, + user: user_ids, + query, + time = 120e3, + nonce = SnowflakeUtil.generate(), + force = false, + } = {}) { + return new Promise((resolve, reject) => { + if (this.guild.memberCount === this.cache.size && !query && !limit && !presences && !user_ids && !force) { + resolve(this.cache); + return; + } + if (!query && !user_ids) query = ''; + if (nonce.length > 32) throw new RangeError('MEMBER_FETCH_NONCE_LENGTH'); + this.guild.shard.send({ + op: OPCodes.REQUEST_GUILD_MEMBERS, + d: { + guild_id: this.guild.id, + presences, + user_ids, + query, + nonce, + limit, + }, + }); + const fetchedMembers = new Collection(); + const option = query || limit || presences || user_ids; + let i = 0; + const handler = (members, _, chunk) => { + timeout.refresh(); + if (chunk.nonce !== nonce) return; + i++; + for (const member of members.values()) { + if (option) fetchedMembers.set(member.id, member); + } + if ( + this.guild.memberCount <= this.cache.size || + (option && members.size < 1000) || + (limit && fetchedMembers.size >= limit) || + i === chunk.count + ) { + this.client.clearTimeout(timeout); + this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); + this.client.decrementMaxListeners(); + let fetched = option ? fetchedMembers : this.cache; + if (user_ids && !Array.isArray(user_ids) && fetched.size) fetched = fetched.first(); + resolve(fetched); + } + }; + const timeout = this.client.setTimeout(() => { + this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); + this.client.decrementMaxListeners(); + reject(new Error('GUILD_MEMBERS_TIMEOUT')); + }, time); + this.client.incrementMaxListeners(); + this.client.on(Events.GUILD_MEMBERS_CHUNK, handler); + }); + } +} + +module.exports = GuildMemberManager; diff --git a/node_modules/discord.js/src/managers/GuildMemberRoleManager.js b/node_modules/discord.js/src/managers/GuildMemberRoleManager.js new file mode 100644 index 0000000..9fc6248 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildMemberRoleManager.js @@ -0,0 +1,161 @@ +'use strict'; + +const { TypeError } = require('../errors'); +const Collection = require('../util/Collection'); + +/** + * Manages API methods for roles of a GuildMember and stores their cache. + */ +class GuildMemberRoleManager { + constructor(member) { + /** + * The GuildMember this manager belongs to + * @type {GuildMember} + */ + this.member = member; + /** + * The Guild this manager belongs to + * @type {Guild} + */ + this.guild = member.guild; + Object.defineProperty(this, 'client', { value: member.client }); + } + + /** + * The filtered collection of roles of the member + * @type {Collection} + * @private + * @readonly + */ + get _roles() { + const everyone = this.guild.roles.everyone; + return this.guild.roles.cache.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone); + } + + /** + * The roles of this member + * @type {Collection} + * @readonly + */ + get cache() { + return this._roles; + } + + /** + * The role of the member used to hoist them in a separate category in the users list + * @type {?Role} + * @readonly + */ + get hoist() { + 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)); + } + + /** + * The role of the member used to set their color + * @type {?Role} + * @readonly + */ + get color() { + 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 role of the member with the highest position + * @type {Role} + * @readonly + */ + get highest() { + return this._roles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this._roles.first()); + } + + /** + * Adds a role (or multiple roles) to the member. + * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to add + * @param {string} [reason] Reason for adding the role(s) + * @returns {Promise} + */ + async add(roleOrRoles, reason) { + if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) { + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r)); + if (roleOrRoles.includes(null)) { + throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true); + } + + const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))]; + return this.set(newRoles, reason); + } else { + roleOrRoles = this.guild.roles.resolve(roleOrRoles); + if (roleOrRoles === null) { + throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes'); + } + + await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].put({ reason }); + + const clone = this.member._clone(); + clone._roles = [...this._roles.keys(), roleOrRoles.id]; + return clone; + } + } + + /** + * Removes a role (or multiple roles) from the member. + * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to remove + * @param {string} [reason] Reason for removing the role(s) + * @returns {Promise} + */ + async remove(roleOrRoles, reason) { + if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) { + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r)); + if (roleOrRoles.includes(null)) { + throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true); + } + + const newRoles = this._roles.filter(role => !roleOrRoles.includes(role)); + return this.set(newRoles, reason); + } else { + roleOrRoles = this.guild.roles.resolve(roleOrRoles); + if (roleOrRoles === null) { + throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true); + } + + await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].delete({ reason }); + + const clone = this.member._clone(); + const newRoles = this._roles.filter(role => role.id !== roleOrRoles.id); + clone._roles = [...newRoles.keys()]; + return clone; + } + } + + /** + * Sets the roles applied to the member. + * @param {Collection|RoleResolvable[]} roles The roles or role IDs to apply + * @param {string} [reason] Reason for applying the roles + * @returns {Promise} + * @example + * // Set the member's roles to a single role + * guildMember.roles.set(['391156570408615936']) + * .then(console.log) + * .catch(console.error); + * @example + * // Remove all the roles from a member + * guildMember.roles.set([]) + * .then(member => console.log(`Member roles is now of ${member.roles.cache.size} size`)) + * .catch(console.error); + */ + set(roles, reason) { + return this.member.edit({ roles }, reason); + } + + clone() { + const clone = new this.constructor(this.member); + clone.member._roles = [...this._roles.keyArray()]; + return clone; + } +} + +module.exports = GuildMemberRoleManager; diff --git a/node_modules/discord.js/src/managers/MessageManager.js b/node_modules/discord.js/src/managers/MessageManager.js new file mode 100644 index 0000000..96a9643 --- /dev/null +++ b/node_modules/discord.js/src/managers/MessageManager.js @@ -0,0 +1,147 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { TypeError } = require('../errors'); +const Message = require('../structures/Message'); +const Collection = require('../util/Collection'); +const LimitedCollection = require('../util/LimitedCollection'); + +/** + * Manages API methods for Messages and holds their cache. + * @extends {BaseManager} + */ +class MessageManager extends BaseManager { + constructor(channel, iterable) { + super(channel.client, iterable, Message, LimitedCollection, channel.client.options.messageCacheMaxSize); + /** + * The channel that the messages belong to + * @type {TextBasedChannel} + */ + this.channel = channel; + } + + /** + * The cache of Messages + * @type {Collection} + * @name MessageManager#cache + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.channel] }); + } + + /** + * 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 a message, or messages, from this channel. + * 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. + * @param {Snowflake|ChannelLogsQueryOptions} [message] The ID of the message to fetch, or query parameters. + * @param {boolean} [cache=true] Whether to cache the message(s) + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise|Promise>} + * @example + * // Get message + * channel.messages.fetch('99539446449315840') + * .then(message => console.log(message.content)) + * .catch(console.error); + * @example + * // Get messages + * channel.messages.fetch({ limit: 10 }) + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + * @example + * // Get messages and filter by user ID + * channel.messages.fetch() + * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) + * .catch(console.error); + */ + fetch(message, cache = true, force = false) { + return typeof message === 'string' ? this._fetchId(message, cache, force) : this._fetchMany(message, cache); + } + + /** + * Fetches the pinned messages of this channel and returns a collection of them. + * The returned Collection does not contain any reaction data of the messages. + * Those need to be fetched separately. + * @param {boolean} [cache=true] Whether to cache the message(s) + * @returns {Promise>} + * @example + * // Get pinned messages + * channel.messages.fetchPinned() + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + */ + fetchPinned(cache = true) { + return this.client.api.channels[this.channel.id].pins.get().then(data => { + const messages = new Collection(); + for (const message of data) messages.set(message.id, this.add(message, cache)); + return messages; + }); + } + + /** + * Data that can be resolved to a Message object. This can be: + * * A Message + * * A Snowflake + * @typedef {Message|Snowflake} MessageResolvable + */ + + /** + * Resolves a MessageResolvable to a Message object. + * @method resolve + * @memberof MessageManager + * @instance + * @param {MessageResolvable} message The message resolvable to resolve + * @returns {?Message} + */ + + /** + * Resolves a MessageResolvable to a Message ID string. + * @method resolveID + * @memberof MessageManager + * @instance + * @param {MessageResolvable} message The message resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Deletes a message, even if it's not cached. + * @param {MessageResolvable} message The message to delete + * @param {string} [reason] Reason for deleting this message, if it does not belong to the client user + * @returns {Promise} + */ + async delete(message, reason) { + message = this.resolveID(message); + if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + + await this.client.api.channels(this.channel.id).messages(message).delete({ reason }); + } + + async _fetchId(messageID, cache, force) { + if (!force) { + const existing = this.cache.get(messageID); + if (existing && !existing.partial) return existing; + } + + const data = await this.client.api.channels[this.channel.id].messages[messageID].get(); + return this.add(data, cache); + } + + async _fetchMany(options = {}, cache) { + const data = await this.client.api.channels[this.channel.id].messages.get({ query: options }); + const messages = new Collection(); + for (const message of data) messages.set(message.id, this.add(message, cache)); + return messages; + } +} + +module.exports = MessageManager; diff --git a/node_modules/discord.js/src/managers/PresenceManager.js b/node_modules/discord.js/src/managers/PresenceManager.js new file mode 100644 index 0000000..c02fe04 --- /dev/null +++ b/node_modules/discord.js/src/managers/PresenceManager.js @@ -0,0 +1,59 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Presence } = require('../structures/Presence'); + +/** + * Manages API methods for Presences and holds their cache. + * @extends {BaseManager} + */ +class PresenceManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Presence); + } + + /** + * The cache of Presences + * @type {Collection} + * @name PresenceManager#cache + */ + + add(data, cache) { + const existing = this.cache.get(data.user.id); + return existing ? existing.patch(data) : super.add(data, cache, { id: data.user.id }); + } + + /** + * Data that can be resolved to a Presence object. This can be: + * * A Presence + * * A UserResolvable + * * A Snowflake + * @typedef {Presence|UserResolvable|Snowflake} PresenceResolvable + */ + + /** + * Resolves a PresenceResolvable to a Presence object. + * @param {PresenceResolvable} presence The presence resolvable to resolve + * @returns {?Presence} + */ + resolve(presence) { + const presenceResolvable = super.resolve(presence); + if (presenceResolvable) return presenceResolvable; + const UserResolvable = this.client.users.resolveID(presence); + return super.resolve(UserResolvable) || null; + } + + /** + * Resolves a PresenceResolvable to a Presence ID string. + * @param {PresenceResolvable} presence The presence resolvable to resolve + * @returns {?Snowflake} + */ + resolveID(presence) { + const presenceResolvable = super.resolveID(presence); + if (presenceResolvable) return presenceResolvable; + const userResolvable = this.client.users.resolveID(presence); + return this.cache.has(userResolvable) ? userResolvable : null; + } +} + +module.exports = PresenceManager; diff --git a/node_modules/discord.js/src/managers/ReactionManager.js b/node_modules/discord.js/src/managers/ReactionManager.js new file mode 100644 index 0000000..e194d84 --- /dev/null +++ b/node_modules/discord.js/src/managers/ReactionManager.js @@ -0,0 +1,69 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const MessageReaction = require('../structures/MessageReaction'); + +/** + * Manages API methods for reactions and holds their cache. + * @extends {BaseManager} + */ +class ReactionManager extends BaseManager { + constructor(message, iterable) { + super(message.client, iterable, MessageReaction); + + /** + * The message that this manager belongs to + * @type {Message} + */ + this.message = message; + } + + add(data, cache) { + return super.add(data, cache, { id: data.emoji.id || data.emoji.name, extras: [this.message] }); + } + + /** + * The reaction cache of this manager + * @type {Collection} + * @name ReactionManager#cache + */ + + /** + * Data that can be resolved to a MessageReaction object. This can be: + * * A MessageReaction + * * A Snowflake + * @typedef {MessageReaction|Snowflake} MessageReactionResolvable + */ + + /** + * Resolves a MessageReactionResolvable to a MessageReaction object. + * @method resolve + * @memberof ReactionManager + * @instance + * @param {MessageReactionResolvable} reaction The MessageReaction to resolve + * @returns {?MessageReaction} + */ + + /** + * Resolves a MessageReactionResolvable to a MessageReaction ID string. + * @method resolveID + * @memberof ReactionManager + * @instance + * @param {MessageReactionResolvable} reaction The MessageReaction to resolve + * @returns {?Snowflake} + */ + + /** + * Removes all reactions from a message. + * @returns {Promise} + */ + removeAll() { + return this.client.api + .channels(this.message.channel.id) + .messages(this.message.id) + .reactions.delete() + .then(() => this.message); + } +} + +module.exports = ReactionManager; diff --git a/node_modules/discord.js/src/managers/ReactionUserManager.js b/node_modules/discord.js/src/managers/ReactionUserManager.js new file mode 100644 index 0000000..f98a236 --- /dev/null +++ b/node_modules/discord.js/src/managers/ReactionUserManager.js @@ -0,0 +1,66 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Error } = require('../errors'); +const Collection = require('../util/Collection'); + +/** + * Manages API methods for users who reacted to a reaction and stores their cache. + * @extends {BaseManager} + */ +class ReactionUserManager extends BaseManager { + constructor(client, iterable, reaction) { + super(client, iterable, { name: 'User' }); + /** + * The reaction that this manager belongs to + * @type {MessageReaction} + */ + this.reaction = reaction; + } + + /** + * The cache of this manager + * @type {Collection} + * @name ReactionUserManager#cache + */ + + /** + * Fetches all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs. + * @param {Object} [options] Options for fetching the users + * @param {number} [options.limit=100] The maximum amount of users to fetch, defaults to 100 + * @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>} + */ + async fetch({ limit = 100, after, before } = {}) { + const message = this.reaction.message; + const data = await this.client.api.channels[message.channel.id].messages[message.id].reactions[ + this.reaction.emoji.identifier + ].get({ query: { limit, before, after } }); + const users = new Collection(); + for (const rawUser of data) { + const user = this.client.users.add(rawUser); + this.cache.set(user.id, user); + users.set(user.id, user); + } + return users; + } + + /** + * Removes a user from this reaction. + * @param {UserResolvable} [user=this.client.user] The user to remove the reaction of + * @returns {Promise} + */ + remove(user = this.client.user) { + const userID = this.client.users.resolveID(user); + if (!userID) return Promise.reject(new Error('REACTION_RESOLVE_USER')); + const message = this.reaction.message; + return this.client.api.channels[message.channel.id].messages[message.id].reactions[this.reaction.emoji.identifier][ + userID === this.client.user.id ? '@me' : userID + ] + .delete() + .then(() => this.reaction); + } +} + +module.exports = ReactionUserManager; diff --git a/node_modules/discord.js/src/managers/RoleManager.js b/node_modules/discord.js/src/managers/RoleManager.js new file mode 100644 index 0000000..a1586b0 --- /dev/null +++ b/node_modules/discord.js/src/managers/RoleManager.js @@ -0,0 +1,146 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Role = require('../structures/Role'); +const Permissions = require('../util/Permissions'); +const { resolveColor } = require('../util/Util'); + +/** + * Manages API methods for roles and stores their cache. + * @extends {BaseManager} + */ +class RoleManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, Role); + /** + * The guild belonging to this manager + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The role cache of this manager + * @type {Collection} + * @name RoleManager#cache + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.guild] }); + } + + /** + * Obtains one or more roles from Discord, or the role cache if they're already available. + * @param {Snowflake} [id] ID or IDs of the role(s) + * @param {boolean} [cache=true] Whether to cache the new roles objects if it weren't already + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + * @example + * // Fetch all roles from the guild + * message.guild.roles.fetch() + * .then(roles => console.log(`There are ${roles.cache.size} roles.`)) + * .catch(console.error); + * @example + * // Fetch a single role + * message.guild.roles.fetch('222078108977594368') + * .then(role => console.log(`The role color is: ${role.color}`)) + * .catch(console.error); + */ + async fetch(id, cache = true, force = false) { + if (id && !force) { + const existing = this.cache.get(id); + if (existing) return existing; + } + + // We cannot fetch a single role, as of this commit's date, Discord API throws with 405 + const roles = await this.client.api.guilds(this.guild.id).roles.get(); + for (const role of roles) this.add(role, cache); + return id ? this.cache.get(id) || null : this; + } + + /** + * 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. + * @method resolve + * @memberof RoleManager + * @instance + * @param {RoleResolvable} role The role resolvable to resolve + * @returns {?Role} + */ + + /** + * Resolves a RoleResolvable to a role ID string. + * @method resolveID + * @memberof RoleManager + * @instance + * @param {RoleResolvable} role The role resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Creates a new role in the guild with given information. + * The position will silently reset to 1 if an invalid one is provided, or none. + * @param {Object} [options] Options + * @param {RoleData} [options.data] The data to create the role with + * @param {string} [options.reason] Reason for creating this role + * @returns {Promise} + * @example + * // Create a new role + * guild.roles.create() + * .then(console.log) + * .catch(console.error); + * @example + * // Create a new role with data and a reason + * guild.roles.create({ + * data: { + * name: 'Super Cool People', + * color: 'BLUE', + * }, + * reason: 'we needed a role for Super Cool People', + * }) + * .then(console.log) + * .catch(console.error); + */ + create({ data = {}, reason } = {}) { + if (data.color) data.color = resolveColor(data.color); + if (data.permissions) data.permissions = Permissions.resolve(data.permissions); + + return this.guild.client.api + .guilds(this.guild.id) + .roles.post({ data, reason }) + .then(r => { + const { role } = this.client.actions.GuildRoleCreate.handle({ + guild_id: this.guild.id, + role: r, + }); + if (data.position) return role.setPosition(data.position, reason); + return role; + }); + } + + /** + * The `@everyone` role of the guild + * @type {Role} + * @readonly + */ + get everyone() { + return this.cache.get(this.guild.id); + } + + /** + * The role with the highest position in the cache + * @type {Role} + * @readonly + */ + get highest() { + return this.cache.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this.cache.first()); + } +} + +module.exports = RoleManager; diff --git a/node_modules/discord.js/src/managers/UserManager.js b/node_modules/discord.js/src/managers/UserManager.js new file mode 100644 index 0000000..8928715 --- /dev/null +++ b/node_modules/discord.js/src/managers/UserManager.js @@ -0,0 +1,72 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const GuildMember = require('../structures/GuildMember'); +const Message = require('../structures/Message'); +const User = require('../structures/User'); + +/** + * Manages API methods for users and stores their cache. + * @extends {BaseManager} + */ +class UserManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, User); + } + + /** + * The cache of this manager + * @type {Collection} + * @name UserManager#cache + */ + + /** + * 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 GuildMember object + * @typedef {User|Snowflake|Message|GuildMember} UserResolvable + */ + + /** + * Resolves a UserResolvable to a User object. + * @param {UserResolvable} user The UserResolvable to identify + * @returns {?User} + */ + resolve(user) { + if (user instanceof GuildMember) return user.user; + if (user instanceof Message) return user.author; + return super.resolve(user); + } + + /** + * Resolves a UserResolvable to a user ID string. + * @param {UserResolvable} user The UserResolvable to identify + * @returns {?Snowflake} + */ + resolveID(user) { + if (user instanceof GuildMember) return user.user.id; + if (user instanceof Message) return user.author.id; + return super.resolveID(user); + } + + /** + * Obtains a user from Discord, or the user cache if it's already available. + * @param {Snowflake} id ID of the user + * @param {boolean} [cache=true] Whether to cache the new user object if it isn't already + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + */ + async fetch(id, cache = true, force = false) { + if (!force) { + const existing = this.cache.get(id); + if (existing && !existing.partial) return existing; + } + + const data = await this.client.api.users(id).get(); + return this.add(data, cache); + } +} + +module.exports = UserManager; diff --git a/node_modules/discord.js/src/managers/VoiceStateManager.js b/node_modules/discord.js/src/managers/VoiceStateManager.js new file mode 100644 index 0000000..35045f7 --- /dev/null +++ b/node_modules/discord.js/src/managers/VoiceStateManager.js @@ -0,0 +1,35 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); + +/** + * Manages API methods for VoiceStates and stores their cache. + * @extends {BaseManager} + */ +class VoiceStateManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, { name: 'VoiceState' }); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this manager + * @type {Collection} + * @name VoiceStateManager#cache + */ + + add(data, cache = true) { + const existing = this.cache.get(data.user_id); + if (existing) return existing._patch(data); + + const entry = new this.holds(this.guild, data); + if (cache) this.cache.set(data.user_id, entry); + return entry; + } +} + +module.exports = VoiceStateManager; diff --git a/node_modules/discord.js/src/rest/APIRequest.js b/node_modules/discord.js/src/rest/APIRequest.js new file mode 100644 index 0000000..837b38f --- /dev/null +++ b/node_modules/discord.js/src/rest/APIRequest.js @@ -0,0 +1,67 @@ +'use strict'; + +const https = require('https'); +const FormData = require('@discordjs/form-data'); +const AbortController = require('abort-controller'); +const fetch = require('node-fetch'); +const { browser, UserAgent } = require('../util/Constants'); + +if (https.Agent) var agent = new https.Agent({ keepAlive: true }); + +class APIRequest { + constructor(rest, method, path, options) { + this.rest = rest; + this.client = rest.client; + this.method = method; + this.route = options.route; + this.options = options; + this.retries = 0; + + let queryString = ''; + if (options.query) { + const query = Object.entries(options.query) + .filter(([, value]) => ![null, 'null', 'undefined'].includes(value) && typeof value !== 'undefined') + .flatMap(([key, value]) => (Array.isArray(value) ? value.map(v => [key, v]) : [[key, value]])); + queryString = new URLSearchParams(query).toString(); + } + this.path = `${path}${queryString && `?${queryString}`}`; + } + + make() { + const API = + this.options.versioned === false + ? this.client.options.http.api + : `${this.client.options.http.api}/v${this.client.options.http.version}`; + const url = API + this.path; + let headers = {}; + + if (this.options.auth !== false) headers.Authorization = this.rest.getAuth(); + if (this.options.reason) headers['X-Audit-Log-Reason'] = encodeURIComponent(this.options.reason); + if (!browser) headers['User-Agent'] = UserAgent; + if (this.options.headers) headers = Object.assign(headers, this.options.headers); + + let body; + if (this.options.files && this.options.files.length) { + body = new FormData(); + for (const file of this.options.files) if (file && file.file) body.append(file.name, file.file, file.name); + if (typeof this.options.data !== 'undefined') body.append('payload_json', JSON.stringify(this.options.data)); + if (!browser) headers = Object.assign(headers, body.getHeaders()); + // eslint-disable-next-line eqeqeq + } else if (this.options.data != null) { + body = JSON.stringify(this.options.data); + headers['Content-Type'] = 'application/json'; + } + + const controller = new AbortController(); + const timeout = this.client.setTimeout(() => controller.abort(), this.client.options.restRequestTimeout); + return fetch(url, { + method: this.method, + headers, + agent, + body, + signal: controller.signal, + }).finally(() => this.client.clearTimeout(timeout)); + } +} + +module.exports = APIRequest; diff --git a/node_modules/discord.js/src/rest/APIRouter.js b/node_modules/discord.js/src/rest/APIRouter.js new file mode 100644 index 0000000..e85c739 --- /dev/null +++ b/node_modules/discord.js/src/rest/APIRouter.js @@ -0,0 +1,53 @@ +'use strict'; + +const noop = () => {}; // eslint-disable-line no-empty-function +const methods = ['get', 'post', 'delete', 'patch', 'put']; +const reflectors = [ + 'toString', + 'valueOf', + 'inspect', + 'constructor', + Symbol.toPrimitive, + Symbol.for('nodejs.util.inspect.custom'), +]; + +function buildRoute(manager) { + const route = ['']; + const handler = { + get(target, name) { + if (reflectors.includes(name)) return () => route.join('/'); + if (methods.includes(name)) { + const routeBucket = []; + for (let i = 0; i < route.length; i++) { + // Reactions routes and sub-routes all share the same bucket + if (route[i - 1] === 'reactions') break; + // Literal IDs should only be taken account if they are the Major ID (the Channel/Guild ID) + if (/\d{16,19}/g.test(route[i]) && !/channels|guilds/.test(route[i - 1])) routeBucket.push(':id'); + // All other parts of the route should be considered as part of the bucket identifier + else routeBucket.push(route[i]); + } + return options => + manager.request( + name, + route.join('/'), + Object.assign( + { + versioned: manager.versioned, + route: routeBucket.join('/'), + }, + options, + ), + ); + } + route.push(name); + return new Proxy(noop, handler); + }, + apply(target, _, args) { + route.push(...args.filter(x => x != null)); // eslint-disable-line eqeqeq + return new Proxy(noop, handler); + }, + }; + return new Proxy(noop, handler); +} + +module.exports = buildRoute; diff --git a/node_modules/discord.js/src/rest/AsyncQueue.js b/node_modules/discord.js/src/rest/AsyncQueue.js new file mode 100644 index 0000000..b465f6a --- /dev/null +++ b/node_modules/discord.js/src/rest/AsyncQueue.js @@ -0,0 +1,95 @@ +/** + * MIT License + * + * Copyright (c) 2020 kyranet, discord.js + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict'; + +// TODO(kyranet, vladfrangu): replace this with discord.js v13's core AsyncQueue. + +/** + * An async queue that preserves the stack and prevents lock-ups. + * @private + */ +class AsyncQueue { + constructor() { + /** + * The promises array. + * @type {Array<{promise: Promise, resolve: Function}>} + * @private + */ + this.promises = []; + } + + /** + * The remaining amount of queued promises + * @type {number} + */ + get remaining() { + return this.promises.length; + } + + /** + * Waits for last promise and queues a new one. + * @returns {Promise} + * @example + * const queue = new AsyncQueue(); + * async function request(url, options) { + * await queue.wait(); + * try { + * const result = await fetch(url, options); + * // Do some operations with 'result' + * } finally { + * // Remove first entry from the queue and resolve for the next entry + * queue.shift(); + * } + * } + * + * request(someUrl1, someOptions1); // Will call fetch() immediately + * request(someUrl2, someOptions2); // Will call fetch() after the first finished + * request(someUrl3, someOptions3); // Will call fetch() after the second finished + */ + wait() { + const next = this.promises.length ? this.promises[this.promises.length - 1].promise : Promise.resolve(); + let resolve; + const promise = new Promise(res => { + resolve = res; + }); + + this.promises.push({ + resolve, + promise, + }); + + return next; + } + + /** + * Frees the queue's lock for the next item to process. + */ + shift() { + const deferred = this.promises.shift(); + if (typeof deferred !== 'undefined') deferred.resolve(); + } +} + +module.exports = AsyncQueue; diff --git a/node_modules/discord.js/src/rest/DiscordAPIError.js b/node_modules/discord.js/src/rest/DiscordAPIError.js new file mode 100644 index 0000000..aebe513 --- /dev/null +++ b/node_modules/discord.js/src/rest/DiscordAPIError.js @@ -0,0 +1,68 @@ +'use strict'; + +/** + * Represents an error from the Discord API. + * @extends Error + */ +class DiscordAPIError extends Error { + constructor(path, error, method, status) { + 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 HTTP method used for the request + * @type {string} + */ + this.method = method; + + /** + * 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 status code + * @type {number} + */ + this.httpStatus = status; + } + + /** + * 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, v] of Object.entries(obj)) { + if (k === 'message') continue; + const newKey = key ? (isNaN(k) ? `${key}.${k}` : `${key}[${k}]`) : k; + + if (v._errors) { + messages.push(`${newKey}: ${v._errors.map(e => e.message).join(' ')}`); + } else if (v.code || v.message) { + messages.push(`${v.code ? `${v.code}: ` : ''}${v.message}`.trim()); + } else if (typeof v === 'string') { + messages.push(v); + } else { + messages = messages.concat(this.flattenErrors(v, newKey)); + } + } + + return messages; + } +} + +module.exports = DiscordAPIError; diff --git a/node_modules/discord.js/src/rest/HTTPError.js b/node_modules/discord.js/src/rest/HTTPError.js new file mode 100644 index 0000000..2911467 --- /dev/null +++ b/node_modules/discord.js/src/rest/HTTPError.js @@ -0,0 +1,37 @@ +'use strict'; + +/** + * Represents a HTTP error from a request. + * @extends Error + */ +class HTTPError extends Error { + constructor(message, name, code, method, path) { + super(message); + + /** + * The name of the error + * @type {string} + */ + this.name = name; + + /** + * HTTP error code returned from the request + * @type {number} + */ + this.code = code || 500; + + /** + * The HTTP method used for the request + * @type {string} + */ + this.method = method; + + /** + * The path of the request relative to the HTTP endpoint + * @type {string} + */ + this.path = path; + } +} + +module.exports = HTTPError; diff --git a/node_modules/discord.js/src/rest/RESTManager.js b/node_modules/discord.js/src/rest/RESTManager.js new file mode 100644 index 0000000..799f602 --- /dev/null +++ b/node_modules/discord.js/src/rest/RESTManager.js @@ -0,0 +1,59 @@ +'use strict'; + +const APIRequest = require('./APIRequest'); +const routeBuilder = require('./APIRouter'); +const RequestHandler = require('./RequestHandler'); +const { Error } = require('../errors'); +const Collection = require('../util/Collection'); +const { Endpoints } = require('../util/Constants'); + +class RESTManager { + constructor(client, tokenPrefix = 'Bot') { + this.client = client; + this.handlers = new Collection(); + this.tokenPrefix = tokenPrefix; + this.versioned = true; + this.globalTimeout = null; + if (client.options.restSweepInterval > 0) { + client.setInterval(() => { + this.handlers.sweep(handler => handler._inactive); + }, client.options.restSweepInterval * 1000); + } + } + + get api() { + return routeBuilder(this); + } + + getAuth() { + const token = this.client.token || this.client.accessToken; + if (token) return `${this.tokenPrefix} ${token}`; + throw new Error('TOKEN_MISSING'); + } + + get cdn() { + return Endpoints.CDN(this.client.options.http.cdn); + } + + request(method, url, options = {}) { + const apiRequest = new APIRequest(this, method, url, options); + let handler = this.handlers.get(apiRequest.route); + + if (!handler) { + handler = new RequestHandler(this); + this.handlers.set(apiRequest.route, handler); + } + + return handler.push(apiRequest); + } + + get endpoint() { + return this.client.options.http.api; + } + + set endpoint(endpoint) { + this.client.options.http.api = endpoint; + } +} + +module.exports = RESTManager; diff --git a/node_modules/discord.js/src/rest/RequestHandler.js b/node_modules/discord.js/src/rest/RequestHandler.js new file mode 100644 index 0000000..b15b2fa --- /dev/null +++ b/node_modules/discord.js/src/rest/RequestHandler.js @@ -0,0 +1,173 @@ +'use strict'; + +const AsyncQueue = require('./AsyncQueue'); +const DiscordAPIError = require('./DiscordAPIError'); +const HTTPError = require('./HTTPError'); +const { + Events: { RATE_LIMIT }, + browser, +} = require('../util/Constants'); +const Util = require('../util/Util'); + +function parseResponse(res) { + if (res.headers.get('content-type').startsWith('application/json')) return res.json(); + if (browser) return res.blob(); + return res.buffer(); +} + +function getAPIOffset(serverDate) { + return new Date(serverDate).getTime() - Date.now(); +} + +function calculateReset(reset, serverDate) { + return new Date(Number(reset) * 1000).getTime() - getAPIOffset(serverDate); +} + +class RequestHandler { + constructor(manager) { + this.manager = manager; + this.queue = new AsyncQueue(); + this.reset = -1; + this.remaining = -1; + this.limit = -1; + this.retryAfter = -1; + } + + async push(request) { + await this.queue.wait(); + try { + return await this.execute(request); + } finally { + this.queue.shift(); + } + } + + get limited() { + return Boolean(this.manager.globalTimeout) || (this.remaining <= 0 && Date.now() < this.reset); + } + + get _inactive() { + return this.queue.remaining === 0 && !this.limited; + } + + async execute(request) { + // After calculations and requests have been done, pre-emptively stop further requests + if (this.limited) { + const timeout = this.reset + this.manager.client.options.restTimeOffset - Date.now(); + + if (this.manager.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.timeout Timeout in ms + * @param {number} rateLimitInfo.limit Number of requests that can be made to this endpoint + * @param {string} rateLimitInfo.method HTTP method used for request that triggered this event + * @param {string} rateLimitInfo.path Path used for request that triggered this event + * @param {string} rateLimitInfo.route Route used for request that triggered this event + */ + this.manager.client.emit(RATE_LIMIT, { + timeout, + limit: this.limit, + method: request.method, + path: request.path, + route: request.route, + }); + } + + if (this.manager.globalTimeout) { + await this.manager.globalTimeout; + } else { + // Wait for the timeout to expire in order to avoid an actual 429 + await Util.delayFor(timeout); + } + } + + // Perform the request + let res; + try { + res = await request.make(); + } catch (error) { + // Retry the specified number of times for request abortions + if (request.retries === this.manager.client.options.retryLimit) { + throw new HTTPError(error.message, error.constructor.name, error.status, request.method, request.path); + } + + request.retries++; + return this.execute(request); + } + + if (res && res.headers) { + const serverDate = res.headers.get('date'); + const limit = res.headers.get('x-ratelimit-limit'); + const remaining = res.headers.get('x-ratelimit-remaining'); + const reset = res.headers.get('x-ratelimit-reset'); + const retryAfter = res.headers.get('retry-after'); + + this.limit = limit ? Number(limit) : Infinity; + this.remaining = remaining ? Number(remaining) : 1; + this.reset = reset ? calculateReset(reset, serverDate) : Date.now(); + this.retryAfter = retryAfter ? Number(retryAfter) : -1; + + // https://github.com/discordapp/discord-api-docs/issues/182 + if (request.route.includes('reactions')) { + this.reset = new Date(serverDate).getTime() - getAPIOffset(serverDate) + 250; + } + + // Handle global ratelimit + if (res.headers.get('x-ratelimit-global')) { + // Set the manager's global timeout as the promise for other requests to "wait" + this.manager.globalTimeout = Util.delayFor(this.retryAfter); + + // Wait for the global timeout to resolve before continuing + await this.manager.globalTimeout; + + // Clean up global timeout + this.manager.globalTimeout = null; + } + } + + // Handle 2xx and 3xx responses + if (res.ok) { + // Nothing wrong with the request, proceed with the next one + return parseResponse(res); + } + + // Handle 4xx responses + if (res.status >= 400 && res.status < 500) { + // Handle ratelimited requests + if (res.status === 429) { + // A ratelimit was hit - this should never happen + this.manager.client.emit('debug', `429 hit on route ${request.route}`); + await Util.delayFor(this.retryAfter); + return this.execute(request); + } + + // Handle possible malformed requests + let data; + try { + data = await parseResponse(res); + } catch (err) { + throw new HTTPError(err.message, err.constructor.name, err.status, request.method, request.path); + } + + throw new DiscordAPIError(request.path, data, request.method, res.status); + } + + // Handle 5xx responses + if (res.status >= 500 && res.status < 600) { + // Retry the specified number of times for possible serverside issues + if (request.retries === this.manager.client.options.retryLimit) { + throw new HTTPError(res.statusText, res.constructor.name, res.status, request.method, request.path); + } + + request.retries++; + return this.execute(request); + } + + // Fallback in the rare case a status code outside the range 200..=599 is returned + return null; + } +} + +module.exports = RequestHandler; 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..75b17c8 --- /dev/null +++ b/node_modules/discord.js/src/sharding/Shard.js @@ -0,0 +1,394 @@ +'use strict'; + +const EventEmitter = require('events'); +const path = require('path'); +const { Error } = require('../errors'); +const Util = require('../util/Util'); +let childProcess = null; +let Worker = null; + +/** + * A self-contained shard created by the {@link ShardingManager}. Each one has a {@link ChildProcess} that contains + * an instance of the bot and its {@link Client}. When its child process/worker exits for any reason, the shard will + * spawn a new one to replace it as necessary. + * @extends EventEmitter + */ +class Shard extends EventEmitter { + /** + * @param {ShardingManager} manager Manager that is creating this shard + * @param {number} id ID of this shard + */ + constructor(manager, id) { + super(); + + if (manager.mode === 'process') childProcess = require('child_process'); + else if (manager.mode === 'worker') Worker = require('worker_threads').Worker; + + /** + * Manager that created the shard + * @type {ShardingManager} + */ + this.manager = manager; + + /** + * ID of the shard in the manager + * @type {number} + */ + this.id = id; + + /** + * Arguments for the shard's process (only when {@link ShardingManager#mode} is `process`) + * @type {string[]} + */ + this.args = manager.shardArgs || []; + + /** + * Arguments for the shard's process executable (only when {@link ShardingManager#mode} is `process`) + * @type {string[]} + */ + this.execArgv = manager.execArgv; + + /** + * Environment variables for the shard's process, or workerData for the shard's worker + * @type {Object} + */ + this.env = Object.assign({}, process.env, { + SHARDING_MANAGER: true, + SHARDS: this.id, + SHARD_COUNT: this.manager.totalShards, + DISCORD_TOKEN: this.manager.token, + }); + + /** + * Whether the shard's {@link Client} is ready + * @type {boolean} + */ + this.ready = false; + + /** + * Process of the shard (if {@link ShardingManager#mode} is `process`) + * @type {?ChildProcess} + */ + this.process = null; + + /** + * Worker of the shard (if {@link ShardingManager#mode} is `worker`) + * @type {?Worker} + */ + this.worker = null; + + /** + * Ongoing promises for calls to {@link Shard#eval}, mapped by the `script` they were called with + * @type {Map} + * @private + */ + this._evals = new Map(); + + /** + * Ongoing promises for calls to {@link Shard#fetchClientValue}, mapped by the `prop` they were called with + * @type {Map} + * @private + */ + this._fetches = new Map(); + + /** + * Listener function for the {@link ChildProcess}' `exit` event + * @type {Function} + * @private + */ + this._exitListener = this._handleExit.bind(this); + } + + /** + * Forks a child process or creates a worker thread for the shard. + * You should not need to call this manually. + * @param {number} [spawnTimeout=30000] The amount in milliseconds to wait until the {@link Client} has become ready + * before resolving. (-1 or Infinity for no wait) + * @returns {Promise} + */ + async spawn(spawnTimeout = 30000) { + if (this.process) throw new Error('SHARDING_PROCESS_EXISTS', this.id); + if (this.worker) throw new Error('SHARDING_WORKER_EXISTS', this.id); + + if (this.manager.mode === 'process') { + this.process = childProcess + .fork(path.resolve(this.manager.file), this.args, { + env: this.env, + execArgv: this.execArgv, + }) + .on('message', this._handleMessage.bind(this)) + .on('exit', this._exitListener); + } else if (this.manager.mode === 'worker') { + this.worker = new Worker(path.resolve(this.manager.file), { workerData: this.env }) + .on('message', this._handleMessage.bind(this)) + .on('exit', this._exitListener); + } + + this._evals.clear(); + this._fetches.clear(); + + /** + * Emitted upon the creation of the shard's child process/worker. + * @event Shard#spawn + * @param {ChildProcess|Worker} process Child process/worker that was created + */ + this.emit('spawn', this.process || this.worker); + + if (spawnTimeout === -1 || spawnTimeout === Infinity) return this.process || this.worker; + await new Promise((resolve, reject) => { + const cleanup = () => { + clearTimeout(spawnTimeoutTimer); + this.off('ready', onReady); + this.off('disconnect', onDisconnect); + this.off('death', onDeath); + }; + + const onReady = () => { + cleanup(); + resolve(); + }; + + const onDisconnect = () => { + cleanup(); + reject(new Error('SHARDING_READY_DISCONNECTED', this.id)); + }; + + const onDeath = () => { + cleanup(); + reject(new Error('SHARDING_READY_DIED', this.id)); + }; + + const onTimeout = () => { + cleanup(); + reject(new Error('SHARDING_READY_TIMEOUT', this.id)); + }; + + const spawnTimeoutTimer = setTimeout(onTimeout, spawnTimeout); + this.once('ready', onReady); + this.once('disconnect', onDisconnect); + this.once('death', onDeath); + }); + return this.process || this.worker; + } + + /** + * Immediately kills the shard's process/worker and does not restart it. + */ + kill() { + if (this.process) { + this.process.removeListener('exit', this._exitListener); + this.process.kill(); + } else { + this.worker.removeListener('exit', this._exitListener); + this.worker.terminate(); + } + + this._handleExit(false); + } + + /** + * Kills and restarts the shard's process/worker. + * @param {number} [delay=500] How long to wait between killing the process/worker and restarting it (in milliseconds) + * @param {number} [spawnTimeout=30000] The amount in milliseconds to wait until the {@link Client} has become ready + * before resolving. (-1 or Infinity for no wait) + * @returns {Promise} + */ + async respawn(delay = 500, spawnTimeout) { + this.kill(); + if (delay > 0) await Util.delayFor(delay); + return this.spawn(spawnTimeout); + } + + /** + * Sends a message to the shard's process/worker. + * @param {*} message Message to send to the shard + * @returns {Promise} + */ + send(message) { + return new Promise((resolve, reject) => { + if (this.process) { + this.process.send(message, err => { + if (err) reject(err); + else resolve(this); + }); + } else { + this.worker.postMessage(message); + 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.cache.size') + * .then(count => console.log(`${count} guilds in shard ${shard.id}`)) + * .catch(console.error); + */ + fetchClientValue(prop) { + // Shard is dead (maybe respawning), don't cache anything and error immediately + if (!this.process && !this.worker) return Promise.reject(new Error('SHARDING_NO_CHILD_EXISTS', this.id)); + + // Cached promise from previous call + if (this._fetches.has(prop)) return this._fetches.get(prop); + + const promise = new Promise((resolve, reject) => { + const child = this.process || this.worker; + + const listener = message => { + if (!message || message._fetchProp !== prop) return; + child.removeListener('message', listener); + this._fetches.delete(prop); + resolve(message._result); + }; + child.on('message', listener); + + this.send({ _fetchProp: prop }).catch(err => { + child.removeListener('message', listener); + this._fetches.delete(prop); + reject(err); + }); + }); + + this._fetches.set(prop, promise); + return promise; + } + + /** + * Evaluates a script or function on the shard, in the context of the {@link Client}. + * @param {string|Function} script JavaScript to run on the shard + * @returns {Promise<*>} Result of the script execution + */ + eval(script) { + // Shard is dead (maybe respawning), don't cache anything and error immediately + if (!this.process && !this.worker) return Promise.reject(new Error('SHARDING_NO_CHILD_EXISTS', this.id)); + + // Cached promise from previous call + if (this._evals.has(script)) return this._evals.get(script); + + const promise = new Promise((resolve, reject) => { + const child = this.process || this.worker; + + const listener = message => { + if (!message || message._eval !== script) return; + child.removeListener('message', listener); + this._evals.delete(script); + if (!message._error) resolve(message._result); + else reject(Util.makeError(message._error)); + }; + child.on('message', listener); + + const _eval = typeof script === 'function' ? `(${script})(this)` : script; + this.send({ _eval }).catch(err => { + child.removeListener('message', listener); + this._evals.delete(script); + reject(err); + }); + }); + + this._evals.set(script, promise); + return promise; + } + + /** + * Handles a message received from the child process/worker. + * @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; + } + + // Shard is requesting a respawn of all shards + if (message._sRespawnAll) { + const { shardDelay, respawnDelay, spawnTimeout } = message._sRespawnAll; + this.manager.respawnAll(shardDelay, respawnDelay, spawnTimeout).catch(() => { + // Do nothing + }); + return; + } + } + + /** + * Emitted upon receiving a message from the child process/worker. + * @event Shard#message + * @param {*} message Message that was received + */ + this.emit('message', message); + } + + /** + * Handles the shard's process/worker 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/worker exiting. + * @event Shard#death + * @param {ChildProcess|Worker} process Child process/worker that exited + */ + this.emit('death', this.process || this.worker); + + this.ready = false; + this.process = null; + this.worker = null; + this._evals.clear(); + this._fetches.clear(); + + if (respawn) this.spawn().catch(err => this.emit('error', err)); + } +} + +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..292a033 --- /dev/null +++ b/node_modules/discord.js/src/sharding/ShardClientUtil.js @@ -0,0 +1,229 @@ +'use strict'; + +const { Events } = require('../util/Constants'); +const Util = require('../util/Util'); + +/** + * Helper class for sharded clients spawned as a child process/worker, such as from a {@link ShardingManager}. + * Utilises IPC to send and receive data to/from the master process and other shards. + */ +class ShardClientUtil { + /** + * @param {Client} client Client of the current shard + * @param {ShardingManagerMode} mode Mode the shard was spawned with + */ + constructor(client, mode) { + /** + * Client for the shard + * @type {Client} + */ + this.client = client; + + /** + * Mode the shard was spawned with + * @type {ShardingManagerMode} + */ + this.mode = mode; + + /** + * Message port for the master process (only when {@link ShardClientUtil#mode} is `worker`) + * @type {?MessagePort} + */ + this.parentPort = null; + + if (mode === 'process') { + 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 }); + }); + } else if (mode === 'worker') { + this.parentPort = require('worker_threads').parentPort; + this.parentPort.on('message', this._handleMessage.bind(this)); + client.on('ready', () => { + this.parentPort.postMessage({ _ready: true }); + }); + client.on('disconnect', () => { + this.parentPort.postMessage({ _disconnect: true }); + }); + client.on('reconnecting', () => { + this.parentPort.postMessage({ _reconnecting: true }); + }); + } + } + + /** + * Array of shard IDs of this client + * @type {number[]} + * @readonly + */ + get ids() { + return this.client.options.shards; + } + + /** + * 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} + * @emits Shard#message + */ + send(message) { + return new Promise((resolve, reject) => { + if (this.mode === 'process') { + process.send(message, err => { + if (err) reject(err); + else resolve(); + }); + } else if (this.mode === 'worker') { + this.parentPort.postMessage(message); + 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>} + * @example + * client.shard.fetchClientValues('guilds.cache.size') + * .then(results => console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`)) + * .catch(console.error); + * @see {@link ShardingManager#fetchClientValues} + */ + fetchClientValues(prop) { + return new Promise((resolve, reject) => { + const parent = this.parentPort || process; + + const listener = message => { + if (!message || message._sFetchProp !== prop) return; + parent.removeListener('message', listener); + if (!message._error) resolve(message._result); + else reject(Util.makeError(message._error)); + }; + parent.on('message', listener); + + this.send({ _sFetchProp: prop }).catch(err => { + parent.removeListener('message', listener); + reject(err); + }); + }); + } + + /** + * Evaluates a script or function on all shards, in the context of the {@link Client}s. + * @param {string|Function} script JavaScript to run on each shard + * @returns {Promise>} Results of the script execution + * @example + * client.shard.broadcastEval('this.guilds.cache.size') + * .then(results => console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`)) + * .catch(console.error); + * @see {@link ShardingManager#broadcastEval} + */ + broadcastEval(script) { + return new Promise((resolve, reject) => { + const parent = this.parentPort || process; + script = typeof script === 'function' ? `(${script})(this)` : script; + + const listener = message => { + if (!message || message._sEval !== script) return; + parent.removeListener('message', listener); + if (!message._error) resolve(message._result); + else reject(Util.makeError(message._error)); + }; + parent.on('message', listener); + + this.send({ _sEval: script }).catch(err => { + parent.removeListener('message', listener); + reject(err); + }); + }); + } + + /** + * Requests a respawn of all shards. + * @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/worker and restarting it + * (in milliseconds) + * @param {number} [spawnTimeout=30000] The amount in milliseconds to wait for a shard to become ready before + * continuing to another. (-1 or Infinity for no wait) + * @returns {Promise} Resolves upon the message being sent + * @see {@link ShardingManager#respawnAll} + */ + respawnAll(shardDelay = 5000, respawnDelay = 500, spawnTimeout = 30000) { + return this.send({ _sRespawnAll: { shardDelay, respawnDelay, spawnTimeout } }); + } + + /** + * Handles an IPC message. + * @param {*} message Message received + * @private + */ + async _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: await 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}`; + /** + * Emitted when the client encounters an error. + * @event Client#error + * @param {Error} error The error encountered + */ + this.client.emit(Events.ERROR, err); + }); + } + + /** + * Creates/gets the singleton of this class. + * @param {Client} client The client to use + * @param {ShardingManagerMode} mode Mode the shard was spawned with + * @returns {ShardClientUtil} + */ + static singleton(client, mode) { + if (!this._singleton) { + this._singleton = new this(client, mode); + } else { + client.emit( + Events.WARN, + 'Multiple clients created in child process/worker; 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..05251d6 --- /dev/null +++ b/node_modules/discord.js/src/sharding/ShardingManager.js @@ -0,0 +1,272 @@ +'use strict'; + +const EventEmitter = require('events'); +const fs = require('fs'); +const path = require('path'); +const Shard = require('./Shard'); +const { Error, TypeError, RangeError } = require('../errors'); +const Collection = require('../util/Collection'); +const Util = require('../util/Util'); + +/** + * This is a utility class that makes multi-process sharding of a bot an easy and painless experience. + * It works by spawning a self-contained {@link ChildProcess} or {@link Worker} for each individual shard, each + * containing its own instance of your bot's {@link Client}. They all have a line of communication with the master + * process, and there are several useful methods that utilise it in order to simplify tasks that are normally difficult + * with sharding. It can spawn a specific number of shards or the amount that Discord suggests for the bot, and takes a + * path to your main bot script to launch for each one. + * @extends {EventEmitter} + */ +class ShardingManager extends EventEmitter { + /** + * The mode to spawn shards with for a {@link ShardingManager}: either "process" to use child processes, or + * "worker" to use [Worker threads](https://nodejs.org/api/worker_threads.html). + * @typedef {Object} ShardingManagerMode + */ + + /** + * @param {string} file Path to your shard script file + * @param {Object} [options] Options for the sharding manager + * @param {string|number} [options.totalShards='auto'] Number of total shards of all shard managers or "auto" + * @param {string|number[]} [options.shardList='auto'] List of shards to spawn or "auto" + * @param {ShardingManagerMode} [options.mode='process'] Which mode to use for shards + * @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 + * (only available when using the `process` mode) + * @param {string[]} [options.execArgv=[]] Arguments to pass to the shard script executable when spawning + * (only available when using the `process` mode) + * @param {string} [options.token] Token to use for automatic shard count and passing to shards + */ + constructor(file, options = {}) { + super(); + options = Util.mergeDefault( + { + totalShards: 'auto', + mode: 'process', + respawn: true, + shardArgs: [], + execArgv: [], + token: process.env.DISCORD_TOKEN, + }, + options, + ); + + /** + * Path to the shard script file + * @type {string} + */ + this.file = file; + if (!file) throw new Error('CLIENT_INVALID_OPTION', 'File', 'specified.'); + if (!path.isAbsolute(file)) this.file = path.resolve(process.cwd(), file); + const stats = fs.statSync(this.file); + if (!stats.isFile()) throw new Error('CLIENT_INVALID_OPTION', 'File', 'a file'); + + /** + * List of shards this sharding manager spawns + * @type {string|number[]} + */ + this.shardList = options.shardList || 'auto'; + if (this.shardList !== 'auto') { + if (!Array.isArray(this.shardList)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array.'); + } + this.shardList = [...new Set(this.shardList)]; + if (this.shardList.length < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'shardList', 'at least 1 ID.'); + if ( + this.shardList.some( + shardID => typeof shardID !== 'number' || isNaN(shardID) || !Number.isInteger(shardID) || shardID < 0, + ) + ) { + throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array of positive integers.'); + } + } + + /** + * Amount of shards that all sharding managers spawn in total + * @type {number} + */ + this.totalShards = options.totalShards || 'auto'; + if (this.totalShards !== 'auto') { + if (typeof this.totalShards !== 'number' || isNaN(this.totalShards)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'a number.'); + } + if (this.totalShards < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'at least 1.'); + if (!Number.isInteger(this.totalShards)) { + throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'an integer.'); + } + } + + /** + * Mode for shards to spawn with + * @type {ShardingManagerMode} + */ + this.mode = options.mode; + if (this.mode !== 'process' && this.mode !== 'worker') { + throw new RangeError('CLIENT_INVALID_OPTION', 'Sharding mode', '"process" or "worker"'); + } + + /** + * Whether shards should automatically respawn upon exiting + * @type {boolean} + */ + this.respawn = options.respawn; + + /** + * An array of arguments to pass to shards (only when {@link ShardingManager#mode} is `process`) + * @type {string[]} + */ + this.shardArgs = options.shardArgs; + + /** + * An array of arguments to pass to the executable (only when {@link ShardingManager#mode} is `process`) + * @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} + */ + this.shards = new Collection(); + + process.env.SHARDING_MANAGER = true; + process.env.SHARDING_MANAGER_MODE = this.mode; + process.env.DISCORD_TOKEN = this.token; + } + + /** + * Creates a single shard. + * Using this method is usually not necessary if you use the spawn method. + * @param {number} [id=this.shards.size] ID of the shard to create + * This is usually not necessary to manually specify. + * @returns {Shard} Note that the created shard needs to be explicitly spawned using its spawn method. + */ + createShard(id = this.shards.size) { + const shard = new Shard(this, id); + this.shards.set(id, shard); + /** + * Emitted upon creating a shard. + * @event ShardingManager#shardCreate + * @param {Shard} shard Shard that was created + */ + this.emit('shardCreate', shard); + return shard; + } + + /** + * Spawns multiple shards. + * @param {number|string} [amount=this.totalShards] Number of shards to spawn + * @param {number} [delay=5500] How long to wait in between spawning each shard (in milliseconds) + * @param {number} [spawnTimeout=30000] The amount in milliseconds to wait until the {@link Client} has become ready + * before resolving. (-1 or Infinity for no wait) + * @returns {Promise>} + */ + async spawn(amount = this.totalShards, delay = 5500, spawnTimeout) { + // Obtain/verify the number of shards to spawn + if (amount === 'auto') { + amount = await Util.fetchRecommendedShards(this.token); + } else { + if (typeof amount !== 'number' || isNaN(amount)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'a number.'); + } + if (amount < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'at least 1.'); + if (!Number.isInteger(amount)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'an integer.'); + } + } + + // Make sure this many shards haven't already been spawned + if (this.shards.size >= amount) throw new Error('SHARDING_ALREADY_SPAWNED', this.shards.size); + if (this.shardList === 'auto' || this.totalShards === 'auto' || this.totalShards !== amount) { + this.shardList = [...Array(amount).keys()]; + } + if (this.totalShards === 'auto' || this.totalShards !== amount) { + this.totalShards = amount; + } + + if (this.shardList.some(shardID => shardID >= amount)) { + throw new RangeError( + 'CLIENT_INVALID_OPTION', + 'Amount of shards', + 'bigger than the highest shardID in the shardList option.', + ); + } + + // Spawn the shards + for (const shardID of this.shardList) { + const promises = []; + const shard = this.createShard(shardID); + promises.push(shard.spawn(spawnTimeout)); + if (delay > 0 && this.shards.size !== this.shardList.length) promises.push(Util.delayFor(delay)); + await Promise.all(promises); // eslint-disable-line no-await-in-loop + } + + return this.shards; + } + + /** + * Sends a message to all shards. + * @param {*} message Message to be sent to the shards + * @returns {Promise} + */ + 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 {@link Client}s. + * @param {string} script JavaScript to run on each shard + * @returns {Promise>} 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>} + * @example + * manager.fetchClientValues('guilds.cache.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('SHARDING_NO_SHARDS')); + if (this.shards.size !== this.shardList.length) return Promise.reject(new Error('SHARDING_IN_PROCESS')); + 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 {number} [spawnTimeout=30000] The amount in milliseconds to wait for a shard to become ready before + * continuing to another. (-1 or Infinity for no wait) + * @returns {Promise>} + */ + async respawnAll(shardDelay = 5000, respawnDelay = 500, spawnTimeout) { + let s = 0; + for (const shard of this.shards.values()) { + const promises = [shard.respawn(respawnDelay, spawnTimeout)]; + if (++s < this.shards.size && shardDelay > 0) promises.push(Util.delayFor(shardDelay)); + await Promise.all(promises); // eslint-disable-line no-await-in-loop + } + return this.shards; + } +} + +module.exports = ShardingManager; diff --git a/node_modules/discord.js/src/structures/APIMessage.js b/node_modules/discord.js/src/structures/APIMessage.js new file mode 100644 index 0000000..3451bdb --- /dev/null +++ b/node_modules/discord.js/src/structures/APIMessage.js @@ -0,0 +1,396 @@ +'use strict'; + +const MessageAttachment = require('./MessageAttachment'); +const MessageEmbed = require('./MessageEmbed'); +const { RangeError } = require('../errors'); +const { browser } = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const MessageFlags = require('../util/MessageFlags'); +const Util = require('../util/Util'); + +/** + * Represents a message to be sent to the API. + */ +class APIMessage { + /** + * @param {MessageTarget} target - The target for this message to be sent to + * @param {MessageOptions|WebhookMessageOptions} options - Options passed in from send + */ + constructor(target, options) { + /** + * The target for this message to be sent to + * @type {MessageTarget} + */ + this.target = target; + + /** + * Options passed in from send + * @type {MessageOptions|WebhookMessageOptions} + */ + this.options = options; + + /** + * Data sendable to the API + * @type {?Object} + */ + this.data = null; + + /** + * Files sendable to the API + * @type {?Object[]} + */ + this.files = null; + } + + /** + * Whether or not the target is a webhook + * @type {boolean} + * @readonly + */ + get isWebhook() { + const Webhook = require('./Webhook'); + const WebhookClient = require('../client/WebhookClient'); + return this.target instanceof Webhook || this.target instanceof WebhookClient; + } + + /** + * Whether or not the target is a user + * @type {boolean} + * @readonly + */ + get isUser() { + const User = require('./User'); + const GuildMember = require('./GuildMember'); + return this.target instanceof User || this.target instanceof GuildMember; + } + + /** + * Whether or not the target is a message + * @type {boolean} + * @readonly + */ + get isMessage() { + const Message = require('./Message'); + return this.target instanceof Message; + } + + /** + * Makes the content of this message. + * @returns {?(string|string[])} + */ + makeContent() { + const GuildMember = require('./GuildMember'); + + let content; + if (this.options.content === null) { + content = ''; + } else if (typeof this.options.content !== 'undefined') { + content = Util.resolveString(this.options.content); + } + + if (typeof content !== 'string') return content; + + const disableMentions = + typeof this.options.disableMentions === 'undefined' + ? this.target.client.options.disableMentions + : this.options.disableMentions; + if (disableMentions === 'all') { + content = Util.removeMentions(content); + } else if (disableMentions === 'everyone') { + content = content.replace(/@([^<>@ ]*)/gmsu, (match, target) => { + if (target.match(/^[&!]?\d+$/)) { + return `@${target}`; + } else { + return `@\u200b${target}`; + } + }); + } + + const isSplit = typeof this.options.split !== 'undefined' && this.options.split !== false; + const isCode = typeof this.options.code !== 'undefined' && this.options.code !== false; + const splitOptions = isSplit ? { ...this.options.split } : undefined; + + let mentionPart = ''; + if (this.options.reply && !this.isUser && this.target.type !== 'dm') { + const id = this.target.client.users.resolveID(this.options.reply); + mentionPart = `<@${this.options.reply instanceof GuildMember && this.options.reply.nickname ? '!' : ''}${id}>, `; + if (isSplit) { + splitOptions.prepend = `${mentionPart}${splitOptions.prepend || ''}`; + } + } + + if (content || mentionPart) { + if (isCode) { + const codeName = typeof this.options.code === 'string' ? this.options.code : ''; + content = `${mentionPart}\`\`\`${codeName}\n${Util.cleanCodeBlockContent(content)}\n\`\`\``; + if (isSplit) { + splitOptions.prepend = `${splitOptions.prepend || ''}\`\`\`${codeName}\n`; + splitOptions.append = `\n\`\`\`${splitOptions.append || ''}`; + } + } else if (mentionPart) { + content = `${mentionPart}${content}`; + } + + if (isSplit) { + content = Util.splitMessage(content, splitOptions); + } + } + + return content; + } + + /** + * Resolves data. + * @returns {APIMessage} + */ + resolveData() { + if (this.data) return this; + + const content = this.makeContent(); + const tts = Boolean(this.options.tts); + + let nonce; + if (typeof this.options.nonce !== 'undefined') { + nonce = parseInt(this.options.nonce); + if (isNaN(nonce) || nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE'); + } + + const embedLikes = []; + if (this.isWebhook) { + if (this.options.embeds) { + embedLikes.push(...this.options.embeds); + } + } else if (this.options.embed) { + embedLikes.push(this.options.embed); + } + const embeds = embedLikes.map(e => new MessageEmbed(e).toJSON()); + + let username; + let avatarURL; + if (this.isWebhook) { + username = this.options.username || this.target.name; + if (this.options.avatarURL) avatarURL = this.options.avatarURL; + } + + let flags; + if (this.isMessage) { + // eslint-disable-next-line eqeqeq + flags = this.options.flags != null ? new MessageFlags(this.options.flags).bitfield : this.target.flags.bitfield; + } + + let allowedMentions = + typeof this.options.allowedMentions === 'undefined' + ? this.target.client.options.allowedMentions + : this.options.allowedMentions; + if (this.options.reply) { + const id = this.target.client.users.resolveID(this.options.reply); + if (allowedMentions) { + // Clone the object as not to alter the ClientOptions object + allowedMentions = Util.cloneObject(allowedMentions); + const parsed = allowedMentions.parse && allowedMentions.parse.includes('users'); + // Check if the mention won't be parsed, and isn't supplied in `users` + if (!parsed && !(allowedMentions.users && allowedMentions.users.includes(id))) { + if (!allowedMentions.users) allowedMentions.users = []; + allowedMentions.users.push(id); + } + } else { + allowedMentions = { users: [id] }; + } + } + + this.data = { + content, + tts, + nonce, + embed: this.options.embed === null ? null : embeds[0], + embeds, + username, + avatar_url: avatarURL, + allowed_mentions: typeof content === 'undefined' ? undefined : allowedMentions, + flags, + }; + return this; + } + + /** + * Resolves files. + * @returns {Promise} + */ + async resolveFiles() { + if (this.files) return this; + + const embedLikes = []; + if (this.isWebhook) { + if (this.options.embeds) { + embedLikes.push(...this.options.embeds); + } + } else if (this.options.embed) { + embedLikes.push(this.options.embed); + } + + const fileLikes = []; + if (this.options.files) { + fileLikes.push(...this.options.files); + } + for (const embed of embedLikes) { + if (embed.files) { + fileLikes.push(...embed.files); + } + } + + this.files = await Promise.all(fileLikes.map(f => this.constructor.resolveFile(f))); + return this; + } + + /** + * Converts this APIMessage into an array of APIMessages for each split content + * @returns {APIMessage[]} + */ + split() { + if (!this.data) this.resolveData(); + + if (!Array.isArray(this.data.content)) return [this]; + + const apiMessages = []; + + for (let i = 0; i < this.data.content.length; i++) { + let data; + let opt; + + if (i === this.data.content.length - 1) { + data = { ...this.data, content: this.data.content[i] }; + opt = { ...this.options, content: this.data.content[i] }; + } else { + data = { content: this.data.content[i], tts: this.data.tts, allowed_mentions: this.options.allowedMentions }; + opt = { content: this.data.content[i], tts: this.data.tts, allowedMentions: this.options.allowedMentions }; + } + + const apiMessage = new APIMessage(this.target, opt); + apiMessage.data = data; + apiMessages.push(apiMessage); + } + + return apiMessages; + } + + /** + * Resolves a single file into an object sendable to the API. + * @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file + * @returns {Object} + */ + static async resolveFile(fileLike) { + let attachment; + let name; + + const findName = thing => { + if (typeof thing === 'string') { + return Util.basename(thing); + } + + if (thing.path) { + return Util.basename(thing.path); + } + + return 'file.jpg'; + }; + + const ownAttachment = + typeof fileLike === 'string' || + fileLike instanceof (browser ? ArrayBuffer : Buffer) || + typeof fileLike.pipe === 'function'; + if (ownAttachment) { + attachment = fileLike; + name = findName(attachment); + } else { + attachment = fileLike.attachment; + name = fileLike.name || findName(attachment); + } + + const resource = await DataResolver.resolveFile(attachment); + return { attachment, name, file: resource }; + } + + /** + * Partitions embeds and attachments. + * @param {Array} items Items to partition + * @returns {Array} + */ + static partitionMessageAdditions(items) { + const embeds = []; + const files = []; + for (const item of items) { + if (item instanceof MessageEmbed) { + embeds.push(item); + } else if (item instanceof MessageAttachment) { + files.push(item); + } + } + + return [embeds, files]; + } + + /** + * Transforms the user-level arguments into a final options object. Passing a transformed options object alone into + * this method will keep it the same, allowing for the reuse of the final options object. + * @param {StringResolvable} [content] Content to send + * @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use + * @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto transformed options + * @param {boolean} [isWebhook=false] Whether or not to use WebhookMessageOptions as the result + * @returns {MessageOptions|WebhookMessageOptions} + */ + static transformOptions(content, options, extra = {}, isWebhook = false) { + if (!options && typeof content === 'object' && !Array.isArray(content)) { + options = content; + content = undefined; + } + + if (!options) { + options = {}; + } else if (options instanceof MessageEmbed) { + return isWebhook ? { content, embeds: [options], ...extra } : { content, embed: options, ...extra }; + } else if (options instanceof MessageAttachment) { + return { content, files: [options], ...extra }; + } + + if (Array.isArray(options)) { + const [embeds, files] = this.partitionMessageAdditions(options); + return isWebhook ? { content, embeds, files, ...extra } : { content, embed: embeds[0], files, ...extra }; + } else if (Array.isArray(content)) { + const [embeds, files] = this.partitionMessageAdditions(content); + if (embeds.length || files.length) { + return isWebhook ? { embeds, files, ...extra } : { embed: embeds[0], files, ...extra }; + } + } + + return { content, ...options, ...extra }; + } + + /** + * Creates an `APIMessage` from user-level arguments. + * @param {MessageTarget} target Target to send to + * @param {StringResolvable} [content] Content to send + * @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use + * @param {MessageOptions|WebhookMessageOptions} [extra={}] - Extra options to add onto transformed options + * @returns {MessageOptions|WebhookMessageOptions} + */ + static create(target, content, options, extra = {}) { + const Webhook = require('./Webhook'); + const WebhookClient = require('../client/WebhookClient'); + + const isWebhook = target instanceof Webhook || target instanceof WebhookClient; + const transformed = this.transformOptions(content, options, extra, isWebhook); + return new this(target, transformed); + } +} + +module.exports = APIMessage; + +/** + * A target for a message. + * @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient} MessageTarget + */ + +/** + * Additional items that can be sent with a message. + * @typedef {MessageEmbed|MessageAttachment|Array} MessageAdditions + */ diff --git a/node_modules/discord.js/src/structures/Base.js b/node_modules/discord.js/src/structures/Base.js new file mode 100644 index 0000000..cd43bf7 --- /dev/null +++ b/node_modules/discord.js/src/structures/Base.js @@ -0,0 +1,43 @@ +'use strict'; + +const Util = require('../util/Util'); + +/** + * Represents a data model that is identifiable by a Snowflake (i.e. Discord API data models). + * @abstract + */ +class Base { + constructor(client) { + /** + * The client that instantiated this + * @name Base#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + } + + _clone() { + return Object.assign(Object.create(this), this); + } + + _patch(data) { + return data; + } + + _update(data) { + const clone = this._clone(); + this._patch(data); + return clone; + } + + toJSON(...props) { + return Util.flatten(this, ...props); + } + + valueOf() { + return this.id; + } +} + +module.exports = Base; diff --git a/node_modules/discord.js/src/structures/BaseGuildEmoji.js b/node_modules/discord.js/src/structures/BaseGuildEmoji.js new file mode 100644 index 0000000..a2007c6 --- /dev/null +++ b/node_modules/discord.js/src/structures/BaseGuildEmoji.js @@ -0,0 +1,66 @@ +'use strict'; + +const Emoji = require('./Emoji'); + +/** + * Parent class for {@link GuildEmoji} and {@link GuildPreviewEmoji}. + * @extends {Emoji} + * @abstract + */ +class BaseGuildEmoji extends Emoji { + constructor(client, data, guild) { + super(client, data); + + /** + * The guild this emoji is a part of + * @type {Guild|GuildPreview} + */ + this.guild = guild; + + this.requireColons = null; + this.managed = null; + this.available = null; + + /** + * Array of role ids this emoji is active for + * @name BaseGuildEmoji#_roles + * @type {Snowflake[]} + * @private + */ + Object.defineProperty(this, '_roles', { value: [], writable: true }); + + this._patch(data); + } + + _patch(data) { + if (data.name) this.name = data.name; + + if (typeof data.require_colons !== 'undefined') { + /** + * Whether or not this emoji requires colons surrounding it + * @type {?boolean} + */ + this.requiresColons = data.require_colons; + } + + if (typeof data.managed !== 'undefined') { + /** + * Whether this emoji is managed by an external service + * @type {?boolean} + */ + this.managed = data.managed; + } + + if (typeof data.available !== 'undefined') { + /** + * Whether this emoji is available + * @type {?boolean} + */ + this.available = data.available; + } + + if (data.roles) this._roles = data.roles; + } +} + +module.exports = BaseGuildEmoji; 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..4ba0140 --- /dev/null +++ b/node_modules/discord.js/src/structures/CategoryChannel.js @@ -0,0 +1,33 @@ +'use strict'; + +const GuildChannel = require('./GuildChannel'); + +/** + * Represents a guild category channel on Discord. + * @extends {GuildChannel} + */ +class CategoryChannel extends GuildChannel { + /** + * Channels that are a part of this category + * @type {Collection} + * @readonly + */ + get children() { + return this.guild.channels.cache.filter(c => c.parentID === this.id); + } + + /** + * Sets the category parent of this channel. + * It is not currently possible to set the parent of a CategoryChannel. + * @method setParent + * @memberof CategoryChannel + * @instance + * @param {?GuildChannel|Snowflake} channel Parent channel + * @param {Object} [options={}] Options to pass + * @param {boolean} [options.lockPermissions=true] Lock the permissions to what the parent's permissions are + * @param {string} [options.reason] Reason for modifying the parent of this channel + * @returns {Promise} + */ +} + +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..baa02ee --- /dev/null +++ b/node_modules/discord.js/src/structures/Channel.js @@ -0,0 +1,161 @@ +'use strict'; + +const Base = require('./Base'); +const { ChannelTypes } = require('../util/Constants'); +const Snowflake = require('../util/Snowflake'); + +/** + * Represents any channel on Discord. + * @extends {Base} + * @abstract + */ +class Channel extends Base { + constructor(client, data) { + super(client); + + const type = Object.keys(ChannelTypes)[data.type]; + /** + * The type of the channel, either: + * * `dm` - a 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 + * * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel + * @type {string} + */ + this.type = type ? type.toLowerCase() : 'unknown'; + + /** + * Whether the channel has been deleted + * @type {boolean} + */ + this.deleted = false; + + if (data) this._patch(data); + } + + _patch(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 at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * 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}!`); + */ + toString() { + return `<#${this.id}>`; + } + + /** + * Deletes this channel. + * @returns {Promise} + * @example + * // Delete the channel + * channel.delete() + * .then(console.log) + * .catch(console.error); + */ + delete() { + return this.client.api + .channels(this.id) + .delete() + .then(() => this); + } + + /** + * Fetches this channel. + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + */ + fetch(force = false) { + return this.client.channels.fetch(this.id, true, force); + } + + /** + * Indicates whether this channel is text-based. + * @returns {boolean} + */ + isText() { + return 'messages' in this; + } + + static create(client, data, guild) { + const Structures = require('../util/Structures'); + let channel; + if (!data.guild_id && !guild) { + if ((data.recipients && data.type !== ChannelTypes.GROUP) || data.type === ChannelTypes.DM) { + const DMChannel = Structures.get('DMChannel'); + channel = new DMChannel(client, data); + } else if (data.type === ChannelTypes.GROUP) { + const PartialGroupDMChannel = require('./PartialGroupDMChannel'); + channel = new PartialGroupDMChannel(client, data); + } + } else { + guild = guild || client.guilds.cache.get(data.guild_id); + if (guild) { + switch (data.type) { + case ChannelTypes.TEXT: { + const TextChannel = Structures.get('TextChannel'); + channel = new TextChannel(guild, data); + break; + } + case ChannelTypes.VOICE: { + const VoiceChannel = Structures.get('VoiceChannel'); + channel = new VoiceChannel(guild, data); + break; + } + case ChannelTypes.CATEGORY: { + const CategoryChannel = Structures.get('CategoryChannel'); + channel = new CategoryChannel(guild, data); + break; + } + case ChannelTypes.NEWS: { + const NewsChannel = Structures.get('NewsChannel'); + channel = new NewsChannel(guild, data); + break; + } + case ChannelTypes.STORE: { + const StoreChannel = Structures.get('StoreChannel'); + channel = new StoreChannel(guild, data); + break; + } + } + if (channel) guild.channels.cache.set(channel.id, channel); + } + } + return channel; + } + + toJSON(...props) { + return super.toJSON({ createdTimestamp: true }, ...props); + } +} + +module.exports = Channel; diff --git a/node_modules/discord.js/src/structures/ClientApplication.js b/node_modules/discord.js/src/structures/ClientApplication.js new file mode 100644 index 0000000..7e35b9e --- /dev/null +++ b/node_modules/discord.js/src/structures/ClientApplication.js @@ -0,0 +1,46 @@ +'use strict'; + +const Team = require('./Team'); +const Application = require('./interfaces/Application'); + +/** + * Represents a Client OAuth2 Application. + * @extends {Application} + */ +class ClientApplication extends Application { + _patch(data) { + super._patch(data); + + /** + * The app's cover image + * @type {?string} + */ + this.cover = data.cover_image || null; + + /** + * The app's RPC origins, if enabled + * @type {string[]} + */ + this.rpcOrigins = data.rpc_origins || []; + + /** + * If this app's bot requires a code grant when using the OAuth2 flow + * @type {?boolean} + */ + this.botRequireCodeGrant = typeof data.bot_require_code_grant !== 'undefined' ? data.bot_require_code_grant : null; + + /** + * If this app's bot is public + * @type {?boolean} + */ + this.botPublic = typeof data.bot_public !== 'undefined' ? data.bot_public : null; + + /** + * The owner of this OAuth application + * @type {?User|Team} + */ + this.owner = data.team ? new Team(this.client, data.team) : data.owner ? this.client.users.add(data.owner) : null; + } +} + +module.exports = ClientApplication; diff --git a/node_modules/discord.js/src/structures/ClientPresence.js b/node_modules/discord.js/src/structures/ClientPresence.js new file mode 100644 index 0000000..a39ba00 --- /dev/null +++ b/node_modules/discord.js/src/structures/ClientPresence.js @@ -0,0 +1,87 @@ +'use strict'; + +const { Presence } = require('./Presence'); +const { TypeError } = require('../errors'); +const Collection = require('../util/Collection'); +const { ActivityTypes, OPCodes } = require('../util/Constants'); + +class ClientPresence extends Presence { + /** + * @param {Client} client The instantiating client + * @param {Object} [data={}] The data for the client presence + */ + constructor(client, data = {}) { + super(client, Object.assign(data, { status: 'online', user: { id: null } })); + } + + async set(presence) { + const packet = await this._parse(presence); + this.patch(packet); + if (typeof presence.shardID === 'undefined') { + this.client.ws.broadcast({ op: OPCodes.STATUS_UPDATE, d: packet }); + } else if (Array.isArray(presence.shardID)) { + for (const shardID of presence.shardID) { + this.client.ws.shards.get(shardID).send({ op: OPCodes.STATUS_UPDATE, d: packet }); + } + } else { + this.client.ws.shards.get(presence.shardID).send({ op: OPCodes.STATUS_UPDATE, d: packet }); + } + return this; + } + + async _parse({ status, since, afk, activity }) { + const applicationID = activity && (activity.application ? activity.application.id || activity.application : null); + let assets = new Collection(); + if (activity) { + if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', 'name', 'string'); + if (!activity.type) activity.type = 0; + if (activity.assets && applicationID) { + try { + const a = await this.client.api.oauth2.applications(applicationID).assets.get(); + for (const asset of a) assets.set(asset.name, asset.id); + } catch {} // eslint-disable-line no-empty + } + } + + const packet = { + afk: afk != null ? afk : false, // eslint-disable-line eqeqeq + since: since != null ? since : null, // eslint-disable-line eqeqeq + status: status || this.status, + game: activity + ? { + type: activity.type, + name: activity.name, + url: activity.url, + details: activity.details || undefined, + state: activity.state || undefined, + assets: activity.assets + ? { + large_text: activity.assets.largeText || undefined, + small_text: activity.assets.smallText || undefined, + large_image: assets.get(activity.assets.largeImage) || activity.assets.largeImage, + small_image: assets.get(activity.assets.smallImage) || activity.assets.smallImage, + } + : undefined, + timestamps: activity.timestamps || undefined, + party: activity.party || undefined, + application_id: applicationID || undefined, + secrets: activity.secrets || undefined, + instance: activity.instance || undefined, + } + : null, + }; + + if ((status || afk || since) && !activity) { + packet.game = this.activities[0] || null; + } + + if (packet.game) { + packet.game.type = + typeof packet.game.type === 'number' ? packet.game.type : ActivityTypes.indexOf(packet.game.type); + } + + return packet; + } +} + +module.exports = ClientPresence; 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..65112b8 --- /dev/null +++ b/node_modules/discord.js/src/structures/ClientUser.js @@ -0,0 +1,178 @@ +'use strict'; + +const DataResolver = require('../util/DataResolver'); +const Structures = require('../util/Structures'); + +/** + * Represents the logged in client's Discord user. + * @extends {User} + */ +class ClientUser extends Structures.get('User') { + constructor(client, data) { + super(client, data); + this._typing = new Map(); + } + + _patch(data) { + super._patch(data); + + if ('verified' in data) { + /** + * Whether or not this account has been verified + * @type {boolean} + */ + this.verified = data.verified; + } + + if ('mfa_enabled' in data) { + /** + * If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account + * @type {?boolean} + */ + this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null; + } else if (typeof this.mfaEnabled === 'undefined') { + this.mfaEnabled = null; + } + + if (data.token) this.client.token = data.token; + } + + /** + * ClientUser's presence + * @type {Presence} + * @readonly + */ + get presence() { + return this.client.presence; + } + + edit(data) { + return this.client.api + .users('@me') + .patch({ data }) + .then(newData => { + this.client.token = newData.token; + const { updated } = this.client.actions.UserUpdate.handle(newData); + if (updated) return updated; + return this; + }); + } + + /** + * Sets the username of the logged in client. + * Changing usernames in Discord is heavily rate limited, with only 2 requests + * every hour. Use this sparingly! + * @param {string} username The new username + * @returns {Promise} + * @example + * // Set username + * client.user.setUsername('discordjs') + * .then(user => console.log(`My new username is ${user.username}`)) + * .catch(console.error); + */ + setUsername(username) { + return this.edit({ username }); + } + + /** + * Sets the avatar of the logged in client. + * @param {BufferResolvable|Base64Resolvable} avatar The new avatar + * @returns {Promise} + * @example + * // Set avatar + * client.user.setAvatar('./avatar.png') + * .then(user => console.log(`New avatar set!`)) + * .catch(console.error); + */ + async setAvatar(avatar) { + return this.edit({ avatar: await DataResolver.resolveImage(avatar) }); + } + + /** + * Data resembling a raw Discord presence. + * @typedef {Object} PresenceData + * @property {PresenceStatusData} [status] Status of the user + * @property {boolean} [afk] Whether the user is AFK + * @property {Object} [activity] Activity the user is playing + * @property {string} [activity.name] Name of the activity + * @property {ActivityType|number} [activity.type] Type of the activity + * @property {string} [activity.url] Twitch / YouTube stream URL + * @property {?number|number[]} [shardID] Shard Id(s) to have the activity set on + */ + + /** + * Sets the full presence of the client user. + * @param {PresenceData} data Data for the presence + * @returns {Promise} + * @example + * // Set the client user's presence + * client.user.setPresence({ activity: { name: 'with discord.js' }, status: 'idle' }) + * .then(console.log) + * .catch(console.error); + */ + setPresence(data) { + return this.client.presence.set(data); + } + + /** + * A user's status. Must be one of: + * * `online` + * * `idle` + * * `invisible` + * * `dnd` (do not disturb) + * @typedef {string} PresenceStatusData + */ + + /** + * Sets the status of the client user. + * @param {PresenceStatusData} status Status to change to + * @param {?number|number[]} [shardID] Shard ID(s) to have the activity set on + * @returns {Promise} + * @example + * // Set the client user's status + * client.user.setStatus('idle') + * .then(console.log) + * .catch(console.error); + */ + setStatus(status, shardID) { + return this.setPresence({ status, shardID }); + } + + /** + * Options for setting an activity. + * @typedef ActivityOptions + * @type {Object} + * @property {string} [url] Twitch / YouTube stream URL + * @property {ActivityType|number} [type] Type of the activity + * @property {?number|number[]} [shardID] Shard Id(s) to have the activity set on + */ + + /** + * Sets the activity the client user is playing. + * @param {string|ActivityOptions} [name] Activity being played, or options for setting the activity + * @param {ActivityOptions} [options] Options for setting the activity + * @returns {Promise} + * @example + * // Set the client user's activity + * client.user.setActivity('discord.js', { type: 'WATCHING' }) + * .then(presence => console.log(`Activity set to ${presence.activities[0].name}`)) + * .catch(console.error); + */ + setActivity(name, options = {}) { + if (!name) return this.setPresence({ activity: null, shardID: options.shardID }); + + const activity = Object.assign({}, options, typeof name === 'object' ? name : { name }); + return this.setPresence({ activity, shardID: activity.shardID }); + } + + /** + * Sets/removes the AFK flag for the client user. + * @param {boolean} afk Whether or not the user is AFK + * @returns {Promise} + */ + setAFK(afk) { + return this.setPresence({ afk }); + } +} + +module.exports = ClientUser; 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..4807597 --- /dev/null +++ b/node_modules/discord.js/src/structures/DMChannel.js @@ -0,0 +1,99 @@ +'use strict'; + +const Channel = require('./Channel'); +const TextBasedChannel = require('./interfaces/TextBasedChannel'); +const MessageManager = require('../managers/MessageManager'); + +/** + * Represents a direct message channel between two users. + * @extends {Channel} + * @implements {TextBasedChannel} + */ +class DMChannel extends Channel { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the DM channel + */ + constructor(client, data) { + super(client, data); + // Override the channel type so partials have a known type + this.type = 'dm'; + /** + * A manager of the messages belonging to this channel + * @type {MessageManager} + */ + this.messages = new MessageManager(this); + this._typing = new Map(); + } + + _patch(data) { + super._patch(data); + + if (data.recipients) { + /** + * The recipient on the other end of the DM + * @type {User} + */ + this.recipient = this.client.users.add(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; + } + + /** + * Whether this DMChannel is a partial + * @type {boolean} + * @readonly + */ + get partial() { + return typeof this.lastMessageID === 'undefined'; + } + + /** + * Fetch this DMChannel. + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + */ + fetch(force = false) { + return this.recipient.createDM(force); + } + + /** + * When concatenated with a string, this automatically returns the recipient's mention instead of the + * DMChannel object. + * @returns {string} + * @example + * // Logs: Hello from <@123456789012345678>! + * console.log(`Hello from ${channel}!`); + */ + toString() { + return this.recipient.toString(); + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + get lastMessage() {} + get lastPinAt() {} + send() {} + startTyping() {} + stopTyping() {} + get typing() {} + get typingCount() {} + createMessageCollector() {} + awaitMessages() {} + // Doesn't work on DM channels; bulkDelete() {} +} + +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..0214ea8 --- /dev/null +++ b/node_modules/discord.js/src/structures/Emoji.js @@ -0,0 +1,104 @@ +'use strict'; + +const Base = require('./Base'); +const Snowflake = require('../util/Snowflake'); + +/** + * Represents an emoji, see {@link GuildEmoji} and {@link ReactionEmoji}. + * @extends {Base} + */ +class Emoji extends Base { + constructor(client, emoji) { + super(client); + /** + * Whether this emoji is animated + * @type {boolean} + */ + this.animated = emoji.animated; + + /** + * The name of this emoji + * @type {string} + */ + this.name = emoji.name; + + /** + * The ID of this emoji + * @type {?Snowflake} + */ + this.id = emoji.id; + + /** + * Whether this emoji has been deleted + * @type {boolean} + */ + this.deleted = false; + } + + /** + * The identifier of this emoji, used for message reactions + * @type {string} + * @readonly + */ + get identifier() { + if (this.id) return `${this.animated ? 'a:' : ''}${this.name}:${this.id}`; + return encodeURIComponent(this.name); + } + + /** + * The URL to the emoji file if its a custom emoji + * @type {?string} + * @readonly + */ + get url() { + if (!this.id) return null; + return this.client.rest.cdn.Emoji(this.id, this.animated ? 'gif' : 'png'); + } + + /** + * The timestamp the 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 emoji was created at, or null if unicode + * @type {?Date} + * @readonly + */ + get createdAt() { + if (!this.id) return null; + return new Date(this.createdTimestamp); + } + + /** + * When concatenated with a string, this automatically returns the text required to form a graphical emoji on Discord + * instead of the Emoji object. + * @returns {string} + * @example + * // Send a custom emoji from a guild: + * const emoji = guild.emojis.cache.first(); + * msg.reply(`Hello! ${emoji}`); + * @example + * // Send the emoji used in a reaction to the channel the reaction is part of + * reaction.message.channel.send(`The emoji used was: ${reaction.emoji}`); + */ + toString() { + return this.id ? `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>` : this.name; + } + + toJSON() { + return super.toJSON({ + guild: 'guildID', + createdTimestamp: true, + url: true, + identifier: true, + }); + } +} + +module.exports = Emoji; 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..664aad2 --- /dev/null +++ b/node_modules/discord.js/src/structures/Guild.js @@ -0,0 +1,1558 @@ +'use strict'; + +const { deprecate } = require('util'); +const Base = require('./Base'); +const GuildAuditLogs = require('./GuildAuditLogs'); +const GuildPreview = require('./GuildPreview'); +const Integration = require('./Integration'); +const Invite = require('./Invite'); +const VoiceRegion = require('./VoiceRegion'); +const Webhook = require('./Webhook'); +const { Error, TypeError } = require('../errors'); +const GuildChannelManager = require('../managers/GuildChannelManager'); +const GuildEmojiManager = require('../managers/GuildEmojiManager'); +const GuildMemberManager = require('../managers/GuildMemberManager'); +const PresenceManager = require('../managers/PresenceManager'); +const RoleManager = require('../managers/RoleManager'); +const VoiceStateManager = require('../managers/VoiceStateManager'); +const Collection = require('../util/Collection'); +const { + ChannelTypes, + DefaultMessageNotifications, + PartialTypes, + VerificationLevels, + ExplicitContentFilterLevels, +} = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const Snowflake = require('../util/Snowflake'); +const SystemChannelFlags = require('../util/SystemChannelFlags'); +const Util = require('../util/Util'); + +/** + * Represents a guild (or a server) on Discord. + * 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`. + * @extends {Base} + */ +class Guild extends Base { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the guild + */ + constructor(client, data) { + super(client); + + /** + * A manager of the members belonging to this guild + * @type {GuildMemberManager} + */ + this.members = new GuildMemberManager(this); + + /** + * A manager of the channels belonging to this guild + * @type {GuildChannelManager} + */ + this.channels = new GuildChannelManager(this); + + /** + * A manager of the roles belonging to this guild + * @type {RoleManager} + */ + this.roles = new RoleManager(this); + + /** + * A manager of the presences belonging to this guild + * @type {PresenceManager} + */ + this.presences = new PresenceManager(this.client); + + /** + * A manager of the voice states of this guild + * @type {VoiceStateManager} + */ + this.voiceStates = new VoiceStateManager(this); + + /** + * 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._patch(data); + if (!data.channels) this.available = false; + } + + /** + * The id of the shard this Guild belongs to. + * @type {number} + */ + this.shardID = data.shardID; + } + + /** + * The Shard this Guild belongs to. + * @type {WebSocketShard} + * @readonly + */ + get shard() { + return this.client.ws.shards.get(this.shardID); + } + + /** + * Sets up the guild. + * @param {*} data The raw data of the guild + * @private + */ + _patch(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 invite splash image + * @type {?string} + */ + this.splash = data.splash; + + /** + * The hash of the guild discovery splash image + * @type {?string} + */ + this.discoverySplash = data.discovery_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 large_threshold members, 50 by default) + * @type {boolean} + */ + this.large = Boolean('large' in data ? data.large : this.large); + + /** + * An array of enabled guild features, here are the possible values: + * * ANIMATED_ICON + * * BANNER + * * COMMERCE + * * COMMUNITY + * * DISCOVERABLE + * * FEATURABLE + * * INVITE_SPLASH + * * NEWS + * * PARTNERED + * * RELAY_ENABLED + * * VANITY_URL + * * VERIFIED + * * VIP_REGIONS + * * WELCOME_SCREEN_ENABLED + * @typedef {string} Features + */ + + /** + * An array of guild features partnered guilds have enabled + * @type {Features[]} + */ + 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 {?Snowflake} + */ + 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} + * @deprecated + */ + this.embedEnabled = data.embed_enabled; + + /** + * 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; + + if (typeof data.premium_subscription_count !== 'undefined') { + /** + * The total number of boosts for this server + * @type {?number} + */ + this.premiumSubscriptionCount = data.premium_subscription_count; + } + + if (typeof data.widget_enabled !== 'undefined') { + /** + * Whether widget images are enabled on this guild + * @type {?boolean} + */ + this.widgetEnabled = data.widget_enabled; + } + + if (typeof data.widget_channel_id !== 'undefined') { + /** + * The widget channel ID, if enabled + * @type {?string} + */ + this.widgetChannelID = data.widget_channel_id; + } + + if (typeof data.embed_channel_id !== 'undefined') { + /** + * The embed channel ID, if enabled + * @type {?string} + * @deprecated + */ + this.embedChannelID = data.embed_channel_id; + } + + /** + * The verification level of the guild + * @type {VerificationLevel} + */ + this.verificationLevel = VerificationLevels[data.verification_level]; + + /** + * The explicit content filter level of the guild + * @type {ExplicitContentFilterLevel} + */ + this.explicitContentFilter = ExplicitContentFilterLevels[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 the guild's default message notifications + * @type {DefaultMessageNotifications|number} + */ + this.defaultMessageNotifications = + DefaultMessageNotifications[data.default_message_notifications] || data.default_message_notifications; + + /** + * The value set for the guild's system channel flags + * @type {Readonly} + */ + this.systemChannelFlags = new SystemChannelFlags(data.system_channel_flags).freeze(); + + if (typeof data.max_members !== 'undefined') { + /** + * The maximum amount of members the guild can have + * @type {?number} + */ + this.maximumMembers = data.max_members; + } else if (typeof this.maximumMembers === 'undefined') { + this.maximumMembers = null; + } + + if (typeof data.max_presences !== 'undefined') { + /** + * The maximum amount of presences the guild can have + * You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter + * @type {?number} + */ + this.maximumPresences = data.max_presences || 25000; + } else if (typeof this.maximumPresences === 'undefined') { + this.maximumPresences = null; + } + + if (typeof data.approximate_member_count !== 'undefined') { + /** + * The approximate amount of members the guild has + * You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter + * @type {?number} + */ + this.approximateMemberCount = data.approximate_member_count; + } else if (typeof this.approximateMemberCount === 'undefined') { + this.approximateMemberCount = null; + } + + if (typeof data.approximate_presence_count !== 'undefined') { + /** + * The approximate amount of presences the guild has + * You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter + * @type {?number} + */ + this.approximatePresenceCount = data.approximate_presence_count; + } else if (typeof this.approximatePresenceCount === 'undefined') { + this.approximatePresenceCount = null; + } + + /** + * The vanity invite code of the guild, if any + * @type {?string} + */ + this.vanityURLCode = data.vanity_url_code; + + /* eslint-disable max-len */ + /** + * The use count of the vanity URL code of the guild, if any + * You will need to fetch this parameter using {@link Guild#fetchVanityData} if you want to receive it + * @type {?number} + */ + this.vanityURLUses = null; + /* eslint-enable max-len */ + + /** + * The description of the guild, if any + * @type {?string} + */ + this.description = data.description; + + /** + * The hash of the guild banner + * @type {?string} + */ + this.banner = data.banner; + + this.id = data.id; + this.available = !data.unavailable; + this.features = data.features || this.features || []; + + /** + * The ID of the rules channel for the guild + * @type {?Snowflake} + */ + this.rulesChannelID = data.rules_channel_id; + + /** + * The ID of the community updates channel for the guild + * @type {?Snowflake} + */ + this.publicUpdatesChannelID = data.public_updates_channel_id; + + /** + * The preferred locale of the guild, defaults to `en-US` + * @type {string} + */ + this.preferredLocale = data.preferred_locale; + + if (data.channels) { + this.channels.cache.clear(); + for (const rawChannel of data.channels) { + this.client.channels.add(rawChannel, this); + } + } + + if (data.roles) { + this.roles.cache.clear(); + for (const role of data.roles) this.roles.add(role); + } + + if (data.members) { + this.members.cache.clear(); + for (const guildUser of data.members) this.members.add(guildUser); + } + + if (data.owner_id) { + /** + * The user ID of this guild's owner + * @type {Snowflake} + */ + this.ownerID = data.owner_id; + } + + if (data.presences) { + for (const presence of data.presences) { + this.presences.add(Object.assign(presence, { guild: this })); + } + } + + if (data.voice_states) { + this.voiceStates.cache.clear(); + for (const voiceState of data.voice_states) { + this.voiceStates.add(voiceState); + } + } + + if (!this.emojis) { + /** + * A manager of the emojis belonging to this guild + * @type {GuildEmojiManager} + */ + this.emojis = new GuildEmojiManager(this); + if (data.emojis) for (const emoji of data.emojis) this.emojis.add(emoji); + } else if (data.emojis) { + this.client.actions.GuildEmojisUpdate.handle({ + guild_id: this.id, + emojis: data.emojis, + }); + } + } + + /** + * The URL to this guild's banner. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + bannerURL({ format, size } = {}) { + if (!this.banner) return null; + return this.client.rest.cdn.Banner(this.id, this.banner, format, size); + } + + /** + * The timestamp the guild was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the guild was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * The time the client user joined the guild + * @type {Date} + * @readonly + */ + get joinedAt() { + return new Date(this.joinedTimestamp); + } + + /** + * If this guild is partnered + * @type {boolean} + * @readonly + */ + get partnered() { + return this.features.includes('PARTNERED'); + } + + /** + * If this guild is verified + * @type {boolean} + * @readonly + */ + get verified() { + return this.features.includes('VERIFIED'); + } + + /** + * The URL to this guild's icon. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + iconURL({ format, size, dynamic } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.Icon(this.id, this.icon, format, size, dynamic); + } + + /** + * The acronym that shows up in place of a guild icon. + * @type {string} + * @readonly + */ + get nameAcronym() { + return this.name + .replace(/'s /g, ' ') + .replace(/\w+/g, e => e[0]) + .replace(/\s/g, ''); + } + + /** + * The URL to this guild's invite splash image. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + splashURL({ format, size } = {}) { + if (!this.splash) return null; + return this.client.rest.cdn.Splash(this.id, this.splash, format, size); + } + + /** + * The URL to this guild's discovery splash image. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + discoverySplashURL({ format, size } = {}) { + if (!this.discoverySplash) return null; + return this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size); + } + + /** + * The owner of the guild + * @type {?GuildMember} + * @readonly + */ + get owner() { + return ( + this.members.cache.get(this.ownerID) || + (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) + ? this.members.add({ user: { id: this.ownerID } }, true) + : null) + ); + } + + /** + * AFK voice channel for this guild + * @type {?VoiceChannel} + * @readonly + */ + get afkChannel() { + return this.client.channels.cache.get(this.afkChannelID) || null; + } + + /** + * System channel for this guild + * @type {?TextChannel} + * @readonly + */ + get systemChannel() { + return this.client.channels.cache.get(this.systemChannelID) || null; + } + + /** + * Widget channel for this guild + * @type {?TextChannel} + * @readonly + */ + get widgetChannel() { + return this.client.channels.cache.get(this.widgetChannelID) || null; + } + + /** + * Embed channel for this guild + * @type {?TextChannel} + * @readonly + * @deprecated + */ + get embedChannel() { + return this.client.channels.cache.get(this.embedChannelID) || null; + } + + /** + * Rules channel for this guild + * @type {?TextChannel} + * @readonly + */ + get rulesChannel() { + return this.client.channels.cache.get(this.rulesChannelID) || null; + } + + /** + * Public updates channel for this guild + * @type {?TextChannel} + * @readonly + */ + get publicUpdatesChannel() { + return this.client.channels.cache.get(this.publicUpdatesChannelID) || null; + } + + /** + * The client user as a GuildMember of this guild + * @type {?GuildMember} + * @readonly + */ + get me() { + return ( + this.members.cache.get(this.client.user.id) || + (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) + ? this.members.add({ user: { id: this.client.user.id } }, true) + : null) + ); + } + + /** + * The voice state for the client user of this guild, if any + * @type {?VoiceState} + * @readonly + */ + get voice() { + return this.voiceStates.cache.get(this.client.user.id); + } + + /** + * 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.members.resolve(user); + } + + /** + * Fetches this guild. + * @returns {Promise} + */ + fetch() { + return this.client.api + .guilds(this.id) + .get({ query: { with_counts: true } }) + .then(data => { + this._patch(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 + */ + + /** + * Fetches information on a banned user from this guild. + * @param {UserResolvable} user The User to fetch the ban info of + * @returns {Promise} + */ + fetchBan(user) { + const id = this.client.users.resolveID(user); + if (!id) throw new Error('FETCH_BAN_RESOLVE_ID'); + return this.client.api + .guilds(this.id) + .bans(id) + .get() + .then(ban => ({ + reason: ban.reason, + user: this.client.users.add(ban.user), + })); + } + + /** + * Fetches a collection of banned users in this guild. + * @returns {Promise>} + */ + fetchBans() { + return this.client.api + .guilds(this.id) + .bans.get() + .then(bans => + bans.reduce((collection, ban) => { + collection.set(ban.user.id, { + reason: ban.reason, + user: this.client.users.add(ban.user), + }); + return collection; + }, new Collection()), + ); + } + + /** + * Fetches a collection of integrations to this guild. + * Resolves with a collection mapping integrations by their ids. + * @param {Object} [options] Options for fetching integrations + * @param {boolean} [options.includeApplications] Whether to include bot and Oauth2 webhook integrations + * @returns {Promise>} + * @example + * // Fetch integrations + * guild.fetchIntegrations() + * .then(integrations => console.log(`Fetched ${integrations.size} integrations`)) + * .catch(console.error); + */ + fetchIntegrations({ includeApplications = false } = {}) { + return this.client.api + .guilds(this.id) + .integrations.get({ + query: { + include_applications: includeApplications, + }, + }) + .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 the integration + * @param {string} reason Reason for creating the integration + * @returns {Promise} + */ + createIntegration(data, reason) { + return this.client.api + .guilds(this.id) + .integrations.post({ data, reason }) + .then(() => this); + } + + /** + * Fetches a collection of invites to this guild. + * Resolves with a collection mapping invites by their codes. + * @returns {Promise>} + * @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.api + .guilds(this.id) + .invites.get() + .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; + }); + } + + /** + * Obtains a guild preview for this guild from Discord. + * @returns {Promise} + */ + fetchPreview() { + return this.client.api + .guilds(this.id) + .preview.get() + .then(data => new GuildPreview(this.client, data)); + } + + /** + * 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} + * @deprecated + * @example + * // Fetch invites + * guild.fetchVanityCode() + * .then(code => { + * console.log(`Vanity URL: https://discord.gg/${code}`); + * }) + * .catch(console.error); + */ + fetchVanityCode() { + return this.fetchVanityData().then(vanity => vanity.code); + } + + /** + * An object containing information about a guild's vanity invite. + * @typedef {Object} Vanity + * @property {?string} code Vanity invite code + * @property {?number} uses How many times this invite has been used + */ + + /** + * Fetches the vanity url invite object to this guild. + * Resolves with an object containing the vanity url invite code and the use count + * @returns {Promise} + * @example + * // Fetch invite data + * guild.fetchVanityData() + * .then(res => { + * console.log(`Vanity URL: https://discord.gg/${res.code} with ${res.uses} uses`); + * }) + * .catch(console.error); + */ + async fetchVanityData() { + if (!this.features.includes('VANITY_URL')) { + throw new Error('VANITY_URL'); + } + const data = await this.client.api.guilds(this.id, 'vanity-url').get(); + this.vanityURLUses = data.uses; + + return data; + } + + /** + * Fetches all webhooks for the guild. + * @returns {Promise>} + * @example + * // Fetch webhooks + * guild.fetchWebhooks() + * .then(webhooks => console.log(`Fetched ${webhooks.size} webhooks`)) + * .catch(console.error); + */ + fetchWebhooks() { + return this.client.api + .guilds(this.id) + .webhooks.get() + .then(data => { + const hooks = new Collection(); + for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); + return hooks; + }); + } + + /** + * Fetches available voice regions. + * @returns {Promise>} + */ + fetchVoiceRegions() { + return this.client.api + .guilds(this.id) + .regions.get() + .then(res => { + const regions = new Collection(); + for (const region of res) regions.set(region.id, new VoiceRegion(region)); + return regions; + }); + } + + /** + * Data for the Guild Widget object + * @typedef {Object} GuildWidget + * @property {boolean} enabled Whether the widget is enabled + * @property {?GuildChannel} channel The widget channel + */ + + /** + * The Guild Widget object + * @typedef {Object} GuildWidgetData + * @property {boolean} enabled Whether the widget is enabled + * @property {?GuildChannelResolvable} channel The widget channel + */ + + /** + * Fetches the guild embed. + * @returns {Promise} + * @deprecated + * @example + * // Fetches the guild embed + * guild.fetchEmbed() + * .then(embed => console.log(`The embed is ${embed.enabled ? 'enabled' : 'disabled'}`)) + * .catch(console.error); + */ + fetchEmbed() { + return this.fetchWidget(); + } + + /** + * Fetches the guild widget. + * @returns {Promise} + * @example + * // Fetches the guild widget + * guild.fetchWidget() + * .then(widget => console.log(`The widget is ${widget.enabled ? 'enabled' : 'disabled'}`)) + * .catch(console.error); + */ + async fetchWidget() { + const data = await this.client.api.guilds(this.id).widget.get(); + this.widgetEnabled = this.embedEnabled = data.enabled; + this.widgetChannelID = this.embedChannelID = data.channel_id; + return { + enabled: data.enabled, + channel: data.channel_id ? this.channels.cache.get(data.channel_id) : null, + }; + } + + /** + * Fetches 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 {number} [options.limit] Limit number of entries + * @param {UserResolvable} [options.user] Only show entries involving this user + * @param {AuditLogAction|number} [options.type] Only show entries involving this action type + * @returns {Promise} + * @example + * // Output audit log entries + * guild.fetchAuditLogs() + * .then(audit => console.log(audit.entries.first())) + * .catch(console.error); + */ + fetchAuditLogs(options = {}) { + if (options.before && options.before instanceof GuildAuditLogs.Entry) options.before = options.before.id; + if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type]; + + return this.client.api + .guilds(this.id) + ['audit-logs'].get({ + query: { + before: options.before, + limit: options.limit, + user_id: this.client.users.resolveID(options.user), + action_type: options.type, + }, + }) + .then(data => GuildAuditLogs.build(this, data)); + } + + /** + * 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|RoleResolvable[]} [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} + */ + addMember(user, options) { + user = this.client.users.resolveID(user); + if (!user) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable')); + if (this.members.cache.has(user)) return Promise.resolve(this.members.cache.get(user)); + options.access_token = options.accessToken; + if (options.roles) { + const roles = []; + for (let role of options.roles instanceof Collection ? options.roles.values() : options.roles) { + role = this.roles.resolve(role); + if (!role) { + return Promise.reject( + new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true), + ); + } + roles.push(role.id); + } + options.roles = roles; + } + return this.client.api + .guilds(this.id) + .members(user) + .put({ data: options }) + .then(data => this.members.add(data)); + } + + /** + * 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 {VerificationLevel|number} [verificationLevel] The verification level of the guild + * @property {ExplicitContentFilterLevel|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 {GuildMemberResolvable} [owner] The owner of the guild + * @property {Base64Resolvable} [splash] The invite splash image of the guild + * @property {Base64Resolvable} [discoverySplash] The discovery splash image of the guild + * @property {Base64Resolvable} [banner] The banner of the guild + * @property {DefaultMessageNotifications|number} [defaultMessageNotifications] The default message notifications + * @property {SystemChannelFlagsResolvable} [systemChannelFlags] The system channel flags of the guild + * @property {ChannelResolvable} [rulesChannel] The rules channel of the guild + * @property {ChannelResolvable} [publicUpdatesChannel] The community updates channel of the guild + * @property {string} [preferredLocale] The preferred locale 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 this guild + * @returns {Promise} + * @example + * // Set the guild name and region + * guild.edit({ + * name: 'Discord Guild', + * region: 'london', + * }) + * .then(updated => console.log(`New guild name ${updated} in region ${updated.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 = + typeof data.verificationLevel === 'number' + ? Number(data.verificationLevel) + : VerificationLevels.indexOf(data.verificationLevel); + } + if (typeof data.afkChannel !== 'undefined') { + _data.afk_channel_id = this.client.channels.resolveID(data.afkChannel); + } + if (typeof data.systemChannel !== 'undefined') { + _data.system_channel_id = this.client.channels.resolveID(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.users.resolveID(data.owner); + if (data.splash) _data.splash = data.splash; + if (data.discoverySplash) _data.discovery_splash = data.discoverySplash; + if (data.banner) _data.banner = data.banner; + if (typeof data.explicitContentFilter !== 'undefined') { + _data.explicit_content_filter = + typeof data.explicitContentFilter === 'number' + ? data.explicitContentFilter + : ExplicitContentFilterLevels.indexOf(data.explicitContentFilter); + } + if (typeof data.defaultMessageNotifications !== 'undefined') { + _data.default_message_notifications = + typeof data.defaultMessageNotifications === 'string' + ? DefaultMessageNotifications.indexOf(data.defaultMessageNotifications) + : data.defaultMessageNotifications; + } + if (typeof data.systemChannelFlags !== 'undefined') { + _data.system_channel_flags = SystemChannelFlags.resolve(data.systemChannelFlags); + } + if (typeof data.rulesChannel !== 'undefined') { + _data.rules_channel_id = this.client.channels.resolveID(data.rulesChannel); + } + if (typeof data.publicUpdatesChannel !== 'undefined') { + _data.public_updates_channel_id = this.client.channels.resolveID(data.publicUpdatesChannel); + } + if (data.preferredLocale) _data.preferred_locale = data.preferredLocale; + return this.client.api + .guilds(this.id) + .patch({ data: _data, reason }) + .then(newData => this.client.actions.GuildUpdate.handle(newData).updated); + } + + /** + * Edits the level of the explicit content filter. + * @param {ExplicitContentFilterLevel|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} + */ + setExplicitContentFilter(explicitContentFilter, reason) { + return this.edit({ explicitContentFilter }, reason); + } + + /* eslint-disable max-len */ + /** + * 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} + */ + setDefaultMessageNotifications(defaultMessageNotifications, reason) { + return this.edit({ defaultMessageNotifications }, reason); + } + /* eslint-enable max-len */ + + /** + * 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} + */ + setSystemChannelFlags(systemChannelFlags, reason) { + return this.edit({ systemChannelFlags }, reason); + } + + /** + * Edits 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} + * @example + * // Edit the guild name + * guild.setName('Discord Guild') + * .then(updated => console.log(`Updated guild name to ${updated.name}`)) + * .catch(console.error); + */ + setName(name, reason) { + return this.edit({ name }, reason); + } + + /** + * Edits 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} + * @example + * // Edit the guild region + * guild.setRegion('london') + * .then(updated => console.log(`Updated guild region to ${updated.region}`)) + * .catch(console.error); + */ + setRegion(region, reason) { + return this.edit({ region }, reason); + } + + /** + * Edits the verification level of the guild. + * @param {VerificationLevel|number} verificationLevel The new verification level of the guild + * @param {string} [reason] Reason for changing the guild's verification level + * @returns {Promise} + * @example + * // Edit the guild verification level + * guild.setVerificationLevel(1) + * .then(updated => console.log(`Updated guild verification level to ${guild.verificationLevel}`)) + * .catch(console.error); + */ + setVerificationLevel(verificationLevel, reason) { + return this.edit({ verificationLevel }, reason); + } + + /** + * Edits 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} + * @example + * // Edit the guild AFK channel + * guild.setAFKChannel(channel) + * .then(updated => console.log(`Updated guild AFK channel to ${guild.afkChannel.name}`)) + * .catch(console.error); + */ + setAFKChannel(afkChannel, reason) { + return this.edit({ afkChannel }, reason); + } + + /** + * Edits 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} + * @example + * // Edit the guild system channel + * guild.setSystemChannel(channel) + * .then(updated => console.log(`Updated guild system channel to ${guild.systemChannel.name}`)) + * .catch(console.error); + */ + setSystemChannel(systemChannel, reason) { + return this.edit({ systemChannel }, reason); + } + + /** + * Edits 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} + * @example + * // Edit the guild AFK channel + * guild.setAFKTimeout(60) + * .then(updated => console.log(`Updated guild AFK timeout to ${guild.afkTimeout}`)) + * .catch(console.error); + */ + setAFKTimeout(afkTimeout, reason) { + return this.edit({ afkTimeout }, reason); + } + + /** + * Sets 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} + * @example + * // Edit the guild icon + * guild.setIcon('./icon.png') + * .then(updated => console.log('Updated the guild icon')) + * .catch(console.error); + */ + async setIcon(icon, reason) { + return this.edit({ icon: await DataResolver.resolveImage(icon), 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} + * @example + * // Edit the guild owner + * guild.setOwner(guild.members.cache.first()) + * .then(updated => console.log(`Updated the guild owner to ${updated.owner.displayName}`)) + * .catch(console.error); + */ + setOwner(owner, reason) { + return this.edit({ owner }, reason); + } + + /** + * Sets a new guild invite splash image. + * @param {Base64Resolvable|BufferResolvable} splash The new invite splash image of the guild + * @param {string} [reason] Reason for changing the guild's invite splash image + * @returns {Promise} + * @example + * // Edit the guild splash + * guild.setSplash('./splash.png') + * .then(updated => console.log('Updated the guild splash')) + * .catch(console.error); + */ + async setSplash(splash, reason) { + return this.edit({ splash: await DataResolver.resolveImage(splash), reason }); + } + + /** + * Sets a new guild discovery splash image. + * @param {Base64Resolvable|BufferResolvable} discoverySplash The new discovery splash image of the guild + * @param {string} [reason] Reason for changing the guild's discovery splash image + * @returns {Promise} + * @example + * // Edit the guild discovery splash + * guild.setDiscoverySplash('./discoverysplash.png') + * .then(updated => console.log('Updated the guild discovery splash')) + * .catch(console.error); + */ + async setDiscoverySplash(discoverySplash, reason) { + return this.edit({ discoverySplash: await DataResolver.resolveImage(discoverySplash), reason }); + } + + /** + * Sets a new guild banner. + * @param {Base64Resolvable|BufferResolvable} banner The new banner of the guild + * @param {string} [reason] Reason for changing the guild's banner + * @returns {Promise} + * @example + * guild.setBanner('./banner.png') + * .then(updated => console.log('Updated the guild banner')) + * .catch(console.error); + */ + async setBanner(banner, reason) { + return this.edit({ banner: await DataResolver.resolveImage(banner), reason }); + } + + /** + * Edits the rules channel of the guild. + * @param {ChannelResolvable} rulesChannel The new rules channel + * @param {string} [reason] Reason for changing the guild's rules channel + * @returns {Promise} + * @example + * // Edit the guild rules channel + * guild.setRulesChannel(channel) + * .then(updated => console.log(`Updated guild rules channel to ${guild.rulesChannel.name}`)) + * .catch(console.error); + */ + setRulesChannel(rulesChannel, reason) { + return this.edit({ rulesChannel }, reason); + } + + /** + * Edits the community updates channel of the guild. + * @param {ChannelResolvable} publicUpdatesChannel The new community updates channel + * @param {string} [reason] Reason for changing the guild's community updates channel + * @returns {Promise} + * @example + * // Edit the guild community updates channel + * guild.setPublicUpdatesChannel(channel) + * .then(updated => console.log(`Updated guild community updates channel to ${guild.publicUpdatesChannel.name}`)) + * .catch(console.error); + */ + setPublicUpdatesChannel(publicUpdatesChannel, reason) { + return this.edit({ publicUpdatesChannel }, reason); + } + + /** + * Edits the preferred locale of the guild. + * @param {string} preferredLocale The new preferred locale of the guild + * @param {string} [reason] Reason for changing the guild's preferred locale + * @returns {Promise} + * @example + * // Edit the guild preferred locale + * guild.setPreferredLocale('en-US') + * .then(updated => console.log(`Updated guild preferred locale to ${guild.preferredLocale}`)) + * .catch(console.error); + */ + setPreferredLocale(preferredLocale, reason) { + return this.edit({ preferredLocale }, reason); + } + + /** + * 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} + * @example + * guild.setChannelPositions([{ channel: channelID, position: newChannelIndex }]) + * .then(guild => console.log(`Updated channel positions for ${guild}`)) + * .catch(console.error); + */ + setChannelPositions(channelPositions) { + const updatedChannels = channelPositions.map(r => ({ + id: this.client.channels.resolveID(r.channel), + position: r.position, + })); + + return this.client.api + .guilds(this.id) + .channels.patch({ data: updatedChannels }) + .then( + () => + this.client.actions.GuildChannelsPositionUpdate.handle({ + guild_id: this.id, + channels: updatedChannels, + }).guild, + ); + } + + /** + * The data needed for updating a guild role's position + * @typedef {Object} GuildRolePosition + * @property {RoleResolveable} role The ID of the role + * @property {number} position The position to update + */ + + /** + * Batch-updates the guild's role positions + * @param {GuildRolePosition[]} rolePositions Role positions to update + * @returns {Promise} + * @example + * guild.setRolePositions([{ role: roleID, position: updatedRoleIndex }]) + * .then(guild => console.log(`Role permissions updated for ${guild}`)) + * .catch(console.error); + */ + setRolePositions(rolePositions) { + // Make sure rolePositions are prepared for API + rolePositions = rolePositions.map(o => ({ + id: this.roles.resolveID(o.role), + position: o.position, + })); + + // Call the API to update role positions + return this.client.api + .guilds(this.id) + .roles.patch({ + data: rolePositions, + }) + .then( + () => + this.client.actions.GuildRolesPositionUpdate.handle({ + guild_id: this.id, + roles: rolePositions, + }).guild, + ); + } + + /** + * Edits the guild's embed. + * @param {GuildWidgetData} embed The embed for the guild + * @param {string} [reason] Reason for changing the guild's embed + * @returns {Promise} + * @deprecated + */ + setEmbed(embed, reason) { + return this.setWidget(embed, reason); + } + + /** + * Edits the guild's widget. + * @param {GuildWidgetData} widget The widget for the guild + * @param {string} [reason] Reason for changing the guild's widget + * @returns {Promise} + */ + setWidget(widget, reason) { + return this.client.api + .guilds(this.id) + .widget.patch({ + data: { + enabled: widget.enabled, + channel_id: this.channels.resolveID(widget.channel), + }, + reason, + }) + .then(() => this); + } + + /** + * Leaves the guild. + * @returns {Promise} + * @example + * // Leave a guild + * guild.leave() + * .then(g => console.log(`Left the guild ${g}`)) + * .catch(console.error); + */ + leave() { + if (this.ownerID === this.client.user.id) return Promise.reject(new Error('GUILD_OWNED')); + return this.client.api + .users('@me') + .guilds(this.id) + .delete() + .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); + } + + /** + * Deletes the guild. + * @returns {Promise} + * @example + * // Delete a guild + * guild.delete() + * .then(g => console.log(`Deleted the guild ${g}`)) + * .catch(console.error); + */ + delete() { + return this.client.api + .guilds(this.id) + .delete() + .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); + } + + /** + * 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 && + guild instanceof this.constructor && + this.id === guild.id && + this.available === guild.available && + this.splash === guild.splash && + this.discoverySplash === guild.discoverySplash && + this.region === guild.region && + this.name === guild.name && + this.memberCount === guild.memberCount && + this.large === guild.large && + this.icon === guild.icon && + this.ownerID === guild.ownerID && + this.verificationLevel === guild.verificationLevel && + this.embedEnabled === guild.embedEnabled && + (this.features === guild.features || + (this.features.length === guild.features.length && + this.features.every((feat, i) => feat === guild.features[i]))); + + if (equal) { + if (this.embedChannel) { + if (!guild.embedChannel || this.embedChannel.id !== guild.embedChannel.id) equal = false; + } else if (guild.embedChannel) { + equal = false; + } + } + + return equal; + } + + /** + * When concatenated with a string, this automatically returns the guild's name instead of the Guild object. + * @returns {string} + * @example + * // Logs: Hello from My Guild! + * console.log(`Hello from ${guild}!`); + */ + toString() { + return this.name; + } + + toJSON() { + const json = super.toJSON({ + available: false, + createdTimestamp: true, + nameAcronym: true, + presences: false, + voiceStates: false, + }); + json.iconURL = this.iconURL(); + json.splashURL = this.splashURL(); + json.discoverySplashURL = this.discoverySplashURL(); + json.bannerURL = this.bannerURL(); + return json; + } + + /** + * Creates a collection of this guild's roles, sorted by their position and IDs. + * @returns {Collection} + * @private + */ + _sortedRoles() { + return Util.discordSort(this.roles.cache); + } + + /** + * Creates a collection of this guild's or a specific category's channels, sorted by their position and IDs. + * @param {GuildChannel} [channel] Category to get the channels of + * @returns {Collection} + * @private + */ + _sortedChannels(channel) { + const category = channel.type === ChannelTypes.CATEGORY; + return Util.discordSort( + this.channels.cache.filter( + c => + (['text', 'news', 'store'].includes(channel.type) + ? ['text', 'news', 'store'].includes(c.type) + : c.type === channel.type) && + (category || c.parent === channel.parent), + ), + ); + } +} + +Guild.prototype.setEmbed = deprecate(Guild.prototype.setEmbed, 'Guild#setEmbed: Use setWidget instead'); + +Guild.prototype.fetchEmbed = deprecate(Guild.prototype.fetchEmbed, 'Guild#fetchEmbed: Use fetchWidget instead'); + +Guild.prototype.fetchVanityCode = deprecate( + Guild.prototype.fetchVanityCode, + 'Guild#fetchVanityCode: Use fetchVanityData() 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..823b003 --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildAuditLogs.js @@ -0,0 +1,509 @@ +'use strict'; + +const Integration = require('./Integration'); +const Webhook = require('./Webhook'); +const Collection = require('../util/Collection'); +const { PartialTypes } = require('../util/Constants'); +const Snowflake = require('../util/Snowflake'); +const Util = require('../util/Util'); + +/** + * 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 {Object} + */ +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 {Object} + */ +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.users.add(user); + /** + * Cached webhooks + * @type {Collection} + * @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} + * @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} + */ + 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} + */ + 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|GuildEmoji|Invite|Webhook|Integration} AuditLogEntryTarget + */ + + /** + * Finds the target type from the entry action. + * @param {AuditLogAction} target The action target + * @returns {AuditLogTargetType} + */ + 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 Targets.UNKNOWN; + } + + /** + * 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'; + } + + toJSON() { + return Util.flatten(this); + } +} + +/** + * Audit logs entry. + */ +class GuildAuditLogsEntry { + 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 presentation + * @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.options.partials.includes(PartialTypes.USER) + ? guild.client.users.add({ id: data.user_id }) + : guild.client.users.cache.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.cache.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.cache.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.cache.get(data.options.id) || { id: data.options.id, type: 'member' }; + break; + + case 'role': + this.extra = guild.roles.cache.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.target = 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.options.partials.includes(PartialTypes.USER) + ? guild.client.users.add({ id: data.target_id }) + : guild.client.users.cache.get(data.target_id); + } else if (targetType === Targets.GUILD) { + this.target = guild.client.guilds.cache.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) { + this.target = guild.members.fetch(guild.client.user.id).then(me => { + if (me.permissions.has('MANAGE_GUILD')) { + const change = this.changes.find(c => c.key === 'code'); + return guild.fetchInvites().then(invites => { + this.target = invites.find(i => i.code === (change.new || change.old)); + }); + } else { + this.target = this.changes.reduce((o, c) => { + o[c.key] = c.new || c.old; + return o; + }, {}); + return this.target; + } + }); + } 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.cache.get(data.target_id) || { id: data.target_id } + : guild.client.users.cache.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`].cache.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 at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + toJSON() { + return Util.flatten(this, { createdTimestamp: true }); + } +} + +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..f397c3c --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildChannel.js @@ -0,0 +1,624 @@ +'use strict'; + +const Channel = require('./Channel'); +const Invite = require('./Invite'); +const PermissionOverwrites = require('./PermissionOverwrites'); +const Role = require('./Role'); +const { Error, TypeError } = require('../errors'); +const Collection = require('../util/Collection'); +const Permissions = require('../util/Permissions'); +const Util = require('../util/Util'); + +/** + * Represents a guild channel from any of the following: + * - {@link TextChannel} + * - {@link VoiceChannel} + * - {@link CategoryChannel} + * - {@link NewsChannel} + * - {@link StoreChannel} + * @extends {Channel} + * @abstract + */ +class GuildChannel extends Channel { + /** + * @param {Guild} guild The guild the guild channel is part of + * @param {Object} data The data for the guild channel + */ + constructor(guild, data) { + super(guild.client, data); + + /** + * The guild the channel is in + * @type {Guild} + */ + this.guild = guild; + } + + _patch(data) { + super._patch(data); + + /** + * The name of the guild channel + * @type {string} + */ + this.name = data.name; + + /** + * The raw position of the channel from discord + * @type {number} + */ + this.rawPosition = data.position; + + /** + * The ID of the category parent of this channel + * @type {?Snowflake} + */ + this.parentID = data.parent_id || null; + + /** + * A map of permission overwrites in this channel for roles and users + * @type {Collection} + */ + 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 category parent of this channel + * @type {?CategoryChannel} + * @readonly + */ + get parent() { + return this.guild.channels.cache.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.bitfield === value.deny.bitfield && + testVal.allow.bitfield === value.allow.bitfield + ); + }); + } + + /** + * The position of the channel + * @type {number} + * @readonly + */ + get position() { + const sorted = this.guild._sortedChannels(this); + return sorted.array().indexOf(sorted.get(this.id)); + } + + /** + * Gets 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 {?Readonly} + */ + permissionsFor(memberOrRole) { + const member = this.guild.members.resolve(memberOrRole); + if (member) return this.memberPermissions(member); + const role = this.guild.roles.resolve(memberOrRole); + if (role) return this.rolePermissions(role); + return null; + } + + overwritesFor(member, verified = false, roles = null) { + if (!verified) member = this.guild.members.resolve(member); + if (!member) return []; + + roles = roles || member.roles.cache; + 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, + }; + } + + /** + * Gets the overall set of permissions for a member in this channel, taking into account channel overwrites. + * @param {GuildMember} member The member to obtain the overall permissions for + * @returns {Readonly} + * @private + */ + memberPermissions(member) { + if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze(); + + const roles = member.roles.cache; + 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 {Role} role The role to obtain the overall permissions for + * @returns {Readonly} + * @private + */ + rolePermissions(role) { + if (role.permissions.has(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 role.permissions + .remove(everyoneOverwrites ? everyoneOverwrites.deny : 0) + .add(everyoneOverwrites ? everyoneOverwrites.allow : 0) + .remove(roleOverwrites ? roleOverwrites.deny : 0) + .add(roleOverwrites ? roleOverwrites.allow : 0) + .freeze(); + } + + /** + * Replaces the permission overwrites in this channel. + * @param {OverwriteResolvable[]|Collection} overwrites + * Permission overwrites the channel gets updated with + * @param {string} [reason] Reason for updating the channel overwrites + * @returns {Promise} + * @example + * channel.overwritePermissions([ + * { + * id: message.author.id, + * deny: ['VIEW_CHANNEL'], + * }, + * ], 'Needed to change permissions'); + */ + overwritePermissions(overwrites, reason) { + if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) { + return Promise.reject( + new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true), + ); + } + return this.edit({ permissionOverwrites: overwrites, reason }).then(() => this); + } + + /** + * Updates Overwrites for a user or role in this channel. (creates if non-existent) + * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update + * @param {PermissionOverwriteOptions} options The options for the update + * @param {string} [reason] Reason for creating/editing this overwrite + * @returns {Promise} + * @example + * // Update or Create permission overwrites for a message author + * message.channel.updateOverwrite(message.author, { + * SEND_MESSAGES: false + * }) + * .then(channel => console.log(channel.permissionOverwrites.get(message.author.id))) + * .catch(console.error); + */ + updateOverwrite(userOrRole, options, reason) { + userOrRole = this.guild.roles.resolve(userOrRole) || this.client.users.resolve(userOrRole); + if (!userOrRole) return Promise.reject(new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role')); + + const existing = this.permissionOverwrites.get(userOrRole.id); + if (existing) return existing.update(options, reason).then(() => this); + return this.createOverwrite(userOrRole, options, reason); + } + + /** + * Overwrites the permissions for a user or role in this channel. (replaces if existent) + * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update + * @param {PermissionOverwriteOptions} options The options for the update + * @param {string} [reason] Reason for creating/editing this overwrite + * @returns {Promise} + * @example + * // Create or Replace permissions overwrites for a message author + * message.channel.createOverwrite(message.author, { + * SEND_MESSAGES: false + * }) + * .then(channel => console.log(channel.permissionOverwrites.get(message.author.id))) + * .catch(console.error); + */ + createOverwrite(userOrRole, options, reason) { + userOrRole = this.guild.roles.resolve(userOrRole) || this.client.users.resolve(userOrRole); + if (!userOrRole) return Promise.reject(new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role')); + + const type = userOrRole instanceof Role ? 'role' : 'member'; + const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options); + + return this.client.api + .channels(this.id) + .permissions[userOrRole.id].put({ + data: { id: userOrRole.id, type, allow: allow.bitfield, deny: deny.bitfield }, + reason, + }) + .then(() => this); + } + + /** + * Locks in the permission overwrites from the parent channel. + * @returns {Promise} + */ + lockPermissions() { + if (!this.parent) return Promise.reject(new Error('GUILD_CHANNEL_ORPHAN')); + const permissionOverwrites = this.parent.permissionOverwrites.map(overwrite => overwrite.toJSON()); + return this.edit({ permissionOverwrites }); + } + + /** + * A collection of members that can see this channel, mapped by their ID + * @type {Collection} + * @readonly + */ + get members() { + const members = new Collection(); + for (const member of this.guild.members.cache.values()) { + if (this.permissionsFor(member).has('VIEW_CHANNEL', false)) { + members.set(member.id, member); + } + } + return members; + } + + /** + * The data for a guild channel. + * @typedef {Object} ChannelData + * @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 voice channel + * @property {?Snowflake} [parentID] The parent ID of the channel + * @property {boolean} [lockPermissions] + * Lock the permissions of the channel to what the parent's permissions are + * @property {OverwriteResolvable[]|Collection} [permissionOverwrites] + * Permission overwrites for the channel + * @property {number} [rateLimitPerUser] The ratelimit per user for the channel in seconds + */ + + /** + * Edits the channel. + * @param {ChannelData} data The new data for the channel + * @param {string} [reason] Reason for editing this channel + * @returns {Promise} + * @example + * // Edit a channel + * channel.edit({ name: 'new-channel' }) + * .then(console.log) + * .catch(console.error); + */ + async edit(data, reason) { + if (typeof data.position !== 'undefined') { + await Util.setPosition( + this, + data.position, + false, + this.guild._sortedChannels(this), + this.client.api.guilds(this.guild.id).channels, + reason, + ).then(updatedChannels => { + this.client.actions.GuildChannelsPositionUpdate.handle({ + guild_id: this.guild.id, + channels: updatedChannels, + }); + }); + } + + let permission_overwrites; + + if (data.permissionOverwrites) { + permission_overwrites = data.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); + } + + if (data.lockPermissions) { + if (data.parentID) { + const newParent = this.guild.channels.resolve(data.parentID); + if (newParent && newParent.type === 'category') { + permission_overwrites = newParent.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); + } + } else if (this.parent) { + permission_overwrites = this.parent.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); + } + } + + const newData = await this.client.api.channels(this.id).patch({ + data: { + name: (data.name || this.name).trim(), + topic: data.topic, + nsfw: data.nsfw, + bitrate: data.bitrate || this.bitrate, + user_limit: typeof data.userLimit !== 'undefined' ? data.userLimit : this.userLimit, + parent_id: data.parentID, + lock_permissions: data.lockPermissions, + rate_limit_per_user: data.rateLimitPerUser, + permission_overwrites, + }, + reason, + }); + + const clone = this._clone(); + clone._patch(newData); + return clone; + } + + /** + * Sets 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} + * @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); + } + + /** + * Sets the category parent of this channel. + * @param {?CategoryChannel|Snowflake} channel Parent channel + * @param {Object} [options={}] Options to pass + * @param {boolean} [options.lockPermissions=true] Lock the permissions to what the parent's permissions are + * @param {string} [options.reason] Reason for modifying the parent of this channel + * @returns {Promise} + * @example + * // Add a parent to a channel + * message.channel.setParent('355908108431917066', { lockPermissions: false }) + * .then(channel => console.log(`New parent of ${message.channel.name}: ${channel.name}`)) + * .catch(console.error); + */ + setParent(channel, { lockPermissions = true, reason } = {}) { + return this.edit( + { + // eslint-disable-next-line no-prototype-builtins + parentID: channel !== null ? (channel.hasOwnProperty('id') ? channel.id : channel) : null, + lockPermissions, + }, + reason, + ); + } + + /** + * Sets 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} + * @example + * // Set a new channel topic + * channel.setTopic('needs more rate limiting') + * .then(newChannel => console.log(`Channel's new topic is ${newChannel.topic}`)) + * .catch(console.error); + */ + setTopic(topic, reason) { + return this.edit({ topic }, reason); + } + + /** + * Sets a new position for the guild channel. + * @param {number} position The new position for the guild channel + * @param {Object} [options] Options for setting position + * @param {boolean} [options.relative=false] Change the position relative to its current value + * @param {string} [options.reason] Reason for changing the position + * @returns {Promise} + * @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, reason } = {}) { + return Util.setPosition( + this, + position, + relative, + this.guild._sortedChannels(this), + this.client.api.guilds(this.guild.id).channels, + reason, + ).then(updatedChannels => { + this.client.actions.GuildChannelsPositionUpdate.handle({ + guild_id: this.guild.id, + channels: updatedChannels, + }); + return this; + }); + } + + /** + * Creates an invite to this guild channel. + * @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} [options.reason] Reason for creating this + * @returns {Promise} + * @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({ temporary = false, maxAge = 86400, maxUses = 0, unique, reason } = {}) { + return this.client.api + .channels(this.id) + .invites.post({ + data: { + temporary, + max_age: maxAge, + max_uses: maxUses, + unique, + }, + reason, + }) + .then(invite => new Invite(this.client, invite)); + } + + /** + * Fetches a collection of invites to this guild channel. + * Resolves with a collection mapping invites by their codes. + * @returns {Promise>} + */ + async fetchInvites() { + const inviteItems = await this.client.api.channels(this.id).invites.get(); + const invites = new Collection(); + for (const inviteItem of inviteItems) { + const invite = new Invite(this.client, inviteItem); + invites.set(invite.code, invite); + } + return invites; + } + + /* eslint-disable max-len */ + /** + * Clones this channel. + * @param {Object} [options] The options + * @param {string} [options.name=this.name] Name of the new channel + * @param {OverwriteResolvable[]|Collection} [options.permissionOverwrites=this.permissionOverwrites] + * Permission overwrites of the new channel + * @param {string} [options.type=this.type] Type of the new channel + * @param {string} [options.topic=this.topic] Topic of the new channel (only text) + * @param {boolean} [options.nsfw=this.nsfw] Whether the new channel is nsfw (only text) + * @param {number} [options.bitrate=this.bitrate] Bitrate of the new channel in bits (only voice) + * @param {number} [options.userLimit=this.userLimit] Maximum amount of users allowed in the new channel (only voice) + * @param {number} [options.rateLimitPerUser=this.rateLimitPerUser] Ratelimit per user for the new channel (only text) + * @param {ChannelResolvable} [options.parent=this.parent] Parent of the new channel + * @param {string} [options.reason] Reason for cloning this channel + * @returns {Promise} + */ + clone(options = {}) { + 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, + }, + options, + ); + return this.guild.channels.create(options.name, options); + } + /* eslint-enable max-len */ + + /** + * 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.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false); + } + + /** + * Whether the channel is manageable by the client user + * @type {boolean} + * @readonly + */ + get manageable() { + if (this.client.user.id === this.guild.ownerID) return true; + if (this.type === 'voice') { + if (!this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false)) { + return false; + } + } else if (!this.viewable) { + return false; + } + return this.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false); + } + + /** + * Whether the channel is viewable by the client user + * @type {boolean} + * @readonly + */ + get viewable() { + 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.VIEW_CHANNEL, false); + } + + /** + * Deletes this channel. + * @param {string} [reason] Reason for deleting this channel + * @returns {Promise} + * @example + * // Delete the channel + * channel.delete('making room for new channels') + * .then(console.log) + * .catch(console.error); + */ + delete(reason) { + return this.client.api + .channels(this.id) + .delete({ reason }) + .then(() => this); + } +} + +module.exports = GuildChannel; diff --git a/node_modules/discord.js/src/structures/GuildEmoji.js b/node_modules/discord.js/src/structures/GuildEmoji.js new file mode 100644 index 0000000..a4ab2a5 --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildEmoji.js @@ -0,0 +1,168 @@ +'use strict'; + +const BaseGuildEmoji = require('./BaseGuildEmoji'); +const { Error } = require('../errors'); +const GuildEmojiRoleManager = require('../managers/GuildEmojiRoleManager'); +const Permissions = require('../util/Permissions'); + +/** + * Represents a custom emoji. + * @extends {BaseGuildEmoji} + */ +class GuildEmoji extends BaseGuildEmoji { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the guild emoji + * @param {Guild} guild The guild the guild emoji is part of + */ + constructor(client, data, guild) { + super(client, data, guild); + + /** + * The user who created this emoji + * @type {?User} + */ + this.author = null; + } + + /** + * The guild this emoji is part of + * @type {Guild} + * @name GuildEmoji#guild + */ + + _clone() { + const clone = super._clone(); + clone._roles = this._roles.slice(); + return clone; + } + + _patch(data) { + super._patch(data); + if (typeof data.user !== 'undefined') this.author = this.client.users.add(data.user); + } + + /** + * Whether the emoji is deletable by the client user + * @type {boolean} + * @readonly + */ + get deletable() { + if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME'); + return !this.managed && this.guild.me.hasPermission(Permissions.FLAGS.MANAGE_EMOJIS); + } + + /** + * A manager for roles this emoji is active for. + * @type {GuildEmojiRoleManager} + * @readonly + */ + get roles() { + return new GuildEmojiRoleManager(this); + } + + /** + * Fetches the author for this emoji + * @returns {Promise} + */ + async fetchAuthor() { + if (this.managed) { + throw new Error('EMOJI_MANAGED'); + } else { + if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME'); + if (!this.guild.me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS)) { + throw new Error('MISSING_MANAGE_EMOJIS_PERMISSION', this.guild); + } + } + const data = await this.client.api.guilds(this.guild.id).emojis(this.id).get(); + this._patch(data); + return this.author; + } + + /** + * Data for editing an emoji. + * @typedef {Object} GuildEmojiEditData + * @property {string} [name] The name of the emoji + * @property {Collection|RoleResolvable[]} [roles] Roles to restrict emoji to + */ + + /** + * Edits the emoji. + * @param {GuildEmojiEditData} data The new data for the emoji + * @param {string} [reason] Reason for editing this emoji + * @returns {Promise} + * @example + * // Edit an emoji + * emoji.edit({ name: 'newemoji' }) + * .then(e => console.log(`Edited emoji ${e}`)) + * .catch(console.error); + */ + edit(data, reason) { + const roles = data.roles ? data.roles.map(r => r.id || r) : undefined; + return this.client.api + .guilds(this.guild.id) + .emojis(this.id) + .patch({ + data: { + name: data.name, + roles, + }, + reason, + }) + .then(newData => { + const clone = this._clone(); + clone._patch(newData); + return clone; + }); + } + + /** + * Sets the name of the emoji. + * @param {string} name The new name for the emoji + * @param {string} [reason] Reason for changing the emoji's name + * @returns {Promise} + */ + setName(name, reason) { + return this.edit({ name }, reason); + } + + /** + * Deletes the emoji. + * @param {string} [reason] Reason for deleting the emoji + * @returns {Promise} + */ + delete(reason) { + return this.client.api + .guilds(this.guild.id) + .emojis(this.id) + .delete({ reason }) + .then(() => this); + } + + /** + * Whether this emoji is the same as another one. + * @param {GuildEmoji|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 GuildEmoji) { + return ( + other.id === this.id && + other.name === this.name && + other.managed === this.managed && + other.requiresColons === this.requiresColons && + other.roles.cache.size === this.roles.cache.size && + other.roles.cache.every(role => this.roles.cache.has(role.id)) + ); + } else { + return ( + other.id === this.id && + other.name === this.name && + other.roles.length === this.roles.cache.size && + other.roles.every(role => this.roles.cache.has(role)) + ); + } + } +} + +module.exports = GuildEmoji; 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..1bd0dfd --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildMember.js @@ -0,0 +1,414 @@ +'use strict'; + +const Base = require('./Base'); +const Role = require('./Role'); +const TextBasedChannel = require('./interfaces/TextBasedChannel'); +const { Error } = require('../errors'); +const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); +const Permissions = require('../util/Permissions'); +let Structures; + +/** + * Represents a member of a guild on Discord. + * @implements {TextBasedChannel} + * @extends {Base} + */ +class GuildMember extends Base { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the guild member + * @param {Guild} guild The guild the member is part of + */ + constructor(client, data, guild) { + super(client); + + /** + * The guild that this member is part of + * @type {Guild} + */ + this.guild = guild; + + /** + * The timestamp the member joined the guild at + * @type {?number} + */ + this.joinedTimestamp = null; + + /** + * The ID of the last message sent by the member in their guild, if one was sent + * @type {?Snowflake} + */ + this.lastMessageID = null; + + /** + * The ID of the channel for the last message sent by the member in their guild, if one was sent + * @type {?Snowflake} + */ + this.lastMessageChannelID = null; + + /** + * The timestamp of when the member used their Nitro boost on the guild, if it was used + * @type {?number} + */ + this.premiumSinceTimestamp = null; + + /** + * Whether the member has been removed from the guild + * @type {boolean} + */ + this.deleted = false; + + /** + * The nickname of this member, if they have one + * @type {?string} + */ + this.nickname = null; + + this._roles = []; + if (data) this._patch(data); + } + + _patch(data) { + if ('user' in data) { + /** + * The user that this guild member instance represents + * @type {User} + */ + this.user = this.client.users.add(data.user, true); + } + + if ('nick' in data) this.nickname = data.nick; + if ('joined_at' in data) this.joinedTimestamp = new Date(data.joined_at).getTime(); + if ('premium_since' in data) this.premiumSinceTimestamp = new Date(data.premium_since).getTime(); + if ('roles' in data) this._roles = data.roles; + } + + _clone() { + const clone = super._clone(); + clone._roles = this._roles.slice(); + return clone; + } + + /** + * Whether this GuildMember is a partial + * @type {boolean} + * @readonly + */ + get partial() { + return !this.joinedTimestamp; + } + + /** + * A manager for the roles belonging to this member + * @type {GuildMemberRoleManager} + * @readonly + */ + get roles() { + return new GuildMemberRoleManager(this); + } + + /** + * The Message object of the last message sent by the member in their guild, if one was sent + * @type {?Message} + * @readonly + */ + get lastMessage() { + const channel = this.guild.channels.cache.get(this.lastMessageChannelID); + return (channel && channel.messages.cache.get(this.lastMessageID)) || null; + } + + /** + * The voice state of this member + * @type {VoiceState} + * @readonly + */ + get voice() { + if (!Structures) Structures = require('../util/Structures'); + const VoiceState = Structures.get('VoiceState'); + return this.guild.voiceStates.cache.get(this.id) || new VoiceState(this.guild, { user_id: this.id }); + } + + /** + * 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 guild member + * @type {Presence} + * @readonly + */ + get presence() { + if (!Structures) Structures = require('../util/Structures'); + const Presence = Structures.get('Presence'); + return ( + this.guild.presences.cache.get(this.id) || + new Presence(this.client, { + user: { + id: this.id, + }, + guild: this.guild, + }) + ); + } + + /** + * The displayed color of this member in base 10 + * @type {number} + * @readonly + */ + get displayColor() { + const role = this.roles.color; + return (role && role.color) || 0; + } + + /** + * The displayed color of this member in hexadecimal + * @type {string} + * @readonly + */ + get displayHexColor() { + const role = this.roles.color; + return (role && role.hexColor) || '#000000'; + } + + /** + * The ID of this member + * @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 {Readonly} + * @readonly + */ + get permissions() { + if (this.user.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze(); + return new Permissions(this.roles.cache.map(role => role.permissions)).freeze(); + } + + /** + * Whether the client user is above this user in the hierarchy, according to role position and guild ownership. + * This is a prerequisite for many moderative actions. + * @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; + if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME'); + return this.guild.me.roles.highest.comparePositionTo(this.roles.highest) > 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 a member in a guild channel, + * taking into account roles and permission overwrites. + * @param {ChannelResolvable} channel The guild channel to use as context + * @returns {Readonly} + */ + permissionsIn(channel) { + channel = this.guild.channels.resolve(channel); + if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE'); + return channel.memberPermissions(this); + } + + /** + * Checks if any of this member's roles have a permission. + * @param {PermissionResolvable} permission Permission(s) to check for + * @param {Object} [options] Options + * @param {boolean} [options.checkAdmin=true] Whether to allow the administrator permission to override + * @param {boolean} [options.checkOwner=true] Whether to allow being the guild's owner to override + * @returns {boolean} + */ + hasPermission(permission, { checkAdmin = true, checkOwner = true } = {}) { + if (checkOwner && this.user.id === this.guild.ownerID) return true; + const permissions = new Permissions(this.roles.cache.map(role => role.permissions)); + return permissions.has(permission, checkAdmin); + } + + /** + * The data for editing a guild member. + * @typedef {Object} GuildMemberEditData + * @property {string} [nick] The nickname to set for the member + * @property {Collection|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} + */ + async edit(data, reason) { + if (data.channel) { + data.channel = this.guild.channels.resolve(data.channel); + if (!data.channel || data.channel.type !== 'voice') { + throw new Error('GUILD_VOICE_CHANNEL_RESOLVE'); + } + data.channel_id = data.channel.id; + data.channel = undefined; + } else if (data.channel === null) { + data.channel_id = null; + data.channel = undefined; + } + if (data.roles) data.roles = data.roles.map(role => (role instanceof Role ? role.id : role)); + let endpoint = this.client.api.guilds(this.guild.id); + if (this.user.id === this.client.user.id) { + const keys = Object.keys(data); + if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members('@me').nick; + else endpoint = endpoint.members(this.id); + } else { + endpoint = endpoint.members(this.id); + } + await endpoint.patch({ data, reason }); + + const clone = this._clone(); + data.user = this.user; + clone._patch(data); + return clone; + } + + /** + * Sets the nickname for this member. + * @param {string} nick The nickname for the guild member + * @param {string} [reason] Reason for setting the nickname + * @returns {Promise} + */ + setNickname(nick, reason) { + return this.edit({ nick }, reason); + } + + /** + * Creates a DM channel between the client and this member. + * @returns {Promise} + */ + createDM() { + return this.user.createDM(); + } + + /** + * Deletes any DMs with this member. + * @returns {Promise} + */ + deleteDM() { + return this.user.deleteDM(); + } + + /** + * Kicks this member from the guild. + * @param {string} [reason] Reason for kicking user + * @returns {Promise} + */ + kick(reason) { + return this.client.api + .guilds(this.guild.id) + .members(this.user.id) + .delete({ reason }) + .then(() => this); + } + + /** + * Bans this guild member. + * @param {Object} [options] Options for the ban + * @param {number} [options.days=0] Number of days of messages to delete, must be between 0 and 7 + * @param {string} [options.reason] Reason for banning + * @returns {Promise} + * @example + * // ban a guild member + * guildMember.ban({ days: 7, reason: 'They deserved it' }) + * .then(console.log) + * .catch(console.error); + */ + ban(options) { + return this.guild.members.ban(this, options); + } + + /** + * Fetches this GuildMember. + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + */ + fetch(force = false) { + return this.guild.members.fetch({ user: this.id, cache: true, force }); + } + + /** + * When concatenated with a string, this automatically returns the user's mention instead of the GuildMember object. + * @returns {string} + * @example + * // Logs: Hello from <@123456789012345678>! + * console.log(`Hello from ${member}!`); + */ + toString() { + return `<@${this.nickname ? '!' : ''}${this.user.id}>`; + } + + toJSON() { + return super.toJSON({ + guild: 'guildID', + user: 'userID', + displayName: true, + speaking: false, + lastMessage: false, + lastMessageID: false, + roles: true, + }); + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + send() {} +} + +TextBasedChannel.applyToClass(GuildMember); + +module.exports = GuildMember; diff --git a/node_modules/discord.js/src/structures/GuildPreview.js b/node_modules/discord.js/src/structures/GuildPreview.js new file mode 100644 index 0000000..76f5342 --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildPreview.js @@ -0,0 +1,157 @@ +'use strict'; + +const Base = require('./Base'); +const GuildPreviewEmoji = require('./GuildPreviewEmoji'); +const Collection = require('../util/Collection'); + +/** + * Represents the data about the guild any bot can preview, connected to the specified guild. + * @extends {Base} + */ +class GuildPreview extends Base { + constructor(client, data) { + super(client); + + if (!data) return; + + this._patch(data); + } + + /** + * Builds the guild with the provided data. + * @param {*} data The raw data of the guild + * @private + */ + _patch(data) { + /** + * The id of this guild + * @type {string} + */ + this.id = data.id; + + /** + * The name of this guild + * @type {string} + */ + this.name = data.name; + + /** + * The icon of this guild + * @type {?string} + */ + this.icon = data.icon; + + /** + * The splash icon of this guild + * @type {?string} + */ + this.splash = data.splash; + + /** + * The discovery splash icon of this guild + * @type {?string} + */ + this.discoverySplash = data.discovery_splash; + + /** + * An array of enabled guild features + * @type {Features[]} + */ + this.features = data.features; + + /** + * The approximate count of members in this guild + * @type {number} + */ + this.approximateMemberCount = data.approximate_member_count; + + /** + * The approximate count of online members in this guild + * @type {number} + */ + this.approximatePresenceCount = data.approximate_presence_count; + + /** + * The description for this guild + * @type {?string} + */ + this.description = data.description || null; + + if (!this.emojis) { + /** + * Collection of emojis belonging to this guild + * @type {Collection} + */ + this.emojis = new Collection(); + } else { + this.emojis.clear(); + } + for (const emoji of data.emojis) { + this.emojis.set(emoji.id, new GuildPreviewEmoji(this.client, emoji, this)); + } + } + + /** + * The URL to this guild's splash. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + splashURL({ format, size } = {}) { + if (!this.splash) return null; + return this.client.rest.cdn.Splash(this.id, this.splash, format, size); + } + + /** + * The URL to this guild's discovery splash. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + discoverySplashURL({ format, size } = {}) { + if (!this.discoverySplash) return null; + return this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size); + } + + /** + * The URL to this guild's icon. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + iconURL({ format, size, dynamic } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.Icon(this.id, this.icon, format, size, dynamic); + } + + /** + * Fetches this guild. + * @returns {Promise} + */ + fetch() { + return this.client.api + .guilds(this.id) + .preview.get() + .then(data => { + this._patch(data); + return this; + }); + } + + /** + * When concatenated with a string, this automatically returns the guild's name instead of the Guild object. + * @returns {string} + * @example + * // Logs: Hello from My Guild! + * console.log(`Hello from ${previewGuild}!`); + */ + toString() { + return this.name; + } + + toJSON() { + const json = super.toJSON(); + json.iconURL = this.iconURL(); + json.splashURL = this.splashURL(); + return json; + } +} + +module.exports = GuildPreview; diff --git a/node_modules/discord.js/src/structures/GuildPreviewEmoji.js b/node_modules/discord.js/src/structures/GuildPreviewEmoji.js new file mode 100644 index 0000000..4c70903 --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildPreviewEmoji.js @@ -0,0 +1,26 @@ +'use strict'; + +const BaseGuildEmoji = require('./BaseGuildEmoji'); + +/** + * Represents an instance of an emoji belonging to a public guild obtained through Discord's preview endpoint. + * @extends {BaseGuildEmoji} + */ +class GuildPreviewEmoji extends BaseGuildEmoji { + /** + * The public guild this emoji is part of + * @type {GuildPreview} + * @name GuildPreviewEmoji#guild + */ + + /** + * Set of roles this emoji is active for + * @type {Set} + * @readonly + */ + get roles() { + return new Set(this._roles); + } +} + +module.exports = GuildPreviewEmoji; 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..a48019a --- /dev/null +++ b/node_modules/discord.js/src/structures/Integration.js @@ -0,0 +1,186 @@ +'use strict'; + +const Base = require('./Base'); +const IntegrationApplication = require('./IntegrationApplication'); + +/** + * 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 extends Base { + constructor(client, data, guild) { + super(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.cache.get(data.role_id); + + if (data.user) { + /** + * The user for this integration + * @type {?User} + */ + this.user = this.client.users.add(data.user); + } else { + this.user = null; + } + + /** + * 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; + + if ('application' in data) { + if (this.application) { + this.application._patch(data.application); + } else { + /** + * The application for this integration + * @type {?IntegrationApplication} + */ + this.application = new IntegrationApplication(this.client, data.application); + } + } else if (!this.application) { + this.application = null; + } + } + + /** + * Sync this integration + * @returns {Promise} + */ + sync() { + this.syncing = true; + return this.client.api + .guilds(this.guild.id) + .integrations(this.id) + .post() + .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} + */ + edit(data, reason) { + if ('expireBehavior' in data) { + data.expire_behavior = data.expireBehavior; + data.expireBehavior = null; + } + if ('expireGracePeriod' in data) { + data.expire_grace_period = data.expireGracePeriod; + data.expireGracePeriod = null; + } + // The option enable_emoticons is only available for Twitch at this moment + return this.client.api + .guilds(this.guild.id) + .integrations(this.id) + .patch({ data, reason }) + .then(() => { + this._patch(data); + return this; + }); + } + + /** + * Deletes this integration. + * @returns {Promise} + * @param {string} [reason] Reason for deleting this integration + */ + delete(reason) { + return this.client.api + .guilds(this.guild.id) + .integrations(this.id) + .delete({ reason }) + .then(() => this); + } + + toJSON() { + return super.toJSON({ + role: 'roleID', + guild: 'guildID', + user: 'userID', + }); + } +} + +module.exports = Integration; diff --git a/node_modules/discord.js/src/structures/IntegrationApplication.js b/node_modules/discord.js/src/structures/IntegrationApplication.js new file mode 100644 index 0000000..40f433a --- /dev/null +++ b/node_modules/discord.js/src/structures/IntegrationApplication.js @@ -0,0 +1,25 @@ +'use strict'; + +const Application = require('./interfaces/Application'); + +/** + * Represents an Integration's OAuth2 Application. + * @extends {Application} + */ +class IntegrationApplication extends Application { + _patch(data) { + super._patch(data); + + if (typeof data.bot !== 'undefined') { + /** + * The bot {@link User user} for this application + * @type {?User} + */ + this.bot = this.client.users.add(data.bot); + } else if (!this.bot) { + this.bot = null; + } + } +} + +module.exports = IntegrationApplication; 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..6833266 --- /dev/null +++ b/node_modules/discord.js/src/structures/Invite.js @@ -0,0 +1,194 @@ +'use strict'; + +const Base = require('./Base'); +const { Endpoints } = require('../util/Constants'); +const Permissions = require('../util/Permissions'); + +/** + * Represents an invitation to a guild channel. + * The only guaranteed properties are `code`, `channel`, and `url`. Other properties can be missing. + * @extends {Base} + */ +class Invite extends Base { + constructor(client, data) { + super(client); + this._patch(data); + } + + _patch(data) { + /** + * The guild the invite is for + * @type {?Guild} + */ + this.guild = data.guild ? this.client.guilds.add(data.guild, false) : null; + + /** + * 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 = 'approximate_presence_count' in data ? data.approximate_presence_count : null; + + /** + * The approximate total number of members of the guild this invite is for + * @type {?number} + */ + this.memberCount = 'approximate_member_count' in data ? data.approximate_member_count : null; + + /** + * Whether or not this invite is temporary + * @type {?boolean} + */ + this.temporary = 'temporary' in data ? data.temporary : null; + + /** + * The maximum age of the invite, in seconds, 0 if never expires + * @type {?number} + */ + this.maxAge = 'max_age' in data ? data.max_age : null; + + /** + * How many times this invite has been used + * @type {?number} + */ + this.uses = 'uses' in data ? data.uses : null; + + /** + * The maximum uses of this invite + * @type {?number} + */ + this.maxUses = 'max_uses' in data ? data.max_uses : null; + + /** + * The user who created this invite + * @type {?User} + */ + this.inviter = data.inviter ? this.client.users.add(data.inviter) : null; + + /** + * The target user for this invite + * @type {?User} + */ + this.targetUser = data.target_user ? this.client.users.add(data.target_user) : null; + + /** + * The type of the target user: + * * 1: STREAM + * @typedef {number} TargetUser + */ + + /** + * The target user type + * @type {?TargetUser} + */ + this.targetUserType = typeof data.target_user_type === 'number' ? data.target_user_type : null; + + /** + * The channel the invite is for + * @type {Channel} + */ + this.channel = this.client.channels.add(data.channel, this.guild, false); + + /** + * The timestamp the invite was created at + * @type {?number} + */ + this.createdTimestamp = 'created_at' in data ? new Date(data.created_at).getTime() : null; + } + + /** + * The time the invite was created at + * @type {?Date} + * @readonly + */ + get createdAt() { + return this.createdTimestamp ? new Date(this.createdTimestamp) : null; + } + + /** + * Whether the invite is deletable by the client user + * @type {boolean} + * @readonly + */ + get deletable() { + const guild = this.guild; + if (!guild || !this.client.guilds.cache.has(guild.id)) return false; + if (!guild.me) throw new Error('GUILD_UNCACHED_ME'); + return ( + this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false) || + guild.me.permissions.has(Permissions.FLAGS.MANAGE_GUILD) + ); + } + + /** + * The timestamp the invite will expire at + * @type {?number} + * @readonly + */ + get expiresTimestamp() { + return this.createdTimestamp && this.maxAge ? this.createdTimestamp + this.maxAge * 1000 : null; + } + + /** + * The time the invite will expire at + * @type {?Date} + * @readonly + */ + get expiresAt() { + const { expiresTimestamp } = this; + return expiresTimestamp ? new Date(expiresTimestamp) : null; + } + + /** + * The URL to the invite + * @type {string} + * @readonly + */ + get url() { + return Endpoints.invite(this.client.options.http.invite, this.code); + } + + /** + * Deletes this invite. + * @param {string} [reason] Reason for deleting this invite + * @returns {Promise} + */ + delete(reason) { + return this.client.api.invites[this.code].delete({ reason }).then(() => this); + } + + /** + * 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; + } + + toJSON() { + return super.toJSON({ + url: true, + expiresTimestamp: true, + presenceCount: false, + memberCount: false, + uses: false, + channel: 'channelID', + inviter: 'inviterID', + guild: 'guildID', + }); + } + + valueOf() { + return this.code; + } +} + +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..1b4302d --- /dev/null +++ b/node_modules/discord.js/src/structures/Message.js @@ -0,0 +1,683 @@ +'use strict'; + +const APIMessage = require('./APIMessage'); +const Base = require('./Base'); +const ClientApplication = require('./ClientApplication'); +const MessageAttachment = require('./MessageAttachment'); +const Embed = require('./MessageEmbed'); +const Mentions = require('./MessageMentions'); +const ReactionCollector = require('./ReactionCollector'); +const { Error, TypeError } = require('../errors'); +const ReactionManager = require('../managers/ReactionManager'); +const Collection = require('../util/Collection'); +const { MessageTypes } = require('../util/Constants'); +const MessageFlags = require('../util/MessageFlags'); +const Permissions = require('../util/Permissions'); +const SnowflakeUtil = require('../util/Snowflake'); +const Util = require('../util/Util'); + +/** + * Represents a message on Discord. + * @extends {Base} + */ +class Message extends Base { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the message + * @param {TextChannel|DMChannel|NewsChannel} channel The channel the message was sent in + */ + constructor(client, data, channel) { + super(client); + + /** + * The channel that the message was sent in + * @type {TextChannel|DMChannel|NewsChannel} + */ + this.channel = channel; + + /** + * Whether this message has been deleted + * @type {boolean} + */ + this.deleted = false; + + if (data) this._patch(data); + } + + _patch(data) { + /** + * The ID of the message + * @type {Snowflake} + */ + this.id = data.id; + + if ('type' in data) { + /** + * The type of the message + * @type {?MessageType} + */ + this.type = MessageTypes[data.type]; + + /** + * Whether or not this message was sent by Discord, not actually a user (e.g. pin notifications) + * @type {?boolean} + */ + this.system = data.type !== 0; + } else if (typeof this.type !== 'string') { + this.system = null; + this.type = null; + } + + if ('content' in data) { + /** + * The content of the message + * @type {?string} + */ + this.content = data.content; + } else if (typeof this.content !== 'string') { + this.content = null; + } + + if ('author' in data) { + /** + * The author of the message + * @type {?User} + */ + this.author = this.client.users.add(data.author, !data.webhook_id); + } else if (!this.author) { + this.author = null; + } + + if ('pinned' in data) { + /** + * Whether or not this message is pinned + * @type {?boolean} + */ + this.pinned = Boolean(data.pinned); + } else if (typeof this.pinned !== 'boolean') { + this.pinned = null; + } + + if ('tts' in data) { + /** + * Whether or not the message was Text-To-Speech + * @type {?boolean} + */ + this.tts = data.tts; + } else if (typeof this.tts !== 'boolean') { + this.tts = null; + } + + /** + * A random number or string used for checking message delivery + * This is only received after the message was sent successfully, and + * lost if re-fetched + * @type {?string} + */ + this.nonce = 'nonce' in data ? data.nonce : null; + + /** + * A list of embeds in the message - e.g. YouTube Player + * @type {MessageEmbed[]} + */ + this.embeds = (data.embeds || []).map(e => new Embed(e, true)); + + /** + * A collection of attachments in the message - e.g. Pictures - mapped by their ID + * @type {Collection} + */ + this.attachments = new Collection(); + if (data.attachments) { + for (const attachment of data.attachments) { + this.attachments.set(attachment.id, new MessageAttachment(attachment.url, attachment.filename, attachment)); + } + } + + /** + * The timestamp the message was sent at + * @type {number} + */ + this.createdTimestamp = SnowflakeUtil.deconstruct(this.id).timestamp; + + /** + * The timestamp the message was last edited at (if applicable) + * @type {?number} + */ + this.editedTimestamp = 'edited_timestamp' in data ? new Date(data.edited_timestamp).getTime() : null; + + /** + * A manager of the reactions belonging to this message + * @type {ReactionManager} + */ + this.reactions = new ReactionManager(this); + if (data.reactions && data.reactions.length > 0) { + for (const reaction of data.reactions) { + this.reactions.add(reaction); + } + } + + /** + * 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; + + /** + * Supplemental application information for group activities + * @type {?ClientApplication} + */ + this.application = data.application ? new ClientApplication(this.client, data.application) : null; + + /** + * Group activity + * @type {?MessageActivity} + */ + this.activity = data.activity + ? { + partyID: data.activity.party_id, + type: data.activity.type, + } + : null; + + /** + * The previous versions of the message, sorted with the most recent first + * @type {Message[]} + * @private + */ + this._edits = []; + + if (this.member && data.member) { + this.member._patch(data.member); + } else if (data.member && this.guild && this.author) { + this.guild.members.add(Object.assign(data.member, { user: this.author })); + } + + /** + * Flags that are applied to the message + * @type {Readonly} + */ + 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; + } + + /** + * Whether or not this message is a partial + * @type {boolean} + * @readonly + */ + get partial() { + return typeof this.content !== 'string' || !this.author; + } + + /** + * Updates the message and returns the old message. + * @param {Object} data Raw Discord message update data + * @returns {Message} + * @private + */ + patch(data) { + const clone = this._clone(); + const { messageEditHistoryMaxSize } = this.client.options; + if (messageEditHistoryMaxSize !== 0) { + const editsLimit = messageEditHistoryMaxSize === -1 ? Infinity : messageEditHistoryMaxSize; + if (this._edits.unshift(clone) > editsLimit) this._edits.pop(); + } + + 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(e, true)); + 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 MessageAttachment(attachment.url, attachment.filename, attachment)); + } + } else { + this.attachments = new Collection(this.attachments); + } + + this.mentions = new Mentions( + this, + 'mentions' in data ? data.mentions : this.mentions.users, + 'mention_roles' in data ? data.mention_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(); + + return clone; + } + + /** + * 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} + * @readonly + */ + get member() { + return this.guild ? this.guild.member(this.author) || null : null; + } + + /** + * The time the message was sent at + * @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 this message + * @type {string} + * @readonly + */ + get url() { + return `https://discord.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() { + // eslint-disable-next-line eqeqeq + return this.content != null ? Util.cleanContent(this.content, this) : null; + } + + /** + * 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 createReactionCollector 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>} + * @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, false))) + ); + } + + /** + * 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, false)) + ); + } + + /** + * Options that can be passed into editMessage. + * @typedef {Object} MessageEditOptions + * @property {string} [content] Content to be edited + * @property {MessageEmbed|Object} [embed] An embed to be added/edited + * @property {string|boolean} [code] Language for optional codeblock formatting to apply + * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content + */ + + /** + * Edits the content of the message. + * @param {StringResolvable|APIMessage} [content] The new content for the message + * @param {MessageEditOptions|MessageEmbed} [options] The options to provide + * @returns {Promise} + * @example + * // Update the content of a message + * message.edit('This is my new content!') + * .then(msg => console.log(`Updated the content of a message to ${msg.content}`)) + * .catch(console.error); + */ + edit(content, options) { + const { data } = + content instanceof APIMessage ? content.resolveData() : APIMessage.create(this, content, options).resolveData(); + return this.client.api.channels[this.channel.id].messages[this.id].patch({ data }).then(d => { + const clone = this._clone(); + clone._patch(d); + return clone; + }); + } + + /** + * Publishes a message in an announcement channel to all channels following it. + * @returns {Promise} + * @example + * // Crosspost a message + * if (message.channel.type === 'news') { + * message.crosspost() + * .then(() => console.log('Crossposted message')) + * .catch(console.error); + * } + */ + async crosspost() { + await this.client.api.channels(this.channel.id).messages(this.id).crosspost.post(); + return this; + } + + /** + * Pins this message to the channel's pinned messages. + * @param {Object} [options] Options for pinning + * @param {string} [options.reason] Reason for pinning + * @returns {Promise} + * @example + * // Pin a message with a reason + * message.pin({ reason: 'important' }) + * .then(console.log) + * .catch(console.error) + */ + pin(options) { + return this.client.api + .channels(this.channel.id) + .pins(this.id) + .put(options) + .then(() => this); + } + + /** + * Unpins this message from the channel's pinned messages. + * @param {Object} [options] Options for unpinning + * @param {string} [options.reason] Reason for unpinning + * @returns {Promise} + * @example + * // Unpin a message with a reason + * message.unpin({ reason: 'no longer relevant' }) + * .then(console.log) + * .catch(console.error) + */ + unpin(options) { + return this.client.api + .channels(this.channel.id) + .pins(this.id) + .delete(options) + .then(() => this); + } + + /** + * Adds a reaction to the message. + * @param {EmojiIdentifierResolvable} emoji The emoji to react with + * @returns {Promise} + * @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.cache.get('123456789012345678')) + * .then(console.log) + * .catch(console.error); + */ + react(emoji) { + emoji = this.client.emojis.resolveIdentifier(emoji); + if (!emoji) throw new TypeError('EMOJI_TYPE'); + + return this.client.api + .channels(this.channel.id) + .messages(this.id) + .reactions(emoji, '@me') + .put() + .then( + () => + this.client.actions.MessageReactionAdd.handle({ + user: this.client.user, + channel: this.channel, + message: this, + emoji: Util.parseEmoji(emoji), + }).reaction, + ); + } + + /** + * Deletes the message. + * @param {Object} [options] Options + * @param {number} [options.timeout=0] How long to wait to delete the message in milliseconds + * @param {string} [options.reason] Reason for deleting this message, if it does not belong to the client user + * @returns {Promise} + * @example + * // Delete a message + * message.delete({ timeout: 5000 }) + * .then(msg => console.log(`Deleted message from ${msg.author.username} after 5 seconds`)) + * .catch(console.error); + */ + delete(options = {}) { + if (typeof options !== 'object') return Promise.reject(new TypeError('INVALID_TYPE', 'options', 'object', true)); + const { timeout = 0, reason } = options; + if (timeout <= 0) { + return this.channel.messages.delete(this.id, reason).then(() => this); + } else { + return new Promise(resolve => { + this.client.setTimeout(() => { + resolve(this.delete({ reason })); + }, timeout); + }); + } + } + + /** + * Replies to the message. + * @param {StringResolvable|APIMessage} [content=''] The content for the message + * @param {MessageOptions|MessageAdditions} [options={}] The options to provide + * @returns {Promise} + * @example + * // Reply to a message + * message.reply('Hey, I\'m a reply!') + * .then(() => console.log(`Sent a reply to ${message.author.username}`)) + * .catch(console.error); + */ + reply(content, options) { + return this.channel.send( + content instanceof APIMessage + ? content + : APIMessage.transformOptions(content, options, { reply: this.member || this.author }), + ); + } + + /** + * Fetch this message. + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + */ + fetch(force = false) { + return this.channel.messages.fetch(this.id, true, force); + } + + /** + * Fetches the webhook used to create this message. + * @returns {Promise} + */ + fetchWebhook() { + if (!this.webhookID) return Promise.reject(new Error('WEBHOOK_MESSAGE')); + 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} + */ + 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({ 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; + } + + toJSON() { + return super.toJSON({ + channel: 'channelID', + author: 'authorID', + application: 'applicationID', + guild: 'guildID', + cleanContent: true, + member: false, + reactions: false, + }); + } +} + +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..f5fb723 --- /dev/null +++ b/node_modules/discord.js/src/structures/MessageAttachment.js @@ -0,0 +1,98 @@ +'use strict'; + +const Util = require('../util/Util'); + +/** + * Represents an attachment in a message. + */ +class MessageAttachment { + /** + * @param {BufferResolvable|Stream} attachment The file + * @param {string} [name=null] The name of the file, if any + * @param {Object} [data] Extra data + */ + constructor(attachment, name = null, data) { + this.attachment = attachment; + /** + * The name of this attachment + * @type {?string} + */ + this.name = name; + if (data) this._patch(data); + } + + /** + * Sets the file of this attachment. + * @param {BufferResolvable|Stream} attachment The file + * @param {string} [name=null] The name of the file, if any + * @returns {MessageAttachment} This attachment + */ + setFile(attachment, name = null) { + this.attachment = attachment; + this.name = name; + return this; + } + + /** + * Sets the name of this attachment. + * @param {string} name The name of the file + * @returns {MessageAttachment} This attachment + */ + setName(name) { + this.name = name; + return this; + } + + _patch(data) { + /** + * The ID of this attachment + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The size of this attachment in bytes + * @type {number} + */ + this.size = 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 or video) + * @type {?number} + */ + this.height = typeof data.height !== 'undefined' ? data.height : null; + + /** + * The width of this attachment (if an image or video) + * @type {?number} + */ + this.width = typeof data.width !== 'undefined' ? data.width : null; + } + + /** + * Whether or not this attachment has been marked as a spoiler + * @type {boolean} + * @readonly + */ + get spoiler() { + return Util.basename(this.url).startsWith('SPOILER_'); + } + + toJSON() { + return Util.flatten(this); + } +} + +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..740928d --- /dev/null +++ b/node_modules/discord.js/src/structures/MessageCollector.js @@ -0,0 +1,129 @@ +'use strict'; + +const Collector = require('./interfaces/Collector'); +const { Events } = require('../util/Constants'); + +/** + * @typedef {CollectorOptions} MessageCollectorOptions + * @property {number} max The maximum amount of messages to collect + * @property {number} maxProcessed The maximum amount of messages to process + */ + +/** + * Collects messages on a channel. + * Will automatically stop if the channel (`'channelDelete'`) or guild (`'guildDelete'`) are deleted. + * @extends {Collector} + */ +class MessageCollector extends Collector { + /** + * @param {TextChannel|DMChannel} 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; + + const bulkDeleteListener = messages => { + for (const message of messages.values()) this.handleDispose(message); + }; + this._handleChannelDeletion = this._handleChannelDeletion.bind(this); + this._handleGuildDeletion = this._handleGuildDeletion.bind(this); + + this.client.incrementMaxListeners(); + this.client.on(Events.MESSAGE_CREATE, this.handleCollect); + this.client.on(Events.MESSAGE_DELETE, this.handleDispose); + this.client.on(Events.MESSAGE_BULK_DELETE, bulkDeleteListener); + this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion); + + this.once('end', () => { + this.client.removeListener(Events.MESSAGE_CREATE, this.handleCollect); + this.client.removeListener(Events.MESSAGE_DELETE, this.handleDispose); + this.client.removeListener(Events.MESSAGE_BULK_DELETE, bulkDeleteListener); + this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion); + this.client.decrementMaxListeners(); + }); + } + + /** + * Handles a message for possible collection. + * @param {Message} message The message that could be collected + * @returns {?Snowflake} + * @private + */ + collect(message) { + /** + * Emitted whenever a message is collected. + * @event MessageCollector#collect + * @param {Message} message The message that was collected + */ + if (message.channel.id !== this.channel.id) return null; + this.received++; + return message.id; + } + + /** + * Handles a message for possible disposal. + * @param {Message} message The message that could be disposed of + * @returns {?Snowflake} + */ + dispose(message) { + /** + * Emitted whenever a message is disposed of. + * @event MessageCollector#dispose + * @param {Message} message The message that was disposed of + */ + return message.channel.id === this.channel.id ? message.id : null; + } + + /** + * Checks after un/collection to see if the collector is done. + * @returns {?string} + * @private + */ + endReason() { + if (this.options.max && this.collected.size >= this.options.max) return 'limit'; + if (this.options.maxProcessed && this.received === this.options.maxProcessed) return 'processedLimit'; + return null; + } + + /** + * Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'. + * @private + * @param {GuildChannel} channel The channel that was deleted + * @returns {void} + */ + _handleChannelDeletion(channel) { + if (channel.id === this.channel.id) { + this.stop('channelDelete'); + } + } + + /** + * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'. + * @private + * @param {Guild} guild The guild that was deleted + * @returns {void} + */ + _handleGuildDeletion(guild) { + if (this.channel.guild && guild.id === this.channel.guild.id) { + this.stop('guildDelete'); + } + } +} + +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..ef007a5 --- /dev/null +++ b/node_modules/discord.js/src/structures/MessageEmbed.js @@ -0,0 +1,461 @@ +'use strict'; + +const { RangeError } = require('../errors'); +const Util = require('../util/Util'); + +/** + * Represents an embed in a message (image/video preview, rich embed, etc.) + */ +class MessageEmbed { + /** + * @name MessageEmbed + * @kind constructor + * @memberof MessageEmbed + * @param {MessageEmbed|Object} [data={}] MessageEmbed to clone or raw embed data + */ + + constructor(data = {}, skipValidation = false) { + this.setup(data, skipValidation); + } + + setup(data, skipValidation) { + /** + * The type of this embed, either: + * * `rich` - a rich embed + * * `image` - an image embed + * * `video` - a video embed + * * `gifv` - a gifv embed + * * `article` - an article embed + * * `link` - a link embed + * @type {string} + */ + this.type = data.type || 'rich'; + + /** + * The title of this embed + * @type {?string} + */ + this.title = 'title' in data ? data.title : null; + + /** + * The description of this embed + * @type {?string} + */ + this.description = 'description' in data ? data.description : null; + + /** + * The URL of this embed + * @type {?string} + */ + this.url = 'url' in data ? data.url : null; + + /** + * The color of this embed + * @type {?number} + */ + this.color = 'color' in data ? Util.resolveColor(data.color) : null; + + /** + * The timestamp of this embed + * @type {?number} + */ + this.timestamp = 'timestamp' in data ? new Date(data.timestamp).getTime() : null; + + /** + * Represents a field of a MessageEmbed + * @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 + */ + + /** + * The fields of this embed + * @type {EmbedField[]} + */ + this.fields = []; + if (data.fields) { + this.fields = skipValidation ? data.fields.map(Util.cloneObject) : this.constructor.normalizeFields(data.fields); + } + + /** + * Represents the thumbnail of a MessageEmbed + * @typedef {Object} MessageEmbedThumbnail + * @property {string} url URL for this thumbnail + * @property {string} proxyURL ProxyURL for this thumbnail + * @property {number} height Height of this thumbnail + * @property {number} width Width of this thumbnail + */ + + /** + * The thumbnail of this embed (if there is one) + * @type {?MessageEmbedThumbnail} + */ + this.thumbnail = data.thumbnail + ? { + url: data.thumbnail.url, + proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url, + height: data.thumbnail.height, + width: data.thumbnail.width, + } + : null; + + /** + * Represents the image of a MessageEmbed + * @typedef {Object} MessageEmbedImage + * @property {string} url URL for this image + * @property {string} proxyURL ProxyURL for this image + * @property {number} height Height of this image + * @property {number} width Width of this image + */ + + /** + * The image of this embed, if there is one + * @type {?MessageEmbedImage} + */ + this.image = data.image + ? { + url: data.image.url, + proxyURL: data.image.proxyURL || data.image.proxy_url, + height: data.image.height, + width: data.image.width, + } + : null; + + /** + * Represents the video of a MessageEmbed + * @typedef {Object} MessageEmbedVideo + * @property {string} url URL of this video + * @property {string} proxyURL ProxyURL for this video + * @property {number} height Height of this video + * @property {number} width Width of this video + */ + + /** + * The video of this embed (if there is one) + * @type {?MessageEmbedVideo} + * @readonly + */ + this.video = data.video + ? { + url: data.video.url, + proxyURL: data.video.proxyURL || data.video.proxy_url, + height: data.video.height, + width: data.video.width, + } + : null; + + /** + * Represents the author field of a MessageEmbed + * @typedef {Object} MessageEmbedAuthor + * @property {string} name The name of this author + * @property {string} url URL of this author + * @property {string} iconURL URL of the icon for this author + * @property {string} proxyIconURL Proxied URL of the icon for this author + */ + + /** + * The author of this embed (if there is one) + * @type {?MessageEmbedAuthor} + */ + this.author = data.author + ? { + name: data.author.name, + url: data.author.url, + iconURL: data.author.iconURL || data.author.icon_url, + proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url, + } + : null; + + /** + * Represents the provider of a MessageEmbed + * @typedef {Object} MessageEmbedProvider + * @property {string} name The name of this provider + * @property {string} url URL of this provider + */ + + /** + * The provider of this embed (if there is one) + * @type {?MessageEmbedProvider} + */ + this.provider = data.provider + ? { + name: data.provider.name, + url: data.provider.name, + } + : null; + + /** + * Represents the footer field of a MessageEmbed + * @typedef {Object} MessageEmbedFooter + * @property {string} text The text of this footer + * @property {string} iconURL URL of the icon for this footer + * @property {string} proxyIconURL Proxied URL of the icon for this footer + */ + + /** + * The footer of this embed + * @type {?MessageEmbedFooter} + */ + this.footer = data.footer + ? { + text: data.footer.text, + iconURL: data.footer.iconURL || data.footer.icon_url, + proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url, + } + : null; + + /** + * The files of this embed + * @type {Array} + */ + this.files = data.files || []; + } + + /** + * The date displayed on this embed + * @type {?Date} + * @readonly + */ + get createdAt() { + return this.timestamp ? new Date(this.timestamp) : null; + } + + /** + * The hexadecimal version of the embed color, with a leading hash + * @type {?string} + * @readonly + */ + get hexColor() { + return this.color ? `#${this.color.toString(16).padStart(6, '0')}` : null; + } + + /** + * The accumulated length for the embed title, description, fields 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) + ); + } + + /** + * Adds a field to the embed (max 25). + * @param {StringResolvable} name The name of this field + * @param {StringResolvable} value The value of this field + * @param {boolean} [inline=false] If this field will be displayed inline + * @returns {MessageEmbed} + */ + addField(name, value, inline) { + return this.addFields({ name, value, inline }); + } + + /** + * Adds fields to the embed (max 25). + * @param {...EmbedFieldData|EmbedFieldData[]} fields The fields to add + * @returns {MessageEmbed} + */ + addFields(...fields) { + this.fields.push(...this.constructor.normalizeFields(fields)); + return this; + } + + /** + * 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|EmbedFieldData[]} [fields] The replacing field objects + * @returns {MessageEmbed} + */ + spliceFields(index, deleteCount, ...fields) { + this.fields.splice(index, deleteCount, ...this.constructor.normalizeFields(...fields)); + 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. Multiple files can be attached. + * @param {Array} files Files to attach + * @returns {MessageEmbed} + */ + attachFiles(files) { + this.files = this.files.concat(files); + return this; + } + + /** + * Sets the author of this embed. + * @param {StringResolvable} name The name of the author + * @param {string} [iconURL] The icon URL of the author + * @param {string} [url] The URL of the author + * @returns {MessageEmbed} + */ + setAuthor(name, iconURL, url) { + this.author = { name: Util.resolveString(name), iconURL, url }; + return this; + } + + /** + * Sets the color of this embed. + * @param {ColorResolvable} color The color of the embed + * @returns {MessageEmbed} + */ + setColor(color) { + this.color = Util.resolveColor(color); + return this; + } + + /** + * Sets the description of this embed. + * @param {StringResolvable} description The description + * @returns {MessageEmbed} + */ + setDescription(description) { + description = Util.resolveString(description); + this.description = description; + return this; + } + + /** + * Sets the footer of this embed. + * @param {StringResolvable} text The text of the footer + * @param {string} [iconURL] The icon URL of the footer + * @returns {MessageEmbed} + */ + setFooter(text, iconURL) { + text = Util.resolveString(text); + this.footer = { text, iconURL }; + return this; + } + + /** + * Sets the image of this embed. + * @param {string} url The URL of the image + * @returns {MessageEmbed} + */ + setImage(url) { + this.image = { url }; + return this; + } + + /** + * Sets the thumbnail of this embed. + * @param {string} url The URL of the thumbnail + * @returns {MessageEmbed} + */ + setThumbnail(url) { + this.thumbnail = { url }; + return this; + } + + /** + * Sets the timestamp of this embed. + * @param {Date|number} [timestamp=Date.now()] The timestamp or date + * @returns {MessageEmbed} + */ + setTimestamp(timestamp = Date.now()) { + if (timestamp instanceof Date) timestamp = timestamp.getTime(); + this.timestamp = timestamp; + return this; + } + + /** + * Sets the title of this embed. + * @param {StringResolvable} title The title + * @returns {MessageEmbed} + */ + setTitle(title) { + title = Util.resolveString(title); + this.title = title; + return this; + } + + /** + * Sets the URL of this embed. + * @param {string} url The URL + * @returns {MessageEmbed} + */ + setURL(url) { + this.url = url; + return this; + } + + /** + * 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, + thumbnail: this.thumbnail, + image: this.image, + author: this.author + ? { + name: this.author.name, + url: this.author.url, + icon_url: this.author.iconURL, + } + : null, + footer: this.footer + ? { + text: this.footer.text, + icon_url: this.footer.iconURL, + } + : 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) throw new RangeError('EMBED_FIELD_NAME'); + value = Util.resolveString(value); + if (!value) throw new RangeError('EMBED_FIELD_VALUE'); + return { name, value, inline }; + } + + /** + * @typedef {Object} EmbedFieldData + * @property {StringResolvable} name The name of this field + * @property {StringResolvable} value The value of this field + * @property {boolean} [inline] If this field will be displayed inline + */ + + /** + * Normalizes field input and resolves strings. + * @param {...EmbedFieldData|EmbedFieldData[]} fields Fields to normalize + * @returns {EmbedField[]} + */ + static normalizeFields(...fields) { + return fields + .flat(2) + .map(field => + this.normalizeField( + field && field.name, + field && field.value, + field && typeof field.inline === 'boolean' ? field.inline : false, + ), + ); + } +} + +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..2ed24c0 --- /dev/null +++ b/node_modules/discord.js/src/structures/MessageMentions.js @@ -0,0 +1,225 @@ +'use strict'; + +const Collection = require('../util/Collection'); +const { ChannelTypes } = require('../util/Constants'); +const Util = require('../util/Util'); + +/** + * Keeps track of mentions in a {@link Message}. + */ +class MessageMentions { + constructor(message, users, roles, everyone, crosspostedChannels) { + /** + * The client the message is from + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: message.client }); + + /** + * The guild the message is in + * @type {?Guild} + * @readonly + */ + Object.defineProperty(this, 'guild', { value: message.guild }); + + /** + * The initial message content + * @type {string} + * @readonly + * @private + */ + Object.defineProperty(this, '_content', { value: message.content }); + + /** + * Whether `@everyone` or `@here` were mentioned + * @type {boolean} + */ + this.everyone = Boolean(everyone); + + if (users) { + if (users instanceof Collection) { + /** + * Any users that were mentioned + * Order as received from the API, not as they appear in the message content + * @type {Collection} + */ + this.users = new Collection(users); + } else { + this.users = new Collection(); + for (const mention of users) { + if (mention.member && message.guild) { + message.guild.members.add(Object.assign(mention.member, { user: mention })); + } + const user = message.client.users.add(mention); + this.users.set(user.id, user); + } + } + } else { + this.users = new Collection(); + } + + if (roles) { + if (roles instanceof Collection) { + /** + * Any roles that were mentioned + * Order as received from the API, not as they appear in the message content + * @type {Collection} + */ + this.roles = new Collection(roles); + } else { + this.roles = new Collection(); + for (const mention of roles) { + const role = message.channel.guild.roles.cache.get(mention); + if (role) this.roles.set(role.id, role); + } + } + } else { + this.roles = new Collection(); + } + + /** + * Cached members for {@link MessageMentions#members} + * @type {?Collection} + * @private + */ + this._members = null; + + /** + * Cached channels for {@link MessageMentions#channels} + * @type {?Collection} + * @private + */ + this._channels = null; + + /** + * Crossposted channel data. + * @typedef {Object} CrosspostedChannel + * @property {string} channelID ID of the mentioned channel + * @property {string} guildID ID of the guild that has the channel + * @property {string} type Type of the channel + * @property {string} name The name of the channel + */ + + if (crosspostedChannels) { + if (crosspostedChannels instanceof Collection) { + /** + * A collection of crossposted channels + * Order as received from the API, not as they appear in the message content + * @type {Collection} + */ + 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) + * Order as received from the API, not as they appear in the message content + * @type {?Collection} + * @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 + * Order as they appear first in the message content + * @type {Collection} + * @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.cache.get(matches[1]); + if (chan) this._channels.set(chan.id, chan); + } + return this._channels; + } + + /** + * Checks if a user, guild member, role, or channel is mentioned. + * Takes into account user mentions, role mentions, and @everyone/@here mentions. + * @param {UserResolvable|RoleResolvable|GuildChannelResolvable} data User/Role/Channel to check + * @param {Object} [options] Options + * @param {boolean} [options.ignoreDirect=false] - Whether to ignore direct mentions to the item + * @param {boolean} [options.ignoreRoles=false] - Whether to ignore role mentions to a guild member + * @param {boolean} [options.ignoreEveryone=false] - Whether to ignore everyone/here mentions + * @returns {boolean} + */ + has(data, { ignoreDirect = false, ignoreRoles = false, ignoreEveryone = false } = {}) { + if (!ignoreEveryone && this.everyone) return true; + const GuildMember = require('./GuildMember'); + if (!ignoreRoles && data instanceof GuildMember) { + for (const role of this.roles.values()) if (data.roles.cache.has(role.id)) return true; + } + + if (!ignoreDirect) { + const id = + this.client.users.resolveID(data) || + (this.guild && this.guild.roles.resolveID(data)) || + this.client.channels.resolveID(data); + + return this.users.has(id) || this.channels.has(id) || this.roles.has(id); + } + + return false; + } + + toJSON() { + return Util.flatten(this, { + members: true, + channels: true, + }); + } +} + +/** + * 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 = /<@!?(\d{17,19})>/g; + +/** + * Regular expression that globally matches role mentions like `<@&297577916114403338>` + * @type {RegExp} + */ +MessageMentions.ROLES_PATTERN = /<@&(\d{17,19})>/g; + +/** + * Regular expression that globally matches channel mentions like `<#222079895583457280>` + * @type {RegExp} + */ +MessageMentions.CHANNELS_PATTERN = /<#(\d{17,19})>/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..ac0e04d --- /dev/null +++ b/node_modules/discord.js/src/structures/MessageReaction.js @@ -0,0 +1,136 @@ +'use strict'; + +const GuildEmoji = require('./GuildEmoji'); +const ReactionEmoji = require('./ReactionEmoji'); +const ReactionUserManager = require('../managers/ReactionUserManager'); +const Util = require('../util/Util'); + +/** + * Represents a reaction to a message. + */ +class MessageReaction { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the message reaction + * @param {Message} message The message the reaction refers to + */ + constructor(client, data, message) { + /** + * The client that instantiated this message reaction + * @name MessageReaction#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + /** + * The message that this reaction refers to + * @type {Message} + */ + this.message = message; + + /** + * Whether the client has given this reaction + * @type {boolean} + */ + this.me = data.me; + + /** + * A manager of the users that have given this reaction + * @type {ReactionUserManager} + */ + this.users = new ReactionUserManager(client, undefined, this); + + this._emoji = new ReactionEmoji(this, data.emoji); + + this._patch(data); + } + + _patch(data) { + // eslint-disable-next-line eqeqeq + if (this.count == undefined) { + /** + * The number of people that have given the same reaction + * @type {?number} + */ + this.count = data.count; + } + } + + /** + * Removes all users from this reaction. + * @returns {Promise} + */ + async remove() { + await this.client.api + .channels(this.message.channel.id) + .messages(this.message.id) + .reactions(this._emoji.identifier) + .delete(); + return this; + } + + /** + * The emoji of this reaction, either an GuildEmoji 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 {GuildEmoji|ReactionEmoji} + * @readonly + */ + get emoji() { + if (this._emoji instanceof GuildEmoji) 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.cache; + if (emojis.has(this._emoji.id)) { + const emoji = emojis.get(this._emoji.id); + this._emoji = emoji; + return emoji; + } + } + return this._emoji; + } + + /** + * Whether or not this reaction is a partial + * @type {boolean} + * @readonly + */ + get partial() { + return this.count === null; + } + + /** + * Fetch this reaction. + * @returns {Promise} + */ + async fetch() { + const message = await this.message.fetch(); + const existing = message.reactions.cache.get(this.emoji.id || this.emoji.name); + // The reaction won't get set when it has been completely removed + this._patch(existing || { count: 0 }); + return this; + } + + toJSON() { + return Util.flatten(this, { emoji: 'emojiID', message: 'messageID' }); + } + + _add(user) { + if (this.partial) return; + this.users.cache.set(user.id, user); + if (!this.me || user.id !== this.message.client.user.id || this.count === 0) this.count++; + if (!this.me) this.me = user.id === this.message.client.user.id; + } + + _remove(user) { + if (this.partial) return; + this.users.cache.delete(user.id); + if (!this.me || user.id !== this.message.client.user.id) this.count--; + if (user.id === this.message.client.user.id) this.me = false; + if (this.count <= 0 && this.users.cache.size === 0) { + this.message.reactions.cache.delete(this.emoji.id || this.emoji.name); + } + } +} + +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..3319719 --- /dev/null +++ b/node_modules/discord.js/src/structures/NewsChannel.js @@ -0,0 +1,38 @@ +'use strict'; + +const TextChannel = require('./TextChannel'); +const { Error } = require('../errors'); + +/** + * Represents a guild news channel on Discord. + * @extends {TextChannel} + */ +class NewsChannel extends TextChannel { + _patch(data) { + super._patch(data); + + // News channels don't have a rate limit per user, remove it + this.rateLimitPerUser = undefined; + } + + /** + * Adds the target to this channel's followers. + * @param {GuildChannelResolvable} channel The channel where the webhook should be created + * @param {string} [reason] Reason for creating the webhook + * @returns {Promise} + * @example + * if (channel.type === 'news') { + * channel.addFollower('222197033908436994', 'Important announcements') + * .then(() => console.log('Added follower')) + * .catch(console.error); + * } + */ + async addFollower(channel, reason) { + const channelID = this.guild.channels.resolveID(channel); + if (!channelID) throw new Error('GUILD_CHANNEL_RESOLVE'); + await this.client.api.channels(this.id).followers.post({ data: { webhook_channel_id: channelID }, reason }); + return this; + } +} + +module.exports = NewsChannel; diff --git a/node_modules/discord.js/src/structures/PartialGroupDMChannel.js b/node_modules/discord.js/src/structures/PartialGroupDMChannel.js new file mode 100644 index 0000000..e398f23 --- /dev/null +++ b/node_modules/discord.js/src/structures/PartialGroupDMChannel.js @@ -0,0 +1,46 @@ +'use strict'; + +const Channel = require('./Channel'); +const { Error } = require('../errors'); + +/** + * Represents a Partial Group DM Channel on Discord. + * @extends {Channel} + */ +class PartialGroupDMChannel extends Channel { + constructor(client, data) { + super(client, data); + + /** + * The name of this Group DM Channel + * @type {string} + */ + this.name = data.name; + + /** + * The hash of the channel icon + * @type {?string} + */ + this.icon = data.icon; + } + + /** + * The URL to this channel's icon. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + iconURL({ format, size } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size); + } + + delete() { + return Promise.reject(new Error('DELETE_GROUP_DM_CHANNEL')); + } + + fetch() { + return Promise.reject(new Error('FETCH_GROUP_DM_CHANNEL')); + } +} + +module.exports = PartialGroupDMChannel; 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..bbc7cd1 --- /dev/null +++ b/node_modules/discord.js/src/structures/PermissionOverwrites.js @@ -0,0 +1,189 @@ +'use strict'; + +const Role = require('./Role'); +const { TypeError } = require('../errors'); +const Permissions = require('../util/Permissions'); +const Util = require('../util/Util'); + +/** + * 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._patch(data); + } + + _patch(data) { + /** + * The ID of this overwrite, either a user ID or a role ID + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The type of a permission overwrite. It can be one of: + * * member + * * role + * @typedef {string} OverwriteType + */ + + /** + * The type of this overwrite + * @type {OverwriteType} + */ + this.type = data.type; + + /** + * The permissions that are denied for the user or role. + * @type {Readonly} + */ + this.deny = new Permissions(data.deny).freeze(); + + /** + * The permissions that are allowed for the user or role. + * @type {Readonly} + */ + this.allow = new Permissions(data.allow).freeze(); + } + + /** + * Updates this permissionOverwrites. + * @param {PermissionOverwriteOptions} options The options for the update + * @param {string} [reason] Reason for creating/editing this overwrite + * @returns {Promise} + * @example + * // Update permission overwrites + * permissionOverwrites.update({ + * SEND_MESSAGES: false + * }) + * .then(channel => console.log(channel.permissionOverwrites.get(message.author.id))) + * .catch(console.error); + */ + update(options, reason) { + const { allow, deny } = this.constructor.resolveOverwriteOptions(options, this); + + return this.channel.client.api + .channels(this.channel.id) + .permissions[this.id].put({ + data: { id: this.id, type: this.type, allow: allow.bitfield, deny: deny.bitfield }, + reason, + }) + .then(() => this); + } + + /** + * Deletes this Permission Overwrite. + * @param {string} [reason] Reason for deleting this overwrite + * @returns {Promise} + */ + delete(reason) { + return this.channel.client.api.channels[this.channel.id].permissions[this.id].delete({ reason }).then(() => this); + } + + toJSON() { + return Util.flatten(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 + */ + + /** + * @typedef {object} ResolvedOverwriteOptions + * @property {Permissions} allow The allowed permissions + * @property {Permissions} deny The denied permissions + */ + + /** + * Resolves bitfield permissions overwrites from an object. + * @param {PermissionOverwriteOptions} options The options for the update + * @param {Object} initialPermissions The initial permissions + * @param {PermissionResolvable} initialPermissions.allow Initial allowed permissions + * @param {PermissionResolvable} initialPermissions.deny Initial denied permissions + * @returns {ResolvedOverwriteOptions} + */ + static resolveOverwriteOptions(options, { allow, deny } = {}) { + allow = new Permissions(allow); + deny = new Permissions(deny); + + for (const [perm, value] of Object.entries(options)) { + if (value === true) { + allow.add(Permissions.FLAGS[perm]); + deny.remove(Permissions.FLAGS[perm]); + } else if (value === false) { + allow.remove(Permissions.FLAGS[perm]); + deny.add(Permissions.FLAGS[perm]); + } else if (value === null) { + allow.remove(Permissions.FLAGS[perm]); + deny.remove(Permissions.FLAGS[perm]); + } + } + + return { allow, deny }; + } + + /** + * The raw data for a permission overwrite + * @typedef {Object} RawOverwriteData + * @property {Snowflake} id The id of the overwrite + * @property {number} allow The permissions to allow + * @property {number} deny The permissions to deny + * @property {OverwriteType} type The type of this OverwriteData + */ + + /** + * Data that can be resolved into {@link RawOverwriteData} + * @typedef {PermissionOverwrites|OverwriteData} OverwriteResolvable + */ + + /** + * Data that can be used for a permission overwrite + * @typedef {Object} OverwriteData + * @property {GuildMemberResolvable|RoleResolvable} id Member or role this overwrite is for + * @property {PermissionResolvable} [allow] The permissions to allow + * @property {PermissionResolvable} [deny] The permissions to deny + * @property {OverwriteType} [type] The type of this OverwriteData + */ + + /** + * Resolves an overwrite into {@link RawOverwriteData}. + * @param {OverwriteResolvable} overwrite The overwrite-like data to resolve + * @param {Guild} guild The guild to resolve from + * @returns {RawOverwriteData} + */ + static resolve(overwrite, guild) { + if (overwrite instanceof this) return overwrite.toJSON(); + if (typeof overwrite.id === 'string' && ['role', 'member'].includes(overwrite.type)) { + return { ...overwrite, allow: Permissions.resolve(overwrite.allow), deny: Permissions.resolve(overwrite.deny) }; + } + + const userOrRole = guild.roles.resolve(overwrite.id) || guild.client.users.resolve(overwrite.id); + if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); + const type = userOrRole instanceof Role ? 'role' : 'member'; + + return { + id: userOrRole.id, + type, + allow: Permissions.resolve(overwrite.allow), + deny: Permissions.resolve(overwrite.deny), + }; + } +} + +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..2ae6b8c --- /dev/null +++ b/node_modules/discord.js/src/structures/Presence.js @@ -0,0 +1,336 @@ +'use strict'; + +const Emoji = require('./Emoji'); +const ActivityFlags = require('../util/ActivityFlags'); +const { ActivityTypes } = require('../util/Constants'); +const Util = require('../util/Util'); + +/** + * Activity sent in a message. + * @typedef {Object} MessageActivity + * @property {string} [partyID] Id of the party represented in activity + * @property {number} [type] Type of activity sent + */ + +/** + * 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 { + /** + * @param {Client} client The instantiating client + * @param {Object} [data={}] The data for the presence + */ + constructor(client, data = {}) { + /** + * The client that instantiated this + * @name Presence#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + /** + * The user ID of this presence + * @type {Snowflake} + */ + this.userID = data.user.id; + + /** + * The guild of this presence + * @type {?Guild} + */ + this.guild = data.guild || null; + + this.patch(data); + } + + /** + * The user of this presence + * @type {?User} + * @readonly + */ + get user() { + return this.client.users.cache.get(this.userID) || null; + } + + /** + * The member of this presence + * @type {?GuildMember} + * @readonly + */ + get member() { + return this.guild.members.cache.get(this.userID) || null; + } + + patch(data) { + /** + * The status of this presence + * @type {PresenceStatus} + */ + this.status = data.status || this.status || 'offline'; + + if (data.activities) { + /** + * The activities of this presence + * @type {Activity[]} + */ + this.activities = data.activities.map(activity => new Activity(this, activity)); + } else if (data.activity || data.game) { + this.activities = [new Activity(this, data.game || data.activity)]; + } 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; + + return this; + } + + _clone() { + const clone = Object.assign(Object.create(this), this); + if (this.activities) clone.activities = this.activities.map(activity => activity._clone()); + return clone; + } + + /** + * 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) + ); + } + + toJSON() { + return Util.flatten(this); + } +} + +/** + * Represents an activity that is part of a user's presence. + */ +class Activity { + constructor(presence, data) { + Object.defineProperty(this, 'presence', { value: presence }); + + /** + * The name of the activity being played + * @type {string} + */ + this.name = data.name; + + /** + * The type of the activity status + * @type {ActivityType} + */ + this.type = ActivityTypes[data.type]; + + /** + * If the activity 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} + * @property {?Date} start When the activity started + * @property {?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} + * @property {?string} id ID of the party + * @property {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; + + this.syncID = data.sync_id; + + /** + * Flags that describe the activity + * @type {Readonly} + */ + this.flags = new ActivityFlags(data.flags).freeze(); + + /** + * Emoji for a custom activity + * @type {?Emoji} + */ + this.emoji = data.emoji ? new Emoji(presence.client, data.emoji) : null; + + /** + * Creation date of the activity + * @type {number} + */ + this.createdTimestamp = new Date(data.created_at).getTime(); + } + + /** + * Whether this activity is equal to another activity. + * @param {Activity} activity The activity to compare with + * @returns {boolean} + */ + equals(activity) { + return ( + this === activity || + (activity && this.name === activity.name && this.type === activity.type && this.url === activity.url) + ); + } + + /** + * The time the activity was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * When concatenated with a string, this automatically returns the activities' name instead of the Activity object. + * @returns {string} + */ + toString() { + return this.name; + } + + _clone() { + return Object.assign(Object.create(this), this); + } +} + +/** + * Assets for a rich presence + */ +class RichPresenceAssets { + constructor(activity, assets) { + Object.defineProperty(this, 'activity', { value: activity }); + + /** + * 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; + } + + /** + * Gets the URL of the small image asset + * @param {Object} [options] Options for the image url + * @param {string} [options.format] Format of the image + * @param {number} [options.size] Size of the image + * @returns {?string} The small image URL + */ + smallImageURL({ format, size } = {}) { + if (!this.smallImage) return null; + return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.smallImage, { + format, + size, + }); + } + + /** + * Gets the URL of the large image asset + * @param {Object} [options] Options for the image url + * @param {string} [options.format] Format of the image + * @param {number} [options.size] Size of the image + * @returns {?string} The large image URL + */ + largeImageURL({ format, size } = {}) { + 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 this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.largeImage, { + format, + size, + }); + } +} + +exports.Presence = Presence; +exports.Activity = Activity; +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..e0fa316 --- /dev/null +++ b/node_modules/discord.js/src/structures/ReactionCollector.js @@ -0,0 +1,188 @@ +'use strict'; + +const Collector = require('./interfaces/Collector'); +const Collection = require('../util/Collection'); +const { Events } = require('../util/Constants'); + +/** + * @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. + * Will automatically stop if the message (`'messageDelete'`), + * channel (`'channelDelete'`), or guild (`'guildDelete'`) are deleted. + * @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 upon which to collect reactions + * @type {Message} + */ + this.message = message; + + /** + * The users which have reacted to this message + * @type {Collection} + */ + this.users = new Collection(); + + /** + * The total number of reactions collected + * @type {number} + */ + this.total = 0; + + this.empty = this.empty.bind(this); + this._handleChannelDeletion = this._handleChannelDeletion.bind(this); + this._handleGuildDeletion = this._handleGuildDeletion.bind(this); + this._handleMessageDeletion = this._handleMessageDeletion.bind(this); + + this.client.incrementMaxListeners(); + this.client.on(Events.MESSAGE_REACTION_ADD, this.handleCollect); + this.client.on(Events.MESSAGE_REACTION_REMOVE, this.handleDispose); + this.client.on(Events.MESSAGE_REACTION_REMOVE_ALL, this.empty); + this.client.on(Events.MESSAGE_DELETE, this._handleMessageDeletion); + this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion); + + this.once('end', () => { + this.client.removeListener(Events.MESSAGE_REACTION_ADD, this.handleCollect); + this.client.removeListener(Events.MESSAGE_REACTION_REMOVE, this.handleDispose); + this.client.removeListener(Events.MESSAGE_REACTION_REMOVE_ALL, this.empty); + this.client.removeListener(Events.MESSAGE_DELETE, this._handleMessageDeletion); + this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion); + this.client.decrementMaxListeners(); + }); + + this.on('collect', (reaction, user) => { + this.total++; + this.users.set(user.id, user); + }); + + this.on('remove', (reaction, user) => { + this.total--; + if (!this.collected.some(r => r.users.cache.has(user.id))) this.users.delete(user.id); + }); + } + + /** + * Handles an incoming reaction for possible collection. + * @param {MessageReaction} reaction The reaction to possibly collect + * @returns {?Snowflake|string} + * @private + */ + collect(reaction) { + /** + * Emitted whenever a reaction is collected. + * @event ReactionCollector#collect + * @param {MessageReaction} reaction The reaction that was collected + * @param {User} user The user that added the reaction + */ + if (reaction.message.id !== this.message.id) return null; + return ReactionCollector.key(reaction); + } + + /** + * Handles a reaction deletion for possible disposal. + * @param {MessageReaction} reaction The reaction to possibly dispose of + * @param {User} user The user that removed the reaction + * @returns {?Snowflake|string} + */ + dispose(reaction, user) { + /** + * Emitted when the reaction had all the users removed and the `dispose` option is set to true. + * @event ReactionCollector#dispose + * @param {MessageReaction} reaction The reaction that was disposed of + * @param {User} user The user that removed the reaction + */ + if (reaction.message.id !== this.message.id) return null; + + /** + * Emitted when the reaction had one user removed and the `dispose` option is set to true. + * @event ReactionCollector#remove + * @param {MessageReaction} reaction The reaction that was removed + * @param {User} user The user that removed the reaction + */ + if (this.collected.has(ReactionCollector.key(reaction)) && this.users.has(user.id)) { + this.emit('remove', reaction, user); + } + return reaction.count ? null : ReactionCollector.key(reaction); + } + + /** + * Empties this reaction collector. + */ + empty() { + this.total = 0; + this.collected.clear(); + this.users.clear(); + this.checkEnd(); + } + + endReason() { + 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; + } + + /** + * Handles checking if the message has been deleted, and if so, stops the collector with the reason 'messageDelete'. + * @private + * @param {Message} message The message that was deleted + * @returns {void} + */ + _handleMessageDeletion(message) { + if (message.id === this.message.id) { + this.stop('messageDelete'); + } + } + + /** + * Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'. + * @private + * @param {GuildChannel} channel The channel that was deleted + * @returns {void} + */ + _handleChannelDeletion(channel) { + if (channel.id === this.message.channel.id) { + this.stop('channelDelete'); + } + } + + /** + * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'. + * @private + * @param {Guild} guild The guild that was deleted + * @returns {void} + */ + _handleGuildDeletion(guild) { + if (this.message.guild && guild.id === this.message.guild.id) { + this.stop('guildDelete'); + } + } + + /** + * Gets the collector key for a reaction. + * @param {MessageReaction} reaction The message reaction to get the key for + * @returns {Snowflake|string} + */ + static key(reaction) { + return reaction.emoji.id || reaction.emoji.name; + } +} + +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..5c4bc13 --- /dev/null +++ b/node_modules/discord.js/src/structures/ReactionEmoji.js @@ -0,0 +1,31 @@ +'use strict'; + +const Emoji = require('./Emoji'); +const Util = require('../util/Util'); + +/** + * 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. + * @extends {Emoji} + */ +class ReactionEmoji extends Emoji { + constructor(reaction, emoji) { + super(reaction.message.client, emoji); + /** + * The message reaction this emoji refers to + * @type {MessageReaction} + */ + this.reaction = reaction; + } + + toJSON() { + return Util.flatten(this, { identifier: true }); + } + + valueOf() { + return this.id; + } +} + +module.exports = ReactionEmoji; 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..94ee2f0 --- /dev/null +++ b/node_modules/discord.js/src/structures/Role.js @@ -0,0 +1,403 @@ +'use strict'; + +const Base = require('./Base'); +const { Error, TypeError } = require('../errors'); +const Permissions = require('../util/Permissions'); +const Snowflake = require('../util/Snowflake'); +const Util = require('../util/Util'); + +/** + * Represents a role on Discord. + * @extends {Base} + */ +class Role extends Base { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the role + * @param {Guild} guild The guild the role is part of + */ + constructor(client, data, guild) { + super(client); + + /** + * The guild that the role belongs to + * @type {Guild} + */ + this.guild = guild; + + if (data) this._patch(data); + } + + _patch(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 raw position of the role from the API + * @type {number} + */ + this.rawPosition = data.position; + + /** + * The permissions of the role + * @type {Readonly} + */ + this.permissions = new Permissions(data.permissions).freeze(); + + /** + * 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; + + /** + * Whether the role has been deleted + * @type {boolean} + */ + this.deleted = false; + } + + /** + * The timestamp the role was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the role was created at + * @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() { + return `#${this.color.toString(16).padStart(6, '0')}`; + } + + /** + * The cached guild members that have this role + * @type {Collection} + * @readonly + */ + get members() { + return this.guild.members.cache.filter(m => m.roles.cache.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)) return false; + return clientMember.roles.highest.comparePositionTo(this) > 0; + } + + /** + * The position of the role in the role manager + * @type {number} + * @readonly + */ + get position() { + const sorted = this.guild._sortedRoles(); + return sorted.array().indexOf(sorted.get(this.id)); + } + + /** + * Compares this role's position to another role's. + * @param {RoleResolvable} 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) { + role = this.guild.roles.resolve(role); + if (!role) throw new TypeError('INVALID_TYPE', 'role', 'Role nor a Snowflake'); + 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} [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] Reason for editing this role + * @returns {Promise} + * @example + * // Edit a role + * role.edit({ name: 'new role' }) + * .then(updated => console.log(`Edited role name to ${updated.name}`)) + * .catch(console.error); + */ + async edit(data, reason) { + if (typeof data.permissions !== 'undefined') data.permissions = Permissions.resolve(data.permissions); + else data.permissions = this.permissions.bitfield; + if (typeof data.position !== 'undefined') { + await Util.setPosition( + this, + data.position, + false, + this.guild._sortedRoles(), + this.client.api.guilds(this.guild.id).roles, + reason, + ).then(updatedRoles => { + this.client.actions.GuildRolesPositionUpdate.handle({ + guild_id: this.guild.id, + roles: updatedRoles, + }); + }); + } + return this.client.api.guilds[this.guild.id].roles[this.id] + .patch({ + data: { + name: data.name || this.name, + color: data.color !== null ? Util.resolveColor(data.color || this.color) : null, + hoist: typeof data.hoist !== 'undefined' ? data.hoist : this.hoist, + permissions: data.permissions, + mentionable: typeof data.mentionable !== 'undefined' ? data.mentionable : this.mentionable, + }, + reason, + }) + .then(role => { + const clone = this._clone(); + clone._patch(role); + return clone; + }); + } + + /** + * Returns `channel.permissionsFor(role)`. Returns permissions for a role in a guild channel, + * taking into account permission overwrites. + * @param {ChannelResolvable} channel The guild channel to use as context + * @returns {Readonly} + */ + permissionsIn(channel) { + channel = this.guild.channels.resolve(channel); + if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE'); + return channel.rolePermissions(this); + } + + /** + * Sets 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} + * @example + * // Set the name of the role + * role.setName('new role') + * .then(updated => console.log(`Updated role name to ${updated.name}`)) + * .catch(console.error); + */ + setName(name, reason) { + return this.edit({ name }, reason); + } + + /** + * Sets 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} + * @example + * // Set the color of a role + * role.setColor('#FF0000') + * .then(updated => console.log(`Set color of role to ${updated.color}`)) + * .catch(console.error); + */ + setColor(color, reason) { + return this.edit({ color }, reason); + } + + /** + * Sets 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} + * @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); + } + + /** + * Sets 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} + * @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); + } + + /** + * Sets 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} + * @example + * // Make the role mentionable + * role.setMentionable(true) + * .then(updated => console.log(`Role updated ${updated.name}`)) + * .catch(console.error); + */ + setMentionable(mentionable, reason) { + return this.edit({ mentionable }, reason); + } + + /** + * Sets the position of the role. + * @param {number} position The position of the role + * @param {Object} [options] Options for setting position + * @param {boolean} [options.relative=false] Change the position relative to its current value + * @param {string} [options.reason] Reason for changing the position + * @returns {Promise} + * @example + * // Set the position of the role + * role.setPosition(1) + * .then(updated => console.log(`Role position: ${updated.position}`)) + * .catch(console.error); + */ + setPosition(position, { relative, reason } = {}) { + return Util.setPosition( + this, + position, + relative, + this.guild._sortedRoles(), + this.client.api.guilds(this.guild.id).roles, + reason, + ).then(updatedRoles => { + this.client.actions.GuildRolesPositionUpdate.handle({ + guild_id: this.guild.id, + roles: updatedRoles, + }); + return this; + }); + } + + /** + * Deletes the role. + * @param {string} [reason] Reason for deleting this role + * @returns {Promise} + * @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.api.guilds[this.guild.id].roles[this.id].delete({ reason }).then(() => { + this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id }); + return this; + }); + } + + /** + * 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.bitfield === role.permissions.bitfield && + this.managed === role.managed + ); + } + + /** + * When concatenated with a string, this automatically returns the role's mention instead of the Role object. + * @returns {string} + * @example + * // Logs: Role: <@&123456789012345678> + * console.log(`Role: ${role}`); + */ + toString() { + if (this.id === this.guild.id) return '@everyone'; + return `<@&${this.id}>`; + } + + toJSON() { + return super.toJSON({ createdTimestamp: true }); + } + + /** + * 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; + } +} + +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..1c518a7 --- /dev/null +++ b/node_modules/discord.js/src/structures/StoreChannel.js @@ -0,0 +1,32 @@ +'use strict'; + +const GuildChannel = require('./GuildChannel'); + +/** + * Represents a guild store channel on Discord. + * @extends {GuildChannel} + */ +class StoreChannel extends GuildChannel { + /** + * @param {*} guild The guild the store channel is part of + * @param {*} data The data for the store channel + */ + constructor(guild, data) { + super(guild, data); + + /** + * If the guild considers this channel NSFW + * @type {boolean} + * @readonly + */ + this.nsfw = Boolean(data.nsfw); + } + + _patch(data) { + super._patch(data); + + if (typeof data.nsfw !== 'undefined') this.nsfw = Boolean(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..a28c5a2 --- /dev/null +++ b/node_modules/discord.js/src/structures/Team.js @@ -0,0 +1,109 @@ +'use strict'; + +const Base = require('./Base'); +const TeamMember = require('./TeamMember'); +const Collection = require('../util/Collection'); +const Snowflake = require('../util/Snowflake'); + +/** + * Represents a Client OAuth2 Application Team. + * @extends {Base} + */ +class Team extends Base { + constructor(client, data) { + super(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} + */ + this.members = new Collection(); + + for (const memberData of data.members) { + const member = new TeamMember(this, memberData); + this.members.set(member.id, member); + } + } + + /** + * The owner of this 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. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} URL to the icon + */ + iconURL({ format, size } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.TeamIcon(this.id, this.icon, { format, size }); + } + + /** + * 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; + } + + toJSON() { + return super.toJSON({ createdTimestamp: true }); + } +} + +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..ba7ecd2 --- /dev/null +++ b/node_modules/discord.js/src/structures/TeamMember.js @@ -0,0 +1,65 @@ +'use strict'; + +const Base = require('./Base'); +const { MembershipStates } = require('../util/Constants'); + +/** + * Represents a Client OAuth2 Application Team Member. + * @extends {Base} + */ +class TeamMember extends Base { + constructor(team, data) { + super(team.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 permissions 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.users.add(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: <@123456789012345678> + * 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..b92e0c7 --- /dev/null +++ b/node_modules/discord.js/src/structures/TextChannel.js @@ -0,0 +1,153 @@ +'use strict'; + +const GuildChannel = require('./GuildChannel'); +const Webhook = require('./Webhook'); +const TextBasedChannel = require('./interfaces/TextBasedChannel'); +const MessageManager = require('../managers/MessageManager'); +const Collection = require('../util/Collection'); +const DataResolver = require('../util/DataResolver'); + +/** + * Represents a guild text channel on Discord. + * @extends {GuildChannel} + * @implements {TextBasedChannel} + */ +class TextChannel extends GuildChannel { + /** + * @param {Guild} guild The guild the text channel is part of + * @param {Object} data The data for the text channel + */ + constructor(guild, data) { + super(guild, data); + /** + * A manager of the messages sent to this channel + * @type {MessageManager} + */ + this.messages = new MessageManager(this); + + /** + * If the guild considers this channel NSFW + * @type {boolean} + * @readonly + */ + this.nsfw = Boolean(data.nsfw); + this._typing = new Map(); + } + + _patch(data) { + super._patch(data); + + /** + * The topic of the text channel + * @type {?string} + */ + this.topic = data.topic; + + if (typeof data.nsfw !== 'undefined') 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 ratelimit per user for this channel in seconds + * @type {number} + */ + this.rateLimitPerUser = data.rate_limit_per_user || 0; + + /** + * 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; + + if (data.messages) for (const message of data.messages) this.messages.add(message); + } + + /** + * 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} + */ + setRateLimitPerUser(rateLimitPerUser, reason) { + return this.edit({ rateLimitPerUser }, reason); + } + + /** + * 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} + */ + setNSFW(nsfw, reason) { + return this.edit({ nsfw }, reason); + } + + /** + * Fetches all webhooks for the channel. + * @returns {Promise>} + * @example + * // Fetch webhooks + * channel.fetchWebhooks() + * .then(hooks => console.log(`This channel has ${hooks.size} hooks`)) + * .catch(console.error); + */ + fetchWebhooks() { + return this.client.api.channels[this.id].webhooks.get().then(data => { + const hooks = new Collection(); + for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); + return hooks; + }); + } + + /** + * Creates a webhook for the channel. + * @param {string} name The name of the webhook + * @param {Object} [options] Options for creating the webhook + * @param {BufferResolvable|Base64Resolvable} [options.avatar] Avatar for the webhook + * @param {string} [options.reason] Reason for creating the webhook + * @returns {Promise} webhook The created webhook + * @example + * // Create a webhook for the current channel + * channel.createWebhook('Snek', { + * avatar: 'https://i.imgur.com/mI8XcpG.jpg', + * reason: 'Needed a cool new Webhook' + * }) + * .then(console.log) + * .catch(console.error) + */ + async createWebhook(name, { avatar, reason } = {}) { + if (typeof avatar === 'string' && !avatar.startsWith('data:')) { + avatar = await DataResolver.resolveImage(avatar); + } + return this.client.api.channels[this.id].webhooks + .post({ + data: { + name, + avatar, + }, + reason, + }) + .then(data => new Webhook(this.client, data)); + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + get lastMessage() {} + get lastPinAt() {} + send() {} + startTyping() {} + stopTyping() {} + get typing() {} + get typingCount() {} + createMessageCollector() {} + awaitMessages() {} + bulkDelete() {} +} + +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..f99bf6d --- /dev/null +++ b/node_modules/discord.js/src/structures/User.js @@ -0,0 +1,341 @@ +'use strict'; + +const Base = require('./Base'); +const TextBasedChannel = require('./interfaces/TextBasedChannel'); +const { Error } = require('../errors'); +const Snowflake = require('../util/Snowflake'); +const UserFlags = require('../util/UserFlags'); + +let Structures; + +/** + * Represents a user on Discord. + * @implements {TextBasedChannel} + * @extends {Base} + */ +class User extends Base { + /** + * @param {Client} client The instantiating client + * @param {Object} data The data for the user + */ + constructor(client, data) { + super(client); + + /** + * The ID of the user + * @type {Snowflake} + */ + this.id = data.id; + + this.system = null; + this.locale = null; + this.flags = null; + + this._patch(data); + } + + _patch(data) { + if ('username' in data) { + /** + * The username of the user + * @type {?string} + */ + this.username = data.username; + } else if (typeof this.username !== 'string') { + this.username = null; + } + + /** + * Whether or not the user is a bot + * @type {boolean} + */ + this.bot = Boolean(data.bot); + + if ('discriminator' in data) { + /** + * A discriminator based on username for the user + * @type {?string} + */ + this.discriminator = data.discriminator; + } else if (typeof this.discriminator !== 'string') { + this.discriminator = null; + } + + if ('avatar' in data) { + /** + * The ID of the user's avatar + * @type {?string} + */ + this.avatar = data.avatar; + } else if (typeof this.avatar !== 'string') { + this.avatar = null; + } + + if ('system' in data) { + /** + * Whether the user is an Official Discord System user (part of the urgent message system) + * @type {?boolean} + */ + this.system = Boolean(data.system); + } + + if ('locale' in data) { + /** + * The locale of the user's client (ISO 639-1) + * @type {?string} + */ + this.locale = data.locale; + } + + if ('public_flags' in data) { + /** + * The flags for this user + * @type {?UserFlags} + */ + this.flags = new UserFlags(data.public_flags); + } + + /** + * The ID of the last message sent by the user, if one was sent + * @type {?Snowflake} + */ + this.lastMessageID = null; + + /** + * The ID of the channel for the last message sent by the user, if one was sent + * @type {?Snowflake} + */ + this.lastMessageChannelID = null; + } + + /** + * Whether this User is a partial + * @type {boolean} + * @readonly + */ + get partial() { + return typeof this.username !== 'string'; + } + + /** + * The timestamp the user was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the user was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * The Message object of the last message sent by the user, if one was sent + * @type {?Message} + * @readonly + */ + get lastMessage() { + const channel = this.client.channels.cache.get(this.lastMessageChannelID); + return (channel && channel.messages.cache.get(this.lastMessageID)) || null; + } + + /** + * The presence of this user + * @type {Presence} + * @readonly + */ + get presence() { + for (const guild of this.client.guilds.cache.values()) { + if (guild.presences.cache.has(this.id)) return guild.presences.cache.get(this.id); + } + if (!Structures) Structures = require('../util/Structures'); + const Presence = Structures.get('Presence'); + return new Presence(this.client, { user: { id: this.id } }); + } + + /** + * A link to the user's avatar. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + avatarURL({ format, size, dynamic } = {}) { + if (!this.avatar) return null; + return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size, dynamic); + } + + /** + * A link to the user's default avatar + * @type {string} + * @readonly + */ + get defaultAvatarURL() { + return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5); + } + + /** + * A link to the user's avatar if they have one. + * Otherwise a link to their default avatar will be returned. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {string} + */ + displayAvatarURL(options) { + return this.avatarURL(options) || this.defaultAvatarURL; + } + + /** + * The Discord "tag" (e.g. `hydrabolt#0001`) for this user + * @type {?string} + * @readonly + */ + get tag() { + return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null; + } + + /** + * Checks whether the user is typing in a channel. + * @param {ChannelResolvable} channel The channel to check in + * @returns {boolean} + */ + typingIn(channel) { + channel = this.client.channels.resolve(channel); + return channel._typing.has(this.id); + } + + /** + * Gets the time that the user started typing. + * @param {ChannelResolvable} channel The channel to get the time in + * @returns {?Date} + */ + typingSinceIn(channel) { + channel = this.client.channels.resolve(channel); + return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null; + } + + /** + * Gets 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.channels.resolve(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.cache.find(c => c.type === 'dm' && c.recipient.id === this.id) || null; + } + + /** + * Creates a DM channel between the client and the user. + * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @returns {Promise} + */ + async createDM(force = false) { + if (!force) { + const { dmChannel } = this; + if (dmChannel && !dmChannel.partial) return dmChannel; + } + + const data = await this.client.api.users(this.client.user.id).channels.post({ + data: { + recipient_id: this.id, + }, + }); + return this.client.actions.ChannelCreate.handle(data).channel; + } + + /** + * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful. + * @returns {Promise} + */ + async deleteDM() { + const { dmChannel } = this; + if (!dmChannel) throw new Error('USER_NO_DMCHANNEL'); + const data = await this.client.api.channels(dmChannel.id).delete(); + return this.client.actions.ChannelDelete.handle(data).channel; + } + + /** + * 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; + + return equal; + } + + /** + * Fetches this user's flags. + * @param {boolean} [force=false] Whether to skip the cache check and request the AP + * @returns {Promise} + */ + async fetchFlags(force = false) { + if (this.flags && !force) return this.flags; + const data = await this.client.api.users(this.id).get(); + this._patch(data); + return this.flags; + } + + /** + * Fetches this user. + * @param {boolean} [force=false] Whether to skip the cache check and request the AP + * @returns {Promise} + */ + fetch(force = false) { + return this.client.users.fetch(this.id, true, force); + } + + /** + * When concatenated with a string, this automatically returns the user's mention instead of the User object. + * @returns {string} + * @example + * // Logs: Hello from <@123456789012345678>! + * console.log(`Hello from ${user}!`); + */ + toString() { + return `<@${this.id}>`; + } + + toJSON(...props) { + const json = super.toJSON( + { + createdTimestamp: true, + defaultAvatarURL: true, + tag: true, + lastMessage: false, + lastMessageID: false, + }, + ...props, + ); + json.avatarURL = this.avatarURL(); + json.displayAvatarURL = this.displayAvatarURL(); + return json; + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + send() {} +} + +TextBasedChannel.applyToClass(User); + +module.exports = User; 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..2b9eb9e --- /dev/null +++ b/node_modules/discord.js/src/structures/VoiceChannel.js @@ -0,0 +1,150 @@ +'use strict'; + +const GuildChannel = require('./GuildChannel'); +const { Error } = require('../errors'); +const Collection = require('../util/Collection'); +const { browser } = require('../util/Constants'); +const Permissions = require('../util/Permissions'); + +/** + * Represents a guild voice channel on Discord. + * @extends {GuildChannel} + */ +class VoiceChannel extends GuildChannel { + _patch(data) { + super._patch(data); + /** + * The bitrate of this voice channel + * @type {number} + */ + this.bitrate = data.bitrate; + + /** + * The maximum amount of users allowed in this channel - 0 means unlimited. + * @type {number} + */ + this.userLimit = data.user_limit; + } + + /** + * The members in this voice channel + * @type {Collection} + * @readonly + */ + get members() { + const coll = new Collection(); + for (const state of this.guild.voiceStates.cache.values()) { + if (state.channelID === this.id && state.member) { + coll.set(state.id, state.member); + } + } + return coll; + } + + /** + * 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, false); + } + + /** + * Whether the channel is editable by the client user + * @type {boolean} + * @readonly + */ + get editable() { + return this.manageable && this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false); + } + + /** + * Whether the channel is joinable by the client user + * @type {boolean} + * @readonly + */ + get joinable() { + if (browser) return false; + if (!this.viewable) return false; + if (!this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false)) return false; + if (this.full && !this.permissionsFor(this.client.user).has(Permissions.FLAGS.MOVE_MEMBERS, false)) 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(Permissions.FLAGS.SPEAK, false); + } + + /** + * Sets the bitrate of the channel. + * @param {number} bitrate The new bitrate + * @param {string} [reason] Reason for changing the channel's bitrate + * @returns {Promise} + * @example + * // Set the bitrate of a voice channel + * voiceChannel.setBitrate(48000) + * .then(vc => console.log(`Set bitrate to ${vc.bitrate}bps for ${vc.name}`)) + * .catch(console.error); + */ + setBitrate(bitrate, reason) { + 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} + * @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} + * @example + * // Join a voice channel + * voiceChannel.join() + * .then(connection => console.log('Connected!')) + * .catch(console.error); + */ + join() { + if (browser) return Promise.reject(new Error('VOICE_NO_BROWSER')); + return this.client.voice.joinChannel(this); + } + + /** + * Leaves this voice channel. + * @example + * // Leave a voice channel + * voiceChannel.leave(); + */ + leave() { + if (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..9626195 --- /dev/null +++ b/node_modules/discord.js/src/structures/VoiceRegion.js @@ -0,0 +1,52 @@ +'use strict'; + +const Util = require('../util/Util'); + +/** + * 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; + } + + toJSON() { + return Util.flatten(this); + } +} + +module.exports = VoiceRegion; diff --git a/node_modules/discord.js/src/structures/VoiceState.js b/node_modules/discord.js/src/structures/VoiceState.js new file mode 100644 index 0000000..731c7ef --- /dev/null +++ b/node_modules/discord.js/src/structures/VoiceState.js @@ -0,0 +1,213 @@ +'use strict'; + +const Base = require('./Base'); +const { Error, TypeError } = require('../errors'); +const { browser } = require('../util/Constants'); + +/** + * Represents the voice state for a Guild Member. + */ +class VoiceState extends Base { + /** + * @param {Guild} guild The guild the voice state is part of + * @param {Object} data The data for the voice state + */ + constructor(guild, data) { + super(guild.client); + /** + * The guild of this voice state + * @type {Guild} + */ + this.guild = guild; + /** + * The ID of the member of this voice state + * @type {Snowflake} + */ + this.id = data.user_id; + this._patch(data); + } + + _patch(data) { + /** + * Whether this member is deafened server-wide + * @type {?boolean} + */ + this.serverDeaf = 'deaf' in data ? data.deaf : null; + /** + * Whether this member is muted server-wide + * @type {?boolean} + */ + this.serverMute = 'mute' in data ? data.mute : null; + /** + * Whether this member is self-deafened + * @type {?boolean} + */ + this.selfDeaf = 'self_deaf' in data ? data.self_deaf : null; + /** + * Whether this member is self-muted + * @type {?boolean} + */ + this.selfMute = 'self_mute' in data ? data.self_mute : null; + /** + * Whether this member's camera is enabled + * @type {?boolean} + */ + this.selfVideo = 'self_video' in data ? data.self_video : null; + /** + * The session ID of this member's connection + * @type {?string} + */ + this.sessionID = 'session_id' in data ? data.session_id : null; + /** + * Whether this member is streaming using "Go Live" + * @type {boolean} + */ + this.streaming = data.self_stream || false; + /** + * The ID of the voice channel that this member is in + * @type {?Snowflake} + */ + this.channelID = data.channel_id || null; + return this; + } + + /** + * The member that this voice state belongs to + * @type {?GuildMember} + * @readonly + */ + get member() { + return this.guild.members.cache.get(this.id) || null; + } + + /** + * The channel that the member is connected to + * @type {?VoiceChannel} + * @readonly + */ + get channel() { + return this.guild.channels.cache.get(this.channelID) || null; + } + + /** + * If this is a voice state of the client user, then this will refer to the active VoiceConnection for this guild + * @type {?VoiceConnection} + * @readonly + */ + get connection() { + if (browser || this.id !== this.client.user.id) return null; + return this.client.voice.connections.get(this.guild.id) || null; + } + + /** + * Whether this member is either self-deafened or server-deafened + * @type {?boolean} + * @readonly + */ + get deaf() { + return this.serverDeaf || this.selfDeaf; + } + + /** + * Whether this member is either self-muted or server-muted + * @type {?boolean} + * @readonly + */ + get mute() { + return this.serverMute || this.selfMute; + } + + /** + * Whether this member is currently speaking. A boolean if the information is available (aka + * the bot is connected to any voice channel in the guild), otherwise this is null + * @type {?boolean} + * @readonly + */ + get speaking() { + return this.channel && this.channel.connection ? Boolean(this.channel.connection._speaking.get(this.id)) : null; + } + + /** + * Mutes/unmutes the member of this voice state. + * @param {boolean} mute Whether or not the member should be muted + * @param {string} [reason] Reason for muting or unmuting + * @returns {Promise} + */ + setMute(mute, reason) { + return this.member ? this.member.edit({ mute }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER')); + } + + /** + * Deafens/undeafens the member of this voice state. + * @param {boolean} deaf Whether or not the member should be deafened + * @param {string} [reason] Reason for deafening or undeafening + * @returns {Promise} + */ + setDeaf(deaf, reason) { + return this.member ? this.member.edit({ deaf }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER')); + } + + /** + * Kicks the member from the voice channel. + * @param {string} [reason] Reason for kicking member from the channel + * @returns {Promise} + */ + kick(reason) { + return this.setChannel(null, reason); + } + + /** + * Moves the member to a different channel, or disconnects them from the one they're in. + * @param {ChannelResolvable|null} [channel] Channel to move the member to, or `null` if you want to disconnect them + * from voice. + * @param {string} [reason] Reason for moving member to another channel or disconnecting + * @returns {Promise} + */ + setChannel(channel, reason) { + return this.member + ? this.member.edit({ channel }, reason) + : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER')); + } + + /** + * Self-mutes/unmutes the bot for this voice state. + * @param {boolean} mute Whether or not the bot should be self-muted + * @returns {Promise} true if the voice state was successfully updated, otherwise false + */ + async setSelfMute(mute) { + if (this.id !== this.client.user.id) throw new Error('VOICE_STATE_NOT_OWN'); + if (typeof mute !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'mute'); + if (!this.connection) return false; + this.selfMute = mute; + await this.connection.sendVoiceStateUpdate(); + return true; + } + + /** + * Self-deafens/undeafens the bot for this voice state. + * @param {boolean} deaf Whether or not the bot should be self-deafened + * @returns {Promise} true if the voice state was successfully updated, otherwise false + */ + async setSelfDeaf(deaf) { + if (this.id !== this.client.user.id) return new Error('VOICE_STATE_NOT_OWN'); + if (typeof deaf !== 'boolean') return new TypeError('VOICE_STATE_INVALID_TYPE', 'deaf'); + if (!this.connection) return false; + this.selfDeaf = deaf; + await this.connection.sendVoiceStateUpdate(); + return true; + } + + toJSON() { + return super.toJSON({ + id: true, + serverDeaf: true, + serverMute: true, + selfDeaf: true, + selfMute: true, + sessionID: true, + channelID: 'channel', + }); + } +} + +module.exports = VoiceState; 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..d0cf7c6 --- /dev/null +++ b/node_modules/discord.js/src/structures/Webhook.js @@ -0,0 +1,273 @@ +'use strict'; + +const APIMessage = require('./APIMessage'); +const Channel = require('./Channel'); +const { WebhookTypes } = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const Snowflake = require('../util/Snowflake'); + +/** + * Represents a webhook. + */ +class Webhook { + constructor(client, data) { + /** + * The client that instantiated the webhook + * @name Webhook#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + if (data) this._patch(data); + } + + _patch(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 = 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.cache.get(data.user.id) : data.user; + } else { + this.owner = null; + } + } + + /** + * Options that can be passed into send. + * @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 {Object[]} [embeds] An array of embeds for the message + * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content + * (see [here](https://discord.com/developers/docs/resources/channel#embed-object) for more details) + * @property {DisableMentionType} [disableMentions=this.client.options.disableMentions] Whether or not all mentions or + * everyone/here mentions should be sanitized to prevent unexpected mentions + * @property {FileOptions[]|string[]} [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. + */ + + /** + * Sends a message with this webhook. + * @param {StringResolvable|APIMessage} [content=''] The content to send + * @param {WebhookMessageOptions|MessageAdditions} [options={}] The options to provide + * @returns {Promise} + * @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); + */ + async send(content, options) { + let apiMessage; + + if (content instanceof APIMessage) { + apiMessage = content.resolveData(); + } else { + apiMessage = APIMessage.create(this, content, options).resolveData(); + if (Array.isArray(apiMessage.data.content)) { + return Promise.all(apiMessage.split().map(this.send.bind(this))); + } + } + + const { data, files } = await apiMessage.resolveFiles(); + return this.client.api + .webhooks(this.id, this.token) + .post({ + data, + files, + query: { wait: true }, + auth: false, + }) + .then(d => { + const channel = this.client.channels ? this.client.channels.cache.get(d.channel_id) : undefined; + if (!channel) return d; + return channel.messages.add(d, false); + }); + } + + /** + * Sends 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.api + .webhooks(this.id, this.token) + .slack.post({ + query: { wait: true }, + auth: false, + data: body, + }) + .then(data => data.toString() === 'ok'); + } + + /** + * Edits the webhook. + * @param {Object} options Options + * @param {string} [options.name=this.name] New name for this webhook + * @param {BufferResolvable} [options.avatar] New avatar for this webhook + * @param {ChannelResolvable} [options.channel] New channel for this webhook + * @param {string} [reason] Reason for editing this webhook + * @returns {Promise} + */ + async edit({ name = this.name, avatar, channel }, reason) { + if (avatar && typeof avatar === 'string' && !avatar.startsWith('data:')) { + avatar = await DataResolver.resolveImage(avatar); + } + if (channel) channel = channel instanceof Channel ? channel.id : channel; + const data = await this.client.api.webhooks(this.id, channel ? undefined : this.token).patch({ + data: { name, avatar, channel_id: channel }, + reason, + }); + + this.name = data.name; + this.avatar = data.avatar; + this.channelID = data.channel_id; + return this; + } + + /** + * Deletes the webhook. + * @param {string} [reason] Reason for deleting this webhook + * @returns {Promise} + */ + delete(reason) { + return this.client.api.webhooks(this.id, this.token).delete({ reason }); + } + /** + * 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); + } + + /** + * The url of this webhook + * @type {string} + * @readonly + */ + get url() { + return this.client.options.http.api + this.client.api.webhooks(this.id, this.token); + } + + /** + * A link to the webhook's avatar. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + avatarURL({ format, size } = {}) { + if (!this.avatar) return null; + return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size); + } + + static applyToClass(structure) { + for (const prop of ['send', 'sendSlackMessage', 'edit', 'delete', 'createdTimestamp', 'createdAt', 'url']) { + Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(Webhook.prototype, prop)); + } + } +} + +module.exports = Webhook; diff --git a/node_modules/discord.js/src/structures/interfaces/Application.js b/node_modules/discord.js/src/structures/interfaces/Application.js new file mode 100644 index 0000000..9781bfa --- /dev/null +++ b/node_modules/discord.js/src/structures/interfaces/Application.js @@ -0,0 +1,125 @@ +'use strict'; + +const { ClientApplicationAssetTypes, Endpoints } = require('../../util/Constants'); +const Snowflake = require('../../util/Snowflake'); +const Base = require('../Base'); + +const AssetTypes = Object.keys(ClientApplicationAssetTypes); + +/** + * Represents an OAuth2 Application. + * @abstract + */ +class Application extends Base { + constructor(client, data) { + super(client); + this._patch(data); + } + + _patch(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 timestamp the app was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the app was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * A link to the application's icon. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} URL to the icon + */ + iconURL({ format, size } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size }); + } + + /** + * A link to this application's cover image. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} URL to the cover image + */ + coverImage({ format, size } = {}) { + if (!this.cover) return null; + return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size }); + } + + /** + * Asset data. + * @typedef {Object} ApplicationAsset + * @property {Snowflake} id The asset ID + * @property {string} name The asset name + * @property {string} type The asset type + */ + + /** + * Gets the clients rich presence assets. + * @returns {Promise>} + */ + fetchAssets() { + return this.client.api.oauth2 + .applications(this.id) + .assets.get() + .then(assets => + assets.map(a => ({ + id: a.id, + name: a.name, + type: AssetTypes[a.type - 1], + })), + ); + } + + /** + * When concatenated with a string, this automatically returns the application's name instead of the + * Oauth2Application object. + * @returns {string} + * @example + * // Logs: Application name: My App + * console.log(`Application name: ${application}`); + */ + toString() { + return this.name; + } + + toJSON() { + return super.toJSON({ createdTimestamp: true }); + } +} + +module.exports = Application; 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..969b3d2 --- /dev/null +++ b/node_modules/discord.js/src/structures/interfaces/Collector.js @@ -0,0 +1,281 @@ +'use strict'; + +const EventEmitter = require('events'); +const Collection = require('../../util/Collection'); +const Util = require('../../util/Util'); + +/** + * 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|Promise} + */ + +/** + * Options to be applied to the collector. + * @typedef {Object} CollectorOptions + * @property {number} [time] How long to run the collector for in milliseconds + * @property {number} [idle] How long to stop the collector after inactivity in milliseconds + * @property {boolean} [dispose=false] Whether to dispose data when it's deleted + */ + +/** + * Abstract class for defining a new Collector. + * @abstract + */ +class Collector extends EventEmitter { + constructor(client, filter, options = {}) { + super(); + + /** + * The client that instantiated this Collector + * @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; + + this.handleCollect = this.handleCollect.bind(this); + this.handleDispose = this.handleDispose.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); + } + + /** + * Call this to handle an event as a collectable element. Accepts any event data as parameters. + * @param {...*} args The arguments emitted by the listener + * @emits Collector#collect + */ + async handleCollect(...args) { + const collect = this.collect(...args); + + if (collect && (await this.filter(...args, this.collected))) { + this.collected.set(collect, args[0]); + + /** + * Emitted whenever an element is collected. + * @event Collector#collect + * @param {...*} args The arguments emitted by the listener + */ + this.emit('collect', ...args); + + if (this._idletimeout) { + this.client.clearTimeout(this._idletimeout); + this._idletimeout = this.client.setTimeout(() => this.stop('idle'), this.options.idle); + } + } + this.checkEnd(); + } + + /** + * Call this to remove an element from the collection. Accepts any event data as parameters. + * @param {...*} args The arguments emitted by the listener + * @emits Collector#dispose + */ + handleDispose(...args) { + if (!this.options.dispose) return; + + const dispose = this.dispose(...args); + if (!dispose || !this.filter(...args) || !this.collected.has(dispose)) return; + this.collected.delete(dispose); + + /** + * Emitted whenever an element is disposed of. + * @event Collector#dispose + * @param {...*} args The arguments emitted by the listener + */ + this.emit('dispose', ...args); + this.checkEnd(); + } + + /** + * Returns 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); + }); + } + + /** + * Stops this collector and emits 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; + + /** + * 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); + } + + /** + * Resets the collectors timeout and idle timer. + * @param {Object} [options] Options + * @param {number} [options.time] How long to run the collector for in milliseconds + * @param {number} [options.idle] How long to stop the collector after inactivity in milliseconds + */ + resetTimer({ time, idle } = {}) { + if (this._timeout) { + this.client.clearTimeout(this._timeout); + this._timeout = this.client.setTimeout(() => this.stop('time'), time || this.options.time); + } + if (this._idletimeout) { + this.client.clearTimeout(this._idletimeout); + this._idletimeout = this.client.setTimeout(() => this.stop('idle'), idle || this.options.idle); + } + } + + /** + * Checks whether the collector should end, and if so, ends it. + */ + checkEnd() { + const reason = this.endReason(); + if (reason) this.stop(reason); + } + + /** + * Allows collectors to be consumed with for-await-of loops + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of} + */ + async *[Symbol.asyncIterator]() { + const queue = []; + const onCollect = item => queue.push(item); + this.on('collect', onCollect); + + try { + while (queue.length || !this.ended) { + if (queue.length) { + yield queue.shift(); + } else { + // eslint-disable-next-line no-await-in-loop + await new Promise(resolve => { + const tick = () => { + this.removeListener('collect', tick); + this.removeListener('end', tick); + return resolve(); + }; + this.on('collect', tick); + this.on('end', tick); + }); + } + } + } finally { + this.removeListener('collect', onCollect); + } + } + + toJSON() { + return Util.flatten(this); + } + + /* eslint-disable no-empty-function, valid-jsdoc */ + /** + * Handles incoming events from the `handleCollect` function. Returns null if the event should not + * be collected, or returns an object describing the data that should be stored. + * @see Collector#handleCollect + * @param {...*} args Any args the event listener emits + * @returns {?{key, value}} Data to insert into collection, if any + * @abstract + */ + collect() {} + + /** + * Handles incoming events from the `handleDispose`. Returns null if the event should not + * be disposed, or returns the key that should be removed. + * @see Collector#handleDispose + * @param {...*} args Any args the event listener emits + * @returns {?*} Key to remove from the collection, if any + * @abstract + */ + dispose() {} + + /** + * The reason this collector has ended or will end with. + * @returns {?string} Reason to end the collector, if any + * @abstract + */ + endReason() {} + /* 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..f5269e9 --- /dev/null +++ b/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js @@ -0,0 +1,393 @@ +'use strict'; + +/* eslint-disable import/order */ +const MessageCollector = require('../MessageCollector'); +const APIMessage = require('../APIMessage'); +const Snowflake = require('../../util/Snowflake'); +const Collection = require('../../util/Collection'); +const { RangeError, TypeError } = require('../../errors'); + +/** + * Interface for classes that have text-channel-like features. + * @interface + */ +class TextBasedChannel { + constructor() { + /** + * A manager of the messages sent to this channel + * @type {MessageManager} + */ + this.messages = new MessageManager(this); + + /** + * The ID of the last message in the channel, if one was sent + * @type {?Snowflake} + */ + this.lastMessageID = null; + + /** + * The timestamp when the last pinned message was pinned, if there was one + * @type {?number} + */ + this.lastPinTimestamp = null; + } + + /** + * The Message object of the last message in the channel, if one was sent + * @type {?Message} + * @readonly + */ + get lastMessage() { + return this.messages.cache.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; + } + + /** + * 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 {string} [content=''] The content for the message + * @property {MessageEmbed|Object} [embed] An embed for the message + * (see [here](https://discord.com/developers/docs/resources/channel#embed-object) for more details) + * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content + * @property {DisableMentionType} [disableMentions=this.client.options.disableMentions] Whether or not all mentions or + * everyone/here mentions should be sanitized to prevent unexpected mentions + * @property {FileOptions[]|BufferResolvable[]} [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) + */ + + /** + * Options provided to control parsing of mentions by Discord + * @typedef {Object} MessageMentionOptions + * @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed + * @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions + * @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions + */ + + /** + * Types of mentions to enable in MessageMentionOptions. + * - `roles` + * - `users` + * - `everyone` + * @typedef {string} MessageMentionTypes + */ + + /** + * The type of mentions to disable. + * - `none` + * - `all` + * - `everyone` + * @typedef {string} DisableMentionType + */ + + /** + * @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=2000] 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 + */ + + /** + * Sends a message to this channel. + * @param {StringResolvable|APIMessage} [content=''] The content to send + * @param {MessageOptions|MessageAdditions} [options={}] The options to provide + * @returns {Promise} + * @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); + */ + async send(content, options) { + const User = require('../User'); + const GuildMember = require('../GuildMember'); + + if (this instanceof User || this instanceof GuildMember) { + return this.createDM().then(dm => dm.send(content, options)); + } + + let apiMessage; + + if (content instanceof APIMessage) { + apiMessage = content.resolveData(); + } else { + apiMessage = APIMessage.create(this, content, options).resolveData(); + if (Array.isArray(apiMessage.data.content)) { + return Promise.all(apiMessage.split().map(this.send.bind(this))); + } + } + + const { data, files } = await apiMessage.resolveFiles(); + return this.client.api.channels[this.id].messages + .post({ data, files }) + .then(d => this.client.actions.MessageCreate.handle(d).message); + } + + /** + * Starts a typing indicator in the channel. + * @param {number} [count=1] The number of times startTyping should be considered to have been called + * @returns {Promise} Resolves once the bot stops typing gracefully, or rejects when an error occurs + * @example + * // Start typing in a channel, or increase the typing count by one + * channel.startTyping(); + * @example + * // Start typing in a channel with a typing count of five, or set it to five + * channel.startTyping(5); + */ + startTyping(count) { + if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT'); + if (this.client.user._typing.has(this.id)) { + const entry = this.client.user._typing.get(this.id); + entry.count = count || entry.count + 1; + return entry.promise; + } + + const entry = {}; + entry.promise = new Promise((resolve, reject) => { + const endpoint = this.client.api.channels[this.id].typing; + Object.assign(entry, { + count: count || 1, + interval: this.client.setInterval(() => { + endpoint.post().catch(error => { + this.client.clearInterval(entry.interval); + this.client.user._typing.delete(this.id); + reject(error); + }); + }, 9000), + resolve, + }); + endpoint.post().catch(error => { + this.client.clearInterval(entry.interval); + this.client.user._typing.delete(this.id); + reject(error); + }); + this.client.user._typing.set(this.id, entry); + }); + return entry.promise; + } + + /** + * Stops the typing indicator in the channel. + * The indicator will only stop if this is called as many times as startTyping(). + * It can take a few seconds for the client user to stop typing. + * @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 regardless of typing count + * 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); + entry.resolve(); + } + } + } + + /** + * 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; + } + + /** + * 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 createMessageCollector 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>} + * @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.createMessageCollector(filter, options); + collector.once('end', (collection, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(collection); + } else { + resolve(collection); + } + }); + }); + } + + /** + * Bulk deletes given messages that are newer than two weeks. + * @param {Collection|MessageResolvable[]|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>} Deleted messages + * @example + * // Bulk delete messages + * channel.bulkDelete(5) + * .then(messages => console.log(`Bulk deleted ${messages.size} messages`)) + * .catch(console.error); + */ + async bulkDelete(messages, filterOld = false) { + if (Array.isArray(messages) || 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 new Collection(); + if (messageIDs.length === 1) { + await this.client.api.channels(this.id).messages(messageIDs[0]).delete(); + const message = this.client.actions.MessageDelete.getMessage( + { + message_id: messageIDs[0], + }, + this, + ); + return message ? new Collection([[message.id, message]]) : new Collection(); + } + await this.client.api.channels[this.id].messages['bulk-delete'].post({ data: { messages: messageIDs } }); + return messageIDs.reduce( + (col, id) => + col.set( + id, + this.client.actions.MessageDeleteBulk.getMessage( + { + message_id: id, + }, + this, + ), + ), + new Collection(), + ); + } + if (!isNaN(messages)) { + const msgs = await this.messages.fetch({ limit: messages }); + return this.bulkDelete(msgs, filterOld); + } + throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); + } + + static applyToClass(structure, full = false, ignore = []) { + const props = ['send']; + if (full) { + props.push( + 'lastMessage', + 'lastPinAt', + 'bulkDelete', + 'startTyping', + 'stopTyping', + 'typing', + 'typingCount', + 'createMessageCollector', + 'awaitMessages', + ); + } + for (const prop of props) { + if (ignore.includes(prop)) continue; + Object.defineProperty( + structure.prototype, + prop, + Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop), + ); + } + } +} + +module.exports = TextBasedChannel; + +// Fixes Circular +const MessageManager = require('../../managers/MessageManager'); diff --git a/node_modules/discord.js/src/util/ActivityFlags.js b/node_modules/discord.js/src/util/ActivityFlags.js new file mode 100644 index 0000000..237ec12 --- /dev/null +++ b/node_modules/discord.js/src/util/ActivityFlags.js @@ -0,0 +1,38 @@ +'use strict'; + +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to interact with an {@link Activity#flags} bitfield. + * @extends {BitField} + */ +class ActivityFlags extends BitField {} + +/** + * @name ActivityFlags + * @kind constructor + * @memberof ActivityFlags + * @param {BitFieldResolvable} [bits=0] Bit(s) to read from + */ + +/** + * Numeric activity flags. All available properties: + * * `INSTANCE` + * * `JOIN` + * * `SPECTATE` + * * `JOIN_REQUEST` + * * `SYNC` + * * `PLAY` + * @type {Object} + * @see {@link https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags} + */ +ActivityFlags.FLAGS = { + INSTANCE: 1 << 0, + JOIN: 1 << 1, + SPECTATE: 1 << 2, + JOIN_REQUEST: 1 << 3, + SYNC: 1 << 4, + PLAY: 1 << 5, +}; + +module.exports = ActivityFlags; 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..4d03aa6 --- /dev/null +++ b/node_modules/discord.js/src/util/BitField.js @@ -0,0 +1,164 @@ +'use strict'; + +const { RangeError } = require('../errors'); + +/** + * Data structure that makes it easy to interact with a bitfield. + */ +class BitField { + /** + * @param {BitFieldResolvable} [bits=0] Bit(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 Bit(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} 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, bit] of Object.entries(this.constructor.FLAGS)) serialized[flag] = this.has(bit, ...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]; + const error = new RangeError('BITFIELD_INVALID'); + error.bit = bit; + throw error; + } +} + +/** + * Numeric bitfield flags. + * Defined in extension classes + * @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..3219fb8 --- /dev/null +++ b/node_modules/discord.js/src/util/Collection.js @@ -0,0 +1,17 @@ +'use strict'; + +const BaseCollection = require('@discordjs/collection'); +const Util = require('./Util'); + +class Collection extends BaseCollection { + toJSON() { + return this.map(e => (typeof e.toJSON === 'function' ? e.toJSON() : Util.flatten(e))); + } +} + +module.exports = Collection; + +/** + * @external Collection + * @see {@link https://discord.js.org/#/docs/collection/master/class/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..2d9ab42 --- /dev/null +++ b/node_modules/discord.js/src/util/Constants.js @@ -0,0 +1,654 @@ +'use strict'; + +const Package = (exports.Package = require('../../package.json')); +const { Error, RangeError } = require('../errors'); +const browser = (exports.browser = typeof window !== 'undefined'); + +/** + * Options for a client. + * @typedef {Object} ClientOptions + * @property {number|number[]|string} [shards] ID of the shard to run, or an array of shard IDs. If not specified, + * the client will spawn {@link ClientOptions#shardCount} shards. If set to `auto`, it will fetch the + * recommended amount of shards from Discord and spawn that amount + * @property {number} [shardCount=1] The total amount of shards used by all processes of this bot + * (e.g. recommended shard count, shard count of the ShardingManager) + * @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 {number} [messageEditHistoryMaxSize=-1] Maximum number of previous versions to hold for an edited message + * (-1 or Infinity for unlimited - don't do this without sweeping, otherwise memory usage may climb indefinitely.) + * @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 {DisableMentionType} [disableMentions='none'] Default value for {@link MessageOptions#disableMentions} + * @property {MessageMentionOptions} [allowedMentions] Default value for {@link MessageOptions#allowedMentions} + * @property {PartialType[]} [partials] Structures allowed to be partial. This means events can be emitted even when + * they're missing all the data for a particular structure. See the "Partials" topic listed in the sidebar for some + * important usage information, as partials require you to put checks in place when handling data. + * @property {number} [restWsBridgeTimeout=5000] Maximum time permitted between REST responses and their + * corresponding websocket events + * @property {number} [restTimeOffset=500] Extra time in milliseconds to wait before continuing to make REST + * requests (higher values will reduce rate-limiting errors on bad connections) + * @property {number} [restRequestTimeout=15000] Time to wait before cancelling a REST request, in milliseconds + * @property {number} [restSweepInterval=60] How frequently to delete inactive request buckets, in seconds + * (or 0 for never) + * @property {number} [retryLimit=1] How many times to retry on 5XX errors (Infinity for indefinite amount of retries) + * @property {PresenceData} [presence] Presence data to use upon login + * @property {WebsocketOptions} [ws] Options for the WebSocket + * @property {HTTPOptions} [http] HTTP options + */ +exports.DefaultOptions = { + shardCount: 1, + messageCacheMaxSize: 200, + messageCacheLifetime: 0, + messageSweepInterval: 0, + messageEditHistoryMaxSize: -1, + fetchAllMembers: false, + disableMentions: 'none', + partials: [], + restWsBridgeTimeout: 5000, + restRequestTimeout: 15000, + retryLimit: 1, + restTimeOffset: 500, + restSweepInterval: 60, + presence: {}, + + /** + * WebSocket options (these are left as snake_case to match the API) + * @typedef {Object} WebsocketOptions + * @property {number} [large_threshold=50] Number of members in a guild after which offline users will no longer be + * sent in the initial guild member list, must be between 50 and 250 + * @property {IntentsResolvable} [intents] Intents to enable for this connection + */ + ws: { + large_threshold: 50, + compress: false, + properties: { + $os: browser ? 'browser' : process.platform, + $browser: 'discord.js', + $device: 'discord.js', + }, + version: 6, + }, + + /** + * HTTP options + * @typedef {Object} HTTPOptions + * @property {number} [version=7] API version to use + * @property {string} [api='https://discord.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, + api: 'https://discord.com/api', + cdn: 'https://cdn.discordapp.com', + invite: 'https://discord.gg', + }, +}; + +exports.UserAgent = browser + ? null + : `DiscordBot (${Package.homepage.split('#')[0]}, ${Package.version}) Node.js/${process.version}`; + +exports.WSCodes = { + 1000: 'WS_CLOSE_REQUESTED', + 4004: 'TOKEN_INVALID', + 4010: 'SHARDING_INVALID', + 4011: 'SHARDING_REQUIRED', + 4013: 'INVALID_INTENTS', + 4014: 'DISALLOWED_INTENTS', +}; + +const AllowedImageFormats = ['webp', 'png', 'jpg', 'jpeg', 'gif']; + +const AllowedImageSizes = Array.from({ length: 9 }, (e, i) => 2 ** (i + 4)); + +function makeImageUrl(root, { format = 'webp', size } = {}) { + if (format && !AllowedImageFormats.includes(format)) throw new Error('IMAGE_FORMAT', format); + if (size && !AllowedImageSizes.includes(size)) throw new RangeError('IMAGE_SIZE', size); + return `${root}.${format}${size ? `?size=${size}` : ''}`; +} +/** + * Options for Image URLs. + * @typedef {Object} ImageURLOptions + * @property {string} [format] One of `webp`, `png`, `jpg`, `jpeg`, `gif`. If no format is provided, + * defaults to `webp`. + * @property {boolean} [dynamic] If true, the format will dynamically change to `gif` for + * animated avatars; the default is false. + * @property {number} [size] One of `16`, `32`, `64`, `128`, `256`, `512`, `1024`, `2048`, `4096` + */ + +exports.Endpoints = { + CDN(root) { + return { + Emoji: (emojiID, format = 'png') => `${root}/emojis/${emojiID}.${format}`, + Asset: name => `${root}/assets/${name}`, + DefaultAvatar: discriminator => `${root}/embed/avatars/${discriminator}.png`, + Avatar: (userID, hash, format = 'webp', size, dynamic = false) => { + if (dynamic) format = hash.startsWith('a_') ? 'gif' : format; + return makeImageUrl(`${root}/avatars/${userID}/${hash}`, { format, size }); + }, + Banner: (guildID, hash, format = 'webp', size) => + makeImageUrl(`${root}/banners/${guildID}/${hash}`, { format, size }), + Icon: (guildID, hash, format = 'webp', size, dynamic = false) => { + if (dynamic) format = hash.startsWith('a_') ? 'gif' : format; + return makeImageUrl(`${root}/icons/${guildID}/${hash}`, { format, size }); + }, + AppIcon: (clientID, hash, { format = 'webp', size } = {}) => + makeImageUrl(`${root}/app-icons/${clientID}/${hash}`, { size, format }), + AppAsset: (clientID, hash, { format = 'webp', size } = {}) => + makeImageUrl(`${root}/app-assets/${clientID}/${hash}`, { size, format }), + GDMIcon: (channelID, hash, format = 'webp', size) => + makeImageUrl(`${root}/channel-icons/${channelID}/${hash}`, { size, format }), + Splash: (guildID, hash, format = 'webp', size) => + makeImageUrl(`${root}/splashes/${guildID}/${hash}`, { size, format }), + DiscoverySplash: (guildID, hash, format = 'webp', size) => + makeImageUrl(`${root}/discovery-splashes/${guildID}/${hash}`, { size, format }), + TeamIcon: (teamID, hash, { format = 'webp', size } = {}) => + makeImageUrl(`${root}/team-icons/${teamID}/${hash}`, { size, format }), + }; + }, + invite: (root, code) => `${root}/${code}`, + botGateway: '/gateway/bot', +}; + +/** + * The current status of the client. Here are the available statuses: + * * READY: 0 + * * CONNECTING: 1 + * * RECONNECTING: 2 + * * IDLE: 3 + * * NEARLY: 4 + * * DISCONNECTED: 5 + * * WAITING_FOR_GUILDS: 6 + * * IDENTIFYING: 7 + * * RESUMING: 8 + * @typedef {number} Status + */ +exports.Status = { + READY: 0, + CONNECTING: 1, + RECONNECTING: 2, + IDLE: 3, + NEARLY: 4, + DISCONNECTED: 5, + WAITING_FOR_GUILDS: 6, + IDENTIFYING: 7, + RESUMING: 8, +}; + +/** + * 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.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, + HELLO: 8, + CLIENT_CONNECT: 12, + CLIENT_DISCONNECT: 13, +}; + +exports.Events = { + RATE_LIMIT: 'rateLimit', + CLIENT_READY: 'ready', + 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', + INVITE_CREATE: 'inviteCreate', + INVITE_DELETE: 'inviteDelete', + GUILD_ROLE_UPDATE: 'roleUpdate', + GUILD_EMOJI_CREATE: 'emojiCreate', + GUILD_EMOJI_DELETE: 'emojiDelete', + GUILD_EMOJI_UPDATE: 'emojiUpdate', + GUILD_BAN_ADD: 'guildBanAdd', + GUILD_BAN_REMOVE: 'guildBanRemove', + 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_ALL: 'messageReactionRemoveAll', + MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji', + USER_UPDATE: 'userUpdate', + PRESENCE_UPDATE: 'presenceUpdate', + VOICE_SERVER_UPDATE: 'voiceServerUpdate', + VOICE_STATE_UPDATE: 'voiceStateUpdate', + VOICE_BROADCAST_SUBSCRIBE: 'subscribe', + VOICE_BROADCAST_UNSUBSCRIBE: 'unsubscribe', + TYPING_START: 'typingStart', + TYPING_STOP: 'typingStop', + WEBHOOKS_UPDATE: 'webhookUpdate', + ERROR: 'error', + WARN: 'warn', + DEBUG: 'debug', + SHARD_DISCONNECT: 'shardDisconnect', + SHARD_ERROR: 'shardError', + SHARD_RECONNECTING: 'shardReconnecting', + SHARD_READY: 'shardReady', + SHARD_RESUME: 'shardResume', + INVALIDATED: 'invalidated', + RAW: 'raw', +}; + +exports.ShardEvents = { + CLOSE: 'close', + DESTROYED: 'destroyed', + INVALID_SESSION: 'invalidSession', + READY: 'ready', + RESUMED: 'resumed', + ALL_READY: 'allReady', +}; + +/** + * The type of Structure allowed to be a partial: + * * USER + * * CHANNEL (only affects DMChannels) + * * GUILD_MEMBER + * * MESSAGE + * * REACTION + * Partials require you to put checks in place when handling data, read the Partials topic listed in the + * sidebar for more information. + * @typedef {string} PartialType + */ +exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', 'REACTION']); + +/** + * The type of a websocket message event, e.g. `MESSAGE_CREATE`. Here are the available events: + * * READY + * * RESUMED + * * GUILD_CREATE + * * GUILD_DELETE + * * GUILD_UPDATE + * * INVITE_CREATE + * * INVITE_DELETE + * * 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 + * * GUILD_EMOJIS_UPDATE + * * 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 + * * MESSAGE_REACTION_REMOVE_EMOJI + * * USER_UPDATE + * * PRESENCE_UPDATE + * * TYPING_START + * * VOICE_STATE_UPDATE + * * VOICE_SERVER_UPDATE + * * WEBHOOKS_UPDATE + * @typedef {string} WSEventType + */ +exports.WSEvents = keyMirror([ + 'READY', + 'RESUMED', + 'GUILD_CREATE', + 'GUILD_DELETE', + 'GUILD_UPDATE', + 'INVITE_CREATE', + 'INVITE_DELETE', + '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', + 'GUILD_EMOJIS_UPDATE', + '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', + 'MESSAGE_REACTION_REMOVE_EMOJI', + 'USER_UPDATE', + 'PRESENCE_UPDATE', + 'TYPING_START', + 'VOICE_STATE_UPDATE', + 'VOICE_SERVER_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', + null, + 'GUILD_DISCOVERY_DISQUALIFIED', + 'GUILD_DISCOVERY_REQUALIFIED', +]; + +/** + * Bots cannot set a `CUSTOM_STATUS`, it is only for custom statuses received from users + * The type of an activity of a users presence, e.g. `PLAYING`. Here are the available types: + * * PLAYING + * * STREAMING + * * LISTENING + * * WATCHING + * * CUSTOM_STATUS + * * COMPETING + * @typedef {string} ActivityType + */ +exports.ActivityTypes = ['PLAYING', 'STREAMING', 'LISTENING', 'WATCHING', 'CUSTOM_STATUS', 'COMPETING']; + +exports.ChannelTypes = { + TEXT: 0, + DM: 1, + VOICE: 2, + GROUP: 3, + CATEGORY: 4, + NEWS: 5, + STORE: 6, +}; + +exports.ClientApplicationAssetTypes = { + SMALL: 1, + BIG: 2, +}; + +exports.Colors = { + DEFAULT: 0x000000, + WHITE: 0xffffff, + AQUA: 0x1abc9c, + GREEN: 0x2ecc71, + BLUE: 0x3498db, + YELLOW: 0xffff00, + 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 explicit content filter levels for a guild: + * * DISABLED + * * MEMBERS_WITHOUT_ROLES + * * ALL_MEMBERS + * @typedef {string} ExplicitContentFilterLevel + */ +exports.ExplicitContentFilterLevels = ['DISABLED', 'MEMBERS_WITHOUT_ROLES', 'ALL_MEMBERS']; + +/** + * The value set for the verification levels for a guild: + * * NONE + * * LOW + * * MEDIUM + * * HIGH + * * VERY_HIGH + * @typedef {string} VerificationLevel + */ +exports.VerificationLevels = ['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_HIGH']; + +/** + * 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', +]; + +function keyMirror(arr) { + let tmp = Object.create(null); + for (const value of arr) tmp[value] = value; + return tmp; +} diff --git a/node_modules/discord.js/src/util/DataResolver.js b/node_modules/discord.js/src/util/DataResolver.js new file mode 100644 index 0000000..3285ffe --- /dev/null +++ b/node_modules/discord.js/src/util/DataResolver.js @@ -0,0 +1,129 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const stream = require('stream'); +const fetch = require('node-fetch'); +const { Error: DiscordError, TypeError } = require('../errors'); +const { browser } = require('../util/Constants'); +const Util = require('../util/Util'); + +/** + * The DataResolver identifies different objects and tries to resolve a specific piece of information from them. + * @private + */ +class DataResolver { + constructor() { + throw new Error(`The ${this.constructor.name} class may not be instantiated.`); + } + + /** + * 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} + */ + static 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; + } + + /** + * Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image. + * @param {BufferResolvable|Base64Resolvable} image The image to be resolved + * @returns {Promise} + */ + static async resolveImage(image) { + if (!image) return null; + if (typeof image === 'string' && image.startsWith('data:')) { + return image; + } + const file = await this.resolveFileAsBuffer(image); + return DataResolver.resolveBase64(file); + } + + /** + * 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} + */ + static resolveBase64(data) { + if (Buffer.isBuffer(data)) 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 + * @typedef {string|Buffer} BufferResolvable + */ + + /** + * @external Stream + * @see {@link https://nodejs.org/api/stream.html} + */ + + /** + * Resolves a BufferResolvable to a Buffer or a Stream. + * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve + * @returns {Promise} + */ + static async resolveFile(resource) { + if (!browser && Buffer.isBuffer(resource)) return resource; + if (browser && resource instanceof ArrayBuffer) return Util.convertToBuffer(resource); + // eslint-disable-next-line no-undef + if (browser && resource instanceof Blob) return resource; + if (resource instanceof stream.Readable) return resource; + + if (typeof resource === 'string') { + if (/^https?:\/\//.test(resource)) { + const res = await fetch(resource); + return browser ? res.blob() : res.body; + } else if (!browser) { + return new Promise((resolve, reject) => { + const file = path.resolve(resource); + fs.stat(file, (err, stats) => { + if (err) return reject(err); + if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file)); + return resolve(fs.createReadStream(file)); + }); + }); + } + } + + throw new TypeError('REQ_RESOURCE_TYPE'); + } + + /** + * Resolves a BufferResolvable to a Buffer. + * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve + * @returns {Promise} + */ + static async resolveFileAsBuffer(resource) { + const file = await this.resolveFile(resource); + if (Buffer.isBuffer(file)) return file; + + const buffers = []; + for await (const data of file) buffers.push(data); + return Buffer.concat(buffers); + } +} + +module.exports = DataResolver; diff --git a/node_modules/discord.js/src/util/Intents.js b/node_modules/discord.js/src/util/Intents.js new file mode 100644 index 0000000..e627c8b --- /dev/null +++ b/node_modules/discord.js/src/util/Intents.js @@ -0,0 +1,83 @@ +'use strict'; +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to calculate intents. + * @extends {BitField} + */ +class Intents extends BitField {} + +/** + * @name Intents + * @kind constructor + * @memberof Intents + * @param {IntentsResolvable} [bits=0] Bit(s) to read from + */ + +/** + * Data that can be resolved to give a permission number. This can be: + * * A string (see {@link Intents.FLAGS}) + * * An intents flag + * * An instance of Intents + * * An array of IntentsResolvable + * @typedef {string|number|Intents|IntentsResolvable[]} IntentsResolvable + */ + +/** + * Numeric websocket intents. All available properties: + * * `GUILDS` + * * `GUILD_MEMBERS` + * * `GUILD_BANS` + * * `GUILD_EMOJIS` + * * `GUILD_INTEGRATIONS` + * * `GUILD_WEBHOOKS` + * * `GUILD_INVITES` + * * `GUILD_VOICE_STATES` + * * `GUILD_PRESENCES` + * * `GUILD_MESSAGES` + * * `GUILD_MESSAGE_REACTIONS` + * * `GUILD_MESSAGE_TYPING` + * * `DIRECT_MESSAGES` + * * `DIRECT_MESSAGE_REACTIONS` + * * `DIRECT_MESSAGE_TYPING` + * @type {Object} + * @see {@link https://discord.com/developers/docs/topics/gateway#list-of-intents} + */ +Intents.FLAGS = { + GUILDS: 1 << 0, + GUILD_MEMBERS: 1 << 1, + GUILD_BANS: 1 << 2, + GUILD_EMOJIS: 1 << 3, + GUILD_INTEGRATIONS: 1 << 4, + GUILD_WEBHOOKS: 1 << 5, + GUILD_INVITES: 1 << 6, + GUILD_VOICE_STATES: 1 << 7, + GUILD_PRESENCES: 1 << 8, + GUILD_MESSAGES: 1 << 9, + GUILD_MESSAGE_REACTIONS: 1 << 10, + GUILD_MESSAGE_TYPING: 1 << 11, + DIRECT_MESSAGES: 1 << 12, + DIRECT_MESSAGE_REACTIONS: 1 << 13, + DIRECT_MESSAGE_TYPING: 1 << 14, +}; + +/** + * Bitfield representing all privileged intents + * @type {number} + * @see {@link https://discord.com/developers/docs/topics/gateway#privileged-intents} + */ +Intents.PRIVILEGED = Intents.FLAGS.GUILD_MEMBERS | Intents.FLAGS.GUILD_PRESENCES; + +/** + * Bitfield representing all intents combined + * @type {number} + */ +Intents.ALL = Object.values(Intents.FLAGS).reduce((acc, p) => acc | p, 0); + +/** + * Bitfield representing all non-privileged intents + * @type {number} + */ +Intents.NON_PRIVILEGED = Intents.ALL & ~Intents.PRIVILEGED; + +module.exports = Intents; diff --git a/node_modules/discord.js/src/util/LimitedCollection.js b/node_modules/discord.js/src/util/LimitedCollection.js new file mode 100644 index 0000000..caa4978 --- /dev/null +++ b/node_modules/discord.js/src/util/LimitedCollection.js @@ -0,0 +1,34 @@ +'use strict'; + +const Collection = require('./Collection.js'); + +/** + * A Collection which holds a max amount of entries. The first key is deleted if the Collection has + * reached max size. + * @extends {Collection} + * @param {number} [maxSize=0] The maximum size of the Collection + * @param {Iterable} [iterable=null] Optional entries passed to the Map constructor. + * @private + */ +class LimitedCollection extends Collection { + constructor(maxSize = 0, iterable = null) { + super(iterable); + /** + * The max size of the Collection. + * @type {number} + */ + this.maxSize = maxSize; + } + + set(key, value) { + if (this.maxSize === 0) return this; + if (this.size >= this.maxSize && !this.has(key)) this.delete(this.firstKey()); + return super.set(key, value); + } + + static get [Symbol.species]() { + return Collection; + } +} + +module.exports = LimitedCollection; 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..536cbd8 --- /dev/null +++ b/node_modules/discord.js/src/util/MessageFlags.js @@ -0,0 +1,36 @@ +'use strict'; + +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to interact with an {@link Message#flags} bitfield. + * @extends {BitField} + */ +class MessageFlags extends BitField {} + +/** + * @name MessageFlags + * @kind constructor + * @memberof MessageFlags + * @param {BitFieldResolvable} [bits=0] Bit(s) to read from + */ + +/** + * Numeric message flags. All available properties: + * * `CROSSPOSTED` + * * `IS_CROSSPOST` + * * `SUPPRESS_EMBEDS` + * * `SOURCE_MESSAGE_DELETED` + * * `URGENT` + * @type {Object} + * @see {@link https://discord.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..98a2838 --- /dev/null +++ b/node_modules/discord.js/src/util/Permissions.js @@ -0,0 +1,131 @@ +'use strict'; + +const BitField = require('./BitField'); + +/** + * 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 { + /** + * @name Permissions + * @kind constructor + * @memberof Permissions + * @param {PermissionResolvable} [bits=0] Bit(s) to read from + */ + + /** + * Data that can be resolved to give a permission number. This can be: + * * A string (see {@link Permissions.FLAGS}) + * * A permission number + * * An instance of Permissions + * * An Array of PermissionResolvable + * @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable + */ + + /** + * 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); + } +} + +/** + * 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` + * * `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) + * * `VIEW_GUILD_INSIGHTS` + * * `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_WEBHOOKS` + * * `MANAGE_EMOJIS` + * @type {Object} + * @see {@link https://discord.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, + 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, + USE_EXTERNAL_EMOJIS: 1 << 18, + VIEW_GUILD_INSIGHTS: 1 << 19, + 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_WEBHOOKS: 1 << 29, + MANAGE_EMOJIS: 1 << 30, +}; + +/** + * Bitfield representing every permission combined + * @type {number} + */ +Permissions.ALL = Object.values(Permissions.FLAGS).reduce((all, p) => all | p, 0); + +/** + * Bitfield representing the default permissions for users + * @type {number} + */ +Permissions.DEFAULT = 104324673; + +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..5d267ae --- /dev/null +++ b/node_modules/discord.js/src/util/Snowflake.js @@ -0,0 +1,84 @@ +'use strict'; + +const Util = require('../util/Util'); + +// 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. + * This hardcodes the worker ID as 1 and the process ID as 0. + * @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 = `${(timestamp - EPOCH).toString(2).padStart(42, '0')}0000100000${(INCREMENT++) + .toString(2) + .padStart(12, '0')}`; + return Util.binaryToID(BINARY); + } + + /** + * 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 = Util.idToBinary(snowflake).toString(2).padStart(64, '0'); + 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; + } +} + +module.exports = SnowflakeUtil; diff --git a/node_modules/discord.js/src/util/Speaking.js b/node_modules/discord.js/src/util/Speaking.js new file mode 100644 index 0000000..5fda560 --- /dev/null +++ b/node_modules/discord.js/src/util/Speaking.js @@ -0,0 +1,33 @@ +'use strict'; + +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to interact with a {@link VoiceConnection#speaking} + * and {@link guildMemberSpeaking} event bitfields. + * @extends {BitField} + */ +class Speaking extends BitField {} + +/** + * @name Speaking + * @kind constructor + * @memberof Speaking + * @param {BitFieldResolvable} [bits=0] Bit(s) to read from + */ + +/** + * Numeric speaking flags. All available properties: + * * `SPEAKING` + * * `SOUNDSHARE` + * * `PRIORITY_SPEAKING` + * @type {Object} + * @see {@link https://discord.com/developers/docs/topics/voice-connections#speaking} + */ +Speaking.FLAGS = { + SPEAKING: 1 << 0, + SOUNDSHARE: 1 << 1, + PRIORITY_SPEAKING: 1 << 2, +}; + +module.exports = Speaking; diff --git a/node_modules/discord.js/src/util/Structures.js b/node_modules/discord.js/src/util/Structures.js new file mode 100644 index 0000000..0c6ab35 --- /dev/null +++ b/node_modules/discord.js/src/util/Structures.js @@ -0,0 +1,112 @@ +'use strict'; + +/** + * An extendable structure: + * * **`GuildEmoji`** + * * **`DMChannel`** + * * **`TextChannel`** + * * **`VoiceChannel`** + * * **`CategoryChannel`** + * * **`NewsChannel`** + * * **`StoreChannel`** + * * **`GuildMember`** + * * **`Guild`** + * * **`Message`** + * * **`MessageReaction`** + * * **`Presence`** + * * **`ClientPresence`** + * * **`VoiceState`** + * * **`Role`** + * * **`User`** + * @typedef {string} ExtendableStructure + */ + +/** + * Allows for the extension of built-in Discord.js structures that are instantiated by {@link BaseManager Managers}. + */ +class Structures { + constructor() { + throw new Error(`The ${this.constructor.name} class may not be instantiated.`); + } + + /** + * Retrieves a structure class. + * @param {string} structure Name of the structure to retrieve + * @returns {Function} + */ + static get(structure) { + if (typeof structure === 'string') return structures[structure]; + throw new TypeError(`"structure" argument must be a string (received ${typeof structure})`); + } + + /** + * Extends a structure. + * Make sure to extend all structures before instantiating your client. + * Extending after doing so may not work as expected. + * @param {ExtendableStructure} structure Name of the structure class to extend + * @param {Function} extender Function that takes the base class to extend as its only parameter and returns the + * extended class/prototype + * @returns {Function} Extended class/prototype returned from the extender + * @example + * const { Structures } = require('discord.js'); + * + * Structures.extend('Guild', Guild => { + * class CoolGuild extends Guild { + * constructor(client, data) { + * super(client, data); + * this.cool = true; + * } + * } + * + * return CoolGuild; + * }); + */ + static extend(structure, extender) { + if (!structures[structure]) throw new RangeError(`"${structure}" is not a valid extensible structure.`); + if (typeof extender !== 'function') { + const received = `(received ${typeof extender})`; + throw new TypeError( + `"extender" argument must be a function that returns the extended structure class/prototype ${received}.`, + ); + } + + const extended = extender(structures[structure]); + if (typeof extended !== 'function') { + const received = `(received ${typeof extended})`; + throw new TypeError(`The extender function must return the extended structure class/prototype ${received}.`); + } + + if (!(extended.prototype instanceof structures[structure])) { + const prototype = Object.getPrototypeOf(extended); + const received = `${extended.name || 'unnamed'}${prototype.name ? ` extends ${prototype.name}` : ''}`; + throw new Error( + 'The class/prototype returned from the extender function must extend the existing structure class/prototype' + + ` (received function ${received}; expected extension of ${structures[structure].name}).`, + ); + } + + structures[structure] = extended; + return extended; + } +} + +const structures = { + GuildEmoji: require('../structures/GuildEmoji'), + DMChannel: require('../structures/DMChannel'), + TextChannel: require('../structures/TextChannel'), + VoiceChannel: require('../structures/VoiceChannel'), + CategoryChannel: require('../structures/CategoryChannel'), + NewsChannel: require('../structures/NewsChannel'), + StoreChannel: require('../structures/StoreChannel'), + GuildMember: require('../structures/GuildMember'), + Guild: require('../structures/Guild'), + Message: require('../structures/Message'), + MessageReaction: require('../structures/MessageReaction'), + Presence: require('../structures/Presence').Presence, + ClientPresence: require('../structures/ClientPresence'), + VoiceState: require('../structures/VoiceState'), + Role: require('../structures/Role'), + User: require('../structures/User'), +}; + +module.exports = Structures; 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..4d08b76 --- /dev/null +++ b/node_modules/discord.js/src/util/SystemChannelFlags.js @@ -0,0 +1,40 @@ +'use strict'; + +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to interact with a {@link Guild#systemChannelFlags} bitfield. + * Note that all event message types are enabled by default, + * and by setting their corresponding flags you are disabling them + * @extends {BitField} + */ +class SystemChannelFlags extends BitField {} + +/** + * @name SystemChannelFlags + * @kind constructor + * @memberof SystemChannelFlags + * @param {SystemChannelFlagsResolvable} [bits=0] Bit(s) to read from + */ + +/** + * 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/UserFlags.js b/node_modules/discord.js/src/util/UserFlags.js new file mode 100644 index 0000000..82c03ed --- /dev/null +++ b/node_modules/discord.js/src/util/UserFlags.js @@ -0,0 +1,55 @@ +'use strict'; +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to interact with a {@link User#flags} bitfield. + * @extends {BitField} + */ +class UserFlags extends BitField {} + +/** + * @name UserFlags + * @kind constructor + * @memberof UserFlags + * @param {BitFieldResolvable} [bits=0] Bit(s) to read from + */ + +/** + * Numeric user flags. All available properties: + * * `DISCORD_EMPLOYEE` + * * `PARTNERED_SERVER_OWNER` + * * `DISCORD_PARTNER` **(deprecated)** + * * `HYPESQUAD_EVENTS` + * * `BUGHUNTER_LEVEL_1` + * * `HOUSE_BRAVERY` + * * `HOUSE_BRILLIANCE` + * * `HOUSE_BALANCE` + * * `EARLY_SUPPORTER` + * * `TEAM_USER` + * * `SYSTEM` + * * `BUGHUNTER_LEVEL_2` + * * `VERIFIED_BOT` + * * `EARLY_VERIFIED_BOT_DEVELOPER` + * * `VERIFIED_DEVELOPER` **(deprecated)** + * @type {Object} + * @see {@link https://discord.com/developers/docs/resources/user#user-object-user-flags} + */ +UserFlags.FLAGS = { + DISCORD_EMPLOYEE: 1 << 0, + PARTNERED_SERVER_OWNER: 1 << 1, + DISCORD_PARTNER: 1 << 1, + HYPESQUAD_EVENTS: 1 << 2, + BUGHUNTER_LEVEL_1: 1 << 3, + HOUSE_BRAVERY: 1 << 6, + HOUSE_BRILLIANCE: 1 << 7, + HOUSE_BALANCE: 1 << 8, + EARLY_SUPPORTER: 1 << 9, + TEAM_USER: 1 << 10, + SYSTEM: 1 << 12, + BUGHUNTER_LEVEL_2: 1 << 14, + VERIFIED_BOT: 1 << 16, + EARLY_VERIFIED_DEVELOPER: 1 << 17, + VERIFIED_DEVELOPER: 1 << 17, +}; + +module.exports = UserFlags; 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..d1909fd --- /dev/null +++ b/node_modules/discord.js/src/util/Util.js @@ -0,0 +1,625 @@ +'use strict'; + +const { parse } = require('path'); +const fetch = require('node-fetch'); +const { Colors, DefaultOptions, Endpoints } = require('./Constants'); +const { Error: DiscordError, RangeError, TypeError } = require('../errors'); +const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); +const isObject = d => typeof d === 'object' && d !== null; + +/** + * 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.`); + } + + /** + * Flatten an object. Any properties that are collections will get converted to an array of keys. + * @param {Object} obj The object to flatten. + * @param {...Object} [props] Specific properties to include/exclude. + * @returns {Object} + */ + static flatten(obj, ...props) { + if (!isObject(obj)) return obj; + + const objProps = Object.keys(obj) + .filter(k => !k.startsWith('_')) + .map(k => ({ [k]: true })); + + props = objProps.length ? Object.assign(...objProps, ...props) : Object.assign({}, ...props); + + const out = {}; + + for (let [prop, newProp] of Object.entries(props)) { + if (!newProp) continue; + newProp = newProp === true ? prop : newProp; + + const element = obj[prop]; + const elemIsObj = isObject(element); + const valueOf = elemIsObj && typeof element.valueOf === 'function' ? element.valueOf() : null; + + // If it's a Collection, make the array of keys + if (element instanceof require('./Collection')) out[newProp] = Array.from(element.keys()); + // If the valueOf is a Collection, use its array of keys + else if (valueOf instanceof require('./Collection')) out[newProp] = Array.from(valueOf.keys()); + // If it's an array, flatten each element + else if (Array.isArray(element)) out[newProp] = element.map(e => Util.flatten(e)); + // If it's an object with a primitive `valueOf`, use that value + else if (typeof valueOf !== 'object') out[newProp] = valueOf; + // If it's a primitive + else if (!elemIsObj) out[newProp] = element; + } + + return out; + } + + /** + * Splits a string into multiple chunks at a designated character that do not exceed a specific length. + * @param {StringResolvable} text Content to split + * @param {SplitOptions} [options] Options controlling the behavior of the split + * @returns {string[]} + */ + static splitMessage(text, { maxLength = 2000, char = '\n', prepend = '', append = '' } = {}) { + text = Util.resolveString(text); + if (text.length <= maxLength) return [text]; + const splitText = text.split(char); + if (splitText.some(chunk => chunk.length > maxLength)) throw new RangeError('SPLIT_MAX_LEN'); + const messages = []; + let msg = ''; + for (const chunk of splitText) { + if (msg && (msg + char + chunk + append).length > maxLength) { + messages.push(msg + append); + msg = prepend; + } + msg += (msg && msg !== prepend ? char : '') + chunk; + } + return messages.concat(msg).filter(m => m); + } + + /** + * Escapes any Discord-flavour markdown in a string. + * @param {string} text Content to escape + * @param {Object} [options={}] What types of markdown to escape + * @param {boolean} [options.codeBlock=true] Whether to escape code blocks or not + * @param {boolean} [options.inlineCode=true] Whether to escape inline code or not + * @param {boolean} [options.bold=true] Whether to escape bolds or not + * @param {boolean} [options.italic=true] Whether to escape italics or not + * @param {boolean} [options.underline=true] Whether to escape underlines or not + * @param {boolean} [options.strikethrough=true] Whether to escape strikethroughs or not + * @param {boolean} [options.spoiler=true] Whether to escape spoilers or not + * @param {boolean} [options.codeBlockContent=true] Whether to escape text inside code blocks or not + * @param {boolean} [options.inlineCodeContent=true] Whether to escape text inside inline code or not + * @returns {string} + */ + static escapeMarkdown( + text, + { + codeBlock = true, + inlineCode = true, + bold = true, + italic = true, + underline = true, + strikethrough = true, + spoiler = true, + codeBlockContent = true, + inlineCodeContent = true, + } = {}, + ) { + if (!codeBlockContent) { + return text + .split('```') + .map((subString, index, array) => { + if (index % 2 && index !== array.length - 1) return subString; + return Util.escapeMarkdown(subString, { + inlineCode, + bold, + italic, + underline, + strikethrough, + spoiler, + inlineCodeContent, + }); + }) + .join(codeBlock ? '\\`\\`\\`' : '```'); + } + if (!inlineCodeContent) { + return text + .split(/(?<=^|[^`])`(?=[^`]|$)/g) + .map((subString, index, array) => { + if (index % 2 && index !== array.length - 1) return subString; + return Util.escapeMarkdown(subString, { + codeBlock, + bold, + italic, + underline, + strikethrough, + spoiler, + }); + }) + .join(inlineCode ? '\\`' : '`'); + } + if (inlineCode) text = Util.escapeInlineCode(text); + if (codeBlock) text = Util.escapeCodeBlock(text); + if (italic) text = Util.escapeItalic(text); + if (bold) text = Util.escapeBold(text); + if (underline) text = Util.escapeUnderline(text); + if (strikethrough) text = Util.escapeStrikethrough(text); + if (spoiler) text = Util.escapeSpoiler(text); + return text; + } + + /** + * Escapes code block markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeCodeBlock(text) { + return text.replace(/```/g, '\\`\\`\\`'); + } + + /** + * Escapes inline code markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeInlineCode(text) { + return text.replace(/(?<=^|[^`])`(?=[^`]|$)/g, '\\`'); + } + + /** + * Escapes italic markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeItalic(text) { + let i = 0; + text = text.replace(/(?<=^|[^*])\*([^*]|\*\*|$)/g, (_, match) => { + if (match === '**') return ++i % 2 ? `\\*${match}` : `${match}\\*`; + return `\\*${match}`; + }); + i = 0; + return text.replace(/(?<=^|[^_])_([^_]|__|$)/g, (_, match) => { + if (match === '__') return ++i % 2 ? `\\_${match}` : `${match}\\_`; + return `\\_${match}`; + }); + } + + /** + * Escapes bold markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeBold(text) { + let i = 0; + return text.replace(/\*\*(\*)?/g, (_, match) => { + if (match) return ++i % 2 ? `${match}\\*\\*` : `\\*\\*${match}`; + return '\\*\\*'; + }); + } + + /** + * Escapes underline markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeUnderline(text) { + let i = 0; + return text.replace(/__(_)?/g, (_, match) => { + if (match) return ++i % 2 ? `${match}\\_\\_` : `\\_\\_${match}`; + return '\\_\\_'; + }); + } + + /** + * Escapes strikethrough markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeStrikethrough(text) { + return text.replace(/~~/g, '\\~\\~'); + } + + /** + * Escapes spoiler markdown in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeSpoiler(text) { + return text.replace(/\|\|/g, '\\|\\|'); + } + + /** + * Gets the recommended shard count from Discord. + * @param {string} token Discord auth token + * @param {number} [guildsPerShard=1000] Number of guilds per shard + * @returns {Promise} The recommended number of shards + */ + static fetchRecommendedShards(token, guildsPerShard = 1000) { + if (!token) throw new DiscordError('TOKEN_MISSING'); + return fetch(`${DefaultOptions.http.api}/v${DefaultOptions.http.version}${Endpoints.botGateway}`, { + method: 'GET', + headers: { Authorization: `Bot ${token.replace(/^Bot\s*/i, '')}` }, + }) + .then(res => { + if (res.ok) return res.json(); + if (res.status === 401) throw new DiscordError('TOKEN_INVALID'); + throw res; + }) + .then(data => data.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 ``) + * @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(/?/); + if (!m) return null; + return { animated: Boolean(m[1]), name: m[2], id: m[3] || null }; + } + + /** + * 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 (!has(given, key) || given[key] === undefined) { + given[key] = def[key]; + } else if (given[key] === Object(given[key])) { + given[key] = Util.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 = Util.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) { + return { + name: err.name, + message: err.message, + stack: err.stack, + }; + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * Can be a number, hex string, an RGB array like: + * ```js + * [255, 0, 255] // purple + * ``` + * or one of the following strings: + * - `DEFAULT` + * - `WHITE` + * - `AQUA` + * - `GREEN` + * - `BLUE` + * - `YELLOW` + * - `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` + * - `BLURPLE` + * - `GREYPLE` + * - `DARK_BUT_NOT_BLACK` + * - `NOT_QUITE_BLACK` + * - `RANDOM` + * @typedef {string|number|number[]} 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 = Colors[color] || parseInt(color.replace('#', ''), 16); + } else if (Array.isArray(color)) { + color = (color[0] << 16) + (color[1] << 8) + color[2]; + } + + if (color < 0 || color > 0xffffff) throw new RangeError('COLOR_RANGE'); + else if (color && isNaN(color)) throw new TypeError('COLOR_CONVERT'); + + return color; + } + + /** + * Sorts by Discord's position and ID. + * @param {Collection} collection Collection of objects to sort + * @returns {Collection} + */ + static discordSort(collection) { + return collection.sorted( + (a, b) => + a.rawPosition - b.rawPosition || + parseInt(b.id.slice(0, -10)) - parseInt(a.id.slice(0, -10)) || + parseInt(b.id.slice(10)) - parseInt(a.id.slice(10)), + ); + } + + /** + * Sets the position of a Channel or Role. + * @param {Channel|Role} item Object to set the position of + * @param {number} position New position for the object + * @param {boolean} relative Whether `position` is relative to its current position + * @param {Collection} sorted A collection of the objects sorted properly + * @param {APIRouter} route Route to call PATCH on + * @param {string} [reason] Reason for the change + * @returns {Promise} Updated item list, with `id` and `position` properties + * @private + */ + static setPosition(item, position, relative, sorted, route, reason) { + let updatedItems = sorted.array(); + Util.moveElementInArray(updatedItems, item, position, relative); + updatedItems = updatedItems.map((r, i) => ({ id: r.id, position: i })); + return route.patch({ data: updatedItems, reason }).then(() => updatedItems); + } + + /** + * Alternative to Node's `path.basename`, removing query string after the extension if it exists. + * @param {string} path Path to get the basename of + * @param {string} [ext] File extension to remove + * @returns {string} Basename of the path + * @private + */ + static basename(path, ext) { + let res = parse(path); + return ext && res.ext.startsWith(ext) ? res.name : res.base.split('?')[0]; + } + + /** + * Transforms a snowflake from a decimal string to a bit string. + * @param {Snowflake} num Snowflake to be transformed + * @returns {string} + * @private + */ + static idToBinary(num) { + let bin = ''; + let high = parseInt(num.slice(0, -10)) || 0; + let low = parseInt(num.slice(-10)); + while (low > 0 || high > 0) { + bin = String(low & 1) + bin; + low = Math.floor(low / 2); + if (high > 0) { + low += 5000000000 * (high % 2); + high = Math.floor(high / 2); + } + } + return bin; + } + + /** + * Transforms a snowflake from a bit string to a decimal string. + * @param {string} num Bit string to be transformed + * @returns {Snowflake} + * @private + */ + static binaryToID(num) { + let dec = ''; + + while (num.length > 50) { + const high = parseInt(num.slice(0, -32), 2); + const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); + + dec = (low % 10).toString() + dec; + num = + Math.floor(high / 10).toString(2) + + Math.floor(low / 10) + .toString(2) + .padStart(32, '0'); + } + + num = parseInt(num, 2); + while (num > 0) { + dec = (num % 10).toString() + dec; + num = Math.floor(num / 10); + } + + return dec; + } + + /** + * Breaks user, role and everyone/here mentions by adding a zero width space after every @ character + * @param {string} str The string to sanitize + * @returns {string} + */ + static removeMentions(str) { + return str.replace(/@/g, '@\u200b'); + } + + /** + * The content to have all mentions replaced by the equivalent text. + * @param {string} str The string to be converted + * @param {Message} message The message object to reference + * @returns {string} + */ + static cleanContent(str, message) { + str = str + .replace(/<@!?[0-9]+>/g, input => { + const id = input.replace(/<|!|>|@/g, ''); + if (message.channel.type === 'dm') { + const user = message.client.users.cache.get(id); + return user ? Util.removeMentions(`@${user.username}`) : input; + } + + const member = message.channel.guild.members.cache.get(id); + if (member) { + return Util.removeMentions(`@${member.displayName}`); + } else { + const user = message.client.users.cache.get(id); + return user ? Util.removeMentions(`@${user.username}`) : input; + } + }) + .replace(/<#[0-9]+>/g, input => { + const channel = message.client.channels.cache.get(input.replace(/<|#|>/g, '')); + return channel ? `#${channel.name}` : input; + }) + .replace(/<@&[0-9]+>/g, input => { + if (message.channel.type === 'dm') return input; + const role = message.guild.roles.cache.get(input.replace(/<|@|>|&/g, '')); + return role ? `@${role.name}` : input; + }); + if (message.client.options.disableMentions === 'everyone') { + str = str.replace(/@([^<>@ ]*)/gmsu, (match, target) => { + if (target.match(/^[&!]?\d+$/)) { + return `@${target}`; + } else { + return `@\u200b${target}`; + } + }); + } + if (message.client.options.disableMentions === 'all') { + return Util.removeMentions(str); + } else { + return str; + } + } + + /** + * The content to put in a codeblock with all codeblock fences replaced by the equivalent backticks. + * @param {string} text The string to be converted + * @returns {string} + */ + static cleanCodeBlockContent(text) { + return text.replace(/```/g, '`\u200b``'); + } + + /** + * Creates a Promise that resolves after a specified duration. + * @param {number} ms How long to wait before resolving (in milliseconds) + * @returns {Promise} + * @private + */ + static delayFor(ms) { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); + } +} + +module.exports = Util; 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..041039f --- /dev/null +++ b/node_modules/discord.js/typings/index.d.ts @@ -0,0 +1,3245 @@ +declare enum ChannelType { + text = 0, + dm = 1, + voice = 2, + group = 3, + category = 4, + news = 5, + store = 6, + unknown = 7, +} + +declare module 'discord.js' { + import BaseCollection from '@discordjs/collection'; + import { ChildProcess } from 'child_process'; + import { EventEmitter } from 'events'; + import { PathLike } from 'fs'; + import { Readable, Stream, Writable } from 'stream'; + import * as WebSocket from 'ws'; + + export const version: string; + + //#region Classes + + export class Activity { + constructor(presence: Presence, data?: object); + public applicationID: Snowflake | null; + public assets: RichPresenceAssets | null; + public readonly createdAt: Date; + public createdTimestamp: number; + public details: string | null; + public emoji: Emoji | null; + public flags: Readonly; + public name: string; + public party: { + id: string | null; + size: [number, number]; + } | null; + public state: string | null; + public timestamps: { + start: Date | null; + end: Date | null; + } | null; + public type: ActivityType; + public url: string | null; + public equals(activity: Activity): boolean; + } + + export class ActivityFlags extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class APIMessage { + constructor(target: MessageTarget, options: MessageOptions | WebhookMessageOptions); + public data: object | null; + public readonly isUser: boolean; + public readonly isWebhook: boolean; + public files: object[] | null; + public options: MessageOptions | WebhookMessageOptions; + public target: MessageTarget; + + public static create( + target: MessageTarget, + content: APIMessageContentResolvable, + options?: undefined, + extra?: MessageOptions | WebhookMessageOptions, + ): APIMessage; + public static create( + target: MessageTarget, + content: StringResolvable, + options: MessageOptions | WebhookMessageOptions | MessageAdditions, + extra?: MessageOptions | WebhookMessageOptions, + ): APIMessage; + public static partitionMessageAdditions( + items: readonly (MessageEmbed | MessageAttachment)[], + ): [MessageEmbed[], MessageAttachment[]]; + public static resolveFile(fileLike: BufferResolvable | Stream | FileOptions | MessageAttachment): Promise; + public static transformOptions( + content: APIMessageContentResolvable, + options?: undefined, + extra?: MessageOptions | WebhookMessageOptions, + isWebhook?: boolean, + ): MessageOptions | WebhookMessageOptions; + public static transformOptions( + content: StringResolvable, + options: MessageOptions | WebhookMessageOptions | MessageAdditions, + extra?: MessageOptions | WebhookMessageOptions, + isWebhook?: boolean, + ): MessageOptions | WebhookMessageOptions; + + public makeContent(): string | string[] | undefined; + public resolve(): Promise; + public resolveData(): this; + public resolveFiles(): Promise; + public split(): APIMessage[]; + } + + export abstract class Application { + constructor(client: Client, data: object); + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public description: string; + public icon: string; + public id: Snowflake; + public name: string; + public coverImage(options?: ImageURLOptions): string; + public fetchAssets(): Promise; + public iconURL(options?: ImageURLOptions): string; + public toJSON(): object; + public toString(): string; + } + + export class Base { + constructor(client: Client); + public readonly client: Client; + public toJSON(...props: { [key: string]: boolean | string }[]): object; + public valueOf(): string; + } + + export class BaseClient extends EventEmitter { + constructor(options?: ClientOptions); + private _timeouts: Set; + private _intervals: Set; + private _immediates: Set; + private readonly api: object; + private rest: object; + private decrementMaxListeners(): void; + private incrementMaxListeners(): void; + + public options: ClientOptions; + public clearInterval(interval: NodeJS.Timeout): void; + public clearTimeout(timeout: NodeJS.Timeout): void; + public clearImmediate(timeout: NodeJS.Immediate): void; + public destroy(): void; + public setInterval(fn: (...args: any[]) => void, delay: number, ...args: any[]): NodeJS.Timeout; + public setTimeout(fn: (...args: any[]) => void, delay: number, ...args: any[]): NodeJS.Timeout; + public setImmediate(fn: (...args: any[]) => void, ...args: any[]): NodeJS.Immediate; + public toJSON(...props: { [key: string]: boolean | string }[]): object; + } + + export class BaseGuildEmoji extends Emoji { + constructor(client: Client, data: object, guild: Guild); + private _roles: string[]; + + public available: boolean | null; + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public guild: Guild | GuildPreview; + public id: Snowflake; + public managed: boolean | null; + public requiresColons: boolean | null; + } + + class BroadcastDispatcher extends VolumeMixin(StreamDispatcher) { + public broadcast: VoiceBroadcast; + } + + export class BitField { + constructor(bits?: BitFieldResolvable); + public bitfield: number; + public add(...bits: BitFieldResolvable[]): BitField; + public any(bit: BitFieldResolvable): boolean; + public equals(bit: BitFieldResolvable): boolean; + public freeze(): Readonly>; + public has(bit: BitFieldResolvable): boolean; + public missing(bits: BitFieldResolvable, ...hasParam: readonly unknown[]): S[]; + public remove(...bits: BitFieldResolvable[]): BitField; + public serialize(...hasParam: readonly unknown[]): Record; + public toArray(...hasParam: readonly unknown[]): S[]; + public toJSON(): number; + public valueOf(): number; + public [Symbol.iterator](): IterableIterator; + public static FLAGS: object; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class CategoryChannel extends GuildChannel { + public readonly children: Collection; + public type: 'category'; + } + + export class Channel extends Base { + constructor(client: Client, data?: object); + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public deleted: boolean; + public id: Snowflake; + public type: keyof typeof ChannelType; + public delete(reason?: string): Promise; + public fetch(force?: boolean): Promise; + public isText(): this is TextChannel | DMChannel | NewsChannel; + public toString(): string; + } + + export class Client extends BaseClient { + constructor(options?: ClientOptions); + private actions: object; + private _eval(script: string): any; + private _validateOptions(options?: ClientOptions): void; + + public channels: ChannelManager; + public readonly emojis: GuildEmojiManager; + public guilds: GuildManager; + public readyAt: Date | null; + public readonly readyTimestamp: number | null; + public shard: ShardClientUtil | null; + public token: string | null; + public readonly uptime: number | null; + public user: ClientUser | null; + public users: UserManager; + public voice: ClientVoiceManager | null; + public ws: WebSocketManager; + public destroy(): void; + public fetchApplication(): Promise; + public fetchGuildPreview(guild: GuildResolvable): Promise; + public fetchInvite(invite: InviteResolvable): Promise; + public fetchVoiceRegions(): Promise>; + public fetchWebhook(id: Snowflake, token?: string): Promise; + public generateInvite(options?: InviteGenerationOptions | PermissionResolvable): Promise; + public login(token?: string): Promise; + public sweepMessages(lifetime?: number): number; + public toJSON(): object; + + public on(event: K, listener: (...args: ClientEvents[K]) => void): this; + public on( + event: Exclude, + listener: (...args: any[]) => void, + ): this; + + public once(event: K, listener: (...args: ClientEvents[K]) => void): this; + public once( + event: Exclude, + listener: (...args: any[]) => void, + ): this; + + public emit(event: K, ...args: ClientEvents[K]): boolean; + public emit(event: Exclude, ...args: any[]): boolean; + + public off(event: K, listener: (...args: ClientEvents[K]) => void): this; + public off( + event: Exclude, + listener: (...args: any[]) => void, + ): this; + + public removeAllListeners(event?: K): this; + public removeAllListeners(event?: Exclude): this; + } + + export class ClientApplication extends Application { + public botPublic: boolean | null; + public botRequireCodeGrant: boolean | null; + public cover: string | null; + public owner: User | Team | null; + public rpcOrigins: string[]; + } + + export class ClientUser extends User { + public mfaEnabled: boolean; + public verified: boolean; + public setActivity(options?: ActivityOptions): Promise; + public setActivity(name: string, options?: ActivityOptions): Promise; + public setAFK(afk: boolean): Promise; + public setAvatar(avatar: BufferResolvable | Base64Resolvable): Promise; + public setPresence(data: PresenceData): Promise; + public setStatus(status: PresenceStatusData, shardID?: number | number[]): Promise; + public setUsername(username: string): Promise; + } + + export class ClientVoiceManager { + constructor(client: Client); + public readonly client: Client; + public connections: Collection; + public broadcasts: VoiceBroadcast[]; + + private joinChannel(channel: VoiceChannel): Promise; + + public createBroadcast(): VoiceBroadcast; + } + + export abstract class Collector extends EventEmitter { + constructor(client: Client, filter: CollectorFilter, options?: CollectorOptions); + private _timeout: NodeJS.Timeout | null; + private _idletimeout: NodeJS.Timeout | null; + + public readonly client: Client; + public collected: Collection; + public ended: boolean; + public filter: CollectorFilter; + public readonly next: Promise; + public options: CollectorOptions; + public checkEnd(): void; + public handleCollect(...args: any[]): void; + public handleDispose(...args: any[]): void; + public stop(reason?: string): void; + public resetTimer(options?: { time?: number; idle?: number }): void; + public [Symbol.asyncIterator](): AsyncIterableIterator; + public toJSON(): object; + + protected listener: (...args: any[]) => void; + public abstract collect(...args: any[]): K; + public abstract dispose(...args: any[]): K; + public abstract endReason(): void; + + public on(event: 'collect' | 'dispose', listener: (...args: any[]) => void): this; + public on(event: 'end', listener: (collected: Collection, reason: string) => void): this; + + public once(event: 'collect' | 'dispose', listener: (...args: any[]) => void): this; + public once(event: 'end', listener: (collected: Collection, reason: string) => void): this; + } + + type AllowedImageFormat = 'webp' | 'png' | 'jpg' | 'jpeg' | 'gif'; + + export const Constants: { + Package: { + name: string; + version: string; + description: string; + author: string; + license: string; + main: PathLike; + types: PathLike; + homepage: string; + keywords: string[]; + bugs: { url: string }; + repository: { type: string; url: string }; + browser: { [key: string]: boolean }; + scripts: { [key: string]: string }; + engines: { [key: string]: string }; + dependencies: { [key: string]: string }; + peerDependencies: { [key: string]: string }; + devDependencies: { [key: string]: string }; + [key: string]: any; + }; + browser: boolean; + DefaultOptions: ClientOptions; + UserAgent: string | null; + Endpoints: { + botGateway: string; + invite: (root: string, code: string) => string; + CDN: ( + root: string, + ) => { + Asset: (name: string) => string; + DefaultAvatar: (id: string | number) => string; + Emoji: (emojiID: string, format: 'png' | 'gif') => string; + Avatar: (userID: string | number, hash: string, format: 'default' | AllowedImageFormat, size: number) => string; + Banner: (guildID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + Icon: (userID: string | number, hash: string, format: 'default' | AllowedImageFormat, size: number) => string; + AppIcon: (userID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + AppAsset: (userID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + GDMIcon: (userID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + Splash: (guildID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + DiscoverySplash: (guildID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + TeamIcon: (teamID: string | number, hash: string, format: AllowedImageFormat, size: number) => string; + }; + }; + WSCodes: { + 1000: 'WS_CLOSE_REQUESTED'; + 4004: 'TOKEN_INVALID'; + 4010: 'SHARDING_INVALID'; + 4011: 'SHARDING_REQUIRED'; + }; + Events: { + RATE_LIMIT: 'rateLimit'; + CLIENT_READY: 'ready'; + RESUMED: 'resumed'; + GUILD_CREATE: 'guildCreate'; + GUILD_DELETE: 'guildDelete'; + GUILD_UPDATE: 'guildUpdate'; + INVITE_CREATE: 'inviteCreate'; + INVITE_DELETE: 'inviteDelete'; + GUILD_UNAVAILABLE: 'guildUnavailable'; + 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'; + 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_ALL: 'messageReactionRemoveAll'; + USER_UPDATE: 'userUpdate'; + PRESENCE_UPDATE: 'presenceUpdate'; + VOICE_STATE_UPDATE: 'voiceStateUpdate'; + VOICE_BROADCAST_SUBSCRIBE: 'subscribe'; + VOICE_BROADCAST_UNSUBSCRIBE: 'unsubscribe'; + TYPING_START: 'typingStart'; + WEBHOOKS_UPDATE: 'webhookUpdate'; + DISCONNECT: 'disconnect'; + RECONNECTING: 'reconnecting'; + ERROR: 'error'; + WARN: 'warn'; + DEBUG: 'debug'; + SHARD_DISCONNECT: 'shardDisconnect'; + SHARD_ERROR: 'shardError'; + SHARD_RECONNECTING: 'shardReconnecting'; + SHARD_READY: 'shardReady'; + SHARD_RESUME: 'shardResume'; + INVALIDATED: 'invalidated'; + RAW: 'raw'; + }; + ShardEvents: { + CLOSE: 'close'; + DESTROYED: 'destroyed'; + INVALID_SESSION: 'invalidSession'; + READY: 'ready'; + RESUMED: 'resumed'; + }; + PartialTypes: { + [K in PartialTypes]: K; + }; + WSEvents: { + [K in WSEventType]: K; + }; + Colors: { + DEFAULT: 0x000000; + WHITE: 0xffffff; + AQUA: 0x1abc9c; + GREEN: 0x2ecc71; + BLUE: 0x3498db; + YELLOW: 0xffff00; + 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; + }; + Status: { + READY: 0; + CONNECTING: 1; + RECONNECTING: 2; + IDLE: 3; + NEARLY: 4; + DISCONNECTED: 5; + }; + 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; + }; + 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; + 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; + }; + VoiceStatus: { + CONNECTED: 0; + CONNECTING: 1; + AUTHENTICATING: 2; + RECONNECTING: 3; + DISCONNECTED: 4; + }; + VoiceOPCodes: { + IDENTIFY: 0; + SELECT_PROTOCOL: 1; + READY: 2; + HEARTBEAT: 3; + SESSION_DESCRIPTION: 4; + SPEAKING: 5; + HELLO: 8; + CLIENT_CONNECT: 12; + CLIENT_DISCONNECT: 13; + }; + ChannelTypes: { + TEXT: 0; + DM: 1; + VOICE: 2; + GROUP: 3; + CATEGORY: 4; + NEWS: 5; + STORE: 6; + }; + ClientApplicationAssetTypes: { + SMALL: 1; + BIG: 2; + }; + MessageTypes: MessageType[]; + ActivityTypes: ActivityType[]; + ExplicitContentFilterLevels: ExplicitContentFilterLevel[]; + DefaultMessageNotifications: DefaultMessageNotifications[]; + VerificationLevels: VerificationLevel[]; + MembershipStates: 'INVITED' | 'ACCEPTED'; + }; + + export class DataResolver { + public static resolveBase64(data: Base64Resolvable): string; + public static resolveFile(resource: BufferResolvable | Stream): Promise; + public static resolveFileAsBuffer(resource: BufferResolvable | Stream): Promise; + public static resolveImage(resource: BufferResolvable | Base64Resolvable): Promise; + public static resolveInviteCode(data: InviteResolvable): string; + } + + export class DiscordAPIError extends Error { + constructor(path: string, error: object, method: string, httpStatus: number); + private static flattenErrors(obj: object, key: string): string[]; + + public code: number; + public method: string; + public path: string; + public httpStatus: number; + } + + export class DMChannel extends TextBasedChannel(Channel, ['bulkDelete']) { + constructor(client: Client, data?: object); + public messages: MessageManager; + public recipient: User; + public readonly partial: false; + public type: 'dm'; + public fetch(force?: boolean): Promise; + } + + export class Emoji extends Base { + constructor(client: Client, emoji: object); + public animated: boolean; + public readonly createdAt: Date | null; + public readonly createdTimestamp: number | null; + public deleted: boolean; + public id: Snowflake | null; + public name: string; + public readonly identifier: string; + public readonly url: string | null; + public toJSON(): object; + public toString(): string; + } + + export class Guild extends Base { + constructor(client: Client, data: object); + private _sortedRoles(): Collection; + private _sortedChannels(channel: Channel): Collection; + private _memberSpeakUpdate(user: Snowflake, speaking: boolean): void; + + public readonly afkChannel: VoiceChannel | null; + public afkChannelID: Snowflake | null; + public afkTimeout: number; + public applicationID: Snowflake | null; + public approximateMemberCount: number | null; + public approximatePresenceCount: number | null; + public available: boolean; + public banner: string | null; + public channels: GuildChannelManager; + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public defaultMessageNotifications: DefaultMessageNotifications | number; + public deleted: boolean; + public description: string | null; + public discoverySplash: string | null; + public embedChannel: GuildChannel | null; + public embedChannelID: Snowflake | null; + public embedEnabled: boolean; + public emojis: GuildEmojiManager; + public explicitContentFilter: ExplicitContentFilterLevel; + public features: GuildFeatures[]; + public icon: string | null; + public id: Snowflake; + public readonly joinedAt: Date; + public joinedTimestamp: number; + public large: boolean; + public maximumMembers: number | null; + public maximumPresences: number | null; + public readonly me: GuildMember | null; + public memberCount: number; + public members: GuildMemberManager; + public mfaLevel: number; + public name: string; + public readonly nameAcronym: string; + public readonly owner: GuildMember | null; + public ownerID: Snowflake; + public readonly partnered: boolean; + public preferredLocale: string; + public premiumSubscriptionCount: number | null; + public premiumTier: PremiumTier; + public presences: PresenceManager; + public readonly publicUpdatesChannel: TextChannel | null; + public publicUpdatesChannelID: Snowflake | null; + public region: string; + public roles: RoleManager; + public readonly rulesChannel: TextChannel | null; + public rulesChannelID: Snowflake | null; + public readonly shard: WebSocketShard; + public shardID: number; + public splash: string | null; + public readonly systemChannel: TextChannel | null; + public systemChannelFlags: Readonly; + public systemChannelID: Snowflake | null; + public vanityURLCode: string | null; + public vanityURLUses: number | null; + public verificationLevel: VerificationLevel; + public readonly verified: boolean; + public readonly voice: VoiceState | null; + public readonly voiceStates: VoiceStateManager; + public readonly widgetChannel: TextChannel | null; + public widgetChannelID: Snowflake | null; + public widgetEnabled: boolean | null; + public addMember(user: UserResolvable, options: AddGuildMemberOptions): Promise; + public bannerURL(options?: ImageURLOptions): string | null; + public createIntegration(data: IntegrationData, reason?: string): Promise; + public delete(): Promise; + public discoverySplashURL(options?: ImageURLOptions): string | null; + public edit(data: GuildEditData, reason?: string): Promise; + public equals(guild: Guild): boolean; + public fetch(): Promise; + public fetchAuditLogs(options?: GuildAuditLogsFetchOptions): Promise; + public fetchBan(user: UserResolvable): Promise<{ user: User; reason: string }>; + public fetchBans(): Promise>; + public fetchEmbed(): Promise; + public fetchIntegrations(options?: FetchIntegrationsOptions): Promise>; + public fetchInvites(): Promise>; + public fetchPreview(): Promise; + public fetchVanityCode(): Promise; + public fetchVanityData(): Promise<{ code: string; uses: number }>; + public fetchVoiceRegions(): Promise>; + public fetchWebhooks(): Promise>; + public fetchWidget(): Promise; + public iconURL(options?: ImageURLOptions & { dynamic?: boolean }): string | null; + public leave(): Promise; + public member(user: UserResolvable): GuildMember | null; + public setAFKChannel(afkChannel: ChannelResolvable | null, reason?: string): Promise; + public setAFKTimeout(afkTimeout: number, reason?: string): Promise; + public setBanner(banner: Base64Resolvable | null, reason?: string): Promise; + public setChannelPositions(channelPositions: readonly ChannelPosition[]): Promise; + public setDefaultMessageNotifications( + defaultMessageNotifications: DefaultMessageNotifications | number, + reason?: string, + ): Promise; + public setDiscoverySplash(discoverySplash: Base64Resolvable | null, reason?: string): Promise; + public setEmbed(embed: GuildWidgetData, reason?: string): Promise; + public setExplicitContentFilter( + explicitContentFilter: ExplicitContentFilterLevel | number, + reason?: string, + ): Promise; + public setIcon(icon: Base64Resolvable | null, reason?: string): Promise; + public setName(name: string, reason?: string): Promise; + public setOwner(owner: GuildMemberResolvable, reason?: string): Promise; + public setPreferredLocale(preferredLocale: string, reason?: string): Promise; + public setPublicUpdatesChannel(publicUpdatesChannel: ChannelResolvable | null, reason?: string): Promise; + public setRegion(region: string, reason?: string): Promise; + public setRolePositions(rolePositions: readonly RolePosition[]): Promise; + public setRulesChannel(rulesChannel: ChannelResolvable | null, reason?: string): Promise; + public setSplash(splash: Base64Resolvable | null, reason?: string): Promise; + public setSystemChannel(systemChannel: ChannelResolvable | null, reason?: string): Promise; + public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise; + public setVerificationLevel(verificationLevel: VerificationLevel | number, reason?: string): Promise; + public setWidget(widget: GuildWidgetData, reason?: string): Promise; + public splashURL(options?: ImageURLOptions): string | null; + public toJSON(): object; + public toString(): string; + } + + export class GuildAuditLogs { + constructor(guild: Guild, data: object); + private webhooks: Collection; + private integrations: Collection; + + public entries: Collection; + + 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; + public static targetType(target: number): GuildAuditLogsTarget; + public toJSON(): object; + } + + class GuildAuditLogsEntry { + constructor(logs: GuildAuditLogs, guild: Guild, data: object); + public action: GuildAuditLogsAction; + public actionType: GuildAuditLogsActionType; + public changes: AuditLogChange[] | null; + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public executor: User; + public extra: object | Role | GuildMember | null; + public id: Snowflake; + public reason: string | null; + public target: + | Guild + | GuildChannel + | User + | Role + | GuildEmoji + | Invite + | Webhook + | Message + | Integration + | { id: Snowflake } + | null; + public targetType: GuildAuditLogsTarget; + public toJSON(): object; + } + + export class GuildChannel extends Channel { + constructor(guild: Guild, data?: object); + private memberPermissions(member: GuildMember): Readonly; + private rolePermissions(role: Role): Readonly; + + public readonly calculatedPosition: number; + public readonly deletable: boolean; + public guild: Guild; + public readonly manageable: boolean; + public readonly members: Collection; + public name: string; + public readonly parent: CategoryChannel | null; + public parentID: Snowflake | null; + public permissionOverwrites: Collection; + public readonly permissionsLocked: boolean | null; + public readonly position: number; + public rawPosition: number; + public type: Exclude; + public readonly viewable: boolean; + public clone(options?: GuildChannelCloneOptions): Promise; + public createInvite(options?: InviteOptions): Promise; + public createOverwrite( + userOrRole: RoleResolvable | UserResolvable, + options: PermissionOverwriteOption, + reason?: string, + ): Promise; + public edit(data: ChannelData, reason?: string): Promise; + public equals(channel: GuildChannel): boolean; + public fetchInvites(): Promise>; + public lockPermissions(): Promise; + public overwritePermissions( + overwrites: readonly OverwriteResolvable[] | Collection, + reason?: string, + ): Promise; + public permissionsFor(memberOrRole: GuildMemberResolvable | RoleResolvable): Readonly | null; + public setName(name: string, reason?: string): Promise; + public setParent( + channel: CategoryChannel | Snowflake | null, + options?: { lockPermissions?: boolean; reason?: string }, + ): Promise; + public setPosition(position: number, options?: { relative?: boolean; reason?: string }): Promise; + public setTopic(topic: string | null, reason?: string): Promise; + public updateOverwrite( + userOrRole: RoleResolvable | UserResolvable, + options: PermissionOverwriteOption, + reason?: string, + ): Promise; + public isText(): this is TextChannel | NewsChannel; + } + + export class GuildEmoji extends BaseGuildEmoji { + public readonly deletable: boolean; + public guild: Guild; + public author: User | null; + public readonly roles: GuildEmojiRoleManager; + public readonly url: string; + public delete(reason?: string): Promise; + public edit(data: GuildEmojiEditData, reason?: string): Promise; + public equals(other: GuildEmoji | object): boolean; + public fetchAuthor(): Promise; + public setName(name: string, reason?: string): Promise; + } + + export class GuildMember extends PartialTextBasedChannel(Base) { + constructor(client: Client, data: object, guild: Guild); + public readonly bannable: boolean; + public deleted: boolean; + public readonly displayColor: number; + public readonly displayHexColor: string; + public readonly displayName: string; + public guild: Guild; + public readonly id: Snowflake; + public readonly joinedAt: Date | null; + public joinedTimestamp: number | null; + public readonly kickable: boolean; + public lastMessageChannelID: Snowflake | null; + public readonly manageable: boolean; + public nickname: string | null; + public readonly partial: false; + public readonly permissions: Readonly; + public readonly premiumSince: Date | null; + public premiumSinceTimestamp: number | null; + public readonly presence: Presence; + public readonly roles: GuildMemberRoleManager; + public user: User; + public readonly voice: VoiceState; + public ban(options?: BanOptions): Promise; + public fetch(force?: boolean): Promise; + public createDM(force?: boolean): Promise; + public deleteDM(): Promise; + public edit(data: GuildMemberEditData, reason?: string): Promise; + public hasPermission( + permission: PermissionResolvable, + options?: { checkAdmin?: boolean; checkOwner?: boolean }, + ): boolean; + public kick(reason?: string): Promise; + public permissionsIn(channel: ChannelResolvable): Readonly; + public setNickname(nickname: string, reason?: string): Promise; + public toJSON(): object; + public toString(): string; + public valueOf(): string; + } + + export class GuildPreview extends Base { + constructor(client: Client, data: object); + public approximateMemberCount: number; + public approximatePresenceCount: number; + public description: string | null; + public discoverySplash: string | null; + public emojis: Collection; + public features: GuildFeatures[]; + public icon: string | null; + public id: string; + public name: string; + public splash: string | null; + public discoverySplashURL(options?: ImageURLOptions): string | null; + public iconURL(options?: ImageURLOptions & { dynamic?: boolean }): string | null; + public splashURL(options?: ImageURLOptions): string | null; + public fetch(): Promise; + public toJSON(): object; + public toString(): string; + } + + export class GuildPreviewEmoji extends BaseGuildEmoji { + constructor(client: Client, data: object, guild: GuildPreview); + public guild: GuildPreview; + public readonly roles: Set; + } + + export class HTTPError extends Error { + constructor(message: string, name: string, code: number, method: string, path: string); + public code: number; + public method: string; + public name: string; + public path: string; + } + + export class Integration extends Base { + constructor(client: Client, data: object, guild: Guild); + public account: IntegrationAccount; + public application: IntegrationApplication | null; + 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: string; + public user: User | null; + public delete(reason?: string): Promise; + public edit(data: IntegrationEditData, reason?: string): Promise; + public sync(): Promise; + } + + export class IntegrationApplication extends Application { + public bot: User | null; + } + + export class Intents extends BitField { + public static FLAGS: Record; + public static PRIVILEGED: number; + public static ALL: number; + public static NON_PRIVILEGED: number; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class Invite extends Base { + constructor(client: Client, data: object); + public channel: GuildChannel | PartialGroupDMChannel; + public code: string; + public readonly deletable: boolean; + public readonly createdAt: Date | null; + public createdTimestamp: number | null; + public readonly expiresAt: Date | null; + public readonly expiresTimestamp: number | null; + public guild: Guild | null; + public inviter: User | null; + public maxAge: number | null; + public maxUses: number | null; + public memberCount: number; + public presenceCount: number; + public targetUser: User | null; + public targetUserType: TargetUser | null; + public temporary: boolean | null; + public readonly url: string; + public uses: number | null; + public delete(reason?: string): Promise; + public toJSON(): object; + public toString(): string; + } + + export class Message extends Base { + constructor(client: Client, data: object, channel: TextChannel | DMChannel | NewsChannel); + private _edits: Message[]; + private patch(data: object): Message; + + public activity: MessageActivity | null; + public application: ClientApplication | null; + public attachments: Collection; + public author: User; + public channel: TextChannel | DMChannel | NewsChannel; + public readonly cleanContent: string; + 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 | null; + public editedTimestamp: number | null; + public readonly edits: Message[]; + public embeds: MessageEmbed[]; + public readonly guild: Guild | null; + public id: Snowflake; + public readonly member: GuildMember | null; + public mentions: MessageMentions; + public nonce: string | null; + public readonly partial: false; + public readonly pinnable: boolean; + public pinned: boolean; + public reactions: ReactionManager; + public system: boolean; + public tts: boolean; + public type: MessageType; + public readonly url: string; + public webhookID: Snowflake | null; + public flags: Readonly; + public reference: MessageReference | null; + public awaitReactions( + filter: CollectorFilter, + options?: AwaitReactionsOptions, + ): Promise>; + public createReactionCollector(filter: CollectorFilter, options?: ReactionCollectorOptions): ReactionCollector; + public delete(options?: { timeout?: number; reason?: string }): Promise; + public edit( + content: APIMessageContentResolvable | MessageEditOptions | MessageEmbed | APIMessage, + ): Promise; + public edit(content: StringResolvable, options: MessageEditOptions | MessageEmbed): Promise; + public equals(message: Message, rawData: object): boolean; + public fetchWebhook(): Promise; + public crosspost(): Promise; + public fetch(force?: boolean): Promise; + public pin(options?: { reason?: string }): Promise; + public react(emoji: EmojiIdentifierResolvable): Promise; + public reply( + content: APIMessageContentResolvable | (MessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + public reply(options: MessageOptions & { split: true | SplitOptions }): Promise; + public reply(options: MessageOptions | APIMessage): Promise; + public reply( + content: StringResolvable, + options: (MessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + public reply( + content: StringResolvable, + options: MessageOptions & { split: true | SplitOptions }, + ): Promise; + public reply(content: StringResolvable, options: MessageOptions): Promise; + public suppressEmbeds(suppress?: boolean): Promise; + public toJSON(): object; + public toString(): string; + public unpin(options?: { reason?: string }): Promise; + } + + export class MessageAttachment { + constructor(attachment: BufferResolvable | Stream, name?: string, data?: object); + + public attachment: BufferResolvable | Stream; + public height: number | null; + public id: Snowflake; + public name: string | null; + public proxyURL: string; + public size: number; + public readonly spoiler: boolean; + public url: string; + public width: number | null; + public setFile(attachment: BufferResolvable | Stream, name?: string): this; + public setName(name: string): this; + public toJSON(): object; + } + + export class MessageCollector extends Collector { + constructor(channel: TextChannel | DMChannel, filter: CollectorFilter, options?: MessageCollectorOptions); + private _handleChannelDeletion(channel: GuildChannel): void; + private _handleGuildDeletion(guild: Guild): void; + + public channel: Channel; + public options: MessageCollectorOptions; + public received: number; + + public collect(message: Message): Snowflake; + public dispose(message: Message): Snowflake; + public endReason(): string; + } + + export class MessageEmbed { + constructor(data?: MessageEmbed | MessageEmbedOptions); + public author: MessageEmbedAuthor | null; + public color: number | null; + public readonly createdAt: Date | null; + public description: string | null; + public fields: EmbedField[]; + public files: (MessageAttachment | string | FileOptions)[]; + public footer: MessageEmbedFooter | null; + public readonly hexColor: string | null; + public image: MessageEmbedImage | null; + public readonly length: number; + public provider: MessageEmbedProvider | null; + public thumbnail: MessageEmbedThumbnail | null; + public timestamp: number | null; + public title: string | null; + public type: string; + public url: string | null; + public readonly video: MessageEmbedVideo | null; + public addField(name: StringResolvable, value: StringResolvable, inline?: boolean): this; + public addFields(...fields: EmbedFieldData[] | EmbedFieldData[][]): this; + public attachFiles(file: (MessageAttachment | FileOptions | string)[]): this; + public setAuthor(name: StringResolvable, iconURL?: string, url?: string): this; + public setColor(color: ColorResolvable): this; + public setDescription(description: StringResolvable): this; + public setFooter(text: StringResolvable, iconURL?: 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 spliceFields(index: number, deleteCount: number, ...fields: EmbedFieldData[] | EmbedFieldData[][]): this; + public toJSON(): object; + + public static normalizeField( + name: StringResolvable, + value: StringResolvable, + inline?: boolean, + ): Required; + public static normalizeFields(...fields: EmbedFieldData[] | EmbedFieldData[][]): Required[]; + } + + export class MessageFlags extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class MessageMentions { + constructor( + message: Message, + users: object[] | Collection, + roles: Snowflake[] | Collection, + everyone: boolean, + ); + private _channels: Collection | null; + private readonly _content: string; + private _members: Collection | null; + + public readonly channels: Collection; + public readonly client: Client; + public everyone: boolean; + public readonly guild: Guild; + public has( + data: UserResolvable | RoleResolvable | GuildChannelResolvable, + options?: { + ignoreDirect?: boolean; + ignoreRoles?: boolean; + ignoreEveryone?: boolean; + }, + ): boolean; + public readonly members: Collection | null; + public roles: Collection; + public users: Collection; + public crosspostedChannels: Collection; + public toJSON(): object; + + public static CHANNELS_PATTERN: RegExp; + public static EVERYONE_PATTERN: RegExp; + public static ROLES_PATTERN: RegExp; + public static USERS_PATTERN: RegExp; + } + + export class MessageReaction { + constructor(client: Client, data: object, message: Message); + private _emoji: GuildEmoji | ReactionEmoji; + + public count: number | null; + public readonly emoji: GuildEmoji | ReactionEmoji; + public me: boolean; + public message: Message; + public readonly partial: boolean; + public users: ReactionUserManager; + public remove(): Promise; + public fetch(): Promise; + public toJSON(): object; + } + + export class NewsChannel extends TextBasedChannel(GuildChannel) { + constructor(guild: Guild, data?: object); + public messages: MessageManager; + public nsfw: boolean; + public topic: string | null; + public type: 'news'; + public createWebhook( + name: string, + options?: { avatar?: BufferResolvable | Base64Resolvable; reason?: string }, + ): Promise; + public setNSFW(nsfw: boolean, reason?: string): Promise; + public fetchWebhooks(): Promise>; + public addFollower(channel: GuildChannelResolvable, reason?: string): Promise; + } + + export class PartialGroupDMChannel extends Channel { + constructor(client: Client, data: object); + public name: string; + public icon: string | null; + public iconURL(options?: ImageURLOptions): string | null; + } + + export class PermissionOverwrites { + constructor(guildChannel: GuildChannel, data?: object); + public allow: Readonly; + public readonly channel: GuildChannel; + public deny: Readonly; + public id: Snowflake; + public type: OverwriteType; + public update(options: PermissionOverwriteOption, reason?: string): Promise; + public delete(reason?: string): Promise; + public toJSON(): object; + public static resolveOverwriteOptions( + options: ResolvedOverwriteOptions, + initialPermissions: { allow?: PermissionResolvable; deny?: PermissionResolvable }, + ): ResolvedOverwriteOptions; + public static resolve(overwrite: OverwriteResolvable, guild: Guild): RawOverwriteData; + } + + export class Permissions extends BitField { + public any(permission: PermissionResolvable, checkAdmin?: boolean): boolean; + public has(permission: PermissionResolvable, checkAdmin?: boolean): boolean; + public missing(bits: BitFieldResolvable, checkAdmin?: boolean): PermissionString[]; + public serialize(checkAdmin?: boolean): Record; + public toArray(checkAdmin?: boolean): PermissionString[]; + + public static ALL: number; + public static DEFAULT: number; + public static FLAGS: PermissionFlags; + public static resolve(permission?: PermissionResolvable): number; + } + + export class Presence { + constructor(client: Client, data?: object); + public activities: Activity[]; + public clientStatus: ClientPresenceStatusData | null; + public guild: Guild | null; + public readonly member: GuildMember | null; + public status: PresenceStatus; + public readonly user: User | null; + public userID: Snowflake; + public equals(presence: Presence): boolean; + } + + export class ReactionCollector extends Collector { + constructor(message: Message, filter: CollectorFilter, options?: ReactionCollectorOptions); + private _handleChannelDeletion(channel: GuildChannel): void; + private _handleGuildDeletion(guild: Guild): void; + private _handleMessageDeletion(message: Message): void; + + public message: Message; + public options: ReactionCollectorOptions; + public total: number; + public users: Collection; + + public static key(reaction: MessageReaction): Snowflake | string; + + public collect(reaction: MessageReaction): Snowflake | string; + public dispose(reaction: MessageReaction, user: User): Snowflake | string; + public empty(): void; + public endReason(): string | null; + + public on(event: 'collect' | 'dispose' | 'remove', listener: (reaction: MessageReaction, user: User) => void): this; + public on( + event: 'end', + listener: (collected: Collection, reason: string) => void, + ): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once( + event: 'collect' | 'dispose' | 'remove', + listener: (reaction: MessageReaction, user: User) => void, + ): this; + public once( + event: 'end', + listener: (collected: Collection, reason: string) => void, + ): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + export class ReactionEmoji extends Emoji { + constructor(reaction: MessageReaction, emoji: object); + public reaction: MessageReaction; + public toJSON(): object; + } + + export class RichPresenceAssets { + constructor(activity: Activity, assets: object); + public largeImage: Snowflake | null; + public largeText: string | null; + public smallImage: Snowflake | null; + public smallText: string | null; + public largeImageURL(options?: ImageURLOptions): string | null; + public smallImageURL(options?: ImageURLOptions): string | null; + } + + export class Role extends Base { + constructor(client: Client, data: object, guild: Guild); + 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; + public mentionable: boolean; + public name: string; + public permissions: Readonly; + public readonly position: number; + public rawPosition: number; + public comparePositionTo(role: Role): number; + public delete(reason?: string): Promise; + public edit(data: RoleData, reason?: string): Promise; + public equals(role: Role): boolean; + public permissionsIn(channel: ChannelResolvable): Readonly; + public setColor(color: ColorResolvable, reason?: string): Promise; + public setHoist(hoist: boolean, reason?: string): Promise; + public setMentionable(mentionable: boolean, reason?: string): Promise; + public setName(name: string, reason?: string): Promise; + public setPermissions(permissions: PermissionResolvable, reason?: string): Promise; + public setPosition(position: number, options?: { relative?: boolean; reason?: string }): Promise; + public toJSON(): object; + public toString(): string; + + public static comparePositions(role1: Role, role2: Role): number; + } + + export class Shard extends EventEmitter { + constructor(manager: ShardingManager, id: number); + private _evals: Map>; + private _exitListener: (...args: any[]) => void; + private _fetches: Map>; + private _handleExit(respawn?: boolean): void; + private _handleMessage(message: any): void; + + public args: string[]; + public execArgv: string[]; + public env: object; + public id: number; + public manager: ShardingManager; + public process: ChildProcess | null; + public ready: boolean; + public worker: any | null; + public eval(script: string): Promise; + public eval(fn: (client: Client) => T): Promise; + public fetchClientValue(prop: string): Promise; + public kill(): void; + public respawn(delay?: number, spawnTimeout?: number): Promise; + public send(message: any): Promise; + public spawn(spawnTimeout?: number): Promise; + + public on(event: 'spawn' | 'death', listener: (child: ChildProcess) => void): this; + public on(event: 'disconnect' | 'ready' | 'reconnecting', listener: () => void): this; + public on(event: 'error', listener: (error: Error) => void): this; + public on(event: 'message', listener: (message: any) => void): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once(event: 'spawn' | 'death', listener: (child: ChildProcess) => void): this; + public once(event: 'disconnect' | 'ready' | 'reconnecting', listener: () => void): this; + public once(event: 'error', listener: (error: Error) => void): this; + public once(event: 'message', listener: (message: any) => void): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + export class ShardClientUtil { + constructor(client: Client, mode: ShardingManagerMode); + private _handleMessage(message: any): void; + private _respond(type: string, message: any): void; + + public client: Client; + public readonly count: number; + public readonly ids: number[]; + public mode: ShardingManagerMode; + public parentPort: any | null; + public broadcastEval(script: string): Promise; + public broadcastEval(fn: (client: Client) => T): Promise; + public fetchClientValues(prop: string): Promise; + public respawnAll(shardDelay?: number, respawnDelay?: number, spawnTimeout?: number): Promise; + public send(message: any): Promise; + + public static singleton(client: Client, mode: ShardingManagerMode): ShardClientUtil; + } + + export class ShardingManager extends EventEmitter { + constructor( + file: string, + options?: { + totalShards?: number | 'auto'; + shardList?: number[] | 'auto'; + mode?: ShardingManagerMode; + respawn?: boolean; + shardArgs?: string[]; + token?: string; + execArgv?: string[]; + }, + ); + + public file: string; + public respawn: boolean; + public shardArgs: string[]; + public shards: Collection; + public token: string | null; + public totalShards: number | 'auto'; + public broadcast(message: any): Promise; + public broadcastEval(script: string): Promise; + public createShard(id: number): Shard; + public fetchClientValues(prop: string): Promise; + public respawnAll( + shardDelay?: number, + respawnDelay?: number, + spawnTimeout?: number, + ): Promise>; + public spawn(amount?: number | 'auto', delay?: number, spawnTimeout?: number): Promise>; + + public on(event: 'shardCreate', listener: (shard: Shard) => void): this; + + public once(event: 'shardCreate', listener: (shard: Shard) => void): this; + } + + export class SnowflakeUtil { + public static deconstruct(snowflake: Snowflake): DeconstructedSnowflake; + public static generate(timestamp?: number | Date): Snowflake; + } + + export class Speaking extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class StoreChannel extends GuildChannel { + constructor(guild: Guild, data?: object); + public nsfw: boolean; + public type: 'store'; + } + + class StreamDispatcher extends VolumeMixin(Writable) { + constructor(player: object, options?: StreamOptions, streams?: object); + public readonly bitrateEditable: boolean; + public broadcast: VoiceBroadcast | null; + public readonly paused: boolean; + public pausedSince: number | null; + public readonly pausedTime: number; + public player: object; + public readonly streamTime: number; + public readonly totalStreamTime: number; + + public pause(silence?: boolean): void; + public resume(): void; + public setBitrate(value: number | 'auto'): boolean; + public setFEC(enabled: boolean): boolean; + public setPLP(value: number): boolean; + + public on(event: 'close' | 'drain' | 'finish' | 'start', listener: () => void): this; + public on(event: 'debug', listener: (info: string) => void): this; + public on(event: 'error', listener: (err: Error) => void): this; + public on(event: 'pipe' | 'unpipe', listener: (src: Readable) => void): this; + public on(event: 'speaking', listener: (speaking: boolean) => void): this; + public on(event: 'volumeChange', listener: (oldVolume: number, newVolume: number) => void): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once(event: 'close' | 'drain' | 'finish' | 'start', listener: () => void): this; + public once(event: 'debug', listener: (info: string) => void): this; + public once(event: 'error', listener: (err: Error) => void): this; + public once(event: 'pipe' | 'unpipe', listener: (src: Readable) => void): this; + public once(event: 'speaking', listener: (speaking: boolean) => void): this; + public once(event: 'volumeChange', listener: (oldVolume: number, newVolume: number) => void): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + export class Structures { + public static get(structure: K): Extendable[K]; + public static get(structure: string): (...args: any[]) => void; + public static extend( + structure: K, + extender: (baseClass: Extendable[K]) => T, + ): T; + public static extend void>( + structure: string, + extender: (baseClass: typeof Function) => T, + ): T; + } + + export class SystemChannelFlags extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class Team extends Base { + constructor(client: Client, data: object); + public id: Snowflake; + public name: string; + public icon: string | null; + public ownerID: Snowflake | null; + public members: Collection; + + public readonly owner: TeamMember; + public readonly createdAt: Date; + public readonly createdTimestamp: number; + + public iconURL(options?: ImageURLOptions): string; + public toJSON(): object; + public toString(): string; + } + + export class TeamMember extends Base { + constructor(team: Team, data: object); + public team: Team; + public readonly id: Snowflake; + public permissions: string[]; + public membershipState: MembershipStates; + public user: User; + + public toString(): string; + } + + export class TextChannel extends TextBasedChannel(GuildChannel) { + constructor(guild: Guild, data?: object); + public messages: MessageManager; + public nsfw: boolean; + public type: 'text'; + public rateLimitPerUser: number; + public topic: string | null; + public createWebhook( + name: string, + options?: { avatar?: BufferResolvable | Base64Resolvable; reason?: string }, + ): Promise; + public setNSFW(nsfw: boolean, reason?: string): Promise; + public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise; + public fetchWebhooks(): Promise>; + } + + export class User extends PartialTextBasedChannel(Base) { + constructor(client: Client, data: object); + public avatar: string | null; + public bot: boolean; + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public discriminator: string; + public readonly defaultAvatarURL: string; + public readonly dmChannel: DMChannel | null; + public flags: Readonly | null; + public id: Snowflake; + public lastMessageID: Snowflake | null; + public locale: string | null; + public readonly partial: false; + public readonly presence: Presence; + public system: boolean | null; + public readonly tag: string; + public username: string; + public avatarURL(options?: ImageURLOptions & { dynamic?: boolean }): string | null; + public createDM(): Promise; + public deleteDM(): Promise; + public displayAvatarURL(options?: ImageURLOptions & { dynamic?: boolean }): string; + public equals(user: User): boolean; + public fetch(force?: boolean): Promise; + public fetchFlags(force?: boolean): Promise; + public toString(): string; + public typingDurationIn(channel: ChannelResolvable): number; + public typingIn(channel: ChannelResolvable): boolean; + public typingSinceIn(channel: ChannelResolvable): Date; + } + + export class UserFlags extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; + } + + export class Util { + public static basename(path: string, ext?: string): string; + public static binaryToID(num: string): Snowflake; + public static cleanContent(str: string, message: Message): string; + public static removeMentions(str: string): string; + public static cloneObject(obj: object): object; + public static convertToBuffer(ab: ArrayBuffer | string): Buffer; + public static delayFor(ms: number): Promise; + public static discordSort( + collection: Collection, + ): Collection; + public static escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string; + public static escapeCodeBlock(text: string): string; + public static escapeInlineCode(text: string): string; + public static escapeBold(text: string): string; + public static escapeItalic(text: string): string; + public static escapeUnderline(text: string): string; + public static escapeStrikethrough(text: string): string; + public static escapeSpoiler(text: string): string; + public static cleanCodeBlockContent(text: string): string; + public static fetchRecommendedShards(token: string, guildsPerShard?: number): Promise; + public static flatten(obj: object, ...props: { [key: string]: boolean | string }[]): object; + public static idToBinary(num: Snowflake): string; + public static makeError(obj: { name: string; message: string; stack: string }): Error; + public static makePlainError(err: Error): { name: string; message: string; stack: string }; + 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 | null } | null; + public static resolveColor(color: ColorResolvable): number; + public static resolveString(data: StringResolvable): string; + public static setPosition( + item: T, + position: number, + relative: boolean, + sorted: Collection, + route: object, + reason?: string, + ): Promise<{ id: Snowflake; position: number }[]>; + public static splitMessage(text: StringResolvable, options?: SplitOptions): string[]; + public static str2ab(str: string): ArrayBuffer; + } + + class VoiceBroadcast extends EventEmitter { + constructor(client: Client); + public client: Client; + public subscribers: StreamDispatcher[]; + public readonly dispatcher: BroadcastDispatcher | null; + public play(input: string | Readable, options?: StreamOptions): BroadcastDispatcher; + public end(): void; + + public on(event: 'end', listener: () => void): this; + public on(event: 'subscribe' | 'unsubscribe', listener: (dispatcher: StreamDispatcher) => void): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once(event: 'end', listener: () => void): this; + public once(event: 'subscribe' | 'unsubscribe', listener: (dispatcher: StreamDispatcher) => void): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + export class VoiceChannel extends GuildChannel { + constructor(guild: Guild, data?: object); + public bitrate: number; + public readonly editable: boolean; + public readonly full: boolean; + public readonly joinable: boolean; + public readonly speakable: boolean; + public type: 'voice'; + public userLimit: number; + public join(): Promise; + public leave(): void; + public setBitrate(bitrate: number, reason?: string): Promise; + public setUserLimit(userLimit: number, reason?: string): Promise; + } + + class VoiceConnection extends EventEmitter { + constructor(voiceManager: ClientVoiceManager, channel: VoiceChannel); + private authentication: object; + private sockets: object; + private ssrcMap: Map; + private _speaking: Map>; + private _disconnect(): void; + 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 sendVoiceStateUpdate(options: object): Promise; + private setSessionID(sessionID: string): void; + private setTokenAndEndpoint(token: string, endpoint: string): void; + private updateChannel(channel: VoiceChannel): void; + + public channel: VoiceChannel; + public readonly client: Client; + public readonly dispatcher: StreamDispatcher; + public player: object; + public receiver: VoiceReceiver; + public speaking: Readonly; + public status: VoiceStatus; + public readonly voice: VoiceState | null; + public voiceManager: ClientVoiceManager; + public disconnect(): void; + public play(input: VoiceBroadcast | Readable | string, options?: StreamOptions): StreamDispatcher; + public setSpeaking(value: BitFieldResolvable): void; + + public on(event: 'authenticated' | 'closing' | 'newSession' | 'ready' | 'reconnecting', listener: () => void): this; + public on(event: 'debug', listener: (message: string) => void): this; + public on(event: 'error' | 'failed' | 'disconnect', listener: (error: Error) => void): this; + public on(event: 'speaking', listener: (user: User, speaking: Readonly) => void): this; + public on(event: 'warn', listener: (warning: string | Error) => void): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once( + event: 'authenticated' | 'closing' | 'newSession' | 'ready' | 'reconnecting', + listener: () => void, + ): this; + public once(event: 'debug', listener: (message: string) => void): this; + public once(event: 'error' | 'failed' | 'disconnect', listener: (error: Error) => void): this; + public once(event: 'speaking', listener: (user: User, speaking: Readonly) => void): this; + public once(event: 'warn', listener: (warning: string | Error) => void): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + class VoiceReceiver extends EventEmitter { + constructor(connection: VoiceConnection); + public createStream( + user: UserResolvable, + options?: { mode?: 'opus' | 'pcm'; end?: 'silence' | 'manual' }, + ): Readable; + + public on(event: 'debug', listener: (error: Error | string) => void): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once(event: 'debug', listener: (error: Error | string) => void): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + export class VoiceRegion { + constructor(data: object); + public custom: boolean; + public deprecated: boolean; + public id: string; + public name: string; + public optimal: boolean; + public vip: boolean; + public toJSON(): object; + } + + export class VoiceState extends Base { + constructor(guild: Guild, data: object); + public readonly channel: VoiceChannel | null; + public channelID: Snowflake | null; + public readonly connection: VoiceConnection | null; + public readonly deaf: boolean | null; + public guild: Guild; + public id: Snowflake; + public readonly member: GuildMember | null; + public readonly mute: boolean | null; + public selfDeaf: boolean | null; + public selfMute: boolean | null; + public serverDeaf: boolean | null; + public serverMute: boolean | null; + public sessionID: string | null; + public streaming: boolean; + public selfVideo: boolean; + public readonly speaking: boolean | null; + + public setDeaf(deaf: boolean, reason?: string): Promise; + public setMute(mute: boolean, reason?: string): Promise; + public kick(reason?: string): Promise; + public setChannel(channel: ChannelResolvable | null, reason?: string): Promise; + public setSelfDeaf(deaf: boolean): Promise; + public setSelfMute(mute: boolean): Promise; + } + + class VolumeInterface extends EventEmitter { + constructor(options?: { volume?: number }); + public readonly volume: number; + public readonly volumeDecibels: number; + public readonly volumeEditable: boolean; + public readonly volumeLogarithmic: number; + public setVolume(volume: number): void; + public setVolumeDecibels(db: number): void; + public setVolumeLogarithmic(value: number): void; + + public on(event: 'volumeChange', listener: (oldVolume: number, newVolume: number) => void): this; + + public once(event: 'volumeChange', listener: (oldVolume: number, newVolume: number) => void): this; + } + + export class Webhook extends WebhookMixin() { + constructor(client: Client, data?: object); + public avatar: string; + public avatarURL(options?: ImageURLOptions): string | null; + public channelID: Snowflake; + public client: Client; + public guildID: Snowflake; + public name: string; + public owner: User | object | null; + public token: string | null; + public type: WebhookTypes; + } + + export class WebhookClient extends WebhookMixin(BaseClient) { + constructor(id: string, token: string, options?: ClientOptions); + public client: this; + public token: string; + } + + export class WebSocketManager extends EventEmitter { + constructor(client: Client); + private totalShards: number | string; + private shardQueue: Set; + private packetQueue: object[]; + private destroyed: boolean; + private reconnecting: boolean; + private sessionStartLimit: { total: number; remaining: number; reset_after: number } | null; + + public readonly client: Client; + public gateway: string | null; + public shards: Collection; + public status: Status; + public readonly ping: number; + + public on(event: WSEventType, listener: (data: any, shardID: number) => void): this; + public once(event: WSEventType, listener: (data: any, shardID: number) => void): this; + + private debug(message: string, shard?: WebSocketShard): void; + private connect(): Promise; + private createShards(): Promise; + private reconnect(): Promise; + private broadcast(packet: object): void; + private destroy(): void; + private _handleSessionLimit(remaining?: number, resetAfter?: number): Promise; + private handlePacket(packet?: object, shard?: WebSocketShard): boolean; + private checkShardsReady(): Promise; + private triggerClientReady(): void; + } + + export class WebSocketShard extends EventEmitter { + constructor(manager: WebSocketManager, id: number); + private sequence: number; + private closeSequence: number; + private sessionID: string | null; + private lastPingTimestamp: number; + private lastHeartbeatAcked: boolean; + private ratelimit: { queue: object[]; total: number; remaining: number; time: 60e3; timer: NodeJS.Timeout | null }; + private connection: WebSocket | null; + private helloTimeout: NodeJS.Timeout | null; + private eventsAttached: boolean; + private expectedGuilds: Set | null; + private readyTimeout: NodeJS.Timeout | null; + + public manager: WebSocketManager; + public id: number; + public status: Status; + public ping: number; + + private debug(message: string): void; + private connect(): Promise; + private onOpen(): void; + private onMessage(event: MessageEvent): void; + private onError(error: ErrorEvent | object): void; + private onClose(event: CloseEvent): void; + private onPacket(packet: object): void; + private checkReady(): void; + private setHelloTimeout(time?: number): void; + private setHeartbeatTimer(time: number): void; + private sendHeartbeat(): void; + private ackHeartbeat(): void; + private identify(): void; + private identifyNew(): void; + private identifyResume(): void; + private _send(data: object): void; + private processQueue(): void; + private destroy(destroyOptions?: { closeCode?: number; reset?: boolean; emit?: boolean; log?: boolean }): void; + private _cleanupConnection(): void; + private _emitDestroyed(): void; + + public send(data: object): void; + public on(event: 'ready' | 'resumed' | 'invalidSession', listener: () => void): this; + public on(event: 'close', listener: (event: CloseEvent) => void): this; + public on(event: 'allReady', listener: (unavailableGuilds?: Set) => void): this; + public on(event: string, listener: (...args: any[]) => void): this; + + public once(event: 'ready' | 'resumed' | 'invalidSession', listener: () => void): this; + public once(event: 'close', listener: (event: CloseEvent) => void): this; + public once(event: 'allReady', listener: (unavailableGuilds?: Set) => void): this; + public once(event: string, listener: (...args: any[]) => void): this; + } + + //#endregion + + //#region Collections + + export class Collection extends BaseCollection { + public flatMap( + fn: (value: V, key: K, collection: this) => Collection, + thisArg?: unknown, + ): Collection; + public flatMap( + fn: (this: This, value: V, key: K, collection: this) => Collection, + thisArg: This, + ): Collection; + public mapValues(fn: (value: V, key: K, collection: this) => T, thisArg?: unknown): Collection; + public mapValues( + fn: (this: This, value: V, key: K, collection: this) => T, + thisArg: This, + ): Collection; + public toJSON(): object; + } + + //#endregion + + //#region Managers + + export class ChannelManager extends BaseManager { + constructor(client: Client, iterable: Iterable); + public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise; + } + + export abstract class BaseManager { + constructor(client: Client, iterable: Iterable, holds: Constructable, cacheType: Collection); + public holds: Constructable; + public cache: Collection; + public cacheType: Collection; + public readonly client: Client; + public add(data: any, cache?: boolean, { id, extras }?: { id: K; extras: any[] }): Holds; + public resolve(resolvable: R): Holds | null; + public resolveID(resolvable: R): K | null; + public valueOf(): Collection; + } + + export class GuildChannelManager extends BaseManager { + constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; + public create(name: string, options: GuildCreateChannelOptions & { type: 'voice' }): Promise; + public create(name: string, options: GuildCreateChannelOptions & { type: 'category' }): Promise; + public create(name: string, options?: GuildCreateChannelOptions & { type?: 'text' }): Promise; + public create( + name: string, + options: GuildCreateChannelOptions, + ): Promise; + } + + export class GuildEmojiManager extends BaseManager { + constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; + public create( + attachment: BufferResolvable | Base64Resolvable, + name: string, + options?: GuildEmojiCreateOptions, + ): Promise; + public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null; + } + + export class GuildEmojiRoleManager { + constructor(emoji: GuildEmoji); + public emoji: GuildEmoji; + public guild: Guild; + public cache: Collection; + public add( + roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, + ): Promise; + public set(roles: readonly RoleResolvable[] | Collection): Promise; + public remove( + roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, + ): Promise; + } + + export class GuildManager extends BaseManager { + constructor(client: Client, iterable?: Iterable); + public create(name: string, options?: GuildCreateOptions): Promise; + public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise; + } + + export class GuildMemberManager extends BaseManager { + constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; + public ban(user: UserResolvable, options?: BanOptions): Promise; + public fetch( + options: UserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: UserResolvable }), + ): Promise; + public fetch(options?: FetchMembersOptions): Promise>; + public prune(options: GuildPruneMembersOptions & { dry?: false; count: false }): Promise; + public prune(options?: GuildPruneMembersOptions): Promise; + public unban(user: UserResolvable, reason?: string): Promise; + } + + export class GuildMemberRoleManager extends OverridableManager { + constructor(member: GuildMember); + public readonly hoist: Role | null; + public readonly color: Role | null; + public readonly highest: Role; + public member: GuildMember; + public guild: Guild; + + public add( + roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, + reason?: string, + ): Promise; + public set(roles: readonly RoleResolvable[] | Collection, reason?: string): Promise; + public remove( + roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, + reason?: string, + ): Promise; + } + + export class MessageManager extends BaseManager { + constructor(channel: TextChannel | DMChannel, iterable?: Iterable); + public channel: TextBasedChannelFields; + public cache: Collection; + public fetch(message: Snowflake, cache?: boolean, force?: boolean): Promise; + public fetch( + options?: ChannelLogsQueryOptions, + cache?: boolean, + force?: boolean, + ): Promise>; + public fetchPinned(cache?: boolean): Promise>; + public delete(message: MessageResolvable, reason?: string): Promise; + } + + // Hacky workaround because changing the signature of an overridden method errors + class OverridableManager extends BaseManager { + public add(data: any, cache: any): any; + public set(key: any): any; + } + + export class PresenceManager extends BaseManager { + constructor(client: Client, iterable?: Iterable); + } + + export class ReactionManager extends BaseManager { + constructor(message: Message, iterable?: Iterable); + public message: Message; + public removeAll(): Promise; + } + + export class ReactionUserManager extends BaseManager { + constructor(client: Client, iterable: Iterable | undefined, reaction: MessageReaction); + public reaction: MessageReaction; + public fetch(options?: { + limit?: number; + after?: Snowflake; + before?: Snowflake; + }): Promise>; + public remove(user?: UserResolvable): Promise; + } + + export class RoleManager extends BaseManager { + constructor(guild: Guild, iterable?: Iterable); + public readonly everyone: Role; + public readonly highest: Role; + public guild: Guild; + + public create(options?: { data?: RoleData; reason?: string }): Promise; + public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise; + public fetch(id?: Snowflake, cache?: boolean, force?: boolean): Promise; + } + + export class UserManager extends BaseManager { + constructor(client: Client, iterable?: Iterable); + public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise; + } + + export class VoiceStateManager extends BaseManager { + constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; + } + + //#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 = new (...args: any[]) => T; + function PartialTextBasedChannel(Base?: Constructable): Constructable; + function TextBasedChannel( + Base?: Constructable, + ignore?: I[], + ): Constructable>; + + interface PartialTextBasedChannelFields { + lastMessageID: Snowflake | null; + readonly lastMessage: Message | null; + send( + content: APIMessageContentResolvable | (MessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + send(options: MessageOptions & { split: true | SplitOptions }): Promise; + send(options: MessageOptions | APIMessage): Promise; + send(content: StringResolvable, options: (MessageOptions & { split?: false }) | MessageAdditions): Promise; + send(content: StringResolvable, options: MessageOptions & { split: true | SplitOptions }): Promise; + send(content: StringResolvable, options: MessageOptions): Promise; + } + + interface TextBasedChannelFields extends PartialTextBasedChannelFields { + _typing: Map; + lastPinTimestamp: number | null; + readonly lastPinAt: Date | null; + typing: boolean; + typingCount: number; + awaitMessages(filter: CollectorFilter, options?: AwaitMessagesOptions): Promise>; + bulkDelete( + messages: Collection | readonly MessageResolvable[] | number, + filterOld?: boolean, + ): Promise>; + createMessageCollector(filter: CollectorFilter, options?: MessageCollectorOptions): MessageCollector; + startTyping(count?: number): Promise; + stopTyping(force?: boolean): void; + } + + function WebhookMixin(Base?: Constructable): Constructable; + + function VolumeMixin(base: Constructable): Constructable; + + interface WebhookFields { + id: Snowflake; + readonly createdAt: Date; + readonly createdTimestamp: number; + readonly url: string; + delete(reason?: string): Promise; + edit(options: WebhookEditData): Promise; + send( + content: APIMessageContentResolvable | (WebhookMessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + send(options: WebhookMessageOptions & { split: true | SplitOptions }): Promise; + send(options: WebhookMessageOptions | APIMessage): Promise; + send( + content: StringResolvable, + options: (WebhookMessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + send( + content: StringResolvable, + options: WebhookMessageOptions & { split: true | SplitOptions }, + ): Promise; + send(content: StringResolvable, options: WebhookMessageOptions): Promise; + sendSlackMessage(body: object): Promise; + } + + //#endregion + + //#region Typedefs + + type ActivityFlagsString = 'INSTANCE' | 'JOIN' | 'SPECTATE' | 'JOIN_REQUEST' | 'SYNC' | 'PLAY'; + + interface ActivityOptions { + name?: string; + url?: string; + type?: ActivityType | number; + shardID?: number | readonly number[]; + } + + type ActivityType = 'PLAYING' | 'STREAMING' | 'LISTENING' | 'WATCHING' | 'CUSTOM_STATUS' | 'COMPETING'; + + interface AddGuildMemberOptions { + accessToken: string; + nick?: string; + roles?: Collection | RoleResolvable[]; + mute?: boolean; + deaf?: boolean; + } + + interface APIError { + UNKNOWN_ACCOUNT: number; + UNKNOWN_APPLICATION: number; + UNKNOWN_CHANNEL: number; + UNKNOWN_GUILD: number; + UNKNOWN_INTEGRATION: number; + UNKNOWN_INVITE: number; + UNKNOWN_MEMBER: number; + UNKNOWN_MESSAGE: number; + UNKNOWN_OVERWRITE: number; + UNKNOWN_PROVIDER: number; + UNKNOWN_ROLE: number; + UNKNOWN_TOKEN: number; + UNKNOWN_USER: number; + UNKNOWN_EMOJI: number; + UNKNOWN_WEBHOOK: number; + BOT_PROHIBITED_ENDPOINT: number; + BOT_ONLY_ENDPOINT: number; + MAXIMUM_GUILDS: number; + MAXIMUM_FRIENDS: number; + MAXIMUM_PINS: number; + MAXIMUM_ROLES: number; + MAXIMUM_REACTIONS: number; + UNAUTHORIZED: number; + MISSING_ACCESS: number; + INVALID_ACCOUNT_TYPE: number; + CANNOT_EXECUTE_ON_DM: number; + EMBED_DISABLED: number; + CANNOT_EDIT_MESSAGE_BY_OTHER: number; + CANNOT_SEND_EMPTY_MESSAGE: number; + CANNOT_MESSAGE_USER: number; + CANNOT_SEND_MESSAGES_IN_VOICE_CHANNEL: number; + CHANNEL_VERIFICATION_LEVEL_TOO_HIGH: number; + OAUTH2_APPLICATION_BOT_ABSENT: number; + MAXIMUM_OAUTH2_APPLICATIONS: number; + INVALID_OAUTH_STATE: number; + MISSING_PERMISSIONS: number; + INVALID_AUTHENTICATION_TOKEN: number; + NOTE_TOO_LONG: number; + INVALID_BULK_DELETE_QUANTITY: number; + CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: number; + CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: number; + BULK_DELETE_MESSAGE_TOO_OLD: number; + INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT: number; + REACTION_BLOCKED: number; + } + + type APIMessageContentResolvable = string | number | boolean | bigint | symbol | readonly StringResolvable[]; + + interface ApplicationAsset { + name: string; + id: Snowflake; + type: 'BIG' | 'SMALL'; + } + + interface AuditLogChange { + key: string; + old?: any; + new?: any; + } + + interface AwaitMessagesOptions extends MessageCollectorOptions { + errors?: string[]; + } + + interface AwaitReactionsOptions extends ReactionCollectorOptions { + errors?: string[]; + } + + interface BanOptions { + days?: number; + reason?: string; + } + + type Base64Resolvable = Buffer | Base64String; + + type Base64String = string; + + type BitFieldResolvable = + | RecursiveReadonlyArray>> + | T + | number + | Readonly>; + + type BufferResolvable = Buffer | string; + + interface ChannelCreationOverwrites { + allow?: PermissionResolvable | number; + deny?: PermissionResolvable | number; + id: RoleResolvable | UserResolvable; + } + + interface ChannelData { + name?: string; + position?: number; + topic?: string; + nsfw?: boolean; + bitrate?: number; + userLimit?: number; + parentID?: Snowflake | null; + rateLimitPerUser?: number; + lockPermissions?: boolean; + permissionOverwrites?: readonly OverwriteResolvable[] | Collection; + } + + interface ChannelLogsQueryOptions { + limit?: number; + before?: Snowflake; + after?: Snowflake; + around?: Snowflake; + } + + interface ChannelPosition { + channel: ChannelResolvable; + position: number; + } + + type ChannelResolvable = Channel | Snowflake; + + interface ClientEvents { + channelCreate: [Channel]; + channelDelete: [Channel | PartialDMChannel]; + channelPinsUpdate: [Channel | PartialDMChannel, Date]; + channelUpdate: [Channel, Channel]; + debug: [string]; + warn: [string]; + disconnect: [any, number]; + emojiCreate: [GuildEmoji]; + emojiDelete: [GuildEmoji]; + emojiUpdate: [GuildEmoji, GuildEmoji]; + error: [Error]; + guildBanAdd: [Guild, User]; + guildBanRemove: [Guild, User]; + guildCreate: [Guild]; + guildDelete: [Guild]; + guildUnavailable: [Guild]; + guildIntegrationsUpdate: [Guild]; + guildMemberAdd: [GuildMember]; + guildMemberAvailable: [GuildMember | PartialGuildMember]; + guildMemberRemove: [GuildMember | PartialGuildMember]; + guildMembersChunk: [ + Collection, + Guild, + { count: number; index: number; nonce: string | undefined }, + ]; + guildMemberSpeaking: [GuildMember | PartialGuildMember, Readonly]; + guildMemberUpdate: [GuildMember | PartialGuildMember, GuildMember]; + guildUpdate: [Guild, Guild]; + inviteCreate: [Invite]; + inviteDelete: [Invite]; + message: [Message]; + messageDelete: [Message | PartialMessage]; + messageReactionRemoveAll: [Message | PartialMessage]; + messageReactionRemoveEmoji: [MessageReaction]; + messageDeleteBulk: [Collection]; + messageReactionAdd: [MessageReaction, User | PartialUser]; + messageReactionRemove: [MessageReaction, User | PartialUser]; + messageUpdate: [Message | PartialMessage, Message | PartialMessage]; + presenceUpdate: [Presence | undefined, Presence]; + rateLimit: [RateLimitData]; + ready: []; + invalidated: []; + roleCreate: [Role]; + roleDelete: [Role]; + roleUpdate: [Role, Role]; + typingStart: [Channel | PartialDMChannel, User | PartialUser]; + userUpdate: [User | PartialUser, User]; + voiceStateUpdate: [VoiceState, VoiceState]; + webhookUpdate: [TextChannel]; + shardDisconnect: [CloseEvent, number]; + shardError: [Error, number]; + shardReady: [number, Set | undefined]; + shardReconnecting: [number]; + shardResume: [number, number]; + } + + interface ClientOptions { + shards?: number | number[] | 'auto'; + shardCount?: number; + messageCacheMaxSize?: number; + messageCacheLifetime?: number; + messageSweepInterval?: number; + messageEditHistoryMaxSize?: number; + fetchAllMembers?: boolean; + disableMentions?: 'none' | 'all' | 'everyone'; + allowedMentions?: MessageMentionOptions; + partials?: PartialTypes[]; + restWsBridgeTimeout?: number; + restTimeOffset?: number; + restRequestTimeout?: number; + restSweepInterval?: number; + retryLimit?: number; + presence?: PresenceData; + ws?: WebSocketOptions; + http?: HTTPOptions; + } + + type ClientPresenceStatus = 'online' | 'idle' | 'dnd'; + + interface ClientPresenceStatusData { + web?: ClientPresenceStatus; + mobile?: ClientPresenceStatus; + desktop?: ClientPresenceStatus; + } + + interface CloseEvent { + wasClean: boolean; + code: number; + reason: string; + target: WebSocket; + } + + type CollectorFilter = (...args: any[]) => boolean | Promise; + + interface CollectorOptions { + time?: number; + idle?: number; + dispose?: boolean; + } + + type ColorResolvable = + | 'DEFAULT' + | 'WHITE' + | 'AQUA' + | 'GREEN' + | 'BLUE' + | 'YELLOW' + | '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' + | 'BLURPLE' + | 'GREYPLE' + | 'DARK_BUT_NOT_BLACK' + | 'NOT_QUITE_BLACK' + | 'RANDOM' + | [number, number, number] + | number + | string; + + interface CrosspostedChannel { + channelID: Snowflake; + guildID: Snowflake; + type: keyof typeof ChannelType; + name: string; + } + + interface DeconstructedSnowflake { + timestamp: number; + readonly date: Date; + workerID: number; + processID: number; + increment: number; + binary: string; + } + + type DefaultMessageNotifications = 'ALL' | 'MENTIONS'; + + interface EmbedField { + name: string; + value: string; + inline: boolean; + } + + interface EmbedFieldData { + name: StringResolvable; + value: StringResolvable; + inline?: boolean; + } + + type EmojiIdentifierResolvable = string | EmojiResolvable; + + type EmojiResolvable = Snowflake | GuildEmoji | ReactionEmoji; + + interface ErrorEvent { + error: any; + message: string; + type: string; + target: WebSocket; + } + + interface EscapeMarkdownOptions { + codeBlock?: boolean; + inlineCode?: boolean; + bold?: boolean; + italic?: boolean; + underline?: boolean; + strikethrough?: boolean; + spoiler?: boolean; + inlineCodeContent?: boolean; + codeBlockContent?: boolean; + } + + type ExplicitContentFilterLevel = 'DISABLED' | 'MEMBERS_WITHOUT_ROLES' | 'ALL_MEMBERS'; + + interface Extendable { + GuildEmoji: typeof GuildEmoji; + DMChannel: typeof DMChannel; + TextChannel: typeof TextChannel; + VoiceChannel: typeof VoiceChannel; + CategoryChannel: typeof CategoryChannel; + NewsChannel: typeof NewsChannel; + StoreChannel: typeof StoreChannel; + GuildMember: typeof GuildMember; + Guild: typeof Guild; + Message: typeof Message; + MessageReaction: typeof MessageReaction; + Presence: typeof Presence; + VoiceState: typeof VoiceState; + Role: typeof Role; + User: typeof User; + } + + interface FetchIntegrationsOptions { + includeApplications?: boolean; + } + + interface FetchMemberOptions { + user: UserResolvable; + cache?: boolean; + force?: boolean; + } + + interface FetchMembersOptions { + user?: UserResolvable | UserResolvable[]; + query?: string; + limit?: number; + withPresences?: boolean; + time?: number; + nonce?: string; + force?: boolean; + } + + interface FileOptions { + attachment: BufferResolvable | Stream; + name?: string; + } + + type GuildAuditLogsAction = keyof GuildAuditLogsActions; + + interface 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'; + + interface GuildAuditLogsFetchOptions { + before?: Snowflake | GuildAuditLogsEntry; + limit?: number; + user?: UserResolvable; + type?: GuildAuditLogsAction | number; + } + + type GuildAuditLogsTarget = keyof GuildAuditLogsTargets; + + interface GuildAuditLogsTargets { + ALL?: string; + GUILD?: string; + CHANNEL?: string; + USER?: string; + ROLE?: string; + INVITE?: string; + WEBHOOK?: string; + EMOJI?: string; + MESSAGE?: string; + INTEGRATION?: string; + UNKNOWN?: string; + } + + type GuildChannelResolvable = Snowflake | GuildChannel; + + interface GuildCreateChannelOptions { + permissionOverwrites?: OverwriteResolvable[] | Collection; + topic?: string; + type?: Exclude< + keyof typeof ChannelType | ChannelType, + 'dm' | 'group' | 'unknown' | ChannelType.dm | ChannelType.group | ChannelType.unknown + >; + nsfw?: boolean; + parent?: ChannelResolvable; + bitrate?: number; + userLimit?: number; + rateLimitPerUser?: number; + position?: number; + reason?: string; + } + + interface GuildChannelCloneOptions extends GuildCreateChannelOptions { + name?: string; + } + + interface GuildCreateOptions { + afkChannelID?: number; + afkTimeout?: number; + channels?: PartialChannelData[]; + defaultMessageNotifications?: DefaultMessageNotifications | number; + explicitContentFilter?: ExplicitContentFilterLevel | number; + icon?: BufferResolvable | Base64Resolvable | null; + region?: string; + roles?: PartialRoleData[]; + systemChannelID?: number; + verificationLevel?: VerificationLevel | number; + } + + interface GuildWidget { + enabled: boolean; + channel: GuildChannel | null; + } + + interface GuildEditData { + name?: string; + region?: string; + verificationLevel?: VerificationLevel | number; + explicitContentFilter?: ExplicitContentFilterLevel | number; + defaultMessageNotifications?: DefaultMessageNotifications | number; + afkChannel?: ChannelResolvable; + systemChannel?: ChannelResolvable; + systemChannelFlags?: SystemChannelFlagsResolvable; + afkTimeout?: number; + icon?: Base64Resolvable; + owner?: GuildMemberResolvable; + splash?: Base64Resolvable; + discoverySplash?: Base64Resolvable; + banner?: Base64Resolvable; + rulesChannel?: ChannelResolvable; + publicUpdatesChannel?: ChannelResolvable; + preferredLocale?: string; + } + + interface GuildEmojiCreateOptions { + roles?: Collection | RoleResolvable[]; + reason?: string; + } + + interface GuildEmojiEditData { + name?: string; + roles?: Collection | RoleResolvable[]; + } + + type GuildFeatures = + | 'ANIMATED_ICON' + | 'BANNER' + | 'COMMERCE' + | 'COMMUNITY' + | 'DISCOVERABLE' + | 'FEATURABLE' + | 'INVITE_SPLASH' + | 'NEWS' + | 'PARTNERED' + | 'RELAY_ENABLED' + | 'VANITY_URL' + | 'VERIFIED' + | 'VIP_REGIONS' + | 'WELCOME_SCREEN_ENABLED'; + + interface GuildMemberEditData { + nick?: string; + roles?: Collection | readonly RoleResolvable[]; + mute?: boolean; + deaf?: boolean; + channel?: ChannelResolvable | null; + } + + type GuildMemberResolvable = GuildMember | UserResolvable; + + type GuildResolvable = Guild | GuildChannel | GuildMember | GuildEmoji | Invite | Role | Snowflake; + + interface GuildPruneMembersOptions { + count?: boolean; + days?: number; + dry?: boolean; + reason?: string; + roles?: RoleResolvable[]; + } + + interface GuildWidgetData { + enabled: boolean; + channel: GuildChannelResolvable | null; + } + + interface HTTPOptions { + api?: string; + version?: number; + host?: string; + cdn?: string; + invite?: string; + } + + type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096; + + interface ImageURLOptions { + format?: AllowedImageFormat; + size?: ImageSize; + } + + interface IntegrationData { + id: string; + type: string; + } + + interface IntegrationEditData { + expireBehavior?: number; + expireGracePeriod?: number; + } + + interface IntegrationAccount { + id: string; + name: string; + } + + type IntentsString = + | 'GUILDS' + | 'GUILD_MEMBERS' + | 'GUILD_BANS' + | 'GUILD_EMOJIS' + | 'GUILD_INTEGRATIONS' + | 'GUILD_WEBHOOKS' + | 'GUILD_INVITES' + | 'GUILD_VOICE_STATES' + | 'GUILD_PRESENCES' + | 'GUILD_MESSAGES' + | 'GUILD_MESSAGE_REACTIONS' + | 'GUILD_MESSAGE_TYPING' + | 'DIRECT_MESSAGES' + | 'DIRECT_MESSAGE_REACTIONS' + | 'DIRECT_MESSAGE_TYPING'; + + interface InviteGenerationOptions { + permissions?: PermissionResolvable; + guild?: GuildResolvable; + disableGuildSelect?: boolean; + } + + interface InviteOptions { + temporary?: boolean; + maxAge?: number; + maxUses?: number; + unique?: boolean; + reason?: string; + } + + type InviteResolvable = string; + + type MembershipStates = 'INVITED' | 'ACCEPTED'; + + type MessageAdditions = MessageEmbed | MessageAttachment | (MessageEmbed | MessageAttachment)[]; + + interface MessageActivity { + partyID: string; + type: number; + } + + interface MessageCollectorOptions extends CollectorOptions { + max?: number; + maxProcessed?: number; + } + + interface MessageEditOptions { + content?: StringResolvable; + embed?: MessageEmbed | MessageEmbedOptions | null; + code?: string | boolean; + flags?: BitFieldResolvable; + allowedMentions?: MessageMentionOptions; + } + + interface MessageEmbedAuthor { + name?: string; + url?: string; + iconURL?: string; + proxyIconURL?: string; + } + + interface MessageEmbedFooter { + text?: string; + iconURL?: string; + proxyIconURL?: string; + } + + interface MessageEmbedImage { + url: string; + proxyURL?: string; + height?: number; + width?: number; + } + + interface MessageEmbedOptions { + title?: string; + description?: string; + url?: string; + timestamp?: Date | number; + color?: ColorResolvable; + fields?: EmbedFieldData[]; + files?: (MessageAttachment | string | FileOptions)[]; + author?: Partial & { icon_url?: string; proxy_icon_url?: string }; + thumbnail?: Partial & { proxy_url?: string }; + image?: Partial & { proxy_url?: string }; + video?: Partial & { proxy_url?: string }; + footer?: Partial & { icon_url?: string; proxy_icon_url?: string }; + } + + interface MessageEmbedProvider { + name: string; + url: string; + } + + interface MessageEmbedThumbnail { + url: string; + proxyURL?: string; + height?: number; + width?: number; + } + + interface MessageEmbedVideo { + url?: string; + proxyURL?: string; + height?: number; + width?: number; + } + + interface MessageEvent { + data: WebSocket.Data; + type: string; + target: WebSocket; + } + + type MessageFlagsString = 'CROSSPOSTED' | 'IS_CROSSPOST' | 'SUPPRESS_EMBEDS' | 'SOURCE_MESSAGE_DELETED' | 'URGENT'; + + interface MessageMentionOptions { + parse?: MessageMentionTypes[]; + roles?: Snowflake[]; + users?: Snowflake[]; + } + + type MessageMentionTypes = 'roles' | 'users' | 'everyone'; + + interface MessageOptions { + tts?: boolean; + nonce?: string; + content?: StringResolvable; + embed?: MessageEmbed | MessageEmbedOptions; + disableMentions?: 'none' | 'all' | 'everyone'; + allowedMentions?: MessageMentionOptions; + files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; + code?: string | boolean; + split?: boolean | SplitOptions; + reply?: UserResolvable; + } + + type MessageReactionResolvable = MessageReaction | Snowflake; + + interface MessageReference { + channelID: string; + guildID: string; + messageID: string | null; + } + + type MessageResolvable = Message | Snowflake; + + type MessageTarget = TextChannel | NewsChannel | DMChannel | User | GuildMember | Webhook | WebhookClient; + + type MessageType = + | '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'; + + interface OverwriteData { + allow?: PermissionResolvable; + deny?: PermissionResolvable; + id: GuildMemberResolvable | RoleResolvable; + type?: OverwriteType; + } + + type OverwriteResolvable = PermissionOverwrites | OverwriteData; + + type OverwriteType = 'member' | 'role'; + + interface PermissionFlags extends Record {} + + interface PermissionObject extends Record {} + + interface PermissionOverwriteOption extends Partial> {} + + type PermissionResolvable = BitFieldResolvable; + + type PermissionString = + | 'CREATE_INSTANT_INVITE' + | 'KICK_MEMBERS' + | 'BAN_MEMBERS' + | 'ADMINISTRATOR' + | 'MANAGE_CHANNELS' + | 'MANAGE_GUILD' + | 'ADD_REACTIONS' + | 'VIEW_AUDIT_LOG' + | 'PRIORITY_SPEAKER' + | 'STREAM' + | 'VIEW_CHANNEL' + | 'SEND_MESSAGES' + | 'SEND_TTS_MESSAGES' + | 'MANAGE_MESSAGES' + | 'EMBED_LINKS' + | 'ATTACH_FILES' + | 'READ_MESSAGE_HISTORY' + | 'MENTION_EVERYONE' + | 'USE_EXTERNAL_EMOJIS' + | 'VIEW_GUILD_INSIGHTS' + | 'CONNECT' + | 'SPEAK' + | 'MUTE_MEMBERS' + | 'DEAFEN_MEMBERS' + | 'MOVE_MEMBERS' + | 'USE_VAD' + | 'CHANGE_NICKNAME' + | 'MANAGE_NICKNAMES' + | 'MANAGE_ROLES' + | 'MANAGE_WEBHOOKS' + | 'MANAGE_EMOJIS'; + + interface RecursiveArray extends ReadonlyArray> {} + + type RecursiveReadonlyArray = ReadonlyArray>; + + interface PermissionOverwriteOptions { + allow: PermissionResolvable; + deny: PermissionResolvable; + id: UserResolvable | RoleResolvable; + } + + type PremiumTier = number; + + interface PresenceData { + status?: PresenceStatusData; + afk?: boolean; + activity?: { + name?: string; + type?: ActivityType | number; + url?: string; + }; + shardID?: number | number[]; + } + + type PresenceResolvable = Presence | UserResolvable | Snowflake; + + type Partialize = { + readonly client: Client; + readonly createdAt: Date; + readonly createdTimestamp: number; + deleted: boolean; + id: string; + partial: true; + fetch(): Promise; + } & { + [K in keyof Omit< + T, + 'client' | 'createdAt' | 'createdTimestamp' | 'id' | 'partial' | 'fetch' | 'deleted' | O + >]: T[K] extends Function ? T[K] : T[K] | null; // tslint:disable-line:ban-types + }; + + interface PartialDMChannel + extends Partialize< + DMChannel, + 'lastMessage' | 'lastMessageID' | 'messages' | 'recipient' | 'type' | 'typing' | 'typingCount' + > { + lastMessage: null; + lastMessageID: undefined; + messages: MessageManager; + recipient: User | PartialUser; + type: 'dm'; + readonly typing: boolean; + readonly typingCount: number; + } + + interface PartialChannelData { + id?: number; + name: string; + topic?: string; + type?: ChannelType; + parentID?: number; + permissionOverwrites?: { + id: number | Snowflake; + type?: OverwriteType; + allow?: PermissionResolvable; + deny?: PermissionResolvable; + }[]; + } + + interface PartialGuildMember + extends Partialize< + GuildMember, + | 'bannable' + | 'displayColor' + | 'displayHexColor' + | 'displayName' + | 'guild' + | 'kickable' + | 'permissions' + | 'roles' + | 'manageable' + | 'presence' + | 'voice' + > { + readonly bannable: boolean; + readonly displayColor: number; + readonly displayHexColor: string; + readonly displayName: string; + guild: Guild; + readonly manageable: boolean; + joinedAt: null; + joinedTimestamp: null; + readonly kickable: boolean; + readonly permissions: GuildMember['permissions']; + readonly presence: GuildMember['presence']; + readonly roles: GuildMember['roles']; + readonly voice: GuildMember['voice']; + } + + interface PartialMessage + extends Partialize< + Message, + | 'attachments' + | 'channel' + | 'deletable' + | 'editable' + | 'mentions' + | 'pinnable' + | 'url' + | 'flags' + | 'edits' + | 'embeds' + > { + attachments: Message['attachments']; + channel: Message['channel']; + readonly deletable: boolean; + readonly editable: boolean; + readonly edits: Message['edits']; + embeds: Message['embeds']; + flags: Message['flags']; + mentions: Message['mentions']; + readonly pinnable: boolean; + reactions: Message['reactions']; + readonly url: string; + } + + interface PartialRoleData extends RoleData { + id?: number; + } + + type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION'; + + interface PartialUser + extends Omit, 'deleted'> { + bot: User['bot']; + flags: User['flags']; + locale: User['locale']; + system: User['system']; + readonly tag: null; + username: null; + } + + type PresenceStatusData = ClientPresenceStatus | 'invisible'; + + type PresenceStatus = PresenceStatusData | 'offline'; + + interface RateLimitData { + timeout: number; + limit: number; + timeDifference: number; + method: string; + path: string; + route: string; + } + + interface RawOverwriteData { + id: Snowflake; + allow: number; + deny: number; + type: OverwriteType; + } + + interface ReactionCollectorOptions extends CollectorOptions { + max?: number; + maxEmojis?: number; + maxUsers?: number; + } + + interface ResolvedOverwriteOptions { + allow: Permissions; + deny: Permissions; + } + + interface RoleData { + name?: string; + color?: ColorResolvable; + hoist?: boolean; + position?: number; + permissions?: PermissionResolvable; + mentionable?: boolean; + } + + interface RolePosition { + role: RoleResolvable; + position: number; + } + + type RoleResolvable = Role | string; + + type ShardingManagerMode = 'process' | 'worker'; + + type Snowflake = string; + + interface SplitOptions { + maxLength?: number; + char?: string; + prepend?: string; + append?: string; + } + + type Status = number; + + interface StreamOptions { + type?: StreamType; + seek?: number; + volume?: number | boolean; + plp?: number; + fec?: boolean; + bitrate?: number | 'auto'; + highWaterMark?: number; + } + + type SpeakingString = 'SPEAKING' | 'SOUNDSHARE' | 'PRIORITY_SPEAKING'; + + type StreamType = 'unknown' | 'converted' | 'opus' | 'ogg/opus' | 'webm/opus'; + + type StringResolvable = string | string[] | any; + + type SystemChannelFlagsString = 'WELCOME_MESSAGE_DISABLED' | 'BOOST_MESSAGE_DISABLED'; + + type SystemChannelFlagsResolvable = BitFieldResolvable; + + type TargetUser = number; + + interface TypingData { + user: User | PartialUser; + since: Date; + lastTimestamp: Date; + elapsedTime: number; + timeout: NodeJS.Timeout; + } + + type UserFlagsString = + | 'DISCORD_EMPLOYEE' + | 'PARTNERED_SERVER_OWNER' + | 'DISCORD_PARTNER' + | 'HYPESQUAD_EVENTS' + | 'BUGHUNTER_LEVEL_1' + | 'HOUSE_BRAVERY' + | 'HOUSE_BRILLIANCE' + | 'HOUSE_BALANCE' + | 'EARLY_SUPPORTER' + | 'TEAM_USER' + | 'SYSTEM' + | 'BUGHUNTER_LEVEL_2' + | 'VERIFIED_BOT' + | 'EARLY_VERIFIED_DEVELOPER' + | 'VERIFIED_DEVELOPER'; + + type UserResolvable = User | Snowflake | Message | GuildMember; + + type VerificationLevel = 'NONE' | 'LOW' | 'MEDIUM' | 'HIGH' | 'VERY_HIGH'; + + type VoiceStatus = number; + + interface WebhookEditData { + name?: string; + avatar?: BufferResolvable; + channel?: ChannelResolvable; + reason?: string; + } + + interface WebhookMessageOptions { + username?: string; + avatarURL?: string; + tts?: boolean; + nonce?: string; + embeds?: (MessageEmbed | object)[]; + disableMentions?: 'none' | 'all' | 'everyone'; + allowedMentions?: MessageMentionOptions; + files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; + code?: string | boolean; + split?: boolean | SplitOptions; + } + + type WebhookTypes = 'Incoming' | 'Channel Follower'; + + interface WebSocketOptions { + large_threshold?: number; + compress?: boolean; + intents?: BitFieldResolvable | number; + properties?: WebSocketProperties; + } + + interface WebSocketProperties { + $os?: string; + $browser?: string; + $device?: string; + } + + type WSEventType = + | 'READY' + | 'RESUMED' + | 'GUILD_CREATE' + | 'GUILD_DELETE' + | 'GUILD_UPDATE' + | 'INVITE_CREATE' + | 'INVITE_DELETE' + | 'GUILD_MEMBER_ADD' + | 'GUILD_MEMBER_REMOVE' + | 'GUILD_MEMBER_UPDATE' + | 'GUILD_MEMBERS_CHUNK' + | 'GUILD_ROLE_CREATE' + | 'GUILD_ROLE_DELETE' + | 'GUILD_ROLE_UPDATE' + | 'GUILD_BAN_ADD' + | 'GUILD_BAN_REMOVE' + | 'GUILD_EMOJIS_UPDATE' + | 'GUILD_INTEGRATIONS_UPDATE' + | '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' + | 'MESSAGE_REACTION_REMOVE_EMOJI' + | 'USER_UPDATE' + | 'PRESENCE_UPDATE' + | 'TYPING_START' + | 'VOICE_STATE_UPDATE' + | 'VOICE_SERVER_UPDATE' + | 'WEBHOOKS_UPDATE'; + + //#endregion +} diff --git a/node_modules/discord.js/typings/index.js b/node_modules/discord.js/typings/index.js new file mode 100644 index 0000000..d4a230a --- /dev/null +++ b/node_modules/discord.js/typings/index.js @@ -0,0 +1,40 @@ +"use strict"; +/// +Object.defineProperty(exports, "__esModule", { value: true }); +const discord_js_1 = require("discord.js"); +const client = new discord_js_1.Client(); +client.on('ready', () => { + console.log(`Client is logged in as ${client.user.tag} and ready!`); +}); +client.on('guildCreate', g => { + const channel = g.channels.cache.random(); + if (!channel) + return; + channel.setName('foo').then(updatedChannel => { + console.log(`New channel name: ${updatedChannel.name}`); + }); +}); +client.on('messageReactionRemoveAll', async (message) => { + console.log(`messageReactionRemoveAll - id: ${message.id} (${message.id.length})`); + if (message.partial) + message = await message.fetch(); + console.log(`messageReactionRemoveAll - content: ${message.content}`); +}); +client.on('message', ({ channel }) => { + assertIsMessage(channel.send('string')); + assertIsMessage(channel.send({})); + assertIsMessage(channel.send({ embed: {} })); + assertIsMessage(channel.send({ another: 'property' }, {})); + const attachment = new discord_js_1.MessageAttachment('file.png'); + const embed = new discord_js_1.MessageEmbed(); + assertIsMessage(channel.send(attachment)); + assertIsMessage(channel.send(embed)); + assertIsMessage(channel.send([attachment, embed])); + assertIsMessageArray(channel.send(Symbol('another primitive'), { split: true })); + assertIsMessageArray(channel.send({ split: true })); + // @ts-expect-error + channel.send(); + // @ts-expect-error + channel.send({ another: 'property' }); +}); +client.login('absolutely-valid-token'); diff --git a/node_modules/discord.js/typings/index.ts b/node_modules/discord.js/typings/index.ts new file mode 100644 index 0000000..db7abe7 --- /dev/null +++ b/node_modules/discord.js/typings/index.ts @@ -0,0 +1,53 @@ +/// + +import { Client, Message, MessageAttachment, MessageEmbed } from 'discord.js'; + +const client: Client = new Client(); + +client.on('ready', () => { + console.log(`Client is logged in as ${client.user!.tag} and ready!`); +}); + +client.on('guildCreate', g => { + const channel = g.channels.cache.random(); + if (!channel) return; + + channel.setName('foo').then(updatedChannel => { + console.log(`New channel name: ${updatedChannel.name}`); + }); +}); + +client.on('messageReactionRemoveAll', async message => { + console.log(`messageReactionRemoveAll - id: ${message.id} (${message.id.length})`); + + if (message.partial) message = await message.fetch(); + + console.log(`messageReactionRemoveAll - content: ${message.content}`); +}); + +// These are to check that stuff is the right type +declare const assertIsMessage: (m: Promise) => void; +declare const assertIsMessageArray: (m: Promise) => void; + +client.on('message', ({ channel }) => { + assertIsMessage(channel.send('string')); + assertIsMessage(channel.send({})); + assertIsMessage(channel.send({ embed: {} })); + assertIsMessage(channel.send({ another: 'property' }, {})); + + const attachment = new MessageAttachment('file.png'); + const embed = new MessageEmbed(); + assertIsMessage(channel.send(attachment)); + assertIsMessage(channel.send(embed)); + assertIsMessage(channel.send([attachment, embed])); + + assertIsMessageArray(channel.send(Symbol('another primitive'), { split: true })); + assertIsMessageArray(channel.send({ split: true })); + + // @ts-expect-error + channel.send(); + // @ts-expect-error + channel.send({ another: 'property' }); +}); + +client.login('absolutely-valid-token'); diff --git a/node_modules/discord.js/webpack/discord.js b/node_modules/discord.js/webpack/discord.js new file mode 100644 index 0000000..1752c0b --- /dev/null +++ b/node_modules/discord.js/webpack/discord.js @@ -0,0 +1,2 @@ +/*! For license information please see discord.js.LICENSE.txt */ +!function webpackUniversalModuleDefinition(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.Discord=n():e.Discord=n()}(window,(function(){return function(e){var n={};function __webpack_require__(t){if(n[t])return n[t].exports;var s=n[t]={i:t,l:!1,exports:{}};return e[t].call(s.exports,s,s.exports,__webpack_require__),s.l=!0,s.exports}return __webpack_require__.m=e,__webpack_require__.c=n,__webpack_require__.d=function(e,n,t){__webpack_require__.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.t=function(e,n){if(1&n&&(e=__webpack_require__(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(__webpack_require__.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var s in e)__webpack_require__.d(t,s,function(n){return e[n]}.bind(null,s));return t},__webpack_require__.n=function(e){var n=e&&e.__esModule?function getDefault(){return e.default}:function getModuleExports(){return e};return __webpack_require__.d(n,"a",n),n},__webpack_require__.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s="./src/index.js")}({"./node_modules/@discordjs/collection/dist/index.js":function(module,exports,__webpack_require__){"use strict";eval("\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.Collection = void 0;\n/**\n * A Map with additional utility methods. This is used throughout discord.js rather than Arrays for anything that has\n * an ID, for significantly improved performance and ease-of-use.\n * @extends {Map}\n * @property {number} size - The amount of elements in this collection.\n */\nclass Collection extends Map {\n constructor(entries) {\n super(entries);\n /**\n * Cached array for the `array()` method - will be reset to `null` whenever `set()` or `delete()` are called\n * @name Collection#_array\n * @type {?Array}\n * @private\n */\n Object.defineProperty(this, '_array', { value: null, writable: true, configurable: true });\n /**\n * Cached array for the `keyArray()` method - will be reset to `null` whenever `set()` or `delete()` are called\n * @name Collection#_keyArray\n * @type {?Array}\n * @private\n */\n Object.defineProperty(this, '_keyArray', { value: null, writable: true, configurable: true });\n }\n /**\n * Identical to [Map.get()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get).\n * Gets an element with the specified key, and returns its value, or `undefined` if the element does not exist.\n * @param {*} key - The key to get from this collection\n * @returns {* | undefined}\n */\n get(key) {\n return super.get(key);\n }\n /**\n * Identical to [Map.set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set).\n * Sets a new element in the collection with the specified key and value.\n * @param {*} key - The key of the element to add\n * @param {*} value - The value of the element to add\n * @returns {Collection}\n */\n set(key, value) {\n this._array = null;\n this._keyArray = null;\n return super.set(key, value);\n }\n /**\n * Identical to [Map.has()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has).\n * Checks if an element exists in the collection.\n * @param {*} key - The key of the element to check for\n * @returns {boolean} `true` if the element exists, `false` if it does not exist.\n */\n has(key) {\n return super.has(key);\n }\n /**\n * Identical to [Map.delete()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete).\n * Deletes an element from the collection.\n * @param {*} key - The key to delete from the collection\n * @returns {boolean} `true` if the element was removed, `false` if the element does not exist.\n */\n delete(key) {\n this._array = null;\n this._keyArray = null;\n return super.delete(key);\n }\n /**\n * Identical to [Map.clear()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear).\n * Removes all elements from the collection.\n * @returns {undefined}\n */\n clear() {\n return super.clear();\n }\n /**\n * Creates an ordered array of the values of this collection, and caches it internally. The array will only be\n * reconstructed if an item is added to or removed from the collection, or if you change the length of the array\n * itself. If you don't want this caching behavior, use `[...collection.values()]` or\n * `Array.from(collection.values())` instead.\n * @returns {Array}\n */\n array() {\n if (!this._array || this._array.length !== this.size)\n this._array = [...this.values()];\n return this._array;\n }\n /**\n * Creates an ordered array of the keys of this collection, and caches it internally. The array will only be\n * reconstructed if an item is added to or removed from the collection, or if you change the length of the array\n * itself. If you don't want this caching behavior, use `[...collection.keys()]` or\n * `Array.from(collection.keys())` instead.\n * @returns {Array}\n */\n keyArray() {\n if (!this._keyArray || this._keyArray.length !== this.size)\n this._keyArray = [...this.keys()];\n return this._keyArray;\n }\n first(amount) {\n if (typeof amount === 'undefined')\n return this.values().next().value;\n if (amount < 0)\n return this.last(amount * -1);\n amount = Math.min(this.size, amount);\n const iter = this.values();\n return Array.from({ length: amount }, () => iter.next().value);\n }\n firstKey(amount) {\n if (typeof amount === 'undefined')\n return this.keys().next().value;\n if (amount < 0)\n return this.lastKey(amount * -1);\n amount = Math.min(this.size, amount);\n const iter = this.keys();\n return Array.from({ length: amount }, () => iter.next().value);\n }\n last(amount) {\n const arr = this.array();\n if (typeof amount === 'undefined')\n return arr[arr.length - 1];\n if (amount < 0)\n return this.first(amount * -1);\n if (!amount)\n return [];\n return arr.slice(-amount);\n }\n lastKey(amount) {\n const arr = this.keyArray();\n if (typeof amount === 'undefined')\n return arr[arr.length - 1];\n if (amount < 0)\n return this.firstKey(amount * -1);\n if (!amount)\n return [];\n return arr.slice(-amount);\n }\n random(amount) {\n let arr = this.array();\n if (typeof amount === 'undefined')\n return arr[Math.floor(Math.random() * arr.length)];\n if (arr.length === 0 || !amount)\n return [];\n arr = arr.slice();\n return Array.from({ length: amount }, () => arr.splice(Math.floor(Math.random() * arr.length), 1)[0]);\n }\n randomKey(amount) {\n let arr = this.keyArray();\n if (typeof amount === 'undefined')\n return arr[Math.floor(Math.random() * arr.length)];\n if (arr.length === 0 || !amount)\n return [];\n arr = arr.slice();\n return Array.from({ length: amount }, () => arr.splice(Math.floor(Math.random() * arr.length), 1)[0]);\n }\n find(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n for (const [key, val] of this) {\n if (fn(val, key, this))\n return val;\n }\n return undefined;\n }\n findKey(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n for (const [key, val] of this) {\n if (fn(val, key, this))\n return key;\n }\n return undefined;\n }\n sweep(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n const previousSize = this.size;\n for (const [key, val] of this) {\n if (fn(val, key, this))\n this.delete(key);\n }\n return previousSize - this.size;\n }\n filter(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n const results = new this.constructor[Symbol.species]();\n for (const [key, val] of this) {\n if (fn(val, key, this))\n results.set(key, val);\n }\n return results;\n }\n partition(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n // TODO: consider removing the from the constructors after TS 3.7.0 is released, as it infers it\n const results = [new this.constructor[Symbol.species](), new this.constructor[Symbol.species]()];\n for (const [key, val] of this) {\n if (fn(val, key, this)) {\n results[0].set(key, val);\n }\n else {\n results[1].set(key, val);\n }\n }\n return results;\n }\n flatMap(fn, thisArg) {\n const collections = this.map(fn, thisArg);\n return new this.constructor[Symbol.species]().concat(...collections);\n }\n map(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n const iter = this.entries();\n return Array.from({ length: this.size }, () => {\n const [key, value] = iter.next().value;\n return fn(value, key, this);\n });\n }\n mapValues(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n const coll = new this.constructor[Symbol.species]();\n for (const [key, val] of this)\n coll.set(key, fn(val, key, this));\n return coll;\n }\n some(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n for (const [key, val] of this) {\n if (fn(val, key, this))\n return true;\n }\n return false;\n }\n every(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n for (const [key, val] of this) {\n if (!fn(val, key, this))\n return false;\n }\n return true;\n }\n /**\n * Applies a function to produce a single value. Identical in behavior to\n * [Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).\n * @param {Function} fn Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`,\n * and `collection`\n * @param {*} [initialValue] Starting value for the accumulator\n * @returns {*}\n * @example collection.reduce((acc, guild) => acc + guild.memberCount, 0);\n */\n reduce(fn, initialValue) {\n let accumulator;\n if (typeof initialValue !== 'undefined') {\n accumulator = initialValue;\n for (const [key, val] of this)\n accumulator = fn(accumulator, val, key, this);\n return accumulator;\n }\n let first = true;\n for (const [key, val] of this) {\n if (first) {\n accumulator = val;\n first = false;\n continue;\n }\n accumulator = fn(accumulator, val, key, this);\n }\n // No items iterated.\n if (first) {\n throw new TypeError('Reduce of empty collection with no initial value');\n }\n return accumulator;\n }\n each(fn, thisArg) {\n this.forEach(fn, thisArg);\n return this;\n }\n tap(fn, thisArg) {\n if (typeof thisArg !== 'undefined')\n fn = fn.bind(thisArg);\n fn(this);\n return this;\n }\n /**\n * Creates an identical shallow copy of this collection.\n * @returns {Collection}\n * @example const newColl = someColl.clone();\n */\n clone() {\n return new this.constructor[Symbol.species](this);\n }\n /**\n * Combines this collection with others into a new collection. None of the source collections are modified.\n * @param {...Collection} collections Collections to merge\n * @returns {Collection}\n * @example const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl);\n */\n concat(...collections) {\n const newColl = this.clone();\n for (const coll of collections) {\n for (const [key, val] of coll)\n newColl.set(key, val);\n }\n return newColl;\n }\n /**\n * Checks if this collection shares identical items with another.\n * This is different to checking for equality using equal-signs, because\n * the collections may be different objects, but contain the same data.\n * @param {Collection} collection Collection to compare with\n * @returns {boolean} Whether the collections have identical contents\n */\n equals(collection) {\n if (!collection)\n return false;\n if (this === collection)\n return true;\n if (this.size !== collection.size)\n return false;\n for (const [key, value] of this) {\n if (!collection.has(key) || value !== collection.get(key)) {\n return false;\n }\n }\n return true;\n }\n /**\n * The sort method sorts the items of a collection in place and returns it.\n * The sort is not necessarily stable in Node 10 or older.\n * The default sort order is according to string Unicode code points.\n * @param {Function} [compareFunction] Specifies a function that defines the sort order.\n * If omitted, the collection is sorted according to each character's Unicode code point value,\n * according to the string conversion of each element.\n * @returns {Collection}\n * @example collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp);\n */\n sort(compareFunction = (x, y) => Number(x > y) || Number(x === y) - 1) {\n const entries = [...this.entries()];\n entries.sort((a, b) => compareFunction(a[1], b[1], a[0], b[0]));\n // Perform clean-up\n super.clear();\n this._array = null;\n this._keyArray = null;\n // Set the new entries\n for (const [k, v] of entries) {\n super.set(k, v);\n }\n return this;\n }\n /**\n * The intersect method returns a new structure containing items where the keys are present in both original structures.\n * @param {Collection} other The other Collection to filter against\n * @returns {Collection}\n */\n intersect(other) {\n return other.filter((_, k) => this.has(k));\n }\n /**\n * The difference method returns a new structure containing items where the key is present in one of the original structures but not the other.\n * @param {Collection} other The other Collection to filter against\n * @returns {Collection}\n */\n difference(other) {\n return other.filter((_, k) => !this.has(k)).concat(this.filter((_, k) => !other.has(k)));\n }\n /**\n * The sorted method sorts the items of a collection and returns it.\n * The sort is not necessarily stable in Node 10 or older.\n * The default sort order is according to string Unicode code points.\n * @param {Function} [compareFunction] Specifies a function that defines the sort order.\n * If omitted, the collection is sorted according to each character's Unicode code point value,\n * according to the string conversion of each element.\n * @returns {Collection}\n * @example collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp);\n */\n sorted(compareFunction = (x, y) => Number(x > y) || Number(x === y) - 1) {\n return new this.constructor[Symbol.species]([...this.entries()])\n .sort((av, bv, ak, bk) => compareFunction(av, bv, ak, bk));\n }\n}\nexports.Collection = Collection;\nCollection.default = Collection;\nmodule.exports = Collection;\nexports.default = Collection;\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiLyIsInNvdXJjZXMiOlsiaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBUUE7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQWlCLFNBQVEsR0FBUztJQU12QyxZQUFtQixPQUErQztRQUNqRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFZjs7Ozs7V0FLRztRQUNILE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUUzRjs7Ozs7V0FLRztRQUNILE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLFdBQVcsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvRixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxHQUFHLENBQUMsR0FBTTtRQUNoQixPQUFPLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEdBQUcsQ0FBQyxHQUFNLEVBQUUsS0FBUTtRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztRQUN0QixPQUFPLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEdBQUcsQ0FBQyxHQUFNO1FBQ2hCLE9BQU8sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN2QixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxNQUFNLENBQUMsR0FBTTtRQUNuQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztRQUN0QixPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLO1FBQ1gsT0FBTyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEtBQUs7UUFDWCxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxJQUFJLENBQUMsSUFBSTtZQUFFLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZGLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNwQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksUUFBUTtRQUNkLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxJQUFJO1lBQUUsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDOUYsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3ZCLENBQUM7SUFVTSxLQUFLLENBQUMsTUFBZTtRQUMzQixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDckUsSUFBSSxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxNQUFNLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUMzQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFVTSxRQUFRLENBQUMsTUFBZTtRQUM5QixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDbkUsSUFBSSxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNqRCxNQUFNLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN6QixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFXTSxJQUFJLENBQUMsTUFBZTtRQUMxQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDekIsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXO1lBQUUsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztRQUM5RCxJQUFJLE1BQU0sR0FBRyxDQUFDO1lBQUUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9DLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDdkIsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDM0IsQ0FBQztJQVdNLE9BQU8sQ0FBQyxNQUFlO1FBQzdCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUM1QixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQzlELElBQUksTUFBTSxHQUFHLENBQUM7WUFBRSxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEVBQUUsQ0FBQztRQUN2QixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzQixDQUFDO0lBVU0sTUFBTSxDQUFDLE1BQWU7UUFDNUIsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3ZCLElBQUksT0FBTyxNQUFNLEtBQUssV0FBVztZQUFFLE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3RGLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDM0MsR0FBRyxHQUFHLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNsQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRyxDQUFDO0lBVU0sU0FBUyxDQUFDLE1BQWU7UUFDL0IsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzFCLElBQUksT0FBTyxNQUFNLEtBQUssV0FBVztZQUFFLE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3RGLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDM0MsR0FBRyxHQUFHLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNsQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBTSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRyxDQUFDO0lBZU0sSUFBSSxDQUFDLEVBQW1ELEVBQUUsT0FBaUI7UUFDakYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRTtZQUM5QixJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxPQUFPLEdBQUcsQ0FBQztTQUNuQztRQUNELE9BQU8sU0FBUyxDQUFDO0lBQ2xCLENBQUM7SUFhTSxPQUFPLENBQUMsRUFBbUQsRUFBRSxPQUFpQjtRQUNwRixJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVc7WUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMxRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQzlCLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDO2dCQUFFLE9BQU8sR0FBRyxDQUFDO1NBQ25DO1FBQ0QsT0FBTyxTQUFTLENBQUM7SUFDbEIsQ0FBQztJQVVNLEtBQUssQ0FBQyxFQUFtRCxFQUFFLE9BQWlCO1FBQ2xGLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDL0IsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRTtZQUM5QixJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3pDO1FBQ0QsT0FBTyxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQztJQUNqQyxDQUFDO0lBYU0sTUFBTSxDQUFDLEVBQW1ELEVBQUUsT0FBaUI7UUFDbkYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBZ0IsQ0FBQztRQUNyRSxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQzlCLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDO2dCQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1NBQzlDO1FBQ0QsT0FBTyxPQUFPLENBQUM7SUFDaEIsQ0FBQztJQVlNLFNBQVMsQ0FBQyxFQUFtRCxFQUFFLE9BQWlCO1FBQ3RGLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELHVHQUF1RztRQUN2RyxNQUFNLE9BQU8sR0FBaUIsQ0FBQyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFnQixFQUFFLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQWdCLENBQUMsQ0FBQztRQUMzSSxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQzlCLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLEVBQUU7Z0JBQ3ZCLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQ3pCO2lCQUFNO2dCQUNOLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQ3pCO1NBQ0Q7UUFDRCxPQUFPLE9BQU8sQ0FBQztJQUNoQixDQUFDO0lBWU0sT0FBTyxDQUFJLEVBQTRELEVBQUUsT0FBaUI7UUFDaEcsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDMUMsT0FBUSxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUE2QixDQUFDLE1BQU0sQ0FBQyxHQUFHLFdBQVcsQ0FBQyxDQUFDO0lBQ2xHLENBQUM7SUFZTSxHQUFHLENBQUksRUFBNkMsRUFBRSxPQUFpQjtRQUM3RSxJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVc7WUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMxRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDNUIsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFNLEVBQUU7WUFDaEQsTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDO1lBQ3ZDLE9BQU8sRUFBRSxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDN0IsQ0FBQyxDQUFDLENBQUM7SUFDSixDQUFDO0lBWU0sU0FBUyxDQUFJLEVBQTZDLEVBQUUsT0FBaUI7UUFDbkYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBNEIsQ0FBQztRQUM5RSxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSTtZQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDakUsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBWU0sSUFBSSxDQUFDLEVBQW1ELEVBQUUsT0FBaUI7UUFDakYsSUFBSSxPQUFPLE9BQU8sS0FBSyxXQUFXO1lBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRTtZQUM5QixJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxPQUFPLElBQUksQ0FBQztTQUNwQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQVlNLEtBQUssQ0FBQyxFQUFtRCxFQUFFLE9BQWlCO1FBQ2xGLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUU7WUFDOUIsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQztnQkFBRSxPQUFPLEtBQUssQ0FBQztTQUN0QztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksTUFBTSxDQUFJLEVBQTZELEVBQUUsWUFBZ0I7UUFDL0YsSUFBSSxXQUFlLENBQUM7UUFFcEIsSUFBSSxPQUFPLFlBQVksS0FBSyxXQUFXLEVBQUU7WUFDeEMsV0FBVyxHQUFHLFlBQVksQ0FBQztZQUMzQixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSTtnQkFBRSxXQUFXLEdBQUcsRUFBRSxDQUFDLFdBQVcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzdFLE9BQU8sV0FBVyxDQUFDO1NBQ25CO1FBQ0QsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUU7WUFDOUIsSUFBSSxLQUFLLEVBQUU7Z0JBQ1YsV0FBVyxHQUFHLEdBQW1CLENBQUM7Z0JBQ2xDLEtBQUssR0FBRyxLQUFLLENBQUM7Z0JBQ2QsU0FBUzthQUNUO1lBQ0QsV0FBVyxHQUFHLEVBQUUsQ0FBQyxXQUFXLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztTQUM5QztRQUVELHFCQUFxQjtRQUNyQixJQUFJLEtBQUssRUFBRTtZQUNWLE1BQU0sSUFBSSxTQUFTLENBQUMsa0RBQWtELENBQUMsQ0FBQztTQUN4RTtRQUVELE9BQU8sV0FBVyxDQUFDO0lBQ3BCLENBQUM7SUFpQk0sSUFBSSxDQUFDLEVBQWdELEVBQUUsT0FBaUI7UUFDOUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFnRCxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3hFLE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQWVNLEdBQUcsQ0FBQyxFQUE4QixFQUFFLE9BQWlCO1FBQzNELElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztZQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNULE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLO1FBQ1gsT0FBTyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBUyxDQUFDO0lBQzNELENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLE1BQU0sQ0FBQyxHQUFHLFdBQStCO1FBQy9DLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixLQUFLLE1BQU0sSUFBSSxJQUFJLFdBQVcsRUFBRTtZQUMvQixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksSUFBSTtnQkFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztTQUNyRDtRQUNELE9BQU8sT0FBTyxDQUFDO0lBQ2hCLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxNQUFNLENBQUMsVUFBNEI7UUFDekMsSUFBSSxDQUFDLFVBQVU7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUM5QixJQUFJLElBQUksS0FBSyxVQUFVO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDckMsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFVBQVUsQ0FBQyxJQUFJO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFDaEQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLElBQUksRUFBRTtZQUNoQyxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLEtBQUssVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRTtnQkFDMUQsT0FBTyxLQUFLLENBQUM7YUFDYjtTQUNEO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQ7Ozs7Ozs7OztPQVNHO0lBQ0ksSUFBSSxDQUFDLGtCQUF3RixDQUFDLENBQUMsRUFBRSxDQUFDLEVBQVUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQ3pKLE1BQU0sT0FBTyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNwQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBVSxFQUFFLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFeEUsbUJBQW1CO1FBQ25CLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1FBQ25CLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBRXRCLHNCQUFzQjtRQUN0QixLQUFLLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksT0FBTyxFQUFFO1lBQzdCLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1NBQ2hCO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFNBQVMsQ0FBQyxLQUF1QjtRQUN2QyxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxVQUFVLENBQUMsS0FBdUI7UUFDeEMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFGLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSSxNQUFNLENBQUMsa0JBQXdGLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBVSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDM0osT0FBUSxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBVTthQUN4RSxJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzdELENBQUM7O0FBSU8sZ0NBQVU7QUFwakJLLGtCQUFPLEdBQXNCLFVBQVUsQ0FBQztBQW1qQmhFLE1BQU0sQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDO0FBRTVCLGtCQUFlLFVBQVUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBpbnRlcmZhY2UgQ29sbGVjdGlvbkNvbnN0cnVjdG9yIHtcblx0bmV3KCk6IENvbGxlY3Rpb248dW5rbm93biwgdW5rbm93bj47XG5cdG5ldzxLLCBWPihlbnRyaWVzPzogUmVhZG9ubHlBcnJheTxyZWFkb25seSBbSywgVl0+IHwgbnVsbCk6IENvbGxlY3Rpb248SywgVj47XG5cdG5ldzxLLCBWPihpdGVyYWJsZTogSXRlcmFibGU8cmVhZG9ubHkgW0ssIFZdPik6IENvbGxlY3Rpb248SywgVj47XG5cdHJlYWRvbmx5IHByb3RvdHlwZTogQ29sbGVjdGlvbjx1bmtub3duLCB1bmtub3duPjtcblx0cmVhZG9ubHkgW1N5bWJvbC5zcGVjaWVzXTogQ29sbGVjdGlvbkNvbnN0cnVjdG9yO1xufVxuXG4vKipcbiAqIEEgTWFwIHdpdGggYWRkaXRpb25hbCB1dGlsaXR5IG1ldGhvZHMuIFRoaXMgaXMgdXNlZCB0aHJvdWdob3V0IGRpc2NvcmQuanMgcmF0aGVyIHRoYW4gQXJyYXlzIGZvciBhbnl0aGluZyB0aGF0IGhhc1xuICogYW4gSUQsIGZvciBzaWduaWZpY2FudGx5IGltcHJvdmVkIHBlcmZvcm1hbmNlIGFuZCBlYXNlLW9mLXVzZS5cbiAqIEBleHRlbmRzIHtNYXB9XG4gKiBAcHJvcGVydHkge251bWJlcn0gc2l6ZSAtIFRoZSBhbW91bnQgb2YgZWxlbWVudHMgaW4gdGhpcyBjb2xsZWN0aW9uLlxuICovXG5jbGFzcyBDb2xsZWN0aW9uPEssIFY+IGV4dGVuZHMgTWFwPEssIFY+IHtcblx0cHJpdmF0ZSBfYXJyYXkhOiBWW10gfCBudWxsO1xuXHRwcml2YXRlIF9rZXlBcnJheSE6IEtbXSB8IG51bGw7XG5cdHB1YmxpYyBzdGF0aWMgcmVhZG9ubHkgZGVmYXVsdDogdHlwZW9mIENvbGxlY3Rpb24gPSBDb2xsZWN0aW9uO1xuXHRwdWJsaWMgWydjb25zdHJ1Y3RvciddOiB0eXBlb2YgQ29sbGVjdGlvbjtcblxuXHRwdWJsaWMgY29uc3RydWN0b3IoZW50cmllcz86IFJlYWRvbmx5QXJyYXk8cmVhZG9ubHkgW0ssIFZdPiB8IG51bGwpIHtcblx0XHRzdXBlcihlbnRyaWVzKTtcblxuXHRcdC8qKlxuXHRcdCAqIENhY2hlZCBhcnJheSBmb3IgdGhlIGBhcnJheSgpYCBtZXRob2QgLSB3aWxsIGJlIHJlc2V0IHRvIGBudWxsYCB3aGVuZXZlciBgc2V0KClgIG9yIGBkZWxldGUoKWAgYXJlIGNhbGxlZFxuXHRcdCAqIEBuYW1lIENvbGxlY3Rpb24jX2FycmF5XG5cdFx0ICogQHR5cGUgez9BcnJheX1cblx0XHQgKiBAcHJpdmF0ZVxuXHRcdCAqL1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnX2FycmF5JywgeyB2YWx1ZTogbnVsbCwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9KTtcblxuXHRcdC8qKlxuXHRcdCAqIENhY2hlZCBhcnJheSBmb3IgdGhlIGBrZXlBcnJheSgpYCBtZXRob2QgLSB3aWxsIGJlIHJlc2V0IHRvIGBudWxsYCB3aGVuZXZlciBgc2V0KClgIG9yIGBkZWxldGUoKWAgYXJlIGNhbGxlZFxuXHRcdCAqIEBuYW1lIENvbGxlY3Rpb24jX2tleUFycmF5XG5cdFx0ICogQHR5cGUgez9BcnJheX1cblx0XHQgKiBAcHJpdmF0ZVxuXHRcdCAqL1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnX2tleUFycmF5JywgeyB2YWx1ZTogbnVsbCwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBJZGVudGljYWwgdG8gW01hcC5nZXQoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL2dldCkuXG5cdCAqIEdldHMgYW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQga2V5LCBhbmQgcmV0dXJucyBpdHMgdmFsdWUsIG9yIGB1bmRlZmluZWRgIGlmIHRoZSBlbGVtZW50IGRvZXMgbm90IGV4aXN0LlxuXHQgKiBAcGFyYW0geyp9IGtleSAtIFRoZSBrZXkgdG8gZ2V0IGZyb20gdGhpcyBjb2xsZWN0aW9uXG5cdCAqIEByZXR1cm5zIHsqIHwgdW5kZWZpbmVkfVxuXHQgKi9cblx0cHVibGljIGdldChrZXk6IEspOiBWIHwgdW5kZWZpbmVkIHtcblx0XHRyZXR1cm4gc3VwZXIuZ2V0KGtleSk7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvIFtNYXAuc2V0KCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcC9zZXQpLlxuXHQgKiBTZXRzIGEgbmV3IGVsZW1lbnQgaW4gdGhlIGNvbGxlY3Rpb24gd2l0aCB0aGUgc3BlY2lmaWVkIGtleSBhbmQgdmFsdWUuXG5cdCAqIEBwYXJhbSB7Kn0ga2V5IC0gVGhlIGtleSBvZiB0aGUgZWxlbWVudCB0byBhZGRcblx0ICogQHBhcmFtIHsqfSB2YWx1ZSAtIFRoZSB2YWx1ZSBvZiB0aGUgZWxlbWVudCB0byBhZGRcblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqL1xuXHRwdWJsaWMgc2V0KGtleTogSywgdmFsdWU6IFYpOiB0aGlzIHtcblx0XHR0aGlzLl9hcnJheSA9IG51bGw7XG5cdFx0dGhpcy5fa2V5QXJyYXkgPSBudWxsO1xuXHRcdHJldHVybiBzdXBlci5zZXQoa2V5LCB2YWx1ZSk7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvIFtNYXAuaGFzKCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcC9oYXMpLlxuXHQgKiBDaGVja3MgaWYgYW4gZWxlbWVudCBleGlzdHMgaW4gdGhlIGNvbGxlY3Rpb24uXG5cdCAqIEBwYXJhbSB7Kn0ga2V5IC0gVGhlIGtleSBvZiB0aGUgZWxlbWVudCB0byBjaGVjayBmb3Jcblx0ICogQHJldHVybnMge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgZWxlbWVudCBleGlzdHMsIGBmYWxzZWAgaWYgaXQgZG9lcyBub3QgZXhpc3QuXG5cdCAqL1xuXHRwdWJsaWMgaGFzKGtleTogSyk6IGJvb2xlYW4ge1xuXHRcdHJldHVybiBzdXBlci5oYXMoa2V5KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBJZGVudGljYWwgdG8gW01hcC5kZWxldGUoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL2RlbGV0ZSkuXG5cdCAqIERlbGV0ZXMgYW4gZWxlbWVudCBmcm9tIHRoZSBjb2xsZWN0aW9uLlxuXHQgKiBAcGFyYW0geyp9IGtleSAtIFRoZSBrZXkgdG8gZGVsZXRlIGZyb20gdGhlIGNvbGxlY3Rpb25cblx0ICogQHJldHVybnMge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgZWxlbWVudCB3YXMgcmVtb3ZlZCwgYGZhbHNlYCBpZiB0aGUgZWxlbWVudCBkb2VzIG5vdCBleGlzdC5cblx0ICovXG5cdHB1YmxpYyBkZWxldGUoa2V5OiBLKTogYm9vbGVhbiB7XG5cdFx0dGhpcy5fYXJyYXkgPSBudWxsO1xuXHRcdHRoaXMuX2tleUFycmF5ID0gbnVsbDtcblx0XHRyZXR1cm4gc3VwZXIuZGVsZXRlKGtleSk7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvIFtNYXAuY2xlYXIoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL2NsZWFyKS5cblx0ICogUmVtb3ZlcyBhbGwgZWxlbWVudHMgZnJvbSB0aGUgY29sbGVjdGlvbi5cblx0ICogQHJldHVybnMge3VuZGVmaW5lZH1cblx0ICovXG5cdHB1YmxpYyBjbGVhcigpOiB2b2lkIHtcblx0XHRyZXR1cm4gc3VwZXIuY2xlYXIoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIG9yZGVyZWQgYXJyYXkgb2YgdGhlIHZhbHVlcyBvZiB0aGlzIGNvbGxlY3Rpb24sIGFuZCBjYWNoZXMgaXQgaW50ZXJuYWxseS4gVGhlIGFycmF5IHdpbGwgb25seSBiZVxuXHQgKiByZWNvbnN0cnVjdGVkIGlmIGFuIGl0ZW0gaXMgYWRkZWQgdG8gb3IgcmVtb3ZlZCBmcm9tIHRoZSBjb2xsZWN0aW9uLCBvciBpZiB5b3UgY2hhbmdlIHRoZSBsZW5ndGggb2YgdGhlIGFycmF5XG5cdCAqIGl0c2VsZi4gSWYgeW91IGRvbid0IHdhbnQgdGhpcyBjYWNoaW5nIGJlaGF2aW9yLCB1c2UgYFsuLi5jb2xsZWN0aW9uLnZhbHVlcygpXWAgb3Jcblx0ICogYEFycmF5LmZyb20oY29sbGVjdGlvbi52YWx1ZXMoKSlgIGluc3RlYWQuXG5cdCAqIEByZXR1cm5zIHtBcnJheX1cblx0ICovXG5cdHB1YmxpYyBhcnJheSgpOiBWW10ge1xuXHRcdGlmICghdGhpcy5fYXJyYXkgfHwgdGhpcy5fYXJyYXkubGVuZ3RoICE9PSB0aGlzLnNpemUpIHRoaXMuX2FycmF5ID0gWy4uLnRoaXMudmFsdWVzKCldO1xuXHRcdHJldHVybiB0aGlzLl9hcnJheTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIG9yZGVyZWQgYXJyYXkgb2YgdGhlIGtleXMgb2YgdGhpcyBjb2xsZWN0aW9uLCBhbmQgY2FjaGVzIGl0IGludGVybmFsbHkuIFRoZSBhcnJheSB3aWxsIG9ubHkgYmVcblx0ICogcmVjb25zdHJ1Y3RlZCBpZiBhbiBpdGVtIGlzIGFkZGVkIHRvIG9yIHJlbW92ZWQgZnJvbSB0aGUgY29sbGVjdGlvbiwgb3IgaWYgeW91IGNoYW5nZSB0aGUgbGVuZ3RoIG9mIHRoZSBhcnJheVxuXHQgKiBpdHNlbGYuIElmIHlvdSBkb24ndCB3YW50IHRoaXMgY2FjaGluZyBiZWhhdmlvciwgdXNlIGBbLi4uY29sbGVjdGlvbi5rZXlzKCldYCBvclxuXHQgKiBgQXJyYXkuZnJvbShjb2xsZWN0aW9uLmtleXMoKSlgIGluc3RlYWQuXG5cdCAqIEByZXR1cm5zIHtBcnJheX1cblx0ICovXG5cdHB1YmxpYyBrZXlBcnJheSgpOiBLW10ge1xuXHRcdGlmICghdGhpcy5fa2V5QXJyYXkgfHwgdGhpcy5fa2V5QXJyYXkubGVuZ3RoICE9PSB0aGlzLnNpemUpIHRoaXMuX2tleUFycmF5ID0gWy4uLnRoaXMua2V5cygpXTtcblx0XHRyZXR1cm4gdGhpcy5fa2V5QXJyYXk7XG5cdH1cblxuXHQvKipcblx0ICogT2J0YWlucyB0aGUgZmlyc3QgdmFsdWUocykgaW4gdGhpcyBjb2xsZWN0aW9uLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gW2Ftb3VudF0gQW1vdW50IG9mIHZhbHVlcyB0byBvYnRhaW4gZnJvbSB0aGUgYmVnaW5uaW5nXG5cdCAqIEByZXR1cm5zIHsqfEFycmF5PCo+fSBBIHNpbmdsZSB2YWx1ZSBpZiBubyBhbW91bnQgaXMgcHJvdmlkZWQgb3IgYW4gYXJyYXkgb2YgdmFsdWVzLCBzdGFydGluZyBmcm9tIHRoZSBlbmQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgZmlyc3QoKTogViB8IHVuZGVmaW5lZDtcblx0cHVibGljIGZpcnN0KGFtb3VudDogbnVtYmVyKTogVltdO1xuXHRwdWJsaWMgZmlyc3QoYW1vdW50PzogbnVtYmVyKTogViB8IFZbXSB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiBhbW91bnQgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdGhpcy52YWx1ZXMoKS5uZXh0KCkudmFsdWU7XG5cdFx0aWYgKGFtb3VudCA8IDApIHJldHVybiB0aGlzLmxhc3QoYW1vdW50ICogLTEpO1xuXHRcdGFtb3VudCA9IE1hdGgubWluKHRoaXMuc2l6ZSwgYW1vdW50KTtcblx0XHRjb25zdCBpdGVyID0gdGhpcy52YWx1ZXMoKTtcblx0XHRyZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogYW1vdW50IH0sICgpOiBWID0+IGl0ZXIubmV4dCgpLnZhbHVlKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBPYnRhaW5zIHRoZSBmaXJzdCBrZXkocykgaW4gdGhpcyBjb2xsZWN0aW9uLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gW2Ftb3VudF0gQW1vdW50IG9mIGtleXMgdG8gb2J0YWluIGZyb20gdGhlIGJlZ2lubmluZ1xuXHQgKiBAcmV0dXJucyB7KnxBcnJheTwqPn0gQSBzaW5nbGUga2V5IGlmIG5vIGFtb3VudCBpcyBwcm92aWRlZCBvciBhbiBhcnJheSBvZiBrZXlzLCBzdGFydGluZyBmcm9tIHRoZSBlbmQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgZmlyc3RLZXkoKTogSyB8IHVuZGVmaW5lZDtcblx0cHVibGljIGZpcnN0S2V5KGFtb3VudDogbnVtYmVyKTogS1tdO1xuXHRwdWJsaWMgZmlyc3RLZXkoYW1vdW50PzogbnVtYmVyKTogSyB8IEtbXSB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiBhbW91bnQgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdGhpcy5rZXlzKCkubmV4dCgpLnZhbHVlO1xuXHRcdGlmIChhbW91bnQgPCAwKSByZXR1cm4gdGhpcy5sYXN0S2V5KGFtb3VudCAqIC0xKTtcblx0XHRhbW91bnQgPSBNYXRoLm1pbih0aGlzLnNpemUsIGFtb3VudCk7XG5cdFx0Y29uc3QgaXRlciA9IHRoaXMua2V5cygpO1xuXHRcdHJldHVybiBBcnJheS5mcm9tKHsgbGVuZ3RoOiBhbW91bnQgfSwgKCk6IEsgPT4gaXRlci5uZXh0KCkudmFsdWUpO1xuXHR9XG5cblx0LyoqXG5cdCAqIE9idGFpbnMgdGhlIGxhc3QgdmFsdWUocykgaW4gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNhcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiB2YWx1ZXMgdG8gb2J0YWluIGZyb20gdGhlIGVuZFxuXHQgKiBAcmV0dXJucyB7KnxBcnJheTwqPn0gQSBzaW5nbGUgdmFsdWUgaWYgbm8gYW1vdW50IGlzIHByb3ZpZGVkIG9yIGFuIGFycmF5IG9mIHZhbHVlcywgc3RhcnRpbmcgZnJvbSB0aGUgc3RhcnQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgbGFzdCgpOiBWIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgbGFzdChhbW91bnQ6IG51bWJlcik6IFZbXTtcblx0cHVibGljIGxhc3QoYW1vdW50PzogbnVtYmVyKTogViB8IFZbXSB8IHVuZGVmaW5lZCB7XG5cdFx0Y29uc3QgYXJyID0gdGhpcy5hcnJheSgpO1xuXHRcdGlmICh0eXBlb2YgYW1vdW50ID09PSAndW5kZWZpbmVkJykgcmV0dXJuIGFyclthcnIubGVuZ3RoIC0gMV07XG5cdFx0aWYgKGFtb3VudCA8IDApIHJldHVybiB0aGlzLmZpcnN0KGFtb3VudCAqIC0xKTtcblx0XHRpZiAoIWFtb3VudCkgcmV0dXJuIFtdO1xuXHRcdHJldHVybiBhcnIuc2xpY2UoLWFtb3VudCk7XG5cdH1cblxuXHQvKipcblx0ICogT2J0YWlucyB0aGUgbGFzdCBrZXkocykgaW4gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNrZXlBcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiBrZXlzIHRvIG9idGFpbiBmcm9tIHRoZSBlbmRcblx0ICogQHJldHVybnMgeyp8QXJyYXk8Kj59IEEgc2luZ2xlIGtleSBpZiBubyBhbW91bnQgaXMgcHJvdmlkZWQgb3IgYW4gYXJyYXkgb2Yga2V5cywgc3RhcnRpbmcgZnJvbSB0aGUgc3RhcnQgaWZcblx0ICogYW1vdW50IGlzIG5lZ2F0aXZlXG5cdCAqL1xuXHRwdWJsaWMgbGFzdEtleSgpOiBLIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgbGFzdEtleShhbW91bnQ6IG51bWJlcik6IEtbXTtcblx0cHVibGljIGxhc3RLZXkoYW1vdW50PzogbnVtYmVyKTogSyB8IEtbXSB8IHVuZGVmaW5lZCB7XG5cdFx0Y29uc3QgYXJyID0gdGhpcy5rZXlBcnJheSgpO1xuXHRcdGlmICh0eXBlb2YgYW1vdW50ID09PSAndW5kZWZpbmVkJykgcmV0dXJuIGFyclthcnIubGVuZ3RoIC0gMV07XG5cdFx0aWYgKGFtb3VudCA8IDApIHJldHVybiB0aGlzLmZpcnN0S2V5KGFtb3VudCAqIC0xKTtcblx0XHRpZiAoIWFtb3VudCkgcmV0dXJuIFtdO1xuXHRcdHJldHVybiBhcnIuc2xpY2UoLWFtb3VudCk7XG5cdH1cblxuXHQvKipcblx0ICogT2J0YWlucyB1bmlxdWUgcmFuZG9tIHZhbHVlKHMpIGZyb20gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNhcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiB2YWx1ZXMgdG8gb2J0YWluIHJhbmRvbWx5XG5cdCAqIEByZXR1cm5zIHsqfEFycmF5PCo+fSBBIHNpbmdsZSB2YWx1ZSBpZiBubyBhbW91bnQgaXMgcHJvdmlkZWQgb3IgYW4gYXJyYXkgb2YgdmFsdWVzXG5cdCAqL1xuXHRwdWJsaWMgcmFuZG9tKCk6IFY7XG5cdHB1YmxpYyByYW5kb20oYW1vdW50OiBudW1iZXIpOiBWW107XG5cdHB1YmxpYyByYW5kb20oYW1vdW50PzogbnVtYmVyKTogViB8IFZbXSB7XG5cdFx0bGV0IGFyciA9IHRoaXMuYXJyYXkoKTtcblx0XHRpZiAodHlwZW9mIGFtb3VudCA9PT0gJ3VuZGVmaW5lZCcpIHJldHVybiBhcnJbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYXJyLmxlbmd0aCldO1xuXHRcdGlmIChhcnIubGVuZ3RoID09PSAwIHx8ICFhbW91bnQpIHJldHVybiBbXTtcblx0XHRhcnIgPSBhcnIuc2xpY2UoKTtcblx0XHRyZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogYW1vdW50IH0sICgpOiBWID0+IGFyci5zcGxpY2UoTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYXJyLmxlbmd0aCksIDEpWzBdKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBPYnRhaW5zIHVuaXF1ZSByYW5kb20ga2V5KHMpIGZyb20gdGhpcyBjb2xsZWN0aW9uLiBUaGlzIHJlbGllcyBvbiB7QGxpbmsgQ29sbGVjdGlvbiNrZXlBcnJheX0sIGFuZCB0aHVzIHRoZSBjYWNoaW5nXG5cdCAqIG1lY2hhbmlzbSBhcHBsaWVzIGhlcmUgYXMgd2VsbC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IFthbW91bnRdIEFtb3VudCBvZiBrZXlzIHRvIG9idGFpbiByYW5kb21seVxuXHQgKiBAcmV0dXJucyB7KnxBcnJheTwqPn0gQSBzaW5nbGUga2V5IGlmIG5vIGFtb3VudCBpcyBwcm92aWRlZCBvciBhbiBhcnJheVxuXHQgKi9cblx0cHVibGljIHJhbmRvbUtleSgpOiBLO1xuXHRwdWJsaWMgcmFuZG9tS2V5KGFtb3VudDogbnVtYmVyKTogS1tdO1xuXHRwdWJsaWMgcmFuZG9tS2V5KGFtb3VudD86IG51bWJlcik6IEsgfCBLW10ge1xuXHRcdGxldCBhcnIgPSB0aGlzLmtleUFycmF5KCk7XG5cdFx0aWYgKHR5cGVvZiBhbW91bnQgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gYXJyW01hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFyci5sZW5ndGgpXTtcblx0XHRpZiAoYXJyLmxlbmd0aCA9PT0gMCB8fCAhYW1vdW50KSByZXR1cm4gW107XG5cdFx0YXJyID0gYXJyLnNsaWNlKCk7XG5cdFx0cmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGFtb3VudCB9LCAoKTogSyA9PiBhcnIuc3BsaWNlKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFyci5sZW5ndGgpLCAxKVswXSk7XG5cdH1cblxuXHQvKipcblx0ICogU2VhcmNoZXMgZm9yIGEgc2luZ2xlIGl0ZW0gd2hlcmUgdGhlIGdpdmVuIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUuIFRoaXMgYmVoYXZlcyBsaWtlXG5cdCAqIFtBcnJheS5maW5kKCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2ZpbmQpLlxuXHQgKiA8d2Fybj5BbGwgY29sbGVjdGlvbnMgdXNlZCBpbiBEaXNjb3JkLmpzIGFyZSBtYXBwZWQgdXNpbmcgdGhlaXIgYGlkYCBwcm9wZXJ0eSwgYW5kIGlmIHlvdSB3YW50IHRvIGZpbmQgYnkgaWQgeW91XG5cdCAqIHNob3VsZCB1c2UgdGhlIGBnZXRgIG1ldGhvZC4gU2VlXG5cdCAqIFtNRE5dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcC9nZXQpIGZvciBkZXRhaWxzLjwvd2Fybj5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGZ1bmN0aW9uIHRvIHRlc3Qgd2l0aCAoc2hvdWxkIHJldHVybiBib29sZWFuKVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHsqfVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmZpbmQodXNlciA9PiB1c2VyLnVzZXJuYW1lID09PSAnQm9iJyk7XG5cdCAqL1xuXHRwdWJsaWMgZmluZChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBWIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZDxUPihmbjogKHRoaXM6IFQsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc6IFQpOiBWIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc/OiB1bmtub3duKTogViB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiB0aGlzQXJnICE9PSAndW5kZWZpbmVkJykgZm4gPSBmbi5iaW5kKHRoaXNBcmcpO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSByZXR1cm4gdmFsO1xuXHRcdH1cblx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNlYXJjaGVzIGZvciB0aGUga2V5IG9mIGEgc2luZ2xlIGl0ZW0gd2hlcmUgdGhlIGdpdmVuIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUuIFRoaXMgYmVoYXZlcyBsaWtlXG5cdCAqIFtBcnJheS5maW5kSW5kZXgoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvZmluZEluZGV4KSxcblx0ICogYnV0IHJldHVybnMgdGhlIGtleSByYXRoZXIgdGhhbiB0aGUgcG9zaXRpb25hbCBpbmRleC5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGZ1bmN0aW9uIHRvIHRlc3Qgd2l0aCAoc2hvdWxkIHJldHVybiBib29sZWFuKVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHsqfVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmZpbmRLZXkodXNlciA9PiB1c2VyLnVzZXJuYW1lID09PSAnQm9iJyk7XG5cdCAqL1xuXHRwdWJsaWMgZmluZEtleShmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBLIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZEtleTxUPihmbjogKHRoaXM6IFQsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc6IFQpOiBLIHwgdW5kZWZpbmVkO1xuXHRwdWJsaWMgZmluZEtleShmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc/OiB1bmtub3duKTogSyB8IHVuZGVmaW5lZCB7XG5cdFx0aWYgKHR5cGVvZiB0aGlzQXJnICE9PSAndW5kZWZpbmVkJykgZm4gPSBmbi5iaW5kKHRoaXNBcmcpO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSByZXR1cm4ga2V5O1xuXHRcdH1cblx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJlbW92ZXMgaXRlbXMgdGhhdCBzYXRpc2Z5IHRoZSBwcm92aWRlZCBmaWx0ZXIgZnVuY3Rpb24uXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIEZ1bmN0aW9uIHVzZWQgdG8gdGVzdCAoc2hvdWxkIHJldHVybiBhIGJvb2xlYW4pXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge251bWJlcn0gVGhlIG51bWJlciBvZiByZW1vdmVkIGVudHJpZXNcblx0ICovXG5cdHB1YmxpYyBzd2VlcChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBudW1iZXI7XG5cdHB1YmxpYyBzd2VlcDxUPihmbjogKHRoaXM6IFQsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc6IFQpOiBudW1iZXI7XG5cdHB1YmxpYyBzd2VlcChmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4sIHRoaXNBcmc/OiB1bmtub3duKTogbnVtYmVyIHtcblx0XHRpZiAodHlwZW9mIHRoaXNBcmcgIT09ICd1bmRlZmluZWQnKSBmbiA9IGZuLmJpbmQodGhpc0FyZyk7XG5cdFx0Y29uc3QgcHJldmlvdXNTaXplID0gdGhpcy5zaXplO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSB0aGlzLmRlbGV0ZShrZXkpO1xuXHRcdH1cblx0XHRyZXR1cm4gcHJldmlvdXNTaXplIC0gdGhpcy5zaXplO1xuXHR9XG5cblx0LyoqXG5cdCAqIElkZW50aWNhbCB0b1xuXHQgKiBbQXJyYXkuZmlsdGVyKCldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2ZpbHRlciksXG5cdCAqIGJ1dCByZXR1cm5zIGEgQ29sbGVjdGlvbiBpbnN0ZWFkIG9mIGFuIEFycmF5LlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBUaGUgZnVuY3Rpb24gdG8gdGVzdCB3aXRoIChzaG91bGQgcmV0dXJuIGJvb2xlYW4pXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqIEBleGFtcGxlIGNvbGxlY3Rpb24uZmlsdGVyKHVzZXIgPT4gdXNlci51c2VybmFtZSA9PT0gJ0JvYicpO1xuXHQgKi9cblx0cHVibGljIGZpbHRlcihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiB0aGlzO1xuXHRwdWJsaWMgZmlsdGVyPFQ+KGZuOiAodGhpczogVCwgdmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gYm9vbGVhbiwgdGhpc0FyZzogVCk6IHRoaXM7XG5cdHB1YmxpYyBmaWx0ZXIoZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnPzogdW5rbm93bik6IHRoaXMge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRjb25zdCByZXN1bHRzID0gbmV3IHRoaXMuY29uc3RydWN0b3JbU3ltYm9sLnNwZWNpZXNdPEssIFY+KCkgYXMgdGhpcztcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykge1xuXHRcdFx0aWYgKGZuKHZhbCwga2V5LCB0aGlzKSkgcmVzdWx0cy5zZXQoa2V5LCB2YWwpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVzdWx0cztcblx0fVxuXG5cdC8qKlxuXHQgKiBQYXJ0aXRpb25zIHRoZSBjb2xsZWN0aW9uIGludG8gdHdvIGNvbGxlY3Rpb25zIHdoZXJlIHRoZSBmaXJzdCBjb2xsZWN0aW9uXG5cdCAqIGNvbnRhaW5zIHRoZSBpdGVtcyB0aGF0IHBhc3NlZCBhbmQgdGhlIHNlY29uZCBjb250YWlucyB0aGUgaXRlbXMgdGhhdCBmYWlsZWQuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIEZ1bmN0aW9uIHVzZWQgdG8gdGVzdCAoc2hvdWxkIHJldHVybiBhIGJvb2xlYW4pXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge0NvbGxlY3Rpb25bXX1cblx0ICogQGV4YW1wbGUgY29uc3QgW2JpZywgc21hbGxdID0gY29sbGVjdGlvbi5wYXJ0aXRpb24oZ3VpbGQgPT4gZ3VpbGQubWVtYmVyQ291bnQgPiAyNTApO1xuXHQgKi9cblx0cHVibGljIHBhcnRpdGlvbihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBbdGhpcywgdGhpc107XG5cdHB1YmxpYyBwYXJ0aXRpb248VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnOiBUKTogW3RoaXMsIHRoaXNdO1xuXHRwdWJsaWMgcGFydGl0aW9uKGZuOiAodmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gYm9vbGVhbiwgdGhpc0FyZz86IHVua25vd24pOiBbdGhpcywgdGhpc10ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHQvLyBUT0RPOiBjb25zaWRlciByZW1vdmluZyB0aGUgPEssIFY+IGZyb20gdGhlIGNvbnN0cnVjdG9ycyBhZnRlciBUUyAzLjcuMCBpcyByZWxlYXNlZCwgYXMgaXQgaW5mZXJzIGl0XG5cdFx0Y29uc3QgcmVzdWx0czogW3RoaXMsIHRoaXNdID0gW25ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXTxLLCBWPigpIGFzIHRoaXMsIG5ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXTxLLCBWPigpIGFzIHRoaXNdO1xuXHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiB0aGlzKSB7XG5cdFx0XHRpZiAoZm4odmFsLCBrZXksIHRoaXMpKSB7XG5cdFx0XHRcdHJlc3VsdHNbMF0uc2V0KGtleSwgdmFsKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJlc3VsdHNbMV0uc2V0KGtleSwgdmFsKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHJlc3VsdHM7XG5cdH1cblxuXHQvKipcblx0ICogTWFwcyBlYWNoIGl0ZW0gaW50byBhIENvbGxlY3Rpb24sIHRoZW4gam9pbnMgdGhlIHJlc3VsdHMgaW50byBhIHNpbmdsZSBDb2xsZWN0aW9uLiBJZGVudGljYWwgaW4gYmVoYXZpb3IgdG9cblx0ICogW0FycmF5LmZsYXRNYXAoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvZmxhdE1hcCkuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIEZ1bmN0aW9uIHRoYXQgcHJvZHVjZXMgYSBuZXcgQ29sbGVjdGlvblxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmZsYXRNYXAoZ3VpbGQgPT4gZ3VpbGQubWVtYmVycy5jYWNoZSk7XG5cdCAqL1xuXHRwdWJsaWMgZmxhdE1hcDxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IENvbGxlY3Rpb248SywgVD4pOiBDb2xsZWN0aW9uPEssIFQ+O1xuXHRwdWJsaWMgZmxhdE1hcDxULCBUaGlzPihmbjogKHRoaXM6IFRoaXMsIHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IENvbGxlY3Rpb248SywgVD4sIHRoaXNBcmc6IFRoaXMpOiBDb2xsZWN0aW9uPEssIFQ+O1xuXHRwdWJsaWMgZmxhdE1hcDxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IENvbGxlY3Rpb248SywgVD4sIHRoaXNBcmc/OiB1bmtub3duKTogQ29sbGVjdGlvbjxLLCBUPiB7XG5cdFx0Y29uc3QgY29sbGVjdGlvbnMgPSB0aGlzLm1hcChmbiwgdGhpc0FyZyk7XG5cdFx0cmV0dXJuIChuZXcgdGhpcy5jb25zdHJ1Y3RvcltTeW1ib2wuc3BlY2llc108SywgVD4oKSBhcyBDb2xsZWN0aW9uPEssIFQ+KS5jb25jYXQoLi4uY29sbGVjdGlvbnMpO1xuXHR9XG5cblx0LyoqXG5cdCAqIE1hcHMgZWFjaCBpdGVtIHRvIGFub3RoZXIgdmFsdWUgaW50byBhbiBhcnJheS4gSWRlbnRpY2FsIGluIGJlaGF2aW9yIHRvXG5cdCAqIFtBcnJheS5tYXAoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvbWFwKS5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdGhhdCBwcm9kdWNlcyBhbiBlbGVtZW50IG9mIHRoZSBuZXcgYXJyYXksIHRha2luZyB0aHJlZSBhcmd1bWVudHNcblx0ICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVmFsdWUgdG8gdXNlIGFzIGB0aGlzYCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvblxuXHQgKiBAcmV0dXJucyB7QXJyYXl9XG5cdCAqIEBleGFtcGxlIGNvbGxlY3Rpb24ubWFwKHVzZXIgPT4gdXNlci50YWcpO1xuXHQgKi9cblx0cHVibGljIG1hcDxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IFQpOiBUW107XG5cdHB1YmxpYyBtYXA8VGhpcywgVD4oZm46ICh0aGlzOiBUaGlzLCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBULCB0aGlzQXJnOiBUaGlzKTogVFtdO1xuXHRwdWJsaWMgbWFwPFQ+KGZuOiAodmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gVCwgdGhpc0FyZz86IHVua25vd24pOiBUW10ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRjb25zdCBpdGVyID0gdGhpcy5lbnRyaWVzKCk7XG5cdFx0cmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IHRoaXMuc2l6ZSB9LCAoKTogVCA9PiB7XG5cdFx0XHRjb25zdCBba2V5LCB2YWx1ZV0gPSBpdGVyLm5leHQoKS52YWx1ZTtcblx0XHRcdHJldHVybiBmbih2YWx1ZSwga2V5LCB0aGlzKTtcblx0XHR9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBNYXBzIGVhY2ggaXRlbSB0byBhbm90aGVyIHZhbHVlIGludG8gYSBjb2xsZWN0aW9uLiBJZGVudGljYWwgaW4gYmVoYXZpb3IgdG9cblx0ICogW0FycmF5Lm1hcCgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9tYXApLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB0aGF0IHByb2R1Y2VzIGFuIGVsZW1lbnQgb2YgdGhlIG5ldyBjb2xsZWN0aW9uLCB0YWtpbmcgdGhyZWUgYXJndW1lbnRzXG5cdCAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFZhbHVlIHRvIHVzZSBhcyBgdGhpc2Agd2hlbiBleGVjdXRpbmcgZnVuY3Rpb25cblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqIEBleGFtcGxlIGNvbGxlY3Rpb24ubWFwVmFsdWVzKHVzZXIgPT4gdXNlci50YWcpO1xuXHQgKi9cblx0cHVibGljIG1hcFZhbHVlczxUPihmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IFQpOiBDb2xsZWN0aW9uPEssIFQ+O1xuXHRwdWJsaWMgbWFwVmFsdWVzPFRoaXMsIFQ+KGZuOiAodGhpczogVGhpcywgdmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gVCwgdGhpc0FyZzogVGhpcyk6IENvbGxlY3Rpb248SywgVD47XG5cdHB1YmxpYyBtYXBWYWx1ZXM8VD4oZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBULCB0aGlzQXJnPzogdW5rbm93bik6IENvbGxlY3Rpb248SywgVD4ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRjb25zdCBjb2xsID0gbmV3IHRoaXMuY29uc3RydWN0b3JbU3ltYm9sLnNwZWNpZXNdPEssIFQ+KCkgYXMgQ29sbGVjdGlvbjxLLCBUPjtcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykgY29sbC5zZXQoa2V5LCBmbih2YWwsIGtleSwgdGhpcykpO1xuXHRcdHJldHVybiBjb2xsO1xuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGVyZSBleGlzdHMgYW4gaXRlbSB0aGF0IHBhc3NlcyBhIHRlc3QuIElkZW50aWNhbCBpbiBiZWhhdmlvciB0b1xuXHQgKiBbQXJyYXkuc29tZSgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9zb21lKS5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdXNlZCB0byB0ZXN0IChzaG91bGQgcmV0dXJuIGEgYm9vbGVhbilcblx0ICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVmFsdWUgdG8gdXNlIGFzIGB0aGlzYCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvblxuXHQgKiBAcmV0dXJucyB7Ym9vbGVhbn1cblx0ICogQGV4YW1wbGUgY29sbGVjdGlvbi5zb21lKHVzZXIgPT4gdXNlci5kaXNjcmltaW5hdG9yID09PSAnMDAwMCcpO1xuXHQgKi9cblx0cHVibGljIHNvbWUoZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuKTogYm9vbGVhbjtcblx0cHVibGljIHNvbWU8VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnOiBUKTogYm9vbGVhbjtcblx0cHVibGljIHNvbWUoZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnPzogdW5rbm93bik6IGJvb2xlYW4ge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykge1xuXHRcdFx0aWYgKGZuKHZhbCwga2V5LCB0aGlzKSkgcmV0dXJuIHRydWU7XG5cdFx0fVxuXHRcdHJldHVybiBmYWxzZTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgYWxsIGl0ZW1zIHBhc3NlcyBhIHRlc3QuIElkZW50aWNhbCBpbiBiZWhhdmlvciB0b1xuXHQgKiBbQXJyYXkuZXZlcnkoKV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvZXZlcnkpLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB1c2VkIHRvIHRlc3QgKHNob3VsZCByZXR1cm4gYSBib29sZWFuKVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHtib29sZWFufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLmV2ZXJ5KHVzZXIgPT4gIXVzZXIuYm90KTtcblx0ICovXG5cdHB1YmxpYyBldmVyeShmbjogKHZhbHVlOiBWLCBrZXk6IEssIGNvbGxlY3Rpb246IHRoaXMpID0+IGJvb2xlYW4pOiBib29sZWFuO1xuXHRwdWJsaWMgZXZlcnk8VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiBib29sZWFuLCB0aGlzQXJnOiBUKTogYm9vbGVhbjtcblx0cHVibGljIGV2ZXJ5KGZuOiAodmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gYm9vbGVhbiwgdGhpc0FyZz86IHVua25vd24pOiBib29sZWFuIHtcblx0XHRpZiAodHlwZW9mIHRoaXNBcmcgIT09ICd1bmRlZmluZWQnKSBmbiA9IGZuLmJpbmQodGhpc0FyZyk7XG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWxdIG9mIHRoaXMpIHtcblx0XHRcdGlmICghZm4odmFsLCBrZXksIHRoaXMpKSByZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdHJldHVybiB0cnVlO1xuXHR9XG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgYSBmdW5jdGlvbiB0byBwcm9kdWNlIGEgc2luZ2xlIHZhbHVlLiBJZGVudGljYWwgaW4gYmVoYXZpb3IgdG9cblx0ICogW0FycmF5LnJlZHVjZSgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9yZWR1Y2UpLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB1c2VkIHRvIHJlZHVjZSwgdGFraW5nIGZvdXIgYXJndW1lbnRzOyBgYWNjdW11bGF0b3JgLCBgY3VycmVudFZhbHVlYCwgYGN1cnJlbnRLZXlgLFxuXHQgKiBhbmQgYGNvbGxlY3Rpb25gXG5cdCAqIEBwYXJhbSB7Kn0gW2luaXRpYWxWYWx1ZV0gU3RhcnRpbmcgdmFsdWUgZm9yIHRoZSBhY2N1bXVsYXRvclxuXHQgKiBAcmV0dXJucyB7Kn1cblx0ICogQGV4YW1wbGUgY29sbGVjdGlvbi5yZWR1Y2UoKGFjYywgZ3VpbGQpID0+IGFjYyArIGd1aWxkLm1lbWJlckNvdW50LCAwKTtcblx0ICovXG5cdHB1YmxpYyByZWR1Y2U8VD4oZm46IChhY2N1bXVsYXRvcjogVCwgdmFsdWU6IFYsIGtleTogSywgY29sbGVjdGlvbjogdGhpcykgPT4gVCwgaW5pdGlhbFZhbHVlPzogVCk6IFQge1xuXHRcdGxldCBhY2N1bXVsYXRvciE6IFQ7XG5cblx0XHRpZiAodHlwZW9mIGluaXRpYWxWYWx1ZSAhPT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdGFjY3VtdWxhdG9yID0gaW5pdGlhbFZhbHVlO1xuXHRcdFx0Zm9yIChjb25zdCBba2V5LCB2YWxdIG9mIHRoaXMpIGFjY3VtdWxhdG9yID0gZm4oYWNjdW11bGF0b3IsIHZhbCwga2V5LCB0aGlzKTtcblx0XHRcdHJldHVybiBhY2N1bXVsYXRvcjtcblx0XHR9XG5cdFx0bGV0IGZpcnN0ID0gdHJ1ZTtcblx0XHRmb3IgKGNvbnN0IFtrZXksIHZhbF0gb2YgdGhpcykge1xuXHRcdFx0aWYgKGZpcnN0KSB7XG5cdFx0XHRcdGFjY3VtdWxhdG9yID0gdmFsIGFzIHVua25vd24gYXMgVDtcblx0XHRcdFx0Zmlyc3QgPSBmYWxzZTtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cdFx0XHRhY2N1bXVsYXRvciA9IGZuKGFjY3VtdWxhdG9yLCB2YWwsIGtleSwgdGhpcyk7XG5cdFx0fVxuXG5cdFx0Ly8gTm8gaXRlbXMgaXRlcmF0ZWQuXG5cdFx0aWYgKGZpcnN0KSB7XG5cdFx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCdSZWR1Y2Ugb2YgZW1wdHkgY29sbGVjdGlvbiB3aXRoIG5vIGluaXRpYWwgdmFsdWUnKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gYWNjdW11bGF0b3I7XG5cdH1cblxuXHQvKipcblx0ICogSWRlbnRpY2FsIHRvXG5cdCAqIFtNYXAuZm9yRWFjaCgpXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9NYXAvZm9yRWFjaCksXG5cdCAqIGJ1dCByZXR1cm5zIHRoZSBjb2xsZWN0aW9uIGluc3RlYWQgb2YgdW5kZWZpbmVkLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBGdW5jdGlvbiB0byBleGVjdXRlIGZvciBlYWNoIGVsZW1lbnRcblx0ICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVmFsdWUgdG8gdXNlIGFzIGB0aGlzYCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvblxuXHQgKiBAcmV0dXJucyB7Q29sbGVjdGlvbn1cblx0ICogQGV4YW1wbGVcblx0ICogY29sbGVjdGlvblxuXHQgKiAgLmVhY2godXNlciA9PiBjb25zb2xlLmxvZyh1c2VyLnVzZXJuYW1lKSlcblx0ICogIC5maWx0ZXIodXNlciA9PiB1c2VyLmJvdClcblx0ICogIC5lYWNoKHVzZXIgPT4gY29uc29sZS5sb2codXNlci51c2VybmFtZSkpO1xuXHQgKi9cblx0cHVibGljIGVhY2goZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkKTogdGhpcztcblx0cHVibGljIGVhY2g8VD4oZm46ICh0aGlzOiBULCB2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkLCB0aGlzQXJnOiBUKTogdGhpcztcblx0cHVibGljIGVhY2goZm46ICh2YWx1ZTogViwga2V5OiBLLCBjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkLCB0aGlzQXJnPzogdW5rbm93bik6IHRoaXMge1xuXHRcdHRoaXMuZm9yRWFjaChmbiBhcyAodmFsdWU6IFYsIGtleTogSywgbWFwOiBNYXA8SywgVj4pID0+IHZvaWQsIHRoaXNBcmcpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJ1bnMgYSBmdW5jdGlvbiBvbiB0aGUgY29sbGVjdGlvbiBhbmQgcmV0dXJucyB0aGUgY29sbGVjdGlvbi5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdG8gZXhlY3V0ZVxuXHQgKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBWYWx1ZSB0byB1c2UgYXMgYHRoaXNgIHdoZW4gZXhlY3V0aW5nIGZ1bmN0aW9uXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZVxuXHQgKiBjb2xsZWN0aW9uXG5cdCAqICAudGFwKGNvbGwgPT4gY29uc29sZS5sb2coY29sbC5zaXplKSlcblx0ICogIC5maWx0ZXIodXNlciA9PiB1c2VyLmJvdClcblx0ICogIC50YXAoY29sbCA9PiBjb25zb2xlLmxvZyhjb2xsLnNpemUpKVxuXHQgKi9cblx0cHVibGljIHRhcChmbjogKGNvbGxlY3Rpb246IHRoaXMpID0+IHZvaWQpOiB0aGlzO1xuXHRwdWJsaWMgdGFwPFQ+KGZuOiAodGhpczogVCwgY29sbGVjdGlvbjogdGhpcykgPT4gdm9pZCwgdGhpc0FyZzogVCk6IHRoaXM7XG5cdHB1YmxpYyB0YXAoZm46IChjb2xsZWN0aW9uOiB0aGlzKSA9PiB2b2lkLCB0aGlzQXJnPzogdW5rbm93bik6IHRoaXMge1xuXHRcdGlmICh0eXBlb2YgdGhpc0FyZyAhPT0gJ3VuZGVmaW5lZCcpIGZuID0gZm4uYmluZCh0aGlzQXJnKTtcblx0XHRmbih0aGlzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIGlkZW50aWNhbCBzaGFsbG93IGNvcHkgb2YgdGhpcyBjb2xsZWN0aW9uLlxuXHQgKiBAcmV0dXJucyB7Q29sbGVjdGlvbn1cblx0ICogQGV4YW1wbGUgY29uc3QgbmV3Q29sbCA9IHNvbWVDb2xsLmNsb25lKCk7XG5cdCAqL1xuXHRwdWJsaWMgY2xvbmUoKTogdGhpcyB7XG5cdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXSh0aGlzKSBhcyB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbWJpbmVzIHRoaXMgY29sbGVjdGlvbiB3aXRoIG90aGVycyBpbnRvIGEgbmV3IGNvbGxlY3Rpb24uIE5vbmUgb2YgdGhlIHNvdXJjZSBjb2xsZWN0aW9ucyBhcmUgbW9kaWZpZWQuXG5cdCAqIEBwYXJhbSB7Li4uQ29sbGVjdGlvbn0gY29sbGVjdGlvbnMgQ29sbGVjdGlvbnMgdG8gbWVyZ2Vcblx0ICogQHJldHVybnMge0NvbGxlY3Rpb259XG5cdCAqIEBleGFtcGxlIGNvbnN0IG5ld0NvbGwgPSBzb21lQ29sbC5jb25jYXQoc29tZU90aGVyQ29sbCwgYW5vdGhlckNvbGwsIG9oQm95QUNvbGwpO1xuXHQgKi9cblx0cHVibGljIGNvbmNhdCguLi5jb2xsZWN0aW9uczogQ29sbGVjdGlvbjxLLCBWPltdKTogdGhpcyB7XG5cdFx0Y29uc3QgbmV3Q29sbCA9IHRoaXMuY2xvbmUoKTtcblx0XHRmb3IgKGNvbnN0IGNvbGwgb2YgY29sbGVjdGlvbnMpIHtcblx0XHRcdGZvciAoY29uc3QgW2tleSwgdmFsXSBvZiBjb2xsKSBuZXdDb2xsLnNldChrZXksIHZhbCk7XG5cdFx0fVxuXHRcdHJldHVybiBuZXdDb2xsO1xuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGlzIGNvbGxlY3Rpb24gc2hhcmVzIGlkZW50aWNhbCBpdGVtcyB3aXRoIGFub3RoZXIuXG5cdCAqIFRoaXMgaXMgZGlmZmVyZW50IHRvIGNoZWNraW5nIGZvciBlcXVhbGl0eSB1c2luZyBlcXVhbC1zaWducywgYmVjYXVzZVxuXHQgKiB0aGUgY29sbGVjdGlvbnMgbWF5IGJlIGRpZmZlcmVudCBvYmplY3RzLCBidXQgY29udGFpbiB0aGUgc2FtZSBkYXRhLlxuXHQgKiBAcGFyYW0ge0NvbGxlY3Rpb259IGNvbGxlY3Rpb24gQ29sbGVjdGlvbiB0byBjb21wYXJlIHdpdGhcblx0ICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIgdGhlIGNvbGxlY3Rpb25zIGhhdmUgaWRlbnRpY2FsIGNvbnRlbnRzXG5cdCAqL1xuXHRwdWJsaWMgZXF1YWxzKGNvbGxlY3Rpb246IENvbGxlY3Rpb248SywgVj4pOiBib29sZWFuIHtcblx0XHRpZiAoIWNvbGxlY3Rpb24pIHJldHVybiBmYWxzZTtcblx0XHRpZiAodGhpcyA9PT0gY29sbGVjdGlvbikgcmV0dXJuIHRydWU7XG5cdFx0aWYgKHRoaXMuc2l6ZSAhPT0gY29sbGVjdGlvbi5zaXplKSByZXR1cm4gZmFsc2U7XG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgdGhpcykge1xuXHRcdFx0aWYgKCFjb2xsZWN0aW9uLmhhcyhrZXkpIHx8IHZhbHVlICE9PSBjb2xsZWN0aW9uLmdldChrZXkpKSB7XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIHNvcnQgbWV0aG9kIHNvcnRzIHRoZSBpdGVtcyBvZiBhIGNvbGxlY3Rpb24gaW4gcGxhY2UgYW5kIHJldHVybnMgaXQuXG5cdCAqIFRoZSBzb3J0IGlzIG5vdCBuZWNlc3NhcmlseSBzdGFibGUgaW4gTm9kZSAxMCBvciBvbGRlci5cblx0ICogVGhlIGRlZmF1bHQgc29ydCBvcmRlciBpcyBhY2NvcmRpbmcgdG8gc3RyaW5nIFVuaWNvZGUgY29kZSBwb2ludHMuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IFtjb21wYXJlRnVuY3Rpb25dIFNwZWNpZmllcyBhIGZ1bmN0aW9uIHRoYXQgZGVmaW5lcyB0aGUgc29ydCBvcmRlci5cblx0ICogSWYgb21pdHRlZCwgdGhlIGNvbGxlY3Rpb24gaXMgc29ydGVkIGFjY29yZGluZyB0byBlYWNoIGNoYXJhY3RlcidzIFVuaWNvZGUgY29kZSBwb2ludCB2YWx1ZSxcblx0ICogYWNjb3JkaW5nIHRvIHRoZSBzdHJpbmcgY29udmVyc2lvbiBvZiBlYWNoIGVsZW1lbnQuXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLnNvcnQoKHVzZXJBLCB1c2VyQikgPT4gdXNlckEuY3JlYXRlZFRpbWVzdGFtcCAtIHVzZXJCLmNyZWF0ZWRUaW1lc3RhbXApO1xuXHQgKi9cblx0cHVibGljIHNvcnQoY29tcGFyZUZ1bmN0aW9uOiAoZmlyc3RWYWx1ZTogViwgc2Vjb25kVmFsdWU6IFYsIGZpcnN0S2V5OiBLLCBzZWNvbmRLZXk6IEspID0+IG51bWJlciA9ICh4LCB5KTogbnVtYmVyID0+IE51bWJlcih4ID4geSkgfHwgTnVtYmVyKHggPT09IHkpIC0gMSk6IHRoaXMge1xuXHRcdGNvbnN0IGVudHJpZXMgPSBbLi4udGhpcy5lbnRyaWVzKCldO1xuXHRcdGVudHJpZXMuc29ydCgoYSwgYik6IG51bWJlciA9PiBjb21wYXJlRnVuY3Rpb24oYVsxXSwgYlsxXSwgYVswXSwgYlswXSkpO1xuXG5cdFx0Ly8gUGVyZm9ybSBjbGVhbi11cFxuXHRcdHN1cGVyLmNsZWFyKCk7XG5cdFx0dGhpcy5fYXJyYXkgPSBudWxsO1xuXHRcdHRoaXMuX2tleUFycmF5ID0gbnVsbDtcblxuXHRcdC8vIFNldCB0aGUgbmV3IGVudHJpZXNcblx0XHRmb3IgKGNvbnN0IFtrLCB2XSBvZiBlbnRyaWVzKSB7XG5cdFx0XHRzdXBlci5zZXQoaywgdik7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBpbnRlcnNlY3QgbWV0aG9kIHJldHVybnMgYSBuZXcgc3RydWN0dXJlIGNvbnRhaW5pbmcgaXRlbXMgd2hlcmUgdGhlIGtleXMgYXJlIHByZXNlbnQgaW4gYm90aCBvcmlnaW5hbCBzdHJ1Y3R1cmVzLlxuXHQgKiBAcGFyYW0ge0NvbGxlY3Rpb259IG90aGVyIFRoZSBvdGhlciBDb2xsZWN0aW9uIHRvIGZpbHRlciBhZ2FpbnN0XG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKi9cblx0cHVibGljIGludGVyc2VjdChvdGhlcjogQ29sbGVjdGlvbjxLLCBWPik6IENvbGxlY3Rpb248SywgVj4ge1xuXHRcdHJldHVybiBvdGhlci5maWx0ZXIoKF8sIGspID0+IHRoaXMuaGFzKGspKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUaGUgZGlmZmVyZW5jZSBtZXRob2QgcmV0dXJucyBhIG5ldyBzdHJ1Y3R1cmUgY29udGFpbmluZyBpdGVtcyB3aGVyZSB0aGUga2V5IGlzIHByZXNlbnQgaW4gb25lIG9mIHRoZSBvcmlnaW5hbCBzdHJ1Y3R1cmVzIGJ1dCBub3QgdGhlIG90aGVyLlxuXHQgKiBAcGFyYW0ge0NvbGxlY3Rpb259IG90aGVyIFRoZSBvdGhlciBDb2xsZWN0aW9uIHRvIGZpbHRlciBhZ2FpbnN0XG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKi9cblx0cHVibGljIGRpZmZlcmVuY2Uob3RoZXI6IENvbGxlY3Rpb248SywgVj4pOiBDb2xsZWN0aW9uPEssIFY+IHtcblx0XHRyZXR1cm4gb3RoZXIuZmlsdGVyKChfLCBrKSA9PiAhdGhpcy5oYXMoaykpLmNvbmNhdCh0aGlzLmZpbHRlcigoXywgaykgPT4gIW90aGVyLmhhcyhrKSkpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBzb3J0ZWQgbWV0aG9kIHNvcnRzIHRoZSBpdGVtcyBvZiBhIGNvbGxlY3Rpb24gYW5kIHJldHVybnMgaXQuXG5cdCAqIFRoZSBzb3J0IGlzIG5vdCBuZWNlc3NhcmlseSBzdGFibGUgaW4gTm9kZSAxMCBvciBvbGRlci5cblx0ICogVGhlIGRlZmF1bHQgc29ydCBvcmRlciBpcyBhY2NvcmRpbmcgdG8gc3RyaW5nIFVuaWNvZGUgY29kZSBwb2ludHMuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IFtjb21wYXJlRnVuY3Rpb25dIFNwZWNpZmllcyBhIGZ1bmN0aW9uIHRoYXQgZGVmaW5lcyB0aGUgc29ydCBvcmRlci5cblx0ICogSWYgb21pdHRlZCwgdGhlIGNvbGxlY3Rpb24gaXMgc29ydGVkIGFjY29yZGluZyB0byBlYWNoIGNoYXJhY3RlcidzIFVuaWNvZGUgY29kZSBwb2ludCB2YWx1ZSxcblx0ICogYWNjb3JkaW5nIHRvIHRoZSBzdHJpbmcgY29udmVyc2lvbiBvZiBlYWNoIGVsZW1lbnQuXG5cdCAqIEByZXR1cm5zIHtDb2xsZWN0aW9ufVxuXHQgKiBAZXhhbXBsZSBjb2xsZWN0aW9uLnNvcnRlZCgodXNlckEsIHVzZXJCKSA9PiB1c2VyQS5jcmVhdGVkVGltZXN0YW1wIC0gdXNlckIuY3JlYXRlZFRpbWVzdGFtcCk7XG5cdCAqL1xuXHRwdWJsaWMgc29ydGVkKGNvbXBhcmVGdW5jdGlvbjogKGZpcnN0VmFsdWU6IFYsIHNlY29uZFZhbHVlOiBWLCBmaXJzdEtleTogSywgc2Vjb25kS2V5OiBLKSA9PiBudW1iZXIgPSAoeCwgeSk6IG51bWJlciA9PiBOdW1iZXIoeCA+IHkpIHx8IE51bWJlcih4ID09PSB5KSAtIDEpOiB0aGlzIHtcblx0XHRyZXR1cm4gKG5ldyB0aGlzLmNvbnN0cnVjdG9yW1N5bWJvbC5zcGVjaWVzXShbLi4udGhpcy5lbnRyaWVzKCldKSBhcyB0aGlzKVxuXHRcdFx0LnNvcnQoKGF2LCBidiwgYWssIGJrKSA9PiBjb21wYXJlRnVuY3Rpb24oYXYsIGJ2LCBhaywgYmspKTtcblx0fVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IENvbGxlY3Rpb247XG5leHBvcnQgeyBDb2xsZWN0aW9uIH07XG5leHBvcnQgZGVmYXVsdCBDb2xsZWN0aW9uO1xuIl19\n\n//# sourceURL=webpack://Discord/./node_modules/@discordjs/collection/dist/index.js?")},"./node_modules/@discordjs/form-data/lib/browser.js":function(module,exports){eval("/* eslint-env browser */\nmodule.exports = typeof self == 'object' ? self.FormData : window.FormData;\n\n\n//# sourceURL=webpack://Discord/./node_modules/@discordjs/form-data/lib/browser.js?")},"./node_modules/abort-controller/browser.js":function(module,exports,__webpack_require__){"use strict";eval('/*globals self, window */\n\n\n/*eslint-disable @mysticatea/prettier */\nconst { AbortController, AbortSignal } =\n typeof self !== "undefined" ? self :\n typeof window !== "undefined" ? window :\n /* otherwise */ undefined\n/*eslint-enable @mysticatea/prettier */\n\nmodule.exports = AbortController\nmodule.exports.AbortSignal = AbortSignal\nmodule.exports.default = AbortController\n\n\n//# sourceURL=webpack://Discord/./node_modules/abort-controller/browser.js?')},"./node_modules/base64-js/index.js":function(module,exports,__webpack_require__){"use strict";eval("\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n lookup[i] = code[i]\n revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n var len = b64.length\n\n if (len % 4 > 0) {\n throw new Error('Invalid string. Length must be a multiple of 4')\n }\n\n // Trim off extra bytes after placeholder bytes are found\n // See: https://github.com/beatgammit/base64-js/issues/42\n var validLen = b64.indexOf('=')\n if (validLen === -1) validLen = len\n\n var placeHoldersLen = validLen === len\n ? 0\n : 4 - (validLen % 4)\n\n return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n var tmp\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n\n var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n var curByte = 0\n\n // if there are placeholders, only get up to the last complete 4 chars\n var len = placeHoldersLen > 0\n ? validLen - 4\n : validLen\n\n var i\n for (i = 0; i < len; i += 4) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 18) |\n (revLookup[b64.charCodeAt(i + 1)] << 12) |\n (revLookup[b64.charCodeAt(i + 2)] << 6) |\n revLookup[b64.charCodeAt(i + 3)]\n arr[curByte++] = (tmp >> 16) & 0xFF\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 2) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 2) |\n (revLookup[b64.charCodeAt(i + 1)] >> 4)\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 1) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 10) |\n (revLookup[b64.charCodeAt(i + 1)] << 4) |\n (revLookup[b64.charCodeAt(i + 2)] >> 2)\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n return arr\n}\n\nfunction tripletToBase64 (num) {\n return lookup[num >> 18 & 0x3F] +\n lookup[num >> 12 & 0x3F] +\n lookup[num >> 6 & 0x3F] +\n lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n var tmp\n var output = []\n for (var i = start; i < end; i += 3) {\n tmp =\n ((uint8[i] << 16) & 0xFF0000) +\n ((uint8[i + 1] << 8) & 0xFF00) +\n (uint8[i + 2] & 0xFF)\n output.push(tripletToBase64(tmp))\n }\n return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n var tmp\n var len = uint8.length\n var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n var parts = []\n var maxChunkLength = 16383 // must be multiple of 3\n\n // go through the array every three bytes, we'll deal with trailing stuff later\n for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n parts.push(encodeChunk(\n uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)\n ))\n }\n\n // pad the end with zeros, but make sure to not forget the extra bytes\n if (extraBytes === 1) {\n tmp = uint8[len - 1]\n parts.push(\n lookup[tmp >> 2] +\n lookup[(tmp << 4) & 0x3F] +\n '=='\n )\n } else if (extraBytes === 2) {\n tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n parts.push(\n lookup[tmp >> 10] +\n lookup[(tmp >> 4) & 0x3F] +\n lookup[(tmp << 2) & 0x3F] +\n '='\n )\n }\n\n return parts.join('')\n}\n\n\n//# sourceURL=webpack://Discord/./node_modules/base64-js/index.js?")},"./node_modules/buffer/index.js":function(module,exports,__webpack_require__){"use strict";eval("/* WEBPACK VAR INJECTION */(function(global) {/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n/* eslint-disable no-proto */\n\n\n\nvar base64 = __webpack_require__(/*! base64-js */ \"./node_modules/base64-js/index.js\")\nvar ieee754 = __webpack_require__(/*! ieee754 */ \"./node_modules/ieee754/index.js\")\nvar isArray = __webpack_require__(/*! isarray */ \"./node_modules/isarray/index.js\")\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Use Object implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * Due to various browser bugs, sometimes the Object implementation will be used even\n * when the browser supports typed arrays.\n *\n * Note:\n *\n * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,\n * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.\n *\n * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.\n *\n * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of\n * incorrect length in some situations.\n\n * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they\n * get the Object implementation, which is slower but behaves correctly.\n */\nBuffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined\n ? global.TYPED_ARRAY_SUPPORT\n : typedArraySupport()\n\n/*\n * Export kMaxLength after typed array support is determined.\n */\nexports.kMaxLength = kMaxLength()\n\nfunction typedArraySupport () {\n try {\n var arr = new Uint8Array(1)\n arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}\n return arr.foo() === 42 && // typed array instances can be augmented\n typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`\n arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`\n } catch (e) {\n return false\n }\n}\n\nfunction kMaxLength () {\n return Buffer.TYPED_ARRAY_SUPPORT\n ? 0x7fffffff\n : 0x3fffffff\n}\n\nfunction createBuffer (that, length) {\n if (kMaxLength() < length) {\n throw new RangeError('Invalid typed array length')\n }\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = new Uint8Array(length)\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n if (that === null) {\n that = new Buffer(length)\n }\n that.length = length\n }\n\n return that\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {\n return new Buffer(arg, encodingOrOffset, length)\n }\n\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new Error(\n 'If encoding is specified then the first argument must be a string'\n )\n }\n return allocUnsafe(this, arg)\n }\n return from(this, arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\n// TODO: Legacy, not needed anymore. Remove in next major version.\nBuffer._augment = function (arr) {\n arr.__proto__ = Buffer.prototype\n return arr\n}\n\nfunction from (that, value, encodingOrOffset, length) {\n if (typeof value === 'number') {\n throw new TypeError('\"value\" argument must not be a number')\n }\n\n if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {\n return fromArrayBuffer(that, value, encodingOrOffset, length)\n }\n\n if (typeof value === 'string') {\n return fromString(that, value, encodingOrOffset)\n }\n\n return fromObject(that, value)\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(null, value, encodingOrOffset, length)\n}\n\nif (Buffer.TYPED_ARRAY_SUPPORT) {\n Buffer.prototype.__proto__ = Uint8Array.prototype\n Buffer.__proto__ = Uint8Array\n if (typeof Symbol !== 'undefined' && Symbol.species &&\n Buffer[Symbol.species] === Buffer) {\n // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97\n Object.defineProperty(Buffer, Symbol.species, {\n value: null,\n configurable: true\n })\n }\n}\n\nfunction assertSize (size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be a number')\n } else if (size < 0) {\n throw new RangeError('\"size\" argument must not be negative')\n }\n}\n\nfunction alloc (that, size, fill, encoding) {\n assertSize(size)\n if (size <= 0) {\n return createBuffer(that, size)\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpretted as a start offset.\n return typeof encoding === 'string'\n ? createBuffer(that, size).fill(fill, encoding)\n : createBuffer(that, size).fill(fill)\n }\n return createBuffer(that, size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(null, size, fill, encoding)\n}\n\nfunction allocUnsafe (that, size) {\n assertSize(size)\n that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) {\n for (var i = 0; i < size; ++i) {\n that[i] = 0\n }\n }\n return that\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(null, size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(null, size)\n}\n\nfunction fromString (that, string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8'\n }\n\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('\"encoding\" must be a valid string encoding')\n }\n\n var length = byteLength(string, encoding) | 0\n that = createBuffer(that, length)\n\n var actual = that.write(string, encoding)\n\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n that = that.slice(0, actual)\n }\n\n return that\n}\n\nfunction fromArrayLike (that, array) {\n var length = array.length < 0 ? 0 : checked(array.length) | 0\n that = createBuffer(that, length)\n for (var i = 0; i < length; i += 1) {\n that[i] = array[i] & 255\n }\n return that\n}\n\nfunction fromArrayBuffer (that, array, byteOffset, length) {\n array.byteLength // this throws if `array` is not a valid ArrayBuffer\n\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\\'offset\\' is out of bounds')\n }\n\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\\'length\\' is out of bounds')\n }\n\n if (byteOffset === undefined && length === undefined) {\n array = new Uint8Array(array)\n } else if (length === undefined) {\n array = new Uint8Array(array, byteOffset)\n } else {\n array = new Uint8Array(array, byteOffset, length)\n }\n\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = array\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n that = fromArrayLike(that, array)\n }\n return that\n}\n\nfunction fromObject (that, obj) {\n if (Buffer.isBuffer(obj)) {\n var len = checked(obj.length) | 0\n that = createBuffer(that, len)\n\n if (that.length === 0) {\n return that\n }\n\n obj.copy(that, 0, 0, len)\n return that\n }\n\n if (obj) {\n if ((typeof ArrayBuffer !== 'undefined' &&\n obj.buffer instanceof ArrayBuffer) || 'length' in obj) {\n if (typeof obj.length !== 'number' || isnan(obj.length)) {\n return createBuffer(that, 0)\n }\n return fromArrayLike(that, obj)\n }\n\n if (obj.type === 'Buffer' && isArray(obj.data)) {\n return fromArrayLike(that, obj.data)\n }\n }\n\n throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')\n}\n\nfunction checked (length) {\n // Note: cannot use `length < kMaxLength()` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= kMaxLength()) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n 'size: 0x' + kMaxLength().toString(16) + ' bytes')\n }\n return length | 0\n}\n\nfunction SlowBuffer (length) {\n if (+length != length) { // eslint-disable-line eqeqeq\n length = 0\n }\n return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n return !!(b != null && b._isBuffer)\n}\n\nBuffer.compare = function compare (a, b) {\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError('Arguments must be Buffers')\n }\n\n if (a === b) return 0\n\n var x = a.length\n var y = b.length\n\n for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i]\n y = b[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true\n default:\n return false\n }\n}\n\nBuffer.concat = function concat (list, length) {\n if (!isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n\n if (list.length === 0) {\n return Buffer.alloc(0)\n }\n\n var i\n if (length === undefined) {\n length = 0\n for (i = 0; i < list.length; ++i) {\n length += list[i].length\n }\n }\n\n var buffer = Buffer.allocUnsafe(length)\n var pos = 0\n for (i = 0; i < list.length; ++i) {\n var buf = list[i]\n if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n buf.copy(buffer, pos)\n pos += buf.length\n }\n return buffer\n}\n\nfunction byteLength (string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length\n }\n if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&\n (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {\n return string.byteLength\n }\n if (typeof string !== 'string') {\n string = '' + string\n }\n\n var len = string.length\n if (len === 0) return 0\n\n // Use a for loop to avoid recursion\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len\n case 'utf8':\n case 'utf-8':\n case undefined:\n return utf8ToBytes(string).length\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2\n case 'hex':\n return len >>> 1\n case 'base64':\n return base64ToBytes(string).length\n default:\n if (loweredCase) return utf8ToBytes(string).length // assume utf8\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n var loweredCase = false\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return ''\n }\n\n if (end === undefined || end > this.length) {\n end = this.length\n }\n\n if (end <= 0) {\n return ''\n }\n\n // Force coersion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0\n start >>>= 0\n\n if (end <= start) {\n return ''\n }\n\n if (!encoding) encoding = 'utf8'\n\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end)\n\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end)\n\n case 'ascii':\n return asciiSlice(this, start, end)\n\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end)\n\n case 'base64':\n return base64Slice(this, start, end)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = (encoding + '').toLowerCase()\n loweredCase = true\n }\n }\n}\n\n// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect\n// Buffer instances.\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n var i = b[n]\n b[n] = b[m]\n b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n var len = this.length\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits')\n }\n for (var i = 0; i < len; i += 2) {\n swap(this, i, i + 1)\n }\n return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n var len = this.length\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits')\n }\n for (var i = 0; i < len; i += 4) {\n swap(this, i, i + 3)\n swap(this, i + 1, i + 2)\n }\n return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n var len = this.length\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits')\n }\n for (var i = 0; i < len; i += 8) {\n swap(this, i, i + 7)\n swap(this, i + 1, i + 6)\n swap(this, i + 2, i + 5)\n swap(this, i + 3, i + 4)\n }\n return this\n}\n\nBuffer.prototype.toString = function toString () {\n var length = this.length | 0\n if (length === 0) return ''\n if (arguments.length === 0) return utf8Slice(this, 0, length)\n return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.equals = function equals (b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n if (this === b) return true\n return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n var str = ''\n var max = exports.INSPECT_MAX_BYTES\n if (this.length > 0) {\n str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')\n if (this.length > max) str += ' ... '\n }\n return ''\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n if (!Buffer.isBuffer(target)) {\n throw new TypeError('Argument must be a Buffer')\n }\n\n if (start === undefined) {\n start = 0\n }\n if (end === undefined) {\n end = target ? target.length : 0\n }\n if (thisStart === undefined) {\n thisStart = 0\n }\n if (thisEnd === undefined) {\n thisEnd = this.length\n }\n\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index')\n }\n\n if (thisStart >= thisEnd && start >= end) {\n return 0\n }\n if (thisStart >= thisEnd) {\n return -1\n }\n if (start >= end) {\n return 1\n }\n\n start >>>= 0\n end >>>= 0\n thisStart >>>= 0\n thisEnd >>>= 0\n\n if (this === target) return 0\n\n var x = thisEnd - thisStart\n var y = end - start\n var len = Math.min(x, y)\n\n var thisCopy = this.slice(thisStart, thisEnd)\n var targetCopy = target.slice(start, end)\n\n for (var i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i]\n y = targetCopy[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset\n byteOffset = 0\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000\n }\n byteOffset = +byteOffset // Coerce to Number.\n if (isNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : (buffer.length - 1)\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n if (byteOffset >= buffer.length) {\n if (dir) return -1\n else byteOffset = buffer.length - 1\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0\n else return -1\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding)\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n } else if (typeof val === 'number') {\n val = val & 0xFF // Search for a byte value [0-255]\n if (Buffer.TYPED_ARRAY_SUPPORT &&\n typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n }\n }\n return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)\n }\n\n throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n var indexSize = 1\n var arrLength = arr.length\n var valLength = val.length\n\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase()\n if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1\n }\n indexSize = 2\n arrLength /= 2\n valLength /= 2\n byteOffset /= 2\n }\n }\n\n function read (buf, i) {\n if (indexSize === 1) {\n return buf[i]\n } else {\n return buf.readUInt16BE(i * indexSize)\n }\n }\n\n var i\n if (dir) {\n var foundIndex = -1\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n } else {\n if (foundIndex !== -1) i -= i - foundIndex\n foundIndex = -1\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n for (i = byteOffset; i >= 0; i--) {\n var found = true\n for (var j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false\n break\n }\n }\n if (found) return i\n }\n }\n\n return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n offset = Number(offset) || 0\n var remaining = buf.length - offset\n if (!length) {\n length = remaining\n } else {\n length = Number(length)\n if (length > remaining) {\n length = remaining\n }\n }\n\n // must be an even number of digits\n var strLen = string.length\n if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')\n\n if (length > strLen / 2) {\n length = strLen / 2\n }\n for (var i = 0; i < length; ++i) {\n var parsed = parseInt(string.substr(i * 2, 2), 16)\n if (isNaN(parsed)) return i\n buf[offset + i] = parsed\n }\n return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction latin1Write (buf, string, offset, length) {\n return asciiWrite(buf, string, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8'\n length = this.length\n offset = 0\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset\n length = this.length\n offset = 0\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset | 0\n if (isFinite(length)) {\n length = length | 0\n if (encoding === undefined) encoding = 'utf8'\n } else {\n encoding = length\n length = undefined\n }\n // legacy write(string, encoding, offset, length) - remove in v0.13\n } else {\n throw new Error(\n 'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n )\n }\n\n var remaining = this.length - offset\n if (length === undefined || length > remaining) length = remaining\n\n if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds')\n }\n\n if (!encoding) encoding = 'utf8'\n\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length)\n\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length)\n\n case 'ascii':\n return asciiWrite(this, string, offset, length)\n\n case 'latin1':\n case 'binary':\n return latin1Write(this, string, offset, length)\n\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n }\n}\n\nfunction base64Slice (buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf)\n } else {\n return base64.fromByteArray(buf.slice(start, end))\n }\n}\n\nfunction utf8Slice (buf, start, end) {\n end = Math.min(buf.length, end)\n var res = []\n\n var i = start\n while (i < end) {\n var firstByte = buf[i]\n var codePoint = null\n var bytesPerSequence = (firstByte > 0xEF) ? 4\n : (firstByte > 0xDF) ? 3\n : (firstByte > 0xBF) ? 2\n : 1\n\n if (i + bytesPerSequence <= end) {\n var secondByte, thirdByte, fourthByte, tempCodePoint\n\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte\n }\n break\n case 2:\n secondByte = buf[i + 1]\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint\n }\n }\n break\n case 3:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint\n }\n }\n break\n case 4:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n fourthByte = buf[i + 3]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint\n }\n }\n }\n }\n\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD\n bytesPerSequence = 1\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000\n res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n codePoint = 0xDC00 | codePoint & 0x3FF\n }\n\n res.push(codePoint)\n i += bytesPerSequence\n }\n\n return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nvar MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n var len = codePoints.length\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n var res = ''\n var i = 0\n while (i < len) {\n res += String.fromCharCode.apply(\n String,\n codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n )\n }\n return res\n}\n\nfunction asciiSlice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F)\n }\n return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i])\n }\n return ret\n}\n\nfunction hexSlice (buf, start, end) {\n var len = buf.length\n\n if (!start || start < 0) start = 0\n if (!end || end < 0 || end > len) end = len\n\n var out = ''\n for (var i = start; i < end; ++i) {\n out += toHex(buf[i])\n }\n return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n var bytes = buf.slice(start, end)\n var res = ''\n for (var i = 0; i < bytes.length; i += 2) {\n res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)\n }\n return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n var len = this.length\n start = ~~start\n end = end === undefined ? len : ~~end\n\n if (start < 0) {\n start += len\n if (start < 0) start = 0\n } else if (start > len) {\n start = len\n }\n\n if (end < 0) {\n end += len\n if (end < 0) end = 0\n } else if (end > len) {\n end = len\n }\n\n if (end < start) end = start\n\n var newBuf\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n newBuf = this.subarray(start, end)\n newBuf.__proto__ = Buffer.prototype\n } else {\n var sliceLen = end - start\n newBuf = new Buffer(sliceLen, undefined)\n for (var i = 0; i < sliceLen; ++i) {\n newBuf[i] = this[i + start]\n }\n }\n\n return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length)\n }\n\n var val = this[offset + --byteLength]\n var mul = 1\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n return this[offset]\n}\n\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return ((this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16)) +\n (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] * 0x1000000) +\n ((this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n this[offset + 3])\n}\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var i = byteLength\n var mul = 1\n var val = this[offset + --i]\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n if (!(this[offset] & 0x80)) return (this[offset])\n return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset] | (this[offset + 1] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset + 1] | (this[offset] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16) |\n (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] << 24) |\n (this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n (this[offset + 3])\n}\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var mul = 1\n var i = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var i = byteLength - 1\n var mul = 1\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nfunction objectWriteUInt16 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {\n buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>\n (littleEndian ? i : 1 - i) * 8\n }\n}\n\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nfunction objectWriteUInt32 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffffffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {\n buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff\n }\n}\n\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset + 3] = (value >>> 24)\n this[offset + 2] = (value >>> 16)\n this[offset + 1] = (value >>> 8)\n this[offset] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = 0\n var mul = 1\n var sub = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = byteLength - 1\n var mul = 1\n var sub = 0\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n if (value < 0) value = 0xff + value + 1\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n this[offset + 2] = (value >>> 16)\n this[offset + 3] = (value >>> 24)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (value < 0) value = 0xffffffff + value + 1\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4)\n return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8)\n return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n if (!start) start = 0\n if (!end && end !== 0) end = this.length\n if (targetStart >= target.length) targetStart = target.length\n if (!targetStart) targetStart = 0\n if (end > 0 && end < start) end = start\n\n // Copy 0 bytes; we're done\n if (end === start) return 0\n if (target.length === 0 || this.length === 0) return 0\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds')\n }\n if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')\n if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n // Are we oob?\n if (end > this.length) end = this.length\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start\n }\n\n var len = end - start\n var i\n\n if (this === target && start < targetStart && targetStart < end) {\n // descending copy from end\n for (i = len - 1; i >= 0; --i) {\n target[i + targetStart] = this[i + start]\n }\n } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {\n // ascending copy from start\n for (i = 0; i < len; ++i) {\n target[i + targetStart] = this[i + start]\n }\n } else {\n Uint8Array.prototype.set.call(\n target,\n this.subarray(start, start + len),\n targetStart\n )\n }\n\n return len\n}\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start\n start = 0\n end = this.length\n } else if (typeof end === 'string') {\n encoding = end\n end = this.length\n }\n if (val.length === 1) {\n var code = val.charCodeAt(0)\n if (code < 256) {\n val = code\n }\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string')\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n } else if (typeof val === 'number') {\n val = val & 255\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index')\n }\n\n if (end <= start) {\n return this\n }\n\n start = start >>> 0\n end = end === undefined ? this.length : end >>> 0\n\n if (!val) val = 0\n\n var i\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val\n }\n } else {\n var bytes = Buffer.isBuffer(val)\n ? val\n : utf8ToBytes(new Buffer(val, encoding).toString())\n var len = bytes.length\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len]\n }\n }\n\n return this\n}\n\n// HELPER FUNCTIONS\n// ================\n\nvar INVALID_BASE64_RE = /[^+\\/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = stringtrim(str).replace(INVALID_BASE64_RE, '')\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return ''\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '='\n }\n return str\n}\n\nfunction stringtrim (str) {\n if (str.trim) return str.trim()\n return str.replace(/^\\s+|\\s+$/g, '')\n}\n\nfunction toHex (n) {\n if (n < 16) return '0' + n.toString(16)\n return n.toString(16)\n}\n\nfunction utf8ToBytes (string, units) {\n units = units || Infinity\n var codePoint\n var length = string.length\n var leadSurrogate = null\n var bytes = []\n\n for (var i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i)\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n }\n\n // valid lead\n leadSurrogate = codePoint\n\n continue\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n leadSurrogate = codePoint\n continue\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n }\n\n leadSurrogate = null\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break\n bytes.push(codePoint)\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break\n bytes.push(\n codePoint >> 0x6 | 0xC0,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break\n bytes.push(\n codePoint >> 0xC | 0xE0,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break\n bytes.push(\n codePoint >> 0x12 | 0xF0,\n codePoint >> 0xC & 0x3F | 0x80,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else {\n throw new Error('Invalid code point')\n }\n }\n\n return bytes\n}\n\nfunction asciiToBytes (str) {\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF)\n }\n return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n var c, hi, lo\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break\n\n c = str.charCodeAt(i)\n hi = c >> 8\n lo = c % 256\n byteArray.push(lo)\n byteArray.push(hi)\n }\n\n return byteArray\n}\n\nfunction base64ToBytes (str) {\n return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n for (var i = 0; i < length; ++i) {\n if ((i + offset >= dst.length) || (i >= src.length)) break\n dst[i + offset] = src[i]\n }\n return i\n}\n\nfunction isnan (val) {\n return val !== val // eslint-disable-line no-self-compare\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\")))\n\n//# sourceURL=webpack://Discord/./node_modules/buffer/index.js?")},"./node_modules/core-util-is/lib/util.js":function(module,exports,__webpack_require__){eval("/* WEBPACK VAR INJECTION */(function(Buffer) {// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n// NOTE: These type checking functions intentionally don't use `instanceof`\n// because it is fragile and can be easily faked with `Object.create()`.\n\nfunction isArray(arg) {\n if (Array.isArray) {\n return Array.isArray(arg);\n }\n return objectToString(arg) === '[object Array]';\n}\nexports.isArray = isArray;\n\nfunction isBoolean(arg) {\n return typeof arg === 'boolean';\n}\nexports.isBoolean = isBoolean;\n\nfunction isNull(arg) {\n return arg === null;\n}\nexports.isNull = isNull;\n\nfunction isNullOrUndefined(arg) {\n return arg == null;\n}\nexports.isNullOrUndefined = isNullOrUndefined;\n\nfunction isNumber(arg) {\n return typeof arg === 'number';\n}\nexports.isNumber = isNumber;\n\nfunction isString(arg) {\n return typeof arg === 'string';\n}\nexports.isString = isString;\n\nfunction isSymbol(arg) {\n return typeof arg === 'symbol';\n}\nexports.isSymbol = isSymbol;\n\nfunction isUndefined(arg) {\n return arg === void 0;\n}\nexports.isUndefined = isUndefined;\n\nfunction isRegExp(re) {\n return objectToString(re) === '[object RegExp]';\n}\nexports.isRegExp = isRegExp;\n\nfunction isObject(arg) {\n return typeof arg === 'object' && arg !== null;\n}\nexports.isObject = isObject;\n\nfunction isDate(d) {\n return objectToString(d) === '[object Date]';\n}\nexports.isDate = isDate;\n\nfunction isError(e) {\n return (objectToString(e) === '[object Error]' || e instanceof Error);\n}\nexports.isError = isError;\n\nfunction isFunction(arg) {\n return typeof arg === 'function';\n}\nexports.isFunction = isFunction;\n\nfunction isPrimitive(arg) {\n return arg === null ||\n typeof arg === 'boolean' ||\n typeof arg === 'number' ||\n typeof arg === 'string' ||\n typeof arg === 'symbol' || // ES6 symbol\n typeof arg === 'undefined';\n}\nexports.isPrimitive = isPrimitive;\n\nexports.isBuffer = Buffer.isBuffer;\n\nfunction objectToString(o) {\n return Object.prototype.toString.call(o);\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ \"./node_modules/buffer/index.js\").Buffer))\n\n//# sourceURL=webpack://Discord/./node_modules/core-util-is/lib/util.js?")},"./node_modules/events/events.js":function(module,exports,__webpack_require__){"use strict";eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function eventListener() {\n if (errorListener !== undefined) {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n var errorListener;\n\n // Adding an error listener is not optional because\n // if an error is thrown on an event emitter we cannot\n // guarantee that the actual event we are waiting will\n // be fired. The result could be a silent way to create\n // memory or file descriptor leaks, which is something\n // we should avoid.\n if (name !== 'error') {\n errorListener = function errorListener(err) {\n emitter.removeListener(name, eventListener);\n reject(err);\n };\n\n emitter.once('error', errorListener);\n }\n\n emitter.once(name, eventListener);\n });\n}\n\n\n//# sourceURL=webpack://Discord/./node_modules/events/events.js?")},"./node_modules/ieee754/index.js":function(module,exports){eval("exports.read = function (buffer, offset, isLE, mLen, nBytes) {\n var e, m\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var nBits = -7\n var i = isLE ? (nBytes - 1) : 0\n var d = isLE ? -1 : 1\n var s = buffer[offset + i]\n\n i += d\n\n e = s & ((1 << (-nBits)) - 1)\n s >>= (-nBits)\n nBits += eLen\n for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n m = e & ((1 << (-nBits)) - 1)\n e >>= (-nBits)\n nBits += mLen\n for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n if (e === 0) {\n e = 1 - eBias\n } else if (e === eMax) {\n return m ? NaN : ((s ? -1 : 1) * Infinity)\n } else {\n m = m + Math.pow(2, mLen)\n e = e - eBias\n }\n return (s ? -1 : 1) * m * Math.pow(2, e - mLen)\n}\n\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n var e, m, c\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)\n var i = isLE ? 0 : (nBytes - 1)\n var d = isLE ? 1 : -1\n var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0\n\n value = Math.abs(value)\n\n if (isNaN(value) || value === Infinity) {\n m = isNaN(value) ? 1 : 0\n e = eMax\n } else {\n e = Math.floor(Math.log(value) / Math.LN2)\n if (value * (c = Math.pow(2, -e)) < 1) {\n e--\n c *= 2\n }\n if (e + eBias >= 1) {\n value += rt / c\n } else {\n value += rt * Math.pow(2, 1 - eBias)\n }\n if (value * c >= 2) {\n e++\n c /= 2\n }\n\n if (e + eBias >= eMax) {\n m = 0\n e = eMax\n } else if (e + eBias >= 1) {\n m = ((value * c) - 1) * Math.pow(2, mLen)\n e = e + eBias\n } else {\n m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)\n e = 0\n }\n }\n\n for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n\n e = (e << mLen) | m\n eLen += mLen\n for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n\n buffer[offset + i - d] |= s * 128\n}\n\n\n//# sourceURL=webpack://Discord/./node_modules/ieee754/index.js?")},"./node_modules/inherits/inherits_browser.js":function(module,exports){eval("if (typeof Object.create === 'function') {\n // implementation from standard node.js 'util' module\n module.exports = function inherits(ctor, superCtor) {\n if (superCtor) {\n ctor.super_ = superCtor\n ctor.prototype = Object.create(superCtor.prototype, {\n constructor: {\n value: ctor,\n enumerable: false,\n writable: true,\n configurable: true\n }\n })\n }\n };\n} else {\n // old school shim for old browsers\n module.exports = function inherits(ctor, superCtor) {\n if (superCtor) {\n ctor.super_ = superCtor\n var TempCtor = function () {}\n TempCtor.prototype = superCtor.prototype\n ctor.prototype = new TempCtor()\n ctor.prototype.constructor = ctor\n }\n }\n}\n\n\n//# sourceURL=webpack://Discord/./node_modules/inherits/inherits_browser.js?")},"./node_modules/isarray/index.js":function(module,exports){eval("var toString = {}.toString;\n\nmodule.exports = Array.isArray || function (arr) {\n return toString.call(arr) == '[object Array]';\n};\n\n\n//# sourceURL=webpack://Discord/./node_modules/isarray/index.js?")},"./node_modules/node-fetch/browser.js":function(module,exports,__webpack_require__){"use strict";eval("\n\n// ref: https://github.com/tc39/proposal-global\nvar getGlobal = function () {\n\t// the only reliable means to get the global object is\n\t// `Function('return this')()`\n\t// However, this causes CSP violations in Chrome apps.\n\tif (typeof self !== 'undefined') { return self; }\n\tif (typeof window !== 'undefined') { return window; }\n\tif (typeof global !== 'undefined') { return global; }\n\tthrow new Error('unable to locate global object');\n}\n\nvar global = getGlobal();\n\nmodule.exports = exports = global.fetch;\n\n// Needed for TypeScript and Webpack.\nexports.default = global.fetch.bind(global);\n\nexports.Headers = global.Headers;\nexports.Request = global.Request;\nexports.Response = global.Response;\n\n//# sourceURL=webpack://Discord/./node_modules/node-fetch/browser.js?")},"./node_modules/node-libs-browser/mock/empty.js":function(module,exports){eval("\n\n//# sourceURL=webpack://Discord/./node_modules/node-libs-browser/mock/empty.js?")},"./node_modules/process-nextick-args/index.js":function(module,exports,__webpack_require__){"use strict";eval("/* WEBPACK VAR INJECTION */(function(process) {\n\nif (typeof process === 'undefined' ||\n !process.version ||\n process.version.indexOf('v0.') === 0 ||\n process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) {\n module.exports = { nextTick: nextTick };\n} else {\n module.exports = process\n}\n\nfunction nextTick(fn, arg1, arg2, arg3) {\n if (typeof fn !== 'function') {\n throw new TypeError('\"callback\" argument must be a function');\n }\n var len = arguments.length;\n var args, i;\n switch (len) {\n case 0:\n case 1:\n return process.nextTick(fn);\n case 2:\n return process.nextTick(function afterTickOne() {\n fn.call(null, arg1);\n });\n case 3:\n return process.nextTick(function afterTickTwo() {\n fn.call(null, arg1, arg2);\n });\n case 4:\n return process.nextTick(function afterTickThree() {\n fn.call(null, arg1, arg2, arg3);\n });\n default:\n args = new Array(len - 1);\n i = 0;\n while (i < args.length) {\n args[i++] = arguments[i];\n }\n return process.nextTick(function afterTick() {\n fn.apply(null, args);\n });\n }\n}\n\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../process/browser.js */ \"./node_modules/process/browser.js\")))\n\n//# sourceURL=webpack://Discord/./node_modules/process-nextick-args/index.js?")},"./node_modules/process/browser.js":function(module,exports){eval("// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n\n\n//# sourceURL=webpack://Discord/./node_modules/process/browser.js?")},"./node_modules/readable-stream/duplex-browser.js":function(module,exports,__webpack_require__){eval('module.exports = __webpack_require__(/*! ./lib/_stream_duplex.js */ "./node_modules/readable-stream/lib/_stream_duplex.js");\n\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/duplex-browser.js?')},"./node_modules/readable-stream/lib/_stream_duplex.js":function(module,exports,__webpack_require__){"use strict";eval('// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// "Software"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n// a duplex stream is just a stream that is both readable and writable.\n// Since JS doesn\'t have multiple prototypal inheritance, this class\n// prototypally inherits from Readable, and then parasitically from\n// Writable.\n\n\n\n/**/\n\nvar pna = __webpack_require__(/*! process-nextick-args */ "./node_modules/process-nextick-args/index.js");\n/**/\n\n/**/\nvar objectKeys = Object.keys || function (obj) {\n var keys = [];\n for (var key in obj) {\n keys.push(key);\n }return keys;\n};\n/**/\n\nmodule.exports = Duplex;\n\n/**/\nvar util = Object.create(__webpack_require__(/*! core-util-is */ "./node_modules/core-util-is/lib/util.js"));\nutil.inherits = __webpack_require__(/*! inherits */ "./node_modules/inherits/inherits_browser.js");\n/**/\n\nvar Readable = __webpack_require__(/*! ./_stream_readable */ "./node_modules/readable-stream/lib/_stream_readable.js");\nvar Writable = __webpack_require__(/*! ./_stream_writable */ "./node_modules/readable-stream/lib/_stream_writable.js");\n\nutil.inherits(Duplex, Readable);\n\n{\n // avoid scope creep, the keys array can then be collected\n var keys = objectKeys(Writable.prototype);\n for (var v = 0; v < keys.length; v++) {\n var method = keys[v];\n if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];\n }\n}\n\nfunction Duplex(options) {\n if (!(this instanceof Duplex)) return new Duplex(options);\n\n Readable.call(this, options);\n Writable.call(this, options);\n\n if (options && options.readable === false) this.readable = false;\n\n if (options && options.writable === false) this.writable = false;\n\n this.allowHalfOpen = true;\n if (options && options.allowHalfOpen === false) this.allowHalfOpen = false;\n\n this.once(\'end\', onend);\n}\n\nObject.defineProperty(Duplex.prototype, \'writableHighWaterMark\', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function () {\n return this._writableState.highWaterMark;\n }\n});\n\n// the no-half-open enforcer\nfunction onend() {\n // if we allow half-open state, or if the writable side ended,\n // then we\'re ok.\n if (this.allowHalfOpen || this._writableState.ended) return;\n\n // no more data can be written.\n // But allow more writes to happen in this tick.\n pna.nextTick(onEndNT, this);\n}\n\nfunction onEndNT(self) {\n self.end();\n}\n\nObject.defineProperty(Duplex.prototype, \'destroyed\', {\n get: function () {\n if (this._readableState === undefined || this._writableState === undefined) {\n return false;\n }\n return this._readableState.destroyed && this._writableState.destroyed;\n },\n set: function (value) {\n // we ignore the value if the stream\n // has not been initialized yet\n if (this._readableState === undefined || this._writableState === undefined) {\n return;\n }\n\n // backward compatibility, the user is explicitly\n // managing destroyed\n this._readableState.destroyed = value;\n this._writableState.destroyed = value;\n }\n});\n\nDuplex.prototype._destroy = function (err, cb) {\n this.push(null);\n this.end();\n\n pna.nextTick(cb, err);\n};\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/_stream_duplex.js?')},"./node_modules/readable-stream/lib/_stream_passthrough.js":function(module,exports,__webpack_require__){"use strict";eval('// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// "Software"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n// a passthrough stream.\n// basically just the most minimal sort of Transform stream.\n// Every written chunk gets output as-is.\n\n\n\nmodule.exports = PassThrough;\n\nvar Transform = __webpack_require__(/*! ./_stream_transform */ "./node_modules/readable-stream/lib/_stream_transform.js");\n\n/**/\nvar util = Object.create(__webpack_require__(/*! core-util-is */ "./node_modules/core-util-is/lib/util.js"));\nutil.inherits = __webpack_require__(/*! inherits */ "./node_modules/inherits/inherits_browser.js");\n/**/\n\nutil.inherits(PassThrough, Transform);\n\nfunction PassThrough(options) {\n if (!(this instanceof PassThrough)) return new PassThrough(options);\n\n Transform.call(this, options);\n}\n\nPassThrough.prototype._transform = function (chunk, encoding, cb) {\n cb(null, chunk);\n};\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/_stream_passthrough.js?')},"./node_modules/readable-stream/lib/_stream_readable.js":function(module,exports,__webpack_require__){"use strict";eval("/* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\n/**/\n\nvar pna = __webpack_require__(/*! process-nextick-args */ \"./node_modules/process-nextick-args/index.js\");\n/**/\n\nmodule.exports = Readable;\n\n/**/\nvar isArray = __webpack_require__(/*! isarray */ \"./node_modules/isarray/index.js\");\n/**/\n\n/**/\nvar Duplex;\n/**/\n\nReadable.ReadableState = ReadableState;\n\n/**/\nvar EE = __webpack_require__(/*! events */ \"./node_modules/events/events.js\").EventEmitter;\n\nvar EElistenerCount = function (emitter, type) {\n return emitter.listeners(type).length;\n};\n/**/\n\n/**/\nvar Stream = __webpack_require__(/*! ./internal/streams/stream */ \"./node_modules/readable-stream/lib/internal/streams/stream-browser.js\");\n/**/\n\n/**/\n\nvar Buffer = __webpack_require__(/*! safe-buffer */ \"./node_modules/safe-buffer/index.js\").Buffer;\nvar OurUint8Array = global.Uint8Array || function () {};\nfunction _uint8ArrayToBuffer(chunk) {\n return Buffer.from(chunk);\n}\nfunction _isUint8Array(obj) {\n return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;\n}\n\n/**/\n\n/**/\nvar util = Object.create(__webpack_require__(/*! core-util-is */ \"./node_modules/core-util-is/lib/util.js\"));\nutil.inherits = __webpack_require__(/*! inherits */ \"./node_modules/inherits/inherits_browser.js\");\n/**/\n\n/**/\nvar debugUtil = __webpack_require__(/*! util */ 1);\nvar debug = void 0;\nif (debugUtil && debugUtil.debuglog) {\n debug = debugUtil.debuglog('stream');\n} else {\n debug = function () {};\n}\n/**/\n\nvar BufferList = __webpack_require__(/*! ./internal/streams/BufferList */ \"./node_modules/readable-stream/lib/internal/streams/BufferList.js\");\nvar destroyImpl = __webpack_require__(/*! ./internal/streams/destroy */ \"./node_modules/readable-stream/lib/internal/streams/destroy.js\");\nvar StringDecoder;\n\nutil.inherits(Readable, Stream);\n\nvar kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume'];\n\nfunction prependListener(emitter, event, fn) {\n // Sadly this is not cacheable as some libraries bundle their own\n // event emitter implementation with them.\n if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn);\n\n // This is a hack to make sure that our error handler is attached before any\n // userland ones. NEVER DO THIS. This is here only because this code needs\n // to continue to work with older versions of Node.js that do not include\n // the prependListener() method. The goal is to eventually remove this hack.\n if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]];\n}\n\nfunction ReadableState(options, stream) {\n Duplex = Duplex || __webpack_require__(/*! ./_stream_duplex */ \"./node_modules/readable-stream/lib/_stream_duplex.js\");\n\n options = options || {};\n\n // Duplex streams are both readable and writable, but share\n // the same options object.\n // However, some cases require setting options to different\n // values for the readable and the writable sides of the duplex stream.\n // These options can be provided separately as readableXXX and writableXXX.\n var isDuplex = stream instanceof Duplex;\n\n // object stream flag. Used to make read(n) ignore n and to\n // make all the buffer merging and length checks go away\n this.objectMode = !!options.objectMode;\n\n if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode;\n\n // the point at which it stops calling _read() to fill the buffer\n // Note: 0 is a valid value, means \"don't call _read preemptively ever\"\n var hwm = options.highWaterMark;\n var readableHwm = options.readableHighWaterMark;\n var defaultHwm = this.objectMode ? 16 : 16 * 1024;\n\n if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (readableHwm || readableHwm === 0)) this.highWaterMark = readableHwm;else this.highWaterMark = defaultHwm;\n\n // cast to ints.\n this.highWaterMark = Math.floor(this.highWaterMark);\n\n // A linked list is used to store data chunks instead of an array because the\n // linked list can remove elements from the beginning faster than\n // array.shift()\n this.buffer = new BufferList();\n this.length = 0;\n this.pipes = null;\n this.pipesCount = 0;\n this.flowing = null;\n this.ended = false;\n this.endEmitted = false;\n this.reading = false;\n\n // a flag to be able to tell if the event 'readable'/'data' is emitted\n // immediately, or on a later tick. We set this to true at first, because\n // any actions that shouldn't happen until \"later\" should generally also\n // not happen before the first read call.\n this.sync = true;\n\n // whenever we return null, then we set a flag to say\n // that we're awaiting a 'readable' event emission.\n this.needReadable = false;\n this.emittedReadable = false;\n this.readableListening = false;\n this.resumeScheduled = false;\n\n // has it been destroyed\n this.destroyed = false;\n\n // Crypto is kind of old and crusty. Historically, its default string\n // encoding is 'binary' so we have to make this configurable.\n // Everything else in the universe uses 'utf8', though.\n this.defaultEncoding = options.defaultEncoding || 'utf8';\n\n // the number of writers that are awaiting a drain event in .pipe()s\n this.awaitDrain = 0;\n\n // if true, a maybeReadMore has been scheduled\n this.readingMore = false;\n\n this.decoder = null;\n this.encoding = null;\n if (options.encoding) {\n if (!StringDecoder) StringDecoder = __webpack_require__(/*! string_decoder/ */ \"./node_modules/string_decoder/lib/string_decoder.js\").StringDecoder;\n this.decoder = new StringDecoder(options.encoding);\n this.encoding = options.encoding;\n }\n}\n\nfunction Readable(options) {\n Duplex = Duplex || __webpack_require__(/*! ./_stream_duplex */ \"./node_modules/readable-stream/lib/_stream_duplex.js\");\n\n if (!(this instanceof Readable)) return new Readable(options);\n\n this._readableState = new ReadableState(options, this);\n\n // legacy\n this.readable = true;\n\n if (options) {\n if (typeof options.read === 'function') this._read = options.read;\n\n if (typeof options.destroy === 'function') this._destroy = options.destroy;\n }\n\n Stream.call(this);\n}\n\nObject.defineProperty(Readable.prototype, 'destroyed', {\n get: function () {\n if (this._readableState === undefined) {\n return false;\n }\n return this._readableState.destroyed;\n },\n set: function (value) {\n // we ignore the value if the stream\n // has not been initialized yet\n if (!this._readableState) {\n return;\n }\n\n // backward compatibility, the user is explicitly\n // managing destroyed\n this._readableState.destroyed = value;\n }\n});\n\nReadable.prototype.destroy = destroyImpl.destroy;\nReadable.prototype._undestroy = destroyImpl.undestroy;\nReadable.prototype._destroy = function (err, cb) {\n this.push(null);\n cb(err);\n};\n\n// Manually shove something into the read() buffer.\n// This returns true if the highWaterMark has not been hit yet,\n// similar to how Writable.write() returns true if you should\n// write() some more.\nReadable.prototype.push = function (chunk, encoding) {\n var state = this._readableState;\n var skipChunkCheck;\n\n if (!state.objectMode) {\n if (typeof chunk === 'string') {\n encoding = encoding || state.defaultEncoding;\n if (encoding !== state.encoding) {\n chunk = Buffer.from(chunk, encoding);\n encoding = '';\n }\n skipChunkCheck = true;\n }\n } else {\n skipChunkCheck = true;\n }\n\n return readableAddChunk(this, chunk, encoding, false, skipChunkCheck);\n};\n\n// Unshift should *always* be something directly out of read()\nReadable.prototype.unshift = function (chunk) {\n return readableAddChunk(this, chunk, null, true, false);\n};\n\nfunction readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) {\n var state = stream._readableState;\n if (chunk === null) {\n state.reading = false;\n onEofChunk(stream, state);\n } else {\n var er;\n if (!skipChunkCheck) er = chunkInvalid(state, chunk);\n if (er) {\n stream.emit('error', er);\n } else if (state.objectMode || chunk && chunk.length > 0) {\n if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) {\n chunk = _uint8ArrayToBuffer(chunk);\n }\n\n if (addToFront) {\n if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true);\n } else if (state.ended) {\n stream.emit('error', new Error('stream.push() after EOF'));\n } else {\n state.reading = false;\n if (state.decoder && !encoding) {\n chunk = state.decoder.write(chunk);\n if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state);\n } else {\n addChunk(stream, state, chunk, false);\n }\n }\n } else if (!addToFront) {\n state.reading = false;\n }\n }\n\n return needMoreData(state);\n}\n\nfunction addChunk(stream, state, chunk, addToFront) {\n if (state.flowing && state.length === 0 && !state.sync) {\n stream.emit('data', chunk);\n stream.read(0);\n } else {\n // update the buffer info.\n state.length += state.objectMode ? 1 : chunk.length;\n if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);\n\n if (state.needReadable) emitReadable(stream);\n }\n maybeReadMore(stream, state);\n}\n\nfunction chunkInvalid(state, chunk) {\n var er;\n if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) {\n er = new TypeError('Invalid non-string/buffer chunk');\n }\n return er;\n}\n\n// if it's past the high water mark, we can push in some more.\n// Also, if we have no data yet, we can stand some\n// more bytes. This is to work around cases where hwm=0,\n// such as the repl. Also, if the push() triggered a\n// readable event, and the user called read(largeNumber) such that\n// needReadable was set, then we ought to push more, so that another\n// 'readable' event will be triggered.\nfunction needMoreData(state) {\n return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0);\n}\n\nReadable.prototype.isPaused = function () {\n return this._readableState.flowing === false;\n};\n\n// backwards compatibility.\nReadable.prototype.setEncoding = function (enc) {\n if (!StringDecoder) StringDecoder = __webpack_require__(/*! string_decoder/ */ \"./node_modules/string_decoder/lib/string_decoder.js\").StringDecoder;\n this._readableState.decoder = new StringDecoder(enc);\n this._readableState.encoding = enc;\n return this;\n};\n\n// Don't raise the hwm > 8MB\nvar MAX_HWM = 0x800000;\nfunction computeNewHighWaterMark(n) {\n if (n >= MAX_HWM) {\n n = MAX_HWM;\n } else {\n // Get the next highest power of 2 to prevent increasing hwm excessively in\n // tiny amounts\n n--;\n n |= n >>> 1;\n n |= n >>> 2;\n n |= n >>> 4;\n n |= n >>> 8;\n n |= n >>> 16;\n n++;\n }\n return n;\n}\n\n// This function is designed to be inlinable, so please take care when making\n// changes to the function body.\nfunction howMuchToRead(n, state) {\n if (n <= 0 || state.length === 0 && state.ended) return 0;\n if (state.objectMode) return 1;\n if (n !== n) {\n // Only flow one buffer at a time\n if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length;\n }\n // If we're asking for more than the current hwm, then raise the hwm.\n if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);\n if (n <= state.length) return n;\n // Don't have enough\n if (!state.ended) {\n state.needReadable = true;\n return 0;\n }\n return state.length;\n}\n\n// you can override either this method, or the async _read(n) below.\nReadable.prototype.read = function (n) {\n debug('read', n);\n n = parseInt(n, 10);\n var state = this._readableState;\n var nOrig = n;\n\n if (n !== 0) state.emittedReadable = false;\n\n // if we're doing read(0) to trigger a readable event, but we\n // already have a bunch of data in the buffer, then just trigger\n // the 'readable' event and move on.\n if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) {\n debug('read: emitReadable', state.length, state.ended);\n if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);\n return null;\n }\n\n n = howMuchToRead(n, state);\n\n // if we've ended, and we're now clear, then finish it up.\n if (n === 0 && state.ended) {\n if (state.length === 0) endReadable(this);\n return null;\n }\n\n // All the actual chunk generation logic needs to be\n // *below* the call to _read. The reason is that in certain\n // synthetic stream cases, such as passthrough streams, _read\n // may be a completely synchronous operation which may change\n // the state of the read buffer, providing enough data when\n // before there was *not* enough.\n //\n // So, the steps are:\n // 1. Figure out what the state of things will be after we do\n // a read from the buffer.\n //\n // 2. If that resulting state will trigger a _read, then call _read.\n // Note that this may be asynchronous, or synchronous. Yes, it is\n // deeply ugly to write APIs this way, but that still doesn't mean\n // that the Readable class should behave improperly, as streams are\n // designed to be sync/async agnostic.\n // Take note if the _read call is sync or async (ie, if the read call\n // has returned yet), so that we know whether or not it's safe to emit\n // 'readable' etc.\n //\n // 3. Actually pull the requested chunks out of the buffer and return.\n\n // if we need a readable event, then we need to do some reading.\n var doRead = state.needReadable;\n debug('need readable', doRead);\n\n // if we currently have less than the highWaterMark, then also read some\n if (state.length === 0 || state.length - n < state.highWaterMark) {\n doRead = true;\n debug('length less than watermark', doRead);\n }\n\n // however, if we've ended, then there's no point, and if we're already\n // reading, then it's unnecessary.\n if (state.ended || state.reading) {\n doRead = false;\n debug('reading or ended', doRead);\n } else if (doRead) {\n debug('do read');\n state.reading = true;\n state.sync = true;\n // if the length is currently zero, then we *need* a readable event.\n if (state.length === 0) state.needReadable = true;\n // call internal read method\n this._read(state.highWaterMark);\n state.sync = false;\n // If _read pushed data synchronously, then `reading` will be false,\n // and we need to re-evaluate how much data we can return to the user.\n if (!state.reading) n = howMuchToRead(nOrig, state);\n }\n\n var ret;\n if (n > 0) ret = fromList(n, state);else ret = null;\n\n if (ret === null) {\n state.needReadable = true;\n n = 0;\n } else {\n state.length -= n;\n }\n\n if (state.length === 0) {\n // If we have nothing in the buffer, then we want to know\n // as soon as we *do* get something into the buffer.\n if (!state.ended) state.needReadable = true;\n\n // If we tried to read() past the EOF, then emit end on the next tick.\n if (nOrig !== n && state.ended) endReadable(this);\n }\n\n if (ret !== null) this.emit('data', ret);\n\n return ret;\n};\n\nfunction onEofChunk(stream, state) {\n if (state.ended) return;\n if (state.decoder) {\n var chunk = state.decoder.end();\n if (chunk && chunk.length) {\n state.buffer.push(chunk);\n state.length += state.objectMode ? 1 : chunk.length;\n }\n }\n state.ended = true;\n\n // emit 'readable' now to make sure it gets picked up.\n emitReadable(stream);\n}\n\n// Don't emit readable right away in sync mode, because this can trigger\n// another read() call => stack overflow. This way, it might trigger\n// a nextTick recursion warning, but that's not so bad.\nfunction emitReadable(stream) {\n var state = stream._readableState;\n state.needReadable = false;\n if (!state.emittedReadable) {\n debug('emitReadable', state.flowing);\n state.emittedReadable = true;\n if (state.sync) pna.nextTick(emitReadable_, stream);else emitReadable_(stream);\n }\n}\n\nfunction emitReadable_(stream) {\n debug('emit readable');\n stream.emit('readable');\n flow(stream);\n}\n\n// at this point, the user has presumably seen the 'readable' event,\n// and called read() to consume some data. that may have triggered\n// in turn another _read(n) call, in which case reading = true if\n// it's in progress.\n// However, if we're not ended, or reading, and the length < hwm,\n// then go ahead and try to read some more preemptively.\nfunction maybeReadMore(stream, state) {\n if (!state.readingMore) {\n state.readingMore = true;\n pna.nextTick(maybeReadMore_, stream, state);\n }\n}\n\nfunction maybeReadMore_(stream, state) {\n var len = state.length;\n while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) {\n debug('maybeReadMore read 0');\n stream.read(0);\n if (len === state.length)\n // didn't get any data, stop spinning.\n break;else len = state.length;\n }\n state.readingMore = false;\n}\n\n// abstract method. to be overridden in specific implementation classes.\n// call cb(er, data) where data is <= n in length.\n// for virtual (non-string, non-buffer) streams, \"length\" is somewhat\n// arbitrary, and perhaps not very meaningful.\nReadable.prototype._read = function (n) {\n this.emit('error', new Error('_read() is not implemented'));\n};\n\nReadable.prototype.pipe = function (dest, pipeOpts) {\n var src = this;\n var state = this._readableState;\n\n switch (state.pipesCount) {\n case 0:\n state.pipes = dest;\n break;\n case 1:\n state.pipes = [state.pipes, dest];\n break;\n default:\n state.pipes.push(dest);\n break;\n }\n state.pipesCount += 1;\n debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);\n\n var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;\n\n var endFn = doEnd ? onend : unpipe;\n if (state.endEmitted) pna.nextTick(endFn);else src.once('end', endFn);\n\n dest.on('unpipe', onunpipe);\n function onunpipe(readable, unpipeInfo) {\n debug('onunpipe');\n if (readable === src) {\n if (unpipeInfo && unpipeInfo.hasUnpiped === false) {\n unpipeInfo.hasUnpiped = true;\n cleanup();\n }\n }\n }\n\n function onend() {\n debug('onend');\n dest.end();\n }\n\n // when the dest drains, it reduces the awaitDrain counter\n // on the source. This would be more elegant with a .once()\n // handler in flow(), but adding and removing repeatedly is\n // too slow.\n var ondrain = pipeOnDrain(src);\n dest.on('drain', ondrain);\n\n var cleanedUp = false;\n function cleanup() {\n debug('cleanup');\n // cleanup event handlers once the pipe is broken\n dest.removeListener('close', onclose);\n dest.removeListener('finish', onfinish);\n dest.removeListener('drain', ondrain);\n dest.removeListener('error', onerror);\n dest.removeListener('unpipe', onunpipe);\n src.removeListener('end', onend);\n src.removeListener('end', unpipe);\n src.removeListener('data', ondata);\n\n cleanedUp = true;\n\n // if the reader is waiting for a drain event from this\n // specific writer, then it would cause it to never start\n // flowing again.\n // So, if this is awaiting a drain, then we just call it now.\n // If we don't know, then assume that we are waiting for one.\n if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();\n }\n\n // If the user pushes more data while we're writing to dest then we'll end up\n // in ondata again. However, we only want to increase awaitDrain once because\n // dest will only emit one 'drain' event for the multiple writes.\n // => Introduce a guard on increasing awaitDrain.\n var increasedAwaitDrain = false;\n src.on('data', ondata);\n function ondata(chunk) {\n debug('ondata');\n increasedAwaitDrain = false;\n var ret = dest.write(chunk);\n if (false === ret && !increasedAwaitDrain) {\n // If the user unpiped during `dest.write()`, it is possible\n // to get stuck in a permanently paused state if that write\n // also returned false.\n // => Check whether `dest` is still a piping destination.\n if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) {\n debug('false write response, pause', src._readableState.awaitDrain);\n src._readableState.awaitDrain++;\n increasedAwaitDrain = true;\n }\n src.pause();\n }\n }\n\n // if the dest has an error, then stop piping into it.\n // however, don't suppress the throwing behavior for this.\n function onerror(er) {\n debug('onerror', er);\n unpipe();\n dest.removeListener('error', onerror);\n if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er);\n }\n\n // Make sure our error handler is attached before userland ones.\n prependListener(dest, 'error', onerror);\n\n // Both close and finish should trigger unpipe, but only once.\n function onclose() {\n dest.removeListener('finish', onfinish);\n unpipe();\n }\n dest.once('close', onclose);\n function onfinish() {\n debug('onfinish');\n dest.removeListener('close', onclose);\n unpipe();\n }\n dest.once('finish', onfinish);\n\n function unpipe() {\n debug('unpipe');\n src.unpipe(dest);\n }\n\n // tell the dest that it's being piped to\n dest.emit('pipe', src);\n\n // start the flow if it hasn't been started already.\n if (!state.flowing) {\n debug('pipe resume');\n src.resume();\n }\n\n return dest;\n};\n\nfunction pipeOnDrain(src) {\n return function () {\n var state = src._readableState;\n debug('pipeOnDrain', state.awaitDrain);\n if (state.awaitDrain) state.awaitDrain--;\n if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {\n state.flowing = true;\n flow(src);\n }\n };\n}\n\nReadable.prototype.unpipe = function (dest) {\n var state = this._readableState;\n var unpipeInfo = { hasUnpiped: false };\n\n // if we're not piping anywhere, then do nothing.\n if (state.pipesCount === 0) return this;\n\n // just one destination. most common case.\n if (state.pipesCount === 1) {\n // passed in one, but it's not the right one.\n if (dest && dest !== state.pipes) return this;\n\n if (!dest) dest = state.pipes;\n\n // got a match.\n state.pipes = null;\n state.pipesCount = 0;\n state.flowing = false;\n if (dest) dest.emit('unpipe', this, unpipeInfo);\n return this;\n }\n\n // slow case. multiple pipe destinations.\n\n if (!dest) {\n // remove all.\n var dests = state.pipes;\n var len = state.pipesCount;\n state.pipes = null;\n state.pipesCount = 0;\n state.flowing = false;\n\n for (var i = 0; i < len; i++) {\n dests[i].emit('unpipe', this, unpipeInfo);\n }return this;\n }\n\n // try to find the right one.\n var index = indexOf(state.pipes, dest);\n if (index === -1) return this;\n\n state.pipes.splice(index, 1);\n state.pipesCount -= 1;\n if (state.pipesCount === 1) state.pipes = state.pipes[0];\n\n dest.emit('unpipe', this, unpipeInfo);\n\n return this;\n};\n\n// set up data events if they are asked for\n// Ensure readable listeners eventually get something\nReadable.prototype.on = function (ev, fn) {\n var res = Stream.prototype.on.call(this, ev, fn);\n\n if (ev === 'data') {\n // Start flowing on next tick if stream isn't explicitly paused\n if (this._readableState.flowing !== false) this.resume();\n } else if (ev === 'readable') {\n var state = this._readableState;\n if (!state.endEmitted && !state.readableListening) {\n state.readableListening = state.needReadable = true;\n state.emittedReadable = false;\n if (!state.reading) {\n pna.nextTick(nReadingNextTick, this);\n } else if (state.length) {\n emitReadable(this);\n }\n }\n }\n\n return res;\n};\nReadable.prototype.addListener = Readable.prototype.on;\n\nfunction nReadingNextTick(self) {\n debug('readable nexttick read 0');\n self.read(0);\n}\n\n// pause() and resume() are remnants of the legacy readable stream API\n// If the user uses them, then switch into old mode.\nReadable.prototype.resume = function () {\n var state = this._readableState;\n if (!state.flowing) {\n debug('resume');\n state.flowing = true;\n resume(this, state);\n }\n return this;\n};\n\nfunction resume(stream, state) {\n if (!state.resumeScheduled) {\n state.resumeScheduled = true;\n pna.nextTick(resume_, stream, state);\n }\n}\n\nfunction resume_(stream, state) {\n if (!state.reading) {\n debug('resume read 0');\n stream.read(0);\n }\n\n state.resumeScheduled = false;\n state.awaitDrain = 0;\n stream.emit('resume');\n flow(stream);\n if (state.flowing && !state.reading) stream.read(0);\n}\n\nReadable.prototype.pause = function () {\n debug('call pause flowing=%j', this._readableState.flowing);\n if (false !== this._readableState.flowing) {\n debug('pause');\n this._readableState.flowing = false;\n this.emit('pause');\n }\n return this;\n};\n\nfunction flow(stream) {\n var state = stream._readableState;\n debug('flow', state.flowing);\n while (state.flowing && stream.read() !== null) {}\n}\n\n// wrap an old-style stream as the async data source.\n// This is *not* part of the readable stream interface.\n// It is an ugly unfortunate mess of history.\nReadable.prototype.wrap = function (stream) {\n var _this = this;\n\n var state = this._readableState;\n var paused = false;\n\n stream.on('end', function () {\n debug('wrapped end');\n if (state.decoder && !state.ended) {\n var chunk = state.decoder.end();\n if (chunk && chunk.length) _this.push(chunk);\n }\n\n _this.push(null);\n });\n\n stream.on('data', function (chunk) {\n debug('wrapped data');\n if (state.decoder) chunk = state.decoder.write(chunk);\n\n // don't skip over falsy values in objectMode\n if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;\n\n var ret = _this.push(chunk);\n if (!ret) {\n paused = true;\n stream.pause();\n }\n });\n\n // proxy all the other methods.\n // important when wrapping filters and duplexes.\n for (var i in stream) {\n if (this[i] === undefined && typeof stream[i] === 'function') {\n this[i] = function (method) {\n return function () {\n return stream[method].apply(stream, arguments);\n };\n }(i);\n }\n }\n\n // proxy certain important events.\n for (var n = 0; n < kProxyEvents.length; n++) {\n stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n]));\n }\n\n // when we try to consume some more bytes, simply unpause the\n // underlying stream.\n this._read = function (n) {\n debug('wrapped _read', n);\n if (paused) {\n paused = false;\n stream.resume();\n }\n };\n\n return this;\n};\n\nObject.defineProperty(Readable.prototype, 'readableHighWaterMark', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function () {\n return this._readableState.highWaterMark;\n }\n});\n\n// exposed for testing purposes only.\nReadable._fromList = fromList;\n\n// Pluck off n bytes from an array of buffers.\n// Length is the combined lengths of all the buffers in the list.\n// This function is designed to be inlinable, so please take care when making\n// changes to the function body.\nfunction fromList(n, state) {\n // nothing buffered\n if (state.length === 0) return null;\n\n var ret;\n if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) {\n // read it all, truncate the list\n if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length);\n state.buffer.clear();\n } else {\n // read part of list\n ret = fromListPartial(n, state.buffer, state.decoder);\n }\n\n return ret;\n}\n\n// Extracts only enough buffered data to satisfy the amount requested.\n// This function is designed to be inlinable, so please take care when making\n// changes to the function body.\nfunction fromListPartial(n, list, hasStrings) {\n var ret;\n if (n < list.head.data.length) {\n // slice is the same for buffers and strings\n ret = list.head.data.slice(0, n);\n list.head.data = list.head.data.slice(n);\n } else if (n === list.head.data.length) {\n // first chunk is a perfect match\n ret = list.shift();\n } else {\n // result spans more than one buffer\n ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list);\n }\n return ret;\n}\n\n// Copies a specified amount of characters from the list of buffered data\n// chunks.\n// This function is designed to be inlinable, so please take care when making\n// changes to the function body.\nfunction copyFromBufferString(n, list) {\n var p = list.head;\n var c = 1;\n var ret = p.data;\n n -= ret.length;\n while (p = p.next) {\n var str = p.data;\n var nb = n > str.length ? str.length : n;\n if (nb === str.length) ret += str;else ret += str.slice(0, n);\n n -= nb;\n if (n === 0) {\n if (nb === str.length) {\n ++c;\n if (p.next) list.head = p.next;else list.head = list.tail = null;\n } else {\n list.head = p;\n p.data = str.slice(nb);\n }\n break;\n }\n ++c;\n }\n list.length -= c;\n return ret;\n}\n\n// Copies a specified amount of bytes from the list of buffered data chunks.\n// This function is designed to be inlinable, so please take care when making\n// changes to the function body.\nfunction copyFromBuffer(n, list) {\n var ret = Buffer.allocUnsafe(n);\n var p = list.head;\n var c = 1;\n p.data.copy(ret);\n n -= p.data.length;\n while (p = p.next) {\n var buf = p.data;\n var nb = n > buf.length ? buf.length : n;\n buf.copy(ret, ret.length - n, 0, nb);\n n -= nb;\n if (n === 0) {\n if (nb === buf.length) {\n ++c;\n if (p.next) list.head = p.next;else list.head = list.tail = null;\n } else {\n list.head = p;\n p.data = buf.slice(nb);\n }\n break;\n }\n ++c;\n }\n list.length -= c;\n return ret;\n}\n\nfunction endReadable(stream) {\n var state = stream._readableState;\n\n // If we get here before consuming all the bytes, then that is a\n // bug in node. Should never happen.\n if (state.length > 0) throw new Error('\"endReadable()\" called on non-empty stream');\n\n if (!state.endEmitted) {\n state.ended = true;\n pna.nextTick(endReadableNT, state, stream);\n }\n}\n\nfunction endReadableNT(state, stream) {\n // Check that we didn't get one last unshift.\n if (!state.endEmitted && state.length === 0) {\n state.endEmitted = true;\n stream.readable = false;\n stream.emit('end');\n }\n}\n\nfunction indexOf(xs, x) {\n for (var i = 0, l = xs.length; i < l; i++) {\n if (xs[i] === x) return i;\n }\n return -1;\n}\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\"), __webpack_require__(/*! ./../../process/browser.js */ \"./node_modules/process/browser.js\")))\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/_stream_readable.js?")},"./node_modules/readable-stream/lib/_stream_transform.js":function(module,exports,__webpack_require__){"use strict";eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n// a transform stream is a readable/writable stream where you do\n// something with the data. Sometimes it's called a \"filter\",\n// but that's not a great name for it, since that implies a thing where\n// some bits pass through, and others are simply ignored. (That would\n// be a valid example of a transform, of course.)\n//\n// While the output is causally related to the input, it's not a\n// necessarily symmetric or synchronous transformation. For example,\n// a zlib stream might take multiple plain-text writes(), and then\n// emit a single compressed chunk some time in the future.\n//\n// Here's how this works:\n//\n// The Transform stream has all the aspects of the readable and writable\n// stream classes. When you write(chunk), that calls _write(chunk,cb)\n// internally, and returns false if there's a lot of pending writes\n// buffered up. When you call read(), that calls _read(n) until\n// there's enough pending readable data buffered up.\n//\n// In a transform stream, the written data is placed in a buffer. When\n// _read(n) is called, it transforms the queued up data, calling the\n// buffered _write cb's as it consumes chunks. If consuming a single\n// written chunk would result in multiple output chunks, then the first\n// outputted bit calls the readcb, and subsequent chunks just go into\n// the read buffer, and will cause it to emit 'readable' if necessary.\n//\n// This way, back-pressure is actually determined by the reading side,\n// since _read has to be called to start processing a new chunk. However,\n// a pathological inflate type of transform can cause excessive buffering\n// here. For example, imagine a stream where every byte of input is\n// interpreted as an integer from 0-255, and then results in that many\n// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in\n// 1kb of data being output. In this case, you could write a very small\n// amount of input, and end up with a very large amount of output. In\n// such a pathological inflating mechanism, there'd be no way to tell\n// the system to stop doing the transform. A single 4MB write could\n// cause the system to run out of memory.\n//\n// However, even in such a pathological case, only a single written chunk\n// would be consumed, and then the rest would wait (un-transformed) until\n// the results of the previous transformed chunk were consumed.\n\n\n\nmodule.exports = Transform;\n\nvar Duplex = __webpack_require__(/*! ./_stream_duplex */ \"./node_modules/readable-stream/lib/_stream_duplex.js\");\n\n/**/\nvar util = Object.create(__webpack_require__(/*! core-util-is */ \"./node_modules/core-util-is/lib/util.js\"));\nutil.inherits = __webpack_require__(/*! inherits */ \"./node_modules/inherits/inherits_browser.js\");\n/**/\n\nutil.inherits(Transform, Duplex);\n\nfunction afterTransform(er, data) {\n var ts = this._transformState;\n ts.transforming = false;\n\n var cb = ts.writecb;\n\n if (!cb) {\n return this.emit('error', new Error('write callback called multiple times'));\n }\n\n ts.writechunk = null;\n ts.writecb = null;\n\n if (data != null) // single equals check for both `null` and `undefined`\n this.push(data);\n\n cb(er);\n\n var rs = this._readableState;\n rs.reading = false;\n if (rs.needReadable || rs.length < rs.highWaterMark) {\n this._read(rs.highWaterMark);\n }\n}\n\nfunction Transform(options) {\n if (!(this instanceof Transform)) return new Transform(options);\n\n Duplex.call(this, options);\n\n this._transformState = {\n afterTransform: afterTransform.bind(this),\n needTransform: false,\n transforming: false,\n writecb: null,\n writechunk: null,\n writeencoding: null\n };\n\n // start out asking for a readable event once data is transformed.\n this._readableState.needReadable = true;\n\n // we have implemented the _read method, and done the other things\n // that Readable wants before the first _read call, so unset the\n // sync guard flag.\n this._readableState.sync = false;\n\n if (options) {\n if (typeof options.transform === 'function') this._transform = options.transform;\n\n if (typeof options.flush === 'function') this._flush = options.flush;\n }\n\n // When the writable side finishes, then flush out anything remaining.\n this.on('prefinish', prefinish);\n}\n\nfunction prefinish() {\n var _this = this;\n\n if (typeof this._flush === 'function') {\n this._flush(function (er, data) {\n done(_this, er, data);\n });\n } else {\n done(this, null, null);\n }\n}\n\nTransform.prototype.push = function (chunk, encoding) {\n this._transformState.needTransform = false;\n return Duplex.prototype.push.call(this, chunk, encoding);\n};\n\n// This is the part where you do stuff!\n// override this function in implementation classes.\n// 'chunk' is an input chunk.\n//\n// Call `push(newChunk)` to pass along transformed output\n// to the readable side. You may call 'push' zero or more times.\n//\n// Call `cb(err)` when you are done with this chunk. If you pass\n// an error, then that'll put the hurt on the whole operation. If you\n// never call cb(), then you'll never get another chunk.\nTransform.prototype._transform = function (chunk, encoding, cb) {\n throw new Error('_transform() is not implemented');\n};\n\nTransform.prototype._write = function (chunk, encoding, cb) {\n var ts = this._transformState;\n ts.writecb = cb;\n ts.writechunk = chunk;\n ts.writeencoding = encoding;\n if (!ts.transforming) {\n var rs = this._readableState;\n if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);\n }\n};\n\n// Doesn't matter what the args are here.\n// _transform does all the work.\n// That we got here means that the readable side wants more data.\nTransform.prototype._read = function (n) {\n var ts = this._transformState;\n\n if (ts.writechunk !== null && ts.writecb && !ts.transforming) {\n ts.transforming = true;\n this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);\n } else {\n // mark that we need a transform, so that any data that comes in\n // will get processed, now that we've asked for it.\n ts.needTransform = true;\n }\n};\n\nTransform.prototype._destroy = function (err, cb) {\n var _this2 = this;\n\n Duplex.prototype._destroy.call(this, err, function (err2) {\n cb(err2);\n _this2.emit('close');\n });\n};\n\nfunction done(stream, er, data) {\n if (er) return stream.emit('error', er);\n\n if (data != null) // single equals check for both `null` and `undefined`\n stream.push(data);\n\n // if there's nothing in the write buffer, then that means\n // that nothing more will ever be provided\n if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0');\n\n if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming');\n\n return stream.push(null);\n}\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/_stream_transform.js?")},"./node_modules/readable-stream/lib/_stream_writable.js":function(module,exports,__webpack_require__){"use strict";eval("/* WEBPACK VAR INJECTION */(function(process, setImmediate, global) {// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n// A bit simpler than readable streams.\n// Implement an async ._write(chunk, encoding, cb), and it'll handle all\n// the drain event emission and buffering.\n\n\n\n/**/\n\nvar pna = __webpack_require__(/*! process-nextick-args */ \"./node_modules/process-nextick-args/index.js\");\n/**/\n\nmodule.exports = Writable;\n\n/* */\nfunction WriteReq(chunk, encoding, cb) {\n this.chunk = chunk;\n this.encoding = encoding;\n this.callback = cb;\n this.next = null;\n}\n\n// It seems a linked list but it is not\n// there will be only 2 of these for each stream\nfunction CorkedRequest(state) {\n var _this = this;\n\n this.next = null;\n this.entry = null;\n this.finish = function () {\n onCorkedFinish(_this, state);\n };\n}\n/* */\n\n/**/\nvar asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;\n/**/\n\n/**/\nvar Duplex;\n/**/\n\nWritable.WritableState = WritableState;\n\n/**/\nvar util = Object.create(__webpack_require__(/*! core-util-is */ \"./node_modules/core-util-is/lib/util.js\"));\nutil.inherits = __webpack_require__(/*! inherits */ \"./node_modules/inherits/inherits_browser.js\");\n/**/\n\n/**/\nvar internalUtil = {\n deprecate: __webpack_require__(/*! util-deprecate */ \"./node_modules/util-deprecate/browser.js\")\n};\n/**/\n\n/**/\nvar Stream = __webpack_require__(/*! ./internal/streams/stream */ \"./node_modules/readable-stream/lib/internal/streams/stream-browser.js\");\n/**/\n\n/**/\n\nvar Buffer = __webpack_require__(/*! safe-buffer */ \"./node_modules/safe-buffer/index.js\").Buffer;\nvar OurUint8Array = global.Uint8Array || function () {};\nfunction _uint8ArrayToBuffer(chunk) {\n return Buffer.from(chunk);\n}\nfunction _isUint8Array(obj) {\n return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;\n}\n\n/**/\n\nvar destroyImpl = __webpack_require__(/*! ./internal/streams/destroy */ \"./node_modules/readable-stream/lib/internal/streams/destroy.js\");\n\nutil.inherits(Writable, Stream);\n\nfunction nop() {}\n\nfunction WritableState(options, stream) {\n Duplex = Duplex || __webpack_require__(/*! ./_stream_duplex */ \"./node_modules/readable-stream/lib/_stream_duplex.js\");\n\n options = options || {};\n\n // Duplex streams are both readable and writable, but share\n // the same options object.\n // However, some cases require setting options to different\n // values for the readable and the writable sides of the duplex stream.\n // These options can be provided separately as readableXXX and writableXXX.\n var isDuplex = stream instanceof Duplex;\n\n // object stream flag to indicate whether or not this stream\n // contains buffers or objects.\n this.objectMode = !!options.objectMode;\n\n if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode;\n\n // the point at which write() starts returning false\n // Note: 0 is a valid value, means that we always return false if\n // the entire buffer is not flushed immediately on write()\n var hwm = options.highWaterMark;\n var writableHwm = options.writableHighWaterMark;\n var defaultHwm = this.objectMode ? 16 : 16 * 1024;\n\n if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm;\n\n // cast to ints.\n this.highWaterMark = Math.floor(this.highWaterMark);\n\n // if _final has been called\n this.finalCalled = false;\n\n // drain event flag.\n this.needDrain = false;\n // at the start of calling end()\n this.ending = false;\n // when end() has been called, and returned\n this.ended = false;\n // when 'finish' is emitted\n this.finished = false;\n\n // has it been destroyed\n this.destroyed = false;\n\n // should we decode strings into buffers before passing to _write?\n // this is here so that some node-core streams can optimize string\n // handling at a lower level.\n var noDecode = options.decodeStrings === false;\n this.decodeStrings = !noDecode;\n\n // Crypto is kind of old and crusty. Historically, its default string\n // encoding is 'binary' so we have to make this configurable.\n // Everything else in the universe uses 'utf8', though.\n this.defaultEncoding = options.defaultEncoding || 'utf8';\n\n // not an actual buffer we keep track of, but a measurement\n // of how much we're waiting to get pushed to some underlying\n // socket or file.\n this.length = 0;\n\n // a flag to see when we're in the middle of a write.\n this.writing = false;\n\n // when true all writes will be buffered until .uncork() call\n this.corked = 0;\n\n // a flag to be able to tell if the onwrite cb is called immediately,\n // or on a later tick. We set this to true at first, because any\n // actions that shouldn't happen until \"later\" should generally also\n // not happen before the first write call.\n this.sync = true;\n\n // a flag to know if we're processing previously buffered items, which\n // may call the _write() callback in the same tick, so that we don't\n // end up in an overlapped onwrite situation.\n this.bufferProcessing = false;\n\n // the callback that's passed to _write(chunk,cb)\n this.onwrite = function (er) {\n onwrite(stream, er);\n };\n\n // the callback that the user supplies to write(chunk,encoding,cb)\n this.writecb = null;\n\n // the amount that is being written when _write is called.\n this.writelen = 0;\n\n this.bufferedRequest = null;\n this.lastBufferedRequest = null;\n\n // number of pending user-supplied write callbacks\n // this must be 0 before 'finish' can be emitted\n this.pendingcb = 0;\n\n // emit prefinish if the only thing we're waiting for is _write cbs\n // This is relevant for synchronous Transform streams\n this.prefinished = false;\n\n // True if the error was already emitted and should not be thrown again\n this.errorEmitted = false;\n\n // count buffered requests\n this.bufferedRequestCount = 0;\n\n // allocate the first CorkedRequest, there is always\n // one allocated and free to use, and we maintain at most two\n this.corkedRequestsFree = new CorkedRequest(this);\n}\n\nWritableState.prototype.getBuffer = function getBuffer() {\n var current = this.bufferedRequest;\n var out = [];\n while (current) {\n out.push(current);\n current = current.next;\n }\n return out;\n};\n\n(function () {\n try {\n Object.defineProperty(WritableState.prototype, 'buffer', {\n get: internalUtil.deprecate(function () {\n return this.getBuffer();\n }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003')\n });\n } catch (_) {}\n})();\n\n// Test _writableState for inheritance to account for Duplex streams,\n// whose prototype chain only points to Readable.\nvar realHasInstance;\nif (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') {\n realHasInstance = Function.prototype[Symbol.hasInstance];\n Object.defineProperty(Writable, Symbol.hasInstance, {\n value: function (object) {\n if (realHasInstance.call(this, object)) return true;\n if (this !== Writable) return false;\n\n return object && object._writableState instanceof WritableState;\n }\n });\n} else {\n realHasInstance = function (object) {\n return object instanceof this;\n };\n}\n\nfunction Writable(options) {\n Duplex = Duplex || __webpack_require__(/*! ./_stream_duplex */ \"./node_modules/readable-stream/lib/_stream_duplex.js\");\n\n // Writable ctor is applied to Duplexes, too.\n // `realHasInstance` is necessary because using plain `instanceof`\n // would return false, as no `_writableState` property is attached.\n\n // Trying to use the custom `instanceof` for Writable here will also break the\n // Node.js LazyTransform implementation, which has a non-trivial getter for\n // `_writableState` that would lead to infinite recursion.\n if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) {\n return new Writable(options);\n }\n\n this._writableState = new WritableState(options, this);\n\n // legacy.\n this.writable = true;\n\n if (options) {\n if (typeof options.write === 'function') this._write = options.write;\n\n if (typeof options.writev === 'function') this._writev = options.writev;\n\n if (typeof options.destroy === 'function') this._destroy = options.destroy;\n\n if (typeof options.final === 'function') this._final = options.final;\n }\n\n Stream.call(this);\n}\n\n// Otherwise people can pipe Writable streams, which is just wrong.\nWritable.prototype.pipe = function () {\n this.emit('error', new Error('Cannot pipe, not readable'));\n};\n\nfunction writeAfterEnd(stream, cb) {\n var er = new Error('write after end');\n // TODO: defer error events consistently everywhere, not just the cb\n stream.emit('error', er);\n pna.nextTick(cb, er);\n}\n\n// Checks that a user-supplied chunk is valid, especially for the particular\n// mode the stream is in. Currently this means that `null` is never accepted\n// and undefined/non-string values are only allowed in object mode.\nfunction validChunk(stream, state, chunk, cb) {\n var valid = true;\n var er = false;\n\n if (chunk === null) {\n er = new TypeError('May not write null values to stream');\n } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) {\n er = new TypeError('Invalid non-string/buffer chunk');\n }\n if (er) {\n stream.emit('error', er);\n pna.nextTick(cb, er);\n valid = false;\n }\n return valid;\n}\n\nWritable.prototype.write = function (chunk, encoding, cb) {\n var state = this._writableState;\n var ret = false;\n var isBuf = !state.objectMode && _isUint8Array(chunk);\n\n if (isBuf && !Buffer.isBuffer(chunk)) {\n chunk = _uint8ArrayToBuffer(chunk);\n }\n\n if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;\n\n if (typeof cb !== 'function') cb = nop;\n\n if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) {\n state.pendingcb++;\n ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb);\n }\n\n return ret;\n};\n\nWritable.prototype.cork = function () {\n var state = this._writableState;\n\n state.corked++;\n};\n\nWritable.prototype.uncork = function () {\n var state = this._writableState;\n\n if (state.corked) {\n state.corked--;\n\n if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);\n }\n};\n\nWritable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {\n // node::ParseEncoding() requires lower case.\n if (typeof encoding === 'string') encoding = encoding.toLowerCase();\n if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding);\n this._writableState.defaultEncoding = encoding;\n return this;\n};\n\nfunction decodeChunk(state, chunk, encoding) {\n if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding);\n }\n return chunk;\n}\n\nObject.defineProperty(Writable.prototype, 'writableHighWaterMark', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function () {\n return this._writableState.highWaterMark;\n }\n});\n\n// if we're already writing something, then just put this\n// in the queue, and wait our turn. Otherwise, call _write\n// If we return false, then we need a drain event, so set that flag.\nfunction writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {\n if (!isBuf) {\n var newChunk = decodeChunk(state, chunk, encoding);\n if (chunk !== newChunk) {\n isBuf = true;\n encoding = 'buffer';\n chunk = newChunk;\n }\n }\n var len = state.objectMode ? 1 : chunk.length;\n\n state.length += len;\n\n var ret = state.length < state.highWaterMark;\n // we must ensure that previous needDrain will not be reset to false.\n if (!ret) state.needDrain = true;\n\n if (state.writing || state.corked) {\n var last = state.lastBufferedRequest;\n state.lastBufferedRequest = {\n chunk: chunk,\n encoding: encoding,\n isBuf: isBuf,\n callback: cb,\n next: null\n };\n if (last) {\n last.next = state.lastBufferedRequest;\n } else {\n state.bufferedRequest = state.lastBufferedRequest;\n }\n state.bufferedRequestCount += 1;\n } else {\n doWrite(stream, state, false, len, chunk, encoding, cb);\n }\n\n return ret;\n}\n\nfunction doWrite(stream, state, writev, len, chunk, encoding, cb) {\n state.writelen = len;\n state.writecb = cb;\n state.writing = true;\n state.sync = true;\n if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);\n state.sync = false;\n}\n\nfunction onwriteError(stream, state, sync, er, cb) {\n --state.pendingcb;\n\n if (sync) {\n // defer the callback if we are being called synchronously\n // to avoid piling up things on the stack\n pna.nextTick(cb, er);\n // this can emit finish, and it will always happen\n // after error\n pna.nextTick(finishMaybe, stream, state);\n stream._writableState.errorEmitted = true;\n stream.emit('error', er);\n } else {\n // the caller expect this to happen before if\n // it is async\n cb(er);\n stream._writableState.errorEmitted = true;\n stream.emit('error', er);\n // this can emit finish, but finish must\n // always follow error\n finishMaybe(stream, state);\n }\n}\n\nfunction onwriteStateUpdate(state) {\n state.writing = false;\n state.writecb = null;\n state.length -= state.writelen;\n state.writelen = 0;\n}\n\nfunction onwrite(stream, er) {\n var state = stream._writableState;\n var sync = state.sync;\n var cb = state.writecb;\n\n onwriteStateUpdate(state);\n\n if (er) onwriteError(stream, state, sync, er, cb);else {\n // Check if we're actually ready to finish, but don't emit yet\n var finished = needFinish(state);\n\n if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {\n clearBuffer(stream, state);\n }\n\n if (sync) {\n /**/\n asyncWrite(afterWrite, stream, state, finished, cb);\n /**/\n } else {\n afterWrite(stream, state, finished, cb);\n }\n }\n}\n\nfunction afterWrite(stream, state, finished, cb) {\n if (!finished) onwriteDrain(stream, state);\n state.pendingcb--;\n cb();\n finishMaybe(stream, state);\n}\n\n// Must force callback to be called on nextTick, so that we don't\n// emit 'drain' before the write() consumer gets the 'false' return\n// value, and has a chance to attach a 'drain' listener.\nfunction onwriteDrain(stream, state) {\n if (state.length === 0 && state.needDrain) {\n state.needDrain = false;\n stream.emit('drain');\n }\n}\n\n// if there's something in the buffer waiting, then process it\nfunction clearBuffer(stream, state) {\n state.bufferProcessing = true;\n var entry = state.bufferedRequest;\n\n if (stream._writev && entry && entry.next) {\n // Fast case, write everything using _writev()\n var l = state.bufferedRequestCount;\n var buffer = new Array(l);\n var holder = state.corkedRequestsFree;\n holder.entry = entry;\n\n var count = 0;\n var allBuffers = true;\n while (entry) {\n buffer[count] = entry;\n if (!entry.isBuf) allBuffers = false;\n entry = entry.next;\n count += 1;\n }\n buffer.allBuffers = allBuffers;\n\n doWrite(stream, state, true, state.length, buffer, '', holder.finish);\n\n // doWrite is almost always async, defer these to save a bit of time\n // as the hot path ends with doWrite\n state.pendingcb++;\n state.lastBufferedRequest = null;\n if (holder.next) {\n state.corkedRequestsFree = holder.next;\n holder.next = null;\n } else {\n state.corkedRequestsFree = new CorkedRequest(state);\n }\n state.bufferedRequestCount = 0;\n } else {\n // Slow case, write chunks one-by-one\n while (entry) {\n var chunk = entry.chunk;\n var encoding = entry.encoding;\n var cb = entry.callback;\n var len = state.objectMode ? 1 : chunk.length;\n\n doWrite(stream, state, false, len, chunk, encoding, cb);\n entry = entry.next;\n state.bufferedRequestCount--;\n // if we didn't call the onwrite immediately, then\n // it means that we need to wait until it does.\n // also, that means that the chunk and cb are currently\n // being processed, so move the buffer counter past them.\n if (state.writing) {\n break;\n }\n }\n\n if (entry === null) state.lastBufferedRequest = null;\n }\n\n state.bufferedRequest = entry;\n state.bufferProcessing = false;\n}\n\nWritable.prototype._write = function (chunk, encoding, cb) {\n cb(new Error('_write() is not implemented'));\n};\n\nWritable.prototype._writev = null;\n\nWritable.prototype.end = function (chunk, encoding, cb) {\n var state = this._writableState;\n\n if (typeof chunk === 'function') {\n cb = chunk;\n chunk = null;\n encoding = null;\n } else if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n if (chunk !== null && chunk !== undefined) this.write(chunk, encoding);\n\n // .end() fully uncorks\n if (state.corked) {\n state.corked = 1;\n this.uncork();\n }\n\n // ignore unnecessary end() calls.\n if (!state.ending && !state.finished) endWritable(this, state, cb);\n};\n\nfunction needFinish(state) {\n return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;\n}\nfunction callFinal(stream, state) {\n stream._final(function (err) {\n state.pendingcb--;\n if (err) {\n stream.emit('error', err);\n }\n state.prefinished = true;\n stream.emit('prefinish');\n finishMaybe(stream, state);\n });\n}\nfunction prefinish(stream, state) {\n if (!state.prefinished && !state.finalCalled) {\n if (typeof stream._final === 'function') {\n state.pendingcb++;\n state.finalCalled = true;\n pna.nextTick(callFinal, stream, state);\n } else {\n state.prefinished = true;\n stream.emit('prefinish');\n }\n }\n}\n\nfunction finishMaybe(stream, state) {\n var need = needFinish(state);\n if (need) {\n prefinish(stream, state);\n if (state.pendingcb === 0) {\n state.finished = true;\n stream.emit('finish');\n }\n }\n return need;\n}\n\nfunction endWritable(stream, state, cb) {\n state.ending = true;\n finishMaybe(stream, state);\n if (cb) {\n if (state.finished) pna.nextTick(cb);else stream.once('finish', cb);\n }\n state.ended = true;\n stream.writable = false;\n}\n\nfunction onCorkedFinish(corkReq, state, err) {\n var entry = corkReq.entry;\n corkReq.entry = null;\n while (entry) {\n var cb = entry.callback;\n state.pendingcb--;\n cb(err);\n entry = entry.next;\n }\n if (state.corkedRequestsFree) {\n state.corkedRequestsFree.next = corkReq;\n } else {\n state.corkedRequestsFree = corkReq;\n }\n}\n\nObject.defineProperty(Writable.prototype, 'destroyed', {\n get: function () {\n if (this._writableState === undefined) {\n return false;\n }\n return this._writableState.destroyed;\n },\n set: function (value) {\n // we ignore the value if the stream\n // has not been initialized yet\n if (!this._writableState) {\n return;\n }\n\n // backward compatibility, the user is explicitly\n // managing destroyed\n this._writableState.destroyed = value;\n }\n});\n\nWritable.prototype.destroy = destroyImpl.destroy;\nWritable.prototype._undestroy = destroyImpl.undestroy;\nWritable.prototype._destroy = function (err, cb) {\n this.end();\n cb(err);\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../process/browser.js */ \"./node_modules/process/browser.js\"), __webpack_require__(/*! ./../../timers-browserify/main.js */ \"./node_modules/timers-browserify/main.js\").setImmediate, __webpack_require__(/*! ./../../webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\")))\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/_stream_writable.js?")},"./node_modules/readable-stream/lib/internal/streams/BufferList.js":function(module,exports,__webpack_require__){"use strict";eval("\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Buffer = __webpack_require__(/*! safe-buffer */ \"./node_modules/safe-buffer/index.js\").Buffer;\nvar util = __webpack_require__(/*! util */ 2);\n\nfunction copyBuffer(src, target, offset) {\n src.copy(target, offset);\n}\n\nmodule.exports = function () {\n function BufferList() {\n _classCallCheck(this, BufferList);\n\n this.head = null;\n this.tail = null;\n this.length = 0;\n }\n\n BufferList.prototype.push = function push(v) {\n var entry = { data: v, next: null };\n if (this.length > 0) this.tail.next = entry;else this.head = entry;\n this.tail = entry;\n ++this.length;\n };\n\n BufferList.prototype.unshift = function unshift(v) {\n var entry = { data: v, next: this.head };\n if (this.length === 0) this.tail = entry;\n this.head = entry;\n ++this.length;\n };\n\n BufferList.prototype.shift = function shift() {\n if (this.length === 0) return;\n var ret = this.head.data;\n if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next;\n --this.length;\n return ret;\n };\n\n BufferList.prototype.clear = function clear() {\n this.head = this.tail = null;\n this.length = 0;\n };\n\n BufferList.prototype.join = function join(s) {\n if (this.length === 0) return '';\n var p = this.head;\n var ret = '' + p.data;\n while (p = p.next) {\n ret += s + p.data;\n }return ret;\n };\n\n BufferList.prototype.concat = function concat(n) {\n if (this.length === 0) return Buffer.alloc(0);\n if (this.length === 1) return this.head.data;\n var ret = Buffer.allocUnsafe(n >>> 0);\n var p = this.head;\n var i = 0;\n while (p) {\n copyBuffer(p.data, ret, i);\n i += p.data.length;\n p = p.next;\n }\n return ret;\n };\n\n return BufferList;\n}();\n\nif (util && util.inspect && util.inspect.custom) {\n module.exports.prototype[util.inspect.custom] = function () {\n var obj = util.inspect({ length: this.length });\n return this.constructor.name + ' ' + obj;\n };\n}\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/internal/streams/BufferList.js?")},"./node_modules/readable-stream/lib/internal/streams/destroy.js":function(module,exports,__webpack_require__){"use strict";eval("\n\n/**/\n\nvar pna = __webpack_require__(/*! process-nextick-args */ \"./node_modules/process-nextick-args/index.js\");\n/**/\n\n// undocumented cb() API, needed for core, not for public API\nfunction destroy(err, cb) {\n var _this = this;\n\n var readableDestroyed = this._readableState && this._readableState.destroyed;\n var writableDestroyed = this._writableState && this._writableState.destroyed;\n\n if (readableDestroyed || writableDestroyed) {\n if (cb) {\n cb(err);\n } else if (err && (!this._writableState || !this._writableState.errorEmitted)) {\n pna.nextTick(emitErrorNT, this, err);\n }\n return this;\n }\n\n // we set destroyed to true before firing error callbacks in order\n // to make it re-entrance safe in case destroy() is called within callbacks\n\n if (this._readableState) {\n this._readableState.destroyed = true;\n }\n\n // if this is a duplex stream mark the writable part as destroyed as well\n if (this._writableState) {\n this._writableState.destroyed = true;\n }\n\n this._destroy(err || null, function (err) {\n if (!cb && err) {\n pna.nextTick(emitErrorNT, _this, err);\n if (_this._writableState) {\n _this._writableState.errorEmitted = true;\n }\n } else if (cb) {\n cb(err);\n }\n });\n\n return this;\n}\n\nfunction undestroy() {\n if (this._readableState) {\n this._readableState.destroyed = false;\n this._readableState.reading = false;\n this._readableState.ended = false;\n this._readableState.endEmitted = false;\n }\n\n if (this._writableState) {\n this._writableState.destroyed = false;\n this._writableState.ended = false;\n this._writableState.ending = false;\n this._writableState.finished = false;\n this._writableState.errorEmitted = false;\n }\n}\n\nfunction emitErrorNT(self, err) {\n self.emit('error', err);\n}\n\nmodule.exports = {\n destroy: destroy,\n undestroy: undestroy\n};\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/internal/streams/destroy.js?")},"./node_modules/readable-stream/lib/internal/streams/stream-browser.js":function(module,exports,__webpack_require__){eval('module.exports = __webpack_require__(/*! events */ "./node_modules/events/events.js").EventEmitter;\n\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/lib/internal/streams/stream-browser.js?')},"./node_modules/readable-stream/passthrough.js":function(module,exports,__webpack_require__){eval('module.exports = __webpack_require__(/*! ./readable */ "./node_modules/readable-stream/readable-browser.js").PassThrough\n\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/passthrough.js?')},"./node_modules/readable-stream/readable-browser.js":function(module,exports,__webpack_require__){eval('exports = module.exports = __webpack_require__(/*! ./lib/_stream_readable.js */ "./node_modules/readable-stream/lib/_stream_readable.js");\nexports.Stream = exports;\nexports.Readable = exports;\nexports.Writable = __webpack_require__(/*! ./lib/_stream_writable.js */ "./node_modules/readable-stream/lib/_stream_writable.js");\nexports.Duplex = __webpack_require__(/*! ./lib/_stream_duplex.js */ "./node_modules/readable-stream/lib/_stream_duplex.js");\nexports.Transform = __webpack_require__(/*! ./lib/_stream_transform.js */ "./node_modules/readable-stream/lib/_stream_transform.js");\nexports.PassThrough = __webpack_require__(/*! ./lib/_stream_passthrough.js */ "./node_modules/readable-stream/lib/_stream_passthrough.js");\n\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/readable-browser.js?')},"./node_modules/readable-stream/transform.js":function(module,exports,__webpack_require__){eval('module.exports = __webpack_require__(/*! ./readable */ "./node_modules/readable-stream/readable-browser.js").Transform\n\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/transform.js?')},"./node_modules/readable-stream/writable-browser.js":function(module,exports,__webpack_require__){eval('module.exports = __webpack_require__(/*! ./lib/_stream_writable.js */ "./node_modules/readable-stream/lib/_stream_writable.js");\n\n\n//# sourceURL=webpack://Discord/./node_modules/readable-stream/writable-browser.js?')},"./node_modules/safe-buffer/index.js":function(module,exports,__webpack_require__){eval("/* eslint-disable node/no-deprecated-api */\nvar buffer = __webpack_require__(/*! buffer */ \"./node_modules/buffer/index.js\")\nvar Buffer = buffer.Buffer\n\n// alternative to using Object.keys for old browsers\nfunction copyProps (src, dst) {\n for (var key in src) {\n dst[key] = src[key]\n }\n}\nif (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {\n module.exports = buffer\n} else {\n // Copy properties from require('buffer')\n copyProps(buffer, exports)\n exports.Buffer = SafeBuffer\n}\n\nfunction SafeBuffer (arg, encodingOrOffset, length) {\n return Buffer(arg, encodingOrOffset, length)\n}\n\n// Copy static methods from Buffer\ncopyProps(Buffer, SafeBuffer)\n\nSafeBuffer.from = function (arg, encodingOrOffset, length) {\n if (typeof arg === 'number') {\n throw new TypeError('Argument must not be a number')\n }\n return Buffer(arg, encodingOrOffset, length)\n}\n\nSafeBuffer.alloc = function (size, fill, encoding) {\n if (typeof size !== 'number') {\n throw new TypeError('Argument must be a number')\n }\n var buf = Buffer(size)\n if (fill !== undefined) {\n if (typeof encoding === 'string') {\n buf.fill(fill, encoding)\n } else {\n buf.fill(fill)\n }\n } else {\n buf.fill(0)\n }\n return buf\n}\n\nSafeBuffer.allocUnsafe = function (size) {\n if (typeof size !== 'number') {\n throw new TypeError('Argument must be a number')\n }\n return Buffer(size)\n}\n\nSafeBuffer.allocUnsafeSlow = function (size) {\n if (typeof size !== 'number') {\n throw new TypeError('Argument must be a number')\n }\n return buffer.SlowBuffer(size)\n}\n\n\n//# sourceURL=webpack://Discord/./node_modules/safe-buffer/index.js?")},"./node_modules/setimmediate/setImmediate.js":function(module,exports,__webpack_require__){eval('/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) {\n "use strict";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== "function") {\n callback = new Function("" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: "Wait until any invocations of this algorithm started before this one have completed."\n // So if we\'re currently running a task, we\'ll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // "too much recursion" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function(handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can\'t be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function() {\n postMessageIsAsynchronous = false;\n };\n global.postMessage("", "*");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = "setImmediate$" + Math.random() + "$";\n var onGlobalMessage = function(event) {\n if (event.source === global &&\n typeof event.data === "string" &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener("message", onGlobalMessage, false);\n } else {\n global.attachEvent("onmessage", onGlobalMessage);\n }\n\n registerImmediate = function(handle) {\n global.postMessage(messagePrefix + handle, "*");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function(event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function(handle) {\n // Create a + + + or + + var nacl = require('tweetnacl'); + nacl.util = require('tweetnacl-util'); + + However it is recommended to use better packages that have wider + compatibility and better performance. Functions from `nacl.util` were never + intended to be robust solution for string conversion and were included for + convenience: cryptography library is not the right place for them. + + Currently calling these functions will throw error pointing to + `tweetnacl-util-js` (in the next version this error message will be removed). + +* Improved detection of available random number generators, making it possible + to use `nacl.randomBytes` and related functions in Web Workers without + changes. + +* Changes to testing (see README). + + +v0.13.3 +------- + +No code changes. + +* Reverted license field in package.json to "Public domain". + +* Fixed typo in README. + + +v0.13.2 +------- + +* Fixed undefined variable bug in fast version of Poly1305. No worries, this + bug was *never* triggered. + +* Specified CC0 public domain dedication. + +* Updated development dependencies. + + +v0.13.1 +------- + +* Exclude `crypto` and `buffer` modules from browserify builds. + + +v0.13.0 +------- + +* Made `nacl-fast` the default version in NPM package. Now + `require("tweetnacl")` will use fast version; to get the original version, + use `require("tweetnacl/nacl.js")`. + +* Cleanup temporary array after generating random bytes. + + +v0.12.2 +------- + +* Improved performance of curve operations, making `nacl.scalarMult`, `nacl.box`, + `nacl.sign` and related functions up to 3x faster in `nacl-fast` version. + + +v0.12.1 +------- + +* Significantly improved performance of Salsa20 (~1.5x faster) and + Poly1305 (~3.5x faster) in `nacl-fast` version. + + +v0.12.0 +------- + +* Instead of using the given secret key directly, TweetNaCl.js now copies it to + a new array in `nacl.box.keyPair.fromSecretKey` and + `nacl.sign.keyPair.fromSecretKey`. + + +v0.11.2 +------- + +* Added new constant: `nacl.sign.seedLength`. + + +v0.11.1 +------- + +* Even faster hash for both short and long inputs (in `nacl-fast`). + + +v0.11.0 +------- + +* Implement `nacl.sign.keyPair.fromSeed` to enable creation of sign key pairs + deterministically from a 32-byte seed. (It behaves like + [libsodium's](http://doc.libsodium.org/public-key_cryptography/public-key_signatures.html) + `crypto_sign_seed_keypair`: the seed becomes a secret part of the secret key.) + +* Fast version now has an improved hash implementation that is 2x-5x faster. + +* Fixed benchmarks, which may have produced incorrect measurements. + + +v0.10.1 +------- + +* Exported undocumented `nacl.lowlevel.crypto_core_hsalsa20`. + + +v0.10.0 +------- + +* **Signature API breaking change!** `nacl.sign` and `nacl.sign.open` now deal + with signed messages, and new `nacl.sign.detached` and + `nacl.sign.detached.verify` are available. + + Previously, `nacl.sign` returned a signature, and `nacl.sign.open` accepted a + message and "detached" signature. This was unlike NaCl's API, which dealt with + signed messages (concatenation of signature and message). + + The new API is: + + nacl.sign(message, secretKey) -> signedMessage + nacl.sign.open(signedMessage, publicKey) -> message | null + + Since detached signatures are common, two new API functions were introduced: + + nacl.sign.detached(message, secretKey) -> signature + nacl.sign.detached.verify(message, signature, publicKey) -> true | false + + (Note that it's `verify`, not `open`, and it returns a boolean value, unlike + `open`, which returns an "unsigned" message.) + +* NPM package now comes without `test` directory to keep it small. + + +v0.9.2 +------ + +* Improved documentation. +* Fast version: increased theoretical message size limit from 2^32-1 to 2^52 + bytes in Poly1305 (and thus, secretbox and box). However this has no impact + in practice since JavaScript arrays or ArrayBuffers are limited to 32-bit + indexes, and most implementations won't allocate more than a gigabyte or so. + (Obviously, there are no tests for the correctness of implementation.) Also, + it's not recommended to use messages that large without splitting them into + smaller packets anyway. + + +v0.9.1 +------ + +* Initial release diff --git a/node_modules/tweetnacl/LICENSE b/node_modules/tweetnacl/LICENSE new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/node_modules/tweetnacl/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md b/node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a8eb4a9 --- /dev/null +++ b/node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ +# Important! + +If your contribution is not trivial (not a typo fix, etc.), we can only accept +it if you dedicate your copyright for the contribution to the public domain. +Make sure you understand what it means (see http://unlicense.org/)! If you +agree, please add yourself to AUTHORS.md file, and include the following text +to your pull request description or a comment in it: + +------------------------------------------------------------------------------ + + I dedicate any and all copyright interest in this software to the + public domain. I make this dedication for the benefit of the public at + large and to the detriment of my heirs and successors. I intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. diff --git a/node_modules/tweetnacl/README.md b/node_modules/tweetnacl/README.md new file mode 100644 index 0000000..022bf6b --- /dev/null +++ b/node_modules/tweetnacl/README.md @@ -0,0 +1,494 @@ +TweetNaCl.js +============ + +Port of [TweetNaCl](http://tweetnacl.cr.yp.to) / [NaCl](http://nacl.cr.yp.to/) +to JavaScript for modern browsers and Node.js. Public domain. + +[![Build Status](https://travis-ci.org/dchest/tweetnacl-js.svg?branch=master) +](https://travis-ci.org/dchest/tweetnacl-js) + +Demo: + +Documentation +============= + +* [Overview](#overview) +* [Audits](#audits) +* [Installation](#installation) +* [Examples](#examples) +* [Usage](#usage) + * [Public-key authenticated encryption (box)](#public-key-authenticated-encryption-box) + * [Secret-key authenticated encryption (secretbox)](#secret-key-authenticated-encryption-secretbox) + * [Scalar multiplication](#scalar-multiplication) + * [Signatures](#signatures) + * [Hashing](#hashing) + * [Random bytes generation](#random-bytes-generation) + * [Constant-time comparison](#constant-time-comparison) +* [System requirements](#system-requirements) +* [Development and testing](#development-and-testing) +* [Benchmarks](#benchmarks) +* [Contributors](#contributors) +* [Who uses it](#who-uses-it) + + +Overview +-------- + +The primary goal of this project is to produce a translation of TweetNaCl to +JavaScript which is as close as possible to the original C implementation, plus +a thin layer of idiomatic high-level API on top of it. + +There are two versions, you can use either of them: + +* `nacl.js` is the port of TweetNaCl with minimum differences from the + original + high-level API. + +* `nacl-fast.js` is like `nacl.js`, but with some functions replaced with + faster versions. (Used by default when importing NPM package.) + + +Audits +------ + +TweetNaCl.js has been audited by [Cure53](https://cure53.de/) in January-February +2017 (audit was sponsored by [Deletype](https://deletype.com)): + +> The overall outcome of this audit signals a particularly positive assessment +> for TweetNaCl-js, as the testing team was unable to find any security +> problems in the library. It has to be noted that this is an exceptionally +> rare result of a source code audit for any project and must be seen as a true +> testament to a development proceeding with security at its core. +> +> To reiterate, the TweetNaCl-js project, the source code was found to be +> bug-free at this point. +> +> [...] +> +> In sum, the testing team is happy to recommend the TweetNaCl-js project as +> likely one of the safer and more secure cryptographic tools among its +> competition. + +[Read full audit report](https://cure53.de/tweetnacl.pdf) + + +Installation +------------ + +You can install TweetNaCl.js via a package manager: + +[Yarn](https://yarnpkg.com/): + + $ yarn add tweetnacl + +[NPM](https://www.npmjs.org/): + + $ npm install tweetnacl + +or [download source code](https://github.com/dchest/tweetnacl-js/releases). + + +Examples +-------- +You can find usage examples in our [wiki](https://github.com/dchest/tweetnacl-js/wiki/Examples). + + +Usage +----- + +All API functions accept and return bytes as `Uint8Array`s. If you need to +encode or decode strings, use functions from + or one of the more robust codec +packages. + +In Node.js v4 and later `Buffer` objects are backed by `Uint8Array`s, so you +can freely pass them to TweetNaCl.js functions as arguments. The returned +objects are still `Uint8Array`s, so if you need `Buffer`s, you'll have to +convert them manually; make sure to convert using copying: `Buffer.from(array)` +(or `new Buffer(array)` in Node.js v4 or earlier), instead of sharing: +`Buffer.from(array.buffer)` (or `new Buffer(array.buffer)` Node 4 or earlier), +because some functions return subarrays of their buffers. + + +### Public-key authenticated encryption (box) + +Implements *x25519-xsalsa20-poly1305*. + +#### nacl.box.keyPair() + +Generates a new random key pair for box and returns it as an object with +`publicKey` and `secretKey` members: + + { + publicKey: ..., // Uint8Array with 32-byte public key + secretKey: ... // Uint8Array with 32-byte secret key + } + + +#### nacl.box.keyPair.fromSecretKey(secretKey) + +Returns a key pair for box with public key corresponding to the given secret +key. + +#### nacl.box(message, nonce, theirPublicKey, mySecretKey) + +Encrypts and authenticates message using peer's public key, our secret key, and +the given nonce, which must be unique for each distinct message for a key pair. + +Returns an encrypted and authenticated message, which is +`nacl.box.overheadLength` longer than the original message. + +#### nacl.box.open(box, nonce, theirPublicKey, mySecretKey) + +Authenticates and decrypts the given box with peer's public key, our secret +key, and the given nonce. + +Returns the original message, or `null` if authentication fails. + +#### nacl.box.before(theirPublicKey, mySecretKey) + +Returns a precomputed shared key which can be used in `nacl.box.after` and +`nacl.box.open.after`. + +#### nacl.box.after(message, nonce, sharedKey) + +Same as `nacl.box`, but uses a shared key precomputed with `nacl.box.before`. + +#### nacl.box.open.after(box, nonce, sharedKey) + +Same as `nacl.box.open`, but uses a shared key precomputed with `nacl.box.before`. + +#### Constants + +##### nacl.box.publicKeyLength = 32 + +Length of public key in bytes. + +##### nacl.box.secretKeyLength = 32 + +Length of secret key in bytes. + +##### nacl.box.sharedKeyLength = 32 + +Length of precomputed shared key in bytes. + +##### nacl.box.nonceLength = 24 + +Length of nonce in bytes. + +##### nacl.box.overheadLength = 16 + +Length of overhead added to box compared to original message. + + +### Secret-key authenticated encryption (secretbox) + +Implements *xsalsa20-poly1305*. + +#### nacl.secretbox(message, nonce, key) + +Encrypts and authenticates message using the key and the nonce. The nonce must +be unique for each distinct message for this key. + +Returns an encrypted and authenticated message, which is +`nacl.secretbox.overheadLength` longer than the original message. + +#### nacl.secretbox.open(box, nonce, key) + +Authenticates and decrypts the given secret box using the key and the nonce. + +Returns the original message, or `null` if authentication fails. + +#### Constants + +##### nacl.secretbox.keyLength = 32 + +Length of key in bytes. + +##### nacl.secretbox.nonceLength = 24 + +Length of nonce in bytes. + +##### nacl.secretbox.overheadLength = 16 + +Length of overhead added to secret box compared to original message. + + +### Scalar multiplication + +Implements *x25519*. + +#### nacl.scalarMult(n, p) + +Multiplies an integer `n` by a group element `p` and returns the resulting +group element. + +#### nacl.scalarMult.base(n) + +Multiplies an integer `n` by a standard group element and returns the resulting +group element. + +#### Constants + +##### nacl.scalarMult.scalarLength = 32 + +Length of scalar in bytes. + +##### nacl.scalarMult.groupElementLength = 32 + +Length of group element in bytes. + + +### Signatures + +Implements [ed25519](http://ed25519.cr.yp.to). + +#### nacl.sign.keyPair() + +Generates new random key pair for signing and returns it as an object with +`publicKey` and `secretKey` members: + + { + publicKey: ..., // Uint8Array with 32-byte public key + secretKey: ... // Uint8Array with 64-byte secret key + } + +#### nacl.sign.keyPair.fromSecretKey(secretKey) + +Returns a signing key pair with public key corresponding to the given +64-byte secret key. The secret key must have been generated by +`nacl.sign.keyPair` or `nacl.sign.keyPair.fromSeed`. + +#### nacl.sign.keyPair.fromSeed(seed) + +Returns a new signing key pair generated deterministically from a 32-byte seed. +The seed must contain enough entropy to be secure. This method is not +recommended for general use: instead, use `nacl.sign.keyPair` to generate a new +key pair from a random seed. + +#### nacl.sign(message, secretKey) + +Signs the message using the secret key and returns a signed message. + +#### nacl.sign.open(signedMessage, publicKey) + +Verifies the signed message and returns the message without signature. + +Returns `null` if verification failed. + +#### nacl.sign.detached(message, secretKey) + +Signs the message using the secret key and returns a signature. + +#### nacl.sign.detached.verify(message, signature, publicKey) + +Verifies the signature for the message and returns `true` if verification +succeeded or `false` if it failed. + +#### Constants + +##### nacl.sign.publicKeyLength = 32 + +Length of signing public key in bytes. + +##### nacl.sign.secretKeyLength = 64 + +Length of signing secret key in bytes. + +##### nacl.sign.seedLength = 32 + +Length of seed for `nacl.sign.keyPair.fromSeed` in bytes. + +##### nacl.sign.signatureLength = 64 + +Length of signature in bytes. + + +### Hashing + +Implements *SHA-512*. + +#### nacl.hash(message) + +Returns SHA-512 hash of the message. + +#### Constants + +##### nacl.hash.hashLength = 64 + +Length of hash in bytes. + + +### Random bytes generation + +#### nacl.randomBytes(length) + +Returns a `Uint8Array` of the given length containing random bytes of +cryptographic quality. + +**Implementation note** + +TweetNaCl.js uses the following methods to generate random bytes, +depending on the platform it runs on: + +* `window.crypto.getRandomValues` (WebCrypto standard) +* `window.msCrypto.getRandomValues` (Internet Explorer 11) +* `crypto.randomBytes` (Node.js) + +If the platform doesn't provide a suitable PRNG, the following functions, +which require random numbers, will throw exception: + +* `nacl.randomBytes` +* `nacl.box.keyPair` +* `nacl.sign.keyPair` + +Other functions are deterministic and will continue working. + +If a platform you are targeting doesn't implement secure random number +generator, but you somehow have a cryptographically-strong source of entropy +(not `Math.random`!), and you know what you are doing, you can plug it into +TweetNaCl.js like this: + + nacl.setPRNG(function(x, n) { + // ... copy n random bytes into x ... + }); + +Note that `nacl.setPRNG` *completely replaces* internal random byte generator +with the one provided. + + +### Constant-time comparison + +#### nacl.verify(x, y) + +Compares `x` and `y` in constant time and returns `true` if their lengths are +non-zero and equal, and their contents are equal. + +Returns `false` if either of the arguments has zero length, or arguments have +different lengths, or their contents differ. + + +System requirements +------------------- + +TweetNaCl.js supports modern browsers that have a cryptographically secure +pseudorandom number generator and typed arrays, including the latest versions +of: + +* Chrome +* Firefox +* Safari (Mac, iOS) +* Internet Explorer 11 + +Other systems: + +* Node.js + + +Development and testing +------------------------ + +Install NPM modules needed for development: + + $ npm install + +To build minified versions: + + $ npm run build + +Tests use minified version, so make sure to rebuild it every time you change +`nacl.js` or `nacl-fast.js`. + +### Testing + +To run tests in Node.js: + + $ npm run test-node + +By default all tests described here work on `nacl.min.js`. To test other +versions, set environment variable `NACL_SRC` to the file name you want to test. +For example, the following command will test fast minified version: + + $ NACL_SRC=nacl-fast.min.js npm run test-node + +To run full suite of tests in Node.js, including comparing outputs of +JavaScript port to outputs of the original C version: + + $ npm run test-node-all + +To prepare tests for browsers: + + $ npm run build-test-browser + +and then open `test/browser/test.html` (or `test/browser/test-fast.html`) to +run them. + +To run tests in both Node and Electron: + + $ npm test + +### Benchmarking + +To run benchmarks in Node.js: + + $ npm run bench + $ NACL_SRC=nacl-fast.min.js npm run bench + +To run benchmarks in a browser, open `test/benchmark/bench.html` (or +`test/benchmark/bench-fast.html`). + + +Benchmarks +---------- + +For reference, here are benchmarks from MacBook Pro (Retina, 13-inch, Mid 2014) +laptop with 2.6 GHz Intel Core i5 CPU (Intel) in Chrome 53/OS X and Xiaomi Redmi +Note 3 smartphone with 1.8 GHz Qualcomm Snapdragon 650 64-bit CPU (ARM) in +Chrome 52/Android: + +| | nacl.js Intel | nacl-fast.js Intel | nacl.js ARM | nacl-fast.js ARM | +| ------------- |:-------------:|:-------------------:|:-------------:|:-----------------:| +| salsa20 | 1.3 MB/s | 128 MB/s | 0.4 MB/s | 43 MB/s | +| poly1305 | 13 MB/s | 171 MB/s | 4 MB/s | 52 MB/s | +| hash | 4 MB/s | 34 MB/s | 0.9 MB/s | 12 MB/s | +| secretbox 1K | 1113 op/s | 57583 op/s | 334 op/s | 14227 op/s | +| box 1K | 145 op/s | 718 op/s | 37 op/s | 368 op/s | +| scalarMult | 171 op/s | 733 op/s | 56 op/s | 380 op/s | +| sign | 77 op/s | 200 op/s | 20 op/s | 61 op/s | +| sign.open | 39 op/s | 102 op/s | 11 op/s | 31 op/s | + +(You can run benchmarks on your devices by clicking on the links at the bottom +of the [home page](https://tweetnacl.js.org)). + +In short, with *nacl-fast.js* and 1024-byte messages you can expect to encrypt and +authenticate more than 57000 messages per second on a typical laptop or more than +14000 messages per second on a $170 smartphone, sign about 200 and verify 100 +messages per second on a laptop or 60 and 30 messages per second on a smartphone, +per CPU core (with Web Workers you can do these operations in parallel), +which is good enough for most applications. + + +Contributors +------------ + +See AUTHORS.md file. + + +Third-party libraries based on TweetNaCl.js +------------------------------------------- + +* [forward-secrecy](https://github.com/alax/forward-secrecy) — Axolotl ratchet implementation +* [nacl-stream](https://github.com/dchest/nacl-stream-js) - streaming encryption +* [tweetnacl-auth-js](https://github.com/dchest/tweetnacl-auth-js) — implementation of [`crypto_auth`](http://nacl.cr.yp.to/auth.html) +* [tweetnacl-sealed-box](https://github.com/whs/tweetnacl-sealed-box) — implementation of [`sealed boxes`](https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes.html) +* [chloride](https://github.com/dominictarr/chloride) - unified API for various NaCl modules + + +Who uses it +----------- + +Some notable users of TweetNaCl.js: + +* [GitHub](https://github.com) +* [MEGA](https://github.com/meganz/webclient) +* [Stellar](https://www.stellar.org/) +* [miniLock](https://github.com/kaepora/miniLock) diff --git a/node_modules/tweetnacl/nacl-fast.js b/node_modules/tweetnacl/nacl-fast.js new file mode 100644 index 0000000..7ea5fb5 --- /dev/null +++ b/node_modules/tweetnacl/nacl-fast.js @@ -0,0 +1,2391 @@ +(function(nacl) { +'use strict'; + +// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. +// Public domain. +// +// Implementation derived from TweetNaCl version 20140427. +// See for details: http://tweetnacl.cr.yp.to/ + +var gf = function(init) { + var i, r = new Float64Array(16); + if (init) for (i = 0; i < init.length; i++) r[i] = init[i]; + return r; +}; + +// Pluggable, initialized in high-level API below. +var randombytes = function(/* x, n */) { throw new Error('no PRNG'); }; + +var _0 = new Uint8Array(16); +var _9 = new Uint8Array(32); _9[0] = 9; + +var gf0 = gf(), + gf1 = gf([1]), + _121665 = gf([0xdb41, 1]), + D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]), + D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]), + X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]), + Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]), + I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]); + +function ts64(x, i, h, l) { + x[i] = (h >> 24) & 0xff; + x[i+1] = (h >> 16) & 0xff; + x[i+2] = (h >> 8) & 0xff; + x[i+3] = h & 0xff; + x[i+4] = (l >> 24) & 0xff; + x[i+5] = (l >> 16) & 0xff; + x[i+6] = (l >> 8) & 0xff; + x[i+7] = l & 0xff; +} + +function vn(x, xi, y, yi, n) { + var i,d = 0; + for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i]; + return (1 & ((d - 1) >>> 8)) - 1; +} + +function crypto_verify_16(x, xi, y, yi) { + return vn(x,xi,y,yi,16); +} + +function crypto_verify_32(x, xi, y, yi) { + return vn(x,xi,y,yi,32); +} + +function core_salsa20(o, p, k, c) { + var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, + j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, + j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, + j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, + j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, + j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, + j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, + j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, + j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, + j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, + j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, + j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, + j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, + j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, + j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, + j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; + + var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, + x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, + x15 = j15, u; + + for (var i = 0; i < 20; i += 2) { + u = x0 + x12 | 0; + x4 ^= u<<7 | u>>>(32-7); + u = x4 + x0 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x4 | 0; + x12 ^= u<<13 | u>>>(32-13); + u = x12 + x8 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x1 | 0; + x9 ^= u<<7 | u>>>(32-7); + u = x9 + x5 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x9 | 0; + x1 ^= u<<13 | u>>>(32-13); + u = x1 + x13 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x6 | 0; + x14 ^= u<<7 | u>>>(32-7); + u = x14 + x10 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x14 | 0; + x6 ^= u<<13 | u>>>(32-13); + u = x6 + x2 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x11 | 0; + x3 ^= u<<7 | u>>>(32-7); + u = x3 + x15 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x3 | 0; + x11 ^= u<<13 | u>>>(32-13); + u = x11 + x7 | 0; + x15 ^= u<<18 | u>>>(32-18); + + u = x0 + x3 | 0; + x1 ^= u<<7 | u>>>(32-7); + u = x1 + x0 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x1 | 0; + x3 ^= u<<13 | u>>>(32-13); + u = x3 + x2 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x4 | 0; + x6 ^= u<<7 | u>>>(32-7); + u = x6 + x5 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x6 | 0; + x4 ^= u<<13 | u>>>(32-13); + u = x4 + x7 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x9 | 0; + x11 ^= u<<7 | u>>>(32-7); + u = x11 + x10 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x11 | 0; + x9 ^= u<<13 | u>>>(32-13); + u = x9 + x8 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x14 | 0; + x12 ^= u<<7 | u>>>(32-7); + u = x12 + x15 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x12 | 0; + x14 ^= u<<13 | u>>>(32-13); + u = x14 + x13 | 0; + x15 ^= u<<18 | u>>>(32-18); + } + x0 = x0 + j0 | 0; + x1 = x1 + j1 | 0; + x2 = x2 + j2 | 0; + x3 = x3 + j3 | 0; + x4 = x4 + j4 | 0; + x5 = x5 + j5 | 0; + x6 = x6 + j6 | 0; + x7 = x7 + j7 | 0; + x8 = x8 + j8 | 0; + x9 = x9 + j9 | 0; + x10 = x10 + j10 | 0; + x11 = x11 + j11 | 0; + x12 = x12 + j12 | 0; + x13 = x13 + j13 | 0; + x14 = x14 + j14 | 0; + x15 = x15 + j15 | 0; + + o[ 0] = x0 >>> 0 & 0xff; + o[ 1] = x0 >>> 8 & 0xff; + o[ 2] = x0 >>> 16 & 0xff; + o[ 3] = x0 >>> 24 & 0xff; + + o[ 4] = x1 >>> 0 & 0xff; + o[ 5] = x1 >>> 8 & 0xff; + o[ 6] = x1 >>> 16 & 0xff; + o[ 7] = x1 >>> 24 & 0xff; + + o[ 8] = x2 >>> 0 & 0xff; + o[ 9] = x2 >>> 8 & 0xff; + o[10] = x2 >>> 16 & 0xff; + o[11] = x2 >>> 24 & 0xff; + + o[12] = x3 >>> 0 & 0xff; + o[13] = x3 >>> 8 & 0xff; + o[14] = x3 >>> 16 & 0xff; + o[15] = x3 >>> 24 & 0xff; + + o[16] = x4 >>> 0 & 0xff; + o[17] = x4 >>> 8 & 0xff; + o[18] = x4 >>> 16 & 0xff; + o[19] = x4 >>> 24 & 0xff; + + o[20] = x5 >>> 0 & 0xff; + o[21] = x5 >>> 8 & 0xff; + o[22] = x5 >>> 16 & 0xff; + o[23] = x5 >>> 24 & 0xff; + + o[24] = x6 >>> 0 & 0xff; + o[25] = x6 >>> 8 & 0xff; + o[26] = x6 >>> 16 & 0xff; + o[27] = x6 >>> 24 & 0xff; + + o[28] = x7 >>> 0 & 0xff; + o[29] = x7 >>> 8 & 0xff; + o[30] = x7 >>> 16 & 0xff; + o[31] = x7 >>> 24 & 0xff; + + o[32] = x8 >>> 0 & 0xff; + o[33] = x8 >>> 8 & 0xff; + o[34] = x8 >>> 16 & 0xff; + o[35] = x8 >>> 24 & 0xff; + + o[36] = x9 >>> 0 & 0xff; + o[37] = x9 >>> 8 & 0xff; + o[38] = x9 >>> 16 & 0xff; + o[39] = x9 >>> 24 & 0xff; + + o[40] = x10 >>> 0 & 0xff; + o[41] = x10 >>> 8 & 0xff; + o[42] = x10 >>> 16 & 0xff; + o[43] = x10 >>> 24 & 0xff; + + o[44] = x11 >>> 0 & 0xff; + o[45] = x11 >>> 8 & 0xff; + o[46] = x11 >>> 16 & 0xff; + o[47] = x11 >>> 24 & 0xff; + + o[48] = x12 >>> 0 & 0xff; + o[49] = x12 >>> 8 & 0xff; + o[50] = x12 >>> 16 & 0xff; + o[51] = x12 >>> 24 & 0xff; + + o[52] = x13 >>> 0 & 0xff; + o[53] = x13 >>> 8 & 0xff; + o[54] = x13 >>> 16 & 0xff; + o[55] = x13 >>> 24 & 0xff; + + o[56] = x14 >>> 0 & 0xff; + o[57] = x14 >>> 8 & 0xff; + o[58] = x14 >>> 16 & 0xff; + o[59] = x14 >>> 24 & 0xff; + + o[60] = x15 >>> 0 & 0xff; + o[61] = x15 >>> 8 & 0xff; + o[62] = x15 >>> 16 & 0xff; + o[63] = x15 >>> 24 & 0xff; +} + +function core_hsalsa20(o,p,k,c) { + var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, + j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, + j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, + j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, + j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, + j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, + j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, + j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, + j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, + j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, + j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, + j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, + j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, + j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, + j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, + j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; + + var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, + x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, + x15 = j15, u; + + for (var i = 0; i < 20; i += 2) { + u = x0 + x12 | 0; + x4 ^= u<<7 | u>>>(32-7); + u = x4 + x0 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x4 | 0; + x12 ^= u<<13 | u>>>(32-13); + u = x12 + x8 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x1 | 0; + x9 ^= u<<7 | u>>>(32-7); + u = x9 + x5 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x9 | 0; + x1 ^= u<<13 | u>>>(32-13); + u = x1 + x13 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x6 | 0; + x14 ^= u<<7 | u>>>(32-7); + u = x14 + x10 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x14 | 0; + x6 ^= u<<13 | u>>>(32-13); + u = x6 + x2 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x11 | 0; + x3 ^= u<<7 | u>>>(32-7); + u = x3 + x15 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x3 | 0; + x11 ^= u<<13 | u>>>(32-13); + u = x11 + x7 | 0; + x15 ^= u<<18 | u>>>(32-18); + + u = x0 + x3 | 0; + x1 ^= u<<7 | u>>>(32-7); + u = x1 + x0 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x1 | 0; + x3 ^= u<<13 | u>>>(32-13); + u = x3 + x2 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x4 | 0; + x6 ^= u<<7 | u>>>(32-7); + u = x6 + x5 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x6 | 0; + x4 ^= u<<13 | u>>>(32-13); + u = x4 + x7 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x9 | 0; + x11 ^= u<<7 | u>>>(32-7); + u = x11 + x10 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x11 | 0; + x9 ^= u<<13 | u>>>(32-13); + u = x9 + x8 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x14 | 0; + x12 ^= u<<7 | u>>>(32-7); + u = x12 + x15 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x12 | 0; + x14 ^= u<<13 | u>>>(32-13); + u = x14 + x13 | 0; + x15 ^= u<<18 | u>>>(32-18); + } + + o[ 0] = x0 >>> 0 & 0xff; + o[ 1] = x0 >>> 8 & 0xff; + o[ 2] = x0 >>> 16 & 0xff; + o[ 3] = x0 >>> 24 & 0xff; + + o[ 4] = x5 >>> 0 & 0xff; + o[ 5] = x5 >>> 8 & 0xff; + o[ 6] = x5 >>> 16 & 0xff; + o[ 7] = x5 >>> 24 & 0xff; + + o[ 8] = x10 >>> 0 & 0xff; + o[ 9] = x10 >>> 8 & 0xff; + o[10] = x10 >>> 16 & 0xff; + o[11] = x10 >>> 24 & 0xff; + + o[12] = x15 >>> 0 & 0xff; + o[13] = x15 >>> 8 & 0xff; + o[14] = x15 >>> 16 & 0xff; + o[15] = x15 >>> 24 & 0xff; + + o[16] = x6 >>> 0 & 0xff; + o[17] = x6 >>> 8 & 0xff; + o[18] = x6 >>> 16 & 0xff; + o[19] = x6 >>> 24 & 0xff; + + o[20] = x7 >>> 0 & 0xff; + o[21] = x7 >>> 8 & 0xff; + o[22] = x7 >>> 16 & 0xff; + o[23] = x7 >>> 24 & 0xff; + + o[24] = x8 >>> 0 & 0xff; + o[25] = x8 >>> 8 & 0xff; + o[26] = x8 >>> 16 & 0xff; + o[27] = x8 >>> 24 & 0xff; + + o[28] = x9 >>> 0 & 0xff; + o[29] = x9 >>> 8 & 0xff; + o[30] = x9 >>> 16 & 0xff; + o[31] = x9 >>> 24 & 0xff; +} + +function crypto_core_salsa20(out,inp,k,c) { + core_salsa20(out,inp,k,c); +} + +function crypto_core_hsalsa20(out,inp,k,c) { + core_hsalsa20(out,inp,k,c); +} + +var sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]); + // "expand 32-byte k" + +function crypto_stream_salsa20_xor(c,cpos,m,mpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = m[mpos+i] ^ x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + mpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = m[mpos+i] ^ x[i]; + } + return 0; +} + +function crypto_stream_salsa20(c,cpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = x[i]; + } + return 0; +} + +function crypto_stream(c,cpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + var sn = new Uint8Array(8); + for (var i = 0; i < 8; i++) sn[i] = n[i+16]; + return crypto_stream_salsa20(c,cpos,d,sn,s); +} + +function crypto_stream_xor(c,cpos,m,mpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + var sn = new Uint8Array(8); + for (var i = 0; i < 8; i++) sn[i] = n[i+16]; + return crypto_stream_salsa20_xor(c,cpos,m,mpos,d,sn,s); +} + +/* +* Port of Andrew Moon's Poly1305-donna-16. Public domain. +* https://github.com/floodyberry/poly1305-donna +*/ + +var poly1305 = function(key) { + this.buffer = new Uint8Array(16); + this.r = new Uint16Array(10); + this.h = new Uint16Array(10); + this.pad = new Uint16Array(8); + this.leftover = 0; + this.fin = 0; + + var t0, t1, t2, t3, t4, t5, t6, t7; + + t0 = key[ 0] & 0xff | (key[ 1] & 0xff) << 8; this.r[0] = ( t0 ) & 0x1fff; + t1 = key[ 2] & 0xff | (key[ 3] & 0xff) << 8; this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff; + t2 = key[ 4] & 0xff | (key[ 5] & 0xff) << 8; this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03; + t3 = key[ 6] & 0xff | (key[ 7] & 0xff) << 8; this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff; + t4 = key[ 8] & 0xff | (key[ 9] & 0xff) << 8; this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff; + this.r[5] = ((t4 >>> 1)) & 0x1ffe; + t5 = key[10] & 0xff | (key[11] & 0xff) << 8; this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff; + t6 = key[12] & 0xff | (key[13] & 0xff) << 8; this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81; + t7 = key[14] & 0xff | (key[15] & 0xff) << 8; this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff; + this.r[9] = ((t7 >>> 5)) & 0x007f; + + this.pad[0] = key[16] & 0xff | (key[17] & 0xff) << 8; + this.pad[1] = key[18] & 0xff | (key[19] & 0xff) << 8; + this.pad[2] = key[20] & 0xff | (key[21] & 0xff) << 8; + this.pad[3] = key[22] & 0xff | (key[23] & 0xff) << 8; + this.pad[4] = key[24] & 0xff | (key[25] & 0xff) << 8; + this.pad[5] = key[26] & 0xff | (key[27] & 0xff) << 8; + this.pad[6] = key[28] & 0xff | (key[29] & 0xff) << 8; + this.pad[7] = key[30] & 0xff | (key[31] & 0xff) << 8; +}; + +poly1305.prototype.blocks = function(m, mpos, bytes) { + var hibit = this.fin ? 0 : (1 << 11); + var t0, t1, t2, t3, t4, t5, t6, t7, c; + var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; + + var h0 = this.h[0], + h1 = this.h[1], + h2 = this.h[2], + h3 = this.h[3], + h4 = this.h[4], + h5 = this.h[5], + h6 = this.h[6], + h7 = this.h[7], + h8 = this.h[8], + h9 = this.h[9]; + + var r0 = this.r[0], + r1 = this.r[1], + r2 = this.r[2], + r3 = this.r[3], + r4 = this.r[4], + r5 = this.r[5], + r6 = this.r[6], + r7 = this.r[7], + r8 = this.r[8], + r9 = this.r[9]; + + while (bytes >= 16) { + t0 = m[mpos+ 0] & 0xff | (m[mpos+ 1] & 0xff) << 8; h0 += ( t0 ) & 0x1fff; + t1 = m[mpos+ 2] & 0xff | (m[mpos+ 3] & 0xff) << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff; + t2 = m[mpos+ 4] & 0xff | (m[mpos+ 5] & 0xff) << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff; + t3 = m[mpos+ 6] & 0xff | (m[mpos+ 7] & 0xff) << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff; + t4 = m[mpos+ 8] & 0xff | (m[mpos+ 9] & 0xff) << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff; + h5 += ((t4 >>> 1)) & 0x1fff; + t5 = m[mpos+10] & 0xff | (m[mpos+11] & 0xff) << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff; + t6 = m[mpos+12] & 0xff | (m[mpos+13] & 0xff) << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff; + t7 = m[mpos+14] & 0xff | (m[mpos+15] & 0xff) << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff; + h9 += ((t7 >>> 5)) | hibit; + + c = 0; + + d0 = c; + d0 += h0 * r0; + d0 += h1 * (5 * r9); + d0 += h2 * (5 * r8); + d0 += h3 * (5 * r7); + d0 += h4 * (5 * r6); + c = (d0 >>> 13); d0 &= 0x1fff; + d0 += h5 * (5 * r5); + d0 += h6 * (5 * r4); + d0 += h7 * (5 * r3); + d0 += h8 * (5 * r2); + d0 += h9 * (5 * r1); + c += (d0 >>> 13); d0 &= 0x1fff; + + d1 = c; + d1 += h0 * r1; + d1 += h1 * r0; + d1 += h2 * (5 * r9); + d1 += h3 * (5 * r8); + d1 += h4 * (5 * r7); + c = (d1 >>> 13); d1 &= 0x1fff; + d1 += h5 * (5 * r6); + d1 += h6 * (5 * r5); + d1 += h7 * (5 * r4); + d1 += h8 * (5 * r3); + d1 += h9 * (5 * r2); + c += (d1 >>> 13); d1 &= 0x1fff; + + d2 = c; + d2 += h0 * r2; + d2 += h1 * r1; + d2 += h2 * r0; + d2 += h3 * (5 * r9); + d2 += h4 * (5 * r8); + c = (d2 >>> 13); d2 &= 0x1fff; + d2 += h5 * (5 * r7); + d2 += h6 * (5 * r6); + d2 += h7 * (5 * r5); + d2 += h8 * (5 * r4); + d2 += h9 * (5 * r3); + c += (d2 >>> 13); d2 &= 0x1fff; + + d3 = c; + d3 += h0 * r3; + d3 += h1 * r2; + d3 += h2 * r1; + d3 += h3 * r0; + d3 += h4 * (5 * r9); + c = (d3 >>> 13); d3 &= 0x1fff; + d3 += h5 * (5 * r8); + d3 += h6 * (5 * r7); + d3 += h7 * (5 * r6); + d3 += h8 * (5 * r5); + d3 += h9 * (5 * r4); + c += (d3 >>> 13); d3 &= 0x1fff; + + d4 = c; + d4 += h0 * r4; + d4 += h1 * r3; + d4 += h2 * r2; + d4 += h3 * r1; + d4 += h4 * r0; + c = (d4 >>> 13); d4 &= 0x1fff; + d4 += h5 * (5 * r9); + d4 += h6 * (5 * r8); + d4 += h7 * (5 * r7); + d4 += h8 * (5 * r6); + d4 += h9 * (5 * r5); + c += (d4 >>> 13); d4 &= 0x1fff; + + d5 = c; + d5 += h0 * r5; + d5 += h1 * r4; + d5 += h2 * r3; + d5 += h3 * r2; + d5 += h4 * r1; + c = (d5 >>> 13); d5 &= 0x1fff; + d5 += h5 * r0; + d5 += h6 * (5 * r9); + d5 += h7 * (5 * r8); + d5 += h8 * (5 * r7); + d5 += h9 * (5 * r6); + c += (d5 >>> 13); d5 &= 0x1fff; + + d6 = c; + d6 += h0 * r6; + d6 += h1 * r5; + d6 += h2 * r4; + d6 += h3 * r3; + d6 += h4 * r2; + c = (d6 >>> 13); d6 &= 0x1fff; + d6 += h5 * r1; + d6 += h6 * r0; + d6 += h7 * (5 * r9); + d6 += h8 * (5 * r8); + d6 += h9 * (5 * r7); + c += (d6 >>> 13); d6 &= 0x1fff; + + d7 = c; + d7 += h0 * r7; + d7 += h1 * r6; + d7 += h2 * r5; + d7 += h3 * r4; + d7 += h4 * r3; + c = (d7 >>> 13); d7 &= 0x1fff; + d7 += h5 * r2; + d7 += h6 * r1; + d7 += h7 * r0; + d7 += h8 * (5 * r9); + d7 += h9 * (5 * r8); + c += (d7 >>> 13); d7 &= 0x1fff; + + d8 = c; + d8 += h0 * r8; + d8 += h1 * r7; + d8 += h2 * r6; + d8 += h3 * r5; + d8 += h4 * r4; + c = (d8 >>> 13); d8 &= 0x1fff; + d8 += h5 * r3; + d8 += h6 * r2; + d8 += h7 * r1; + d8 += h8 * r0; + d8 += h9 * (5 * r9); + c += (d8 >>> 13); d8 &= 0x1fff; + + d9 = c; + d9 += h0 * r9; + d9 += h1 * r8; + d9 += h2 * r7; + d9 += h3 * r6; + d9 += h4 * r5; + c = (d9 >>> 13); d9 &= 0x1fff; + d9 += h5 * r4; + d9 += h6 * r3; + d9 += h7 * r2; + d9 += h8 * r1; + d9 += h9 * r0; + c += (d9 >>> 13); d9 &= 0x1fff; + + c = (((c << 2) + c)) | 0; + c = (c + d0) | 0; + d0 = c & 0x1fff; + c = (c >>> 13); + d1 += c; + + h0 = d0; + h1 = d1; + h2 = d2; + h3 = d3; + h4 = d4; + h5 = d5; + h6 = d6; + h7 = d7; + h8 = d8; + h9 = d9; + + mpos += 16; + bytes -= 16; + } + this.h[0] = h0; + this.h[1] = h1; + this.h[2] = h2; + this.h[3] = h3; + this.h[4] = h4; + this.h[5] = h5; + this.h[6] = h6; + this.h[7] = h7; + this.h[8] = h8; + this.h[9] = h9; +}; + +poly1305.prototype.finish = function(mac, macpos) { + var g = new Uint16Array(10); + var c, mask, f, i; + + if (this.leftover) { + i = this.leftover; + this.buffer[i++] = 1; + for (; i < 16; i++) this.buffer[i] = 0; + this.fin = 1; + this.blocks(this.buffer, 0, 16); + } + + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + for (i = 2; i < 10; i++) { + this.h[i] += c; + c = this.h[i] >>> 13; + this.h[i] &= 0x1fff; + } + this.h[0] += (c * 5); + c = this.h[0] >>> 13; + this.h[0] &= 0x1fff; + this.h[1] += c; + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + this.h[2] += c; + + g[0] = this.h[0] + 5; + c = g[0] >>> 13; + g[0] &= 0x1fff; + for (i = 1; i < 10; i++) { + g[i] = this.h[i] + c; + c = g[i] >>> 13; + g[i] &= 0x1fff; + } + g[9] -= (1 << 13); + + mask = (c ^ 1) - 1; + for (i = 0; i < 10; i++) g[i] &= mask; + mask = ~mask; + for (i = 0; i < 10; i++) this.h[i] = (this.h[i] & mask) | g[i]; + + this.h[0] = ((this.h[0] ) | (this.h[1] << 13) ) & 0xffff; + this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10) ) & 0xffff; + this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7) ) & 0xffff; + this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4) ) & 0xffff; + this.h[4] = ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff; + this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11) ) & 0xffff; + this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8) ) & 0xffff; + this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5) ) & 0xffff; + + f = this.h[0] + this.pad[0]; + this.h[0] = f & 0xffff; + for (i = 1; i < 8; i++) { + f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0; + this.h[i] = f & 0xffff; + } + + mac[macpos+ 0] = (this.h[0] >>> 0) & 0xff; + mac[macpos+ 1] = (this.h[0] >>> 8) & 0xff; + mac[macpos+ 2] = (this.h[1] >>> 0) & 0xff; + mac[macpos+ 3] = (this.h[1] >>> 8) & 0xff; + mac[macpos+ 4] = (this.h[2] >>> 0) & 0xff; + mac[macpos+ 5] = (this.h[2] >>> 8) & 0xff; + mac[macpos+ 6] = (this.h[3] >>> 0) & 0xff; + mac[macpos+ 7] = (this.h[3] >>> 8) & 0xff; + mac[macpos+ 8] = (this.h[4] >>> 0) & 0xff; + mac[macpos+ 9] = (this.h[4] >>> 8) & 0xff; + mac[macpos+10] = (this.h[5] >>> 0) & 0xff; + mac[macpos+11] = (this.h[5] >>> 8) & 0xff; + mac[macpos+12] = (this.h[6] >>> 0) & 0xff; + mac[macpos+13] = (this.h[6] >>> 8) & 0xff; + mac[macpos+14] = (this.h[7] >>> 0) & 0xff; + mac[macpos+15] = (this.h[7] >>> 8) & 0xff; +}; + +poly1305.prototype.update = function(m, mpos, bytes) { + var i, want; + + if (this.leftover) { + want = (16 - this.leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + this.buffer[this.leftover + i] = m[mpos+i]; + bytes -= want; + mpos += want; + this.leftover += want; + if (this.leftover < 16) + return; + this.blocks(this.buffer, 0, 16); + this.leftover = 0; + } + + if (bytes >= 16) { + want = bytes - (bytes % 16); + this.blocks(m, mpos, want); + mpos += want; + bytes -= want; + } + + if (bytes) { + for (i = 0; i < bytes; i++) + this.buffer[this.leftover + i] = m[mpos+i]; + this.leftover += bytes; + } +}; + +function crypto_onetimeauth(out, outpos, m, mpos, n, k) { + var s = new poly1305(k); + s.update(m, mpos, n); + s.finish(out, outpos); + return 0; +} + +function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) { + var x = new Uint8Array(16); + crypto_onetimeauth(x,0,m,mpos,n,k); + return crypto_verify_16(h,hpos,x,0); +} + +function crypto_secretbox(c,m,d,n,k) { + var i; + if (d < 32) return -1; + crypto_stream_xor(c,0,m,0,d,n,k); + crypto_onetimeauth(c, 16, c, 32, d - 32, c); + for (i = 0; i < 16; i++) c[i] = 0; + return 0; +} + +function crypto_secretbox_open(m,c,d,n,k) { + var i; + var x = new Uint8Array(32); + if (d < 32) return -1; + crypto_stream(x,0,32,n,k); + if (crypto_onetimeauth_verify(c, 16,c, 32,d - 32,x) !== 0) return -1; + crypto_stream_xor(m,0,c,0,d,n,k); + for (i = 0; i < 32; i++) m[i] = 0; + return 0; +} + +function set25519(r, a) { + var i; + for (i = 0; i < 16; i++) r[i] = a[i]|0; +} + +function car25519(o) { + var i, v, c = 1; + for (i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c-1 + 37 * (c-1); +} + +function sel25519(p, q, b) { + var t, c = ~(b-1); + for (var i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} + +function pack25519(o, n) { + var i, j, b; + var m = gf(), t = gf(); + for (i = 0; i < 16; i++) t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1); + m[i-1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1); + b = (m[15]>>16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1-b); + } + for (i = 0; i < 16; i++) { + o[2*i] = t[i] & 0xff; + o[2*i+1] = t[i]>>8; + } +} + +function neq25519(a, b) { + var c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); +} + +function par25519(a) { + var d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; +} + +function unpack25519(o, n) { + var i; + for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8); + o[15] &= 0x7fff; +} + +function A(o, a, b) { + for (var i = 0; i < 16; i++) o[i] = a[i] + b[i]; +} + +function Z(o, a, b) { + for (var i = 0; i < 16; i++) o[i] = a[i] - b[i]; +} + +function M(o, a, b) { + var v, c, + t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, + t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, + t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, + t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, + b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5], + b6 = b[6], + b7 = b[7], + b8 = b[8], + b9 = b[9], + b10 = b[10], + b11 = b[11], + b12 = b[12], + b13 = b[13], + b14 = b[14], + b15 = b[15]; + + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + // t15 left as is + + // first car + c = 1; + v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536; + v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536; + v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536; + v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536; + v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536; + v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536; + v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536; + v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536; + v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536; + v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536; + v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536; + v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536; + v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536; + v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536; + v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536; + v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536; + t0 += c-1 + 37 * (c-1); + + // second car + c = 1; + v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536; + v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536; + v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536; + v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536; + v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536; + v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536; + v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536; + v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536; + v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536; + v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536; + v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536; + v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536; + v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536; + v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536; + v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536; + v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536; + t0 += c-1 + 37 * (c-1); + + o[ 0] = t0; + o[ 1] = t1; + o[ 2] = t2; + o[ 3] = t3; + o[ 4] = t4; + o[ 5] = t5; + o[ 6] = t6; + o[ 7] = t7; + o[ 8] = t8; + o[ 9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; +} + +function S(o, a) { + M(o, a, a); +} + +function inv25519(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S(c, c); + if(a !== 2 && a !== 4) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function pow2523(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S(c, c); + if(a !== 1) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function crypto_scalarmult(q, n, p) { + var z = new Uint8Array(32); + var x = new Float64Array(80), r, i; + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(); + for (i = 0; i < 31; i++) z[i] = n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + for (i = 0; i < 16; i++) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for (i=254; i>=0; --i) { + r=(z[i>>>3]>>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + for (i = 0; i < 16; i++) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + var x32 = x.subarray(32); + var x16 = x.subarray(16); + inv25519(x32,x32); + M(x16,x16,x32); + pack25519(q,x16); + return 0; +} + +function crypto_scalarmult_base(q, n) { + return crypto_scalarmult(q, n, _9); +} + +function crypto_box_keypair(y, x) { + randombytes(x, 32); + return crypto_scalarmult_base(y, x); +} + +function crypto_box_beforenm(k, y, x) { + var s = new Uint8Array(32); + crypto_scalarmult(s, x, y); + return crypto_core_hsalsa20(k, _0, s, sigma); +} + +var crypto_box_afternm = crypto_secretbox; +var crypto_box_open_afternm = crypto_secretbox_open; + +function crypto_box(c, m, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_afternm(c, m, d, n, k); +} + +function crypto_box_open(m, c, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_open_afternm(m, c, d, n, k); +} + +var K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 +]; + +function crypto_hashblocks_hl(hh, hl, m, n) { + var wh = new Int32Array(16), wl = new Int32Array(16), + bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, + bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, + th, tl, i, j, h, l, a, b, c, d; + + var ah0 = hh[0], + ah1 = hh[1], + ah2 = hh[2], + ah3 = hh[3], + ah4 = hh[4], + ah5 = hh[5], + ah6 = hh[6], + ah7 = hh[7], + + al0 = hl[0], + al1 = hl[1], + al2 = hl[2], + al3 = hl[3], + al4 = hl[4], + al5 = hl[5], + al6 = hl[6], + al7 = hl[7]; + + var pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) { + j = 8 * i + pos; + wh[i] = (m[j+0] << 24) | (m[j+1] << 16) | (m[j+2] << 8) | m[j+3]; + wl[i] = (m[j+4] << 24) | (m[j+5] << 16) | (m[j+6] << 8) | m[j+7]; + } + for (i = 0; i < 80; i++) { + bh0 = ah0; + bh1 = ah1; + bh2 = ah2; + bh3 = ah3; + bh4 = ah4; + bh5 = ah5; + bh6 = ah6; + bh7 = ah7; + + bl0 = al0; + bl1 = al1; + bl2 = al2; + bl3 = al3; + bl4 = al4; + bl5 = al5; + bl6 = al6; + bl7 = al7; + + // add + h = ah7; + l = al7; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + // Sigma1 + h = ((ah4 >>> 14) | (al4 << (32-14))) ^ ((ah4 >>> 18) | (al4 << (32-18))) ^ ((al4 >>> (41-32)) | (ah4 << (32-(41-32)))); + l = ((al4 >>> 14) | (ah4 << (32-14))) ^ ((al4 >>> 18) | (ah4 << (32-18))) ^ ((ah4 >>> (41-32)) | (al4 << (32-(41-32)))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // Ch + h = (ah4 & ah5) ^ (~ah4 & ah6); + l = (al4 & al5) ^ (~al4 & al6); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // K + h = K[i*2]; + l = K[i*2+1]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // w + h = wh[i%16]; + l = wl[i%16]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + th = c & 0xffff | d << 16; + tl = a & 0xffff | b << 16; + + // add + h = th; + l = tl; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + // Sigma0 + h = ((ah0 >>> 28) | (al0 << (32-28))) ^ ((al0 >>> (34-32)) | (ah0 << (32-(34-32)))) ^ ((al0 >>> (39-32)) | (ah0 << (32-(39-32)))); + l = ((al0 >>> 28) | (ah0 << (32-28))) ^ ((ah0 >>> (34-32)) | (al0 << (32-(34-32)))) ^ ((ah0 >>> (39-32)) | (al0 << (32-(39-32)))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // Maj + h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); + l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + bh7 = (c & 0xffff) | (d << 16); + bl7 = (a & 0xffff) | (b << 16); + + // add + h = bh3; + l = bl3; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = th; + l = tl; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + bh3 = (c & 0xffff) | (d << 16); + bl3 = (a & 0xffff) | (b << 16); + + ah1 = bh0; + ah2 = bh1; + ah3 = bh2; + ah4 = bh3; + ah5 = bh4; + ah6 = bh5; + ah7 = bh6; + ah0 = bh7; + + al1 = bl0; + al2 = bl1; + al3 = bl2; + al4 = bl3; + al5 = bl4; + al6 = bl5; + al7 = bl6; + al0 = bl7; + + if (i%16 === 15) { + for (j = 0; j < 16; j++) { + // add + h = wh[j]; + l = wl[j]; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = wh[(j+9)%16]; + l = wl[(j+9)%16]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // sigma0 + th = wh[(j+1)%16]; + tl = wl[(j+1)%16]; + h = ((th >>> 1) | (tl << (32-1))) ^ ((th >>> 8) | (tl << (32-8))) ^ (th >>> 7); + l = ((tl >>> 1) | (th << (32-1))) ^ ((tl >>> 8) | (th << (32-8))) ^ ((tl >>> 7) | (th << (32-7))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // sigma1 + th = wh[(j+14)%16]; + tl = wl[(j+14)%16]; + h = ((th >>> 19) | (tl << (32-19))) ^ ((tl >>> (61-32)) | (th << (32-(61-32)))) ^ (th >>> 6); + l = ((tl >>> 19) | (th << (32-19))) ^ ((th >>> (61-32)) | (tl << (32-(61-32)))) ^ ((tl >>> 6) | (th << (32-6))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + wh[j] = (c & 0xffff) | (d << 16); + wl[j] = (a & 0xffff) | (b << 16); + } + } + } + + // add + h = ah0; + l = al0; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[0]; + l = hl[0]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[0] = ah0 = (c & 0xffff) | (d << 16); + hl[0] = al0 = (a & 0xffff) | (b << 16); + + h = ah1; + l = al1; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[1]; + l = hl[1]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[1] = ah1 = (c & 0xffff) | (d << 16); + hl[1] = al1 = (a & 0xffff) | (b << 16); + + h = ah2; + l = al2; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[2]; + l = hl[2]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[2] = ah2 = (c & 0xffff) | (d << 16); + hl[2] = al2 = (a & 0xffff) | (b << 16); + + h = ah3; + l = al3; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[3]; + l = hl[3]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[3] = ah3 = (c & 0xffff) | (d << 16); + hl[3] = al3 = (a & 0xffff) | (b << 16); + + h = ah4; + l = al4; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[4]; + l = hl[4]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[4] = ah4 = (c & 0xffff) | (d << 16); + hl[4] = al4 = (a & 0xffff) | (b << 16); + + h = ah5; + l = al5; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[5]; + l = hl[5]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[5] = ah5 = (c & 0xffff) | (d << 16); + hl[5] = al5 = (a & 0xffff) | (b << 16); + + h = ah6; + l = al6; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[6]; + l = hl[6]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[6] = ah6 = (c & 0xffff) | (d << 16); + hl[6] = al6 = (a & 0xffff) | (b << 16); + + h = ah7; + l = al7; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[7]; + l = hl[7]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[7] = ah7 = (c & 0xffff) | (d << 16); + hl[7] = al7 = (a & 0xffff) | (b << 16); + + pos += 128; + n -= 128; + } + + return n; +} + +function crypto_hash(out, m, n) { + var hh = new Int32Array(8), + hl = new Int32Array(8), + x = new Uint8Array(256), + i, b = n; + + hh[0] = 0x6a09e667; + hh[1] = 0xbb67ae85; + hh[2] = 0x3c6ef372; + hh[3] = 0xa54ff53a; + hh[4] = 0x510e527f; + hh[5] = 0x9b05688c; + hh[6] = 0x1f83d9ab; + hh[7] = 0x5be0cd19; + + hl[0] = 0xf3bcc908; + hl[1] = 0x84caa73b; + hl[2] = 0xfe94f82b; + hl[3] = 0x5f1d36f1; + hl[4] = 0xade682d1; + hl[5] = 0x2b3e6c1f; + hl[6] = 0xfb41bd6b; + hl[7] = 0x137e2179; + + crypto_hashblocks_hl(hh, hl, m, n); + n %= 128; + + for (i = 0; i < n; i++) x[i] = m[b-n+i]; + x[n] = 128; + + n = 256-128*(n<112?1:0); + x[n-9] = 0; + ts64(x, n-8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(hh, hl, x, n); + + for (i = 0; i < 8; i++) ts64(out, 8*i, hh[i], hl[i]); + + return 0; +} + +function add(p, q) { + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(), + g = gf(), h = gf(), t = gf(); + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +function cswap(p, q, b) { + var i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } +} + +function pack(r, p) { + var tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +function scalarmult(p, q, s) { + var b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i/8)|0] >> (i&7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } +} + +function scalarbase(p, s) { + var q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); +} + +function crypto_sign_keypair(pk, sk, seeded) { + var d = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()]; + var i; + + if (!seeded) randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p, d); + pack(pk, p); + + for (i = 0; i < 32; i++) sk[i+32] = pk[i]; + return 0; +} + +var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]); + +function modL(r, x) { + var carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = Math.floor((x[j] + 128) / 256); + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +function reduce(r) { + var x = new Float64Array(64), i; + for (i = 0; i < 64; i++) x[i] = r[i]; + for (i = 0; i < 64; i++) r[i] = 0; + modL(r, x); +} + +// Note: difference from C - smlen returned, not passed as argument. +function crypto_sign(sm, m, n, sk) { + var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + var i, j, x = new Float64Array(64); + var p = [gf(), gf(), gf(), gf()]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + var smlen = n + 64; + for (i = 0; i < n; i++) sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm.subarray(32), n+32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + + for (i = 32; i < 64; i++) sm[i] = sk[i]; + crypto_hash(h, sm, n + 64); + reduce(h); + + for (i = 0; i < 64; i++) x[i] = 0; + for (i = 0; i < 32; i++) x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i+j] += h[i] * d[j]; + } + } + + modL(sm.subarray(32), x); + return smlen; +} + +function unpackneg(r, p) { + var t = gf(), chk = gf(), num = gf(), + den = gf(), den2 = gf(), den4 = gf(), + den6 = gf(); + + set25519(r[2], gf1); + unpack25519(r[1], p); + S(num, r[1]); + M(den, num, D); + Z(num, num, r[2]); + A(den, r[2], den); + + S(den2, den); + S(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) M(r[0], r[0], I); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]); + + M(r[3], r[0], r[1]); + return 0; +} + +function crypto_sign_open(m, sm, n, pk) { + var i; + var t = new Uint8Array(32), h = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()], + q = [gf(), gf(), gf(), gf()]; + + if (n < 64) return -1; + + if (unpackneg(q, pk)) return -1; + + for (i = 0; i < n; i++) m[i] = sm[i]; + for (i = 0; i < 32; i++) m[i+32] = pk[i]; + crypto_hash(h, m, n); + reduce(h); + scalarmult(p, q, h); + + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) m[i] = 0; + return -1; + } + + for (i = 0; i < n; i++) m[i] = sm[i + 64]; + return n; +} + +var crypto_secretbox_KEYBYTES = 32, + crypto_secretbox_NONCEBYTES = 24, + crypto_secretbox_ZEROBYTES = 32, + crypto_secretbox_BOXZEROBYTES = 16, + crypto_scalarmult_BYTES = 32, + crypto_scalarmult_SCALARBYTES = 32, + crypto_box_PUBLICKEYBYTES = 32, + crypto_box_SECRETKEYBYTES = 32, + crypto_box_BEFORENMBYTES = 32, + crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES, + crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES, + crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES, + crypto_sign_BYTES = 64, + crypto_sign_PUBLICKEYBYTES = 32, + crypto_sign_SECRETKEYBYTES = 64, + crypto_sign_SEEDBYTES = 32, + crypto_hash_BYTES = 64; + +nacl.lowlevel = { + crypto_core_hsalsa20: crypto_core_hsalsa20, + crypto_stream_xor: crypto_stream_xor, + crypto_stream: crypto_stream, + crypto_stream_salsa20_xor: crypto_stream_salsa20_xor, + crypto_stream_salsa20: crypto_stream_salsa20, + crypto_onetimeauth: crypto_onetimeauth, + crypto_onetimeauth_verify: crypto_onetimeauth_verify, + crypto_verify_16: crypto_verify_16, + crypto_verify_32: crypto_verify_32, + crypto_secretbox: crypto_secretbox, + crypto_secretbox_open: crypto_secretbox_open, + crypto_scalarmult: crypto_scalarmult, + crypto_scalarmult_base: crypto_scalarmult_base, + crypto_box_beforenm: crypto_box_beforenm, + crypto_box_afternm: crypto_box_afternm, + crypto_box: crypto_box, + crypto_box_open: crypto_box_open, + crypto_box_keypair: crypto_box_keypair, + crypto_hash: crypto_hash, + crypto_sign: crypto_sign, + crypto_sign_keypair: crypto_sign_keypair, + crypto_sign_open: crypto_sign_open, + + crypto_secretbox_KEYBYTES: crypto_secretbox_KEYBYTES, + crypto_secretbox_NONCEBYTES: crypto_secretbox_NONCEBYTES, + crypto_secretbox_ZEROBYTES: crypto_secretbox_ZEROBYTES, + crypto_secretbox_BOXZEROBYTES: crypto_secretbox_BOXZEROBYTES, + crypto_scalarmult_BYTES: crypto_scalarmult_BYTES, + crypto_scalarmult_SCALARBYTES: crypto_scalarmult_SCALARBYTES, + crypto_box_PUBLICKEYBYTES: crypto_box_PUBLICKEYBYTES, + crypto_box_SECRETKEYBYTES: crypto_box_SECRETKEYBYTES, + crypto_box_BEFORENMBYTES: crypto_box_BEFORENMBYTES, + crypto_box_NONCEBYTES: crypto_box_NONCEBYTES, + crypto_box_ZEROBYTES: crypto_box_ZEROBYTES, + crypto_box_BOXZEROBYTES: crypto_box_BOXZEROBYTES, + crypto_sign_BYTES: crypto_sign_BYTES, + crypto_sign_PUBLICKEYBYTES: crypto_sign_PUBLICKEYBYTES, + crypto_sign_SECRETKEYBYTES: crypto_sign_SECRETKEYBYTES, + crypto_sign_SEEDBYTES: crypto_sign_SEEDBYTES, + crypto_hash_BYTES: crypto_hash_BYTES, + + gf: gf, + D: D, + L: L, + pack25519: pack25519, + unpack25519: unpack25519, + M: M, + A: A, + S: S, + Z: Z, + pow2523: pow2523, + add: add, + set25519: set25519, + modL: modL, + scalarmult: scalarmult, + scalarbase: scalarbase, +}; + +/* High-level API */ + +function checkLengths(k, n) { + if (k.length !== crypto_secretbox_KEYBYTES) throw new Error('bad key size'); + if (n.length !== crypto_secretbox_NONCEBYTES) throw new Error('bad nonce size'); +} + +function checkBoxLengths(pk, sk) { + if (pk.length !== crypto_box_PUBLICKEYBYTES) throw new Error('bad public key size'); + if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error('bad secret key size'); +} + +function checkArrayTypes() { + for (var i = 0; i < arguments.length; i++) { + if (!(arguments[i] instanceof Uint8Array)) + throw new TypeError('unexpected type, use Uint8Array'); + } +} + +function cleanup(arr) { + for (var i = 0; i < arr.length; i++) arr[i] = 0; +} + +nacl.randomBytes = function(n) { + var b = new Uint8Array(n); + randombytes(b, n); + return b; +}; + +nacl.secretbox = function(msg, nonce, key) { + checkArrayTypes(msg, nonce, key); + checkLengths(key, nonce); + var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length); + var c = new Uint8Array(m.length); + for (var i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i]; + crypto_secretbox(c, m, m.length, nonce, key); + return c.subarray(crypto_secretbox_BOXZEROBYTES); +}; + +nacl.secretbox.open = function(box, nonce, key) { + checkArrayTypes(box, nonce, key); + checkLengths(key, nonce); + var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length); + var m = new Uint8Array(c.length); + for (var i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i]; + if (c.length < 32) return null; + if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return null; + return m.subarray(crypto_secretbox_ZEROBYTES); +}; + +nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES; +nacl.secretbox.nonceLength = crypto_secretbox_NONCEBYTES; +nacl.secretbox.overheadLength = crypto_secretbox_BOXZEROBYTES; + +nacl.scalarMult = function(n, p) { + checkArrayTypes(n, p); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult(q, n, p); + return q; +}; + +nacl.scalarMult.base = function(n) { + checkArrayTypes(n); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult_base(q, n); + return q; +}; + +nacl.scalarMult.scalarLength = crypto_scalarmult_SCALARBYTES; +nacl.scalarMult.groupElementLength = crypto_scalarmult_BYTES; + +nacl.box = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox(msg, nonce, k); +}; + +nacl.box.before = function(publicKey, secretKey) { + checkArrayTypes(publicKey, secretKey); + checkBoxLengths(publicKey, secretKey); + var k = new Uint8Array(crypto_box_BEFORENMBYTES); + crypto_box_beforenm(k, publicKey, secretKey); + return k; +}; + +nacl.box.after = nacl.secretbox; + +nacl.box.open = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox.open(msg, nonce, k); +}; + +nacl.box.open.after = nacl.secretbox.open; + +nacl.box.keyPair = function() { + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_box_SECRETKEYBYTES); + crypto_box_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.box.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_box_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + crypto_scalarmult_base(pk, secretKey); + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES; +nacl.box.secretKeyLength = crypto_box_SECRETKEYBYTES; +nacl.box.sharedKeyLength = crypto_box_BEFORENMBYTES; +nacl.box.nonceLength = crypto_box_NONCEBYTES; +nacl.box.overheadLength = nacl.secretbox.overheadLength; + +nacl.sign = function(msg, secretKey) { + checkArrayTypes(msg, secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + return signedMsg; +}; + +nacl.sign.open = function(signedMsg, publicKey) { + checkArrayTypes(signedMsg, publicKey); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var tmp = new Uint8Array(signedMsg.length); + var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); + if (mlen < 0) return null; + var m = new Uint8Array(mlen); + for (var i = 0; i < m.length; i++) m[i] = tmp[i]; + return m; +}; + +nacl.sign.detached = function(msg, secretKey) { + var signedMsg = nacl.sign(msg, secretKey); + var sig = new Uint8Array(crypto_sign_BYTES); + for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i]; + return sig; +}; + +nacl.sign.detached.verify = function(msg, sig, publicKey) { + checkArrayTypes(msg, sig, publicKey); + if (sig.length !== crypto_sign_BYTES) + throw new Error('bad signature size'); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var sm = new Uint8Array(crypto_sign_BYTES + msg.length); + var m = new Uint8Array(crypto_sign_BYTES + msg.length); + var i; + for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i]; + for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i]; + return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0); +}; + +nacl.sign.keyPair = function() { + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + crypto_sign_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i]; + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.sign.keyPair.fromSeed = function(seed) { + checkArrayTypes(seed); + if (seed.length !== crypto_sign_SEEDBYTES) + throw new Error('bad seed size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (var i = 0; i < 32; i++) sk[i] = seed[i]; + crypto_sign_keypair(pk, sk, true); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.publicKeyLength = crypto_sign_PUBLICKEYBYTES; +nacl.sign.secretKeyLength = crypto_sign_SECRETKEYBYTES; +nacl.sign.seedLength = crypto_sign_SEEDBYTES; +nacl.sign.signatureLength = crypto_sign_BYTES; + +nacl.hash = function(msg) { + checkArrayTypes(msg); + var h = new Uint8Array(crypto_hash_BYTES); + crypto_hash(h, msg, msg.length); + return h; +}; + +nacl.hash.hashLength = crypto_hash_BYTES; + +nacl.verify = function(x, y) { + checkArrayTypes(x, y); + // Zero length arguments are considered not equal. + if (x.length === 0 || y.length === 0) return false; + if (x.length !== y.length) return false; + return (vn(x, 0, y, 0, x.length) === 0) ? true : false; +}; + +nacl.setPRNG = function(fn) { + randombytes = fn; +}; + +(function() { + // Initialize PRNG if environment provides CSPRNG. + // If not, methods calling randombytes will throw. + var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null; + if (crypto && crypto.getRandomValues) { + // Browsers. + var QUOTA = 65536; + nacl.setPRNG(function(x, n) { + var i, v = new Uint8Array(n); + for (i = 0; i < n; i += QUOTA) { + crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); + } + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } else if (typeof require !== 'undefined') { + // Node.js. + crypto = require('crypto'); + if (crypto && crypto.randomBytes) { + nacl.setPRNG(function(x, n) { + var i, v = crypto.randomBytes(n); + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } + } +})(); + +})(typeof module !== 'undefined' && module.exports ? module.exports : (self.nacl = self.nacl || {})); diff --git a/node_modules/tweetnacl/nacl-fast.min.js b/node_modules/tweetnacl/nacl-fast.min.js new file mode 100644 index 0000000..348ec2e --- /dev/null +++ b/node_modules/tweetnacl/nacl-fast.min.js @@ -0,0 +1 @@ +!function(i){"use strict";var v=function(r){var t,n=new Float64Array(16);if(r)for(t=0;t>24&255,r[t+1]=n>>16&255,r[t+2]=n>>8&255,r[t+3]=255&n,r[t+4]=e>>24&255,r[t+5]=e>>16&255,r[t+6]=e>>8&255,r[t+7]=255&e}function w(r,t,n,e,o){var i,h=0;for(i=0;i>>8)-1}function b(r,t,n,e){return w(r,t,n,e,16)}function g(r,t,n,e){return w(r,t,n,e,32)}function A(r,t,n,e){!function(r,t,n,e){for(var o,i=255&e[0]|(255&e[1])<<8|(255&e[2])<<16|(255&e[3])<<24,h=255&n[0]|(255&n[1])<<8|(255&n[2])<<16|(255&n[3])<<24,a=255&n[4]|(255&n[5])<<8|(255&n[6])<<16|(255&n[7])<<24,f=255&n[8]|(255&n[9])<<8|(255&n[10])<<16|(255&n[11])<<24,s=255&n[12]|(255&n[13])<<8|(255&n[14])<<16|(255&n[15])<<24,u=255&e[4]|(255&e[5])<<8|(255&e[6])<<16|(255&e[7])<<24,c=255&t[0]|(255&t[1])<<8|(255&t[2])<<16|(255&t[3])<<24,y=255&t[4]|(255&t[5])<<8|(255&t[6])<<16|(255&t[7])<<24,l=255&t[8]|(255&t[9])<<8|(255&t[10])<<16|(255&t[11])<<24,w=255&t[12]|(255&t[13])<<8|(255&t[14])<<16|(255&t[15])<<24,v=255&e[8]|(255&e[9])<<8|(255&e[10])<<16|(255&e[11])<<24,p=255&n[16]|(255&n[17])<<8|(255&n[18])<<16|(255&n[19])<<24,b=255&n[20]|(255&n[21])<<8|(255&n[22])<<16|(255&n[23])<<24,g=255&n[24]|(255&n[25])<<8|(255&n[26])<<16|(255&n[27])<<24,A=255&n[28]|(255&n[29])<<8|(255&n[30])<<16|(255&n[31])<<24,_=255&e[12]|(255&e[13])<<8|(255&e[14])<<16|(255&e[15])<<24,U=i,d=h,E=a,x=f,M=s,m=u,B=c,S=y,k=l,K=w,Y=v,L=p,T=b,z=g,R=A,P=_,N=0;N<20;N+=2)U^=(o=(T^=(o=(k^=(o=(M^=(o=U+T|0)<<7|o>>>25)+U|0)<<9|o>>>23)+M|0)<<13|o>>>19)+k|0)<<18|o>>>14,m^=(o=(d^=(o=(z^=(o=(K^=(o=m+d|0)<<7|o>>>25)+m|0)<<9|o>>>23)+K|0)<<13|o>>>19)+z|0)<<18|o>>>14,Y^=(o=(B^=(o=(E^=(o=(R^=(o=Y+B|0)<<7|o>>>25)+Y|0)<<9|o>>>23)+R|0)<<13|o>>>19)+E|0)<<18|o>>>14,P^=(o=(L^=(o=(S^=(o=(x^=(o=P+L|0)<<7|o>>>25)+P|0)<<9|o>>>23)+x|0)<<13|o>>>19)+S|0)<<18|o>>>14,U^=(o=(x^=(o=(E^=(o=(d^=(o=U+x|0)<<7|o>>>25)+U|0)<<9|o>>>23)+d|0)<<13|o>>>19)+E|0)<<18|o>>>14,m^=(o=(M^=(o=(S^=(o=(B^=(o=m+M|0)<<7|o>>>25)+m|0)<<9|o>>>23)+B|0)<<13|o>>>19)+S|0)<<18|o>>>14,Y^=(o=(K^=(o=(k^=(o=(L^=(o=Y+K|0)<<7|o>>>25)+Y|0)<<9|o>>>23)+L|0)<<13|o>>>19)+k|0)<<18|o>>>14,P^=(o=(R^=(o=(z^=(o=(T^=(o=P+R|0)<<7|o>>>25)+P|0)<<9|o>>>23)+T|0)<<13|o>>>19)+z|0)<<18|o>>>14;U=U+i|0,d=d+h|0,E=E+a|0,x=x+f|0,M=M+s|0,m=m+u|0,B=B+c|0,S=S+y|0,k=k+l|0,K=K+w|0,Y=Y+v|0,L=L+p|0,T=T+b|0,z=z+g|0,R=R+A|0,P=P+_|0,r[0]=U>>>0&255,r[1]=U>>>8&255,r[2]=U>>>16&255,r[3]=U>>>24&255,r[4]=d>>>0&255,r[5]=d>>>8&255,r[6]=d>>>16&255,r[7]=d>>>24&255,r[8]=E>>>0&255,r[9]=E>>>8&255,r[10]=E>>>16&255,r[11]=E>>>24&255,r[12]=x>>>0&255,r[13]=x>>>8&255,r[14]=x>>>16&255,r[15]=x>>>24&255,r[16]=M>>>0&255,r[17]=M>>>8&255,r[18]=M>>>16&255,r[19]=M>>>24&255,r[20]=m>>>0&255,r[21]=m>>>8&255,r[22]=m>>>16&255,r[23]=m>>>24&255,r[24]=B>>>0&255,r[25]=B>>>8&255,r[26]=B>>>16&255,r[27]=B>>>24&255,r[28]=S>>>0&255,r[29]=S>>>8&255,r[30]=S>>>16&255,r[31]=S>>>24&255,r[32]=k>>>0&255,r[33]=k>>>8&255,r[34]=k>>>16&255,r[35]=k>>>24&255,r[36]=K>>>0&255,r[37]=K>>>8&255,r[38]=K>>>16&255,r[39]=K>>>24&255,r[40]=Y>>>0&255,r[41]=Y>>>8&255,r[42]=Y>>>16&255,r[43]=Y>>>24&255,r[44]=L>>>0&255,r[45]=L>>>8&255,r[46]=L>>>16&255,r[47]=L>>>24&255,r[48]=T>>>0&255,r[49]=T>>>8&255,r[50]=T>>>16&255,r[51]=T>>>24&255,r[52]=z>>>0&255,r[53]=z>>>8&255,r[54]=z>>>16&255,r[55]=z>>>24&255,r[56]=R>>>0&255,r[57]=R>>>8&255,r[58]=R>>>16&255,r[59]=R>>>24&255,r[60]=P>>>0&255,r[61]=P>>>8&255,r[62]=P>>>16&255,r[63]=P>>>24&255}(r,t,n,e)}function _(r,t,n,e){!function(r,t,n,e){for(var o,i=255&e[0]|(255&e[1])<<8|(255&e[2])<<16|(255&e[3])<<24,h=255&n[0]|(255&n[1])<<8|(255&n[2])<<16|(255&n[3])<<24,a=255&n[4]|(255&n[5])<<8|(255&n[6])<<16|(255&n[7])<<24,f=255&n[8]|(255&n[9])<<8|(255&n[10])<<16|(255&n[11])<<24,s=255&n[12]|(255&n[13])<<8|(255&n[14])<<16|(255&n[15])<<24,u=255&e[4]|(255&e[5])<<8|(255&e[6])<<16|(255&e[7])<<24,c=255&t[0]|(255&t[1])<<8|(255&t[2])<<16|(255&t[3])<<24,y=255&t[4]|(255&t[5])<<8|(255&t[6])<<16|(255&t[7])<<24,l=255&t[8]|(255&t[9])<<8|(255&t[10])<<16|(255&t[11])<<24,w=255&t[12]|(255&t[13])<<8|(255&t[14])<<16|(255&t[15])<<24,v=255&e[8]|(255&e[9])<<8|(255&e[10])<<16|(255&e[11])<<24,p=255&n[16]|(255&n[17])<<8|(255&n[18])<<16|(255&n[19])<<24,b=255&n[20]|(255&n[21])<<8|(255&n[22])<<16|(255&n[23])<<24,g=255&n[24]|(255&n[25])<<8|(255&n[26])<<16|(255&n[27])<<24,A=255&n[28]|(255&n[29])<<8|(255&n[30])<<16|(255&n[31])<<24,_=255&e[12]|(255&e[13])<<8|(255&e[14])<<16|(255&e[15])<<24,U=0;U<20;U+=2)i^=(o=(b^=(o=(l^=(o=(s^=(o=i+b|0)<<7|o>>>25)+i|0)<<9|o>>>23)+s|0)<<13|o>>>19)+l|0)<<18|o>>>14,u^=(o=(h^=(o=(g^=(o=(w^=(o=u+h|0)<<7|o>>>25)+u|0)<<9|o>>>23)+w|0)<<13|o>>>19)+g|0)<<18|o>>>14,v^=(o=(c^=(o=(a^=(o=(A^=(o=v+c|0)<<7|o>>>25)+v|0)<<9|o>>>23)+A|0)<<13|o>>>19)+a|0)<<18|o>>>14,_^=(o=(p^=(o=(y^=(o=(f^=(o=_+p|0)<<7|o>>>25)+_|0)<<9|o>>>23)+f|0)<<13|o>>>19)+y|0)<<18|o>>>14,i^=(o=(f^=(o=(a^=(o=(h^=(o=i+f|0)<<7|o>>>25)+i|0)<<9|o>>>23)+h|0)<<13|o>>>19)+a|0)<<18|o>>>14,u^=(o=(s^=(o=(y^=(o=(c^=(o=u+s|0)<<7|o>>>25)+u|0)<<9|o>>>23)+c|0)<<13|o>>>19)+y|0)<<18|o>>>14,v^=(o=(w^=(o=(l^=(o=(p^=(o=v+w|0)<<7|o>>>25)+v|0)<<9|o>>>23)+p|0)<<13|o>>>19)+l|0)<<18|o>>>14,_^=(o=(A^=(o=(g^=(o=(b^=(o=_+A|0)<<7|o>>>25)+_|0)<<9|o>>>23)+b|0)<<13|o>>>19)+g|0)<<18|o>>>14;r[0]=i>>>0&255,r[1]=i>>>8&255,r[2]=i>>>16&255,r[3]=i>>>24&255,r[4]=u>>>0&255,r[5]=u>>>8&255,r[6]=u>>>16&255,r[7]=u>>>24&255,r[8]=v>>>0&255,r[9]=v>>>8&255,r[10]=v>>>16&255,r[11]=v>>>24&255,r[12]=_>>>0&255,r[13]=_>>>8&255,r[14]=_>>>16&255,r[15]=_>>>24&255,r[16]=c>>>0&255,r[17]=c>>>8&255,r[18]=c>>>16&255,r[19]=c>>>24&255,r[20]=y>>>0&255,r[21]=y>>>8&255,r[22]=y>>>16&255,r[23]=y>>>24&255,r[24]=l>>>0&255,r[25]=l>>>8&255,r[26]=l>>>16&255,r[27]=l>>>24&255,r[28]=w>>>0&255,r[29]=w>>>8&255,r[30]=w>>>16&255,r[31]=w>>>24&255}(r,t,n,e)}var U=new Uint8Array([101,120,112,97,110,100,32,51,50,45,98,121,116,101,32,107]);function d(r,t,n,e,o,i,h){var a,f,s=new Uint8Array(16),u=new Uint8Array(64);for(f=0;f<16;f++)s[f]=0;for(f=0;f<8;f++)s[f]=i[f];for(;64<=o;){for(A(u,s,h,U),f=0;f<64;f++)r[t+f]=n[e+f]^u[f];for(a=1,f=8;f<16;f++)a=a+(255&s[f])|0,s[f]=255&a,a>>>=8;o-=64,t+=64,e+=64}if(0>>=8;n-=64,t+=64}if(0>>13|n<<3),e=255&r[4]|(255&r[5])<<8,this.r[2]=7939&(n>>>10|e<<6),o=255&r[6]|(255&r[7])<<8,this.r[3]=8191&(e>>>7|o<<9),i=255&r[8]|(255&r[9])<<8,this.r[4]=255&(o>>>4|i<<12),this.r[5]=i>>>1&8190,h=255&r[10]|(255&r[11])<<8,this.r[6]=8191&(i>>>14|h<<2),a=255&r[12]|(255&r[13])<<8,this.r[7]=8065&(h>>>11|a<<5),f=255&r[14]|(255&r[15])<<8,this.r[8]=8191&(a>>>8|f<<8),this.r[9]=f>>>5&127,this.pad[0]=255&r[16]|(255&r[17])<<8,this.pad[1]=255&r[18]|(255&r[19])<<8,this.pad[2]=255&r[20]|(255&r[21])<<8,this.pad[3]=255&r[22]|(255&r[23])<<8,this.pad[4]=255&r[24]|(255&r[25])<<8,this.pad[5]=255&r[26]|(255&r[27])<<8,this.pad[6]=255&r[28]|(255&r[29])<<8,this.pad[7]=255&r[30]|(255&r[31])<<8};function B(r,t,n,e,o,i){var h=new m(i);return h.update(n,e,o),h.finish(r,t),0}function S(r,t,n,e,o,i){var h=new Uint8Array(16);return B(h,0,n,e,o,i),b(r,t,h,0)}function k(r,t,n,e,o){var i;if(n<32)return-1;for(M(r,0,t,0,n,e,o),B(r,16,r,32,n-32,r),i=0;i<16;i++)r[i]=0;return 0}function K(r,t,n,e,o){var i,h=new Uint8Array(32);if(n<32)return-1;if(x(h,0,32,e,o),0!==S(t,16,t,32,n-32,h))return-1;for(M(r,0,t,0,n,e,o),i=0;i<32;i++)r[i]=0;return 0}function Y(r,t){var n;for(n=0;n<16;n++)r[n]=0|t[n]}function L(r){var t,n,e=1;for(t=0;t<16;t++)n=r[t]+e+65535,e=Math.floor(n/65536),r[t]=n-65536*e;r[0]+=e-1+37*(e-1)}function T(r,t,n){for(var e,o=~(n-1),i=0;i<16;i++)e=o&(r[i]^t[i]),r[i]^=e,t[i]^=e}function z(r,t){var n,e,o,i=v(),h=v();for(n=0;n<16;n++)h[n]=t[n];for(L(h),L(h),L(h),e=0;e<2;e++){for(i[0]=h[0]-65517,n=1;n<15;n++)i[n]=h[n]-65535-(i[n-1]>>16&1),i[n-1]&=65535;i[15]=h[15]-32767-(i[14]>>16&1),o=i[15]>>16&1,i[14]&=65535,T(h,i,1-o)}for(n=0;n<16;n++)r[2*n]=255&h[n],r[2*n+1]=h[n]>>8}function R(r,t){var n=new Uint8Array(32),e=new Uint8Array(32);return z(n,r),z(e,t),g(n,0,e,0)}function P(r){var t=new Uint8Array(32);return z(t,r),1&t[0]}function N(r,t){var n;for(n=0;n<16;n++)r[n]=t[2*n]+(t[2*n+1]<<8);r[15]&=32767}function O(r,t,n){for(var e=0;e<16;e++)r[e]=t[e]+n[e]}function C(r,t,n){for(var e=0;e<16;e++)r[e]=t[e]-n[e]}function F(r,t,n){var e,o,i=0,h=0,a=0,f=0,s=0,u=0,c=0,y=0,l=0,w=0,v=0,p=0,b=0,g=0,A=0,_=0,U=0,d=0,E=0,x=0,M=0,m=0,B=0,S=0,k=0,K=0,Y=0,L=0,T=0,z=0,R=0,P=n[0],N=n[1],O=n[2],C=n[3],F=n[4],I=n[5],Z=n[6],G=n[7],q=n[8],D=n[9],V=n[10],X=n[11],j=n[12],H=n[13],J=n[14],Q=n[15];i+=(e=t[0])*P,h+=e*N,a+=e*O,f+=e*C,s+=e*F,u+=e*I,c+=e*Z,y+=e*G,l+=e*q,w+=e*D,v+=e*V,p+=e*X,b+=e*j,g+=e*H,A+=e*J,_+=e*Q,h+=(e=t[1])*P,a+=e*N,f+=e*O,s+=e*C,u+=e*F,c+=e*I,y+=e*Z,l+=e*G,w+=e*q,v+=e*D,p+=e*V,b+=e*X,g+=e*j,A+=e*H,_+=e*J,U+=e*Q,a+=(e=t[2])*P,f+=e*N,s+=e*O,u+=e*C,c+=e*F,y+=e*I,l+=e*Z,w+=e*G,v+=e*q,p+=e*D,b+=e*V,g+=e*X,A+=e*j,_+=e*H,U+=e*J,d+=e*Q,f+=(e=t[3])*P,s+=e*N,u+=e*O,c+=e*C,y+=e*F,l+=e*I,w+=e*Z,v+=e*G,p+=e*q,b+=e*D,g+=e*V,A+=e*X,_+=e*j,U+=e*H,d+=e*J,E+=e*Q,s+=(e=t[4])*P,u+=e*N,c+=e*O,y+=e*C,l+=e*F,w+=e*I,v+=e*Z,p+=e*G,b+=e*q,g+=e*D,A+=e*V,_+=e*X,U+=e*j,d+=e*H,E+=e*J,x+=e*Q,u+=(e=t[5])*P,c+=e*N,y+=e*O,l+=e*C,w+=e*F,v+=e*I,p+=e*Z,b+=e*G,g+=e*q,A+=e*D,_+=e*V,U+=e*X,d+=e*j,E+=e*H,x+=e*J,M+=e*Q,c+=(e=t[6])*P,y+=e*N,l+=e*O,w+=e*C,v+=e*F,p+=e*I,b+=e*Z,g+=e*G,A+=e*q,_+=e*D,U+=e*V,d+=e*X,E+=e*j,x+=e*H,M+=e*J,m+=e*Q,y+=(e=t[7])*P,l+=e*N,w+=e*O,v+=e*C,p+=e*F,b+=e*I,g+=e*Z,A+=e*G,_+=e*q,U+=e*D,d+=e*V,E+=e*X,x+=e*j,M+=e*H,m+=e*J,B+=e*Q,l+=(e=t[8])*P,w+=e*N,v+=e*O,p+=e*C,b+=e*F,g+=e*I,A+=e*Z,_+=e*G,U+=e*q,d+=e*D,E+=e*V,x+=e*X,M+=e*j,m+=e*H,B+=e*J,S+=e*Q,w+=(e=t[9])*P,v+=e*N,p+=e*O,b+=e*C,g+=e*F,A+=e*I,_+=e*Z,U+=e*G,d+=e*q,E+=e*D,x+=e*V,M+=e*X,m+=e*j,B+=e*H,S+=e*J,k+=e*Q,v+=(e=t[10])*P,p+=e*N,b+=e*O,g+=e*C,A+=e*F,_+=e*I,U+=e*Z,d+=e*G,E+=e*q,x+=e*D,M+=e*V,m+=e*X,B+=e*j,S+=e*H,k+=e*J,K+=e*Q,p+=(e=t[11])*P,b+=e*N,g+=e*O,A+=e*C,_+=e*F,U+=e*I,d+=e*Z,E+=e*G,x+=e*q,M+=e*D,m+=e*V,B+=e*X,S+=e*j,k+=e*H,K+=e*J,Y+=e*Q,b+=(e=t[12])*P,g+=e*N,A+=e*O,_+=e*C,U+=e*F,d+=e*I,E+=e*Z,x+=e*G,M+=e*q,m+=e*D,B+=e*V,S+=e*X,k+=e*j,K+=e*H,Y+=e*J,L+=e*Q,g+=(e=t[13])*P,A+=e*N,_+=e*O,U+=e*C,d+=e*F,E+=e*I,x+=e*Z,M+=e*G,m+=e*q,B+=e*D,S+=e*V,k+=e*X,K+=e*j,Y+=e*H,L+=e*J,T+=e*Q,A+=(e=t[14])*P,_+=e*N,U+=e*O,d+=e*C,E+=e*F,x+=e*I,M+=e*Z,m+=e*G,B+=e*q,S+=e*D,k+=e*V,K+=e*X,Y+=e*j,L+=e*H,T+=e*J,z+=e*Q,_+=(e=t[15])*P,h+=38*(d+=e*O),a+=38*(E+=e*C),f+=38*(x+=e*F),s+=38*(M+=e*I),u+=38*(m+=e*Z),c+=38*(B+=e*G),y+=38*(S+=e*q),l+=38*(k+=e*D),w+=38*(K+=e*V),v+=38*(Y+=e*X),p+=38*(L+=e*j),b+=38*(T+=e*H),g+=38*(z+=e*J),A+=38*(R+=e*Q),i=(e=(i+=38*(U+=e*N))+(o=1)+65535)-65536*(o=Math.floor(e/65536)),h=(e=h+o+65535)-65536*(o=Math.floor(e/65536)),a=(e=a+o+65535)-65536*(o=Math.floor(e/65536)),f=(e=f+o+65535)-65536*(o=Math.floor(e/65536)),s=(e=s+o+65535)-65536*(o=Math.floor(e/65536)),u=(e=u+o+65535)-65536*(o=Math.floor(e/65536)),c=(e=c+o+65535)-65536*(o=Math.floor(e/65536)),y=(e=y+o+65535)-65536*(o=Math.floor(e/65536)),l=(e=l+o+65535)-65536*(o=Math.floor(e/65536)),w=(e=w+o+65535)-65536*(o=Math.floor(e/65536)),v=(e=v+o+65535)-65536*(o=Math.floor(e/65536)),p=(e=p+o+65535)-65536*(o=Math.floor(e/65536)),b=(e=b+o+65535)-65536*(o=Math.floor(e/65536)),g=(e=g+o+65535)-65536*(o=Math.floor(e/65536)),A=(e=A+o+65535)-65536*(o=Math.floor(e/65536)),_=(e=_+o+65535)-65536*(o=Math.floor(e/65536)),i=(e=(i+=o-1+37*(o-1))+(o=1)+65535)-65536*(o=Math.floor(e/65536)),h=(e=h+o+65535)-65536*(o=Math.floor(e/65536)),a=(e=a+o+65535)-65536*(o=Math.floor(e/65536)),f=(e=f+o+65535)-65536*(o=Math.floor(e/65536)),s=(e=s+o+65535)-65536*(o=Math.floor(e/65536)),u=(e=u+o+65535)-65536*(o=Math.floor(e/65536)),c=(e=c+o+65535)-65536*(o=Math.floor(e/65536)),y=(e=y+o+65535)-65536*(o=Math.floor(e/65536)),l=(e=l+o+65535)-65536*(o=Math.floor(e/65536)),w=(e=w+o+65535)-65536*(o=Math.floor(e/65536)),v=(e=v+o+65535)-65536*(o=Math.floor(e/65536)),p=(e=p+o+65535)-65536*(o=Math.floor(e/65536)),b=(e=b+o+65535)-65536*(o=Math.floor(e/65536)),g=(e=g+o+65535)-65536*(o=Math.floor(e/65536)),A=(e=A+o+65535)-65536*(o=Math.floor(e/65536)),_=(e=_+o+65535)-65536*(o=Math.floor(e/65536)),i+=o-1+37*(o-1),r[0]=i,r[1]=h,r[2]=a,r[3]=f,r[4]=s,r[5]=u,r[6]=c,r[7]=y,r[8]=l,r[9]=w,r[10]=v,r[11]=p,r[12]=b,r[13]=g,r[14]=A,r[15]=_}function I(r,t){F(r,t,t)}function Z(r,t){var n,e=v();for(n=0;n<16;n++)e[n]=t[n];for(n=253;0<=n;n--)I(e,e),2!==n&&4!==n&&F(e,e,t);for(n=0;n<16;n++)r[n]=e[n]}function G(r,t){var n,e=v();for(n=0;n<16;n++)e[n]=t[n];for(n=250;0<=n;n--)I(e,e),1!==n&&F(e,e,t);for(n=0;n<16;n++)r[n]=e[n]}function q(r,t,n){var e,o,i=new Uint8Array(32),h=new Float64Array(80),a=v(),f=v(),s=v(),u=v(),c=v(),y=v();for(o=0;o<31;o++)i[o]=t[o];for(i[31]=127&t[31]|64,i[0]&=248,N(h,n),o=0;o<16;o++)f[o]=h[o],u[o]=a[o]=s[o]=0;for(a[0]=u[0]=1,o=254;0<=o;--o)T(a,f,e=i[o>>>3]>>>(7&o)&1),T(s,u,e),O(c,a,s),C(a,a,s),O(s,f,u),C(f,f,u),I(u,c),I(y,a),F(a,s,a),F(s,f,c),O(c,a,s),C(a,a,s),I(f,a),C(s,u,y),F(a,s,p),O(a,a,u),F(s,s,a),F(a,u,y),F(u,f,h),I(f,c),T(a,f,e),T(s,u,e);for(o=0;o<16;o++)h[o+16]=a[o],h[o+32]=s[o],h[o+48]=f[o],h[o+64]=u[o];var l=h.subarray(32),w=h.subarray(16);return Z(l,l),F(w,w,l),z(r,w),0}function D(r,t){return q(r,t,n)}function V(r,t){return h(t,32),D(r,t)}function X(r,t,n){var e=new Uint8Array(32);return q(e,n,t),_(r,o,e,U)}m.prototype.blocks=function(r,t,n){for(var e,o,i,h,a,f,s,u,c,y,l,w,v,p,b,g,A,_,U,d=this.fin?0:2048,E=this.h[0],x=this.h[1],M=this.h[2],m=this.h[3],B=this.h[4],S=this.h[5],k=this.h[6],K=this.h[7],Y=this.h[8],L=this.h[9],T=this.r[0],z=this.r[1],R=this.r[2],P=this.r[3],N=this.r[4],O=this.r[5],C=this.r[6],F=this.r[7],I=this.r[8],Z=this.r[9];16<=n;)y=c=0,y+=(E+=8191&(e=255&r[t+0]|(255&r[t+1])<<8))*T,y+=(x+=8191&(e>>>13|(o=255&r[t+2]|(255&r[t+3])<<8)<<3))*(5*Z),y+=(M+=8191&(o>>>10|(i=255&r[t+4]|(255&r[t+5])<<8)<<6))*(5*I),y+=(m+=8191&(i>>>7|(h=255&r[t+6]|(255&r[t+7])<<8)<<9))*(5*F),c=(y+=(B+=8191&(h>>>4|(a=255&r[t+8]|(255&r[t+9])<<8)<<12))*(5*C))>>>13,y&=8191,y+=(S+=a>>>1&8191)*(5*O),y+=(k+=8191&(a>>>14|(f=255&r[t+10]|(255&r[t+11])<<8)<<2))*(5*N),y+=(K+=8191&(f>>>11|(s=255&r[t+12]|(255&r[t+13])<<8)<<5))*(5*P),y+=(Y+=8191&(s>>>8|(u=255&r[t+14]|(255&r[t+15])<<8)<<8))*(5*R),l=c+=(y+=(L+=u>>>5|d)*(5*z))>>>13,l+=E*z,l+=x*T,l+=M*(5*Z),l+=m*(5*I),c=(l+=B*(5*F))>>>13,l&=8191,l+=S*(5*C),l+=k*(5*O),l+=K*(5*N),l+=Y*(5*P),c+=(l+=L*(5*R))>>>13,l&=8191,w=c,w+=E*R,w+=x*z,w+=M*T,w+=m*(5*Z),c=(w+=B*(5*I))>>>13,w&=8191,w+=S*(5*F),w+=k*(5*C),w+=K*(5*O),w+=Y*(5*N),v=c+=(w+=L*(5*P))>>>13,v+=E*P,v+=x*R,v+=M*z,v+=m*T,c=(v+=B*(5*Z))>>>13,v&=8191,v+=S*(5*I),v+=k*(5*F),v+=K*(5*C),v+=Y*(5*O),p=c+=(v+=L*(5*N))>>>13,p+=E*N,p+=x*P,p+=M*R,p+=m*z,c=(p+=B*T)>>>13,p&=8191,p+=S*(5*Z),p+=k*(5*I),p+=K*(5*F),p+=Y*(5*C),b=c+=(p+=L*(5*O))>>>13,b+=E*O,b+=x*N,b+=M*P,b+=m*R,c=(b+=B*z)>>>13,b&=8191,b+=S*T,b+=k*(5*Z),b+=K*(5*I),b+=Y*(5*F),g=c+=(b+=L*(5*C))>>>13,g+=E*C,g+=x*O,g+=M*N,g+=m*P,c=(g+=B*R)>>>13,g&=8191,g+=S*z,g+=k*T,g+=K*(5*Z),g+=Y*(5*I),A=c+=(g+=L*(5*F))>>>13,A+=E*F,A+=x*C,A+=M*O,A+=m*N,c=(A+=B*P)>>>13,A&=8191,A+=S*R,A+=k*z,A+=K*T,A+=Y*(5*Z),_=c+=(A+=L*(5*I))>>>13,_+=E*I,_+=x*F,_+=M*C,_+=m*O,c=(_+=B*N)>>>13,_&=8191,_+=S*P,_+=k*R,_+=K*z,_+=Y*T,U=c+=(_+=L*(5*Z))>>>13,U+=E*Z,U+=x*I,U+=M*F,U+=m*C,c=(U+=B*O)>>>13,U&=8191,U+=S*N,U+=k*P,U+=K*R,U+=Y*z,E=y=8191&(c=(c=((c+=(U+=L*T)>>>13)<<2)+c|0)+(y&=8191)|0),x=l+=c>>>=13,M=w&=8191,m=v&=8191,B=p&=8191,S=b&=8191,k=g&=8191,K=A&=8191,Y=_&=8191,L=U&=8191,t+=16,n-=16;this.h[0]=E,this.h[1]=x,this.h[2]=M,this.h[3]=m,this.h[4]=B,this.h[5]=S,this.h[6]=k,this.h[7]=K,this.h[8]=Y,this.h[9]=L},m.prototype.finish=function(r,t){var n,e,o,i,h=new Uint16Array(10);if(this.leftover){for(i=this.leftover,this.buffer[i++]=1;i<16;i++)this.buffer[i]=0;this.fin=1,this.blocks(this.buffer,0,16)}for(n=this.h[1]>>>13,this.h[1]&=8191,i=2;i<10;i++)this.h[i]+=n,n=this.h[i]>>>13,this.h[i]&=8191;for(this.h[0]+=5*n,n=this.h[0]>>>13,this.h[0]&=8191,this.h[1]+=n,n=this.h[1]>>>13,this.h[1]&=8191,this.h[2]+=n,h[0]=this.h[0]+5,n=h[0]>>>13,h[0]&=8191,i=1;i<10;i++)h[i]=this.h[i]+n,n=h[i]>>>13,h[i]&=8191;for(h[9]-=8192,e=(1^n)-1,i=0;i<10;i++)h[i]&=e;for(e=~e,i=0;i<10;i++)this.h[i]=this.h[i]&e|h[i];for(this.h[0]=65535&(this.h[0]|this.h[1]<<13),this.h[1]=65535&(this.h[1]>>>3|this.h[2]<<10),this.h[2]=65535&(this.h[2]>>>6|this.h[3]<<7),this.h[3]=65535&(this.h[3]>>>9|this.h[4]<<4),this.h[4]=65535&(this.h[4]>>>12|this.h[5]<<1|this.h[6]<<14),this.h[5]=65535&(this.h[6]>>>2|this.h[7]<<11),this.h[6]=65535&(this.h[7]>>>5|this.h[8]<<8),this.h[7]=65535&(this.h[8]>>>8|this.h[9]<<5),o=this.h[0]+this.pad[0],this.h[0]=65535&o,i=1;i<8;i++)o=(this.h[i]+this.pad[i]|0)+(o>>>16)|0,this.h[i]=65535&o;r[t+0]=this.h[0]>>>0&255,r[t+1]=this.h[0]>>>8&255,r[t+2]=this.h[1]>>>0&255,r[t+3]=this.h[1]>>>8&255,r[t+4]=this.h[2]>>>0&255,r[t+5]=this.h[2]>>>8&255,r[t+6]=this.h[3]>>>0&255,r[t+7]=this.h[3]>>>8&255,r[t+8]=this.h[4]>>>0&255,r[t+9]=this.h[4]>>>8&255,r[t+10]=this.h[5]>>>0&255,r[t+11]=this.h[5]>>>8&255,r[t+12]=this.h[6]>>>0&255,r[t+13]=this.h[6]>>>8&255,r[t+14]=this.h[7]>>>0&255,r[t+15]=this.h[7]>>>8&255},m.prototype.update=function(r,t,n){var e,o;if(this.leftover){for(n<(o=16-this.leftover)&&(o=n),e=0;e>>16,m=65535&(d=N),B=d>>>16,x+=65535&(E=((w=Z)>>>14|(a=z)<<18)^(Z>>>18|z<<14)^(z>>>9|Z<<23)),M+=E>>>16,m+=65535&(d=(z>>>14|Z<<18)^(z>>>18|Z<<14)^(Z>>>9|z<<23)),B+=d>>>16,x+=65535&(E=Z&(v=G)^~Z&(p=q)),M+=E>>>16,m+=65535&(d=z&(f=R)^~z&(s=P)),B+=d>>>16,d=J[2*_],x+=65535&(E=J[2*_+1]),M+=E>>>16,m+=65535&d,B+=d>>>16,d=S[_%16],M+=(E=k[_%16])>>>16,m+=65535&d,B+=d>>>16,m+=(M+=(x+=65535&E)>>>16)>>>16,x=65535&(E=A=65535&x|M<<16),M=E>>>16,m=65535&(d=g=65535&m|(B+=m>>>16)<<16),B=d>>>16,x+=65535&(E=(O>>>28|K<<4)^(K>>>2|O<<30)^(K>>>7|O<<25)),M+=E>>>16,m+=65535&(d=(K>>>28|O<<4)^(O>>>2|K<<30)^(O>>>7|K<<25)),B+=d>>>16,M+=(E=O&C^O&F^C&F)>>>16,m+=65535&(d=K&Y^K&L^Y&L),B+=d>>>16,u=65535&(m+=(M+=(x+=65535&E)>>>16)>>>16)|(B+=m>>>16)<<16,b=65535&x|M<<16,x=65535&(E=l),M=E>>>16,m=65535&(d=h),B=d>>>16,M+=(E=A)>>>16,m+=65535&(d=g),B+=d>>>16,Y=K,L=o,T=i,z=h=65535&(m+=(M+=(x+=65535&E)>>>16)>>>16)|(B+=m>>>16)<<16,R=a,P=f,N=s,K=u,C=O,F=c,I=y,Z=l=65535&x|M<<16,G=w,q=v,D=p,O=b,_%16==15)for(U=0;U<16;U++)d=S[U],x=65535&(E=k[U]),M=E>>>16,m=65535&d,B=d>>>16,d=S[(U+9)%16],x+=65535&(E=k[(U+9)%16]),M+=E>>>16,m+=65535&d,B+=d>>>16,g=S[(U+1)%16],x+=65535&(E=((A=k[(U+1)%16])>>>1|g<<31)^(A>>>8|g<<24)^(A>>>7|g<<25)),M+=E>>>16,m+=65535&(d=(g>>>1|A<<31)^(g>>>8|A<<24)^g>>>7),B+=d>>>16,g=S[(U+14)%16],M+=(E=((A=k[(U+14)%16])>>>19|g<<13)^(g>>>29|A<<3)^(A>>>6|g<<26))>>>16,m+=65535&(d=(g>>>19|A<<13)^(A>>>29|g<<3)^g>>>6),B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,S[U]=65535&m|B<<16,k[U]=65535&x|M<<16;x=65535&(E=O),M=E>>>16,m=65535&(d=K),B=d>>>16,d=r[0],M+=(E=t[0])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[0]=K=65535&m|B<<16,t[0]=O=65535&x|M<<16,x=65535&(E=C),M=E>>>16,m=65535&(d=Y),B=d>>>16,d=r[1],M+=(E=t[1])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[1]=Y=65535&m|B<<16,t[1]=C=65535&x|M<<16,x=65535&(E=F),M=E>>>16,m=65535&(d=L),B=d>>>16,d=r[2],M+=(E=t[2])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[2]=L=65535&m|B<<16,t[2]=F=65535&x|M<<16,x=65535&(E=I),M=E>>>16,m=65535&(d=T),B=d>>>16,d=r[3],M+=(E=t[3])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[3]=T=65535&m|B<<16,t[3]=I=65535&x|M<<16,x=65535&(E=Z),M=E>>>16,m=65535&(d=z),B=d>>>16,d=r[4],M+=(E=t[4])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[4]=z=65535&m|B<<16,t[4]=Z=65535&x|M<<16,x=65535&(E=G),M=E>>>16,m=65535&(d=R),B=d>>>16,d=r[5],M+=(E=t[5])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[5]=R=65535&m|B<<16,t[5]=G=65535&x|M<<16,x=65535&(E=q),M=E>>>16,m=65535&(d=P),B=d>>>16,d=r[6],M+=(E=t[6])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[6]=P=65535&m|B<<16,t[6]=q=65535&x|M<<16,x=65535&(E=D),M=E>>>16,m=65535&(d=N),B=d>>>16,d=r[7],M+=(E=t[7])>>>16,m+=65535&d,B+=d>>>16,B+=(m+=(M+=(x+=65535&E)>>>16)>>>16)>>>16,r[7]=N=65535&m|B<<16,t[7]=D=65535&x|M<<16,V+=128,e-=128}return e}function W(r,t,n){var e,o=new Int32Array(8),i=new Int32Array(8),h=new Uint8Array(256),a=n;for(o[0]=1779033703,o[1]=3144134277,o[2]=1013904242,o[3]=2773480762,o[4]=1359893119,o[5]=2600822924,o[6]=528734635,o[7]=1541459225,i[0]=4089235720,i[1]=2227873595,i[2]=4271175723,i[3]=1595750129,i[4]=2917565137,i[5]=725511199,i[6]=4215389547,i[7]=327033209,Q(o,i,t,n),n%=128,e=0;e>(7&o)&1),$(t,r),$(r,r),rr(r,t,e)}function er(r,t){var n=[v(),v(),v(),v()];Y(n[0],e),Y(n[1],a),Y(n[2],u),F(n[3],e,a),nr(r,n,t)}function or(r,t,n){var e,o=new Uint8Array(64),i=[v(),v(),v(),v()];for(n||h(t,32),W(o,t,32),o[0]&=248,o[31]&=127,o[31]|=64,er(i,o),tr(r,i),e=0;e<32;e++)t[e+32]=r[e];return 0}var ir=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]);function hr(r,t){var n,e,o,i;for(e=63;32<=e;--e){for(n=0,o=e-32,i=e-12;o>4)*ir[o],n=t[o]>>8,t[o]&=255;for(o=0;o<32;o++)t[o]-=n*ir[o];for(e=0;e<32;e++)t[e+1]+=t[e]>>8,r[e]=255&t[e]}function ar(r){var t,n=new Float64Array(64);for(t=0;t<64;t++)n[t]=r[t];for(t=0;t<64;t++)r[t]=0;hr(r,n)}function fr(r,t,n,e){var o,i,h=new Uint8Array(64),a=new Uint8Array(64),f=new Uint8Array(64),s=new Float64Array(64),u=[v(),v(),v(),v()];W(h,e,32),h[0]&=248,h[31]&=127,h[31]|=64;var c=n+64;for(o=0;o>7&&C(r[0],s,r[0]),F(r[3],r[0],r[1])}(f,e))return-1;for(o=0;o void): void; +} diff --git a/node_modules/tweetnacl/nacl.js b/node_modules/tweetnacl/nacl.js new file mode 100644 index 0000000..fd8e678 --- /dev/null +++ b/node_modules/tweetnacl/nacl.js @@ -0,0 +1,1178 @@ +(function(nacl) { +'use strict'; + +// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. +// Public domain. +// +// Implementation derived from TweetNaCl version 20140427. +// See for details: http://tweetnacl.cr.yp.to/ + +var u64 = function(h, l) { this.hi = h|0 >>> 0; this.lo = l|0 >>> 0; }; +var gf = function(init) { + var i, r = new Float64Array(16); + if (init) for (i = 0; i < init.length; i++) r[i] = init[i]; + return r; +}; + +// Pluggable, initialized in high-level API below. +var randombytes = function(/* x, n */) { throw new Error('no PRNG'); }; + +var _0 = new Uint8Array(16); +var _9 = new Uint8Array(32); _9[0] = 9; + +var gf0 = gf(), + gf1 = gf([1]), + _121665 = gf([0xdb41, 1]), + D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]), + D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]), + X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]), + Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]), + I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]); + +function L32(x, c) { return (x << c) | (x >>> (32 - c)); } + +function ld32(x, i) { + var u = x[i+3] & 0xff; + u = (u<<8)|(x[i+2] & 0xff); + u = (u<<8)|(x[i+1] & 0xff); + return (u<<8)|(x[i+0] & 0xff); +} + +function dl64(x, i) { + var h = (x[i] << 24) | (x[i+1] << 16) | (x[i+2] << 8) | x[i+3]; + var l = (x[i+4] << 24) | (x[i+5] << 16) | (x[i+6] << 8) | x[i+7]; + return new u64(h, l); +} + +function st32(x, j, u) { + var i; + for (i = 0; i < 4; i++) { x[j+i] = u & 255; u >>>= 8; } +} + +function ts64(x, i, u) { + x[i] = (u.hi >> 24) & 0xff; + x[i+1] = (u.hi >> 16) & 0xff; + x[i+2] = (u.hi >> 8) & 0xff; + x[i+3] = u.hi & 0xff; + x[i+4] = (u.lo >> 24) & 0xff; + x[i+5] = (u.lo >> 16) & 0xff; + x[i+6] = (u.lo >> 8) & 0xff; + x[i+7] = u.lo & 0xff; +} + +function vn(x, xi, y, yi, n) { + var i,d = 0; + for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i]; + return (1 & ((d - 1) >>> 8)) - 1; +} + +function crypto_verify_16(x, xi, y, yi) { + return vn(x,xi,y,yi,16); +} + +function crypto_verify_32(x, xi, y, yi) { + return vn(x,xi,y,yi,32); +} + +function core(out,inp,k,c,h) { + var w = new Uint32Array(16), x = new Uint32Array(16), + y = new Uint32Array(16), t = new Uint32Array(4); + var i, j, m; + + for (i = 0; i < 4; i++) { + x[5*i] = ld32(c, 4*i); + x[1+i] = ld32(k, 4*i); + x[6+i] = ld32(inp, 4*i); + x[11+i] = ld32(k, 16+4*i); + } + + for (i = 0; i < 16; i++) y[i] = x[i]; + + for (i = 0; i < 20; i++) { + for (j = 0; j < 4; j++) { + for (m = 0; m < 4; m++) t[m] = x[(5*j+4*m)%16]; + t[1] ^= L32((t[0]+t[3])|0, 7); + t[2] ^= L32((t[1]+t[0])|0, 9); + t[3] ^= L32((t[2]+t[1])|0,13); + t[0] ^= L32((t[3]+t[2])|0,18); + for (m = 0; m < 4; m++) w[4*j+(j+m)%4] = t[m]; + } + for (m = 0; m < 16; m++) x[m] = w[m]; + } + + if (h) { + for (i = 0; i < 16; i++) x[i] = (x[i] + y[i]) | 0; + for (i = 0; i < 4; i++) { + x[5*i] = (x[5*i] - ld32(c, 4*i)) | 0; + x[6+i] = (x[6+i] - ld32(inp, 4*i)) | 0; + } + for (i = 0; i < 4; i++) { + st32(out,4*i,x[5*i]); + st32(out,16+4*i,x[6+i]); + } + } else { + for (i = 0; i < 16; i++) st32(out, 4 * i, (x[i] + y[i]) | 0); + } +} + +function crypto_core_salsa20(out,inp,k,c) { + core(out,inp,k,c,false); + return 0; +} + +function crypto_core_hsalsa20(out,inp,k,c) { + core(out,inp,k,c,true); + return 0; +} + +var sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]); + // "expand 32-byte k" + +function crypto_stream_salsa20_xor(c,cpos,m,mpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + if (!b) return 0; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = (m?m[mpos+i]:0) ^ x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + if (m) mpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = (m?m[mpos+i]:0) ^ x[i]; + } + return 0; +} + +function crypto_stream_salsa20(c,cpos,d,n,k) { + return crypto_stream_salsa20_xor(c,cpos,null,0,d,n,k); +} + +function crypto_stream(c,cpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20(c,cpos,d,n.subarray(16),s); +} + +function crypto_stream_xor(c,cpos,m,mpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20_xor(c,cpos,m,mpos,d,n.subarray(16),s); +} + +function add1305(h, c) { + var j, u = 0; + for (j = 0; j < 17; j++) { + u = (u + ((h[j] + c[j]) | 0)) | 0; + h[j] = u & 255; + u >>>= 8; + } +} + +var minusp = new Uint32Array([ + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 +]); + +function crypto_onetimeauth(out, outpos, m, mpos, n, k) { + var s, i, j, u; + var x = new Uint32Array(17), r = new Uint32Array(17), + h = new Uint32Array(17), c = new Uint32Array(17), + g = new Uint32Array(17); + for (j = 0; j < 17; j++) r[j]=h[j]=0; + for (j = 0; j < 16; j++) r[j]=k[j]; + r[3]&=15; + r[4]&=252; + r[7]&=15; + r[8]&=252; + r[11]&=15; + r[12]&=252; + r[15]&=15; + + while (n > 0) { + for (j = 0; j < 17; j++) c[j] = 0; + for (j = 0; (j < 16) && (j < n); ++j) c[j] = m[mpos+j]; + c[j] = 1; + mpos += j; n -= j; + add1305(h,c); + for (i = 0; i < 17; i++) { + x[i] = 0; + for (j = 0; j < 17; j++) x[i] = (x[i] + (h[j] * ((j <= i) ? r[i - j] : ((320 * r[i + 17 - j])|0))) | 0) | 0; + } + for (i = 0; i < 17; i++) h[i] = x[i]; + u = 0; + for (j = 0; j < 16; j++) { + u = (u + h[j]) | 0; + h[j] = u & 255; + u >>>= 8; + } + u = (u + h[16]) | 0; h[16] = u & 3; + u = (5 * (u >>> 2)) | 0; + for (j = 0; j < 16; j++) { + u = (u + h[j]) | 0; + h[j] = u & 255; + u >>>= 8; + } + u = (u + h[16]) | 0; h[16] = u; + } + + for (j = 0; j < 17; j++) g[j] = h[j]; + add1305(h,minusp); + s = (-(h[16] >>> 7) | 0); + for (j = 0; j < 17; j++) h[j] ^= s & (g[j] ^ h[j]); + + for (j = 0; j < 16; j++) c[j] = k[j + 16]; + c[16] = 0; + add1305(h,c); + for (j = 0; j < 16; j++) out[outpos+j] = h[j]; + return 0; +} + +function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) { + var x = new Uint8Array(16); + crypto_onetimeauth(x,0,m,mpos,n,k); + return crypto_verify_16(h,hpos,x,0); +} + +function crypto_secretbox(c,m,d,n,k) { + var i; + if (d < 32) return -1; + crypto_stream_xor(c,0,m,0,d,n,k); + crypto_onetimeauth(c, 16, c, 32, d - 32, c); + for (i = 0; i < 16; i++) c[i] = 0; + return 0; +} + +function crypto_secretbox_open(m,c,d,n,k) { + var i; + var x = new Uint8Array(32); + if (d < 32) return -1; + crypto_stream(x,0,32,n,k); + if (crypto_onetimeauth_verify(c, 16,c, 32,d - 32,x) !== 0) return -1; + crypto_stream_xor(m,0,c,0,d,n,k); + for (i = 0; i < 32; i++) m[i] = 0; + return 0; +} + +function set25519(r, a) { + var i; + for (i = 0; i < 16; i++) r[i] = a[i]|0; +} + +function car25519(o) { + var c; + var i; + for (i = 0; i < 16; i++) { + o[i] += 65536; + c = Math.floor(o[i] / 65536); + o[(i+1)*(i<15?1:0)] += c - 1 + 37 * (c-1) * (i===15?1:0); + o[i] -= (c * 65536); + } +} + +function sel25519(p, q, b) { + var t, c = ~(b-1); + for (var i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} + +function pack25519(o, n) { + var i, j, b; + var m = gf(), t = gf(); + for (i = 0; i < 16; i++) t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1); + m[i-1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1); + b = (m[15]>>16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1-b); + } + for (i = 0; i < 16; i++) { + o[2*i] = t[i] & 0xff; + o[2*i+1] = t[i]>>8; + } +} + +function neq25519(a, b) { + var c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); +} + +function par25519(a) { + var d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; +} + +function unpack25519(o, n) { + var i; + for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8); + o[15] &= 0x7fff; +} + +function A(o, a, b) { + var i; + for (i = 0; i < 16; i++) o[i] = (a[i] + b[i])|0; +} + +function Z(o, a, b) { + var i; + for (i = 0; i < 16; i++) o[i] = (a[i] - b[i])|0; +} + +function M(o, a, b) { + var i, j, t = new Float64Array(31); + for (i = 0; i < 31; i++) t[i] = 0; + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + t[i+j] += a[i] * b[j]; + } + } + for (i = 0; i < 15; i++) { + t[i] += 38 * t[i+16]; + } + for (i = 0; i < 16; i++) o[i] = t[i]; + car25519(o); + car25519(o); +} + +function S(o, a) { + M(o, a, a); +} + +function inv25519(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S(c, c); + if(a !== 2 && a !== 4) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function pow2523(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S(c, c); + if(a !== 1) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function crypto_scalarmult(q, n, p) { + var z = new Uint8Array(32); + var x = new Float64Array(80), r, i; + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(); + for (i = 0; i < 31; i++) z[i] = n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + for (i = 0; i < 16; i++) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for (i=254; i>=0; --i) { + r=(z[i>>>3]>>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + for (i = 0; i < 16; i++) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + var x32 = x.subarray(32); + var x16 = x.subarray(16); + inv25519(x32,x32); + M(x16,x16,x32); + pack25519(q,x16); + return 0; +} + +function crypto_scalarmult_base(q, n) { + return crypto_scalarmult(q, n, _9); +} + +function crypto_box_keypair(y, x) { + randombytes(x, 32); + return crypto_scalarmult_base(y, x); +} + +function crypto_box_beforenm(k, y, x) { + var s = new Uint8Array(32); + crypto_scalarmult(s, x, y); + return crypto_core_hsalsa20(k, _0, s, sigma); +} + +var crypto_box_afternm = crypto_secretbox; +var crypto_box_open_afternm = crypto_secretbox_open; + +function crypto_box(c, m, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_afternm(c, m, d, n, k); +} + +function crypto_box_open(m, c, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_open_afternm(m, c, d, n, k); +} + +function add64() { + var a = 0, b = 0, c = 0, d = 0, m16 = 65535, l, h, i; + for (i = 0; i < arguments.length; i++) { + l = arguments[i].lo; + h = arguments[i].hi; + a += (l & m16); b += (l >>> 16); + c += (h & m16); d += (h >>> 16); + } + + b += (a >>> 16); + c += (b >>> 16); + d += (c >>> 16); + + return new u64((c & m16) | (d << 16), (a & m16) | (b << 16)); +} + +function shr64(x, c) { + return new u64((x.hi >>> c), (x.lo >>> c) | (x.hi << (32 - c))); +} + +function xor64() { + var l = 0, h = 0, i; + for (i = 0; i < arguments.length; i++) { + l ^= arguments[i].lo; + h ^= arguments[i].hi; + } + return new u64(h, l); +} + +function R(x, c) { + var h, l, c1 = 32 - c; + if (c < 32) { + h = (x.hi >>> c) | (x.lo << c1); + l = (x.lo >>> c) | (x.hi << c1); + } else if (c < 64) { + h = (x.lo >>> c) | (x.hi << c1); + l = (x.hi >>> c) | (x.lo << c1); + } + return new u64(h, l); +} + +function Ch(x, y, z) { + var h = (x.hi & y.hi) ^ (~x.hi & z.hi), + l = (x.lo & y.lo) ^ (~x.lo & z.lo); + return new u64(h, l); +} + +function Maj(x, y, z) { + var h = (x.hi & y.hi) ^ (x.hi & z.hi) ^ (y.hi & z.hi), + l = (x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo); + return new u64(h, l); +} + +function Sigma0(x) { return xor64(R(x,28), R(x,34), R(x,39)); } +function Sigma1(x) { return xor64(R(x,14), R(x,18), R(x,41)); } +function sigma0(x) { return xor64(R(x, 1), R(x, 8), shr64(x,7)); } +function sigma1(x) { return xor64(R(x,19), R(x,61), shr64(x,6)); } + +var K = [ + new u64(0x428a2f98, 0xd728ae22), new u64(0x71374491, 0x23ef65cd), + new u64(0xb5c0fbcf, 0xec4d3b2f), new u64(0xe9b5dba5, 0x8189dbbc), + new u64(0x3956c25b, 0xf348b538), new u64(0x59f111f1, 0xb605d019), + new u64(0x923f82a4, 0xaf194f9b), new u64(0xab1c5ed5, 0xda6d8118), + new u64(0xd807aa98, 0xa3030242), new u64(0x12835b01, 0x45706fbe), + new u64(0x243185be, 0x4ee4b28c), new u64(0x550c7dc3, 0xd5ffb4e2), + new u64(0x72be5d74, 0xf27b896f), new u64(0x80deb1fe, 0x3b1696b1), + new u64(0x9bdc06a7, 0x25c71235), new u64(0xc19bf174, 0xcf692694), + new u64(0xe49b69c1, 0x9ef14ad2), new u64(0xefbe4786, 0x384f25e3), + new u64(0x0fc19dc6, 0x8b8cd5b5), new u64(0x240ca1cc, 0x77ac9c65), + new u64(0x2de92c6f, 0x592b0275), new u64(0x4a7484aa, 0x6ea6e483), + new u64(0x5cb0a9dc, 0xbd41fbd4), new u64(0x76f988da, 0x831153b5), + new u64(0x983e5152, 0xee66dfab), new u64(0xa831c66d, 0x2db43210), + new u64(0xb00327c8, 0x98fb213f), new u64(0xbf597fc7, 0xbeef0ee4), + new u64(0xc6e00bf3, 0x3da88fc2), new u64(0xd5a79147, 0x930aa725), + new u64(0x06ca6351, 0xe003826f), new u64(0x14292967, 0x0a0e6e70), + new u64(0x27b70a85, 0x46d22ffc), new u64(0x2e1b2138, 0x5c26c926), + new u64(0x4d2c6dfc, 0x5ac42aed), new u64(0x53380d13, 0x9d95b3df), + new u64(0x650a7354, 0x8baf63de), new u64(0x766a0abb, 0x3c77b2a8), + new u64(0x81c2c92e, 0x47edaee6), new u64(0x92722c85, 0x1482353b), + new u64(0xa2bfe8a1, 0x4cf10364), new u64(0xa81a664b, 0xbc423001), + new u64(0xc24b8b70, 0xd0f89791), new u64(0xc76c51a3, 0x0654be30), + new u64(0xd192e819, 0xd6ef5218), new u64(0xd6990624, 0x5565a910), + new u64(0xf40e3585, 0x5771202a), new u64(0x106aa070, 0x32bbd1b8), + new u64(0x19a4c116, 0xb8d2d0c8), new u64(0x1e376c08, 0x5141ab53), + new u64(0x2748774c, 0xdf8eeb99), new u64(0x34b0bcb5, 0xe19b48a8), + new u64(0x391c0cb3, 0xc5c95a63), new u64(0x4ed8aa4a, 0xe3418acb), + new u64(0x5b9cca4f, 0x7763e373), new u64(0x682e6ff3, 0xd6b2b8a3), + new u64(0x748f82ee, 0x5defb2fc), new u64(0x78a5636f, 0x43172f60), + new u64(0x84c87814, 0xa1f0ab72), new u64(0x8cc70208, 0x1a6439ec), + new u64(0x90befffa, 0x23631e28), new u64(0xa4506ceb, 0xde82bde9), + new u64(0xbef9a3f7, 0xb2c67915), new u64(0xc67178f2, 0xe372532b), + new u64(0xca273ece, 0xea26619c), new u64(0xd186b8c7, 0x21c0c207), + new u64(0xeada7dd6, 0xcde0eb1e), new u64(0xf57d4f7f, 0xee6ed178), + new u64(0x06f067aa, 0x72176fba), new u64(0x0a637dc5, 0xa2c898a6), + new u64(0x113f9804, 0xbef90dae), new u64(0x1b710b35, 0x131c471b), + new u64(0x28db77f5, 0x23047d84), new u64(0x32caab7b, 0x40c72493), + new u64(0x3c9ebe0a, 0x15c9bebc), new u64(0x431d67c4, 0x9c100d4c), + new u64(0x4cc5d4be, 0xcb3e42b6), new u64(0x597f299c, 0xfc657e2a), + new u64(0x5fcb6fab, 0x3ad6faec), new u64(0x6c44198c, 0x4a475817) +]; + +function crypto_hashblocks(x, m, n) { + var z = [], b = [], a = [], w = [], t, i, j; + + for (i = 0; i < 8; i++) z[i] = a[i] = dl64(x, 8*i); + + var pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) w[i] = dl64(m, 8*i+pos); + for (i = 0; i < 80; i++) { + for (j = 0; j < 8; j++) b[j] = a[j]; + t = add64(a[7], Sigma1(a[4]), Ch(a[4], a[5], a[6]), K[i], w[i%16]); + b[7] = add64(t, Sigma0(a[0]), Maj(a[0], a[1], a[2])); + b[3] = add64(b[3], t); + for (j = 0; j < 8; j++) a[(j+1)%8] = b[j]; + if (i%16 === 15) { + for (j = 0; j < 16; j++) { + w[j] = add64(w[j], w[(j+9)%16], sigma0(w[(j+1)%16]), sigma1(w[(j+14)%16])); + } + } + } + + for (i = 0; i < 8; i++) { + a[i] = add64(a[i], z[i]); + z[i] = a[i]; + } + + pos += 128; + n -= 128; + } + + for (i = 0; i < 8; i++) ts64(x, 8*i, z[i]); + return n; +} + +var iv = new Uint8Array([ + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +]); + +function crypto_hash(out, m, n) { + var h = new Uint8Array(64), x = new Uint8Array(256); + var i, b = n; + + for (i = 0; i < 64; i++) h[i] = iv[i]; + + crypto_hashblocks(h, m, n); + n %= 128; + + for (i = 0; i < 256; i++) x[i] = 0; + for (i = 0; i < n; i++) x[i] = m[b-n+i]; + x[n] = 128; + + n = 256-128*(n<112?1:0); + x[n-9] = 0; + ts64(x, n-8, new u64((b / 0x20000000) | 0, b << 3)); + crypto_hashblocks(h, x, n); + + for (i = 0; i < 64; i++) out[i] = h[i]; + + return 0; +} + +function add(p, q) { + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(), + g = gf(), h = gf(), t = gf(); + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +function cswap(p, q, b) { + var i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } +} + +function pack(r, p) { + var tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +function scalarmult(p, q, s) { + var b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i/8)|0] >> (i&7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } +} + +function scalarbase(p, s) { + var q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); +} + +function crypto_sign_keypair(pk, sk, seeded) { + var d = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()]; + var i; + + if (!seeded) randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p, d); + pack(pk, p); + + for (i = 0; i < 32; i++) sk[i+32] = pk[i]; + return 0; +} + +var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]); + +function modL(r, x) { + var carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = Math.floor((x[j] + 128) / 256); + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +function reduce(r) { + var x = new Float64Array(64), i; + for (i = 0; i < 64; i++) x[i] = r[i]; + for (i = 0; i < 64; i++) r[i] = 0; + modL(r, x); +} + +// Note: difference from C - smlen returned, not passed as argument. +function crypto_sign(sm, m, n, sk) { + var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + var i, j, x = new Float64Array(64); + var p = [gf(), gf(), gf(), gf()]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + var smlen = n + 64; + for (i = 0; i < n; i++) sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm.subarray(32), n+32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + + for (i = 32; i < 64; i++) sm[i] = sk[i]; + crypto_hash(h, sm, n + 64); + reduce(h); + + for (i = 0; i < 64; i++) x[i] = 0; + for (i = 0; i < 32; i++) x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i+j] += h[i] * d[j]; + } + } + + modL(sm.subarray(32), x); + return smlen; +} + +function unpackneg(r, p) { + var t = gf(), chk = gf(), num = gf(), + den = gf(), den2 = gf(), den4 = gf(), + den6 = gf(); + + set25519(r[2], gf1); + unpack25519(r[1], p); + S(num, r[1]); + M(den, num, D); + Z(num, num, r[2]); + A(den, r[2], den); + + S(den2, den); + S(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) M(r[0], r[0], I); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]); + + M(r[3], r[0], r[1]); + return 0; +} + +function crypto_sign_open(m, sm, n, pk) { + var i; + var t = new Uint8Array(32), h = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()], + q = [gf(), gf(), gf(), gf()]; + + if (n < 64) return -1; + + if (unpackneg(q, pk)) return -1; + + for (i = 0; i < n; i++) m[i] = sm[i]; + for (i = 0; i < 32; i++) m[i+32] = pk[i]; + crypto_hash(h, m, n); + reduce(h); + scalarmult(p, q, h); + + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) m[i] = 0; + return -1; + } + + for (i = 0; i < n; i++) m[i] = sm[i + 64]; + return n; +} + +var crypto_secretbox_KEYBYTES = 32, + crypto_secretbox_NONCEBYTES = 24, + crypto_secretbox_ZEROBYTES = 32, + crypto_secretbox_BOXZEROBYTES = 16, + crypto_scalarmult_BYTES = 32, + crypto_scalarmult_SCALARBYTES = 32, + crypto_box_PUBLICKEYBYTES = 32, + crypto_box_SECRETKEYBYTES = 32, + crypto_box_BEFORENMBYTES = 32, + crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES, + crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES, + crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES, + crypto_sign_BYTES = 64, + crypto_sign_PUBLICKEYBYTES = 32, + crypto_sign_SECRETKEYBYTES = 64, + crypto_sign_SEEDBYTES = 32, + crypto_hash_BYTES = 64; + +nacl.lowlevel = { + crypto_core_hsalsa20: crypto_core_hsalsa20, + crypto_stream_xor: crypto_stream_xor, + crypto_stream: crypto_stream, + crypto_stream_salsa20_xor: crypto_stream_salsa20_xor, + crypto_stream_salsa20: crypto_stream_salsa20, + crypto_onetimeauth: crypto_onetimeauth, + crypto_onetimeauth_verify: crypto_onetimeauth_verify, + crypto_verify_16: crypto_verify_16, + crypto_verify_32: crypto_verify_32, + crypto_secretbox: crypto_secretbox, + crypto_secretbox_open: crypto_secretbox_open, + crypto_scalarmult: crypto_scalarmult, + crypto_scalarmult_base: crypto_scalarmult_base, + crypto_box_beforenm: crypto_box_beforenm, + crypto_box_afternm: crypto_box_afternm, + crypto_box: crypto_box, + crypto_box_open: crypto_box_open, + crypto_box_keypair: crypto_box_keypair, + crypto_hash: crypto_hash, + crypto_sign: crypto_sign, + crypto_sign_keypair: crypto_sign_keypair, + crypto_sign_open: crypto_sign_open, + + crypto_secretbox_KEYBYTES: crypto_secretbox_KEYBYTES, + crypto_secretbox_NONCEBYTES: crypto_secretbox_NONCEBYTES, + crypto_secretbox_ZEROBYTES: crypto_secretbox_ZEROBYTES, + crypto_secretbox_BOXZEROBYTES: crypto_secretbox_BOXZEROBYTES, + crypto_scalarmult_BYTES: crypto_scalarmult_BYTES, + crypto_scalarmult_SCALARBYTES: crypto_scalarmult_SCALARBYTES, + crypto_box_PUBLICKEYBYTES: crypto_box_PUBLICKEYBYTES, + crypto_box_SECRETKEYBYTES: crypto_box_SECRETKEYBYTES, + crypto_box_BEFORENMBYTES: crypto_box_BEFORENMBYTES, + crypto_box_NONCEBYTES: crypto_box_NONCEBYTES, + crypto_box_ZEROBYTES: crypto_box_ZEROBYTES, + crypto_box_BOXZEROBYTES: crypto_box_BOXZEROBYTES, + crypto_sign_BYTES: crypto_sign_BYTES, + crypto_sign_PUBLICKEYBYTES: crypto_sign_PUBLICKEYBYTES, + crypto_sign_SECRETKEYBYTES: crypto_sign_SECRETKEYBYTES, + crypto_sign_SEEDBYTES: crypto_sign_SEEDBYTES, + crypto_hash_BYTES: crypto_hash_BYTES, + + gf: gf, + D: D, + L: L, + pack25519: pack25519, + unpack25519: unpack25519, + M: M, + A: A, + S: S, + Z: Z, + pow2523: pow2523, + add: add, + set25519: set25519, + modL: modL, + scalarmult: scalarmult, + scalarbase: scalarbase, +}; + +/* High-level API */ + +function checkLengths(k, n) { + if (k.length !== crypto_secretbox_KEYBYTES) throw new Error('bad key size'); + if (n.length !== crypto_secretbox_NONCEBYTES) throw new Error('bad nonce size'); +} + +function checkBoxLengths(pk, sk) { + if (pk.length !== crypto_box_PUBLICKEYBYTES) throw new Error('bad public key size'); + if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error('bad secret key size'); +} + +function checkArrayTypes() { + for (var i = 0; i < arguments.length; i++) { + if (!(arguments[i] instanceof Uint8Array)) + throw new TypeError('unexpected type, use Uint8Array'); + } +} + +function cleanup(arr) { + for (var i = 0; i < arr.length; i++) arr[i] = 0; +} + +nacl.randomBytes = function(n) { + var b = new Uint8Array(n); + randombytes(b, n); + return b; +}; + +nacl.secretbox = function(msg, nonce, key) { + checkArrayTypes(msg, nonce, key); + checkLengths(key, nonce); + var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length); + var c = new Uint8Array(m.length); + for (var i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i]; + crypto_secretbox(c, m, m.length, nonce, key); + return c.subarray(crypto_secretbox_BOXZEROBYTES); +}; + +nacl.secretbox.open = function(box, nonce, key) { + checkArrayTypes(box, nonce, key); + checkLengths(key, nonce); + var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length); + var m = new Uint8Array(c.length); + for (var i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i]; + if (c.length < 32) return null; + if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return null; + return m.subarray(crypto_secretbox_ZEROBYTES); +}; + +nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES; +nacl.secretbox.nonceLength = crypto_secretbox_NONCEBYTES; +nacl.secretbox.overheadLength = crypto_secretbox_BOXZEROBYTES; + +nacl.scalarMult = function(n, p) { + checkArrayTypes(n, p); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult(q, n, p); + return q; +}; + +nacl.scalarMult.base = function(n) { + checkArrayTypes(n); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult_base(q, n); + return q; +}; + +nacl.scalarMult.scalarLength = crypto_scalarmult_SCALARBYTES; +nacl.scalarMult.groupElementLength = crypto_scalarmult_BYTES; + +nacl.box = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox(msg, nonce, k); +}; + +nacl.box.before = function(publicKey, secretKey) { + checkArrayTypes(publicKey, secretKey); + checkBoxLengths(publicKey, secretKey); + var k = new Uint8Array(crypto_box_BEFORENMBYTES); + crypto_box_beforenm(k, publicKey, secretKey); + return k; +}; + +nacl.box.after = nacl.secretbox; + +nacl.box.open = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox.open(msg, nonce, k); +}; + +nacl.box.open.after = nacl.secretbox.open; + +nacl.box.keyPair = function() { + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_box_SECRETKEYBYTES); + crypto_box_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.box.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_box_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + crypto_scalarmult_base(pk, secretKey); + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES; +nacl.box.secretKeyLength = crypto_box_SECRETKEYBYTES; +nacl.box.sharedKeyLength = crypto_box_BEFORENMBYTES; +nacl.box.nonceLength = crypto_box_NONCEBYTES; +nacl.box.overheadLength = nacl.secretbox.overheadLength; + +nacl.sign = function(msg, secretKey) { + checkArrayTypes(msg, secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + return signedMsg; +}; + +nacl.sign.open = function(signedMsg, publicKey) { + checkArrayTypes(signedMsg, publicKey); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var tmp = new Uint8Array(signedMsg.length); + var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); + if (mlen < 0) return null; + var m = new Uint8Array(mlen); + for (var i = 0; i < m.length; i++) m[i] = tmp[i]; + return m; +}; + +nacl.sign.detached = function(msg, secretKey) { + var signedMsg = nacl.sign(msg, secretKey); + var sig = new Uint8Array(crypto_sign_BYTES); + for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i]; + return sig; +}; + +nacl.sign.detached.verify = function(msg, sig, publicKey) { + checkArrayTypes(msg, sig, publicKey); + if (sig.length !== crypto_sign_BYTES) + throw new Error('bad signature size'); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var sm = new Uint8Array(crypto_sign_BYTES + msg.length); + var m = new Uint8Array(crypto_sign_BYTES + msg.length); + var i; + for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i]; + for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i]; + return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0); +}; + +nacl.sign.keyPair = function() { + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + crypto_sign_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i]; + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.sign.keyPair.fromSeed = function(seed) { + checkArrayTypes(seed); + if (seed.length !== crypto_sign_SEEDBYTES) + throw new Error('bad seed size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (var i = 0; i < 32; i++) sk[i] = seed[i]; + crypto_sign_keypair(pk, sk, true); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.publicKeyLength = crypto_sign_PUBLICKEYBYTES; +nacl.sign.secretKeyLength = crypto_sign_SECRETKEYBYTES; +nacl.sign.seedLength = crypto_sign_SEEDBYTES; +nacl.sign.signatureLength = crypto_sign_BYTES; + +nacl.hash = function(msg) { + checkArrayTypes(msg); + var h = new Uint8Array(crypto_hash_BYTES); + crypto_hash(h, msg, msg.length); + return h; +}; + +nacl.hash.hashLength = crypto_hash_BYTES; + +nacl.verify = function(x, y) { + checkArrayTypes(x, y); + // Zero length arguments are considered not equal. + if (x.length === 0 || y.length === 0) return false; + if (x.length !== y.length) return false; + return (vn(x, 0, y, 0, x.length) === 0) ? true : false; +}; + +nacl.setPRNG = function(fn) { + randombytes = fn; +}; + +(function() { + // Initialize PRNG if environment provides CSPRNG. + // If not, methods calling randombytes will throw. + var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null; + if (crypto && crypto.getRandomValues) { + // Browsers. + var QUOTA = 65536; + nacl.setPRNG(function(x, n) { + var i, v = new Uint8Array(n); + for (i = 0; i < n; i += QUOTA) { + crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); + } + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } else if (typeof require !== 'undefined') { + // Node.js. + crypto = require('crypto'); + if (crypto && crypto.randomBytes) { + nacl.setPRNG(function(x, n) { + var i, v = crypto.randomBytes(n); + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } + } +})(); + +})(typeof module !== 'undefined' && module.exports ? module.exports : (self.nacl = self.nacl || {})); diff --git a/node_modules/tweetnacl/nacl.min.js b/node_modules/tweetnacl/nacl.min.js new file mode 100644 index 0000000..65340cc --- /dev/null +++ b/node_modules/tweetnacl/nacl.min.js @@ -0,0 +1 @@ +!function(i){"use strict";var m=function(r,n){this.hi=0|r,this.lo=0|n},v=function(r){var n,e=new Float64Array(16);if(r)for(n=0;n>>32-n}function b(r,n){var e=255&r[n+3];return(e=(e=e<<8|255&r[n+2])<<8|255&r[n+1])<<8|255&r[n+0]}function B(r,n){var e=r[n]<<24|r[n+1]<<16|r[n+2]<<8|r[n+3],t=r[n+4]<<24|r[n+5]<<16|r[n+6]<<8|r[n+7];return new m(e,t)}function p(r,n,e){var t;for(t=0;t<4;t++)r[n+t]=255&e,e>>>=8}function S(r,n,e){r[n]=e.hi>>24&255,r[n+1]=e.hi>>16&255,r[n+2]=e.hi>>8&255,r[n+3]=255&e.hi,r[n+4]=e.lo>>24&255,r[n+5]=e.lo>>16&255,r[n+6]=e.lo>>8&255,r[n+7]=255&e.lo}function u(r,n,e,t,o){var i,a=0;for(i=0;i>>8)-1}function A(r,n,e,t){return u(r,n,e,t,16)}function _(r,n,e,t){return u(r,n,e,t,32)}function U(r,n,e,t,o){var i,a,f,u=new Uint32Array(16),c=new Uint32Array(16),w=new Uint32Array(16),y=new Uint32Array(4);for(i=0;i<4;i++)c[5*i]=b(t,4*i),c[1+i]=b(e,4*i),c[6+i]=b(n,4*i),c[11+i]=b(e,16+4*i);for(i=0;i<16;i++)w[i]=c[i];for(i=0;i<20;i++){for(a=0;a<4;a++){for(f=0;f<4;f++)y[f]=c[(5*a+4*f)%16];for(y[1]^=h(y[0]+y[3]|0,7),y[2]^=h(y[1]+y[0]|0,9),y[3]^=h(y[2]+y[1]|0,13),y[0]^=h(y[3]+y[2]|0,18),f=0;f<4;f++)u[4*a+(a+f)%4]=y[f]}for(f=0;f<16;f++)c[f]=u[f]}if(o){for(i=0;i<16;i++)c[i]=c[i]+w[i]|0;for(i=0;i<4;i++)c[5*i]=c[5*i]-b(t,4*i)|0,c[6+i]=c[6+i]-b(n,4*i)|0;for(i=0;i<4;i++)p(r,4*i,c[5*i]),p(r,16+4*i,c[6+i])}else for(i=0;i<16;i++)p(r,4*i,c[i]+w[i]|0)}function E(r,n,e,t){U(r,n,e,t,!1)}function x(r,n,e,t){return U(r,n,e,t,!0),0}var d=new Uint8Array([101,120,112,97,110,100,32,51,50,45,98,121,116,101,32,107]);function K(r,n,e,t,o,i,a){var f,u,c=new Uint8Array(16),w=new Uint8Array(64);if(!o)return 0;for(u=0;u<16;u++)c[u]=0;for(u=0;u<8;u++)c[u]=i[u];for(;64<=o;){for(E(w,c,a,d),u=0;u<64;u++)r[n+u]=(e?e[t+u]:0)^w[u];for(f=1,u=8;u<16;u++)f=f+(255&c[u])|0,c[u]=255&f,f>>>=8;o-=64,n+=64,e&&(t+=64)}if(0>>=8}var z=new Uint32Array([5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,252]);function R(r,n,e,t,o,i){var a,f,u,c,w=new Uint32Array(17),y=new Uint32Array(17),l=new Uint32Array(17),s=new Uint32Array(17),h=new Uint32Array(17);for(u=0;u<17;u++)y[u]=l[u]=0;for(u=0;u<16;u++)y[u]=i[u];for(y[3]&=15,y[4]&=252,y[7]&=15,y[8]&=252,y[11]&=15,y[12]&=252,y[15]&=15;0>>=8;for(c=c+l[16]|0,l[16]=3&c,c=5*(c>>>2)|0,u=0;u<16;u++)c=c+l[u]|0,l[u]=255&c,c>>>=8;c=c+l[16]|0,l[16]=c}for(u=0;u<17;u++)h[u]=l[u];for(k(l,z),a=0|-(l[16]>>>7),u=0;u<17;u++)l[u]^=a&(h[u]^l[u]);for(u=0;u<16;u++)s[u]=i[u+16];for(s[16]=0,k(l,s),u=0;u<16;u++)r[n+u]=l[u];return 0}function P(r,n,e,t,o,i){var a=new Uint8Array(16);return R(a,0,e,t,o,i),A(r,n,a,0)}function M(r,n,e,t,o){var i;if(e<32)return-1;for(T(r,0,n,0,e,t,o),R(r,16,r,32,e-32,r),i=0;i<16;i++)r[i]=0;return 0}function N(r,n,e,t,o){var i,a=new Uint8Array(32);if(e<32)return-1;if(L(a,0,32,t,o),0!==P(n,16,n,32,e-32,a))return-1;for(T(r,0,n,0,e,t,o),i=0;i<32;i++)r[i]=0;return 0}function O(r,n){var e;for(e=0;e<16;e++)r[e]=0|n[e]}function C(r){var n,e;for(e=0;e<16;e++)r[e]+=65536,n=Math.floor(r[e]/65536),r[(e+1)*(e<15?1:0)]+=n-1+37*(n-1)*(15===e?1:0),r[e]-=65536*n}function F(r,n,e){for(var t,o=~(e-1),i=0;i<16;i++)t=o&(r[i]^n[i]),r[i]^=t,n[i]^=t}function Z(r,n){var e,t,o,i=v(),a=v();for(e=0;e<16;e++)a[e]=n[e];for(C(a),C(a),C(a),t=0;t<2;t++){for(i[0]=a[0]-65517,e=1;e<15;e++)i[e]=a[e]-65535-(i[e-1]>>16&1),i[e-1]&=65535;i[15]=a[15]-32767-(i[14]>>16&1),o=i[15]>>16&1,i[14]&=65535,F(a,i,1-o)}for(e=0;e<16;e++)r[2*e]=255&a[e],r[2*e+1]=a[e]>>8}function G(r,n){var e=new Uint8Array(32),t=new Uint8Array(32);return Z(e,r),Z(t,n),_(e,0,t,0)}function q(r){var n=new Uint8Array(32);return Z(n,r),1&n[0]}function D(r,n){var e;for(e=0;e<16;e++)r[e]=n[2*e]+(n[2*e+1]<<8);r[15]&=32767}function I(r,n,e){var t;for(t=0;t<16;t++)r[t]=n[t]+e[t]|0}function V(r,n,e){var t;for(t=0;t<16;t++)r[t]=n[t]-e[t]|0}function X(r,n,e){var t,o,i=new Float64Array(31);for(t=0;t<31;t++)i[t]=0;for(t=0;t<16;t++)for(o=0;o<16;o++)i[t+o]+=n[t]*e[o];for(t=0;t<15;t++)i[t]+=38*i[t+16];for(t=0;t<16;t++)r[t]=i[t];C(r),C(r)}function j(r,n){X(r,n,n)}function H(r,n){var e,t=v();for(e=0;e<16;e++)t[e]=n[e];for(e=253;0<=e;e--)j(t,t),2!==e&&4!==e&&X(t,t,n);for(e=0;e<16;e++)r[e]=t[e]}function J(r,n){var e,t=v();for(e=0;e<16;e++)t[e]=n[e];for(e=250;0<=e;e--)j(t,t),1!==e&&X(t,t,n);for(e=0;e<16;e++)r[e]=t[e]}function Q(r,n,e){var t,o,i=new Uint8Array(32),a=new Float64Array(80),f=v(),u=v(),c=v(),w=v(),y=v(),l=v();for(o=0;o<31;o++)i[o]=n[o];for(i[31]=127&n[31]|64,i[0]&=248,D(a,e),o=0;o<16;o++)u[o]=a[o],w[o]=f[o]=c[o]=0;for(f[0]=w[0]=1,o=254;0<=o;--o)F(f,u,t=i[o>>>3]>>>(7&o)&1),F(c,w,t),I(y,f,c),V(f,f,c),I(c,u,w),V(u,u,w),j(w,y),j(l,f),X(f,c,f),X(c,u,y),I(y,f,c),V(f,f,c),j(u,f),V(c,w,l),X(f,c,g),I(f,f,w),X(c,c,f),X(f,w,l),X(w,u,a),j(u,y),F(f,u,t),F(c,w,t);for(o=0;o<16;o++)a[o+16]=f[o],a[o+32]=c[o],a[o+48]=u[o],a[o+64]=w[o];var s=a.subarray(32),h=a.subarray(16);return H(s,s),X(h,h,s),Z(r,h),0}function W(r,n){return Q(r,n,e)}function $(r,n){return a(n,32),W(r,n)}function rr(r,n,e){var t=new Uint8Array(32);return Q(t,e,n),x(r,o,t,d)}var nr=M,er=N;function tr(){var r,n,e,t=0,o=0,i=0,a=0,f=65535;for(e=0;e>>16,i+=(n=arguments[e].hi)&f,a+=n>>>16;return new m((i+=(o+=t>>>16)>>>16)&f|(a+=i>>>16)<<16,t&f|o<<16)}function or(r,n){return new m(r.hi>>>n,r.lo>>>n|r.hi<<32-n)}function ir(){var r,n=0,e=0;for(r=0;r>>n|r.lo<>>n|r.hi<>>n|r.hi<>>n|r.lo<>(7&o)&1),yr(n,r),yr(r,r),lr(r,n,t)}function vr(r,n){var e=[v(),v(),v(),v()];O(e[0],t),O(e[1],f),O(e[2],w),X(e[3],t,f),hr(r,e,n)}function gr(r,n,e){var t,o=new Uint8Array(64),i=[v(),v(),v(),v()];for(e||a(n,32),wr(o,n,32),o[0]&=248,o[31]&=127,o[31]|=64,vr(i,o),sr(r,i),t=0;t<32;t++)n[t+32]=r[t];return 0}var br=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]);function pr(r,n){var e,t,o,i;for(t=63;32<=t;--t){for(e=0,o=t-32,i=t-12;o>4)*br[o],e=n[o]>>8,n[o]&=255;for(o=0;o<32;o++)n[o]-=e*br[o];for(t=0;t<32;t++)n[t+1]+=n[t]>>8,r[t]=255&n[t]}function Ar(r){var n,e=new Float64Array(64);for(n=0;n<64;n++)e[n]=r[n];for(n=0;n<64;n++)r[n]=0;pr(r,e)}function _r(r,n,e,t){var o,i,a=new Uint8Array(64),f=new Uint8Array(64),u=new Uint8Array(64),c=new Float64Array(64),w=[v(),v(),v(),v()];wr(a,t,32),a[0]&=248,a[31]&=127,a[31]|=64;var y=e+64;for(o=0;o>7&&V(r[0],c,r[0]),X(r[3],r[0],r[1])}(u,t))return-1;for(o=0;o/dev/null && browserify test/browser/init.js test/*.quick.js | uglifyjs -c -m -o test/browser/_bundle-quick.js 2>/dev/null", + "lint": "eslint nacl.js nacl-fast.js test/*.js test/benchmark/*.js", + "test": "npm run test-node-all", + "test-node": "tape test/*.js | faucet", + "test-node-all": "make -C test/c && tape test/*.js test/c/*.js | faucet" + }, + "types": "nacl.d.ts", + "version": "1.0.3" +} diff --git a/node_modules/ws/LICENSE b/node_modules/ws/LICENSE new file mode 100644 index 0000000..a145cd1 --- /dev/null +++ b/node_modules/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011 Einar Otto Stangvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md new file mode 100644 index 0000000..f36a354 --- /dev/null +++ b/node_modules/ws/README.md @@ -0,0 +1,496 @@ +# ws: a Node.js WebSocket library + +[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws) +[![Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.com/websockets/ws) +[![Windows x86 Build](https://img.shields.io/appveyor/ci/lpinca/ws/master.svg?logo=appveyor)](https://ci.appveyor.com/project/lpinca/ws) +[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/github/websockets/ws) + +ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and +server implementation. + +Passes the quite extensive Autobahn test suite: [server][server-report], +[client][client-report]. + +**Note**: This module does not work in the browser. The client in the docs is a +reference to a back end with the role of a client in the WebSocket +communication. Browser clients must use the native +[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) +object. To make the same code work seamlessly on Node.js and the browser, you +can use one of the many wrappers available on npm, like +[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws). + +## Table of Contents + +- [Protocol support](#protocol-support) +- [Installing](#installing) + - [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance) +- [API docs](#api-docs) +- [WebSocket compression](#websocket-compression) +- [Usage examples](#usage-examples) + - [Sending and receiving text data](#sending-and-receiving-text-data) + - [Sending binary data](#sending-binary-data) + - [Simple server](#simple-server) + - [External HTTP/S server](#external-https-server) + - [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server) + - [Client authentication](#client-authentication) + - [Server broadcast](#server-broadcast) + - [echo.websocket.org demo](#echowebsocketorg-demo) + - [Use the Node.js streams API](#use-the-nodejs-streams-api) + - [Other examples](#other-examples) +- [FAQ](#faq) + - [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client) + - [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections) + - [How to connect via a proxy?](#how-to-connect-via-a-proxy) +- [Changelog](#changelog) +- [License](#license) + +## Protocol support + +- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`) +- **HyBi drafts 13-17** (Current default, alternatively option + `protocolVersion: 13`) + +## Installing + +``` +npm install ws +``` + +### Opt-in for performance and spec compliance + +There are 2 optional modules that can be installed along side with the ws +module. These modules are binary addons which improve certain operations. +Prebuilt binaries are available for the most popular platforms so you don't +necessarily need to have a C++ compiler installed on your machine. + +- `npm install --save-optional bufferutil`: Allows to efficiently perform + operations such as masking and unmasking the data payload of the WebSocket + frames. +- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a + message contains valid UTF-8 as required by the spec. + +## API docs + +See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and +utility functions. + +## WebSocket compression + +ws supports the [permessage-deflate extension][permessage-deflate] which enables +the client and server to negotiate a compression algorithm and its parameters, +and then selectively apply it to the data payloads of each WebSocket message. + +The extension is disabled by default on the server and enabled by default on the +client. It adds a significant overhead in terms of performance and memory +consumption so we suggest to enable it only if it is really needed. + +Note that Node.js has a variety of issues with high-performance compression, +where increased concurrency, especially on Linux, can lead to [catastrophic +memory fragmentation][node-zlib-bug] and slow performance. If you intend to use +permessage-deflate in production, it is worthwhile to set up a test +representative of your workload and ensure Node.js/zlib will handle it with +acceptable performance and memory usage. + +Tuning of permessage-deflate can be done via the options defined below. You can +also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly +into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs]. + +See [the docs][ws-server-options] for more options. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ + port: 8080, + perMessageDeflate: { + zlibDeflateOptions: { + // See zlib defaults. + chunkSize: 1024, + memLevel: 7, + level: 3 + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + // Other options settable: + clientNoContextTakeover: true, // Defaults to negotiated value. + serverNoContextTakeover: true, // Defaults to negotiated value. + serverMaxWindowBits: 10, // Defaults to negotiated value. + // Below options specified as default values. + concurrencyLimit: 10, // Limits zlib concurrency for perf. + threshold: 1024 // Size (in bytes) below which messages + // should not be compressed. + } +}); +``` + +The client will only use the extension if it is supported and enabled on the +server. To always disable the extension on the client set the +`perMessageDeflate` option to `false`. + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path', { + perMessageDeflate: false +}); +``` + +## Usage examples + +### Sending and receiving text data + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + ws.send('something'); +}); + +ws.on('message', function incoming(data) { + console.log(data); +}); +``` + +### Sending binary data + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + const array = new Float32Array(5); + + for (var i = 0; i < array.length; ++i) { + array[i] = i / 2; + } + + ws.send(array); +}); +``` + +### Simple server + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); +``` + +### External HTTP/S server + +```js +const fs = require('fs'); +const https = require('https'); +const WebSocket = require('ws'); + +const server = https.createServer({ + cert: fs.readFileSync('/path/to/cert.pem'), + key: fs.readFileSync('/path/to/key.pem') +}); +const wss = new WebSocket.Server({ server }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); + +server.listen(8080); +``` + +### Multiple servers sharing a single HTTP/S server + +```js +const http = require('http'); +const WebSocket = require('ws'); +const url = require('url'); + +const server = http.createServer(); +const wss1 = new WebSocket.Server({ noServer: true }); +const wss2 = new WebSocket.Server({ noServer: true }); + +wss1.on('connection', function connection(ws) { + // ... +}); + +wss2.on('connection', function connection(ws) { + // ... +}); + +server.on('upgrade', function upgrade(request, socket, head) { + const pathname = url.parse(request.url).pathname; + + if (pathname === '/foo') { + wss1.handleUpgrade(request, socket, head, function done(ws) { + wss1.emit('connection', ws, request); + }); + } else if (pathname === '/bar') { + wss2.handleUpgrade(request, socket, head, function done(ws) { + wss2.emit('connection', ws, request); + }); + } else { + socket.destroy(); + } +}); + +server.listen(8080); +``` + +### Client authentication + +```js +const http = require('http'); +const WebSocket = require('ws'); + +const server = http.createServer(); +const wss = new WebSocket.Server({ noServer: true }); + +wss.on('connection', function connection(ws, request, client) { + ws.on('message', function message(msg) { + console.log(`Received message ${msg} from user ${client}`); + }); +}); + +server.on('upgrade', function upgrade(request, socket, head) { + // This function is not defined on purpose. Implement it with your own logic. + authenticate(request, (err, client) => { + if (err || !client) { + socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); + socket.destroy(); + return; + } + + wss.handleUpgrade(request, socket, head, function done(ws) { + wss.emit('connection', ws, request, client); + }); + }); +}); + +server.listen(8080); +``` + +Also see the provided [example][session-parse-example] using `express-session`. + +### Server broadcast + +A client WebSocket broadcasting to all connected WebSocket clients, including +itself. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(data) { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); + }); +}); +``` + +A client WebSocket broadcasting to every other connected WebSocket clients, +excluding itself. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(data) { + wss.clients.forEach(function each(client) { + if (client !== ws && client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); + }); +}); +``` + +### echo.websocket.org demo + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('wss://echo.websocket.org/', { + origin: 'https://websocket.org' +}); + +ws.on('open', function open() { + console.log('connected'); + ws.send(Date.now()); +}); + +ws.on('close', function close() { + console.log('disconnected'); +}); + +ws.on('message', function incoming(data) { + console.log(`Roundtrip time: ${Date.now() - data} ms`); + + setTimeout(function timeout() { + ws.send(Date.now()); + }, 500); +}); +``` + +### Use the Node.js streams API + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('wss://echo.websocket.org/', { + origin: 'https://websocket.org' +}); + +const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' }); + +duplex.pipe(process.stdout); +process.stdin.pipe(duplex); +``` + +### Other examples + +For a full example with a browser client communicating with a ws server, see the +examples folder. + +Otherwise, see the test cases. + +## FAQ + +### How to get the IP address of the client? + +The remote IP address can be obtained from the raw socket. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws, req) { + const ip = req.socket.remoteAddress; +}); +``` + +When the server runs behind a proxy like NGINX, the de-facto standard is to use +the `X-Forwarded-For` header. + +```js +wss.on('connection', function connection(ws, req) { + const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0]; +}); +``` + +### How to detect and close broken connections? + +Sometimes the link between the server and the client can be interrupted in a way +that keeps both the server and the client unaware of the broken state of the +connection (e.g. when pulling the cord). + +In these cases ping messages can be used as a means to verify that the remote +endpoint is still responsive. + +```js +const WebSocket = require('ws'); + +function noop() {} + +function heartbeat() { + this.isAlive = true; +} + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.isAlive = true; + ws.on('pong', heartbeat); +}); + +const interval = setInterval(function ping() { + wss.clients.forEach(function each(ws) { + if (ws.isAlive === false) return ws.terminate(); + + ws.isAlive = false; + ws.ping(noop); + }); +}, 30000); + +wss.on('close', function close() { + clearInterval(interval); +}); +``` + +Pong messages are automatically sent in response to ping messages as required by +the spec. + +Just like the server example above your clients might as well lose connection +without knowing it. You might want to add a ping listener on your clients to +prevent that. A simple implementation would be: + +```js +const WebSocket = require('ws'); + +function heartbeat() { + clearTimeout(this.pingTimeout); + + // Use `WebSocket#terminate()`, which immediately destroys the connection, + // instead of `WebSocket#close()`, which waits for the close timer. + // Delay should be equal to the interval at which your server + // sends out pings plus a conservative assumption of the latency. + this.pingTimeout = setTimeout(() => { + this.terminate(); + }, 30000 + 1000); +} + +const client = new WebSocket('wss://echo.websocket.org/'); + +client.on('open', heartbeat); +client.on('ping', heartbeat); +client.on('close', function clear() { + clearTimeout(this.pingTimeout); +}); +``` + +### How to connect via a proxy? + +Use a custom `http.Agent` implementation like [https-proxy-agent][] or +[socks-proxy-agent][]. + +## Changelog + +We're using the GitHub [releases][changelog] for changelog entries. + +## License + +[MIT](LICENSE) + +[changelog]: https://github.com/websockets/ws/releases +[client-report]: http://websockets.github.io/ws/autobahn/clients/ +[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent +[node-zlib-bug]: https://github.com/nodejs/node/issues/8871 +[node-zlib-deflaterawdocs]: + https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options +[permessage-deflate]: https://tools.ietf.org/html/rfc7692 +[server-report]: http://websockets.github.io/ws/autobahn/servers/ +[session-parse-example]: ./examples/express-session-parse +[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent +[ws-server-options]: + https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback diff --git a/node_modules/ws/browser.js b/node_modules/ws/browser.js new file mode 100644 index 0000000..ca4f628 --- /dev/null +++ b/node_modules/ws/browser.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function () { + throw new Error( + 'ws does not work in the browser. Browser clients must use the native ' + + 'WebSocket object' + ); +}; diff --git a/node_modules/ws/index.js b/node_modules/ws/index.js new file mode 100644 index 0000000..722c786 --- /dev/null +++ b/node_modules/ws/index.js @@ -0,0 +1,10 @@ +'use strict'; + +const WebSocket = require('./lib/websocket'); + +WebSocket.createWebSocketStream = require('./lib/stream'); +WebSocket.Server = require('./lib/websocket-server'); +WebSocket.Receiver = require('./lib/receiver'); +WebSocket.Sender = require('./lib/sender'); + +module.exports = WebSocket; diff --git a/node_modules/ws/lib/buffer-util.js b/node_modules/ws/lib/buffer-util.js new file mode 100644 index 0000000..6fd84c3 --- /dev/null +++ b/node_modules/ws/lib/buffer-util.js @@ -0,0 +1,129 @@ +'use strict'; + +const { EMPTY_BUFFER } = require('./constants'); + +/** + * Merges an array of buffers into a new buffer. + * + * @param {Buffer[]} list The array of buffers to concat + * @param {Number} totalLength The total length of buffers in the list + * @return {Buffer} The resulting buffer + * @public + */ +function concat(list, totalLength) { + if (list.length === 0) return EMPTY_BUFFER; + if (list.length === 1) return list[0]; + + const target = Buffer.allocUnsafe(totalLength); + let offset = 0; + + for (let i = 0; i < list.length; i++) { + const buf = list[i]; + target.set(buf, offset); + offset += buf.length; + } + + if (offset < totalLength) return target.slice(0, offset); + + return target; +} + +/** + * Masks a buffer using the given mask. + * + * @param {Buffer} source The buffer to mask + * @param {Buffer} mask The mask to use + * @param {Buffer} output The buffer where to store the result + * @param {Number} offset The offset at which to start writing + * @param {Number} length The number of bytes to mask. + * @public + */ +function _mask(source, mask, output, offset, length) { + for (let i = 0; i < length; i++) { + output[offset + i] = source[i] ^ mask[i & 3]; + } +} + +/** + * Unmasks a buffer using the given mask. + * + * @param {Buffer} buffer The buffer to unmask + * @param {Buffer} mask The mask to use + * @public + */ +function _unmask(buffer, mask) { + // Required until https://github.com/nodejs/node/issues/9006 is resolved. + const length = buffer.length; + for (let i = 0; i < length; i++) { + buffer[i] ^= mask[i & 3]; + } +} + +/** + * Converts a buffer to an `ArrayBuffer`. + * + * @param {Buffer} buf The buffer to convert + * @return {ArrayBuffer} Converted buffer + * @public + */ +function toArrayBuffer(buf) { + if (buf.byteLength === buf.buffer.byteLength) { + return buf.buffer; + } + + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); +} + +/** + * Converts `data` to a `Buffer`. + * + * @param {*} data The data to convert + * @return {Buffer} The buffer + * @throws {TypeError} + * @public + */ +function toBuffer(data) { + toBuffer.readOnly = true; + + if (Buffer.isBuffer(data)) return data; + + let buf; + + if (data instanceof ArrayBuffer) { + buf = Buffer.from(data); + } else if (ArrayBuffer.isView(data)) { + buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength); + } else { + buf = Buffer.from(data); + toBuffer.readOnly = false; + } + + return buf; +} + +try { + const bufferUtil = require('bufferutil'); + const bu = bufferUtil.BufferUtil || bufferUtil; + + module.exports = { + concat, + mask(source, mask, output, offset, length) { + if (length < 48) _mask(source, mask, output, offset, length); + else bu.mask(source, mask, output, offset, length); + }, + toArrayBuffer, + toBuffer, + unmask(buffer, mask) { + if (buffer.length < 32) _unmask(buffer, mask); + else bu.unmask(buffer, mask); + } + }; +} catch (e) /* istanbul ignore next */ { + module.exports = { + concat, + mask: _mask, + toArrayBuffer, + toBuffer, + unmask: _unmask + }; +} diff --git a/node_modules/ws/lib/constants.js b/node_modules/ws/lib/constants.js new file mode 100644 index 0000000..4082981 --- /dev/null +++ b/node_modules/ws/lib/constants.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'], + GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + kStatusCode: Symbol('status-code'), + kWebSocket: Symbol('websocket'), + EMPTY_BUFFER: Buffer.alloc(0), + NOOP: () => {} +}; diff --git a/node_modules/ws/lib/event-target.js b/node_modules/ws/lib/event-target.js new file mode 100644 index 0000000..d38e1f2 --- /dev/null +++ b/node_modules/ws/lib/event-target.js @@ -0,0 +1,177 @@ +'use strict'; + +/** + * Class representing an event. + * + * @private + */ +class Event { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @param {Object} target A reference to the target to which the event was dispatched + */ + constructor(type, target) { + this.target = target; + this.type = type; + } +} + +/** + * Class representing a message event. + * + * @extends Event + * @private + */ +class MessageEvent extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(data, target) { + super('message', target); + + this.data = data; + } +} + +/** + * Class representing a close event. + * + * @extends Event + * @private + */ +class CloseEvent extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {Number} code The status code explaining why the connection is being closed + * @param {String} reason A human-readable string explaining why the connection is closing + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(code, reason, target) { + super('close', target); + + this.wasClean = target._closeFrameReceived && target._closeFrameSent; + this.reason = reason; + this.code = code; + } +} + +/** + * Class representing an open event. + * + * @extends Event + * @private + */ +class OpenEvent extends Event { + /** + * Create a new `OpenEvent`. + * + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(target) { + super('open', target); + } +} + +/** + * Class representing an error event. + * + * @extends Event + * @private + */ +class ErrorEvent extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {Object} error The error that generated this event + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(error, target) { + super('error', target); + + this.message = error.message; + this.error = error; + } +} + +/** + * This provides methods for emulating the `EventTarget` interface. It's not + * meant to be used directly. + * + * @mixin + */ +const EventTarget = { + /** + * Register an event listener. + * + * @param {String} type A string representing the event type to listen for + * @param {Function} listener The listener to add + * @param {Object} options An options object specifies characteristics about + * the event listener + * @param {Boolean} options.once A `Boolean`` indicating that the listener + * should be invoked at most once after being added. If `true`, the + * listener would be automatically removed when invoked. + * @public + */ + addEventListener(type, listener, options) { + if (typeof listener !== 'function') return; + + function onMessage(data) { + listener.call(this, new MessageEvent(data, this)); + } + + function onClose(code, message) { + listener.call(this, new CloseEvent(code, message, this)); + } + + function onError(error) { + listener.call(this, new ErrorEvent(error, this)); + } + + function onOpen() { + listener.call(this, new OpenEvent(this)); + } + + const method = options && options.once ? 'once' : 'on'; + + if (type === 'message') { + onMessage._listener = listener; + this[method](type, onMessage); + } else if (type === 'close') { + onClose._listener = listener; + this[method](type, onClose); + } else if (type === 'error') { + onError._listener = listener; + this[method](type, onError); + } else if (type === 'open') { + onOpen._listener = listener; + this[method](type, onOpen); + } else { + this[method](type, listener); + } + }, + + /** + * Remove an event listener. + * + * @param {String} type A string representing the event type to remove + * @param {Function} listener The listener to remove + * @public + */ + removeEventListener(type, listener) { + const listeners = this.listeners(type); + + for (let i = 0; i < listeners.length; i++) { + if (listeners[i] === listener || listeners[i]._listener === listener) { + this.removeListener(type, listeners[i]); + } + } + } +}; + +module.exports = EventTarget; diff --git a/node_modules/ws/lib/extension.js b/node_modules/ws/lib/extension.js new file mode 100644 index 0000000..87a4213 --- /dev/null +++ b/node_modules/ws/lib/extension.js @@ -0,0 +1,223 @@ +'use strict'; + +// +// Allowed token characters: +// +// '!', '#', '$', '%', '&', ''', '*', '+', '-', +// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' +// +// tokenChars[32] === 0 // ' ' +// tokenChars[33] === 1 // '!' +// tokenChars[34] === 0 // '"' +// ... +// +// prettier-ignore +const tokenChars = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 +]; + +/** + * Adds an offer to the map of extension offers or a parameter to the map of + * parameters. + * + * @param {Object} dest The map of extension offers or parameters + * @param {String} name The extension or parameter name + * @param {(Object|Boolean|String)} elem The extension parameters or the + * parameter value + * @private + */ +function push(dest, name, elem) { + if (dest[name] === undefined) dest[name] = [elem]; + else dest[name].push(elem); +} + +/** + * Parses the `Sec-WebSocket-Extensions` header into an object. + * + * @param {String} header The field value of the header + * @return {Object} The parsed object + * @public + */ +function parse(header) { + const offers = Object.create(null); + + if (header === undefined || header === '') return offers; + + let params = Object.create(null); + let mustUnescape = false; + let isEscaping = false; + let inQuotes = false; + let extensionName; + let paramName; + let start = -1; + let end = -1; + let i = 0; + + for (; i < header.length; i++) { + const code = header.charCodeAt(i); + + if (extensionName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + const name = header.slice(start, end); + if (code === 0x2c) { + push(offers, name, params); + params = Object.create(null); + } else { + extensionName = name; + } + + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (paramName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 || code === 0x09) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + push(params, header.slice(start, end), true); + if (code === 0x2c) { + push(offers, extensionName, params); + params = Object.create(null); + extensionName = undefined; + } + + start = end = -1; + } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) { + paramName = header.slice(start, i); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else { + // + // The value of a quoted-string after unescaping must conform to the + // token ABNF, so only token characters are valid. + // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 + // + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (start === -1) start = i; + else if (!mustUnescape) mustUnescape = true; + isEscaping = false; + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x22 /* '"' */ && start !== -1) { + inQuotes = false; + end = i; + } else if (code === 0x5c /* '\' */) { + isEscaping = true; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { + inQuotes = true; + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (start !== -1 && (code === 0x20 || code === 0x09)) { + if (end === -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + let value = header.slice(start, end); + if (mustUnescape) { + value = value.replace(/\\/g, ''); + mustUnescape = false; + } + push(params, paramName, value); + if (code === 0x2c) { + push(offers, extensionName, params); + params = Object.create(null); + extensionName = undefined; + } + + paramName = undefined; + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + } + + if (start === -1 || inQuotes) { + throw new SyntaxError('Unexpected end of input'); + } + + if (end === -1) end = i; + const token = header.slice(start, end); + if (extensionName === undefined) { + push(offers, token, params); + } else { + if (paramName === undefined) { + push(params, token, true); + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, '')); + } else { + push(params, paramName, token); + } + push(offers, extensionName, params); + } + + return offers; +} + +/** + * Builds the `Sec-WebSocket-Extensions` header field value. + * + * @param {Object} extensions The map of extensions and parameters to format + * @return {String} A string representing the given object + * @public + */ +function format(extensions) { + return Object.keys(extensions) + .map((extension) => { + let configurations = extensions[extension]; + if (!Array.isArray(configurations)) configurations = [configurations]; + return configurations + .map((params) => { + return [extension] + .concat( + Object.keys(params).map((k) => { + let values = params[k]; + if (!Array.isArray(values)) values = [values]; + return values + .map((v) => (v === true ? k : `${k}=${v}`)) + .join('; '); + }) + ) + .join('; '); + }) + .join(', '); + }) + .join(', '); +} + +module.exports = { format, parse }; diff --git a/node_modules/ws/lib/limiter.js b/node_modules/ws/lib/limiter.js new file mode 100644 index 0000000..8e99086 --- /dev/null +++ b/node_modules/ws/lib/limiter.js @@ -0,0 +1,54 @@ +'use strict'; + +const kDone = Symbol('kDone'); +const kRun = Symbol('kRun'); + +/** + * A very simple job queue with adjustable concurrency. Adapted from + * https://github.com/STRML/async-limiter + */ +class Limiter { + /** + * Creates a new `Limiter`. + * + * @param {Number} concurrency The maximum number of jobs allowed to run + * concurrently + */ + constructor(concurrency) { + this[kDone] = () => { + this.pending--; + this[kRun](); + }; + this.concurrency = concurrency || Infinity; + this.jobs = []; + this.pending = 0; + } + + /** + * Adds a job to the queue. + * + * @public + */ + add(job) { + this.jobs.push(job); + this[kRun](); + } + + /** + * Removes a job from the queue and runs it if possible. + * + * @private + */ + [kRun]() { + if (this.pending === this.concurrency) return; + + if (this.jobs.length) { + const job = this.jobs.shift(); + + this.pending++; + job(this[kDone]); + } + } +} + +module.exports = Limiter; diff --git a/node_modules/ws/lib/permessage-deflate.js b/node_modules/ws/lib/permessage-deflate.js new file mode 100644 index 0000000..7bb7c98 --- /dev/null +++ b/node_modules/ws/lib/permessage-deflate.js @@ -0,0 +1,512 @@ +'use strict'; + +const zlib = require('zlib'); + +const bufferUtil = require('./buffer-util'); +const Limiter = require('./limiter'); +const { kStatusCode, NOOP } = require('./constants'); + +const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]); +const kPerMessageDeflate = Symbol('permessage-deflate'); +const kTotalLength = Symbol('total-length'); +const kCallback = Symbol('callback'); +const kBuffers = Symbol('buffers'); +const kError = Symbol('error'); + +// +// We limit zlib concurrency, which prevents severe memory fragmentation +// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913 +// and https://github.com/websockets/ws/issues/1202 +// +// Intentionally global; it's the global thread pool that's an issue. +// +let zlibLimiter; + +/** + * permessage-deflate implementation. + */ +class PerMessageDeflate { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} options Configuration options + * @param {Boolean} options.serverNoContextTakeover Request/accept disabling + * of server context takeover + * @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge + * disabling of client context takeover + * @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the + * use of a custom server window size + * @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support + * for, or request, a custom client window size + * @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate + * @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate + * @param {Number} options.threshold Size (in bytes) below which messages + * should not be compressed + * @param {Number} options.concurrencyLimit The number of concurrent calls to + * zlib + * @param {Boolean} isServer Create the instance in either server or client + * mode + * @param {Number} maxPayload The maximum allowed message length + */ + constructor(options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0; + this._options = options || {}; + this._threshold = + this._options.threshold !== undefined ? this._options.threshold : 1024; + this._isServer = !!isServer; + this._deflate = null; + this._inflate = null; + + this.params = null; + + if (!zlibLimiter) { + const concurrency = + this._options.concurrencyLimit !== undefined + ? this._options.concurrencyLimit + : 10; + zlibLimiter = new Limiter(concurrency); + } + } + + /** + * @type {String} + */ + static get extensionName() { + return 'permessage-deflate'; + } + + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer() { + const params = {}; + + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + + return params; + } + + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept(configurations) { + configurations = this.normalizeParams(configurations); + + this.params = this._isServer + ? this.acceptAsServer(configurations) + : this.acceptAsClient(configurations); + + return this.params; + } + + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup() { + if (this._inflate) { + this._inflate.close(); + this._inflate = null; + } + + if (this._deflate) { + const callback = this._deflate[kCallback]; + + this._deflate.close(); + this._deflate = null; + + if (callback) { + callback( + new Error( + 'The deflate stream was closed while data was being processed' + ) + ); + } + } + } + + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer(offers) { + const opts = this._options; + const accepted = offers.find((params) => { + if ( + (opts.serverNoContextTakeover === false && + params.server_no_context_takeover) || + (params.server_max_window_bits && + (opts.serverMaxWindowBits === false || + (typeof opts.serverMaxWindowBits === 'number' && + opts.serverMaxWindowBits > params.server_max_window_bits))) || + (typeof opts.clientMaxWindowBits === 'number' && + !params.client_max_window_bits) + ) { + return false; + } + + return true; + }); + + if (!accepted) { + throw new Error('None of the extension offers can be accepted'); + } + + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true; + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (typeof opts.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = opts.serverMaxWindowBits; + } + if (typeof opts.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = opts.clientMaxWindowBits; + } else if ( + accepted.client_max_window_bits === true || + opts.clientMaxWindowBits === false + ) { + delete accepted.client_max_window_bits; + } + + return accepted; + } + + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient(response) { + const params = response[0]; + + if ( + this._options.clientNoContextTakeover === false && + params.client_no_context_takeover + ) { + throw new Error('Unexpected parameter "client_no_context_takeover"'); + } + + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === 'number') { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } + } else if ( + this._options.clientMaxWindowBits === false || + (typeof this._options.clientMaxWindowBits === 'number' && + params.client_max_window_bits > this._options.clientMaxWindowBits) + ) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ); + } + + return params; + } + + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams(configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + let value = params[key]; + + if (value.length > 1) { + throw new Error(`Parameter "${key}" must have only a single value`); + } + + value = value[0]; + + if (key === 'client_max_window_bits') { + if (value !== true) { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else if (key === 'server_max_window_bits') { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if ( + key === 'client_no_context_takeover' || + key === 'server_no_context_takeover' + ) { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else { + throw new Error(`Unknown parameter "${key}"`); + } + + params[key] = value; + }); + }); + + return configurations; + } + + /** + * Decompress data. Concurrency limited. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress(data, fin, callback) { + zlibLimiter.add((done) => { + this._decompress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Compress data. Concurrency limited. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress(data, fin, callback) { + zlibLimiter.add((done) => { + this._compress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress(data, fin, callback) { + const endpoint = this._isServer ? 'client' : 'server'; + + if (!this._inflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._inflate = zlib.createInflateRaw({ + ...this._options.zlibInflateOptions, + windowBits + }); + this._inflate[kPerMessageDeflate] = this; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + this._inflate.on('error', inflateOnError); + this._inflate.on('data', inflateOnData); + } + + this._inflate[kCallback] = callback; + + this._inflate.write(data); + if (fin) this._inflate.write(TRAILER); + + this._inflate.flush(() => { + const err = this._inflate[kError]; + + if (err) { + this._inflate.close(); + this._inflate = null; + callback(err); + return; + } + + const data = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ); + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._inflate.close(); + this._inflate = null; + } else { + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + } + + callback(null, data); + }); + } + + /** + * Compress data. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress(data, fin, callback) { + const endpoint = this._isServer ? 'server' : 'client'; + + if (!this._deflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._deflate = zlib.createDeflateRaw({ + ...this._options.zlibDeflateOptions, + windowBits + }); + + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + + // + // An `'error'` event is emitted, only on Node.js < 10.0.0, if the + // `zlib.DeflateRaw` instance is closed while data is being processed. + // This can happen if `PerMessageDeflate#cleanup()` is called at the wrong + // time due to an abnormal WebSocket closure. + // + this._deflate.on('error', NOOP); + this._deflate.on('data', deflateOnData); + } + + this._deflate[kCallback] = callback; + + this._deflate.write(data); + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + if (!this._deflate) { + // + // The deflate stream was closed while data was being processed. + // + return; + } + + let data = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ); + + if (fin) data = data.slice(0, data.length - 4); + + // + // Ensure that the callback will not be called again in + // `PerMessageDeflate#cleanup()`. + // + this._deflate[kCallback] = null; + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._deflate.close(); + this._deflate = null; + } else { + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + } + + callback(null, data); + }); + } +} + +module.exports = PerMessageDeflate; + +/** + * The listener of the `zlib.DeflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function deflateOnData(chunk) { + this[kBuffers].push(chunk); + this[kTotalLength] += chunk.length; +} + +/** + * The listener of the `zlib.InflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function inflateOnData(chunk) { + this[kTotalLength] += chunk.length; + + if ( + this[kPerMessageDeflate]._maxPayload < 1 || + this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload + ) { + this[kBuffers].push(chunk); + return; + } + + this[kError] = new RangeError('Max payload size exceeded'); + this[kError][kStatusCode] = 1009; + this.removeListener('data', inflateOnData); + this.reset(); +} + +/** + * The listener of the `zlib.InflateRaw` stream `'error'` event. + * + * @param {Error} err The emitted error + * @private + */ +function inflateOnError(err) { + // + // There is no need to call `Zlib#close()` as the handle is automatically + // closed when an error is emitted. + // + this[kPerMessageDeflate]._inflate = null; + err[kStatusCode] = 1007; + this[kCallback](err); +} diff --git a/node_modules/ws/lib/receiver.js b/node_modules/ws/lib/receiver.js new file mode 100644 index 0000000..d762393 --- /dev/null +++ b/node_modules/ws/lib/receiver.js @@ -0,0 +1,507 @@ +'use strict'; + +const { Writable } = require('stream'); + +const PerMessageDeflate = require('./permessage-deflate'); +const { + BINARY_TYPES, + EMPTY_BUFFER, + kStatusCode, + kWebSocket +} = require('./constants'); +const { concat, toArrayBuffer, unmask } = require('./buffer-util'); +const { isValidStatusCode, isValidUTF8 } = require('./validation'); + +const GET_INFO = 0; +const GET_PAYLOAD_LENGTH_16 = 1; +const GET_PAYLOAD_LENGTH_64 = 2; +const GET_MASK = 3; +const GET_DATA = 4; +const INFLATING = 5; + +/** + * HyBi Receiver implementation. + * + * @extends stream.Writable + */ +class Receiver extends Writable { + /** + * Creates a Receiver instance. + * + * @param {String} binaryType The type for binary data + * @param {Object} extensions An object containing the negotiated extensions + * @param {Boolean} isServer Specifies whether to operate in client or server + * mode + * @param {Number} maxPayload The maximum allowed message length + */ + constructor(binaryType, extensions, isServer, maxPayload) { + super(); + + this._binaryType = binaryType || BINARY_TYPES[0]; + this[kWebSocket] = undefined; + this._extensions = extensions || {}; + this._isServer = !!isServer; + this._maxPayload = maxPayload | 0; + + this._bufferedBytes = 0; + this._buffers = []; + + this._compressed = false; + this._payloadLength = 0; + this._mask = undefined; + this._fragmented = 0; + this._masked = false; + this._fin = false; + this._opcode = 0; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragments = []; + + this._state = GET_INFO; + this._loop = false; + } + + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + * @private + */ + _write(chunk, encoding, cb) { + if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); + + this._bufferedBytes += chunk.length; + this._buffers.push(chunk); + this.startLoop(cb); + } + + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume(n) { + this._bufferedBytes -= n; + + if (n === this._buffers[0].length) return this._buffers.shift(); + + if (n < this._buffers[0].length) { + const buf = this._buffers[0]; + this._buffers[0] = buf.slice(n); + return buf.slice(0, n); + } + + const dst = Buffer.allocUnsafe(n); + + do { + const buf = this._buffers[0]; + const offset = dst.length - n; + + if (n >= buf.length) { + dst.set(this._buffers.shift(), offset); + } else { + dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset); + this._buffers[0] = buf.slice(n); + } + + n -= buf.length; + } while (n > 0); + + return dst; + } + + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop(cb) { + let err; + this._loop = true; + + do { + switch (this._state) { + case GET_INFO: + err = this.getInfo(); + break; + case GET_PAYLOAD_LENGTH_16: + err = this.getPayloadLength16(); + break; + case GET_PAYLOAD_LENGTH_64: + err = this.getPayloadLength64(); + break; + case GET_MASK: + this.getMask(); + break; + case GET_DATA: + err = this.getData(cb); + break; + default: + // `INFLATING` + this._loop = false; + return; + } + } while (this._loop); + + cb(err); + } + + /** + * Reads the first two bytes of a frame. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getInfo() { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + const buf = this.consume(2); + + if ((buf[0] & 0x30) !== 0x00) { + this._loop = false; + return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002); + } + + const compressed = (buf[0] & 0x40) === 0x40; + + if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + this._fin = (buf[0] & 0x80) === 0x80; + this._opcode = buf[0] & 0x0f; + this._payloadLength = buf[1] & 0x7f; + + if (this._opcode === 0x00) { + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (!this._fragmented) { + this._loop = false; + return error(RangeError, 'invalid opcode 0', true, 1002); + } + + this._opcode = this._fragmented; + } else if (this._opcode === 0x01 || this._opcode === 0x02) { + if (this._fragmented) { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + this._compressed = compressed; + } else if (this._opcode > 0x07 && this._opcode < 0x0b) { + if (!this._fin) { + this._loop = false; + return error(RangeError, 'FIN must be set', true, 1002); + } + + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (this._payloadLength > 0x7d) { + this._loop = false; + return error( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002 + ); + } + } else { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + if (!this._fin && !this._fragmented) this._fragmented = this._opcode; + this._masked = (buf[1] & 0x80) === 0x80; + + if (this._isServer) { + if (!this._masked) { + this._loop = false; + return error(RangeError, 'MASK must be set', true, 1002); + } + } else if (this._masked) { + this._loop = false; + return error(RangeError, 'MASK must be clear', true, 1002); + } + + if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; + else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; + else return this.haveLength(); + } + + /** + * Gets extended payload length (7+16). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength16() { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + this._payloadLength = this.consume(2).readUInt16BE(0); + return this.haveLength(); + } + + /** + * Gets extended payload length (7+64). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength64() { + if (this._bufferedBytes < 8) { + this._loop = false; + return; + } + + const buf = this.consume(8); + const num = buf.readUInt32BE(0); + + // + // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned + // if payload length is greater than this number. + // + if (num > Math.pow(2, 53 - 32) - 1) { + this._loop = false; + return error( + RangeError, + 'Unsupported WebSocket frame: payload length > 2^53 - 1', + false, + 1009 + ); + } + + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); + return this.haveLength(); + } + + /** + * Payload length has been read. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + haveLength() { + if (this._payloadLength && this._opcode < 0x08) { + this._totalPayloadLength += this._payloadLength; + if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { + this._loop = false; + return error(RangeError, 'Max payload size exceeded', false, 1009); + } + } + + if (this._masked) this._state = GET_MASK; + else this._state = GET_DATA; + } + + /** + * Reads mask bytes. + * + * @private + */ + getMask() { + if (this._bufferedBytes < 4) { + this._loop = false; + return; + } + + this._mask = this.consume(4); + this._state = GET_DATA; + } + + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + getData(cb) { + let data = EMPTY_BUFFER; + + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false; + return; + } + + data = this.consume(this._payloadLength); + if (this._masked) unmask(data, this._mask); + } + + if (this._opcode > 0x07) return this.controlMessage(data); + + if (this._compressed) { + this._state = INFLATING; + this.decompress(data, cb); + return; + } + + if (data.length) { + // + // This message is not compressed so its lenght is the sum of the payload + // length of all fragments. + // + this._messageLength = this._totalPayloadLength; + this._fragments.push(data); + } + + return this.dataMessage(); + } + + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress(data, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) return cb(err); + + if (buf.length) { + this._messageLength += buf.length; + if (this._messageLength > this._maxPayload && this._maxPayload > 0) { + return cb( + error(RangeError, 'Max payload size exceeded', false, 1009) + ); + } + + this._fragments.push(buf); + } + + const er = this.dataMessage(); + if (er) return cb(er); + + this.startLoop(cb); + }); + } + + /** + * Handles a data message. + * + * @return {(Error|undefined)} A possible error + * @private + */ + dataMessage() { + if (this._fin) { + const messageLength = this._messageLength; + const fragments = this._fragments; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragmented = 0; + this._fragments = []; + + if (this._opcode === 2) { + let data; + + if (this._binaryType === 'nodebuffer') { + data = concat(fragments, messageLength); + } else if (this._binaryType === 'arraybuffer') { + data = toArrayBuffer(concat(fragments, messageLength)); + } else { + data = fragments; + } + + this.emit('message', data); + } else { + const buf = concat(fragments, messageLength); + + if (!isValidUTF8(buf)) { + this._loop = false; + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('message', buf.toString()); + } + } + + this._state = GET_INFO; + } + + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage(data) { + if (this._opcode === 0x08) { + this._loop = false; + + if (data.length === 0) { + this.emit('conclude', 1005, ''); + this.end(); + } else if (data.length === 1) { + return error(RangeError, 'invalid payload length 1', true, 1002); + } else { + const code = data.readUInt16BE(0); + + if (!isValidStatusCode(code)) { + return error(RangeError, `invalid status code ${code}`, true, 1002); + } + + const buf = data.slice(2); + + if (!isValidUTF8(buf)) { + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('conclude', code, buf.toString()); + this.end(); + } + } else if (this._opcode === 0x09) { + this.emit('ping', data); + } else { + this.emit('pong', data); + } + + this._state = GET_INFO; + } +} + +module.exports = Receiver; + +/** + * Builds an error object. + * + * @param {(Error|RangeError)} ErrorCtor The error constructor + * @param {String} message The error message + * @param {Boolean} prefix Specifies whether or not to add a default prefix to + * `message` + * @param {Number} statusCode The status code + * @return {(Error|RangeError)} The error + * @private + */ +function error(ErrorCtor, message, prefix, statusCode) { + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ); + + Error.captureStackTrace(err, error); + err[kStatusCode] = statusCode; + return err; +} diff --git a/node_modules/ws/lib/sender.js b/node_modules/ws/lib/sender.js new file mode 100644 index 0000000..3ea2fc1 --- /dev/null +++ b/node_modules/ws/lib/sender.js @@ -0,0 +1,392 @@ +'use strict'; + +const { randomFillSync } = require('crypto'); + +const PerMessageDeflate = require('./permessage-deflate'); +const { EMPTY_BUFFER } = require('./constants'); +const { isValidStatusCode } = require('./validation'); +const { mask: applyMask, toBuffer } = require('./buffer-util'); + +const mask = Buffer.alloc(4); + +/** + * HyBi Sender implementation. + */ +class Sender { + /** + * Creates a Sender instance. + * + * @param {net.Socket} socket The connection socket + * @param {Object} extensions An object containing the negotiated extensions + */ + constructor(socket, extensions) { + this._extensions = extensions || {}; + this._socket = socket; + + this._firstFragment = true; + this._compress = false; + + this._bufferedBytes = 0; + this._deflating = false; + this._queue = []; + } + + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {Buffer} data The data to frame + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @return {Buffer[]} The framed data as a list of `Buffer` instances + * @public + */ + static frame(data, options) { + const merge = options.mask && options.readOnly; + let offset = options.mask ? 6 : 2; + let payloadLength = data.length; + + if (data.length >= 65536) { + offset += 8; + payloadLength = 127; + } else if (data.length > 125) { + offset += 2; + payloadLength = 126; + } + + const target = Buffer.allocUnsafe(merge ? data.length + offset : offset); + + target[0] = options.fin ? options.opcode | 0x80 : options.opcode; + if (options.rsv1) target[0] |= 0x40; + + target[1] = payloadLength; + + if (payloadLength === 126) { + target.writeUInt16BE(data.length, 2); + } else if (payloadLength === 127) { + target.writeUInt32BE(0, 2); + target.writeUInt32BE(data.length, 6); + } + + if (!options.mask) return [target, data]; + + randomFillSync(mask, 0, 4); + + target[1] |= 0x80; + target[offset - 4] = mask[0]; + target[offset - 3] = mask[1]; + target[offset - 2] = mask[2]; + target[offset - 1] = mask[3]; + + if (merge) { + applyMask(data, mask, target, offset, data.length); + return [target]; + } + + applyMask(data, mask, data, 0, data.length); + return [target, data]; + } + + /** + * Sends a close message to the other peer. + * + * @param {(Number|undefined)} code The status code component of the body + * @param {String} data The message component of the body + * @param {Boolean} mask Specifies whether or not to mask the message + * @param {Function} cb Callback + * @public + */ + close(code, data, mask, cb) { + let buf; + + if (code === undefined) { + buf = EMPTY_BUFFER; + } else if (typeof code !== 'number' || !isValidStatusCode(code)) { + throw new TypeError('First argument must be a valid error code number'); + } else if (data === undefined || data === '') { + buf = Buffer.allocUnsafe(2); + buf.writeUInt16BE(code, 0); + } else { + const length = Buffer.byteLength(data); + + if (length > 123) { + throw new RangeError('The message must not be greater than 123 bytes'); + } + + buf = Buffer.allocUnsafe(2 + length); + buf.writeUInt16BE(code, 0); + buf.write(data, 2); + } + + if (this._deflating) { + this.enqueue([this.doClose, buf, mask, cb]); + } else { + this.doClose(buf, mask, cb); + } + } + + /** + * Frames and sends a close message. + * + * @param {Buffer} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @private + */ + doClose(data, mask, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x08, + mask, + readOnly: false + }), + cb + ); + } + + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + ping(data, mask, cb) { + const buf = toBuffer(data); + + if (buf.length > 125) { + throw new RangeError('The data size must not be greater than 125 bytes'); + } + + if (this._deflating) { + this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]); + } else { + this.doPing(buf, mask, toBuffer.readOnly, cb); + } + } + + /** + * Frames and sends a ping message. + * + * @param {Buffer} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPing(data, mask, readOnly, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x09, + mask, + readOnly + }), + cb + ); + } + + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + pong(data, mask, cb) { + const buf = toBuffer(data); + + if (buf.length > 125) { + throw new RangeError('The data size must not be greater than 125 bytes'); + } + + if (this._deflating) { + this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]); + } else { + this.doPong(buf, mask, toBuffer.readOnly, cb); + } + } + + /** + * Frames and sends a pong message. + * + * @param {Buffer} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPong(data, mask, readOnly, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x0a, + mask, + readOnly + }), + cb + ); + } + + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + send(data, options, cb) { + const buf = toBuffer(data); + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + let opcode = options.binary ? 2 : 1; + let rsv1 = options.compress; + + if (this._firstFragment) { + this._firstFragment = false; + if (rsv1 && perMessageDeflate) { + rsv1 = buf.length >= perMessageDeflate._threshold; + } + this._compress = rsv1; + } else { + rsv1 = false; + opcode = 0; + } + + if (options.fin) this._firstFragment = true; + + if (perMessageDeflate) { + const opts = { + fin: options.fin, + rsv1, + opcode, + mask: options.mask, + readOnly: toBuffer.readOnly + }; + + if (this._deflating) { + this.enqueue([this.dispatch, buf, this._compress, opts, cb]); + } else { + this.dispatch(buf, this._compress, opts, cb); + } + } else { + this.sendFrame( + Sender.frame(buf, { + fin: options.fin, + rsv1: false, + opcode, + mask: options.mask, + readOnly: toBuffer.readOnly + }), + cb + ); + } + } + + /** + * Dispatches a data message. + * + * @param {Buffer} data The message to send + * @param {Boolean} compress Specifies whether or not to compress `data` + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @param {Function} cb Callback + * @private + */ + dispatch(data, compress, options, cb) { + if (!compress) { + this.sendFrame(Sender.frame(data, options), cb); + return; + } + + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + this._bufferedBytes += data.length; + this._deflating = true; + perMessageDeflate.compress(data, options.fin, (_, buf) => { + if (this._socket.destroyed) { + const err = new Error( + 'The socket was closed while data was being compressed' + ); + + if (typeof cb === 'function') cb(err); + + for (let i = 0; i < this._queue.length; i++) { + const callback = this._queue[i][4]; + + if (typeof callback === 'function') callback(err); + } + + return; + } + + this._bufferedBytes -= data.length; + this._deflating = false; + options.readOnly = false; + this.sendFrame(Sender.frame(buf, options), cb); + this.dequeue(); + }); + } + + /** + * Executes queued send operations. + * + * @private + */ + dequeue() { + while (!this._deflating && this._queue.length) { + const params = this._queue.shift(); + + this._bufferedBytes -= params[1].length; + Reflect.apply(params[0], this, params.slice(1)); + } + } + + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue(params) { + this._bufferedBytes += params[1].length; + this._queue.push(params); + } + + /** + * Sends a frame. + * + * @param {Buffer[]} list The frame to send + * @param {Function} cb Callback + * @private + */ + sendFrame(list, cb) { + if (list.length === 2) { + this._socket.cork(); + this._socket.write(list[0]); + this._socket.write(list[1], cb); + this._socket.uncork(); + } else { + this._socket.write(list[0], cb); + } + } +} + +module.exports = Sender; diff --git a/node_modules/ws/lib/stream.js b/node_modules/ws/lib/stream.js new file mode 100644 index 0000000..6e2f0b4 --- /dev/null +++ b/node_modules/ws/lib/stream.js @@ -0,0 +1,164 @@ +'use strict'; + +const { Duplex } = require('stream'); + +/** + * Emits the `'close'` event on a stream. + * + * @param {stream.Duplex} The stream. + * @private + */ +function emitClose(stream) { + stream.emit('close'); +} + +/** + * The listener of the `'end'` event. + * + * @private + */ +function duplexOnEnd() { + if (!this.destroyed && this._writableState.finished) { + this.destroy(); + } +} + +/** + * The listener of the `'error'` event. + * + * @private + */ +function duplexOnError(err) { + this.removeListener('error', duplexOnError); + this.destroy(); + if (this.listenerCount('error') === 0) { + // Do not suppress the throwing behavior. + this.emit('error', err); + } +} + +/** + * Wraps a `WebSocket` in a duplex stream. + * + * @param {WebSocket} ws The `WebSocket` to wrap + * @param {Object} options The options for the `Duplex` constructor + * @return {stream.Duplex} The duplex stream + * @public + */ +function createWebSocketStream(ws, options) { + let resumeOnReceiverDrain = true; + + function receiverOnDrain() { + if (resumeOnReceiverDrain) ws._socket.resume(); + } + + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + ws._receiver.removeAllListeners('drain'); + ws._receiver.on('drain', receiverOnDrain); + }); + } else { + ws._receiver.removeAllListeners('drain'); + ws._receiver.on('drain', receiverOnDrain); + } + + const duplex = new Duplex({ + ...options, + autoDestroy: false, + emitClose: false, + objectMode: false, + writableObjectMode: false + }); + + ws.on('message', function message(msg) { + if (!duplex.push(msg)) { + resumeOnReceiverDrain = false; + ws._socket.pause(); + } + }); + + ws.once('error', function error(err) { + if (duplex.destroyed) return; + + duplex.destroy(err); + }); + + ws.once('close', function close() { + if (duplex.destroyed) return; + + duplex.push(null); + }); + + duplex._destroy = function (err, callback) { + if (ws.readyState === ws.CLOSED) { + callback(err); + process.nextTick(emitClose, duplex); + return; + } + + let called = false; + + ws.once('error', function error(err) { + called = true; + callback(err); + }); + + ws.once('close', function close() { + if (!called) callback(err); + process.nextTick(emitClose, duplex); + }); + ws.terminate(); + }; + + duplex._final = function (callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + duplex._final(callback); + }); + return; + } + + // If the value of the `_socket` property is `null` it means that `ws` is a + // client websocket and the handshake failed. In fact, when this happens, a + // socket is never assigned to the websocket. Wait for the `'error'` event + // that will be emitted by the websocket. + if (ws._socket === null) return; + + if (ws._socket._writableState.finished) { + callback(); + if (duplex._readableState.endEmitted) duplex.destroy(); + } else { + ws._socket.once('finish', function finish() { + // `duplex` is not destroyed here because the `'end'` event will be + // emitted on `duplex` after this `'finish'` event. The EOF signaling + // `null` chunk is, in fact, pushed when the websocket emits `'close'`. + callback(); + }); + ws.close(); + } + }; + + duplex._read = function () { + if (ws.readyState === ws.OPEN && !resumeOnReceiverDrain) { + resumeOnReceiverDrain = true; + if (!ws._receiver._writableState.needDrain) ws._socket.resume(); + } + }; + + duplex._write = function (chunk, encoding, callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + duplex._write(chunk, encoding, callback); + }); + return; + } + + ws.send(chunk, callback); + }; + + duplex.on('end', duplexOnEnd); + duplex.on('error', duplexOnError); + return duplex; +} + +module.exports = createWebSocketStream; diff --git a/node_modules/ws/lib/validation.js b/node_modules/ws/lib/validation.js new file mode 100644 index 0000000..32db5a5 --- /dev/null +++ b/node_modules/ws/lib/validation.js @@ -0,0 +1,30 @@ +'use strict'; + +try { + const isValidUTF8 = require('utf-8-validate'); + + exports.isValidUTF8 = + typeof isValidUTF8 === 'object' + ? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0 + : isValidUTF8; +} catch (e) /* istanbul ignore next */ { + exports.isValidUTF8 = () => true; +} + +/** + * Checks if a status code is allowed in a close frame. + * + * @param {Number} code The status code + * @return {Boolean} `true` if the status code is valid, else `false` + * @public + */ +exports.isValidStatusCode = (code) => { + return ( + (code >= 1000 && + code <= 1014 && + code !== 1004 && + code !== 1005 && + code !== 1006) || + (code >= 3000 && code <= 4999) + ); +}; diff --git a/node_modules/ws/lib/websocket-server.js b/node_modules/ws/lib/websocket-server.js new file mode 100644 index 0000000..0d3f395 --- /dev/null +++ b/node_modules/ws/lib/websocket-server.js @@ -0,0 +1,404 @@ +'use strict'; + +const EventEmitter = require('events'); +const { createHash } = require('crypto'); +const { createServer, STATUS_CODES } = require('http'); + +const PerMessageDeflate = require('./permessage-deflate'); +const WebSocket = require('./websocket'); +const { format, parse } = require('./extension'); +const { GUID, kWebSocket } = require('./constants'); + +const keyRegex = /^[+/0-9A-Za-z]{22}==$/; + +/** + * Class representing a WebSocket server. + * + * @extends EventEmitter + */ +class WebSocketServer extends EventEmitter { + /** + * Create a `WebSocketServer` instance. + * + * @param {Object} options Configuration options + * @param {Number} options.backlog The maximum length of the queue of pending + * connections + * @param {Boolean} options.clientTracking Specifies whether or not to track + * clients + * @param {Function} options.handleProtocols A hook to handle protocols + * @param {String} options.host The hostname where to bind the server + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Boolean} options.noServer Enable no server mode + * @param {String} options.path Accept only connections matching this path + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable + * permessage-deflate + * @param {Number} options.port The port where to bind the server + * @param {http.Server} options.server A pre-created HTTP/S server to use + * @param {Function} options.verifyClient A hook to reject connections + * @param {Function} callback A listener for the `listening` event + */ + constructor(options, callback) { + super(); + + options = { + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: false, + handleProtocols: null, + clientTracking: true, + verifyClient: null, + noServer: false, + backlog: null, // use default (511 as implemented in net.js) + server: null, + host: null, + path: null, + port: null, + ...options + }; + + if (options.port == null && !options.server && !options.noServer) { + throw new TypeError( + 'One of the "port", "server", or "noServer" options must be specified' + ); + } + + if (options.port != null) { + this._server = createServer((req, res) => { + const body = STATUS_CODES[426]; + + res.writeHead(426, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain' + }); + res.end(body); + }); + this._server.listen( + options.port, + options.host, + options.backlog, + callback + ); + } else if (options.server) { + this._server = options.server; + } + + if (this._server) { + this._removeListeners = addListeners(this._server, { + listening: this.emit.bind(this, 'listening'), + error: this.emit.bind(this, 'error'), + upgrade: (req, socket, head) => { + this.handleUpgrade(req, socket, head, (ws) => { + this.emit('connection', ws, req); + }); + } + }); + } + + if (options.perMessageDeflate === true) options.perMessageDeflate = {}; + if (options.clientTracking) this.clients = new Set(); + this.options = options; + } + + /** + * Returns the bound address, the address family name, and port of the server + * as reported by the operating system if listening on an IP socket. + * If the server is listening on a pipe or UNIX domain socket, the name is + * returned as a string. + * + * @return {(Object|String|null)} The address of the server + * @public + */ + address() { + if (this.options.noServer) { + throw new Error('The server is operating in "noServer" mode'); + } + + if (!this._server) return null; + return this._server.address(); + } + + /** + * Close the server. + * + * @param {Function} cb Callback + * @public + */ + close(cb) { + if (cb) this.once('close', cb); + + // + // Terminate all associated clients. + // + if (this.clients) { + for (const client of this.clients) client.terminate(); + } + + const server = this._server; + + if (server) { + this._removeListeners(); + this._removeListeners = this._server = null; + + // + // Close the http server if it was internally created. + // + if (this.options.port != null) { + server.close(() => this.emit('close')); + return; + } + } + + process.nextTick(emitClose, this); + } + + /** + * See if a given request should be handled by this server instance. + * + * @param {http.IncomingMessage} req Request object to inspect + * @return {Boolean} `true` if the request is valid, else `false` + * @public + */ + shouldHandle(req) { + if (this.options.path) { + const index = req.url.indexOf('?'); + const pathname = index !== -1 ? req.url.slice(0, index) : req.url; + + if (pathname !== this.options.path) return false; + } + + return true; + } + + /** + * Handle a HTTP Upgrade request. + * + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @public + */ + handleUpgrade(req, socket, head, cb) { + socket.on('error', socketOnError); + + const key = + req.headers['sec-websocket-key'] !== undefined + ? req.headers['sec-websocket-key'].trim() + : false; + const version = +req.headers['sec-websocket-version']; + const extensions = {}; + + if ( + req.method !== 'GET' || + req.headers.upgrade.toLowerCase() !== 'websocket' || + !key || + !keyRegex.test(key) || + (version !== 8 && version !== 13) || + !this.shouldHandle(req) + ) { + return abortHandshake(socket, 400); + } + + if (this.options.perMessageDeflate) { + const perMessageDeflate = new PerMessageDeflate( + this.options.perMessageDeflate, + true, + this.options.maxPayload + ); + + try { + const offers = parse(req.headers['sec-websocket-extensions']); + + if (offers[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); + extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + } catch (err) { + return abortHandshake(socket, 400); + } + } + + // + // Optionally call external client verification handler. + // + if (this.options.verifyClient) { + const info = { + origin: + req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`], + secure: !!(req.connection.authorized || req.connection.encrypted), + req + }; + + if (this.options.verifyClient.length === 2) { + this.options.verifyClient(info, (verified, code, message, headers) => { + if (!verified) { + return abortHandshake(socket, code || 401, message, headers); + } + + this.completeUpgrade(key, extensions, req, socket, head, cb); + }); + return; + } + + if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); + } + + this.completeUpgrade(key, extensions, req, socket, head, cb); + } + + /** + * Upgrade the connection to WebSocket. + * + * @param {String} key The value of the `Sec-WebSocket-Key` header + * @param {Object} extensions The accepted extensions + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @throws {Error} If called more than once with the same socket + * @private + */ + completeUpgrade(key, extensions, req, socket, head, cb) { + // + // Destroy the socket if the client has already sent a FIN packet. + // + if (!socket.readable || !socket.writable) return socket.destroy(); + + if (socket[kWebSocket]) { + throw new Error( + 'server.handleUpgrade() was called more than once with the same ' + + 'socket, possibly due to a misconfiguration' + ); + } + + const digest = createHash('sha1') + .update(key + GUID) + .digest('base64'); + + const headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + `Sec-WebSocket-Accept: ${digest}` + ]; + + const ws = new WebSocket(null); + let protocol = req.headers['sec-websocket-protocol']; + + if (protocol) { + protocol = protocol.trim().split(/ *, */); + + // + // Optionally call external protocol selection handler. + // + if (this.options.handleProtocols) { + protocol = this.options.handleProtocols(protocol, req); + } else { + protocol = protocol[0]; + } + + if (protocol) { + headers.push(`Sec-WebSocket-Protocol: ${protocol}`); + ws.protocol = protocol; + } + } + + if (extensions[PerMessageDeflate.extensionName]) { + const params = extensions[PerMessageDeflate.extensionName].params; + const value = format({ + [PerMessageDeflate.extensionName]: [params] + }); + headers.push(`Sec-WebSocket-Extensions: ${value}`); + ws._extensions = extensions; + } + + // + // Allow external modification/inspection of handshake headers. + // + this.emit('headers', headers, req); + + socket.write(headers.concat('\r\n').join('\r\n')); + socket.removeListener('error', socketOnError); + + ws.setSocket(socket, head, this.options.maxPayload); + + if (this.clients) { + this.clients.add(ws); + ws.on('close', () => this.clients.delete(ws)); + } + + cb(ws); + } +} + +module.exports = WebSocketServer; + +/** + * Add event listeners on an `EventEmitter` using a map of + * pairs. + * + * @param {EventEmitter} server The event emitter + * @param {Object.} map The listeners to add + * @return {Function} A function that will remove the added listeners when called + * @private + */ +function addListeners(server, map) { + for (const event of Object.keys(map)) server.on(event, map[event]); + + return function removeListeners() { + for (const event of Object.keys(map)) { + server.removeListener(event, map[event]); + } + }; +} + +/** + * Emit a `'close'` event on an `EventEmitter`. + * + * @param {EventEmitter} server The event emitter + * @private + */ +function emitClose(server) { + server.emit('close'); +} + +/** + * Handle premature socket errors. + * + * @private + */ +function socketOnError() { + this.destroy(); +} + +/** + * Close the connection when preconditions are not fulfilled. + * + * @param {net.Socket} socket The socket of the upgrade request + * @param {Number} code The HTTP response status code + * @param {String} [message] The HTTP response body + * @param {Object} [headers] Additional HTTP response headers + * @private + */ +function abortHandshake(socket, code, message, headers) { + if (socket.writable) { + message = message || STATUS_CODES[code]; + headers = { + Connection: 'close', + 'Content-Type': 'text/html', + 'Content-Length': Buffer.byteLength(message), + ...headers + }; + + socket.write( + `HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\n` + + Object.keys(headers) + .map((h) => `${h}: ${headers[h]}`) + .join('\r\n') + + '\r\n\r\n' + + message + ); + } + + socket.removeListener('error', socketOnError); + socket.destroy(); +} diff --git a/node_modules/ws/lib/websocket.js b/node_modules/ws/lib/websocket.js new file mode 100644 index 0000000..ce6f34c --- /dev/null +++ b/node_modules/ws/lib/websocket.js @@ -0,0 +1,905 @@ +'use strict'; + +const EventEmitter = require('events'); +const https = require('https'); +const http = require('http'); +const net = require('net'); +const tls = require('tls'); +const { randomBytes, createHash } = require('crypto'); +const { URL } = require('url'); + +const PerMessageDeflate = require('./permessage-deflate'); +const Receiver = require('./receiver'); +const Sender = require('./sender'); +const { + BINARY_TYPES, + EMPTY_BUFFER, + GUID, + kStatusCode, + kWebSocket, + NOOP +} = require('./constants'); +const { addEventListener, removeEventListener } = require('./event-target'); +const { format, parse } = require('./extension'); +const { toBuffer } = require('./buffer-util'); + +const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; +const protocolVersions = [8, 13]; +const closeTimeout = 30 * 1000; + +/** + * Class representing a WebSocket. + * + * @extends EventEmitter + */ +class WebSocket extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|url.URL)} address The URL to which to connect + * @param {(String|String[])} protocols The subprotocols + * @param {Object} options Connection options + */ + constructor(address, protocols, options) { + super(); + + this.readyState = WebSocket.CONNECTING; + this.protocol = ''; + + this._binaryType = BINARY_TYPES[0]; + this._closeFrameReceived = false; + this._closeFrameSent = false; + this._closeMessage = ''; + this._closeTimer = null; + this._closeCode = 1006; + this._extensions = {}; + this._receiver = null; + this._sender = null; + this._socket = null; + + if (address !== null) { + this._bufferedAmount = 0; + this._isServer = false; + this._redirects = 0; + + if (Array.isArray(protocols)) { + protocols = protocols.join(', '); + } else if (typeof protocols === 'object' && protocols !== null) { + options = protocols; + protocols = undefined; + } + + initAsClient(this, address, protocols, options); + } else { + this._isServer = true; + } + } + + get CONNECTING() { + return WebSocket.CONNECTING; + } + get CLOSING() { + return WebSocket.CLOSING; + } + get CLOSED() { + return WebSocket.CLOSED; + } + get OPEN() { + return WebSocket.OPEN; + } + + /** + * This deviates from the WHATWG interface since ws doesn't support the + * required default "blob" type (instead we define a custom "nodebuffer" + * type). + * + * @type {String} + */ + get binaryType() { + return this._binaryType; + } + + set binaryType(type) { + if (!BINARY_TYPES.includes(type)) return; + + this._binaryType = type; + + // + // Allow to change `binaryType` on the fly. + // + if (this._receiver) this._receiver._binaryType = type; + } + + /** + * @type {Number} + */ + get bufferedAmount() { + if (!this._socket) return this._bufferedAmount; + + return this._socket._writableState.length + this._sender._bufferedBytes; + } + + /** + * @type {String} + */ + get extensions() { + return Object.keys(this._extensions).join(); + } + + /** + * Set up the socket and the internal resources. + * + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Number} maxPayload The maximum allowed message size + * @private + */ + setSocket(socket, head, maxPayload) { + const receiver = new Receiver( + this._binaryType, + this._extensions, + this._isServer, + maxPayload + ); + + this._sender = new Sender(socket, this._extensions); + this._receiver = receiver; + this._socket = socket; + + receiver[kWebSocket] = this; + socket[kWebSocket] = this; + + receiver.on('conclude', receiverOnConclude); + receiver.on('drain', receiverOnDrain); + receiver.on('error', receiverOnError); + receiver.on('message', receiverOnMessage); + receiver.on('ping', receiverOnPing); + receiver.on('pong', receiverOnPong); + + socket.setTimeout(0); + socket.setNoDelay(); + + if (head.length > 0) socket.unshift(head); + + socket.on('close', socketOnClose); + socket.on('data', socketOnData); + socket.on('end', socketOnEnd); + socket.on('error', socketOnError); + + this.readyState = WebSocket.OPEN; + this.emit('open'); + } + + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose() { + if (!this._socket) { + this.readyState = WebSocket.CLOSED; + this.emit('close', this._closeCode, this._closeMessage); + return; + } + + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup(); + } + + this._receiver.removeAllListeners(); + this.readyState = WebSocket.CLOSED; + this.emit('close', this._closeCode, this._closeMessage); + } + + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} code Status code explaining why the connection is closing + * @param {String} data A string explaining why the connection is closing + * @public + */ + close(code, data) { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this.readyState === WebSocket.CLOSING) { + if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); + return; + } + + this.readyState = WebSocket.CLOSING; + this._sender.close(code, data, !this._isServer, (err) => { + // + // This error is handled by the `'error'` listener on the socket. We only + // want to know if the close frame has been sent here. + // + if (err) return; + + this._closeFrameSent = true; + if (this._closeFrameReceived) this._socket.end(); + }); + + // + // Specify a timeout for the closing handshake to complete. + // + this._closeTimer = setTimeout( + this._socket.destroy.bind(this._socket), + closeTimeout + ); + } + + /** + * Send a ping. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the ping is sent + * @public + */ + ping(data, mask, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + if (mask === undefined) mask = !this._isServer; + this._sender.ping(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a pong. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the pong is sent + * @public + */ + pong(data, mask, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + if (mask === undefined) mask = !this._isServer; + this._sender.pong(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress + * `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback which is executed when data is written out + * @public + */ + send(data, options, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + const opts = { + binary: typeof data !== 'string', + mask: !this._isServer, + compress: true, + fin: true, + ...options + }; + + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false; + } + + this._sender.send(data || EMPTY_BUFFER, opts, cb); + } + + /** + * Forcibly close the connection. + * + * @public + */ + terminate() { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this._socket) { + this.readyState = WebSocket.CLOSING; + this._socket.destroy(); + } + } +} + +readyStates.forEach((readyState, i) => { + WebSocket[readyState] = i; +}); + +// +// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. +// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface +// +['open', 'error', 'close', 'message'].forEach((method) => { + Object.defineProperty(WebSocket.prototype, `on${method}`, { + /** + * Return the listener of the event. + * + * @return {(Function|undefined)} The event listener or `undefined` + * @public + */ + get() { + const listeners = this.listeners(method); + for (let i = 0; i < listeners.length; i++) { + if (listeners[i]._listener) return listeners[i]._listener; + } + + return undefined; + }, + /** + * Add a listener for the event. + * + * @param {Function} listener The listener to add + * @public + */ + set(listener) { + const listeners = this.listeners(method); + for (let i = 0; i < listeners.length; i++) { + // + // Remove only the listeners added via `addEventListener`. + // + if (listeners[i]._listener) this.removeListener(method, listeners[i]); + } + this.addEventListener(method, listener); + } + }); +}); + +WebSocket.prototype.addEventListener = addEventListener; +WebSocket.prototype.removeEventListener = removeEventListener; + +module.exports = WebSocket; + +/** + * Initialize a WebSocket client. + * + * @param {WebSocket} websocket The client to initialize + * @param {(String|url.URL)} address The URL to which to connect + * @param {String} protocols The subprotocols + * @param {Object} options Connection options + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable + * permessage-deflate + * @param {Number} options.handshakeTimeout Timeout in milliseconds for the + * handshake request + * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` + * header + * @param {String} options.origin Value of the `Origin` or + * `Sec-WebSocket-Origin` header + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Boolean} options.followRedirects Whether or not to follow redirects + * @param {Number} options.maxRedirects The maximum number of redirects allowed + * @private + */ +function initAsClient(websocket, address, protocols, options) { + const opts = { + protocolVersion: protocolVersions[1], + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: true, + followRedirects: false, + maxRedirects: 10, + ...options, + createConnection: undefined, + socketPath: undefined, + hostname: undefined, + protocol: undefined, + timeout: undefined, + method: undefined, + host: undefined, + path: undefined, + port: undefined + }; + + if (!protocolVersions.includes(opts.protocolVersion)) { + throw new RangeError( + `Unsupported protocol version: ${opts.protocolVersion} ` + + `(supported versions: ${protocolVersions.join(', ')})` + ); + } + + let parsedUrl; + + if (address instanceof URL) { + parsedUrl = address; + websocket.url = address.href; + } else { + parsedUrl = new URL(address); + websocket.url = address; + } + + const isUnixSocket = parsedUrl.protocol === 'ws+unix:'; + + if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) { + throw new Error(`Invalid URL: ${websocket.url}`); + } + + const isSecure = + parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:'; + const defaultPort = isSecure ? 443 : 80; + const key = randomBytes(16).toString('base64'); + const get = isSecure ? https.get : http.get; + let perMessageDeflate; + + opts.createConnection = isSecure ? tlsConnect : netConnect; + opts.defaultPort = opts.defaultPort || defaultPort; + opts.port = parsedUrl.port || defaultPort; + opts.host = parsedUrl.hostname.startsWith('[') + ? parsedUrl.hostname.slice(1, -1) + : parsedUrl.hostname; + opts.headers = { + 'Sec-WebSocket-Version': opts.protocolVersion, + 'Sec-WebSocket-Key': key, + Connection: 'Upgrade', + Upgrade: 'websocket', + ...opts.headers + }; + opts.path = parsedUrl.pathname + parsedUrl.search; + opts.timeout = opts.handshakeTimeout; + + if (opts.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, + false, + opts.maxPayload + ); + opts.headers['Sec-WebSocket-Extensions'] = format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer() + }); + } + if (protocols) { + opts.headers['Sec-WebSocket-Protocol'] = protocols; + } + if (opts.origin) { + if (opts.protocolVersion < 13) { + opts.headers['Sec-WebSocket-Origin'] = opts.origin; + } else { + opts.headers.Origin = opts.origin; + } + } + if (parsedUrl.username || parsedUrl.password) { + opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; + } + + if (isUnixSocket) { + const parts = opts.path.split(':'); + + opts.socketPath = parts[0]; + opts.path = parts[1]; + } + + let req = (websocket._req = get(opts)); + + if (opts.timeout) { + req.on('timeout', () => { + abortHandshake(websocket, req, 'Opening handshake has timed out'); + }); + } + + req.on('error', (err) => { + if (websocket._req.aborted) return; + + req = websocket._req = null; + websocket.readyState = WebSocket.CLOSING; + websocket.emit('error', err); + websocket.emitClose(); + }); + + req.on('response', (res) => { + const location = res.headers.location; + const statusCode = res.statusCode; + + if ( + location && + opts.followRedirects && + statusCode >= 300 && + statusCode < 400 + ) { + if (++websocket._redirects > opts.maxRedirects) { + abortHandshake(websocket, req, 'Maximum redirects exceeded'); + return; + } + + req.abort(); + + const addr = new URL(location, address); + + initAsClient(websocket, addr, protocols, options); + } else if (!websocket.emit('unexpected-response', req, res)) { + abortHandshake( + websocket, + req, + `Unexpected server response: ${res.statusCode}` + ); + } + }); + + req.on('upgrade', (res, socket, head) => { + websocket.emit('upgrade', res); + + // + // The user may have closed the connection from a listener of the `upgrade` + // event. + // + if (websocket.readyState !== WebSocket.CONNECTING) return; + + req = websocket._req = null; + + const digest = createHash('sha1') + .update(key + GUID) + .digest('base64'); + + if (res.headers['sec-websocket-accept'] !== digest) { + abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header'); + return; + } + + const serverProt = res.headers['sec-websocket-protocol']; + const protList = (protocols || '').split(/, */); + let protError; + + if (!protocols && serverProt) { + protError = 'Server sent a subprotocol but none was requested'; + } else if (protocols && !serverProt) { + protError = 'Server sent no subprotocol'; + } else if (serverProt && !protList.includes(serverProt)) { + protError = 'Server sent an invalid subprotocol'; + } + + if (protError) { + abortHandshake(websocket, socket, protError); + return; + } + + if (serverProt) websocket.protocol = serverProt; + + if (perMessageDeflate) { + try { + const extensions = parse(res.headers['sec-websocket-extensions']); + + if (extensions[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); + websocket._extensions[ + PerMessageDeflate.extensionName + ] = perMessageDeflate; + } + } catch (err) { + abortHandshake( + websocket, + socket, + 'Invalid Sec-WebSocket-Extensions header' + ); + return; + } + } + + websocket.setSocket(socket, head, opts.maxPayload); + }); +} + +/** + * Create a `net.Socket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {net.Socket} The newly created socket used to start the connection + * @private + */ +function netConnect(options) { + options.path = options.socketPath; + return net.connect(options); +} + +/** + * Create a `tls.TLSSocket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {tls.TLSSocket} The newly created socket used to start the connection + * @private + */ +function tlsConnect(options) { + options.path = undefined; + + if (!options.servername && options.servername !== '') { + options.servername = options.host; + } + + return tls.connect(options); +} + +/** + * Abort the handshake and emit an error. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {(http.ClientRequest|net.Socket)} stream The request to abort or the + * socket to destroy + * @param {String} message The error message + * @private + */ +function abortHandshake(websocket, stream, message) { + websocket.readyState = WebSocket.CLOSING; + + const err = new Error(message); + Error.captureStackTrace(err, abortHandshake); + + if (stream.setHeader) { + stream.abort(); + stream.once('abort', websocket.emitClose.bind(websocket)); + websocket.emit('error', err); + } else { + stream.destroy(err); + stream.once('error', websocket.emit.bind(websocket, 'error')); + stream.once('close', websocket.emitClose.bind(websocket)); + } +} + +/** + * Handle cases where the `ping()`, `pong()`, or `send()` methods are called + * when the `readyState` attribute is `CLOSING` or `CLOSED`. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {*} data The data to send + * @param {Function} cb Callback + * @private + */ +function sendAfterClose(websocket, data, cb) { + if (data) { + const length = toBuffer(data).length; + + // + // The `_bufferedAmount` property is used only when the peer is a client and + // the opening handshake fails. Under these circumstances, in fact, the + // `setSocket()` method is not called, so the `_socket` and `_sender` + // properties are set to `null`. + // + if (websocket._socket) websocket._sender._bufferedBytes += length; + else websocket._bufferedAmount += length; + } + + if (cb) { + const err = new Error( + `WebSocket is not open: readyState ${websocket.readyState} ` + + `(${readyStates[websocket.readyState]})` + ); + cb(err); + } +} + +/** + * The listener of the `Receiver` `'conclude'` event. + * + * @param {Number} code The status code + * @param {String} reason The reason for closing + * @private + */ +function receiverOnConclude(code, reason) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + websocket._socket.resume(); + + websocket._closeFrameReceived = true; + websocket._closeMessage = reason; + websocket._closeCode = code; + + if (code === 1005) websocket.close(); + else websocket.close(code, reason); +} + +/** + * The listener of the `Receiver` `'drain'` event. + * + * @private + */ +function receiverOnDrain() { + this[kWebSocket]._socket.resume(); +} + +/** + * The listener of the `Receiver` `'error'` event. + * + * @param {(RangeError|Error)} err The emitted error + * @private + */ +function receiverOnError(err) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + + websocket.readyState = WebSocket.CLOSING; + websocket._closeCode = err[kStatusCode]; + websocket.emit('error', err); + websocket._socket.destroy(); +} + +/** + * The listener of the `Receiver` `'finish'` event. + * + * @private + */ +function receiverOnFinish() { + this[kWebSocket].emitClose(); +} + +/** + * The listener of the `Receiver` `'message'` event. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message + * @private + */ +function receiverOnMessage(data) { + this[kWebSocket].emit('message', data); +} + +/** + * The listener of the `Receiver` `'ping'` event. + * + * @param {Buffer} data The data included in the ping frame + * @private + */ +function receiverOnPing(data) { + const websocket = this[kWebSocket]; + + websocket.pong(data, !websocket._isServer, NOOP); + websocket.emit('ping', data); +} + +/** + * The listener of the `Receiver` `'pong'` event. + * + * @param {Buffer} data The data included in the pong frame + * @private + */ +function receiverOnPong(data) { + this[kWebSocket].emit('pong', data); +} + +/** + * The listener of the `net.Socket` `'close'` event. + * + * @private + */ +function socketOnClose() { + const websocket = this[kWebSocket]; + + this.removeListener('close', socketOnClose); + this.removeListener('end', socketOnEnd); + + websocket.readyState = WebSocket.CLOSING; + + // + // The close frame might not have been received or the `'end'` event emitted, + // for example, if the socket was destroyed due to an error. Ensure that the + // `receiver` stream is closed after writing any remaining buffered data to + // it. If the readable side of the socket is in flowing mode then there is no + // buffered data as everything has been already written and `readable.read()` + // will return `null`. If instead, the socket is paused, any possible buffered + // data will be read as a single chunk and emitted synchronously in a single + // `'data'` event. + // + websocket._socket.read(); + websocket._receiver.end(); + + this.removeListener('data', socketOnData); + this[kWebSocket] = undefined; + + clearTimeout(websocket._closeTimer); + + if ( + websocket._receiver._writableState.finished || + websocket._receiver._writableState.errorEmitted + ) { + websocket.emitClose(); + } else { + websocket._receiver.on('error', receiverOnFinish); + websocket._receiver.on('finish', receiverOnFinish); + } +} + +/** + * The listener of the `net.Socket` `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function socketOnData(chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause(); + } +} + +/** + * The listener of the `net.Socket` `'end'` event. + * + * @private + */ +function socketOnEnd() { + const websocket = this[kWebSocket]; + + websocket.readyState = WebSocket.CLOSING; + websocket._receiver.end(); + this.end(); +} + +/** + * The listener of the `net.Socket` `'error'` event. + * + * @private + */ +function socketOnError() { + const websocket = this[kWebSocket]; + + this.removeListener('error', socketOnError); + this.on('error', NOOP); + + if (websocket) { + websocket.readyState = WebSocket.CLOSING; + this.destroy(); + } +} diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json new file mode 100644 index 0000000..77f836e --- /dev/null +++ b/node_modules/ws/package.json @@ -0,0 +1,91 @@ +{ + "_from": "ws@^7.3.1", + "_id": "ws@7.3.1", + "_inBundle": false, + "_integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "_location": "/ws", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "ws@^7.3.1", + "name": "ws", + "escapedName": "ws", + "rawSpec": "^7.3.1", + "saveSpec": null, + "fetchSpec": "^7.3.1" + }, + "_requiredBy": [ + "/discord.js" + ], + "_resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "_shasum": "d0547bf67f7ce4f12a72dfe31262c68d7dc551c8", + "_spec": "ws@^7.3.1", + "_where": "C:\\Users\\hlack\\Desktop\\discord bot\\node_modules\\discord.js", + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/websockets/ws/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", + "devDependencies": { + "benchmark": "^2.1.4", + "bufferutil": "^4.0.1", + "coveralls": "^3.0.3", + "eslint": "^7.2.0", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-prettier": "^3.0.1", + "mocha": "^7.0.0", + "nyc": "^15.0.0", + "prettier": "^2.0.5", + "utf-8-validate": "^5.0.2" + }, + "engines": { + "node": ">=8.3.0" + }, + "files": [ + "browser.js", + "index.js", + "lib/*.js" + ], + "homepage": "https://github.com/websockets/ws", + "keywords": [ + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "license": "MIT", + "main": "index.js", + "name": "ws", + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + }, + "repository": { + "type": "git", + "url": "git+https://github.com/websockets/ws.git" + }, + "scripts": { + "integration": "mocha --throw-deprecation test/*.integration.js", + "lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\"", + "test": "nyc --reporter=html --reporter=text mocha --throw-deprecation test/*.test.js" + }, + "version": "7.3.1" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6ca6533 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,107 @@ +{ + "name": "discord-bot", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@discordjs/collection": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", + "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" + }, + "@discordjs/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "discord.js": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.4.1.tgz", + "integrity": "sha512-KxOB8LOAN3GmrvkD6a6Fr1nlfArIFZ+q7Uqg4T/5duB90GZy9a0/Py2E+Y+eHKP6ZUCR2mbNMLCcHGjahiaNqA==", + "requires": { + "@discordjs/collection": "^0.1.6", + "@discordjs/form-data": "^3.0.1", + "abort-controller": "^3.0.0", + "node-fetch": "^2.6.1", + "prism-media": "^1.2.2", + "setimmediate": "^1.0.5", + "tweetnacl": "^1.0.3", + "ws": "^7.3.1" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "prism-media": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz", + "integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw==" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e44b9c7 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "discord-bot", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "discord.js": "^12.4.1" + } +}