djs-selfbot-v13 3.2.2 → 3.7.1

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 (363) hide show
  1. package/README.md +37 -36
  2. package/package.json +89 -85
  3. package/src/WebSocket.js +39 -39
  4. package/src/client/BaseClient.js +86 -86
  5. package/src/client/Client.js +934 -836
  6. package/src/client/WebhookClient.js +61 -61
  7. package/src/client/actions/Action.js +116 -120
  8. package/src/client/actions/ActionsManager.js +80 -78
  9. package/src/client/actions/ApplicationCommandPermissionsUpdate.js +34 -34
  10. package/src/client/actions/AutoModerationActionExecution.js +27 -27
  11. package/src/client/actions/AutoModerationRuleCreate.js +28 -28
  12. package/src/client/actions/AutoModerationRuleDelete.js +32 -32
  13. package/src/client/actions/AutoModerationRuleUpdate.js +30 -30
  14. package/src/client/actions/ChannelCreate.js +23 -23
  15. package/src/client/actions/ChannelDelete.js +39 -39
  16. package/src/client/actions/ChannelUpdate.js +43 -43
  17. package/src/client/actions/GuildAuditLogEntryCreate.js +29 -29
  18. package/src/client/actions/GuildBanAdd.js +20 -20
  19. package/src/client/actions/GuildBanRemove.js +25 -25
  20. package/src/client/actions/GuildChannelsPositionUpdate.js +21 -21
  21. package/src/client/actions/GuildDelete.js +65 -65
  22. package/src/client/actions/GuildEmojiCreate.js +20 -20
  23. package/src/client/actions/GuildEmojiDelete.js +21 -21
  24. package/src/client/actions/GuildEmojiUpdate.js +20 -20
  25. package/src/client/actions/GuildEmojisUpdate.js +34 -34
  26. package/src/client/actions/GuildIntegrationsUpdate.js +19 -19
  27. package/src/client/actions/GuildMemberRemove.js +33 -32
  28. package/src/client/actions/GuildMemberUpdate.js +44 -43
  29. package/src/client/actions/GuildRoleCreate.js +25 -25
  30. package/src/client/actions/GuildRoleDelete.js +31 -31
  31. package/src/client/actions/GuildRoleUpdate.js +39 -39
  32. package/src/client/actions/GuildRolesPositionUpdate.js +21 -21
  33. package/src/client/actions/GuildScheduledEventCreate.js +27 -27
  34. package/src/client/actions/GuildScheduledEventDelete.js +31 -31
  35. package/src/client/actions/GuildScheduledEventUpdate.js +30 -30
  36. package/src/client/actions/GuildScheduledEventUserAdd.js +32 -32
  37. package/src/client/actions/GuildScheduledEventUserRemove.js +32 -32
  38. package/src/client/actions/GuildStickerCreate.js +20 -20
  39. package/src/client/actions/GuildStickerDelete.js +21 -21
  40. package/src/client/actions/GuildStickerUpdate.js +20 -20
  41. package/src/client/actions/GuildStickersUpdate.js +34 -34
  42. package/src/client/actions/GuildUpdate.js +33 -33
  43. package/src/client/actions/InviteCreate.js +28 -28
  44. package/src/client/actions/InviteDelete.js +30 -30
  45. package/src/client/actions/MessageCreate.js +50 -46
  46. package/src/client/actions/MessageDelete.js +32 -32
  47. package/src/client/actions/MessageDeleteBulk.js +46 -46
  48. package/src/client/actions/MessagePollVoteAdd.js +33 -0
  49. package/src/client/actions/MessagePollVoteRemove.js +33 -0
  50. package/src/client/actions/MessageReactionAdd.js +68 -56
  51. package/src/client/actions/MessageReactionRemove.js +50 -45
  52. package/src/client/actions/MessageReactionRemoveAll.js +33 -33
  53. package/src/client/actions/MessageReactionRemoveEmoji.js +28 -28
  54. package/src/client/actions/MessageUpdate.js +26 -26
  55. package/src/client/actions/PresenceUpdate.js +50 -46
  56. package/src/client/actions/StageInstanceCreate.js +28 -28
  57. package/src/client/actions/StageInstanceDelete.js +33 -33
  58. package/src/client/actions/StageInstanceUpdate.js +30 -30
  59. package/src/client/actions/ThreadCreate.js +24 -24
  60. package/src/client/actions/ThreadDelete.js +32 -32
  61. package/src/client/actions/ThreadListSync.js +59 -59
  62. package/src/client/actions/ThreadMemberUpdate.js +30 -30
  63. package/src/client/actions/ThreadMembersUpdate.js +34 -34
  64. package/src/client/actions/TypingStart.js +29 -29
  65. package/src/client/actions/UserUpdate.js +35 -35
  66. package/src/client/actions/VoiceStateUpdate.js +50 -57
  67. package/src/client/actions/WebhooksUpdate.js +20 -20
  68. package/src/client/voice/ClientVoiceManager.js +151 -51
  69. package/src/client/voice/VoiceConnection.js +1249 -0
  70. package/src/client/voice/dispatcher/AnnexBDispatcher.js +120 -0
  71. package/src/client/voice/dispatcher/AudioDispatcher.js +145 -0
  72. package/src/client/voice/dispatcher/BaseDispatcher.js +459 -0
  73. package/src/client/voice/dispatcher/VPxDispatcher.js +54 -0
  74. package/src/client/voice/dispatcher/VideoDispatcher.js +68 -0
  75. package/src/client/voice/networking/VoiceUDPClient.js +173 -0
  76. package/src/client/voice/networking/VoiceWebSocket.js +286 -0
  77. package/src/client/voice/player/MediaPlayer.js +321 -0
  78. package/src/client/voice/player/processing/AnnexBNalSplitter.js +244 -0
  79. package/src/client/voice/player/processing/IvfSplitter.js +106 -0
  80. package/src/client/voice/player/processing/PCMInsertSilence.js +37 -0
  81. package/src/client/voice/receiver/PacketHandler.js +260 -0
  82. package/src/client/voice/receiver/Receiver.js +96 -0
  83. package/src/client/voice/receiver/Recorder.js +173 -0
  84. package/src/client/voice/util/Function.js +116 -0
  85. package/src/client/voice/util/PlayInterface.js +122 -0
  86. package/src/client/voice/util/Secretbox.js +64 -0
  87. package/src/client/voice/util/Silence.js +16 -0
  88. package/src/client/voice/util/Socket.js +62 -0
  89. package/src/client/voice/util/VolumeInterface.js +104 -0
  90. package/src/client/websocket/WebSocketManager.js +392 -392
  91. package/src/client/websocket/WebSocketShard.js +907 -906
  92. package/src/client/websocket/handlers/APPLICATION_COMMAND_CREATE.js +18 -18
  93. package/src/client/websocket/handlers/APPLICATION_COMMAND_DELETE.js +20 -20
  94. package/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js +5 -5
  95. package/src/client/websocket/handlers/APPLICATION_COMMAND_UPDATE.js +20 -20
  96. package/src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js +5 -5
  97. package/src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js +5 -5
  98. package/src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js +5 -5
  99. package/src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js +5 -5
  100. package/src/client/websocket/handlers/CALL_CREATE.js +14 -14
  101. package/src/client/websocket/handlers/CALL_DELETE.js +11 -11
  102. package/src/client/websocket/handlers/CALL_UPDATE.js +11 -11
  103. package/src/client/websocket/handlers/CHANNEL_CREATE.js +5 -5
  104. package/src/client/websocket/handlers/CHANNEL_DELETE.js +5 -5
  105. package/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js +22 -22
  106. package/src/client/websocket/handlers/CHANNEL_RECIPIENT_ADD.js +19 -19
  107. package/src/client/websocket/handlers/CHANNEL_RECIPIENT_REMOVE.js +16 -16
  108. package/src/client/websocket/handlers/CHANNEL_UPDATE.js +16 -16
  109. package/src/client/websocket/handlers/GUILD_AUDIT_LOG_ENTRY_CREATE.js +5 -5
  110. package/src/client/websocket/handlers/GUILD_BAN_ADD.js +5 -5
  111. package/src/client/websocket/handlers/GUILD_BAN_REMOVE.js +5 -5
  112. package/src/client/websocket/handlers/GUILD_CREATE.js +52 -52
  113. package/src/client/websocket/handlers/GUILD_DELETE.js +5 -5
  114. package/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js +5 -5
  115. package/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js +5 -5
  116. package/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js +39 -39
  117. package/src/client/websocket/handlers/GUILD_MEMBER_ADD.js +20 -19
  118. package/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js +5 -5
  119. package/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js +5 -5
  120. package/src/client/websocket/handlers/GUILD_ROLE_CREATE.js +5 -5
  121. package/src/client/websocket/handlers/GUILD_ROLE_DELETE.js +5 -5
  122. package/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js +5 -5
  123. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_CREATE.js +5 -5
  124. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_DELETE.js +5 -5
  125. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_UPDATE.js +5 -5
  126. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_USER_ADD.js +5 -5
  127. package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_USER_REMOVE.js +5 -5
  128. package/src/client/websocket/handlers/GUILD_STICKERS_UPDATE.js +5 -5
  129. package/src/client/websocket/handlers/GUILD_UPDATE.js +5 -5
  130. package/src/client/websocket/handlers/INTERACTION_MODAL_CREATE.js +12 -12
  131. package/src/client/websocket/handlers/INVITE_CREATE.js +5 -5
  132. package/src/client/websocket/handlers/INVITE_DELETE.js +5 -5
  133. package/src/client/websocket/handlers/MESSAGE_CREATE.js +5 -5
  134. package/src/client/websocket/handlers/MESSAGE_DELETE.js +5 -5
  135. package/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js +5 -5
  136. package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_ADD.js +5 -22
  137. package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_REMOVE.js +5 -12
  138. package/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js +5 -5
  139. package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js +5 -5
  140. package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js +5 -5
  141. package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js +5 -5
  142. package/src/client/websocket/handlers/MESSAGE_UPDATE.js +16 -16
  143. package/src/client/websocket/handlers/PRESENCE_UPDATE.js +5 -5
  144. package/src/client/websocket/handlers/READY.js +121 -120
  145. package/src/client/websocket/handlers/RELATIONSHIP_ADD.js +19 -19
  146. package/src/client/websocket/handlers/RELATIONSHIP_REMOVE.js +17 -17
  147. package/src/client/websocket/handlers/RELATIONSHIP_UPDATE.js +41 -41
  148. package/src/client/websocket/handlers/RESUMED.js +14 -14
  149. package/src/client/websocket/handlers/STAGE_INSTANCE_CREATE.js +5 -5
  150. package/src/client/websocket/handlers/STAGE_INSTANCE_DELETE.js +5 -5
  151. package/src/client/websocket/handlers/STAGE_INSTANCE_UPDATE.js +5 -5
  152. package/src/client/websocket/handlers/THREAD_CREATE.js +5 -5
  153. package/src/client/websocket/handlers/THREAD_DELETE.js +5 -5
  154. package/src/client/websocket/handlers/THREAD_LIST_SYNC.js +5 -5
  155. package/src/client/websocket/handlers/THREAD_MEMBERS_UPDATE.js +5 -5
  156. package/src/client/websocket/handlers/THREAD_MEMBER_UPDATE.js +5 -5
  157. package/src/client/websocket/handlers/THREAD_UPDATE.js +16 -16
  158. package/src/client/websocket/handlers/TYPING_START.js +5 -5
  159. package/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js +6 -6
  160. package/src/client/websocket/handlers/USER_NOTE_UPDATE.js +5 -5
  161. package/src/client/websocket/handlers/USER_REQUIRED_ACTION_UPDATE.js +78 -78
  162. package/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js +5 -5
  163. package/src/client/websocket/handlers/USER_UPDATE.js +5 -5
  164. package/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js +16 -0
  165. package/src/client/websocket/handlers/VOICE_CHANNEL_STATUS_UPDATE.js +12 -12
  166. package/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js +6 -6
  167. package/src/client/websocket/handlers/VOICE_STATE_UPDATE.js +5 -5
  168. package/src/client/websocket/handlers/WEBHOOKS_UPDATE.js +5 -5
  169. package/src/client/websocket/handlers/index.js +84 -83
  170. package/src/errors/DJSError.js +61 -61
  171. package/src/errors/Messages.js +217 -183
  172. package/src/errors/index.js +4 -4
  173. package/src/index.js +172 -159
  174. package/src/managers/ApplicationCommandManager.js +264 -264
  175. package/src/managers/ApplicationCommandPermissionsManager.js +417 -417
  176. package/src/managers/AutoModerationRuleManager.js +296 -296
  177. package/src/managers/BaseGuildEmojiManager.js +80 -80
  178. package/src/managers/BaseManager.js +19 -19
  179. package/src/managers/BillingManager.js +66 -66
  180. package/src/managers/CachedManager.js +71 -71
  181. package/src/managers/ChannelManager.js +148 -138
  182. package/src/managers/ClientUserSettingManager.js +372 -372
  183. package/src/managers/DataManager.js +61 -61
  184. package/src/managers/GuildBanManager.js +250 -250
  185. package/src/managers/GuildChannelManager.js +488 -488
  186. package/src/managers/GuildEmojiManager.js +171 -171
  187. package/src/managers/GuildEmojiRoleManager.js +118 -118
  188. package/src/managers/GuildForumThreadManager.js +108 -108
  189. package/src/managers/GuildInviteManager.js +213 -213
  190. package/src/managers/GuildManager.js +338 -304
  191. package/src/managers/GuildMemberManager.js +599 -597
  192. package/src/managers/GuildMemberRoleManager.js +195 -191
  193. package/src/managers/GuildScheduledEventManager.js +314 -296
  194. package/src/managers/GuildSettingManager.js +155 -155
  195. package/src/managers/GuildStickerManager.js +179 -179
  196. package/src/managers/GuildTextThreadManager.js +98 -98
  197. package/src/managers/InteractionManager.js +39 -39
  198. package/src/managers/MessageManager.js +423 -391
  199. package/src/managers/PermissionOverwriteManager.js +164 -166
  200. package/src/managers/PresenceManager.js +71 -58
  201. package/src/managers/ReactionManager.js +67 -67
  202. package/src/managers/ReactionUserManager.js +73 -71
  203. package/src/managers/RelationshipManager.js +278 -265
  204. package/src/managers/RoleManager.js +448 -352
  205. package/src/managers/SessionManager.js +66 -0
  206. package/src/managers/StageInstanceManager.js +162 -162
  207. package/src/managers/ThreadManager.js +175 -174
  208. package/src/managers/ThreadMemberManager.js +186 -186
  209. package/src/managers/UserManager.js +136 -146
  210. package/src/managers/UserNoteManager.js +53 -53
  211. package/src/managers/VoiceStateManager.js +59 -37
  212. package/src/rest/APIRequest.js +154 -160
  213. package/src/rest/APIRouter.js +53 -53
  214. package/src/rest/DiscordAPIError.js +119 -104
  215. package/src/rest/HTTPError.js +62 -62
  216. package/src/rest/RESTManager.js +67 -62
  217. package/src/rest/RateLimitError.js +55 -55
  218. package/src/rest/RequestHandler.js +466 -444
  219. package/src/sharding/Shard.js +444 -443
  220. package/src/sharding/ShardClientUtil.js +279 -275
  221. package/src/sharding/ShardingManager.js +319 -318
  222. package/src/structures/AnonymousGuild.js +98 -98
  223. package/src/structures/ApplicationCommand.js +593 -593
  224. package/src/structures/ApplicationRoleConnectionMetadata.js +48 -48
  225. package/src/structures/AutoModerationActionExecution.js +89 -89
  226. package/src/structures/AutoModerationRule.js +294 -294
  227. package/src/structures/AutocompleteInteraction.js +107 -107
  228. package/src/structures/Base.js +43 -43
  229. package/src/structures/BaseCommandInteraction.js +211 -211
  230. package/src/structures/BaseGuild.js +116 -116
  231. package/src/structures/BaseGuildEmoji.js +56 -56
  232. package/src/structures/BaseGuildTextChannel.js +191 -191
  233. package/src/structures/BaseGuildVoiceChannel.js +241 -241
  234. package/src/structures/BaseMessageComponent.js +181 -114
  235. package/src/structures/ButtonInteraction.js +11 -11
  236. package/src/structures/CallState.js +63 -63
  237. package/src/structures/CategoryChannel.js +85 -85
  238. package/src/structures/Channel.js +284 -270
  239. package/src/structures/ClientPresence.js +77 -85
  240. package/src/structures/ClientUser.js +479 -448
  241. package/src/structures/CommandInteraction.js +41 -41
  242. package/src/structures/CommandInteractionOptionResolver.js +276 -276
  243. package/src/structures/ContainerComponent.js +68 -0
  244. package/src/structures/ContextMenuInteraction.js +65 -65
  245. package/src/structures/DMChannel.js +219 -217
  246. package/src/structures/DirectoryChannel.js +20 -20
  247. package/src/structures/Emoji.js +148 -148
  248. package/src/structures/FileComponent.js +49 -0
  249. package/src/structures/ForumChannel.js +31 -261
  250. package/src/structures/GroupDMChannel.js +394 -387
  251. package/src/structures/Guild.js +1643 -1608
  252. package/src/structures/GuildAuditLogs.js +746 -729
  253. package/src/structures/GuildBan.js +59 -59
  254. package/src/structures/GuildBoost.js +108 -108
  255. package/src/structures/GuildChannel.js +470 -468
  256. package/src/structures/GuildEmoji.js +161 -161
  257. package/src/structures/GuildMember.js +636 -568
  258. package/src/structures/GuildPreview.js +191 -191
  259. package/src/structures/GuildPreviewEmoji.js +27 -27
  260. package/src/structures/GuildScheduledEvent.js +536 -441
  261. package/src/structures/GuildTemplate.js +236 -236
  262. package/src/structures/Integration.js +188 -188
  263. package/src/structures/IntegrationApplication.js +96 -96
  264. package/src/structures/Interaction.js +290 -290
  265. package/src/structures/InteractionCollector.js +248 -248
  266. package/src/structures/InteractionWebhook.js +43 -43
  267. package/src/structures/Invite.js +358 -358
  268. package/src/structures/InviteGuild.js +23 -23
  269. package/src/structures/InviteStageInstance.js +86 -86
  270. package/src/structures/MediaChannel.js +11 -0
  271. package/src/structures/MediaGalleryComponent.js +41 -0
  272. package/src/structures/MediaGalleryItem.js +47 -0
  273. package/src/structures/Message.js +1252 -1227
  274. package/src/structures/MessageActionRow.js +105 -103
  275. package/src/structures/MessageAttachment.js +216 -204
  276. package/src/structures/MessageButton.js +166 -165
  277. package/src/structures/MessageCollector.js +146 -146
  278. package/src/structures/MessageComponentInteraction.js +120 -120
  279. package/src/structures/MessageContextMenuInteraction.js +20 -20
  280. package/src/structures/MessageEmbed.js +596 -586
  281. package/src/structures/MessageMentions.js +273 -273
  282. package/src/structures/MessagePayload.js +354 -318
  283. package/src/structures/MessageReaction.js +181 -171
  284. package/src/structures/MessageSelectMenu.js +141 -140
  285. package/src/structures/Modal.js +161 -161
  286. package/src/structures/ModalSubmitFieldsResolver.js +53 -53
  287. package/src/structures/ModalSubmitInteraction.js +119 -119
  288. package/src/structures/NewsChannel.js +32 -32
  289. package/src/structures/OAuth2Guild.js +28 -28
  290. package/src/structures/PermissionOverwrites.js +198 -196
  291. package/src/structures/Poll.js +108 -0
  292. package/src/structures/PollAnswer.js +88 -0
  293. package/src/structures/Presence.js +1105 -1101
  294. package/src/structures/ReactionCollector.js +229 -229
  295. package/src/structures/ReactionEmoji.js +31 -31
  296. package/src/structures/Role.js +590 -531
  297. package/src/structures/SectionComponent.js +48 -0
  298. package/src/structures/SelectMenuInteraction.js +21 -21
  299. package/src/structures/SeparatorComponent.js +48 -0
  300. package/src/structures/Session.js +81 -0
  301. package/src/structures/StageChannel.js +104 -104
  302. package/src/structures/StageInstance.js +208 -208
  303. package/src/structures/Sticker.js +310 -310
  304. package/src/structures/StickerPack.js +95 -95
  305. package/src/structures/StoreChannel.js +56 -56
  306. package/src/structures/Team.js +118 -118
  307. package/src/structures/TeamMember.js +80 -71
  308. package/src/structures/TextChannel.js +33 -33
  309. package/src/structures/TextDisplayComponent.js +40 -0
  310. package/src/structures/TextInputComponent.js +132 -131
  311. package/src/structures/ThreadChannel.js +605 -607
  312. package/src/structures/ThreadMember.js +105 -105
  313. package/src/structures/ThreadOnlyChannel.js +249 -0
  314. package/src/structures/ThumbnailComponent.js +57 -0
  315. package/src/structures/Typing.js +74 -74
  316. package/src/structures/UnfurledMediaItem.js +29 -0
  317. package/src/structures/User.js +640 -543
  318. package/src/structures/UserContextMenuInteraction.js +29 -29
  319. package/src/structures/VoiceChannel.js +110 -110
  320. package/src/structures/VoiceChannelEffect.js +69 -0
  321. package/src/structures/VoiceRegion.js +53 -53
  322. package/src/structures/VoiceState.js +354 -341
  323. package/src/structures/WebEmbed.js +373 -373
  324. package/src/structures/Webhook.js +478 -467
  325. package/src/structures/WelcomeChannel.js +60 -60
  326. package/src/structures/WelcomeScreen.js +48 -48
  327. package/src/structures/Widget.js +87 -87
  328. package/src/structures/WidgetMember.js +99 -99
  329. package/src/structures/interfaces/Application.js +825 -313
  330. package/src/structures/interfaces/Collector.js +300 -300
  331. package/src/structures/interfaces/InteractionResponses.js +313 -313
  332. package/src/structures/interfaces/TextBasedChannel.js +759 -719
  333. package/src/util/APITypes.js +59 -0
  334. package/src/util/ActivityFlags.js +44 -44
  335. package/src/util/ApplicationFlags.js +76 -76
  336. package/src/util/AttachmentFlags.js +38 -38
  337. package/src/util/BitField.js +170 -170
  338. package/src/util/ChannelFlags.js +45 -45
  339. package/src/util/Constants.js +1914 -1773
  340. package/src/util/DataResolver.js +146 -145
  341. package/src/util/Formatters.js +228 -228
  342. package/src/util/GuildMemberFlags.js +43 -43
  343. package/src/util/Intents.js +74 -74
  344. package/src/util/InviteFlags.js +34 -29
  345. package/src/util/LimitedCollection.js +131 -131
  346. package/src/util/MessageFlags.js +63 -54
  347. package/src/util/Options.js +358 -336
  348. package/src/util/Permissions.js +202 -202
  349. package/src/util/PremiumUsageFlags.js +31 -31
  350. package/src/util/PurchasedFlags.js +33 -33
  351. package/src/util/RemoteAuth.js +382 -379
  352. package/src/util/RoleFlags.js +37 -37
  353. package/src/util/SnowflakeUtil.js +92 -92
  354. package/src/util/Speaking.js +33 -0
  355. package/src/util/Sweepers.js +466 -466
  356. package/src/util/SystemChannelFlags.js +55 -55
  357. package/src/util/ThreadMemberFlags.js +30 -30
  358. package/src/util/UserFlags.js +104 -104
  359. package/src/util/Util.js +1048 -889
  360. package/typings/enums.d.ts +439 -297
  361. package/typings/index.d.ts +8247 -7432
  362. package/typings/rawDataTypes.d.ts +403 -342
  363. package/src/structures/MessagePoll.js +0 -238
@@ -1,1101 +1,1105 @@
1
- 'use strict';
2
-
3
- const { randomUUID } = require('node:crypto');
4
- const Base = require('./Base');
5
- const ActivityFlags = require('../util/ActivityFlags');
6
- const { ActivityTypes } = require('../util/Constants');
7
- const Util = require('../util/Util');
8
-
9
- /**
10
- * Activity sent in a message.
11
- * @typedef {Object} MessageActivity
12
- * @property {string} [partyId] Id of the party represented in activity
13
- * @property {MessageActivityType} type Type of activity sent
14
- */
15
-
16
- /**
17
- * @external MessageActivityType
18
- * @see {@link https://discord-api-types.dev/api/discord-api-types-v9/enum/MessageActivityType}
19
- */
20
-
21
- /**
22
- * The status of this presence:
23
- * * **`online`** - user is online
24
- * * **`idle`** - user is AFK
25
- * * **`offline`** - user is offline or invisible
26
- * * **`dnd`** - user is in Do Not Disturb
27
- * @typedef {string} PresenceStatus
28
- */
29
-
30
- /**
31
- * The status of this presence:
32
- * * **`online`** - user is online
33
- * * **`idle`** - user is AFK
34
- * * **`dnd`** - user is in Do Not Disturb
35
- * @typedef {string} ClientPresenceStatus
36
- */
37
-
38
- /**
39
- * Represents a user's presence.
40
- * @extends {Base}
41
- */
42
- class Presence extends Base {
43
- constructor(client, data = {}) {
44
- super(client);
45
-
46
- /**
47
- * The presence's user id
48
- * @type {Snowflake}
49
- */
50
- this.userId = data.user.id;
51
-
52
- /**
53
- * The guild this presence is in
54
- * @type {?Guild}
55
- */
56
- this.guild = data.guild ?? null;
57
-
58
- this._patch(data);
59
- }
60
-
61
- /**
62
- * The user of this presence
63
- * @type {?User}
64
- * @readonly
65
- */
66
- get user() {
67
- return this.client.users.resolve(this.userId);
68
- }
69
-
70
- /**
71
- * The member of this presence
72
- * @type {?GuildMember}
73
- * @readonly
74
- */
75
- get member() {
76
- return this.guild.members.resolve(this.userId);
77
- }
78
-
79
- _patch(data) {
80
- if ('status' in data) {
81
- /**
82
- * The status of this presence
83
- * @type {PresenceStatus}
84
- */
85
- this.status = data.status;
86
- } else {
87
- this.status ??= 'offline';
88
- }
89
-
90
- if ('activities' in data) {
91
- /**
92
- * The activities of this presence (Always `Activity[]` if not ClientUser)
93
- * @type {CustomStatus[]|RichPresence[]|SpotifyRPC[]|Activity[]}
94
- */
95
- this.activities = data.activities.map(activity => {
96
- if (this.userId == this.client.user.id) {
97
- if ([ActivityTypes.CUSTOM, 'CUSTOM'].includes(activity.type)) {
98
- return new CustomStatus(this.client, activity);
99
- } else if (activity.id == 'spotify:1') {
100
- return new SpotifyRPC(this.client, activity);
101
- } else {
102
- return new RichPresence(this.client, activity);
103
- }
104
- } else {
105
- return new Activity(this, activity);
106
- }
107
- });
108
- } else {
109
- this.activities ??= [];
110
- }
111
-
112
- if ('client_status' in data) {
113
- /**
114
- * The devices this presence is on
115
- * @type {?Object}
116
- * @property {?ClientPresenceStatus} web The current presence in the web application
117
- * @property {?ClientPresenceStatus} mobile The current presence in the mobile application
118
- * @property {?ClientPresenceStatus} desktop The current presence in the desktop application
119
- */
120
- this.clientStatus = data.client_status;
121
- } else {
122
- this.clientStatus ??= null;
123
- }
124
-
125
- if ('last_modified' in data) {
126
- /**
127
- * The timestamp this presence was last updated
128
- * @type {number}
129
- */
130
- this.lastModified = data.last_modified;
131
- }
132
-
133
- return this;
134
- }
135
-
136
- _clone() {
137
- const clone = Object.assign(Object.create(this), this);
138
- clone.activities = this.activities.map(activity => activity._clone());
139
- return clone;
140
- }
141
-
142
- /**
143
- * Whether this presence is equal to another.
144
- * @param {Presence} presence The presence to compare with
145
- * @returns {boolean}
146
- */
147
- equals(presence) {
148
- return (
149
- this === presence ||
150
- (presence &&
151
- this.status === presence.status &&
152
- this.activities.length === presence.activities.length &&
153
- this.activities.every((activity, index) => activity.equals(presence.activities[index])) &&
154
- this.clientStatus?.web === presence.clientStatus?.web &&
155
- this.clientStatus?.mobile === presence.clientStatus?.mobile &&
156
- this.clientStatus?.desktop === presence.clientStatus?.desktop)
157
- );
158
- }
159
-
160
- toJSON() {
161
- return Util.flatten(this);
162
- }
163
- }
164
-
165
- /**
166
- * The platform of this activity:
167
- * * **`desktop`**
168
- * * **`samsung`** - playing on Samsung Galaxy
169
- * * **`xbox`** - playing on Xbox Live
170
- * * **`ios`**
171
- * * **`android`**
172
- * * **`embedded`**
173
- * * **`ps4`**
174
- * * **`ps5`**
175
- * @typedef {string} ActivityPlatform
176
- */
177
-
178
- /**
179
- * Represents an activity that is part of a user's presence.
180
- */
181
- class Activity {
182
- constructor(presence, data) {
183
- if (!(presence instanceof Presence)) {
184
- throw new Error("Class constructor Activity cannot be invoked without 'presence'");
185
- }
186
- /**
187
- * The presence of the Activity
188
- * @type {Presence}
189
- * @readonly
190
- * @name Activity#presence
191
- */
192
- Object.defineProperty(this, 'presence', { value: presence });
193
-
194
- this._patch(data);
195
- }
196
-
197
- _patch(data = {}) {
198
- if ('id' in data) {
199
- /**
200
- * The activity's id
201
- * @type {string}
202
- */
203
- this.id = data.id;
204
- }
205
-
206
- if ('name' in data) {
207
- /**
208
- * The activity's name
209
- * @type {string}
210
- */
211
- this.name = data.name;
212
- }
213
-
214
- if ('type' in data) {
215
- /**
216
- * The activity status's type
217
- * @type {ActivityType}
218
- */
219
- this.type = typeof data.type === 'number' ? ActivityTypes[data.type] : data.type;
220
- }
221
-
222
- if ('url' in data) {
223
- /**
224
- * If the activity is being streamed, a link to the stream
225
- * @type {?string}
226
- */
227
- this.url = data.url;
228
- } else {
229
- this.url = null;
230
- }
231
-
232
- if ('created_at' in data || 'createdTimestamp' in data) {
233
- /**
234
- * Creation date of the activity
235
- * @type {number}
236
- */
237
- this.createdTimestamp = data.created_at || data.createdTimestamp;
238
- }
239
-
240
- if ('session_id' in data) {
241
- /**
242
- * The game's or Spotify session's id
243
- * @type {?string}
244
- */
245
- this.sessionId = data.session_id;
246
- } else {
247
- this.sessionId = this.presence.client?.sessionId;
248
- }
249
-
250
- if ('platform' in data) {
251
- /**
252
- * The platform the game is being played on
253
- * @type {?ActivityPlatform}
254
- */
255
- this.platform = data.platform;
256
- } else {
257
- this.platform = null;
258
- }
259
-
260
- if ('timestamps' in data && data.timestamps) {
261
- /**
262
- * Represents timestamps of an activity
263
- * @typedef {Object} ActivityTimestamps
264
- * @property {?number} start When the activity started
265
- * @property {?number} end When the activity will end
266
- */
267
-
268
- /**
269
- * Timestamps for the activity
270
- * @type {?ActivityTimestamps}
271
- */
272
- this.timestamps = {
273
- start: data.timestamps.start ? new Date(data.timestamps.start).getTime() : null,
274
- end: data.timestamps.end ? new Date(data.timestamps.end).getTime() : null,
275
- };
276
- } else {
277
- this.timestamps = null;
278
- }
279
-
280
- if ('application_id' in data || 'applicationId' in data) {
281
- /**
282
- * The id of the application associated with this activity
283
- * @type {?Snowflake}
284
- */
285
- this.applicationId = data.application_id || data.applicationId;
286
- } else {
287
- this.applicationId = null;
288
- }
289
-
290
- if ('details' in data) {
291
- /**
292
- * Details about the activity
293
- * @type {?string}
294
- */
295
- this.details = data.details;
296
- } else {
297
- this.details = null;
298
- }
299
-
300
- if ('state' in data) {
301
- /**
302
- * State of the activity
303
- * @type {?string}
304
- */
305
- this.state = data.state;
306
- } else {
307
- this.state = null;
308
- }
309
-
310
- if ('sync_id' in data || 'syncId' in data) {
311
- /**
312
- * The Spotify song's id
313
- * @type {?string}
314
- */
315
- this.syncId = data.sync_id || data.syncId;
316
- } else {
317
- this.syncId = null;
318
- }
319
-
320
- if ('flags' in data) {
321
- /**
322
- * Flags that describe the activity
323
- * @type {Readonly<ActivityFlags>}
324
- */
325
- this.flags = new ActivityFlags(data.flags).freeze();
326
- } else {
327
- this.flags = new ActivityFlags().freeze();
328
- }
329
-
330
- if ('buttons' in data) {
331
- /**
332
- * The labels of the buttons of this rich presence
333
- * @type {string[]}
334
- */
335
- this.buttons = data.buttons;
336
- } else {
337
- this.buttons = [];
338
- }
339
-
340
- if ('emoji' in data && data.emoji) {
341
- /**
342
- * Emoji for a custom activity
343
- * @type {?EmojiIdentifierResolvable}
344
- */
345
- this.emoji = Util.resolvePartialEmoji(data.emoji);
346
- } else {
347
- this.emoji = null;
348
- }
349
-
350
- if ('party' in data) {
351
- /**
352
- * Represents a party of an activity
353
- * @typedef {Object} ActivityParty
354
- * @property {?string} id The party's id
355
- * @property {number[]} size Size of the party as `[current, max]`
356
- */
357
-
358
- /**
359
- * Party of the activity
360
- * @type {?ActivityParty}
361
- */
362
- this.party = data.party;
363
- } else {
364
- this.party = null;
365
- }
366
-
367
- /**
368
- * Assets for rich presence
369
- * @type {?RichPresenceAssets}
370
- */
371
- this.assets = new RichPresenceAssets(this, data.assets);
372
- }
373
-
374
- /**
375
- * Whether this activity is equal to another activity.
376
- * @param {Activity} activity The activity to compare with
377
- * @returns {boolean}
378
- */
379
- equals(activity) {
380
- return (
381
- this === activity ||
382
- (activity &&
383
- this.name === activity.name &&
384
- this.type === activity.type &&
385
- this.url === activity.url &&
386
- this.state === activity.state &&
387
- this.details === activity.details &&
388
- this.emoji?.id === activity.emoji?.id &&
389
- this.emoji?.name === activity.emoji?.name)
390
- );
391
- }
392
-
393
- /**
394
- * The time the activity was created at
395
- * @type {Date}
396
- * @readonly
397
- */
398
- get createdAt() {
399
- return new Date(this.createdTimestamp);
400
- }
401
-
402
- /**
403
- * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
404
- * @returns {string}
405
- */
406
- toString() {
407
- return this.name;
408
- }
409
-
410
- _clone() {
411
- return Object.assign(Object.create(this), this);
412
- }
413
-
414
- toJSON(...props) {
415
- return Util.clearNullOrUndefinedObject({
416
- ...Util.flatten(this, ...props),
417
- type: typeof this.type === 'number' ? this.type : ActivityTypes[this.type],
418
- });
419
- }
420
- }
421
-
422
- /**
423
- * Assets for a rich presence
424
- */
425
- class RichPresenceAssets {
426
- constructor(activity, assets) {
427
- /**
428
- * The activity of the RichPresenceAssets
429
- * @type {Activity}
430
- * @readonly
431
- * @name RichPresenceAssets#activity
432
- */
433
- Object.defineProperty(this, 'activity', { value: activity });
434
-
435
- this._patch(assets);
436
- }
437
-
438
- _patch(assets = {}) {
439
- if ('large_text' in assets || 'largeText' in assets) {
440
- /**
441
- * Hover text for the large image
442
- * @type {?string}
443
- */
444
- this.largeText = assets.large_text || assets.largeText;
445
- } else {
446
- this.largeText = null;
447
- }
448
-
449
- if ('small_text' in assets || 'smallText' in assets) {
450
- /**
451
- * Hover text for the small image
452
- * @type {?string}
453
- */
454
- this.smallText = assets.small_text || assets.smallText;
455
- } else {
456
- this.smallText = null;
457
- }
458
-
459
- if ('large_image' in assets || 'largeImage' in assets) {
460
- /**
461
- * The large image asset's id
462
- * @type {?Snowflake}
463
- */
464
- this.largeImage = assets.large_image || assets.largeImage;
465
- } else {
466
- this.largeImage = null;
467
- }
468
-
469
- if ('small_image' in assets || 'smallImage' in assets) {
470
- /**
471
- * The small image asset's id
472
- * @type {?Snowflake}
473
- */
474
- this.smallImage = assets.small_image || assets.smallImage;
475
- } else {
476
- this.smallImage = null;
477
- }
478
- }
479
-
480
- /**
481
- * Gets the URL of the small image asset
482
- * @param {StaticImageURLOptions} [options] Options for the image URL
483
- * @returns {?string}
484
- */
485
- smallImageURL({ format, size } = {}) {
486
- if (!this.smallImage) return null;
487
- if (this.smallImage.includes(':')) {
488
- const [platform, id] = this.smallImage.split(':');
489
- switch (platform) {
490
- case 'mp':
491
- return `https://media.discordapp.net/${id}`;
492
- case 'spotify':
493
- return `https://i.scdn.co/image/${id}`;
494
- case 'youtube':
495
- return `https://i.ytimg.com/vi/${id}/hqdefault_live.jpg`;
496
- case 'twitch':
497
- return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${id}.png`;
498
- default:
499
- return null;
500
- }
501
- }
502
-
503
- return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationId, this.smallImage, {
504
- format,
505
- size,
506
- });
507
- }
508
-
509
- /**
510
- * Gets the URL of the large image asset
511
- * @param {StaticImageURLOptions} [options] Options for the image URL
512
- * @returns {?string}
513
- */
514
- largeImageURL({ format, size } = {}) {
515
- if (!this.largeImage) return null;
516
- if (this.largeImage.includes(':')) {
517
- const [platform, id] = this.largeImage.split(':');
518
- switch (platform) {
519
- case 'mp':
520
- return `https://media.discordapp.net/${id}`;
521
- case 'spotify':
522
- return `https://i.scdn.co/image/${id}`;
523
- case 'youtube':
524
- return `https://i.ytimg.com/vi/${id}/hqdefault_live.jpg`;
525
- case 'twitch':
526
- return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${id}.png`;
527
- default:
528
- return null;
529
- }
530
- }
531
-
532
- return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationId, this.largeImage, {
533
- format,
534
- size,
535
- });
536
- }
537
-
538
- static parseImage(image) {
539
- if (typeof image != 'string') {
540
- image = null;
541
- } else if (URL.canParse(image) && ['http:', 'https:'].includes(new URL(image).protocol)) {
542
- // Discord URL:
543
- image = image
544
- .replace('https://cdn.discordapp.com/', 'mp:')
545
- .replace('http://cdn.discordapp.com/', 'mp:')
546
- .replace('https://media.discordapp.net/', 'mp:')
547
- .replace('http://media.discordapp.net/', 'mp:');
548
- //
549
- if (!image.startsWith('mp:')) {
550
- throw new Error('INVALID_URL');
551
- }
552
- } else if (/^[0-9]{17,19}$/.test(image)) {
553
- // ID Assets
554
- } else if (['mp:', 'youtube:', 'spotify:', 'twitch:'].some(v => image.startsWith(v))) {
555
- // Image
556
- } else if (image.startsWith('external/')) {
557
- image = `mp:${image}`;
558
- }
559
- return image;
560
- }
561
-
562
- toJSON() {
563
- if (!this.largeImage && !this.largeText && !this.smallImage && !this.smallText) return null;
564
- return {
565
- large_image: RichPresenceAssets.parseImage(this.largeImage),
566
- large_text: this.largeText,
567
- small_image: RichPresenceAssets.parseImage(this.smallImage),
568
- small_text: this.smallText,
569
- };
570
- }
571
-
572
- /**
573
- * @typedef {string} RichPresenceImage
574
- * Support:
575
- * - cdn.discordapp.com
576
- * - media.discordapp.net
577
- * - Assets ID (https://discord.com/api/v9/oauth2/applications/{application_id}/assets)
578
- * - Media Proxy (mp:external/{hash})
579
- * - Twitch (twitch:{username})
580
- * - YouTube (youtube:{video_id})
581
- * - Spotify (spotify:{image_id})
582
- */
583
-
584
- /**
585
- * Set the large image of this activity
586
- * @param {?RichPresenceImage} image The large image asset's id
587
- * @returns {RichPresenceAssets}
588
- */
589
- setLargeImage(image) {
590
- image = RichPresenceAssets.parseImage(image);
591
- this.largeImage = image;
592
- return this;
593
- }
594
-
595
- /**
596
- * Set the small image of this activity
597
- * @param {?RichPresenceImage} image The small image asset's id
598
- * @returns {RichPresenceAssets}
599
- */
600
- setSmallImage(image) {
601
- image = RichPresenceAssets.parseImage(image);
602
- this.smallImage = image;
603
- return this;
604
- }
605
-
606
- /**
607
- * Hover text for the large image
608
- * @param {string} text Assets text
609
- * @returns {RichPresenceAssets}
610
- */
611
- setLargeText(text) {
612
- this.largeText = text;
613
- return this;
614
- }
615
-
616
- /**
617
- * Hover text for the small image
618
- * @param {string} text Assets text
619
- * @returns {RichPresenceAssets}
620
- */
621
- setSmallText(text) {
622
- this.smallText = text;
623
- return this;
624
- }
625
- }
626
-
627
- class CustomStatus extends Activity {
628
- /**
629
- * @typedef {Object} CustomStatusOptions
630
- * @property {string} [state] The state to be displayed
631
- * @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed
632
- */
633
-
634
- /**
635
- * @param {Client} client Discord Client
636
- * @param {CustomStatus|CustomStatusOptions} [data={}] CustomStatus to clone or raw data
637
- */
638
- constructor(client, data = {}) {
639
- if (!client) throw new Error("Class constructor CustomStatus cannot be invoked without 'client'");
640
- super('presence' in client ? client.presence : client, {
641
- name: 'Custom Status',
642
- type: ActivityTypes.CUSTOM,
643
- ...data,
644
- });
645
- }
646
-
647
- /**
648
- * Set the emoji of this activity
649
- * @param {EmojiIdentifierResolvable} emoji The emoji to be displayed
650
- * @returns {CustomStatus}
651
- */
652
- setEmoji(emoji) {
653
- this.emoji = Util.resolvePartialEmoji(emoji);
654
- return this;
655
- }
656
-
657
- /**
658
- * Set state of this activity
659
- * @param {string | null} state The state to be displayed
660
- * @returns {CustomStatus}
661
- */
662
- setState(state) {
663
- if (typeof state == 'string' && state.length > 128) throw new Error('State must be less than 128 characters');
664
- this.state = state;
665
- return this;
666
- }
667
-
668
- /**
669
- * Returns an object that can be used to set the status
670
- * @returns {CustomStatus}
671
- */
672
- toJSON() {
673
- if (!this.emoji & !this.state) throw new Error('CustomStatus must have at least one of emoji or state');
674
- return {
675
- name: this.name,
676
- emoji: this.emoji,
677
- type: this.type,
678
- state: this.state,
679
- };
680
- }
681
- }
682
-
683
- class RichPresence extends Activity {
684
- /**
685
- * @param {Client} client Discord client
686
- * @param {RichPresence} [data={}] RichPresence to clone or raw data
687
- */
688
- constructor(client, data = {}) {
689
- if (!client) throw new Error("Class constructor RichPresence cannot be invoked without 'client'");
690
- super('presence' in client ? client.presence : client, { type: 0, ...data });
691
- this.setup(data);
692
- }
693
-
694
- /**
695
- * Sets the status from a JSON object
696
- * @param {RichPresence} data data
697
- * @private
698
- */
699
- setup(data = {}) {
700
- this.secrets = 'secrets' in data ? data.secrets : {};
701
- this.metadata = 'metadata' in data ? data.metadata : {};
702
- }
703
-
704
- /**
705
- * Set the large image of this activity
706
- * @param {?RichPresenceImage} image The large image asset's id
707
- * @returns {RichPresence}
708
- */
709
- setAssetsLargeImage(image) {
710
- this.assets.setLargeImage(image);
711
- return this;
712
- }
713
-
714
- /**
715
- * Set the small image of this activity
716
- * @param {?RichPresenceImage} image The small image asset's id
717
- * @returns {RichPresence}
718
- */
719
- setAssetsSmallImage(image) {
720
- this.assets.setSmallImage(image);
721
- return this;
722
- }
723
-
724
- /**
725
- * Hover text for the large image
726
- * @param {string} text Assets text
727
- * @returns {RichPresence}
728
- */
729
- setAssetsLargeText(text) {
730
- this.assets.setLargeText(text);
731
- return this;
732
- }
733
-
734
- /**
735
- * Hover text for the small image
736
- * @param {string} text Assets text
737
- * @returns {RichPresence}
738
- */
739
- setAssetsSmallText(text) {
740
- this.assets.setSmallText(text);
741
- return this;
742
- }
743
-
744
- /**
745
- * Set the name of the activity
746
- * @param {?string} name The activity's name
747
- * @returns {RichPresence}
748
- */
749
- setName(name) {
750
- this.name = name;
751
- return this;
752
- }
753
-
754
- /**
755
- * If the activity is being streamed, a link to the stream
756
- * @param {?string} url URL of the stream
757
- * @returns {RichPresence}
758
- */
759
- setURL(url) {
760
- if (typeof url == 'string' && !URL.canParse(url)) throw new Error('URL must be a valid URL');
761
- this.url = url;
762
- return this;
763
- }
764
-
765
- /**
766
- * The activity status's type
767
- * @param {?ActivityTypes} type The type of activity
768
- * @returns {RichPresence}
769
- */
770
- setType(type) {
771
- this.type = typeof type == 'number' ? type : ActivityTypes[type];
772
- return this;
773
- }
774
-
775
- /**
776
- * Set the application id of this activity
777
- * @param {?Snowflake} id Bot's id
778
- * @returns {RichPresence}
779
- */
780
- setApplicationId(id) {
781
- this.applicationId = id;
782
- return this;
783
- }
784
-
785
- /**
786
- * Set the state of the activity
787
- * @param {?string} state The state of the activity
788
- * @returns {RichPresence}
789
- */
790
- setState(state) {
791
- this.state = state;
792
- return this;
793
- }
794
-
795
- /**
796
- * Set the details of the activity
797
- * @param {?string} details The details of the activity
798
- * @returns {RichPresence}
799
- */
800
- setDetails(details) {
801
- this.details = details;
802
- return this;
803
- }
804
-
805
- /**
806
- * @typedef {Object} RichParty
807
- * @property {string} id The id of the party
808
- * @property {number} max The maximum number of members in the party
809
- * @property {number} current The current number of members in the party
810
- */
811
-
812
- /**
813
- * Set the party of this activity
814
- * @param {?RichParty} party The party to be displayed
815
- * @returns {RichPresence}
816
- */
817
- setParty(party) {
818
- if (typeof party == 'object') {
819
- if (!party.max || typeof party.max != 'number') throw new Error('Party must have max number');
820
- if (!party.current || typeof party.current != 'number') throw new Error('Party must have current');
821
- if (party.current > party.max) throw new Error('Party current must be less than max number');
822
- if (!party.id || typeof party.id != 'string') party.id = randomUUID();
823
- this.party = {
824
- size: [party.current, party.max],
825
- id: party.id,
826
- };
827
- } else {
828
- this.party = null;
829
- }
830
- return this;
831
- }
832
-
833
- /**
834
- * Sets the start timestamp of the activity
835
- * @param {Date|number|null} timestamp The timestamp of the start of the activity
836
- * @returns {RichPresence}
837
- */
838
- setStartTimestamp(timestamp) {
839
- if (!this.timestamps) this.timestamps = {};
840
- if (timestamp instanceof Date) timestamp = timestamp.getTime();
841
- this.timestamps.start = timestamp;
842
- return this;
843
- }
844
-
845
- /**
846
- * Sets the end timestamp of the activity
847
- * @param {Date|number|null} timestamp The timestamp of the end of the activity
848
- * @returns {RichPresence}
849
- */
850
- setEndTimestamp(timestamp) {
851
- if (!this.timestamps) this.timestamps = {};
852
- if (timestamp instanceof Date) timestamp = timestamp.getTime();
853
- this.timestamps.end = timestamp;
854
- return this;
855
- }
856
-
857
- /**
858
- * @typedef {object} RichButton
859
- * @property {string} name The name of the button
860
- * @property {string} url The url of the button
861
- */
862
- /**
863
- * Set the buttons of the rich presence
864
- * @param {...?RichButton} button A list of buttons to set
865
- * @returns {RichPresence}
866
- */
867
- setButtons(...button) {
868
- if (button.length == 0) {
869
- this.buttons = [];
870
- delete this.metadata.button_urls;
871
- return this;
872
- } else if (button.length > 2) {
873
- throw new Error('RichPresence can only have up to 2 buttons');
874
- }
875
-
876
- this.buttons = [];
877
- this.metadata.button_urls = [];
878
-
879
- button.flat(2).forEach(b => {
880
- if (b.name && b.url) {
881
- this.buttons.push(b.name);
882
- if (!URL.canParse(b.url)) throw new Error('Button url must be a valid url');
883
- this.metadata.button_urls.push(b.url);
884
- } else {
885
- throw new Error('Button must have name and url');
886
- }
887
- });
888
- return this;
889
- }
890
-
891
- /**
892
- * The platform the activity is being played on
893
- * @param {ActivityPlatform | null} platform Any platform
894
- * @returns {RichPresence}
895
- */
896
- setPlatform(platform) {
897
- this.platform = platform;
898
- return this;
899
- }
900
-
901
- /**
902
- * Secrets for rich presence joining and spectating (send-only)
903
- * @param {?string} join Secrets for rich presence joining
904
- * @returns {RichPresence}
905
- */
906
- setJoinSecret(join) {
907
- this.secrets.join = join;
908
- return this;
909
- }
910
-
911
- /**
912
- * Add a button to the rich presence
913
- * @param {string} name The name of the button
914
- * @param {string} url The url of the button
915
- * @returns {RichPresence}
916
- */
917
- addButton(name, url) {
918
- if (!name || !url) {
919
- throw new Error('Button must have name and url');
920
- }
921
- if (typeof name !== 'string') throw new Error('Button name must be a string');
922
- if (!URL.canParse(url)) throw new Error('Button url must be a valid url');
923
- this.buttons.push(name);
924
- if (Array.isArray(this.metadata.button_urls)) this.metadata.button_urls.push(url);
925
- else this.metadata.button_urls = [url];
926
- return this;
927
- }
928
-
929
- /**
930
- * Convert the rich presence to a JSON object
931
- * @returns {Object}
932
- */
933
- toJSON(...props) {
934
- return super.toJSON(
935
- {
936
- applicationId: 'application_id',
937
- sessionId: 'session_id',
938
- syncId: 'sync_id',
939
- createdTimestamp: 'created_at',
940
- },
941
- ...props,
942
- );
943
- }
944
-
945
- /**
946
- * @typedef {Object} ExternalAssets
947
- * @property {?string} url Orginal url of the image
948
- * @property {?string} external_asset_path Proxy url of the image (Using to RPC)
949
- */
950
-
951
- /**
952
- * Get Assets from a RichPresence (Util)
953
- * @param {Client} client Discord Client
954
- * @param {Snowflake} applicationId Application id
955
- * @param {string} image1 URL image 1 (not from Discord)
956
- * @param {string} image2 URL image 2 (not from Discord)
957
- * @returns {ExternalAssets[]}
958
- */
959
- static async getExternal(client, applicationId, image1 = '', image2 = '') {
960
- if (!client || !client.token || !client.api) throw new Error('Client must be set');
961
- // Check if applicationId is discord snowflake (17 , 18, 19 numbers)
962
- if (!/^[0-9]{17,19}$/.test(applicationId)) {
963
- throw new Error('Application id must be a Discord Snowflake');
964
- }
965
- // Check if large_image is a valid url
966
- if (image1 && image1.length > 0 && !URL.canParse(image1)) {
967
- throw new Error('Image 1 must be a valid url');
968
- }
969
- // Check if small_image is a valid url
970
- if (image2 && image2.length > 0 && !URL.canParse(image2)) {
971
- throw new Error('Image 2 must be a valid url');
972
- }
973
- const data_ = [];
974
- if (image1) data_.push(image1);
975
- if (image2) data_.push(image2);
976
- const res = await client.api.applications[applicationId]['external-assets'].post({
977
- data: {
978
- urls: data_,
979
- },
980
- });
981
- return res;
982
- }
983
-
984
- /**
985
- * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
986
- * @returns {string}
987
- */
988
- toString() {
989
- return this.name;
990
- }
991
-
992
- _clone() {
993
- return Object.assign(Object.create(this), this);
994
- }
995
- }
996
-
997
- /**
998
- * @extends {RichPresence}
999
- */
1000
- class SpotifyRPC extends RichPresence {
1001
- /**
1002
- * Create a new RichPresence (Spotify style)
1003
- * @param {Client} client Discord Client
1004
- * @param {SpotifyRPC} [options] Options for the Spotify RPC
1005
- */
1006
- constructor(client, options = {}) {
1007
- if (!client) throw new Error("Class constructor SpotifyRPC cannot be invoked without 'client'");
1008
- super(client, {
1009
- name: 'Spotify',
1010
- type: ActivityTypes.LISTENING,
1011
- party: {
1012
- id: `spotify:${client.user.id}`,
1013
- },
1014
- id: 'spotify:1',
1015
- flags: 48, // Sync + Play (ActivityFlags)
1016
- ...options,
1017
- });
1018
- this.setup(options);
1019
- }
1020
- /**
1021
- * Sets the status from a JSON object
1022
- * @param {SpotifyRPC} options data
1023
- * @private
1024
- */
1025
- setup(options) {
1026
- /**
1027
- * @typedef {Object} SpotifyMetadata
1028
- * @property {string} album_id The Spotify ID of the album of the song being played
1029
- * @property {Array<string>} artist_ids The Spotify IDs of the artists of the song being played
1030
- * @property {string} context_uri The Spotify URI of the current player context
1031
- */
1032
-
1033
- /**
1034
- * Spotify metadata
1035
- * @type {SpotifyMetadata}
1036
- */
1037
- this.metadata = {
1038
- album_id: options.metadata?.album_id || null,
1039
- artist_ids: options.metadata?.artist_ids || [],
1040
- context_uri: options.metadata?.context_uri || null,
1041
- };
1042
- }
1043
-
1044
- /**
1045
- * Set Spotify song id to sync with
1046
- * @param {string} id Song id
1047
- * @returns {SpotifyRPC}
1048
- */
1049
- setSongId(id) {
1050
- this.syncId = id;
1051
- return this;
1052
- }
1053
-
1054
- /**
1055
- * Add the artist id
1056
- * @param {string} id Artist id
1057
- * @returns {SpotifyRPC}
1058
- */
1059
- addArtistId(id) {
1060
- if (!this.metadata.artist_ids) this.metadata.artist_ids = [];
1061
- this.metadata.artist_ids.push(id);
1062
- return this;
1063
- }
1064
-
1065
- /**
1066
- * Set the artist ids
1067
- * @param {string | Array<string>} ids Artist ids
1068
- * @returns {SpotifyRPC}
1069
- */
1070
- setArtistIds(...ids) {
1071
- if (!ids?.length) {
1072
- this.metadata.artist_ids = [];
1073
- return this;
1074
- }
1075
- if (!this.metadata.artist_ids) this.metadata.artist_ids = [];
1076
- ids.flat(2).forEach(id => this.metadata.artist_ids.push(id));
1077
- return this;
1078
- }
1079
-
1080
- /**
1081
- * Set the album id
1082
- * @param {string} id Album id
1083
- * @returns {SpotifyRPC}
1084
- */
1085
- setAlbumId(id) {
1086
- this.metadata.album_id = id;
1087
- this.metadata.context_uri = `spotify:album:${id}`;
1088
- return this;
1089
- }
1090
-
1091
- toJSON() {
1092
- return super.toJSON({ id: false, emoji: false, platform: false, buttons: false });
1093
- }
1094
- }
1095
-
1096
- exports.Presence = Presence;
1097
- exports.Activity = Activity;
1098
- exports.RichPresenceAssets = RichPresenceAssets;
1099
- exports.CustomStatus = CustomStatus;
1100
- exports.RichPresence = RichPresence;
1101
- exports.SpotifyRPC = SpotifyRPC;
1
+ 'use strict';
2
+
3
+ const { randomUUID } = require('node:crypto');
4
+ const Base = require('./Base');
5
+ const ActivityFlags = require('../util/ActivityFlags');
6
+ const { ActivityTypes } = require('../util/Constants');
7
+ const Util = require('../util/Util');
8
+
9
+ /**
10
+ * Activity sent in a message.
11
+ * @typedef {Object} MessageActivity
12
+ * @property {string} [partyId] Id of the party represented in activity
13
+ * @property {MessageActivityType} type Type of activity sent
14
+ */
15
+
16
+ /**
17
+ * The status of this presence:
18
+ * * **`online`** - user is online
19
+ * * **`idle`** - user is AFK
20
+ * * **`offline`** - user is offline or invisible
21
+ * * **`dnd`** - user is in Do Not Disturb
22
+ * @typedef {string} PresenceStatus
23
+ */
24
+
25
+ /**
26
+ * The status of this presence:
27
+ * * **`online`** - user is online
28
+ * * **`idle`** - user is AFK
29
+ * * **`dnd`** - user is in Do Not Disturb
30
+ * @typedef {string} ClientPresenceStatus
31
+ */
32
+
33
+ /**
34
+ * Represents a user's presence.
35
+ * @extends {Base}
36
+ */
37
+ class Presence extends Base {
38
+ constructor(client, data = {}) {
39
+ super(client);
40
+
41
+ /**
42
+ * The presence's user id
43
+ * @type {Snowflake}
44
+ */
45
+ this.userId = data.user.id;
46
+
47
+ /**
48
+ * The guild this presence is in
49
+ * @type {?Guild}
50
+ */
51
+ this.guild = data.guild ?? null;
52
+
53
+ this._patch(data);
54
+ }
55
+
56
+ /**
57
+ * The user of this presence
58
+ * @type {?User}
59
+ * @readonly
60
+ */
61
+ get user() {
62
+ return this.client.users.resolve(this.userId);
63
+ }
64
+
65
+ /**
66
+ * The member of this presence
67
+ * @type {?GuildMember}
68
+ * @readonly
69
+ */
70
+ get member() {
71
+ return this.guild.members.resolve(this.userId);
72
+ }
73
+
74
+ _patch(data) {
75
+ if ('status' in data) {
76
+ /**
77
+ * The status of this presence
78
+ * @type {PresenceStatus}
79
+ */
80
+ this.status = data.status;
81
+ } else {
82
+ this.status ??= 'offline';
83
+ }
84
+
85
+ if ('activities' in data) {
86
+ /**
87
+ * The activities of this presence (Always `Activity[]` if not ClientUser)
88
+ * @type {CustomStatus[]|RichPresence[]|SpotifyRPC[]|Activity[]}
89
+ */
90
+ this.activities = data.activities.map(activity => {
91
+ if (this.userId == this.client.user.id) {
92
+ if ([ActivityTypes.CUSTOM, 'CUSTOM'].includes(activity.type)) {
93
+ return new CustomStatus(this.client, activity);
94
+ } else if (activity.id == 'spotify:1') {
95
+ return new SpotifyRPC(this.client, activity);
96
+ } else {
97
+ return new RichPresence(this.client, activity);
98
+ }
99
+ } else {
100
+ return new Activity(this, activity);
101
+ }
102
+ });
103
+ } else {
104
+ this.activities ??= [];
105
+ }
106
+
107
+ if ('client_status' in data) {
108
+ /**
109
+ * The devices this presence is on
110
+ * @type {?Object}
111
+ * @property {?ClientPresenceStatus} web The current presence in the web application
112
+ * @property {?ClientPresenceStatus} mobile The current presence in the mobile application
113
+ * @property {?ClientPresenceStatus} desktop The current presence in the desktop application
114
+ */
115
+ this.clientStatus = data.client_status;
116
+ } else {
117
+ this.clientStatus ??= null;
118
+ }
119
+
120
+ if ('last_modified' in data) {
121
+ /**
122
+ * The timestamp this presence was last updated
123
+ * @type {number}
124
+ */
125
+ this.lastModified = data.last_modified;
126
+ }
127
+
128
+ if ('afk' in data) {
129
+ this.afk = data.afk;
130
+ } else {
131
+ this.afk ??= false;
132
+ }
133
+
134
+ if ('since' in data) {
135
+ this.since = data.since;
136
+ } else {
137
+ this.since ??= 0;
138
+ }
139
+
140
+ return this;
141
+ }
142
+
143
+ _clone() {
144
+ const clone = Object.assign(Object.create(this), this);
145
+ clone.activities = this.activities.map(activity => activity._clone());
146
+ return clone;
147
+ }
148
+
149
+ /**
150
+ * Whether this presence is equal to another.
151
+ * @param {Presence} presence The presence to compare with
152
+ * @returns {boolean}
153
+ */
154
+ equals(presence) {
155
+ return (
156
+ this === presence ||
157
+ (presence &&
158
+ this.status === presence.status &&
159
+ this.clientStatus?.web === presence.clientStatus?.web &&
160
+ this.clientStatus?.mobile === presence.clientStatus?.mobile &&
161
+ this.clientStatus?.desktop === presence.clientStatus?.desktop &&
162
+ this.activities.length === presence.activities.length &&
163
+ this.activities.every((activity, index) => activity.equals(presence.activities[index])))
164
+ );
165
+ }
166
+
167
+ toJSON() {
168
+ return Util.flatten(this);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * The platform of this activity:
174
+ * * **`desktop`**
175
+ * * **`samsung`** - playing on Samsung Galaxy
176
+ * * **`xbox`** - playing on Xbox Live
177
+ * * **`ios`**
178
+ * * **`android`**
179
+ * * **`embedded`**
180
+ * * **`ps4`**
181
+ * * **`ps5`**
182
+ * @typedef {string} ActivityPlatform
183
+ */
184
+
185
+ /**
186
+ * Represents an activity that is part of a user's presence.
187
+ */
188
+ class Activity {
189
+ constructor(presence, data) {
190
+ if (!(presence instanceof Presence)) {
191
+ throw new Error("Class constructor Activity cannot be invoked without 'presence'");
192
+ }
193
+ /**
194
+ * The presence of the Activity
195
+ * @type {Presence}
196
+ * @readonly
197
+ * @name Activity#presence
198
+ */
199
+ Object.defineProperty(this, 'presence', { value: presence });
200
+
201
+ this._patch(data);
202
+ }
203
+
204
+ _patch(data = {}) {
205
+ if ('id' in data) {
206
+ /**
207
+ * The activity's id
208
+ * @type {string}
209
+ */
210
+ this.id = data.id;
211
+ }
212
+
213
+ if ('name' in data) {
214
+ /**
215
+ * The activity's name
216
+ * @type {string}
217
+ */
218
+ this.name = data.name;
219
+ }
220
+
221
+ if ('type' in data) {
222
+ /**
223
+ * The activity status's type
224
+ * @type {ActivityType}
225
+ */
226
+ this.type = typeof data.type === 'number' ? ActivityTypes[data.type] : data.type;
227
+ }
228
+
229
+ if ('url' in data) {
230
+ /**
231
+ * If the activity is being streamed, a link to the stream
232
+ * @type {?string}
233
+ */
234
+ this.url = data.url;
235
+ } else {
236
+ this.url = null;
237
+ }
238
+
239
+ if ('created_at' in data || 'createdTimestamp' in data) {
240
+ /**
241
+ * Creation date of the activity
242
+ * @type {number}
243
+ */
244
+ this.createdTimestamp = data.created_at || data.createdTimestamp;
245
+ }
246
+
247
+ if ('session_id' in data) {
248
+ /**
249
+ * The game's or Spotify session's id
250
+ * @type {?string}
251
+ */
252
+ this.sessionId = data.session_id;
253
+ } else {
254
+ this.sessionId = this.presence.client?.sessionId;
255
+ }
256
+
257
+ if ('platform' in data) {
258
+ /**
259
+ * The platform the game is being played on
260
+ * @type {?ActivityPlatform}
261
+ */
262
+ this.platform = data.platform;
263
+ } else {
264
+ this.platform = null;
265
+ }
266
+
267
+ if ('timestamps' in data && data.timestamps) {
268
+ /**
269
+ * Represents timestamps of an activity
270
+ * @typedef {Object} ActivityTimestamps
271
+ * @property {?number} start When the activity started
272
+ * @property {?number} end When the activity will end
273
+ */
274
+
275
+ /**
276
+ * Timestamps for the activity
277
+ * @type {?ActivityTimestamps}
278
+ */
279
+ this.timestamps = {
280
+ start: data.timestamps.start ? new Date(data.timestamps.start).getTime() : null,
281
+ end: data.timestamps.end ? new Date(data.timestamps.end).getTime() : null,
282
+ };
283
+ } else {
284
+ this.timestamps = null;
285
+ }
286
+
287
+ if ('application_id' in data || 'applicationId' in data) {
288
+ /**
289
+ * The id of the application associated with this activity
290
+ * @type {?Snowflake}
291
+ */
292
+ this.applicationId = data.application_id || data.applicationId;
293
+ } else {
294
+ this.applicationId = null;
295
+ }
296
+
297
+ if ('details' in data) {
298
+ /**
299
+ * Details about the activity
300
+ * @type {?string}
301
+ */
302
+ this.details = data.details;
303
+ } else {
304
+ this.details = null;
305
+ }
306
+
307
+ if ('state' in data) {
308
+ /**
309
+ * State of the activity
310
+ * @type {?string}
311
+ */
312
+ this.state = data.state;
313
+ } else {
314
+ this.state = null;
315
+ }
316
+
317
+ if ('sync_id' in data || 'syncId' in data) {
318
+ /**
319
+ * The sync id of the activity
320
+ * <info>This property is not documented by Discord and represents the track id in spotify activities.</info>
321
+ * @type {?string}
322
+ */
323
+ this.syncId = data.sync_id || data.syncId;
324
+ } else {
325
+ this.syncId = null;
326
+ }
327
+
328
+ if ('flags' in data) {
329
+ /**
330
+ * Flags that describe the activity
331
+ * @type {Readonly<ActivityFlags>}
332
+ */
333
+ this.flags = new ActivityFlags(data.flags).freeze();
334
+ } else {
335
+ this.flags = new ActivityFlags().freeze();
336
+ }
337
+
338
+ if ('buttons' in data) {
339
+ /**
340
+ * The labels of the buttons of this rich presence
341
+ * @type {string[]}
342
+ */
343
+ this.buttons = data.buttons;
344
+ } else {
345
+ this.buttons = [];
346
+ }
347
+
348
+ if ('emoji' in data && data.emoji) {
349
+ /**
350
+ * Emoji for a custom activity
351
+ * @type {?EmojiIdentifierResolvable}
352
+ */
353
+ this.emoji = Util.resolvePartialEmoji(data.emoji);
354
+ } else {
355
+ this.emoji = null;
356
+ }
357
+
358
+ if ('party' in data) {
359
+ /**
360
+ * Represents a party of an activity
361
+ * @typedef {Object} ActivityParty
362
+ * @property {?string} id The party's id
363
+ * @property {number[]} size Size of the party as `[current, max]`
364
+ */
365
+
366
+ /**
367
+ * Party of the activity
368
+ * @type {?ActivityParty}
369
+ */
370
+ this.party = data.party;
371
+ } else {
372
+ this.party = null;
373
+ }
374
+
375
+ /**
376
+ * Assets for rich presence
377
+ * @type {?RichPresenceAssets}
378
+ */
379
+ this.assets = new RichPresenceAssets(this, data.assets);
380
+ }
381
+
382
+ /**
383
+ * Whether this activity is equal to another activity.
384
+ * @param {Activity} activity The activity to compare with
385
+ * @returns {boolean}
386
+ */
387
+ equals(activity) {
388
+ return (
389
+ this === activity ||
390
+ (activity &&
391
+ this.name === activity.name &&
392
+ this.type === activity.type &&
393
+ this.url === activity.url &&
394
+ this.state === activity.state &&
395
+ this.details === activity.details &&
396
+ this.emoji?.id === activity.emoji?.id &&
397
+ this.emoji?.name === activity.emoji?.name)
398
+ );
399
+ }
400
+
401
+ /**
402
+ * The time the activity was created at
403
+ * @type {Date}
404
+ * @readonly
405
+ */
406
+ get createdAt() {
407
+ return new Date(this.createdTimestamp);
408
+ }
409
+
410
+ /**
411
+ * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
412
+ * @returns {string}
413
+ */
414
+ toString() {
415
+ return this.name;
416
+ }
417
+
418
+ _clone() {
419
+ return Object.assign(Object.create(this), this);
420
+ }
421
+
422
+ toJSON(...props) {
423
+ return Util.clearNullOrUndefinedObject({
424
+ ...Util.flatten(this, ...props),
425
+ type: typeof this.type === 'number' ? this.type : ActivityTypes[this.type],
426
+ });
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Assets for a rich presence
432
+ */
433
+ class RichPresenceAssets {
434
+ constructor(activity, assets) {
435
+ /**
436
+ * The activity of the RichPresenceAssets
437
+ * @type {Activity}
438
+ * @readonly
439
+ * @name RichPresenceAssets#activity
440
+ */
441
+ Object.defineProperty(this, 'activity', { value: activity });
442
+
443
+ this._patch(assets);
444
+ }
445
+
446
+ _patch(assets = {}) {
447
+ if ('large_text' in assets || 'largeText' in assets) {
448
+ /**
449
+ * Hover text for the large image
450
+ * @type {?string}
451
+ */
452
+ this.largeText = assets.large_text || assets.largeText;
453
+ } else {
454
+ this.largeText = null;
455
+ }
456
+
457
+ if ('small_text' in assets || 'smallText' in assets) {
458
+ /**
459
+ * Hover text for the small image
460
+ * @type {?string}
461
+ */
462
+ this.smallText = assets.small_text || assets.smallText;
463
+ } else {
464
+ this.smallText = null;
465
+ }
466
+
467
+ if ('large_image' in assets || 'largeImage' in assets) {
468
+ /**
469
+ * The large image asset's id
470
+ * @type {?Snowflake}
471
+ */
472
+ this.largeImage = assets.large_image || assets.largeImage;
473
+ } else {
474
+ this.largeImage = null;
475
+ }
476
+
477
+ if ('small_image' in assets || 'smallImage' in assets) {
478
+ /**
479
+ * The small image asset's id
480
+ * @type {?Snowflake}
481
+ */
482
+ this.smallImage = assets.small_image || assets.smallImage;
483
+ } else {
484
+ this.smallImage = null;
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Gets the URL of the small image asset
490
+ * @param {StaticImageURLOptions} [options] Options for the image URL
491
+ * @returns {?string}
492
+ */
493
+ smallImageURL({ format, size } = {}) {
494
+ if (!this.smallImage) return null;
495
+ if (this.smallImage.includes(':')) {
496
+ const [platform, id] = this.smallImage.split(':');
497
+ switch (platform) {
498
+ case 'mp':
499
+ return `https://media.discordapp.net/${id}`;
500
+ case 'spotify':
501
+ return `https://i.scdn.co/image/${id}`;
502
+ case 'youtube':
503
+ return `https://i.ytimg.com/vi/${id}/hqdefault_live.jpg`;
504
+ case 'twitch':
505
+ return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${id}.png`;
506
+ default:
507
+ return null;
508
+ }
509
+ }
510
+
511
+ return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationId, this.smallImage, {
512
+ format,
513
+ size,
514
+ });
515
+ }
516
+
517
+ /**
518
+ * Gets the URL of the large image asset
519
+ * @param {StaticImageURLOptions} [options] Options for the image URL
520
+ * @returns {?string}
521
+ */
522
+ largeImageURL({ format, size } = {}) {
523
+ if (!this.largeImage) return null;
524
+ if (this.largeImage.includes(':')) {
525
+ const [platform, id] = this.largeImage.split(':');
526
+ switch (platform) {
527
+ case 'mp':
528
+ return `https://media.discordapp.net/${id}`;
529
+ case 'spotify':
530
+ return `https://i.scdn.co/image/${id}`;
531
+ case 'youtube':
532
+ return `https://i.ytimg.com/vi/${id}/hqdefault_live.jpg`;
533
+ case 'twitch':
534
+ return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${id}.png`;
535
+ default:
536
+ return null;
537
+ }
538
+ }
539
+
540
+ return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationId, this.largeImage, {
541
+ format,
542
+ size,
543
+ });
544
+ }
545
+
546
+ static parseImage(image) {
547
+ if (typeof image != 'string') {
548
+ image = null;
549
+ } else if (URL.canParse(image) && ['http:', 'https:'].includes(new URL(image).protocol)) {
550
+ // Discord URL:
551
+ image = image
552
+ .replace('https://cdn.discordapp.com/', 'mp:')
553
+ .replace('http://cdn.discordapp.com/', 'mp:')
554
+ .replace('https://media.discordapp.net/', 'mp:')
555
+ .replace('http://media.discordapp.net/', 'mp:');
556
+ //
557
+ if (!image.startsWith('mp:')) {
558
+ throw new Error('INVALID_URL');
559
+ }
560
+ } else if (/^[0-9]{17,19}$/.test(image)) {
561
+ // ID Assets
562
+ } else if (['mp:', 'youtube:', 'spotify:', 'twitch:'].some(v => image.startsWith(v))) {
563
+ // Image
564
+ } else if (image.startsWith('external/')) {
565
+ image = `mp:${image}`;
566
+ }
567
+ return image;
568
+ }
569
+
570
+ toJSON() {
571
+ if (!this.largeImage && !this.largeText && !this.smallImage && !this.smallText) return null;
572
+ return {
573
+ large_image: RichPresenceAssets.parseImage(this.largeImage),
574
+ large_text: this.largeText,
575
+ small_image: RichPresenceAssets.parseImage(this.smallImage),
576
+ small_text: this.smallText,
577
+ };
578
+ }
579
+
580
+ /**
581
+ * @typedef {string} RichPresenceImage
582
+ * Support:
583
+ * - cdn.discordapp.com
584
+ * - media.discordapp.net
585
+ * - Assets ID (https://discord.com/api/v9/oauth2/applications/{application_id}/assets)
586
+ * - Media Proxy (mp:external/{hash})
587
+ * - Twitch (twitch:{username})
588
+ * - YouTube (youtube:{video_id})
589
+ * - Spotify (spotify:{image_id})
590
+ */
591
+
592
+ /**
593
+ * Set the large image of this activity
594
+ * @param {?RichPresenceImage} image The large image asset's id
595
+ * @returns {RichPresenceAssets}
596
+ */
597
+ setLargeImage(image) {
598
+ image = RichPresenceAssets.parseImage(image);
599
+ this.largeImage = image;
600
+ return this;
601
+ }
602
+
603
+ /**
604
+ * Set the small image of this activity
605
+ * @param {?RichPresenceImage} image The small image asset's id
606
+ * @returns {RichPresenceAssets}
607
+ */
608
+ setSmallImage(image) {
609
+ image = RichPresenceAssets.parseImage(image);
610
+ this.smallImage = image;
611
+ return this;
612
+ }
613
+
614
+ /**
615
+ * Hover text for the large image
616
+ * @param {string} text Assets text
617
+ * @returns {RichPresenceAssets}
618
+ */
619
+ setLargeText(text) {
620
+ this.largeText = text;
621
+ return this;
622
+ }
623
+
624
+ /**
625
+ * Hover text for the small image
626
+ * @param {string} text Assets text
627
+ * @returns {RichPresenceAssets}
628
+ */
629
+ setSmallText(text) {
630
+ this.smallText = text;
631
+ return this;
632
+ }
633
+ }
634
+
635
+ class CustomStatus extends Activity {
636
+ /**
637
+ * @typedef {Object} CustomStatusOptions
638
+ * @property {string} [state] The state to be displayed
639
+ * @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed
640
+ */
641
+
642
+ /**
643
+ * @param {Client} client Discord Client
644
+ * @param {CustomStatus|CustomStatusOptions} [data={}] CustomStatus to clone or raw data
645
+ */
646
+ constructor(client, data = {}) {
647
+ if (!client) throw new Error("Class constructor CustomStatus cannot be invoked without 'client'");
648
+ super('presence' in client ? client.presence : client, {
649
+ name: ' ',
650
+ type: ActivityTypes.CUSTOM,
651
+ ...data,
652
+ });
653
+ }
654
+
655
+ /**
656
+ * Set the emoji of this activity
657
+ * @param {EmojiIdentifierResolvable} emoji The emoji to be displayed
658
+ * @returns {CustomStatus}
659
+ */
660
+ setEmoji(emoji) {
661
+ this.emoji = Util.resolvePartialEmoji(emoji);
662
+ return this;
663
+ }
664
+
665
+ /**
666
+ * Set state of this activity
667
+ * @param {string | null} state The state to be displayed
668
+ * @returns {CustomStatus}
669
+ */
670
+ setState(state) {
671
+ if (typeof state == 'string' && state.length > 128) throw new Error('State must be less than 128 characters');
672
+ this.state = state;
673
+ return this;
674
+ }
675
+
676
+ /**
677
+ * Returns an object that can be used to set the status
678
+ * @returns {CustomStatus}
679
+ */
680
+ toJSON() {
681
+ if (!this.emoji & !this.state) throw new Error('CustomStatus must have at least one of emoji or state');
682
+ return {
683
+ name: this.name,
684
+ emoji: this.emoji,
685
+ type: ActivityTypes.CUSTOM,
686
+ state: this.state,
687
+ };
688
+ }
689
+ }
690
+
691
+ class RichPresence extends Activity {
692
+ /**
693
+ * @param {Client} client Discord client
694
+ * @param {RichPresence} [data={}] RichPresence to clone or raw data
695
+ */
696
+ constructor(client, data = {}) {
697
+ if (!client) throw new Error("Class constructor RichPresence cannot be invoked without 'client'");
698
+ super('presence' in client ? client.presence : client, { type: 0, ...data });
699
+ this.setup(data);
700
+ }
701
+
702
+ /**
703
+ * Sets the status from a JSON object
704
+ * @param {RichPresence} data data
705
+ * @private
706
+ */
707
+ setup(data = {}) {
708
+ this.secrets = 'secrets' in data ? data.secrets : {};
709
+ this.metadata = 'metadata' in data ? data.metadata : {};
710
+ }
711
+
712
+ /**
713
+ * Set the large image of this activity
714
+ * @param {?RichPresenceImage} image The large image asset's id
715
+ * @returns {RichPresence}
716
+ */
717
+ setAssetsLargeImage(image) {
718
+ this.assets.setLargeImage(image);
719
+ return this;
720
+ }
721
+
722
+ /**
723
+ * Set the small image of this activity
724
+ * @param {?RichPresenceImage} image The small image asset's id
725
+ * @returns {RichPresence}
726
+ */
727
+ setAssetsSmallImage(image) {
728
+ this.assets.setSmallImage(image);
729
+ return this;
730
+ }
731
+
732
+ /**
733
+ * Hover text for the large image
734
+ * @param {string} text Assets text
735
+ * @returns {RichPresence}
736
+ */
737
+ setAssetsLargeText(text) {
738
+ this.assets.setLargeText(text);
739
+ return this;
740
+ }
741
+
742
+ /**
743
+ * Hover text for the small image
744
+ * @param {string} text Assets text
745
+ * @returns {RichPresence}
746
+ */
747
+ setAssetsSmallText(text) {
748
+ this.assets.setSmallText(text);
749
+ return this;
750
+ }
751
+
752
+ /**
753
+ * Set the name of the activity
754
+ * @param {?string} name The activity's name
755
+ * @returns {RichPresence}
756
+ */
757
+ setName(name) {
758
+ this.name = name;
759
+ return this;
760
+ }
761
+
762
+ /**
763
+ * If the activity is being streamed, a link to the stream
764
+ * @param {?string} url URL of the stream
765
+ * @returns {RichPresence}
766
+ */
767
+ setURL(url) {
768
+ if (typeof url == 'string' && !URL.canParse(url)) throw new Error('URL must be a valid URL');
769
+ this.url = url;
770
+ return this;
771
+ }
772
+
773
+ /**
774
+ * The activity status's type
775
+ * @param {?ActivityTypes} type The type of activity
776
+ * @returns {RichPresence}
777
+ */
778
+ setType(type) {
779
+ this.type = typeof type == 'number' ? type : ActivityTypes[type];
780
+ return this;
781
+ }
782
+
783
+ /**
784
+ * Set the application id of this activity
785
+ * @param {?Snowflake} id Bot's id
786
+ * @returns {RichPresence}
787
+ */
788
+ setApplicationId(id) {
789
+ this.applicationId = id;
790
+ return this;
791
+ }
792
+
793
+ /**
794
+ * Set the state of the activity
795
+ * @param {?string} state The state of the activity
796
+ * @returns {RichPresence}
797
+ */
798
+ setState(state) {
799
+ this.state = state;
800
+ return this;
801
+ }
802
+
803
+ /**
804
+ * Set the details of the activity
805
+ * @param {?string} details The details of the activity
806
+ * @returns {RichPresence}
807
+ */
808
+ setDetails(details) {
809
+ this.details = details;
810
+ return this;
811
+ }
812
+
813
+ /**
814
+ * @typedef {Object} RichParty
815
+ * @property {string} id The id of the party
816
+ * @property {number} max The maximum number of members in the party
817
+ * @property {number} current The current number of members in the party
818
+ */
819
+
820
+ /**
821
+ * Set the party of this activity
822
+ * @param {?RichParty} party The party to be displayed
823
+ * @returns {RichPresence}
824
+ */
825
+ setParty(party) {
826
+ if (typeof party == 'object') {
827
+ if (!party.max || typeof party.max != 'number') throw new Error('Party must have max number');
828
+ if (!party.current || typeof party.current != 'number') throw new Error('Party must have current');
829
+ if (party.current > party.max) throw new Error('Party current must be less than max number');
830
+ if (!party.id || typeof party.id != 'string') party.id = randomUUID();
831
+ this.party = {
832
+ size: [party.current, party.max],
833
+ id: party.id,
834
+ };
835
+ } else {
836
+ this.party = null;
837
+ }
838
+ return this;
839
+ }
840
+
841
+ /**
842
+ * Sets the start timestamp of the activity
843
+ * @param {Date|number|null} timestamp The timestamp of the start of the activity
844
+ * @returns {RichPresence}
845
+ */
846
+ setStartTimestamp(timestamp) {
847
+ if (!this.timestamps) this.timestamps = {};
848
+ if (timestamp instanceof Date) timestamp = timestamp.getTime();
849
+ this.timestamps.start = timestamp;
850
+ return this;
851
+ }
852
+
853
+ /**
854
+ * Sets the end timestamp of the activity
855
+ * @param {Date|number|null} timestamp The timestamp of the end of the activity
856
+ * @returns {RichPresence}
857
+ */
858
+ setEndTimestamp(timestamp) {
859
+ if (!this.timestamps) this.timestamps = {};
860
+ if (timestamp instanceof Date) timestamp = timestamp.getTime();
861
+ this.timestamps.end = timestamp;
862
+ return this;
863
+ }
864
+
865
+ /**
866
+ * @typedef {object} RichButton
867
+ * @property {string} name The name of the button
868
+ * @property {string} url The url of the button
869
+ */
870
+ /**
871
+ * Set the buttons of the rich presence
872
+ * @param {...?RichButton} button A list of buttons to set
873
+ * @returns {RichPresence}
874
+ */
875
+ setButtons(...button) {
876
+ if (button.length == 0) {
877
+ this.buttons = [];
878
+ delete this.metadata.button_urls;
879
+ return this;
880
+ } else if (button.length > 2) {
881
+ throw new Error('RichPresence can only have up to 2 buttons');
882
+ }
883
+
884
+ this.buttons = [];
885
+ this.metadata.button_urls = [];
886
+
887
+ button.flat(2).forEach(b => {
888
+ if (b.name && b.url) {
889
+ this.buttons.push(b.name);
890
+ if (!URL.canParse(b.url)) throw new Error('Button url must be a valid url');
891
+ this.metadata.button_urls.push(b.url);
892
+ } else {
893
+ throw new Error('Button must have name and url');
894
+ }
895
+ });
896
+ return this;
897
+ }
898
+
899
+ /**
900
+ * The platform the activity is being played on
901
+ * @param {ActivityPlatform | null} platform Any platform
902
+ * @returns {RichPresence}
903
+ */
904
+ setPlatform(platform) {
905
+ this.platform = platform;
906
+ return this;
907
+ }
908
+
909
+ /**
910
+ * Secrets for rich presence joining and spectating (send-only)
911
+ * @param {?string} join Secrets for rich presence joining
912
+ * @returns {RichPresence}
913
+ */
914
+ setJoinSecret(join) {
915
+ this.secrets.join = join;
916
+ return this;
917
+ }
918
+
919
+ /**
920
+ * Add a button to the rich presence
921
+ * @param {string} name The name of the button
922
+ * @param {string} url The url of the button
923
+ * @returns {RichPresence}
924
+ */
925
+ addButton(name, url) {
926
+ if (!name || !url) {
927
+ throw new Error('Button must have name and url');
928
+ }
929
+ if (typeof name !== 'string') throw new Error('Button name must be a string');
930
+ if (!URL.canParse(url)) throw new Error('Button url must be a valid url');
931
+ this.buttons.push(name);
932
+ if (Array.isArray(this.metadata.button_urls)) this.metadata.button_urls.push(url);
933
+ else this.metadata.button_urls = [url];
934
+ return this;
935
+ }
936
+
937
+ /**
938
+ * Convert the rich presence to a JSON object
939
+ * @returns {Object}
940
+ */
941
+ toJSON(...props) {
942
+ return super.toJSON(
943
+ {
944
+ applicationId: 'application_id',
945
+ sessionId: 'session_id',
946
+ syncId: 'sync_id',
947
+ createdTimestamp: 'created_at',
948
+ },
949
+ ...props,
950
+ );
951
+ }
952
+
953
+ /**
954
+ * @typedef {Object} ExternalAssets
955
+ * @property {?string} url Orginal url of the image
956
+ * @property {?string} external_asset_path Proxy url of the image (Using to RPC)
957
+ */
958
+
959
+ /**
960
+ * Retrieves external assets from a RichPresence
961
+ * @param {Client} client - The Discord client instance.
962
+ * @param {Snowflake} applicationId - The application ID associated with the Rich Presence.
963
+ * @param {...string} images - 1 or 2 external image URLs (not hosted by Discord).
964
+ * @returns {Promise<ExternalAssets[]>}
965
+ */
966
+ static async getExternal(client, applicationId, ...images) {
967
+ if (!client || !client.token || !client.api) throw new Error('Client must be set');
968
+ // Check if applicationId is discord snowflake (17 , 18, 19 numbers)
969
+ if (!/^[0-9]{17,19}$/.test(applicationId)) {
970
+ throw new Error('Application id must be a Discord Snowflake');
971
+ }
972
+ // Check if images are 1 or 2
973
+ if (images.length > 2) {
974
+ throw new Error('RichPresence can only have up to 2 external images');
975
+ }
976
+ // Check if all images are valid URLs
977
+ if (images.some(image => !URL.canParse(image))) {
978
+ throw new Error('Each image must be a valid URL.');
979
+ }
980
+ const res = await client.api.applications[applicationId]['external-assets'].post({
981
+ data: {
982
+ urls: images,
983
+ },
984
+ });
985
+ return res;
986
+ }
987
+
988
+ /**
989
+ * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
990
+ * @returns {string}
991
+ */
992
+ toString() {
993
+ return this.name;
994
+ }
995
+
996
+ _clone() {
997
+ return Object.assign(Object.create(this), this);
998
+ }
999
+ }
1000
+
1001
+ /**
1002
+ * @extends {RichPresence}
1003
+ */
1004
+ class SpotifyRPC extends RichPresence {
1005
+ /**
1006
+ * Create a new RichPresence (Spotify style)
1007
+ * @param {Client} client Discord Client
1008
+ * @param {SpotifyRPC} [options] Options for the Spotify RPC
1009
+ */
1010
+ constructor(client, options = {}) {
1011
+ if (!client) throw new Error("Class constructor SpotifyRPC cannot be invoked without 'client'");
1012
+ super(client, {
1013
+ name: 'Spotify',
1014
+ type: ActivityTypes.LISTENING,
1015
+ party: {
1016
+ id: `spotify:${client.user.id}`,
1017
+ },
1018
+ id: 'spotify:1',
1019
+ flags: 48, // Sync + Play (ActivityFlags)
1020
+ ...options,
1021
+ });
1022
+ this.setup(options);
1023
+ }
1024
+ /**
1025
+ * Sets the status from a JSON object
1026
+ * @param {SpotifyRPC} options data
1027
+ * @private
1028
+ */
1029
+ setup(options) {
1030
+ /**
1031
+ * @typedef {Object} SpotifyMetadata
1032
+ * @property {string} album_id The Spotify ID of the album of the song being played
1033
+ * @property {Array<string>} artist_ids The Spotify IDs of the artists of the song being played
1034
+ * @property {string} context_uri The Spotify URI of the current player context
1035
+ */
1036
+
1037
+ /**
1038
+ * Spotify metadata
1039
+ * @type {SpotifyMetadata}
1040
+ */
1041
+ this.metadata = {
1042
+ album_id: options.metadata?.album_id || null,
1043
+ artist_ids: options.metadata?.artist_ids || [],
1044
+ context_uri: options.metadata?.context_uri || null,
1045
+ };
1046
+ }
1047
+
1048
+ /**
1049
+ * Set Spotify song id to sync with
1050
+ * @param {string} id Song id
1051
+ * @returns {SpotifyRPC}
1052
+ */
1053
+ setSongId(id) {
1054
+ this.syncId = id;
1055
+ return this;
1056
+ }
1057
+
1058
+ /**
1059
+ * Add the artist id
1060
+ * @param {string} id Artist id
1061
+ * @returns {SpotifyRPC}
1062
+ */
1063
+ addArtistId(id) {
1064
+ if (!this.metadata.artist_ids) this.metadata.artist_ids = [];
1065
+ this.metadata.artist_ids.push(id);
1066
+ return this;
1067
+ }
1068
+
1069
+ /**
1070
+ * Set the artist ids
1071
+ * @param {string | Array<string>} ids Artist ids
1072
+ * @returns {SpotifyRPC}
1073
+ */
1074
+ setArtistIds(...ids) {
1075
+ if (!ids?.length) {
1076
+ this.metadata.artist_ids = [];
1077
+ return this;
1078
+ }
1079
+ if (!this.metadata.artist_ids) this.metadata.artist_ids = [];
1080
+ ids.flat(2).forEach(id => this.metadata.artist_ids.push(id));
1081
+ return this;
1082
+ }
1083
+
1084
+ /**
1085
+ * Set the album id
1086
+ * @param {string} id Album id
1087
+ * @returns {SpotifyRPC}
1088
+ */
1089
+ setAlbumId(id) {
1090
+ this.metadata.album_id = id;
1091
+ this.metadata.context_uri = `spotify:album:${id}`;
1092
+ return this;
1093
+ }
1094
+
1095
+ toJSON() {
1096
+ return super.toJSON({ id: false, emoji: false, platform: false, buttons: false });
1097
+ }
1098
+ }
1099
+
1100
+ exports.Presence = Presence;
1101
+ exports.Activity = Activity;
1102
+ exports.RichPresenceAssets = RichPresenceAssets;
1103
+ exports.CustomStatus = CustomStatus;
1104
+ exports.RichPresence = RichPresence;
1105
+ exports.SpotifyRPC = SpotifyRPC;