@biggy1337/discord-sbjs 1.3.11

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