@erinjs/core 1.0.0

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 (389) hide show
  1. package/core/.changeset/README.md +8 -0
  2. package/core/.changeset/community-bootstrap-release.md +17 -0
  3. package/core/.changeset/config.json +11 -0
  4. package/core/.changeset/no-changelog.js +16 -0
  5. package/core/.changeset/pre.json +17 -0
  6. package/core/.editorconfig +13 -0
  7. package/core/.gitattributes +2 -0
  8. package/core/.github/CODE_OF_CONDUCT.md +23 -0
  9. package/core/.github/FUNDING.yml +7 -0
  10. package/core/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  11. package/core/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  12. package/core/.github/PULL_REQUEST_TEMPLATE.md +16 -0
  13. package/core/.github/dependabot.yml +16 -0
  14. package/core/.github/workflows/autoapp.yml +16 -0
  15. package/core/.github/workflows/ci.yml +187 -0
  16. package/core/.github/workflows/codeql.yml +30 -0
  17. package/core/.github/workflows/deploy-docs.yml +54 -0
  18. package/core/.github/workflows/publish.yml +43 -0
  19. package/core/.lintstagedrc.json +4 -0
  20. package/core/.nvmrc +1 -0
  21. package/core/.prettierignore +8 -0
  22. package/core/.prettierrc +11 -0
  23. package/core/CONTRIBUTING.md +70 -0
  24. package/core/LICENSE +203 -0
  25. package/core/README.md +61 -0
  26. package/core/SECURITY.md +21 -0
  27. package/core/apps/docs/index.html +28 -0
  28. package/core/apps/docs/middleware.ts +21 -0
  29. package/core/apps/docs/package.json +33 -0
  30. package/core/apps/docs/public/@flux.png +0 -0
  31. package/core/apps/docs/public/docs/latest/guides.json +1420 -0
  32. package/core/apps/docs/public/docs/latest/main.json +14981 -0
  33. package/core/apps/docs/public/docs/latest/rag-index.json +1 -0
  34. package/core/apps/docs/public/docs/v1.0.5/guides.json +226 -0
  35. package/core/apps/docs/public/docs/v1.0.5/main.json +7920 -0
  36. package/core/apps/docs/public/docs/v1.0.6/guides.json +226 -0
  37. package/core/apps/docs/public/docs/v1.0.6/main.json +7920 -0
  38. package/core/apps/docs/public/docs/v1.0.7/guides.json +259 -0
  39. package/core/apps/docs/public/docs/v1.0.7/main.json +8652 -0
  40. package/core/apps/docs/public/docs/v1.0.8/guides.json +313 -0
  41. package/core/apps/docs/public/docs/v1.0.8/main.json +9618 -0
  42. package/core/apps/docs/public/docs/v1.0.9/guides.json +319 -0
  43. package/core/apps/docs/public/docs/v1.0.9/main.json +10694 -0
  44. package/core/apps/docs/public/docs/v1.1.0/guides.json +589 -0
  45. package/core/apps/docs/public/docs/v1.1.0/main.json +12576 -0
  46. package/core/apps/docs/public/docs/v1.1.2/guides.json +650 -0
  47. package/core/apps/docs/public/docs/v1.1.2/main.json +13239 -0
  48. package/core/apps/docs/public/docs/v1.1.3/guides.json +650 -0
  49. package/core/apps/docs/public/docs/v1.1.3/main.json +13239 -0
  50. package/core/apps/docs/public/docs/v1.1.4/guides.json +708 -0
  51. package/core/apps/docs/public/docs/v1.1.4/main.json +13231 -0
  52. package/core/apps/docs/public/docs/v1.1.5/guides.json +1035 -0
  53. package/core/apps/docs/public/docs/v1.1.5/main.json +13838 -0
  54. package/core/apps/docs/public/docs/v1.1.6/guides.json +1041 -0
  55. package/core/apps/docs/public/docs/v1.1.6/main.json +14313 -0
  56. package/core/apps/docs/public/docs/v1.1.8/guides.json +1047 -0
  57. package/core/apps/docs/public/docs/v1.1.8/main.json +14421 -0
  58. package/core/apps/docs/public/docs/v1.1.9/guides.json +1047 -0
  59. package/core/apps/docs/public/docs/v1.1.9/main.json +14421 -0
  60. package/core/apps/docs/public/docs/v1.2.0/guides.json +1212 -0
  61. package/core/apps/docs/public/docs/v1.2.0/main.json +14663 -0
  62. package/core/apps/docs/public/docs/v1.2.1/guides.json +1293 -0
  63. package/core/apps/docs/public/docs/v1.2.1/main.json +14828 -0
  64. package/core/apps/docs/public/docs/v1.2.2/guides.json +1293 -0
  65. package/core/apps/docs/public/docs/v1.2.2/main.json +15025 -0
  66. package/core/apps/docs/public/docs/v1.2.3/guides.json +1420 -0
  67. package/core/apps/docs/public/docs/v1.2.3/main.json +14954 -0
  68. package/core/apps/docs/public/docs/v1.2.4/guides.json +1420 -0
  69. package/core/apps/docs/public/docs/v1.2.4/main.json +14981 -0
  70. package/core/apps/docs/public/docs/versions.json +24 -0
  71. package/core/apps/docs/public/flux.png +0 -0
  72. package/core/apps/docs/public/locales/en.json +50 -0
  73. package/core/apps/docs/public/locales/guides-en.json +512 -0
  74. package/core/apps/docs/public/robots.txt +4 -0
  75. package/core/apps/docs/public/sitemap.xml +33 -0
  76. package/core/apps/docs/src/App.vue +538 -0
  77. package/core/apps/docs/src/components/ApiCategorySection.vue +42 -0
  78. package/core/apps/docs/src/components/ApiDiscordCompat.vue +65 -0
  79. package/core/apps/docs/src/components/ApiEndpointCard.vue +313 -0
  80. package/core/apps/docs/src/components/ApiSchemaBlock.vue +131 -0
  81. package/core/apps/docs/src/components/CodeBlock.vue +177 -0
  82. package/core/apps/docs/src/components/CommunityCallout.vue +90 -0
  83. package/core/apps/docs/src/components/ConstructorSection.vue +82 -0
  84. package/core/apps/docs/src/components/DocDescription.vue +40 -0
  85. package/core/apps/docs/src/components/FluxerLogo.vue +3 -0
  86. package/core/apps/docs/src/components/Footer.vue +106 -0
  87. package/core/apps/docs/src/components/GuideCodeBlock.vue +102 -0
  88. package/core/apps/docs/src/components/GuideDiscordCompat.vue +77 -0
  89. package/core/apps/docs/src/components/GuideDiscordCompatCallout.vue +83 -0
  90. package/core/apps/docs/src/components/GuideTable.vue +77 -0
  91. package/core/apps/docs/src/components/GuideTip.vue +38 -0
  92. package/core/apps/docs/src/components/MethodsSection.vue +195 -0
  93. package/core/apps/docs/src/components/ParamsTable.vue +70 -0
  94. package/core/apps/docs/src/components/PropertiesSection.vue +143 -0
  95. package/core/apps/docs/src/components/SearchBar.vue +76 -0
  96. package/core/apps/docs/src/components/SearchModal.vue +361 -0
  97. package/core/apps/docs/src/components/SidebarNav.vue +225 -0
  98. package/core/apps/docs/src/components/SponsorBanner.vue +153 -0
  99. package/core/apps/docs/src/components/TypeSignature.vue +187 -0
  100. package/core/apps/docs/src/components/VersionPicker.vue +191 -0
  101. package/core/apps/docs/src/composables/useSearchIndex.ts +144 -0
  102. package/core/apps/docs/src/composables/useVersionedPath.ts +20 -0
  103. package/core/apps/docs/src/data/apiEndpoints.ts +1073 -0
  104. package/core/apps/docs/src/data/changelog.ts +717 -0
  105. package/core/apps/docs/src/data/guides.ts +2362 -0
  106. package/core/apps/docs/src/env.d.ts +7 -0
  107. package/core/apps/docs/src/locales/guides-en.json +512 -0
  108. package/core/apps/docs/src/main.ts +27 -0
  109. package/core/apps/docs/src/pages/ApiReferenceLayout.vue +175 -0
  110. package/core/apps/docs/src/pages/ApiReferencePage.vue +128 -0
  111. package/core/apps/docs/src/pages/Changelog.vue +288 -0
  112. package/core/apps/docs/src/pages/ClassPage.vue +319 -0
  113. package/core/apps/docs/src/pages/ClassesList.vue +100 -0
  114. package/core/apps/docs/src/pages/DocsLayout.vue +127 -0
  115. package/core/apps/docs/src/pages/GuidePage.vue +279 -0
  116. package/core/apps/docs/src/pages/GuidesIndex.vue +166 -0
  117. package/core/apps/docs/src/pages/GuidesLayout.vue +245 -0
  118. package/core/apps/docs/src/pages/Home.vue +125 -0
  119. package/core/apps/docs/src/pages/NotFound.vue +57 -0
  120. package/core/apps/docs/src/pages/TypedefPage.vue +230 -0
  121. package/core/apps/docs/src/pages/TypedefsList.vue +168 -0
  122. package/core/apps/docs/src/pages/VersionLayout.vue +15 -0
  123. package/core/apps/docs/src/router.ts +73 -0
  124. package/core/apps/docs/src/stores/docs.ts +54 -0
  125. package/core/apps/docs/src/stores/guides.ts +53 -0
  126. package/core/apps/docs/src/stores/version.ts +67 -0
  127. package/core/apps/docs/src/styles/main.css +278 -0
  128. package/core/apps/docs/src/styles/prism.css +95 -0
  129. package/core/apps/docs/src/types/doc-schema.ts +112 -0
  130. package/core/apps/docs/tsconfig.json +17 -0
  131. package/core/apps/docs/tsconfig.node.json +10 -0
  132. package/core/apps/docs/vite.config.d.ts +2 -0
  133. package/core/apps/docs/vite.config.js +26 -0
  134. package/core/apps/docs/vite.config.ts +28 -0
  135. package/core/apps/docs-vitepress/.vitepress/config.ts +141 -0
  136. package/core/apps/docs-vitepress/api-data/latest/main.json +15035 -0
  137. package/core/apps/docs-vitepress/api-data/v1.2.4/main.json +15035 -0
  138. package/core/apps/docs-vitepress/api-data/versions.json +6 -0
  139. package/core/apps/docs-vitepress/index.md +15 -0
  140. package/core/apps/docs-vitepress/package-lock.json +2924 -0
  141. package/core/apps/docs-vitepress/package.json +20 -0
  142. package/core/apps/docs-vitepress/public/CNAME +1 -0
  143. package/core/apps/docs-vitepress/scripts/generate-api.ts +243 -0
  144. package/core/apps/docs-vitepress/scripts/migrate-guides.ts +129 -0
  145. package/core/apps/docs-vitepress/tsconfig.json +11 -0
  146. package/core/apps/docs-vitepress/v/latest/guides/attachments-by-url.md +57 -0
  147. package/core/apps/docs-vitepress/v/latest/guides/attachments.md +62 -0
  148. package/core/apps/docs-vitepress/v/latest/guides/basic-bot.md +49 -0
  149. package/core/apps/docs-vitepress/v/latest/guides/channels.md +180 -0
  150. package/core/apps/docs-vitepress/v/latest/guides/deprecated-apis.md +58 -0
  151. package/core/apps/docs-vitepress/v/latest/guides/discord-js-compatibility.md +42 -0
  152. package/core/apps/docs-vitepress/v/latest/guides/editing-embeds.md +65 -0
  153. package/core/apps/docs-vitepress/v/latest/guides/embed-media.md +87 -0
  154. package/core/apps/docs-vitepress/v/latest/guides/embeds.md +166 -0
  155. package/core/apps/docs-vitepress/v/latest/guides/emojis.md +77 -0
  156. package/core/apps/docs-vitepress/v/latest/guides/events.md +202 -0
  157. package/core/apps/docs-vitepress/v/latest/guides/gifs.md +47 -0
  158. package/core/apps/docs-vitepress/v/latest/guides/installation.md +10 -0
  159. package/core/apps/docs-vitepress/v/latest/guides/moderation.md +89 -0
  160. package/core/apps/docs-vitepress/v/latest/guides/permissions.md +130 -0
  161. package/core/apps/docs-vitepress/v/latest/guides/prefix-commands.md +41 -0
  162. package/core/apps/docs-vitepress/v/latest/guides/profile-urls.md +58 -0
  163. package/core/apps/docs-vitepress/v/latest/guides/reactions.md +69 -0
  164. package/core/apps/docs-vitepress/v/latest/guides/roles.md +130 -0
  165. package/core/apps/docs-vitepress/v/latest/guides/sending-without-reply.md +172 -0
  166. package/core/apps/docs-vitepress/v/latest/guides/voice.md +109 -0
  167. package/core/apps/docs-vitepress/v/latest/guides/wait-for-guilds.md +37 -0
  168. package/core/apps/docs-vitepress/v/latest/guides/webhook-attachments-embeds.md +73 -0
  169. package/core/apps/docs-vitepress/v/latest/guides/webhooks.md +131 -0
  170. package/core/eslint.config.js +80 -0
  171. package/core/examples/.env.example +22 -0
  172. package/core/examples/README.md +68 -0
  173. package/core/examples/first-steps-bot.js +118 -0
  174. package/core/examples/minimal-bot.js +17 -0
  175. package/core/examples/moderation-bot.js +209 -0
  176. package/core/examples/package.json +14 -0
  177. package/core/examples/ping-bot.js +1146 -0
  178. package/core/examples/reaction-bot.js +70 -0
  179. package/core/examples/reaction-roles-bot.js +140 -0
  180. package/core/examples/webhook-bot.js +239 -0
  181. package/core/flux.png +0 -0
  182. package/core/package.json +78 -0
  183. package/core/packages/builders/package.json +51 -0
  184. package/core/packages/builders/src/index.ts +13 -0
  185. package/core/packages/builders/src/messages/AttachmentBuilder.test.ts +79 -0
  186. package/core/packages/builders/src/messages/AttachmentBuilder.ts +69 -0
  187. package/core/packages/builders/src/messages/EmbedBuilder.test.ts +266 -0
  188. package/core/packages/builders/src/messages/EmbedBuilder.ts +239 -0
  189. package/core/packages/builders/src/messages/MessagePayload.test.ts +118 -0
  190. package/core/packages/builders/src/messages/MessagePayload.ts +122 -0
  191. package/core/packages/builders/tsconfig.json +9 -0
  192. package/core/packages/builders/tsup.config.ts +9 -0
  193. package/core/packages/builders/vitest.config.ts +9 -0
  194. package/core/packages/collection/package.json +47 -0
  195. package/core/packages/collection/src/Collection.test.ts +232 -0
  196. package/core/packages/collection/src/Collection.ts +196 -0
  197. package/core/packages/collection/src/index.ts +1 -0
  198. package/core/packages/collection/tsconfig.json +9 -0
  199. package/core/packages/collection/tsup.config.ts +9 -0
  200. package/core/packages/collection/vitest.config.ts +9 -0
  201. package/core/packages/docgen/package.json +26 -0
  202. package/core/packages/docgen/src/extract.ts +262 -0
  203. package/core/packages/docgen/src/formatType.ts +24 -0
  204. package/core/packages/docgen/src/index.ts +103 -0
  205. package/core/packages/docgen/src/schema.ts +100 -0
  206. package/core/packages/docgen/src/visitor.ts +147 -0
  207. package/core/packages/docgen/tsconfig.json +9 -0
  208. package/core/packages/docgen/tsup.config.ts +9 -0
  209. package/core/packages/fluxer-core/README.md +26 -0
  210. package/core/packages/fluxer-core/package.json +60 -0
  211. package/core/packages/fluxer-core/src/client/ChannelManager.ts +143 -0
  212. package/core/packages/fluxer-core/src/client/Client.gateway.test.ts +84 -0
  213. package/core/packages/fluxer-core/src/client/Client.resolveEmoji.test.ts +45 -0
  214. package/core/packages/fluxer-core/src/client/Client.ts +558 -0
  215. package/core/packages/fluxer-core/src/client/ClientUser.ts +40 -0
  216. package/core/packages/fluxer-core/src/client/EventHandlerRegistry.ts +469 -0
  217. package/core/packages/fluxer-core/src/client/GuildManager.ts +79 -0
  218. package/core/packages/fluxer-core/src/client/GuildMemberManager.ts +91 -0
  219. package/core/packages/fluxer-core/src/client/MessageManager.ts +58 -0
  220. package/core/packages/fluxer-core/src/client/UsersManager.ts +122 -0
  221. package/core/packages/fluxer-core/src/errors/ErrorCodes.test.ts +19 -0
  222. package/core/packages/fluxer-core/src/errors/ErrorCodes.ts +12 -0
  223. package/core/packages/fluxer-core/src/errors/FluxerError.test.ts +32 -0
  224. package/core/packages/fluxer-core/src/errors/FluxerError.ts +15 -0
  225. package/core/packages/fluxer-core/src/index.ts +85 -0
  226. package/core/packages/fluxer-core/src/structures/Base.ts +7 -0
  227. package/core/packages/fluxer-core/src/structures/Channel.ts +508 -0
  228. package/core/packages/fluxer-core/src/structures/Guild.test.ts +189 -0
  229. package/core/packages/fluxer-core/src/structures/Guild.ts +734 -0
  230. package/core/packages/fluxer-core/src/structures/GuildBan.ts +35 -0
  231. package/core/packages/fluxer-core/src/structures/GuildEmoji.ts +57 -0
  232. package/core/packages/fluxer-core/src/structures/GuildMember.test.ts +203 -0
  233. package/core/packages/fluxer-core/src/structures/GuildMember.ts +213 -0
  234. package/core/packages/fluxer-core/src/structures/GuildMemberRoleManager.ts +121 -0
  235. package/core/packages/fluxer-core/src/structures/GuildSticker.ts +56 -0
  236. package/core/packages/fluxer-core/src/structures/Invite.test.ts +103 -0
  237. package/core/packages/fluxer-core/src/structures/Invite.ts +121 -0
  238. package/core/packages/fluxer-core/src/structures/Message.test.ts +109 -0
  239. package/core/packages/fluxer-core/src/structures/Message.ts +397 -0
  240. package/core/packages/fluxer-core/src/structures/MessageReaction.ts +72 -0
  241. package/core/packages/fluxer-core/src/structures/PartialMessage.ts +12 -0
  242. package/core/packages/fluxer-core/src/structures/Role.test.ts +77 -0
  243. package/core/packages/fluxer-core/src/structures/Role.ts +112 -0
  244. package/core/packages/fluxer-core/src/structures/User.test.ts +110 -0
  245. package/core/packages/fluxer-core/src/structures/User.ts +109 -0
  246. package/core/packages/fluxer-core/src/structures/Webhook.test.ts +109 -0
  247. package/core/packages/fluxer-core/src/structures/Webhook.ts +258 -0
  248. package/core/packages/fluxer-core/src/util/Constants.test.ts +16 -0
  249. package/core/packages/fluxer-core/src/util/Constants.ts +7 -0
  250. package/core/packages/fluxer-core/src/util/Events.ts +46 -0
  251. package/core/packages/fluxer-core/src/util/MessageCollector.ts +87 -0
  252. package/core/packages/fluxer-core/src/util/Options.ts +33 -0
  253. package/core/packages/fluxer-core/src/util/ReactionCollector.ts +116 -0
  254. package/core/packages/fluxer-core/src/util/cdn.test.ts +108 -0
  255. package/core/packages/fluxer-core/src/util/cdn.ts +130 -0
  256. package/core/packages/fluxer-core/src/util/guildUtils.ts +33 -0
  257. package/core/packages/fluxer-core/src/util/messageUtils.test.ts +74 -0
  258. package/core/packages/fluxer-core/src/util/messageUtils.ts +119 -0
  259. package/core/packages/fluxer-core/src/util/permissions.test.ts +95 -0
  260. package/core/packages/fluxer-core/src/util/permissions.ts +43 -0
  261. package/core/packages/fluxer-core/tsconfig.json +9 -0
  262. package/core/packages/fluxer-core/tsup.config.ts +9 -0
  263. package/core/packages/fluxer-core/vitest.config.ts +9 -0
  264. package/core/packages/rest/package.json +52 -0
  265. package/core/packages/rest/src/REST.test.ts +64 -0
  266. package/core/packages/rest/src/REST.ts +90 -0
  267. package/core/packages/rest/src/RateLimitManager.test.ts +71 -0
  268. package/core/packages/rest/src/RateLimitManager.ts +60 -0
  269. package/core/packages/rest/src/RequestManager.test.ts +87 -0
  270. package/core/packages/rest/src/RequestManager.ts +172 -0
  271. package/core/packages/rest/src/errors/FluxerAPIError.test.ts +57 -0
  272. package/core/packages/rest/src/errors/FluxerAPIError.ts +21 -0
  273. package/core/packages/rest/src/errors/HTTPError.test.ts +55 -0
  274. package/core/packages/rest/src/errors/HTTPError.ts +25 -0
  275. package/core/packages/rest/src/errors/RateLimitError.test.ts +41 -0
  276. package/core/packages/rest/src/errors/RateLimitError.ts +15 -0
  277. package/core/packages/rest/src/errors/index.ts +3 -0
  278. package/core/packages/rest/src/index.ts +6 -0
  279. package/core/packages/rest/src/utils/constants.test.ts +31 -0
  280. package/core/packages/rest/src/utils/constants.ts +5 -0
  281. package/core/packages/rest/src/utils/files.test.ts +37 -0
  282. package/core/packages/rest/src/utils/files.ts +75 -0
  283. package/core/packages/rest/tsconfig.json +9 -0
  284. package/core/packages/rest/tsup.config.ts +9 -0
  285. package/core/packages/rest/vitest.config.ts +9 -0
  286. package/core/packages/types/package.json +46 -0
  287. package/core/packages/types/src/api/ban.ts +8 -0
  288. package/core/packages/types/src/api/channel.ts +65 -0
  289. package/core/packages/types/src/api/embed.ts +82 -0
  290. package/core/packages/types/src/api/emoji.ts +12 -0
  291. package/core/packages/types/src/api/errors.ts +68 -0
  292. package/core/packages/types/src/api/gateway.ts +14 -0
  293. package/core/packages/types/src/api/guild.ts +123 -0
  294. package/core/packages/types/src/api/index.ts +15 -0
  295. package/core/packages/types/src/api/instance.ts +32 -0
  296. package/core/packages/types/src/api/interaction.ts +26 -0
  297. package/core/packages/types/src/api/invite.ts +28 -0
  298. package/core/packages/types/src/api/message.ts +140 -0
  299. package/core/packages/types/src/api/role.ts +41 -0
  300. package/core/packages/types/src/api/sticker.ts +14 -0
  301. package/core/packages/types/src/api/user.ts +79 -0
  302. package/core/packages/types/src/api/webhook.ts +41 -0
  303. package/core/packages/types/src/common/index.ts +1 -0
  304. package/core/packages/types/src/common/snowflake.test.ts +9 -0
  305. package/core/packages/types/src/common/snowflake.ts +8 -0
  306. package/core/packages/types/src/gateway/events.ts +189 -0
  307. package/core/packages/types/src/gateway/index.ts +3 -0
  308. package/core/packages/types/src/gateway/opcodes.ts +17 -0
  309. package/core/packages/types/src/gateway/payloads.ts +481 -0
  310. package/core/packages/types/src/index.ts +4 -0
  311. package/core/packages/types/src/rest/index.ts +1 -0
  312. package/core/packages/types/src/rest/routes.test.ts +169 -0
  313. package/core/packages/types/src/rest/routes.ts +109 -0
  314. package/core/packages/types/tsconfig.json +9 -0
  315. package/core/packages/types/tsup.config.ts +9 -0
  316. package/core/packages/types/vitest.config.ts +9 -0
  317. package/core/packages/util/package.json +51 -0
  318. package/core/packages/util/src/BitField.test.ts +96 -0
  319. package/core/packages/util/src/BitField.ts +105 -0
  320. package/core/packages/util/src/MessageFlagsBitField.test.ts +42 -0
  321. package/core/packages/util/src/MessageFlagsBitField.ts +20 -0
  322. package/core/packages/util/src/PermissionsBitField.test.ts +79 -0
  323. package/core/packages/util/src/PermissionsBitField.ts +97 -0
  324. package/core/packages/util/src/SnowflakeUtil.test.ts +69 -0
  325. package/core/packages/util/src/SnowflakeUtil.ts +65 -0
  326. package/core/packages/util/src/UserFlagsBitField.test.ts +39 -0
  327. package/core/packages/util/src/UserFlagsBitField.ts +48 -0
  328. package/core/packages/util/src/deprecation.test.ts +44 -0
  329. package/core/packages/util/src/deprecation.ts +28 -0
  330. package/core/packages/util/src/emojiShortcodes.generated.ts +5 -0
  331. package/core/packages/util/src/emojiShortcodes.test.ts +41 -0
  332. package/core/packages/util/src/emojiShortcodes.ts +22 -0
  333. package/core/packages/util/src/formatters.test.ts +65 -0
  334. package/core/packages/util/src/formatters.ts +35 -0
  335. package/core/packages/util/src/index.ts +34 -0
  336. package/core/packages/util/src/resolvers.test.ts +198 -0
  337. package/core/packages/util/src/resolvers.ts +127 -0
  338. package/core/packages/util/src/tenorUtils.test.ts +75 -0
  339. package/core/packages/util/src/tenorUtils.ts +86 -0
  340. package/core/packages/util/tsconfig.json +9 -0
  341. package/core/packages/util/tsup.config.ts +9 -0
  342. package/core/packages/util/vitest.config.ts +9 -0
  343. package/core/packages/voice/README.md +42 -0
  344. package/core/packages/voice/package.json +67 -0
  345. package/core/packages/voice/src/LiveKitRtcConnection.receive.test.ts +24 -0
  346. package/core/packages/voice/src/LiveKitRtcConnection.ts +1767 -0
  347. package/core/packages/voice/src/VoiceConnection.ts +413 -0
  348. package/core/packages/voice/src/VoiceManager.receive.test.ts +61 -0
  349. package/core/packages/voice/src/VoiceManager.test.ts +44 -0
  350. package/core/packages/voice/src/VoiceManager.ts +503 -0
  351. package/core/packages/voice/src/exports.test.ts +38 -0
  352. package/core/packages/voice/src/index.ts +51 -0
  353. package/core/packages/voice/src/livekit.test.ts +48 -0
  354. package/core/packages/voice/src/livekit.ts +33 -0
  355. package/core/packages/voice/src/mp4box.d.ts +32 -0
  356. package/core/packages/voice/src/opusUtils.test.ts +29 -0
  357. package/core/packages/voice/src/opusUtils.ts +86 -0
  358. package/core/packages/voice/src/streamPreviewPlaceholder.test.ts +16 -0
  359. package/core/packages/voice/src/streamPreviewPlaceholder.ts +8 -0
  360. package/core/packages/voice/src/ws.d.ts +1 -0
  361. package/core/packages/voice/tsconfig.json +5 -0
  362. package/core/packages/voice/tsup.config.ts +10 -0
  363. package/core/packages/voice/vitest.config.ts +9 -0
  364. package/core/packages/ws/package.json +52 -0
  365. package/core/packages/ws/src/WebSocketManager.ts +130 -0
  366. package/core/packages/ws/src/WebSocketShard.ts +296 -0
  367. package/core/packages/ws/src/index.ts +12 -0
  368. package/core/packages/ws/src/utils/constants.test.ts +46 -0
  369. package/core/packages/ws/src/utils/constants.ts +22 -0
  370. package/core/packages/ws/src/utils/getWebSocket.ts +55 -0
  371. package/core/packages/ws/src/ws.d.ts +10 -0
  372. package/core/packages/ws/tsconfig.json +9 -0
  373. package/core/packages/ws/tsup.config.ts +9 -0
  374. package/core/pnpm-lock.yaml +7033 -0
  375. package/core/pnpm-workspace.yaml +4 -0
  376. package/core/scripts/generate-ai-rag.ts +240 -0
  377. package/core/scripts/generate-docs.ts +143 -0
  378. package/core/scripts/generate-emoji-shortcodes.ts +58 -0
  379. package/core/scripts/generate-types.ts +6 -0
  380. package/core/scripts/publish-ordered.js +63 -0
  381. package/core/scripts/test-cjs-require.mjs +43 -0
  382. package/core/scripts/test-esm-imports.mjs +42 -0
  383. package/core/scripts/test-package-exports.mjs +98 -0
  384. package/core/scripts/test-smoke.mjs +103 -0
  385. package/core/tsconfig.json +18 -0
  386. package/core/turbo.json +30 -0
  387. package/core/vitest.config.ts +17 -0
  388. package/core/wrangler.jsonc +9 -0
  389. package/package.json +26 -0
@@ -0,0 +1,397 @@
1
+ import { Client } from '../client/Client.js';
2
+ import { Base } from './Base.js';
3
+ import { Collection } from '@erinjs/collection';
4
+ import {
5
+ APIMessage,
6
+ APIMessageAttachment,
7
+ APIMessageReaction,
8
+ APIMessageSticker,
9
+ APIMessageReference,
10
+ APIMessageSnapshot,
11
+ APIMessageCall,
12
+ APIEmbed,
13
+ APIUserPartial,
14
+ } from '@erinjs/types';
15
+ import { MessageType, MessageFlags, Routes } from '@erinjs/types';
16
+ import { EmbedBuilder } from '@erinjs/builders';
17
+ import { User } from './User.js';
18
+ import { Channel, TextChannel, DMChannel, GuildChannel } from './Channel.js';
19
+ import { Guild } from './Guild.js';
20
+
21
+ import {
22
+ buildSendBody,
23
+ resolveMessageFiles,
24
+ type MessageSendOptions,
25
+ SendBodyResult,
26
+ ResolvedMessageFile,
27
+ } from '../util/messageUtils.js';
28
+ import { ReactionCollector } from '../util/ReactionCollector.js';
29
+ import { ReactionCollectorOptions } from '../util/ReactionCollector.js';
30
+
31
+ /** Options for editing a message (content and/or embeds). */
32
+ export interface MessageEditOptions {
33
+ /** New text content */
34
+ content?: string;
35
+ /** New embeds (replaces existing) */
36
+ embeds?: (APIEmbed | EmbedBuilder)[];
37
+ }
38
+
39
+ export type MessagePayload = {
40
+ files?: ResolvedMessageFile[];
41
+ body: SendBodyResult & { message_reference?: APIMessageReference; flags?: number };
42
+ };
43
+
44
+ /** Options for message.reply() — ping toggle and reply-to-different-message. */
45
+ export interface ReplyOptions {
46
+ /** Whether to ping the replied-to user (default true). Use false to suppress the mention notification. */
47
+ ping?: boolean;
48
+ /** Reply to a different message instead of this one. Default: this message. */
49
+ replyTo?: Message | { channelId: string; messageId: string };
50
+ }
51
+
52
+ /** Re-export for convenience. */
53
+ export type { MessageSendOptions } from '../util/messageUtils.js';
54
+
55
+ /** Represents a message in a channel. */
56
+ export class Message extends Base {
57
+ readonly client: Client;
58
+ readonly id: string;
59
+ readonly channelId: string;
60
+ readonly guildId: string | null;
61
+ readonly author: User;
62
+ content: string;
63
+ readonly createdAt: Date;
64
+ readonly editedAt: Date | null;
65
+ pinned: boolean;
66
+ readonly attachments: Collection<string, APIMessageAttachment>;
67
+ readonly type: MessageType;
68
+ readonly flags: number;
69
+ readonly mentionEveryone: boolean;
70
+ readonly tts: boolean;
71
+ readonly embeds: APIEmbed[];
72
+ readonly stickers: APIMessageSticker[];
73
+ readonly reactions: APIMessageReaction[];
74
+ readonly messageReference: APIMessageReference | null;
75
+ readonly messageSnapshots: APIMessageSnapshot[];
76
+ readonly call: APIMessageCall | null;
77
+ readonly referencedMessage: Message | null;
78
+ /** Webhook ID if this message was sent via webhook. Null otherwise. */
79
+ readonly webhookId: string | null;
80
+ /** Users mentioned in this message. */
81
+ readonly mentions: User[];
82
+ /** Role IDs mentioned in this message. */
83
+ readonly mentionRoles: string[];
84
+ /** Client-side nonce for acknowledgment. Null if not provided. */
85
+ readonly nonce: string | null;
86
+
87
+ /**
88
+ * Channel where this message was sent. Resolved from cache; null if not cached.
89
+ * Messages can only exist in text-based channels (text, DM, announcement), so this always has send() when non-null.
90
+ */
91
+ get channel(): (TextChannel | DMChannel | GuildChannel) | null {
92
+ return (this.client.channels.get(this.channelId) ?? null) as
93
+ | (TextChannel | DMChannel | GuildChannel)
94
+ | null;
95
+ }
96
+
97
+ /** Guild where this message was sent. Resolved from cache; null for DMs or if not cached. */
98
+ get guild(): Guild | null {
99
+ return this.guildId ? (this.client.guilds.get(this.guildId) ?? null) : null;
100
+ }
101
+
102
+ /**
103
+ * Resolve the channel (from cache or API). Use when you need the channel and it may not be cached.
104
+ * @returns The channel
105
+ * @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
106
+ */
107
+ async resolveChannel(): Promise<Channel> {
108
+ return this.client.channels.resolve(this.channelId);
109
+ }
110
+
111
+ /**
112
+ * Resolve the guild (from cache or API). Returns null for DMs.
113
+ * @returns The guild, or null if this is a DM or guild not found
114
+ */
115
+ async resolveGuild(): Promise<Guild | null> {
116
+ return this.guildId ? this.client.guilds.resolve(this.guildId) : null;
117
+ }
118
+
119
+ /** @param data - API message from POST/PATCH /channels/{id}/messages or gateway MESSAGE_CREATE */
120
+ constructor(client: Client, data: APIMessage) {
121
+ super();
122
+ this.client = client;
123
+ this.id = data.id;
124
+ this.channelId = data.channel_id;
125
+ this.guildId = data.guild_id ?? null;
126
+ this.author = client.getOrCreateUser(data.author);
127
+ this.content = data.content;
128
+ this.createdAt = new Date(data.timestamp);
129
+ this.editedAt = data.edited_timestamp ? new Date(data.edited_timestamp) : null;
130
+ this.pinned = data.pinned;
131
+ this.attachments = new Collection();
132
+ for (const a of data.attachments ?? []) this.attachments.set(a.id, a);
133
+ this.type = (data.type ?? MessageType.Default) as MessageType;
134
+ this.flags = data.flags ?? 0;
135
+ this.mentionEveryone = data.mention_everyone ?? false;
136
+ this.tts = data.tts ?? false;
137
+ this.embeds = data.embeds ?? [];
138
+ this.stickers = data.stickers ?? [];
139
+ this.reactions = data.reactions ?? [];
140
+ this.messageReference = data.message_reference ?? null;
141
+ this.messageSnapshots = data.message_snapshots ?? [];
142
+ this.call = data.call ?? null;
143
+ this.referencedMessage = data.referenced_message
144
+ ? new Message(client, data.referenced_message)
145
+ : null;
146
+ this.webhookId = data.webhook_id ?? null;
147
+ this.mentions = (data.mentions ?? []).map((u) => client.getOrCreateUser(u));
148
+ this.mentionRoles = data.mention_roles ?? [];
149
+ this.nonce = data.nonce ?? null;
150
+ }
151
+
152
+ /**
153
+ * Send a message to this channel without replying. Use when you want a standalone message.
154
+ * @param options - Text content or object with content, embeds, and/or files
155
+ * @example
156
+ * await message.send('Pong!');
157
+ * await message.send({ embeds: [embed] }); // EmbedBuilder auto-converted
158
+ * await message.send({ content: 'File', files: [{ name: 'data.txt', data }] });
159
+ */
160
+ async send(options: string | MessageSendOptions): Promise<Message> {
161
+ const payload = await Message._createMessageBody(options);
162
+ return this._send(payload);
163
+ }
164
+
165
+ /**
166
+ * Send a message to a specific channel. Use for logging, forwarding, or sending to another channel in the guild.
167
+ * @param channelId - Snowflake of the target channel (e.g. log channel ID)
168
+ * @param options - Text content or object with content and/or embeds
169
+ * @example
170
+ * await message.sendTo(logChannelId, 'User ' + message.author.username + ' said: ' + message.content);
171
+ * await message.sendTo(announceChannelId, { embeds: [embed] });
172
+ */
173
+ async sendTo(channelId: string, options: MessageSendOptions): Promise<Message> {
174
+ return this.client.channels.send(channelId, options);
175
+ }
176
+
177
+ /**
178
+ * Reply to this message (shows as a reply in the client).
179
+ * @param options - Text content or object with content, embeds, and/or reply options (ping, replyTo)
180
+ * @example
181
+ * await message.reply('Pong!');
182
+ * await message.reply({ embeds: [embed] });
183
+ * await message.reply('No ping!', { ping: false });
184
+ * await message.reply({ content: 'Reply to other', replyTo: otherMessage });
185
+ */
186
+ async reply(
187
+ options: string | (MessageSendOptions & ReplyOptions),
188
+ replyOptions?: ReplyOptions,
189
+ ): Promise<Message> {
190
+ const opts = typeof options === 'string' ? { content: options } : options;
191
+ const mergedReply: ReplyOptions | undefined =
192
+ (replyOptions ?? (opts.ping !== undefined || opts.replyTo !== undefined))
193
+ ? { ping: opts.ping, replyTo: opts.replyTo }
194
+ : undefined;
195
+
196
+ const refMessage = mergedReply?.replyTo ?? this;
197
+ const ref =
198
+ refMessage instanceof Message
199
+ ? {
200
+ channel_id: refMessage.channelId,
201
+ message_id: refMessage.id,
202
+ guild_id: refMessage.guildId ?? undefined,
203
+ }
204
+ : {
205
+ channel_id: refMessage.channelId,
206
+ message_id: refMessage.messageId,
207
+ guild_id: undefined as string | undefined,
208
+ };
209
+
210
+ const payload = await Message._createMessageBody(opts, ref, mergedReply?.ping !== false);
211
+ return this._send(payload);
212
+ }
213
+
214
+ /** Exposed for testing purposes, use Message.reply() or send() for normal use */
215
+ static async _createMessageBody(
216
+ content: string | MessageSendOptions,
217
+ referenced_message?: { channel_id: string; message_id: string; guild_id?: string },
218
+ ping?: boolean,
219
+ ): Promise<MessagePayload> {
220
+ if (typeof content === 'string') {
221
+ if (content.length === 0) {
222
+ throw new RangeError('Cannot send an empty message');
223
+ }
224
+ content = { content };
225
+ }
226
+ const base = buildSendBody(content);
227
+ const files = content.files?.length ? await resolveMessageFiles(content.files) : undefined;
228
+ const body: MessagePayload['body'] = { ...base };
229
+ if (referenced_message) {
230
+ body.message_reference = referenced_message;
231
+ if (ping === false) {
232
+ body.flags = (body.flags ?? 0) | MessageFlags.SuppressNotifications;
233
+ }
234
+ }
235
+ return { files, body };
236
+ }
237
+
238
+ async _send(payload: MessagePayload): Promise<Message> {
239
+ const data = await this.client.rest.post<APIMessage>(
240
+ Routes.channelMessages(this.channelId),
241
+ payload,
242
+ );
243
+ this.client._addMessageToCache(this.channelId, data);
244
+ return new Message(this.client, data);
245
+ }
246
+
247
+ /**
248
+ * Edit this message. Only the author (or admins) can edit.
249
+ * @param options - New content and/or embeds
250
+ */
251
+ async edit(options: MessageEditOptions): Promise<Message> {
252
+ const body: { content?: string; embeds?: APIEmbed[] } = {};
253
+ if (options.content !== undefined) body.content = options.content;
254
+ if (options.embeds?.length) {
255
+ body.embeds = options.embeds.map((e) => (e instanceof EmbedBuilder ? e.toJSON() : e));
256
+ }
257
+ const data = await this.client.rest.patch(Routes.channelMessage(this.channelId, this.id), {
258
+ body,
259
+ });
260
+ return new Message(this.client, data as APIMessage);
261
+ }
262
+
263
+ /**
264
+ * Create a reaction collector for this message.
265
+ * Collects reactions matching the filter until time expires or max is reached.
266
+ * @param options - Filter, time (ms), and max count
267
+ * @example
268
+ * const collector = message.createReactionCollector({ filter: (r, u) => u.id === userId, time: 10000 });
269
+ * collector.on('collect', (reaction, user) => console.log(user.username, 'reacted with', reaction.emoji.name));
270
+ * collector.on('end', (collected, reason) => { ... });
271
+ */
272
+ createReactionCollector(options?: ReactionCollectorOptions): ReactionCollector {
273
+ return new ReactionCollector(this.client, this.id, this.channelId, options);
274
+ }
275
+
276
+ /**
277
+ * Re-fetch this message from the API to get the latest content, embeds, reactions, etc.
278
+ * Use when you have a stale Message (e.g. from an old event or cache) and need fresh data.
279
+ * @returns The updated message
280
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message was deleted or does not exist
281
+ * @example
282
+ * const updated = await message.fetch();
283
+ * console.log('Latest content:', updated.content);
284
+ */
285
+ async fetch(): Promise<Message> {
286
+ return this.client.channels.fetchMessage(this.channelId, this.id);
287
+ }
288
+
289
+ /** Delete this message. */
290
+ async delete(): Promise<void> {
291
+ await this.client.rest.delete(Routes.channelMessage(this.channelId, this.id));
292
+ }
293
+
294
+ /**
295
+ * Delete a specific attachment from this message.
296
+ * DELETE /channels/{id}/messages/{id}/attachments/{attachmentId}.
297
+ */
298
+ async deleteAttachment(attachmentId: string): Promise<void> {
299
+ await this.client.rest.delete(
300
+ Routes.channelMessageAttachment(this.channelId, this.id, attachmentId),
301
+ { auth: true },
302
+ );
303
+ this.attachments.delete(attachmentId);
304
+ }
305
+
306
+ /** Pin this message to the channel. Requires Manage Messages permission. */
307
+ async pin(): Promise<void> {
308
+ await this.client.rest.put(Routes.channelPinMessage(this.channelId, this.id));
309
+ this.pinned = true;
310
+ }
311
+
312
+ /** Unpin this message from the channel. Requires Manage Messages permission. */
313
+ async unpin(): Promise<void> {
314
+ await this.client.rest.delete(Routes.channelPinMessage(this.channelId, this.id));
315
+ this.pinned = false;
316
+ }
317
+
318
+ /**
319
+ * Format emoji for reaction API: unicode string or "name:id" for custom.
320
+ * For string resolution (e.g. :name:), use client.resolveEmoji; Message methods resolve automatically when guildId is available.
321
+ */
322
+ private static formatEmoji(emoji: string | { name: string; id: string }): string {
323
+ if (typeof emoji === 'string') return emoji;
324
+ return `${emoji.name}:${emoji.id}`;
325
+ }
326
+
327
+ private resolveEmojiForReaction(
328
+ emoji: string | { name: string; id?: string; animated?: boolean },
329
+ ): Promise<string> {
330
+ return this.client.resolveEmoji(emoji, this.guildId);
331
+ }
332
+
333
+ /**
334
+ * Add a reaction to this message (as the bot).
335
+ * @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`
336
+ */
337
+ async react(emoji: string | { name: string; id?: string; animated?: boolean }): Promise<void> {
338
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
339
+ const route = `${Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/@me`;
340
+ await this.client.rest.put(route);
341
+ }
342
+
343
+ /**
344
+ * Remove the bot's reaction, or a specific user's reaction if userId is provided.
345
+ * @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`
346
+ * @param userId - If provided, removes that user's reaction (requires moderator permissions)
347
+ */
348
+ async removeReaction(
349
+ emoji: string | { name: string; id?: string; animated?: boolean },
350
+ userId?: string,
351
+ ): Promise<void> {
352
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
353
+ const route = `${Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/${userId ?? '@me'}`;
354
+ await this.client.rest.delete(route);
355
+ }
356
+
357
+ /**
358
+ * Remove all reactions from this message.
359
+ * Requires moderator permissions.
360
+ */
361
+ async removeAllReactions(): Promise<void> {
362
+ await this.client.rest.delete(Routes.channelMessageReactions(this.channelId, this.id));
363
+ }
364
+
365
+ /**
366
+ * Remove all reactions of a specific emoji from this message.
367
+ * @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`. Requires moderator permissions.
368
+ */
369
+ async removeReactionEmoji(
370
+ emoji: string | { name: string; id?: string; animated?: boolean },
371
+ ): Promise<void> {
372
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
373
+ await this.client.rest.delete(Routes.channelMessageReaction(this.channelId, this.id, emojiStr));
374
+ }
375
+
376
+ /**
377
+ * Fetch users who reacted with the given emoji.
378
+ * @param emoji - Unicode emoji or custom `{ name, id }`
379
+ * @param options - limit (1–100), after (user ID for pagination)
380
+ * @returns Array of User objects
381
+ */
382
+ async fetchReactionUsers(
383
+ emoji: string | { name: string; id?: string; animated?: boolean },
384
+ options?: { limit?: number; after?: string },
385
+ ): Promise<User[]> {
386
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
387
+ const params = new URLSearchParams();
388
+ if (options?.limit != null) params.set('limit', String(options.limit));
389
+ if (options?.after) params.set('after', options.after);
390
+ const qs = params.toString();
391
+ const route =
392
+ Routes.channelMessageReaction(this.channelId, this.id, emojiStr) + (qs ? `?${qs}` : '');
393
+ const data = await this.client.rest.get<{ users?: APIUserPartial[] } | APIUserPartial[]>(route);
394
+ const list = Array.isArray(data) ? data : (data?.users ?? []);
395
+ return list.map((u) => this.client.getOrCreateUser(u));
396
+ }
397
+ }
@@ -0,0 +1,72 @@
1
+ import { APIMessage, Routes } from '@erinjs/types';
2
+ import { FluxerAPIError, RateLimitError } from '@erinjs/rest';
3
+ import { FluxerError } from '../errors/FluxerError.js';
4
+ import { ErrorCodes } from '../errors/ErrorCodes.js';
5
+ import { Client } from '../client/Client.js';
6
+ import { Base } from './Base.js';
7
+ import { Message } from './Message.js';
8
+ import { Guild } from './Guild.js';
9
+ import {
10
+ GatewayMessageReactionAddDispatchData,
11
+ GatewayMessageReactionRemoveDispatchData,
12
+ GatewayReactionEmoji,
13
+ } from '@erinjs/types';
14
+
15
+ /** Represents a reaction added to or removed from a message. */
16
+ export class MessageReaction extends Base {
17
+ readonly client: Client;
18
+ readonly messageId: string;
19
+ readonly channelId: string;
20
+ readonly guildId: string | null;
21
+ readonly emoji: GatewayReactionEmoji;
22
+ /** Raw gateway payload for low-level access. */
23
+ readonly _data: GatewayMessageReactionAddDispatchData | GatewayMessageReactionRemoveDispatchData;
24
+
25
+ constructor(
26
+ client: Client,
27
+ data: GatewayMessageReactionAddDispatchData | GatewayMessageReactionRemoveDispatchData,
28
+ ) {
29
+ super();
30
+ this.client = client;
31
+ this._data = data;
32
+ this.messageId = data.message_id;
33
+ this.channelId = data.channel_id;
34
+ this.guildId = data.guild_id ?? null;
35
+ this.emoji = data.emoji;
36
+ }
37
+
38
+ /** Emoji as a string: unicode or "name:id" for custom. */
39
+ get emojiIdentifier(): string {
40
+ return this.emoji.id ? `${this.emoji.name}:${this.emoji.id}` : this.emoji.name;
41
+ }
42
+
43
+ /** Guild where this reaction was added. Resolved from cache; null for DMs or if not cached. */
44
+ get guild(): Guild | null {
45
+ return this.guildId ? (this.client.guilds.get(this.guildId) ?? null) : null;
46
+ }
47
+
48
+ /**
49
+ * Fetch the message this reaction belongs to.
50
+ * Use when you need to edit, delete, or otherwise interact with the message.
51
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
52
+ */
53
+ async fetchMessage(): Promise<Message> {
54
+ try {
55
+ const data = await this.client.rest.get<APIMessage>(
56
+ Routes.channelMessage(this.channelId, this.messageId),
57
+ );
58
+ return new Message(this.client, data);
59
+ } catch (err) {
60
+ if (err instanceof RateLimitError) throw err;
61
+ if (err instanceof FluxerAPIError && err.statusCode === 404) {
62
+ throw new FluxerError(`Message ${this.messageId} not found in channel ${this.channelId}`, {
63
+ code: ErrorCodes.MessageNotFound,
64
+ cause: err,
65
+ });
66
+ }
67
+ throw err instanceof FluxerError
68
+ ? err
69
+ : new FluxerError(String(err), { cause: err as Error });
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,12 @@
1
+ import { Channel } from './Channel.js';
2
+
3
+ /** Minimal message data for MessageDelete when the full message is not available. */
4
+ export interface PartialMessage {
5
+ id: string;
6
+ channelId: string;
7
+ channel?: Channel | null;
8
+ /** Message content, when provided by the gateway (e.g. Fluxer). */
9
+ content?: string | null;
10
+ /** Author user ID, when provided by the gateway (e.g. Fluxer). */
11
+ authorId?: string | null;
12
+ }
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Client, Role } from '../';
3
+ import { PermissionFlags } from '@erinjs/util';
4
+
5
+ function createMockClient() {
6
+ return {} as Client;
7
+ }
8
+
9
+ function createRole(
10
+ permissions: string | bigint,
11
+ overrides: Partial<{ id: string; name: string }> = {},
12
+ ) {
13
+ return new Role(
14
+ createMockClient(),
15
+ {
16
+ permissions: permissions.toString(),
17
+ id: overrides.id ?? '1',
18
+ name: overrides.name ?? 'Role',
19
+ color: 0,
20
+ position: 0,
21
+ hoist: false,
22
+ mentionable: false,
23
+ },
24
+ 'guild1',
25
+ );
26
+ }
27
+
28
+ describe('Role.permissions', () => {
29
+ describe('has()', () => {
30
+ it('returns true when role has Administrator (grants all permissions)', () => {
31
+ const role = createRole(PermissionFlags.Administrator); // 1 << 3 = Administrator
32
+ expect(role.permissions.has(PermissionFlags.Administrator)).toBe(true);
33
+ expect(role.permissions.has(PermissionFlags.SendMessages)).toBe(true);
34
+ expect(role.permissions.has(PermissionFlags.BanMembers)).toBe(true);
35
+ expect(role.permissions.has(PermissionFlags.ManageChannels)).toBe(true);
36
+ });
37
+
38
+ it('returns true when role has specific permission', () => {
39
+ const role = createRole('2048'); // SendMessages
40
+ expect(role.permissions.has(PermissionFlags.SendMessages)).toBe(true);
41
+ expect(role.permissions.has(PermissionFlags.BanMembers)).toBe(false);
42
+ expect(role.permissions.has(PermissionFlags.ViewChannel)).toBe(false);
43
+ });
44
+
45
+ it('returns true for string permission name', () => {
46
+ const role = createRole(PermissionFlags.SendMessages);
47
+ expect(role.permissions.has('SendMessages')).toBe(true);
48
+ expect(role.permissions.has('BanMembers')).toBe(false);
49
+ });
50
+
51
+ it('returns false when role has no permissions', () => {
52
+ const role = createRole('0');
53
+ expect(role.permissions.has(PermissionFlags.SendMessages)).toBe(false);
54
+ expect(role.permissions.has(PermissionFlags.Administrator)).toBe(false);
55
+ });
56
+
57
+ it('throws an error for invalid permission name', () => {
58
+ const role = createRole(PermissionFlags.Administrator);
59
+ expect(() => role.permissions.has('NonExistent' as never)).toThrow(RangeError);
60
+ });
61
+
62
+ it('handles combined permission bitfield', () => {
63
+ // SendMessages (2048) | ViewChannel (1024) = 3072
64
+ const role = createRole(String(PermissionFlags.SendMessages | PermissionFlags.ViewChannel));
65
+ expect(role.permissions.has(PermissionFlags.SendMessages)).toBe(true);
66
+ expect(role.permissions.has(PermissionFlags.ViewChannel)).toBe(true);
67
+ expect(role.permissions.has(PermissionFlags.BanMembers)).toBe(false);
68
+ });
69
+ });
70
+
71
+ describe('toString()', () => {
72
+ it('returns role mention format', () => {
73
+ const role = createRole('0', { id: '123456789' });
74
+ expect(role.toString()).toBe('<@&123456789>');
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,112 @@
1
+ import { Client } from '../client/Client.js';
2
+ import { Base } from './Base.js';
3
+ import { APIRole, RESTUpdateRoleBody } from '@erinjs/types';
4
+ import { Routes } from '@erinjs/types';
5
+ import {
6
+ PermissionFlags,
7
+ resolvePermissionsToBitfield,
8
+ type PermissionResolvable,
9
+ ALL_PERMISSIONS_BIGINT,
10
+ PermissionsBitField,
11
+ } from '@erinjs/util';
12
+
13
+ /** Represents a role in a guild. */
14
+ export class Role extends Base {
15
+ readonly client: Client;
16
+ readonly id: string;
17
+ readonly guildId: string;
18
+ name: string;
19
+ color: number;
20
+ position: number;
21
+ _permissions: string;
22
+ hoist: boolean;
23
+ mentionable: boolean;
24
+ unicodeEmoji: string | null;
25
+ /** Separately sorted position for hoisted roles. Null if not set. */
26
+ hoistPosition: number | null;
27
+
28
+ /** @param client - The client instance */
29
+ /** @param data - API role from GET /guilds/{id}/roles or gateway role events */
30
+ /** @param guildId - The guild this role belongs to */
31
+ constructor(client: Client, data: APIRole, guildId: string) {
32
+ super();
33
+ this.client = client;
34
+ this.id = data.id;
35
+ this.guildId = guildId;
36
+ this.name = data.name;
37
+ this.color = data.color;
38
+ this.position = data.position;
39
+ this._permissions = data.permissions;
40
+ this.hoist = !!data.hoist;
41
+ this.mentionable = !!data.mentionable;
42
+ this.unicodeEmoji = data.unicode_emoji ?? null;
43
+ this.hoistPosition = data.hoist_position ?? null;
44
+ }
45
+
46
+ get permissions(): PermissionsBitField {
47
+ const bits = BigInt(this._permissions);
48
+ return new PermissionsBitField(
49
+ (bits & PermissionFlags.Administrator) !== 0n ? ALL_PERMISSIONS_BIGINT : bits,
50
+ );
51
+ }
52
+
53
+ /** Update mutable fields from fresh API data. Used by edit and gateway events. */
54
+ _patch(data: Partial<APIRole>): void {
55
+ if (data.name !== undefined) this.name = data.name;
56
+ if (data.color !== undefined) this.color = data.color;
57
+ if (data.position !== undefined) this.position = data.position;
58
+ if (data.permissions !== undefined) this._permissions = data.permissions;
59
+ if (data.hoist !== undefined) this.hoist = !!data.hoist;
60
+ if (data.mentionable !== undefined) this.mentionable = !!data.mentionable;
61
+ if (data.unicode_emoji !== undefined) this.unicodeEmoji = data.unicode_emoji ?? null;
62
+ if (data.hoist_position !== undefined) this.hoistPosition = data.hoist_position ?? null;
63
+ }
64
+
65
+ /** Returns a mention string (e.g. `<@&123456>`). */
66
+ toString(): string {
67
+ return `<@&${this.id}>`;
68
+ }
69
+
70
+ /**
71
+ * Edit this role.
72
+ * Requires Manage Roles permission.
73
+ * @param options - Role updates (permissions accepts PermissionResolvable for convenience)
74
+ * @returns This role (updated in place)
75
+ * @example
76
+ * await role.edit({ name: 'Moderator', permissions: ['BanMembers', 'KickMembers'] });
77
+ */
78
+ async edit(
79
+ options: RESTUpdateRoleBody & { permissions?: string | PermissionResolvable },
80
+ ): Promise<Role> {
81
+ const body: Record<string, unknown> = {};
82
+ if (options.name !== undefined) body.name = options.name;
83
+ if (options.permissions !== undefined) {
84
+ body.permissions =
85
+ typeof options.permissions === 'string'
86
+ ? options.permissions
87
+ : resolvePermissionsToBitfield(options.permissions);
88
+ }
89
+ if (options.color !== undefined) body.color = options.color;
90
+ if (options.hoist !== undefined) body.hoist = options.hoist;
91
+ if (options.mentionable !== undefined) body.mentionable = options.mentionable;
92
+ if (options.unicode_emoji !== undefined) body.unicode_emoji = options.unicode_emoji;
93
+ if (options.position !== undefined) body.position = options.position;
94
+ if (options.hoist_position !== undefined) body.hoist_position = options.hoist_position;
95
+ const data = await this.client.rest.patch<APIRole>(Routes.guildRole(this.guildId, this.id), {
96
+ body: Object.keys(body).length ? body : undefined,
97
+ auth: true,
98
+ });
99
+ this._patch(data);
100
+ return this;
101
+ }
102
+
103
+ /**
104
+ * Delete this role.
105
+ * Requires Manage Roles permission.
106
+ */
107
+ async delete(): Promise<void> {
108
+ await this.client.rest.delete(Routes.guildRole(this.guildId, this.id), { auth: true });
109
+ const guild = this.client.guilds.get(this.guildId);
110
+ if (guild) guild.roles.delete(this.id);
111
+ }
112
+ }