discord-sb-v13 0.0.1-security → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of discord-sb-v13 might be problematic. Click here for more details.

Files changed (343) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +119 -5
  3. package/package.json +101 -6
  4. package/src/WebSocket.js +39 -0
  5. package/src/client/BaseClient.js +87 -0
  6. package/src/client/Client.js +1154 -0
  7. package/src/client/WebhookClient.js +61 -0
  8. package/src/client/actions/Action.js +115 -0
  9. package/src/client/actions/ActionsManager.js +72 -0
  10. package/src/client/actions/ApplicationCommandPermissionsUpdate.js +34 -0
  11. package/src/client/actions/AutoModerationActionExecution.js +26 -0
  12. package/src/client/actions/AutoModerationRuleCreate.js +27 -0
  13. package/src/client/actions/AutoModerationRuleDelete.js +31 -0
  14. package/src/client/actions/AutoModerationRuleUpdate.js +29 -0
  15. package/src/client/actions/ChannelCreate.js +23 -0
  16. package/src/client/actions/ChannelDelete.js +39 -0
  17. package/src/client/actions/ChannelUpdate.js +34 -0
  18. package/src/client/actions/GuildAuditLogEntryCreate.js +29 -0
  19. package/src/client/actions/GuildBanAdd.js +20 -0
  20. package/src/client/actions/GuildBanRemove.js +25 -0
  21. package/src/client/actions/GuildChannelsPositionUpdate.js +21 -0
  22. package/src/client/actions/GuildDelete.js +65 -0
  23. package/src/client/actions/GuildEmojiCreate.js +20 -0
  24. package/src/client/actions/GuildEmojiDelete.js +21 -0
  25. package/src/client/actions/GuildEmojiUpdate.js +20 -0
  26. package/src/client/actions/GuildEmojisUpdate.js +34 -0
  27. package/src/client/actions/GuildIntegrationsUpdate.js +19 -0
  28. package/src/client/actions/GuildMemberRemove.js +33 -0
  29. package/src/client/actions/GuildMemberUpdate.js +44 -0
  30. package/src/client/actions/GuildRoleCreate.js +25 -0
  31. package/src/client/actions/GuildRoleDelete.js +31 -0
  32. package/src/client/actions/GuildRoleUpdate.js +39 -0
  33. package/src/client/actions/GuildRolesPositionUpdate.js +21 -0
  34. package/src/client/actions/GuildScheduledEventCreate.js +27 -0
  35. package/src/client/actions/GuildScheduledEventDelete.js +31 -0
  36. package/src/client/actions/GuildScheduledEventUpdate.js +30 -0
  37. package/src/client/actions/GuildScheduledEventUserAdd.js +32 -0
  38. package/src/client/actions/GuildScheduledEventUserRemove.js +32 -0
  39. package/src/client/actions/GuildStickerCreate.js +20 -0
  40. package/src/client/actions/GuildStickerDelete.js +21 -0
  41. package/src/client/actions/GuildStickerUpdate.js +20 -0
  42. package/src/client/actions/GuildStickersUpdate.js +34 -0
  43. package/src/client/actions/GuildUpdate.js +33 -0
  44. package/src/client/actions/InteractionCreate.js +115 -0
  45. package/src/client/actions/InviteCreate.js +28 -0
  46. package/src/client/actions/InviteDelete.js +30 -0
  47. package/src/client/actions/MessageCreate.js +50 -0
  48. package/src/client/actions/MessageDelete.js +32 -0
  49. package/src/client/actions/MessageDeleteBulk.js +46 -0
  50. package/src/client/actions/MessageReactionAdd.js +56 -0
  51. package/src/client/actions/MessageReactionRemove.js +45 -0
  52. package/src/client/actions/MessageReactionRemoveAll.js +33 -0
  53. package/src/client/actions/MessageReactionRemoveEmoji.js +28 -0
  54. package/src/client/actions/MessageUpdate.js +26 -0
  55. package/src/client/actions/PresenceUpdate.js +45 -0
  56. package/src/client/actions/StageInstanceCreate.js +28 -0
  57. package/src/client/actions/StageInstanceDelete.js +33 -0
  58. package/src/client/actions/StageInstanceUpdate.js +30 -0
  59. package/src/client/actions/ThreadCreate.js +24 -0
  60. package/src/client/actions/ThreadDelete.js +32 -0
  61. package/src/client/actions/ThreadListSync.js +59 -0
  62. package/src/client/actions/ThreadMemberUpdate.js +30 -0
  63. package/src/client/actions/ThreadMembersUpdate.js +34 -0
  64. package/src/client/actions/TypingStart.js +29 -0
  65. package/src/client/actions/UserUpdate.js +35 -0
  66. package/src/client/actions/VoiceStateUpdate.js +57 -0
  67. package/src/client/actions/WebhooksUpdate.js +20 -0
  68. package/src/client/voice/ClientVoiceManager.js +51 -0
  69. package/src/client/websocket/WebSocketManager.js +412 -0
  70. package/src/client/websocket/WebSocketShard.js +908 -0
  71. package/src/client/websocket/handlers/APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE.js +23 -0
  72. package/src/client/websocket/handlers/APPLICATION_COMMAND_CREATE.js +18 -0
  73. package/src/client/websocket/handlers/APPLICATION_COMMAND_DELETE.js +20 -0
  74. package/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js +5 -0
  75. package/src/client/websocket/handlers/APPLICATION_COMMAND_UPDATE.js +20 -0
  76. package/src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js +5 -0
  77. package/src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js +5 -0
  78. package/src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js +5 -0
  79. package/src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js +5 -0
  80. package/src/client/websocket/handlers/CALL_CREATE.js +14 -0
  81. package/src/client/websocket/handlers/CALL_DELETE.js +11 -0
  82. package/src/client/websocket/handlers/CALL_UPDATE.js +11 -0
  83. package/src/client/websocket/handlers/CHANNEL_CREATE.js +5 -0
  84. package/src/client/websocket/handlers/CHANNEL_DELETE.js +5 -0
  85. package/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js +22 -0
  86. package/src/client/websocket/handlers/CHANNEL_RECIPIENT_ADD.js +16 -0
  87. package/src/client/websocket/handlers/CHANNEL_RECIPIENT_REMOVE.js +16 -0
  88. package/src/client/websocket/handlers/CHANNEL_UPDATE.js +16 -0
  89. package/src/client/websocket/handlers/GUILD_APPLICATION_COMMANDS_UPDATE.js +11 -0
  90. package/src/client/websocket/handlers/GUILD_AUDIT_LOG_ENTRY_CREATE.js +5 -0
  91. package/src/client/websocket/handlers/GUILD_BAN_ADD.js +5 -0
  92. package/src/client/websocket/handlers/GUILD_BAN_REMOVE.js +5 -0
  93. package/src/client/websocket/handlers/GUILD_CREATE.js +46 -0
  94. package/src/client/websocket/handlers/GUILD_DELETE.js +5 -0
  95. package/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js +5 -0
  96. package/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js +5 -0
  97. package/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js +39 -0
  98. package/src/client/websocket/handlers/GUILD_MEMBER_ADD.js +20 -0
  99. package/src/client/websocket/handlers/GUILD_MEMBER_LIST_UPDATE.js +55 -0
  100. package/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js +5 -0
  101. package/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js +5 -0
  102. package/src/client/websocket/handlers/GUILD_ROLE_CREATE.js +5 -0
  103. package/src/client/websocket/handlers/GUILD_ROLE_DELETE.js +5 -0
  104. package/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js +5 -0
  105. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_CREATE.js +5 -0
  106. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_DELETE.js +5 -0
  107. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_UPDATE.js +5 -0
  108. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_USER_ADD.js +5 -0
  109. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_USER_REMOVE.js +5 -0
  110. package/src/client/websocket/handlers/GUILD_STICKERS_UPDATE.js +5 -0
  111. package/src/client/websocket/handlers/GUILD_UPDATE.js +5 -0
  112. package/src/client/websocket/handlers/INTERACTION_CREATE.js +16 -0
  113. package/src/client/websocket/handlers/INTERACTION_FAILURE.js +18 -0
  114. package/src/client/websocket/handlers/INTERACTION_MODAL_CREATE.js +11 -0
  115. package/src/client/websocket/handlers/INTERACTION_SUCCESS.js +30 -0
  116. package/src/client/websocket/handlers/INVITE_CREATE.js +5 -0
  117. package/src/client/websocket/handlers/INVITE_DELETE.js +5 -0
  118. package/src/client/websocket/handlers/MESSAGE_ACK.js +16 -0
  119. package/src/client/websocket/handlers/MESSAGE_CREATE.js +5 -0
  120. package/src/client/websocket/handlers/MESSAGE_DELETE.js +5 -0
  121. package/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js +5 -0
  122. package/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js +5 -0
  123. package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js +5 -0
  124. package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js +5 -0
  125. package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js +5 -0
  126. package/src/client/websocket/handlers/MESSAGE_UPDATE.js +16 -0
  127. package/src/client/websocket/handlers/PRESENCE_UPDATE.js +5 -0
  128. package/src/client/websocket/handlers/READY.js +172 -0
  129. package/src/client/websocket/handlers/RELATIONSHIP_ADD.js +17 -0
  130. package/src/client/websocket/handlers/RELATIONSHIP_REMOVE.js +15 -0
  131. package/src/client/websocket/handlers/RELATIONSHIP_UPDATE.js +18 -0
  132. package/src/client/websocket/handlers/RESUMED.js +14 -0
  133. package/src/client/websocket/handlers/STAGE_INSTANCE_CREATE.js +5 -0
  134. package/src/client/websocket/handlers/STAGE_INSTANCE_DELETE.js +5 -0
  135. package/src/client/websocket/handlers/STAGE_INSTANCE_UPDATE.js +5 -0
  136. package/src/client/websocket/handlers/THREAD_CREATE.js +5 -0
  137. package/src/client/websocket/handlers/THREAD_DELETE.js +5 -0
  138. package/src/client/websocket/handlers/THREAD_LIST_SYNC.js +5 -0
  139. package/src/client/websocket/handlers/THREAD_MEMBERS_UPDATE.js +5 -0
  140. package/src/client/websocket/handlers/THREAD_MEMBER_UPDATE.js +5 -0
  141. package/src/client/websocket/handlers/THREAD_UPDATE.js +16 -0
  142. package/src/client/websocket/handlers/TYPING_START.js +5 -0
  143. package/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js +12 -0
  144. package/src/client/websocket/handlers/USER_NOTE_UPDATE.js +5 -0
  145. package/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js +9 -0
  146. package/src/client/websocket/handlers/USER_UPDATE.js +5 -0
  147. package/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js +6 -0
  148. package/src/client/websocket/handlers/VOICE_STATE_UPDATE.js +5 -0
  149. package/src/client/websocket/handlers/WEBHOOKS_UPDATE.js +5 -0
  150. package/src/client/websocket/handlers/index.js +86 -0
  151. package/src/errors/DJSError.js +61 -0
  152. package/src/errors/Messages.js +227 -0
  153. package/src/errors/index.js +4 -0
  154. package/src/index.js +190 -0
  155. package/src/main.js +42 -0
  156. package/src/managers/ApplicationCommandManager.js +267 -0
  157. package/src/managers/ApplicationCommandPermissionsManager.js +425 -0
  158. package/src/managers/AutoModerationRuleManager.js +296 -0
  159. package/src/managers/BaseGuildEmojiManager.js +80 -0
  160. package/src/managers/BaseManager.js +19 -0
  161. package/src/managers/BillingManager.js +66 -0
  162. package/src/managers/CachedManager.js +71 -0
  163. package/src/managers/ChannelManager.js +139 -0
  164. package/src/managers/ClientUserSettingManager.js +490 -0
  165. package/src/managers/DataManager.js +61 -0
  166. package/src/managers/DeveloperPortalManager.js +104 -0
  167. package/src/managers/GuildApplicationCommandManager.js +28 -0
  168. package/src/managers/GuildBanManager.js +204 -0
  169. package/src/managers/GuildChannelManager.js +502 -0
  170. package/src/managers/GuildEmojiManager.js +171 -0
  171. package/src/managers/GuildEmojiRoleManager.js +118 -0
  172. package/src/managers/GuildFolderManager.js +24 -0
  173. package/src/managers/GuildForumThreadManager.js +114 -0
  174. package/src/managers/GuildInviteManager.js +213 -0
  175. package/src/managers/GuildManager.js +304 -0
  176. package/src/managers/GuildMemberManager.js +724 -0
  177. package/src/managers/GuildMemberRoleManager.js +191 -0
  178. package/src/managers/GuildScheduledEventManager.js +296 -0
  179. package/src/managers/GuildSettingManager.js +148 -0
  180. package/src/managers/GuildStickerManager.js +179 -0
  181. package/src/managers/GuildTextThreadManager.js +98 -0
  182. package/src/managers/InteractionManager.js +39 -0
  183. package/src/managers/MessageManager.js +393 -0
  184. package/src/managers/PermissionOverwriteManager.js +166 -0
  185. package/src/managers/PresenceManager.js +58 -0
  186. package/src/managers/ReactionManager.js +67 -0
  187. package/src/managers/ReactionUserManager.js +71 -0
  188. package/src/managers/RelationshipManager.js +258 -0
  189. package/src/managers/RoleManager.js +352 -0
  190. package/src/managers/SessionManager.js +57 -0
  191. package/src/managers/StageInstanceManager.js +162 -0
  192. package/src/managers/ThreadManager.js +207 -0
  193. package/src/managers/ThreadMemberManager.js +186 -0
  194. package/src/managers/UserManager.js +150 -0
  195. package/src/managers/VoiceStateManager.js +37 -0
  196. package/src/rest/APIRequest.js +136 -0
  197. package/src/rest/APIRouter.js +53 -0
  198. package/src/rest/CaptchaSolver.js +78 -0
  199. package/src/rest/DiscordAPIError.js +103 -0
  200. package/src/rest/HTTPError.js +62 -0
  201. package/src/rest/RESTManager.js +81 -0
  202. package/src/rest/RateLimitError.js +55 -0
  203. package/src/rest/RequestHandler.js +446 -0
  204. package/src/sharding/Shard.js +443 -0
  205. package/src/sharding/ShardClientUtil.js +275 -0
  206. package/src/sharding/ShardingManager.js +318 -0
  207. package/src/structures/AnonymousGuild.js +98 -0
  208. package/src/structures/ApplicationCommand.js +1028 -0
  209. package/src/structures/ApplicationRoleConnectionMetadata.js +45 -0
  210. package/src/structures/AutoModerationActionExecution.js +89 -0
  211. package/src/structures/AutoModerationRule.js +294 -0
  212. package/src/structures/AutocompleteInteraction.js +106 -0
  213. package/src/structures/Base.js +43 -0
  214. package/src/structures/BaseCommandInteraction.js +211 -0
  215. package/src/structures/BaseGuild.js +116 -0
  216. package/src/structures/BaseGuildEmoji.js +56 -0
  217. package/src/structures/BaseGuildTextChannel.js +193 -0
  218. package/src/structures/BaseGuildVoiceChannel.js +243 -0
  219. package/src/structures/BaseMessageComponent.js +114 -0
  220. package/src/structures/ButtonInteraction.js +11 -0
  221. package/src/structures/Call.js +58 -0
  222. package/src/structures/CategoryChannel.js +83 -0
  223. package/src/structures/Channel.js +271 -0
  224. package/src/structures/ClientApplication.js +204 -0
  225. package/src/structures/ClientPresence.js +84 -0
  226. package/src/structures/ClientUser.js +624 -0
  227. package/src/structures/CommandInteraction.js +41 -0
  228. package/src/structures/CommandInteractionOptionResolver.js +276 -0
  229. package/src/structures/ContextMenuInteraction.js +65 -0
  230. package/src/structures/DMChannel.js +280 -0
  231. package/src/structures/DeveloperPortalApplication.js +520 -0
  232. package/src/structures/DirectoryChannel.js +20 -0
  233. package/src/structures/Emoji.js +148 -0
  234. package/src/structures/ForumChannel.js +271 -0
  235. package/src/structures/Guild.js +1744 -0
  236. package/src/structures/GuildAuditLogs.js +734 -0
  237. package/src/structures/GuildBan.js +59 -0
  238. package/src/structures/GuildBoost.js +108 -0
  239. package/src/structures/GuildChannel.js +454 -0
  240. package/src/structures/GuildEmoji.js +161 -0
  241. package/src/structures/GuildFolder.js +75 -0
  242. package/src/structures/GuildMember.js +686 -0
  243. package/src/structures/GuildPreview.js +191 -0
  244. package/src/structures/GuildPreviewEmoji.js +27 -0
  245. package/src/structures/GuildScheduledEvent.js +441 -0
  246. package/src/structures/GuildTemplate.js +236 -0
  247. package/src/structures/Integration.js +188 -0
  248. package/src/structures/IntegrationApplication.js +96 -0
  249. package/src/structures/Interaction.js +351 -0
  250. package/src/structures/InteractionCollector.js +248 -0
  251. package/src/structures/InteractionResponse.js +114 -0
  252. package/src/structures/InteractionWebhook.js +43 -0
  253. package/src/structures/Invite.js +375 -0
  254. package/src/structures/InviteGuild.js +23 -0
  255. package/src/structures/InviteStageInstance.js +86 -0
  256. package/src/structures/Message.js +1188 -0
  257. package/src/structures/MessageActionRow.js +103 -0
  258. package/src/structures/MessageAttachment.js +193 -0
  259. package/src/structures/MessageButton.js +231 -0
  260. package/src/structures/MessageCollector.js +146 -0
  261. package/src/structures/MessageComponentInteraction.js +120 -0
  262. package/src/structures/MessageContextMenuInteraction.js +20 -0
  263. package/src/structures/MessageEmbed.js +586 -0
  264. package/src/structures/MessageMentions.js +272 -0
  265. package/src/structures/MessagePayload.js +358 -0
  266. package/src/structures/MessageReaction.js +171 -0
  267. package/src/structures/MessageSelectMenu.js +391 -0
  268. package/src/structures/Modal.js +279 -0
  269. package/src/structures/ModalSubmitFieldsResolver.js +53 -0
  270. package/src/structures/ModalSubmitInteraction.js +119 -0
  271. package/src/structures/NewsChannel.js +32 -0
  272. package/src/structures/OAuth2Guild.js +28 -0
  273. package/src/structures/PartialGroupDMChannel.js +430 -0
  274. package/src/structures/PermissionOverwrites.js +196 -0
  275. package/src/structures/Presence.js +441 -0
  276. package/src/structures/ReactionCollector.js +229 -0
  277. package/src/structures/ReactionEmoji.js +31 -0
  278. package/src/structures/RichPresence.js +722 -0
  279. package/src/structures/Role.js +515 -0
  280. package/src/structures/SelectMenuInteraction.js +170 -0
  281. package/src/structures/Session.js +81 -0
  282. package/src/structures/StageChannel.js +104 -0
  283. package/src/structures/StageInstance.js +208 -0
  284. package/src/structures/Sticker.js +310 -0
  285. package/src/structures/StickerPack.js +95 -0
  286. package/src/structures/StoreChannel.js +56 -0
  287. package/src/structures/Team.js +167 -0
  288. package/src/structures/TeamMember.js +71 -0
  289. package/src/structures/TextChannel.js +33 -0
  290. package/src/structures/TextInputComponent.js +201 -0
  291. package/src/structures/ThreadChannel.js +626 -0
  292. package/src/structures/ThreadMember.js +105 -0
  293. package/src/structures/Typing.js +74 -0
  294. package/src/structures/User.js +697 -0
  295. package/src/structures/UserContextMenuInteraction.js +29 -0
  296. package/src/structures/VoiceChannel.js +110 -0
  297. package/src/structures/VoiceRegion.js +53 -0
  298. package/src/structures/VoiceState.js +306 -0
  299. package/src/structures/WebEmbed.js +401 -0
  300. package/src/structures/Webhook.js +461 -0
  301. package/src/structures/WelcomeChannel.js +60 -0
  302. package/src/structures/WelcomeScreen.js +48 -0
  303. package/src/structures/Widget.js +87 -0
  304. package/src/structures/WidgetMember.js +99 -0
  305. package/src/structures/interfaces/Application.js +190 -0
  306. package/src/structures/interfaces/Collector.js +300 -0
  307. package/src/structures/interfaces/InteractionResponses.js +313 -0
  308. package/src/structures/interfaces/TextBasedChannel.js +566 -0
  309. package/src/util/ActivityFlags.js +44 -0
  310. package/src/util/ApplicationFlags.js +74 -0
  311. package/src/util/BitField.js +170 -0
  312. package/src/util/ChannelFlags.js +45 -0
  313. package/src/util/Constants.js +1917 -0
  314. package/src/util/DataResolver.js +145 -0
  315. package/src/util/Formatters.js +214 -0
  316. package/src/util/GuildMemberFlags.js +43 -0
  317. package/src/util/Intents.js +74 -0
  318. package/src/util/LimitedCollection.js +131 -0
  319. package/src/util/MessageFlags.js +54 -0
  320. package/src/util/Options.js +360 -0
  321. package/src/util/Permissions.js +187 -0
  322. package/src/util/PremiumUsageFlags.js +31 -0
  323. package/src/util/PurchasedFlags.js +31 -0
  324. package/src/util/RemoteAuth.js +522 -0
  325. package/src/util/SnowflakeUtil.js +92 -0
  326. package/src/util/Sweepers.js +466 -0
  327. package/src/util/SystemChannelFlags.js +55 -0
  328. package/src/util/ThreadMemberFlags.js +30 -0
  329. package/src/util/UserFlags.js +104 -0
  330. package/src/util/Util.js +741 -0
  331. package/src/util/Voice.js +1456 -0
  332. package/src/util/arRPC/index.js +229 -0
  333. package/src/util/arRPC/process/detectable.json +1 -0
  334. package/src/util/arRPC/process/index.js +102 -0
  335. package/src/util/arRPC/process/native/index.js +5 -0
  336. package/src/util/arRPC/process/native/linux.js +37 -0
  337. package/src/util/arRPC/process/native/win32.js +25 -0
  338. package/src/util/arRPC/transports/ipc.js +281 -0
  339. package/src/util/arRPC/transports/websocket.js +128 -0
  340. package/typings/enums.d.ts +346 -0
  341. package/typings/index.d.ts +7725 -0
  342. package/typings/index.test-d.ts +0 -0
  343. package/typings/rawDataTypes.d.ts +283 -0
@@ -0,0 +1,908 @@
1
+ 'use strict';
2
+
3
+ const EventEmitter = require('node:events');
4
+ const { setTimeout, setInterval, clearTimeout } = require('node:timers');
5
+ const WebSocket = require('../../WebSocket');
6
+ const { Status, Events, ShardEvents, Opcodes, WSEvents, WSCodes } = require('../../util/Constants');
7
+ const Intents = require('../../util/Intents');
8
+
9
+ const STATUS_KEYS = Object.keys(Status);
10
+ const CONNECTION_STATE = Object.keys(WebSocket.WebSocket);
11
+
12
+ let zlib;
13
+
14
+ try {
15
+ zlib = require('zlib-sync');
16
+ } catch {} // eslint-disable-line no-empty
17
+
18
+ /**
19
+ * Represents a Shard's WebSocket connection
20
+ */
21
+ class WebSocketShard extends EventEmitter {
22
+ constructor(manager, id) {
23
+ super();
24
+
25
+ /**
26
+ * The WebSocketManager of the shard
27
+ * @type {WebSocketManager}
28
+ */
29
+ this.manager = manager;
30
+
31
+ /**
32
+ * The shard's id
33
+ * @type {number}
34
+ */
35
+ this.id = id;
36
+
37
+ /**
38
+ * The current status of the shard
39
+ * @type {Status}
40
+ */
41
+ this.status = Status.IDLE;
42
+
43
+ /**
44
+ * The current sequence of the shard
45
+ * @type {number}
46
+ * @private
47
+ */
48
+ this.sequence = -1;
49
+
50
+ /**
51
+ * The sequence of the shard after close
52
+ * @type {number}
53
+ * @private
54
+ */
55
+ this.closeSequence = 0;
56
+
57
+ /**
58
+ * The current session id of the shard
59
+ * @type {?string}
60
+ * @private
61
+ */
62
+ this.sessionId = null;
63
+
64
+ /**
65
+ * URL to use when resuming
66
+ * @type {?string}
67
+ * @private
68
+ */
69
+ this.resumeURL = null;
70
+
71
+ /**
72
+ * The previous heartbeat ping of the shard
73
+ * @type {number}
74
+ */
75
+ this.ping = -1;
76
+
77
+ /**
78
+ * The last time a ping was sent (a timestamp)
79
+ * @type {number}
80
+ * @private
81
+ */
82
+ this.lastPingTimestamp = -1;
83
+
84
+ /**
85
+ * If we received a heartbeat ack back. Used to identify zombie connections
86
+ * @type {boolean}
87
+ * @private
88
+ */
89
+ this.lastHeartbeatAcked = true;
90
+
91
+ /**
92
+ * Used to prevent calling {@link WebSocketShard#event:close} twice while closing or terminating the WebSocket.
93
+ * @type {boolean}
94
+ * @private
95
+ */
96
+ this.closeEmitted = false;
97
+
98
+ /**
99
+ * Contains the rate limit queue and metadata
100
+ * @name WebSocketShard#ratelimit
101
+ * @type {Object}
102
+ * @private
103
+ */
104
+ Object.defineProperty(this, 'ratelimit', {
105
+ value: {
106
+ queue: [],
107
+ total: 120,
108
+ remaining: 120,
109
+ time: 60e3,
110
+ timer: null,
111
+ },
112
+ });
113
+
114
+ /**
115
+ * The WebSocket connection for the current shard
116
+ * @name WebSocketShard#connection
117
+ * @type {?WebSocket}
118
+ * @private
119
+ */
120
+ Object.defineProperty(this, 'connection', { value: null, writable: true });
121
+
122
+ /**
123
+ * @external Inflate
124
+ * @see {@link https://www.npmjs.com/package/zlib-sync}
125
+ */
126
+
127
+ /**
128
+ * The compression to use
129
+ * @name WebSocketShard#inflate
130
+ * @type {?Inflate}
131
+ * @private
132
+ */
133
+ Object.defineProperty(this, 'inflate', { value: null, writable: true });
134
+
135
+ /**
136
+ * The HELLO timeout
137
+ * @name WebSocketShard#helloTimeout
138
+ * @type {?NodeJS.Timeout}
139
+ * @private
140
+ */
141
+ Object.defineProperty(this, 'helloTimeout', { value: null, writable: true });
142
+
143
+ /**
144
+ * The WebSocket timeout.
145
+ * @name WebSocketShard#wsCloseTimeout
146
+ * @type {?NodeJS.Timeout}
147
+ * @private
148
+ */
149
+ Object.defineProperty(this, 'wsCloseTimeout', { value: null, writable: true });
150
+
151
+ /**
152
+ * If the manager attached its event handlers on the shard
153
+ * @name WebSocketShard#eventsAttached
154
+ * @type {boolean}
155
+ * @private
156
+ */
157
+ Object.defineProperty(this, 'eventsAttached', { value: false, writable: true });
158
+
159
+ /**
160
+ * A set of guild ids this shard expects to receive
161
+ * @name WebSocketShard#expectedGuilds
162
+ * @type {?Set<string>}
163
+ * @private
164
+ */
165
+ Object.defineProperty(this, 'expectedGuilds', { value: null, writable: true });
166
+
167
+ /**
168
+ * The ready timeout
169
+ * @name WebSocketShard#readyTimeout
170
+ * @type {?NodeJS.Timeout}
171
+ * @private
172
+ */
173
+ Object.defineProperty(this, 'readyTimeout', { value: null, writable: true });
174
+
175
+ /**
176
+ * Time when the WebSocket connection was opened
177
+ * @name WebSocketShard#connectedAt
178
+ * @type {number}
179
+ * @private
180
+ */
181
+ Object.defineProperty(this, 'connectedAt', { value: 0, writable: true });
182
+ }
183
+
184
+ /**
185
+ * Emits a debug event.
186
+ * @param {string} message The debug message
187
+ * @private
188
+ */
189
+ debug(message) {
190
+ this.manager.debug(message, this);
191
+ }
192
+
193
+ /**
194
+ * Connects the shard to the gateway.
195
+ * @private
196
+ * @returns {Promise<void>} A promise that will resolve if the shard turns ready successfully,
197
+ * or reject if we couldn't connect
198
+ */
199
+ connect() {
200
+ const { client } = this.manager;
201
+
202
+ const gateway = this.resumeURL ?? this.manager.gateway;
203
+
204
+ if (this.connection?.readyState === WebSocket.OPEN && this.status === Status.READY) {
205
+ return Promise.resolve();
206
+ }
207
+
208
+ return new Promise((resolve, reject) => {
209
+ const cleanup = () => {
210
+ this.removeListener(ShardEvents.CLOSE, onClose);
211
+ this.removeListener(ShardEvents.READY, onReady);
212
+ this.removeListener(ShardEvents.RESUMED, onResumed);
213
+ this.removeListener(ShardEvents.INVALID_SESSION, onInvalidOrDestroyed);
214
+ this.removeListener(ShardEvents.DESTROYED, onInvalidOrDestroyed);
215
+ };
216
+
217
+ const onReady = () => {
218
+ cleanup();
219
+ resolve();
220
+ };
221
+
222
+ const onResumed = () => {
223
+ cleanup();
224
+ resolve();
225
+ };
226
+
227
+ const onClose = event => {
228
+ cleanup();
229
+ reject(event);
230
+ };
231
+
232
+ const onInvalidOrDestroyed = () => {
233
+ cleanup();
234
+ // eslint-disable-next-line prefer-promise-reject-errors
235
+ reject();
236
+ };
237
+
238
+ this.once(ShardEvents.READY, onReady);
239
+ this.once(ShardEvents.RESUMED, onResumed);
240
+ this.once(ShardEvents.CLOSE, onClose);
241
+ this.once(ShardEvents.INVALID_SESSION, onInvalidOrDestroyed);
242
+ this.once(ShardEvents.DESTROYED, onInvalidOrDestroyed);
243
+
244
+ if (this.connection?.readyState === WebSocket.OPEN) {
245
+ this.debug('An open connection was found, attempting an immediate identify.');
246
+ this.identify();
247
+ return;
248
+ }
249
+
250
+ if (this.connection) {
251
+ this.debug(`A connection object was found. Cleaning up before continuing.
252
+ State: ${CONNECTION_STATE[this.connection.readyState]}`);
253
+ this.destroy({ emit: false });
254
+ }
255
+
256
+ const wsQuery = { v: client.options.ws.version };
257
+
258
+ if (zlib) {
259
+ this.inflate = new zlib.Inflate({
260
+ chunkSize: 65535,
261
+ flush: zlib.Z_SYNC_FLUSH,
262
+ to: WebSocket.encoding === 'json' ? 'string' : '',
263
+ });
264
+ wsQuery.compress = 'zlib-stream';
265
+ }
266
+
267
+ this.debug(
268
+ `[CONNECT]
269
+ Gateway : ${gateway}
270
+ Version : ${client.options.ws.version}
271
+ Encoding : ${WebSocket.encoding}
272
+ Compression: ${zlib ? 'zlib-stream' : 'none'}
273
+ Proxy : ${client.options.proxy || 'none'}`,
274
+ );
275
+
276
+ this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING;
277
+ this.setHelloTimeout();
278
+ this.setWsCloseTimeout(-1);
279
+ this.connectedAt = Date.now();
280
+
281
+ let args = { handshakeTimeout: 30_000 };
282
+ if (client.options.proxy.length > 0) {
283
+ const proxy = require('proxy-agent');
284
+ args.agent = new proxy(client.options.proxy);
285
+ this.debug(`Using proxy ${client.options.proxy}`, args);
286
+ }
287
+ // Adding a handshake timeout to just make sure no zombie connection appears.
288
+ const ws = (this.connection = WebSocket.create(gateway, wsQuery, args));
289
+ ws.onopen = this.onOpen.bind(this);
290
+ ws.onmessage = this.onMessage.bind(this);
291
+ ws.onerror = this.onError.bind(this);
292
+ ws.onclose = this.onClose.bind(this);
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Called whenever a connection is opened to the gateway.
298
+ * @private
299
+ */
300
+ onOpen() {
301
+ this.debug(`[CONNECTED] Took ${Date.now() - this.connectedAt}ms`);
302
+ this.status = Status.NEARLY;
303
+ }
304
+
305
+ /**
306
+ * Called whenever a message is received.
307
+ * @param {MessageEvent} event Event received
308
+ * @private
309
+ */
310
+ onMessage({ data }) {
311
+ let raw;
312
+ if (data instanceof ArrayBuffer) data = new Uint8Array(data);
313
+ if (zlib) {
314
+ const l = data.length;
315
+ const flush =
316
+ l >= 4 && data[l - 4] === 0x00 && data[l - 3] === 0x00 && data[l - 2] === 0xff && data[l - 1] === 0xff;
317
+
318
+ this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH);
319
+ if (!flush) return;
320
+ raw = this.inflate.result;
321
+ } else {
322
+ raw = data;
323
+ }
324
+ let packet;
325
+ try {
326
+ packet = WebSocket.unpack(raw);
327
+ } catch (err) {
328
+ this.manager.client.emit(Events.SHARD_ERROR, err, this.id);
329
+ return;
330
+ }
331
+ this.manager.client.emit(Events.RAW, packet, this.id);
332
+ if (packet.op === Opcodes.DISPATCH) this.manager.emit(packet.t, packet.d, this.id);
333
+ this.onPacket(packet);
334
+ }
335
+
336
+ /**
337
+ * Called whenever an error occurs with the WebSocket.
338
+ * @param {ErrorEvent} event The error that occurred
339
+ * @private
340
+ */
341
+ onError(event) {
342
+ const error = event?.error ?? event;
343
+ if (!error) return;
344
+
345
+ /**
346
+ * Emitted whenever a shard's WebSocket encounters a connection error.
347
+ * @event Client#shardError
348
+ * @param {Error} error The encountered error
349
+ * @param {number} shardId The shard that encountered this error
350
+ */
351
+ this.manager.client.emit(Events.SHARD_ERROR, error, this.id);
352
+ }
353
+
354
+ /**
355
+ * @external CloseEvent
356
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
357
+ */
358
+
359
+ /**
360
+ * @external ErrorEvent
361
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent}
362
+ */
363
+
364
+ /**
365
+ * @external MessageEvent
366
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent}
367
+ */
368
+
369
+ /**
370
+ * Called whenever a connection to the gateway is closed.
371
+ * @param {CloseEvent} event Close event that was received
372
+ * @private
373
+ */
374
+ onClose(event) {
375
+ this.closeEmitted = true;
376
+ if (this.sequence !== -1) this.closeSequence = this.sequence;
377
+ this.sequence = -1;
378
+ this.setHeartbeatTimer(-1);
379
+ this.setHelloTimeout(-1);
380
+ // Clearing the WebSocket close timeout as close was emitted.
381
+ this.setWsCloseTimeout(-1);
382
+ // If we still have a connection object, clean up its listeners
383
+ if (this.connection) {
384
+ this._cleanupConnection();
385
+ // Having this after _cleanupConnection to just clean up the connection and not listen to ws.onclose
386
+ this.destroy({ reset: !this.sessionId, emit: false, log: false });
387
+ }
388
+ this.status = Status.DISCONNECTED;
389
+ this.emitClose(event);
390
+ }
391
+
392
+ /**
393
+ * This method is responsible to emit close event for this shard.
394
+ * This method helps the shard reconnect.
395
+ * @param {CloseEvent} [event] Close event that was received
396
+ */
397
+ emitClose(
398
+ event = {
399
+ code: 1011,
400
+ reason: WSCodes[1011],
401
+ wasClean: false,
402
+ },
403
+ ) {
404
+ this.debug(`[CLOSE]
405
+ Event Code: ${event.code}
406
+ Clean : ${event.wasClean}
407
+ Reason : ${event.reason ?? 'No reason received'}`);
408
+ /**
409
+ * Emitted when a shard's WebSocket closes.
410
+ * @private
411
+ * @event WebSocketShard#close
412
+ * @param {CloseEvent} event The received event
413
+ */
414
+ this.emit(ShardEvents.CLOSE, event);
415
+ }
416
+
417
+ /**
418
+ * Called whenever a packet is received.
419
+ * @param {Object} packet The received packet
420
+ * @private
421
+ */
422
+ onPacket(packet) {
423
+ if (!packet) {
424
+ this.debug(`Received broken packet: '${packet}'.`);
425
+ return;
426
+ }
427
+
428
+ switch (packet.t) {
429
+ case WSEvents.READY:
430
+ /**
431
+ * Emitted when the shard receives the READY payload and is now waiting for guilds
432
+ * @event WebSocketShard#ready
433
+ */
434
+ this.emit(ShardEvents.READY);
435
+
436
+ this.sessionId = packet.d.session_id;
437
+ this.resumeURL = packet.d.resume_gateway_url;
438
+ this.expectedGuilds = new Set(packet.d.guilds.filter(d => d?.unavailable == true).map(d => d.id));
439
+ this.status = Status.WAITING_FOR_GUILDS;
440
+ this.debug(`[READY] Session ${this.sessionId} | ResumeURL ${this.resumeURL}`);
441
+ this.lastHeartbeatAcked = true;
442
+ this.sendHeartbeat('ReadyHeartbeat');
443
+ break;
444
+ case WSEvents.RESUMED: {
445
+ /**
446
+ * Emitted when the shard resumes successfully
447
+ * @event WebSocketShard#resumed
448
+ */
449
+ this.emit(ShardEvents.RESUMED);
450
+
451
+ this.status = Status.READY;
452
+ const replayed = packet.s - this.closeSequence;
453
+ this.debug(`[RESUMED] Session ${this.sessionId} | Replayed ${replayed} events.`);
454
+ this.lastHeartbeatAcked = true;
455
+ this.sendHeartbeat('ResumeHeartbeat');
456
+ break;
457
+ }
458
+ }
459
+
460
+ if (packet.s > this.sequence) this.sequence = packet.s;
461
+
462
+ switch (packet.op) {
463
+ case Opcodes.HELLO:
464
+ this.setHelloTimeout(-1);
465
+ this.setHeartbeatTimer(packet.d.heartbeat_interval);
466
+ this.identify();
467
+ break;
468
+ case Opcodes.RECONNECT:
469
+ this.debug('[RECONNECT] Discord asked us to reconnect');
470
+ this.destroy({ closeCode: 4_000 });
471
+ break;
472
+ case Opcodes.INVALID_SESSION:
473
+ this.debug(`[INVALID SESSION] Resumable: ${packet.d}.`);
474
+ // If we can resume the session, do so immediately
475
+ if (packet.d) {
476
+ this.identifyResume();
477
+ return;
478
+ }
479
+ // Reset the sequence
480
+ this.sequence = -1;
481
+ // Reset the session id as it's invalid
482
+ this.sessionId = null;
483
+ // Set the status to reconnecting
484
+ this.status = Status.RECONNECTING;
485
+ // Finally, emit the INVALID_SESSION event
486
+ /**
487
+ * Emitted when the session has been invalidated.
488
+ * @event WebSocketShard#invalidSession
489
+ */
490
+ this.emit(ShardEvents.INVALID_SESSION);
491
+ break;
492
+ case Opcodes.HEARTBEAT_ACK:
493
+ this.ackHeartbeat();
494
+ break;
495
+ case Opcodes.HEARTBEAT:
496
+ this.sendHeartbeat('HeartbeatRequest', true);
497
+ break;
498
+ default:
499
+ this.manager.handlePacket(packet, this);
500
+ if (this.status === Status.WAITING_FOR_GUILDS && packet.t === WSEvents.GUILD_CREATE) {
501
+ this.expectedGuilds.delete(packet.d.id);
502
+ this.checkReady();
503
+ }
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Checks if the shard can be marked as ready
509
+ * @private
510
+ */
511
+ checkReady() {
512
+ // Step 0. Clear the ready timeout, if it exists
513
+ if (this.readyTimeout) {
514
+ clearTimeout(this.readyTimeout);
515
+ this.readyTimeout = null;
516
+ }
517
+ // Step 1. If we don't have any other guilds pending, we are ready
518
+ if (!this.expectedGuilds.size) {
519
+ this.debug('Shard received all its guilds. Marking as fully ready.');
520
+ this.status = Status.READY;
521
+
522
+ /**
523
+ * Emitted when the shard is fully ready.
524
+ * This event is emitted if:
525
+ * * all guilds were received by this shard
526
+ * * the ready timeout expired, and some guilds are unavailable
527
+ * @event WebSocketShard#allReady
528
+ * @param {?Set<string>} unavailableGuilds Set of unavailable guilds, if any
529
+ */
530
+ this.emit(ShardEvents.ALL_READY);
531
+ return;
532
+ }
533
+ const hasGuildsIntent = new Intents(this.manager.client.options.intents).has(Intents.FLAGS.GUILDS);
534
+ // Step 2. Create a timeout that will mark the shard as ready if there are still unavailable guilds
535
+ // * The timeout is 15 seconds by default
536
+ // * This can be optionally changed in the client options via the `waitGuildTimeout` option
537
+ // * a timeout time of zero will skip this timeout, which potentially could cause the Client to miss guilds.
538
+
539
+ const { waitGuildTimeout } = this.manager.client.options;
540
+
541
+ this.readyTimeout = setTimeout(() => {
542
+ this.debug(
543
+ `Shard ${hasGuildsIntent ? 'did' : 'will'} not receive any more guild packets` +
544
+ `${hasGuildsIntent ? ` in ${waitGuildTimeout} ms` : ''}.\nUnavailable guild count: ${
545
+ this.expectedGuilds.size
546
+ }`,
547
+ );
548
+
549
+ this.readyTimeout = null;
550
+
551
+ this.status = Status.READY;
552
+
553
+ this.emit(ShardEvents.ALL_READY, this.expectedGuilds);
554
+ // }, hasGuildsIntent && waitGuildTimeout).unref();
555
+ }, waitGuildTimeout).unref();
556
+ }
557
+
558
+ /**
559
+ * Sets the HELLO packet timeout.
560
+ * @param {number} [time] If set to -1, it will clear the hello timeout
561
+ * @private
562
+ */
563
+ setHelloTimeout(time) {
564
+ if (time === -1) {
565
+ if (this.helloTimeout) {
566
+ this.debug('Clearing the HELLO timeout.');
567
+ clearTimeout(this.helloTimeout);
568
+ this.helloTimeout = null;
569
+ }
570
+ return;
571
+ }
572
+ this.debug('Setting a HELLO timeout for 20s.');
573
+ this.helloTimeout = setTimeout(() => {
574
+ this.debug('Did not receive HELLO in time. Destroying and connecting again.');
575
+ this.destroy({ reset: true, closeCode: 4009 });
576
+ }, 20_000).unref();
577
+ }
578
+
579
+ /**
580
+ * Sets the WebSocket Close timeout.
581
+ * This method is responsible for detecting any zombie connections if the WebSocket fails to close properly.
582
+ * @param {number} [time] If set to -1, it will clear the timeout
583
+ * @private
584
+ */
585
+ setWsCloseTimeout(time) {
586
+ if (this.wsCloseTimeout) {
587
+ this.debug('[WebSocket] Clearing the close timeout.');
588
+ clearTimeout(this.wsCloseTimeout);
589
+ }
590
+ if (time === -1) {
591
+ this.wsCloseTimeout = null;
592
+ return;
593
+ }
594
+ this.wsCloseTimeout = setTimeout(() => {
595
+ this.setWsCloseTimeout(-1);
596
+ // Check if close event was emitted.
597
+ if (this.closeEmitted) {
598
+ this.debug(`[WebSocket] close was already emitted, assuming the connection was closed properly.`);
599
+ // Setting the variable false to check for zombie connections.
600
+ this.closeEmitted = false;
601
+ return;
602
+ }
603
+
604
+ this.debug(
605
+ // eslint-disable-next-line max-len
606
+ `[WebSocket] Close Emitted: ${this.closeEmitted} | did not close properly, assuming a zombie connection.\nEmitting close and reconnecting again.`,
607
+ );
608
+
609
+ if (this.connection) this._cleanupConnection();
610
+
611
+ this.emitClose({
612
+ code: 4009,
613
+ reason: 'Session time out.',
614
+ wasClean: false,
615
+ });
616
+ }, time);
617
+ }
618
+
619
+ /**
620
+ * Sets the heartbeat timer for this shard.
621
+ * @param {number} time If -1, clears the interval, any other number sets an interval
622
+ * @private
623
+ */
624
+ setHeartbeatTimer(time) {
625
+ if (time === -1) {
626
+ if (this.heartbeatInterval) {
627
+ this.debug('Clearing the heartbeat interval.');
628
+ clearInterval(this.heartbeatInterval);
629
+ this.heartbeatInterval = null;
630
+ }
631
+ return;
632
+ }
633
+ this.debug(`Setting a heartbeat interval for ${time}ms.`);
634
+ // Sanity checks
635
+ if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
636
+ this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), time).unref();
637
+ }
638
+
639
+ /**
640
+ * Sends a heartbeat to the WebSocket.
641
+ * If this shard didn't receive a heartbeat last time, it will destroy it and reconnect
642
+ * @param {string} [tag='HeartbeatTimer'] What caused this heartbeat to be sent
643
+ * @param {boolean} [ignoreHeartbeatAck] If we should send the heartbeat forcefully.
644
+ * @private
645
+ */
646
+ sendHeartbeat(
647
+ tag = 'HeartbeatTimer',
648
+ ignoreHeartbeatAck = [Status.WAITING_FOR_GUILDS, Status.IDENTIFYING, Status.RESUMING].includes(this.status),
649
+ ) {
650
+ if (ignoreHeartbeatAck && !this.lastHeartbeatAcked) {
651
+ this.debug(`[${tag}] Didn't process heartbeat ack yet but we are still connected. Sending one now.`);
652
+ } else if (!this.lastHeartbeatAcked) {
653
+ this.debug(
654
+ `[${tag}] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting.
655
+ Status : ${STATUS_KEYS[this.status]}
656
+ Sequence : ${this.sequence}
657
+ Connection State: ${this.connection ? CONNECTION_STATE[this.connection.readyState] : 'No Connection??'}`,
658
+ );
659
+
660
+ this.destroy({ reset: true, closeCode: 4009 });
661
+ return;
662
+ }
663
+
664
+ this.debug(`[${tag}] Sending a heartbeat.`);
665
+ this.lastHeartbeatAcked = false;
666
+ this.lastPingTimestamp = Date.now();
667
+ this.send({ op: Opcodes.HEARTBEAT, d: this.sequence }, true);
668
+ }
669
+
670
+ /**
671
+ * Acknowledges a heartbeat.
672
+ * @private
673
+ */
674
+ ackHeartbeat() {
675
+ this.lastHeartbeatAcked = true;
676
+ const latency = Date.now() - this.lastPingTimestamp;
677
+ this.debug(`Heartbeat acknowledged, latency of ${latency}ms.`);
678
+ this.ping = latency;
679
+ }
680
+
681
+ /**
682
+ * Identifies the client on the connection.
683
+ * @private
684
+ * @returns {void}
685
+ */
686
+ identify() {
687
+ return this.sessionId ? this.identifyResume() : this.identifyNew();
688
+ }
689
+
690
+ /**
691
+ * Identifies as a new connection on the gateway.
692
+ * @private
693
+ */
694
+ identifyNew() {
695
+ const { client } = this.manager;
696
+ if (!client.token) {
697
+ this.debug('[IDENTIFY] No token available to identify a new session.');
698
+ return;
699
+ }
700
+
701
+ this.status = Status.IDENTIFYING;
702
+
703
+ // Clone the identify payload and assign the token and shard info
704
+ client.options.ws.properties = Object.assign(client.options.ws.properties, {
705
+ browser_user_agent: client.options.http.headers['User-Agent'],
706
+ });
707
+ Object.keys(client.options.ws.properties)
708
+ .filter(k => k.startsWith('$'))
709
+ .forEach(k => {
710
+ client.options.ws.properties[k.slice(1)] = client.options.ws.properties[k];
711
+ delete client.options.ws.properties[k];
712
+ });
713
+ const d = {
714
+ ...client.options.ws,
715
+ // Remove, Req by dolfies_person [Reddit]: intents: Intents.resolve(client.options.intents),
716
+ token: client.token,
717
+ // Remove: shard: [this.id, Number(client.options.shardCount)],
718
+ };
719
+
720
+ this.debug(
721
+ `[IDENTIFY] Shard ${this.id}/${client.options.shardCount} with intents: ${Intents.resolve(
722
+ client.options.intents,
723
+ )} 😊`,
724
+ );
725
+ this.send({ op: Opcodes.IDENTIFY, d }, true);
726
+ }
727
+
728
+ /**
729
+ * Resumes a session on the gateway.
730
+ * @private
731
+ */
732
+ identifyResume() {
733
+ if (!this.sessionId) {
734
+ this.debug('[RESUME] No session id was present; identifying as a new session.');
735
+ this.identifyNew();
736
+ return;
737
+ }
738
+
739
+ this.status = Status.RESUMING;
740
+
741
+ this.debug(`[RESUME] Session ${this.sessionId}, sequence ${this.closeSequence}`);
742
+
743
+ const d = {
744
+ token: this.manager.client.token,
745
+ session_id: this.sessionId,
746
+ seq: this.closeSequence,
747
+ };
748
+
749
+ this.send({ op: Opcodes.RESUME, d }, true);
750
+ }
751
+
752
+ /**
753
+ * Adds a packet to the queue to be sent to the gateway.
754
+ * <warn>If you use this method, make sure you understand that you need to provide
755
+ * a full [Payload](https://discord.com/developers/docs/topics/gateway-events#payload-structure).
756
+ * Do not use this method if you don't know what you're doing.</warn>
757
+ * @param {Object} data The full packet to send
758
+ * @param {boolean} [important=false] If this packet should be added first in queue
759
+ */
760
+ send(data, important = false) {
761
+ this.ratelimit.queue[important ? 'unshift' : 'push'](data);
762
+ this.processQueue();
763
+ }
764
+
765
+ /**
766
+ * Sends data, bypassing the queue.
767
+ * @param {Object} data Packet to send
768
+ * @returns {void}
769
+ * @private
770
+ */
771
+ _send(data) {
772
+ if (this.connection?.readyState !== WebSocket.OPEN) {
773
+ this.debug(`Tried to send packet '${JSON.stringify(data)}' but no WebSocket is available!`);
774
+ this.destroy({ closeCode: 4_000 });
775
+ return;
776
+ }
777
+
778
+ this.connection.send(WebSocket.pack(data), err => {
779
+ if (err) this.manager.client.emit(Events.SHARD_ERROR, err, this.id);
780
+ });
781
+ }
782
+
783
+ /**
784
+ * Processes the current WebSocket queue.
785
+ * @returns {void}
786
+ * @private
787
+ */
788
+ processQueue() {
789
+ if (this.ratelimit.remaining === 0) return;
790
+ if (this.ratelimit.queue.length === 0) return;
791
+ if (this.ratelimit.remaining === this.ratelimit.total) {
792
+ this.ratelimit.timer = setTimeout(() => {
793
+ this.ratelimit.remaining = this.ratelimit.total;
794
+ this.processQueue();
795
+ }, this.ratelimit.time).unref();
796
+ }
797
+ while (this.ratelimit.remaining > 0) {
798
+ const item = this.ratelimit.queue.shift();
799
+ if (!item) return;
800
+ this._send(item);
801
+ this.ratelimit.remaining--;
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Destroys this shard and closes its WebSocket connection.
807
+ * @param {Object} [options={ closeCode: 1000, reset: false, emit: true, log: true }] Options for destroying the shard
808
+ * @private
809
+ */
810
+ destroy({ closeCode = 1_000, reset = false, emit = true, log = true } = {}) {
811
+ if (log) {
812
+ this.debug(`[DESTROY]
813
+ Close Code : ${closeCode}
814
+ Reset : ${reset}
815
+ Emit DESTROYED: ${emit}`);
816
+ }
817
+
818
+ // Step 0: Remove all timers
819
+ this.setHeartbeatTimer(-1);
820
+ this.setHelloTimeout(-1);
821
+ this.debug(
822
+ `[WebSocket] Destroy: Attempting to close the WebSocket. | WS State: ${
823
+ CONNECTION_STATE[this.connection?.readyState ?? WebSocket.CLOSED]
824
+ }`,
825
+ );
826
+ // Step 1: Close the WebSocket connection, if any, otherwise, emit DESTROYED
827
+ if (this.connection) {
828
+ // If the connection is currently opened, we will (hopefully) receive close
829
+ if (this.connection.readyState === WebSocket.OPEN) {
830
+ this.connection.close(closeCode);
831
+ this.debug(`[WebSocket] Close: Tried closing. | WS State: ${CONNECTION_STATE[this.connection.readyState]}`);
832
+ } else {
833
+ // Connection is not OPEN
834
+ this.debug(`WS State: ${CONNECTION_STATE[this.connection.readyState]}`);
835
+ // Attempt to close the connection just in case
836
+ try {
837
+ this.connection.close(closeCode);
838
+ } catch (err) {
839
+ this.debug(
840
+ `[WebSocket] Close: Something went wrong while closing the WebSocket: ${
841
+ err.message || err
842
+ }. Forcefully terminating the connection | WS State: ${CONNECTION_STATE[this.connection.readyState]}`,
843
+ );
844
+ this.connection.terminate();
845
+ }
846
+ // Emit the destroyed event if needed
847
+ if (emit) this._emitDestroyed();
848
+ }
849
+ } else if (emit) {
850
+ // We requested a destroy, but we had no connection. Emit destroyed
851
+ this._emitDestroyed();
852
+ }
853
+
854
+ this.debug(
855
+ `[WebSocket] Adding a WebSocket close timeout to ensure a correct WS reconnect.
856
+ Timeout: ${this.manager.client.options.closeTimeout}ms`,
857
+ );
858
+ this.setWsCloseTimeout(this.manager.client.options.closeTimeout);
859
+
860
+ // Step 2: Null the connection object
861
+ this.connection = null;
862
+
863
+ // Step 3: Set the shard status to DISCONNECTED
864
+ this.status = Status.DISCONNECTED;
865
+
866
+ // Step 4: Cache the old sequence (use to attempt a resume)
867
+ if (this.sequence !== -1) this.closeSequence = this.sequence;
868
+
869
+ // Step 5: Reset the sequence, resume URL and session id if requested
870
+ if (reset) {
871
+ this.resumeURL = null;
872
+ this.sequence = -1;
873
+ this.sessionId = null;
874
+ }
875
+
876
+ // Step 6: reset the rate limit data
877
+ this.ratelimit.remaining = this.ratelimit.total;
878
+ this.ratelimit.queue.length = 0;
879
+ if (this.ratelimit.timer) {
880
+ clearTimeout(this.ratelimit.timer);
881
+ this.ratelimit.timer = null;
882
+ }
883
+ }
884
+
885
+ /**
886
+ * Cleans up the WebSocket connection listeners.
887
+ * @private
888
+ */
889
+ _cleanupConnection() {
890
+ this.connection.onopen = this.connection.onclose = this.connection.onmessage = null;
891
+ this.connection.onerror = () => null;
892
+ }
893
+
894
+ /**
895
+ * Emits the DESTROYED event on the shard
896
+ * @private
897
+ */
898
+ _emitDestroyed() {
899
+ /**
900
+ * Emitted when a shard is destroyed, but no WebSocket connection was present.
901
+ * @private
902
+ * @event WebSocketShard#destroyed
903
+ */
904
+ this.emit(ShardEvents.DESTROYED);
905
+ }
906
+ }
907
+
908
+ module.exports = WebSocketShard;