baff.js 0.0.0-stage → 0.1.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 (262) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +7 -0
  3. package/README.md +244 -3
  4. package/dist/cjs/builders/EmbedBuilder.d.ts +51 -0
  5. package/dist/cjs/builders/EmbedBuilder.js +150 -0
  6. package/dist/cjs/builders/SlashCommandBuilder.d.ts +69 -0
  7. package/dist/cjs/builders/SlashCommandBuilder.js +185 -0
  8. package/dist/cjs/builders/components/ActionRowBuilder.d.ts +14 -0
  9. package/dist/cjs/builders/components/ActionRowBuilder.js +45 -0
  10. package/dist/cjs/builders/components/ButtonBuilder.d.ts +19 -0
  11. package/dist/cjs/builders/components/ButtonBuilder.js +69 -0
  12. package/dist/cjs/builders/components/LayoutBuilders.d.ts +63 -0
  13. package/dist/cjs/builders/components/LayoutBuilders.js +179 -0
  14. package/dist/cjs/builders/components/ModalBuilder.d.ts +36 -0
  15. package/dist/cjs/builders/components/ModalBuilder.js +118 -0
  16. package/dist/cjs/builders/components/SelectMenuBuilder.d.ts +80 -0
  17. package/dist/cjs/builders/components/SelectMenuBuilder.js +192 -0
  18. package/dist/cjs/builders/components/shared.d.ts +16 -0
  19. package/dist/cjs/builders/components/shared.js +32 -0
  20. package/dist/cjs/builders/messagePayload.d.ts +35 -0
  21. package/dist/cjs/builders/messagePayload.js +88 -0
  22. package/dist/cjs/builders/validate.d.ts +12 -0
  23. package/dist/cjs/builders/validate.js +54 -0
  24. package/dist/cjs/client/Client.d.ts +63 -0
  25. package/dist/cjs/client/Client.js +152 -0
  26. package/dist/cjs/client/dispatch.d.ts +2 -0
  27. package/dist/cjs/client/dispatch.js +224 -0
  28. package/dist/cjs/client/events.d.ts +129 -0
  29. package/dist/cjs/client/events.js +2 -0
  30. package/dist/cjs/collectors/Collector.d.ts +37 -0
  31. package/dist/cjs/collectors/Collector.js +84 -0
  32. package/dist/cjs/collectors/InteractionCollector.d.ts +21 -0
  33. package/dist/cjs/collectors/InteractionCollector.js +44 -0
  34. package/dist/cjs/collectors/MessageCollector.d.ts +14 -0
  35. package/dist/cjs/collectors/MessageCollector.js +23 -0
  36. package/dist/cjs/errors/BaffError.d.ts +26 -0
  37. package/dist/cjs/errors/BaffError.js +39 -0
  38. package/dist/cjs/gateway/Gateway.d.ts +53 -0
  39. package/dist/cjs/gateway/Gateway.js +128 -0
  40. package/dist/cjs/http/handler.d.ts +11 -0
  41. package/dist/cjs/http/handler.js +72 -0
  42. package/dist/cjs/http/verify.d.ts +11 -0
  43. package/dist/cjs/http/verify.js +28 -0
  44. package/dist/cjs/index.d.ts +51 -0
  45. package/dist/cjs/index.js +185 -0
  46. package/dist/cjs/managers/CachedManager.d.ts +16 -0
  47. package/dist/cjs/managers/CachedManager.js +18 -0
  48. package/dist/cjs/managers/ChannelManager.d.ts +23 -0
  49. package/dist/cjs/managers/ChannelManager.js +85 -0
  50. package/dist/cjs/managers/CommandManager.d.ts +32 -0
  51. package/dist/cjs/managers/CommandManager.js +72 -0
  52. package/dist/cjs/managers/EventManager.d.ts +15 -0
  53. package/dist/cjs/managers/EventManager.js +36 -0
  54. package/dist/cjs/managers/ExpressionManagers.d.ts +36 -0
  55. package/dist/cjs/managers/ExpressionManagers.js +73 -0
  56. package/dist/cjs/managers/InviteManager.d.ts +17 -0
  57. package/dist/cjs/managers/InviteManager.js +26 -0
  58. package/dist/cjs/managers/MemberManager.d.ts +31 -0
  59. package/dist/cjs/managers/MemberManager.js +62 -0
  60. package/dist/cjs/managers/MessageManager.d.ts +32 -0
  61. package/dist/cjs/managers/MessageManager.js +79 -0
  62. package/dist/cjs/managers/RoleManager.d.ts +22 -0
  63. package/dist/cjs/managers/RoleManager.js +73 -0
  64. package/dist/cjs/managers/ServerChannelManager.d.ts +20 -0
  65. package/dist/cjs/managers/ServerChannelManager.js +30 -0
  66. package/dist/cjs/managers/ServerManager.d.ts +12 -0
  67. package/dist/cjs/managers/ServerManager.js +62 -0
  68. package/dist/cjs/managers/ThreadManager.d.ts +12 -0
  69. package/dist/cjs/managers/ThreadManager.js +20 -0
  70. package/dist/cjs/managers/UserManager.d.ts +11 -0
  71. package/dist/cjs/managers/UserManager.js +38 -0
  72. package/dist/cjs/managers/VoiceManager.d.ts +8 -0
  73. package/dist/cjs/managers/VoiceManager.js +17 -0
  74. package/dist/cjs/package.json +1 -0
  75. package/dist/cjs/rest/REST.d.ts +58 -0
  76. package/dist/cjs/rest/REST.js +193 -0
  77. package/dist/cjs/rest/Routes.d.ts +54 -0
  78. package/dist/cjs/rest/Routes.js +58 -0
  79. package/dist/cjs/rest/files.d.ts +19 -0
  80. package/dist/cjs/rest/files.js +37 -0
  81. package/dist/cjs/structures/Base.d.ts +13 -0
  82. package/dist/cjs/structures/Base.js +30 -0
  83. package/dist/cjs/structures/Message.d.ts +91 -0
  84. package/dist/cjs/structures/Message.js +200 -0
  85. package/dist/cjs/structures/Role.d.ts +34 -0
  86. package/dist/cjs/structures/Role.js +84 -0
  87. package/dist/cjs/structures/ScheduledEvent.d.ts +37 -0
  88. package/dist/cjs/structures/ScheduledEvent.js +73 -0
  89. package/dist/cjs/structures/Server.d.ts +68 -0
  90. package/dist/cjs/structures/Server.js +124 -0
  91. package/dist/cjs/structures/ServerAssets.d.ts +48 -0
  92. package/dist/cjs/structures/ServerAssets.js +104 -0
  93. package/dist/cjs/structures/ServerMember.d.ts +47 -0
  94. package/dist/cjs/structures/ServerMember.js +138 -0
  95. package/dist/cjs/structures/User.d.ts +35 -0
  96. package/dist/cjs/structures/User.js +87 -0
  97. package/dist/cjs/structures/Webhook.d.ts +30 -0
  98. package/dist/cjs/structures/Webhook.js +68 -0
  99. package/dist/cjs/structures/channels/BaseChannel.d.ts +49 -0
  100. package/dist/cjs/structures/channels/BaseChannel.js +81 -0
  101. package/dist/cjs/structures/channels/DMChannel.d.ts +9 -0
  102. package/dist/cjs/structures/channels/DMChannel.js +21 -0
  103. package/dist/cjs/structures/channels/ServerChannels.d.ts +57 -0
  104. package/dist/cjs/structures/channels/ServerChannels.js +138 -0
  105. package/dist/cjs/structures/channels/TextBasedChannel.d.ts +25 -0
  106. package/dist/cjs/structures/channels/TextBasedChannel.js +53 -0
  107. package/dist/cjs/structures/interactions/BaseInteraction.d.ts +74 -0
  108. package/dist/cjs/structures/interactions/BaseInteraction.js +221 -0
  109. package/dist/cjs/structures/interactions/CommandInteraction.d.ts +44 -0
  110. package/dist/cjs/structures/interactions/CommandInteraction.js +109 -0
  111. package/dist/cjs/structures/interactions/ComponentInteraction.d.ts +44 -0
  112. package/dist/cjs/structures/interactions/ComponentInteraction.js +64 -0
  113. package/dist/cjs/structures/interactions/ModalSubmitInteraction.d.ts +29 -0
  114. package/dist/cjs/structures/interactions/ModalSubmitInteraction.js +42 -0
  115. package/dist/cjs/structures/interactions/createInteraction.d.ts +4 -0
  116. package/dist/cjs/structures/interactions/createInteraction.js +34 -0
  117. package/dist/cjs/types.d.ts +369 -0
  118. package/dist/cjs/types.js +2 -0
  119. package/dist/cjs/util/Collection.d.ts +32 -0
  120. package/dist/cjs/util/Collection.js +145 -0
  121. package/dist/cjs/util/Permissions.d.ts +87 -0
  122. package/dist/cjs/util/Permissions.js +113 -0
  123. package/dist/cjs/util/Snowflake.d.ts +20 -0
  124. package/dist/cjs/util/Snowflake.js +40 -0
  125. package/dist/cjs/util/TypedEmitter.d.ts +17 -0
  126. package/dist/cjs/util/TypedEmitter.js +5 -0
  127. package/dist/cjs/util/constants.d.ts +78 -0
  128. package/dist/cjs/util/constants.js +81 -0
  129. package/dist/cjs/util/emoji.d.ts +8 -0
  130. package/dist/cjs/util/emoji.js +10 -0
  131. package/dist/cjs/util/formatters.d.ts +29 -0
  132. package/dist/cjs/util/formatters.js +55 -0
  133. package/dist/esm/builders/EmbedBuilder.d.ts +51 -0
  134. package/dist/esm/builders/EmbedBuilder.js +145 -0
  135. package/dist/esm/builders/SlashCommandBuilder.d.ts +69 -0
  136. package/dist/esm/builders/SlashCommandBuilder.js +170 -0
  137. package/dist/esm/builders/components/ActionRowBuilder.d.ts +14 -0
  138. package/dist/esm/builders/components/ActionRowBuilder.js +41 -0
  139. package/dist/esm/builders/components/ButtonBuilder.d.ts +19 -0
  140. package/dist/esm/builders/components/ButtonBuilder.js +65 -0
  141. package/dist/esm/builders/components/LayoutBuilders.d.ts +63 -0
  142. package/dist/esm/builders/components/LayoutBuilders.js +169 -0
  143. package/dist/esm/builders/components/ModalBuilder.d.ts +36 -0
  144. package/dist/esm/builders/components/ModalBuilder.js +113 -0
  145. package/dist/esm/builders/components/SelectMenuBuilder.d.ts +80 -0
  146. package/dist/esm/builders/components/SelectMenuBuilder.js +183 -0
  147. package/dist/esm/builders/components/shared.d.ts +16 -0
  148. package/dist/esm/builders/components/shared.js +26 -0
  149. package/dist/esm/builders/messagePayload.d.ts +35 -0
  150. package/dist/esm/builders/messagePayload.js +83 -0
  151. package/dist/esm/builders/validate.d.ts +12 -0
  152. package/dist/esm/builders/validate.js +46 -0
  153. package/dist/esm/client/Client.d.ts +63 -0
  154. package/dist/esm/client/Client.js +147 -0
  155. package/dist/esm/client/dispatch.d.ts +2 -0
  156. package/dist/esm/client/dispatch.js +221 -0
  157. package/dist/esm/client/events.d.ts +129 -0
  158. package/dist/esm/client/events.js +1 -0
  159. package/dist/esm/collectors/Collector.d.ts +37 -0
  160. package/dist/esm/collectors/Collector.js +78 -0
  161. package/dist/esm/collectors/InteractionCollector.d.ts +21 -0
  162. package/dist/esm/collectors/InteractionCollector.js +40 -0
  163. package/dist/esm/collectors/MessageCollector.d.ts +14 -0
  164. package/dist/esm/collectors/MessageCollector.js +19 -0
  165. package/dist/esm/errors/BaffError.d.ts +26 -0
  166. package/dist/esm/errors/BaffError.js +33 -0
  167. package/dist/esm/gateway/Gateway.d.ts +53 -0
  168. package/dist/esm/gateway/Gateway.js +124 -0
  169. package/dist/esm/http/handler.d.ts +11 -0
  170. package/dist/esm/http/handler.js +68 -0
  171. package/dist/esm/http/verify.d.ts +11 -0
  172. package/dist/esm/http/verify.js +22 -0
  173. package/dist/esm/index.d.ts +51 -0
  174. package/dist/esm/index.js +49 -0
  175. package/dist/esm/managers/CachedManager.d.ts +16 -0
  176. package/dist/esm/managers/CachedManager.js +14 -0
  177. package/dist/esm/managers/ChannelManager.d.ts +23 -0
  178. package/dist/esm/managers/ChannelManager.js +81 -0
  179. package/dist/esm/managers/CommandManager.d.ts +32 -0
  180. package/dist/esm/managers/CommandManager.js +67 -0
  181. package/dist/esm/managers/EventManager.d.ts +15 -0
  182. package/dist/esm/managers/EventManager.js +32 -0
  183. package/dist/esm/managers/ExpressionManagers.d.ts +36 -0
  184. package/dist/esm/managers/ExpressionManagers.js +68 -0
  185. package/dist/esm/managers/InviteManager.d.ts +17 -0
  186. package/dist/esm/managers/InviteManager.js +22 -0
  187. package/dist/esm/managers/MemberManager.d.ts +31 -0
  188. package/dist/esm/managers/MemberManager.js +58 -0
  189. package/dist/esm/managers/MessageManager.d.ts +32 -0
  190. package/dist/esm/managers/MessageManager.js +75 -0
  191. package/dist/esm/managers/RoleManager.d.ts +22 -0
  192. package/dist/esm/managers/RoleManager.js +69 -0
  193. package/dist/esm/managers/ServerChannelManager.d.ts +20 -0
  194. package/dist/esm/managers/ServerChannelManager.js +26 -0
  195. package/dist/esm/managers/ServerManager.d.ts +12 -0
  196. package/dist/esm/managers/ServerManager.js +58 -0
  197. package/dist/esm/managers/ThreadManager.d.ts +12 -0
  198. package/dist/esm/managers/ThreadManager.js +16 -0
  199. package/dist/esm/managers/UserManager.d.ts +11 -0
  200. package/dist/esm/managers/UserManager.js +34 -0
  201. package/dist/esm/managers/VoiceManager.d.ts +8 -0
  202. package/dist/esm/managers/VoiceManager.js +13 -0
  203. package/dist/esm/package.json +1 -0
  204. package/dist/esm/rest/REST.d.ts +58 -0
  205. package/dist/esm/rest/REST.js +188 -0
  206. package/dist/esm/rest/Routes.d.ts +54 -0
  207. package/dist/esm/rest/Routes.js +55 -0
  208. package/dist/esm/rest/files.d.ts +19 -0
  209. package/dist/esm/rest/files.js +31 -0
  210. package/dist/esm/structures/Base.d.ts +13 -0
  211. package/dist/esm/structures/Base.js +25 -0
  212. package/dist/esm/structures/Message.d.ts +91 -0
  213. package/dist/esm/structures/Message.js +194 -0
  214. package/dist/esm/structures/Role.d.ts +34 -0
  215. package/dist/esm/structures/Role.js +79 -0
  216. package/dist/esm/structures/ScheduledEvent.d.ts +37 -0
  217. package/dist/esm/structures/ScheduledEvent.js +68 -0
  218. package/dist/esm/structures/Server.d.ts +68 -0
  219. package/dist/esm/structures/Server.js +120 -0
  220. package/dist/esm/structures/ServerAssets.d.ts +48 -0
  221. package/dist/esm/structures/ServerAssets.js +98 -0
  222. package/dist/esm/structures/ServerMember.d.ts +47 -0
  223. package/dist/esm/structures/ServerMember.js +133 -0
  224. package/dist/esm/structures/User.d.ts +35 -0
  225. package/dist/esm/structures/User.js +82 -0
  226. package/dist/esm/structures/Webhook.d.ts +30 -0
  227. package/dist/esm/structures/Webhook.js +63 -0
  228. package/dist/esm/structures/channels/BaseChannel.d.ts +49 -0
  229. package/dist/esm/structures/channels/BaseChannel.js +77 -0
  230. package/dist/esm/structures/channels/DMChannel.d.ts +9 -0
  231. package/dist/esm/structures/channels/DMChannel.js +17 -0
  232. package/dist/esm/structures/channels/ServerChannels.d.ts +57 -0
  233. package/dist/esm/structures/channels/ServerChannels.js +129 -0
  234. package/dist/esm/structures/channels/TextBasedChannel.d.ts +25 -0
  235. package/dist/esm/structures/channels/TextBasedChannel.js +49 -0
  236. package/dist/esm/structures/interactions/BaseInteraction.d.ts +74 -0
  237. package/dist/esm/structures/interactions/BaseInteraction.js +217 -0
  238. package/dist/esm/structures/interactions/CommandInteraction.d.ts +44 -0
  239. package/dist/esm/structures/interactions/CommandInteraction.js +104 -0
  240. package/dist/esm/structures/interactions/ComponentInteraction.d.ts +44 -0
  241. package/dist/esm/structures/interactions/ComponentInteraction.js +53 -0
  242. package/dist/esm/structures/interactions/ModalSubmitInteraction.d.ts +29 -0
  243. package/dist/esm/structures/interactions/ModalSubmitInteraction.js +37 -0
  244. package/dist/esm/structures/interactions/createInteraction.d.ts +4 -0
  245. package/dist/esm/structures/interactions/createInteraction.js +31 -0
  246. package/dist/esm/types.d.ts +369 -0
  247. package/dist/esm/types.js +1 -0
  248. package/dist/esm/util/Collection.d.ts +32 -0
  249. package/dist/esm/util/Collection.js +141 -0
  250. package/dist/esm/util/Permissions.d.ts +87 -0
  251. package/dist/esm/util/Permissions.js +109 -0
  252. package/dist/esm/util/Snowflake.d.ts +20 -0
  253. package/dist/esm/util/Snowflake.js +37 -0
  254. package/dist/esm/util/TypedEmitter.d.ts +17 -0
  255. package/dist/esm/util/TypedEmitter.js +2 -0
  256. package/dist/esm/util/constants.d.ts +78 -0
  257. package/dist/esm/util/constants.js +78 -0
  258. package/dist/esm/util/emoji.d.ts +8 -0
  259. package/dist/esm/util/emoji.js +7 -0
  260. package/dist/esm/util/formatters.d.ts +29 -0
  261. package/dist/esm/util/formatters.js +34 -0
  262. package/package.json +58 -6
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to baff.js are listed here. The project follows [semantic versioning](https://semver.org); before 1.0, minor versions may include breaking changes.
4
+
5
+ ## 0.1.0
6
+
7
+ First release.
8
+
9
+ - `Client` with gateway connection, automatic reconnect with backoff, and caches for servers, channels, users, members, roles and messages.
10
+ - Typed events for the gateway events documented at docs.baff.gg, plus `raw`.
11
+ - `REST` with per-route queues, `429` handling, message send spacing, retries for idempotent requests, multipart uploads and typed errors (`BaffError`, `BaffAPIError`, `BaffRateLimitError`).
12
+ - Structures with methods: `Message`, `User`, `ServerMember`, `Server`, `Role`, channels (`TextChannel`, `ThreadChannel`, `DMChannel`, `VoiceChannel`, `CategoryChannel`, `ForumChannel`), `Emoji`, `Sticker`, `Invite`, `Webhook`, `ScheduledEvent`.
13
+ - Slash command builders (`SlashCommandBuilder`, `PrefixCommandBuilder`) checked against the registration rules, and `client.application.commands` / `server.commands`.
14
+ - Interactions: command, button, select menu and modal submit, with `reply`, `deferReply`, `editReply`, `followUp`, `deleteReply`, `update`, `deferUpdate` and `showModal`.
15
+ - Builders for embeds, action rows, buttons, select menus, modals, text inputs and components v2 layouts, validated against the server's limits.
16
+ - `MessageCollector`, `InteractionCollector`, `awaitMessages`, `awaitMessageComponent`.
17
+ - HTTP interactions: `createInteractionsHandler`, `toNodeListener`, `verifyKey` (HMAC-SHA256, constant-time compare, replay window).
18
+ - `PermissionsBitField`, `Snowflake`, `Collection`, mention and markdown formatters, `Colors`, `Routes`.
19
+ - `WebhookClient` for posting through a webhook URL without a bot.
20
+ - Dual ESM and CommonJS builds with type declarations, and no runtime dependencies.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2026 baff.gg. All rights reserved.
2
+
3
+ baff.js is free to use. You may install it and use it, unmodified, to build bots, apps and tools that work with baff.gg, including commercial ones.
4
+
5
+ You may not redistribute baff.js or a modified version of it, publish it under another name, or use it to build a competing library, without written permission from baff.gg.
6
+
7
+ baff.js is provided as is, without warranty of any kind. baff.gg is not liable for any damages arising from its use.
package/README.md CHANGED
@@ -1,3 +1,244 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # baff.js
2
+
3
+ A library for building bots on [baff.gg](https://baff.gg). It wraps the REST API and the gateway, turns slash commands, buttons, select menus and forms into objects with methods, and validates embeds and components before they leave your process. No runtime dependencies.
4
+
5
+ If you have used discord.js, the shapes will be familiar: `Client`, event listeners, `interaction.reply()`, builders, collectors, `Collection`, permission bitfields. baff.gg calls guilds **servers**, and the event and type names follow baff.gg's own gateway (`serverCreate`, `memberUpdate`, `ServerMember`).
6
+
7
+ ```bash
8
+ npm install baff.js
9
+ bun add baff.js
10
+ ```
11
+
12
+ ## Requirements
13
+
14
+ - Node.js 22 or newer, Bun, or Deno. The gateway uses the runtime's built-in `WebSocket`, `fetch` and `FormData`.
15
+ - Node.js 20 works too: start it with `--experimental-websocket`, or install the `ws` package and pass it in: `new Client({ WebSocket: WebSocket })`.
16
+ - A bot token and the application id from the [Developer Portal](https://baff.gg/developers). baff.gg does not tell a bot its application id, so pass it as `applicationId` (it is only needed to register commands and to build an install link).
17
+
18
+ ## A first bot
19
+
20
+ ```js
21
+ import { Client, SlashCommandBuilder } from 'baff.js';
22
+
23
+ const client = new Client({ token: process.env.BOT_TOKEN, applicationId: process.env.APP_ID });
24
+
25
+ client.once('ready', async () => {
26
+ await client.requireApplication().commands.set([
27
+ new SlashCommandBuilder().setName('ping').setDescription('Checks the bot is awake')
28
+ ]);
29
+ console.log(`Logged in as ${client.user.username}`);
30
+ });
31
+
32
+ client.on('interactionCreate', async (interaction) => {
33
+ if (interaction.isChatInputCommand() && interaction.commandName === 'ping') {
34
+ await interaction.reply('Pong!');
35
+ }
36
+ });
37
+
38
+ await client.login();
39
+ ```
40
+
41
+ `commands.set` replaces every global command with the list you pass, like the API's `PUT`. Use `server.commands.set(...)` for commands that belong to one server.
42
+
43
+ More complete bots are in [`examples/`](./examples): a ping command, buttons with a select menu and a form, an embed with a components v2 layout, and a signed HTTP interactions endpoint.
44
+
45
+ ## Slash commands
46
+
47
+ ```js
48
+ const command = new SlashCommandBuilder()
49
+ .setName('partner')
50
+ .setDescription('Manage partnered servers')
51
+ .addSubcommand((sub) =>
52
+ sub.setName('add').setDescription('Partner a server').addStringOption((o) => o.setName('server').setDescription('Name or id').setRequired(true))
53
+ )
54
+ .addSubcommandGroup((group) =>
55
+ group.setName('settings').setDescription('Settings').addSubcommand((sub) => sub.setName('show').setDescription('Show them'))
56
+ );
57
+ ```
58
+
59
+ `toJSON()` (called by `commands.set`) applies the same checks the server does and throws a `BaffError` that names the problem: names of 1-32 lowercase letters, numbers, `-` or `_`, descriptions of 1-100 characters, at most 25 options, required options before optional ones, and a command that holds either subcommands or plain options, never both.
60
+
61
+ Option types are `string`, `integer`, `number`, `boolean`, `user`, `channel` and `role`. baff.gg does not support choices, autocomplete or min/max values on options, so there are no builder methods for them.
62
+
63
+ Read the values with `interaction.options`:
64
+
65
+ ```js
66
+ const text = interaction.options.getString('text', true); // string, throws if missing
67
+ const times = interaction.options.getInteger('times'); // number | null
68
+ const sub = interaction.options.getSubcommand(); // 'add'
69
+ const group = interaction.options.getSubcommandGroup(); // 'settings' | null
70
+ const userId = interaction.options.getUserId('who'); // user, role and channel options arrive as ids
71
+ const user = interaction.options.getUser('who'); // from the cache, or null
72
+ const fetched = await interaction.options.fetchUser('who');
73
+ ```
74
+
75
+ Prefix commands (the `!help` kind, which only show up in the command picker) use `PrefixCommandBuilder`.
76
+
77
+ ### Replying
78
+
79
+ ```js
80
+ await interaction.reply({ content: 'Only you can see this', ephemeral: true });
81
+ await interaction.deferReply(); // "Bot is thinking..."
82
+ await interaction.editReply('Done'); // after a defer, this sends the real answer
83
+ await interaction.followUp('One more'); // up to 15 minutes after the interaction
84
+ await interaction.showModal(modal);
85
+ ```
86
+
87
+ You have 3 seconds to answer or defer. Two things work differently from what you might expect, because of what the baff.gg API offers:
88
+
89
+ - After the first answer, `editReply` and `deleteReply` on a command look the reply message up in the channel (by command name and author) and edit it with your bot token. If the same person runs the same command several times at once in one channel, it can pick the wrong message.
90
+ - Ephemeral replies are not stored, so they cannot be edited or deleted afterwards. Answer a component click with `update()` to change an ephemeral message that has components.
91
+
92
+ ## Components, embeds and layouts
93
+
94
+ ```js
95
+ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, EmbedBuilder, Colors } from 'baff.js';
96
+
97
+ await channel.send({
98
+ embeds: [new EmbedBuilder().setTitle('Deploy finished').setDescription('**v1.4.2** is live').setColor(Colors.Baff)],
99
+ components: [
100
+ new ActionRowBuilder().addComponents(
101
+ new ButtonBuilder().setCustomId('ok').setLabel('Nice').setStyle(ButtonStyle.Success),
102
+ new ButtonBuilder().setLabel('Notes').setURL('https://example.com/notes')
103
+ ),
104
+ new ActionRowBuilder().addComponents(
105
+ new StringSelectMenuBuilder().setCustomId('pick').addOptions({ label: 'A', value: 'a' }, { label: 'B', value: 'b' })
106
+ )
107
+ ]
108
+ });
109
+ ```
110
+
111
+ Builders: `EmbedBuilder`, `ActionRowBuilder`, `ButtonBuilder`, `StringSelectMenuBuilder`, `UserSelectMenuBuilder`, `RoleSelectMenuBuilder`, `MentionableSelectMenuBuilder`, `ChannelSelectMenuBuilder`, `ModalBuilder`, `TextInputBuilder`, and for components v2 `ContainerBuilder`, `SectionBuilder`, `TextDisplayBuilder`, `ThumbnailBuilder`, `MediaGalleryBuilder`, `SeparatorBuilder`.
112
+
113
+ Embed and component limits (10 embeds, 6000 characters, 25 fields, 5 rows, 40 components, 4000 characters of text displays, unique custom ids) are checked when the builder is turned into JSON, so the error shows up in your code with the field name instead of as a rejected request. Pass a layout component and the `IsComponentsV2` flag is set for you; such a message cannot also carry `content` or `embeds`.
114
+
115
+ Clicks arrive as interactions:
116
+
117
+ ```js
118
+ client.on('interactionCreate', async (interaction) => {
119
+ if (interaction.isButton()) await interaction.update({ content: `${interaction.customId} pressed`, components: [] });
120
+ if (interaction.isStringSelectMenu()) await interaction.reply({ content: interaction.values.join(', '), ephemeral: true });
121
+ if (interaction.isModalSubmit()) await interaction.reply(interaction.fields.getTextInputValue('topic'));
122
+ });
123
+ ```
124
+
125
+ User, role, mentionable and channel selects expose `users`, `roles` and `channels` for what was picked.
126
+
127
+ ## Collectors
128
+
129
+ ```js
130
+ const answers = await channel.awaitMessages({ filter: (m) => m.author.id === userId, max: 1, time: 30_000, errors: ['time'] });
131
+
132
+ const click = await message.awaitMessageComponent({ time: 60_000, filter: (i) => i.user.id === userId });
133
+
134
+ const collector = message.createMessageComponentCollector({ time: 60_000 });
135
+ collector.on('collect', (interaction) => interaction.deferUpdate());
136
+ collector.on('end', (collected, reason) => console.log(reason));
137
+ ```
138
+
139
+ `awaitMessages` rejects with a `CollectorError` (which carries `collected` and `reason`) when the collector ends for one of the reasons in `errors`. `awaitMessageComponent` rejects on timeout. Both `MessageCollector` and `InteractionCollector` are exported.
140
+
141
+ ## HTTP interactions
142
+
143
+ If you set an Interactions Endpoint URL in the portal, baff.gg POSTs interactions there instead of sending them on the gateway. `createInteractionsHandler` verifies the signature, answers pings, and hands each interaction to your `interactionCreate` listener:
144
+
145
+ ```js
146
+ import { createServer } from 'node:http';
147
+ import { Client, createInteractionsHandler, toNodeListener } from 'baff.js';
148
+
149
+ const client = new Client({ token: process.env.BOT_TOKEN, applicationId: process.env.APP_ID });
150
+ client.on('interactionCreate', (interaction) => interaction.isChatInputCommand() && interaction.reply('Pong!'));
151
+
152
+ createServer(toNodeListener(createInteractionsHandler({ secret: process.env.SIGNING_SECRET, client }))).listen(3000);
153
+ ```
154
+
155
+ The first answer you give is returned as the HTTP response, which is what serverless hosts need; later answers (`followUp`, `editReply`) go through the REST API. The handler is a plain `(Request) => Promise<Response>`, so it also works as a Cloudflare Worker, Bun or Deno handler. Without a gateway connection you do not need to call `login()`.
156
+
157
+ To check signatures yourself:
158
+
159
+ ```js
160
+ import { verifyKey } from 'baff.js';
161
+
162
+ verifyKey(rawBody, signatureHeader, timestampHeader, signingSecret); // boolean
163
+ ```
164
+
165
+ The signature is the hex HMAC-SHA256 of `timestamp + "." + body`. `verifyKey` compares in constant time and rejects timestamps more than 5 minutes away from now (`{ toleranceSeconds }` changes that). Pass the raw body exactly as received.
166
+
167
+ ## Errors and rate limits
168
+
169
+ Every failure is a `BaffError` with a stable `code`. API failures are `BaffAPIError`, with `status`, `method`, `path`, `field` and the parsed `body`:
170
+
171
+ ```js
172
+ try {
173
+ await channel.send({ embeds: [{ title: 'x'.repeat(300) }] });
174
+ } catch (error) {
175
+ if (error instanceof BaffAPIError) console.log(error.status, error.code, error.field);
176
+ }
177
+ ```
178
+
179
+ `REST` queues requests per route, waits out `429` responses (using `Retry-After` when the server sends it), spaces message sends to the 5 per 5 seconds baff.gg allows, and retries `GET`, `PUT` and `DELETE` after network errors and `5xx` answers. A `429` with the code `slowmode` is a channel rule, not a throttle, so it is thrown at once as a `BaffRateLimitError`.
180
+
181
+ ## Permissions, snowflakes and formatting
182
+
183
+ ```js
184
+ import { PermissionsBitField, PermissionFlagsBits, Snowflake, userMention, escapeMarkdown } from 'baff.js';
185
+
186
+ const perms = new PermissionsBitField(['ViewChannel', 'SendMessages']);
187
+ perms.has('SendMessages'); // true
188
+ perms.missing(['BanMembers']); // ['BanMembers']
189
+ interaction.memberPermissions?.has('BanMembers'); // channel-accurate, from the interaction
190
+ member.permissions; // server-wide, from role bits (channel overrides are not applied)
191
+
192
+ Snowflake.deconstruct('103745999999999999').date;
193
+ userMention(user.id); // <@id>
194
+ ```
195
+
196
+ Permission bits are bigints. Ids are strings, as in the API.
197
+
198
+ ## Servers, channels and caches
199
+
200
+ ```js
201
+ const server = await client.servers.fetch('103700000000000000');
202
+ server.channels.cache.find((c) => c.name === 'general');
203
+ await server.members.fetch(); // one request, fills server.members.cache
204
+ await server.members.ban(userId, { reason: 'spam' });
205
+ await server.roles.create({ name: 'Mods', color: '#2ec4b6', permissions: ['KickMembers'] });
206
+ await server.invites.create({ maxUses: 5 });
207
+ ```
208
+
209
+ On login the client loads every server the bot is in (one list request plus one request per server) so `client.servers`, `client.channels` and the roles are ready when `ready` fires. Pass `fetchServersOnReady: false` to skip that, and `fetchMembersOnReady: true` to also load the member lists. baff.gg has no route for a single channel, so `client.channels.fetch(id)` reloads the bot's servers and DMs to find it.
210
+
211
+ Events keep the caches in step. The gateway does not replay what happened while the bot was disconnected: after a reconnect the client reloads its servers and then emits `reconnected`.
212
+
213
+ ## Events
214
+
215
+ `ready`, `reconnecting`, `reconnected`, `disconnect`, `raw`, `debug`, `error`, `serverCreate`, `serverUpdate`, `serverRemove`, `channelCreate`, `channelUpdate`, `channelDelete`, `roleCreate`, `roleUpdate`, `roleDelete`, `threadCreate`, `threadUpdate`, `messageCreate`, `messageUpdate`, `messageDelete`, `messageReactionAdd`, `messageReactionRemove`, `messagePin`, `typingStart`, `mention`, `pollVote`, `memberAdd`, `memberRemove`, `memberUpdate`, `presenceUpdate`, `userUpdate`, `clanTagUpdate`, `dmOpen`, `voiceStateUpdate`, `soundboardPlay`, `callAlone`, `stageInstanceCreate`, `stageInstanceUpdate`, `stageInstanceDelete`, `voiceChannelStatusUpdate`, `serverStickersUpdate`, `serverSoundboardUpdate`, `serverEventCreate`, `serverEventUpdate`, `serverEventDelete`, `serverEventUserAdd`, `serverEventUserRemove`, `interactionCreate`.
216
+
217
+ Each one maps to a gateway event of the same name in [the docs](https://docs.baff.gg/gateway) (`MESSAGE_CREATE` is `messageCreate`). Events that describe something that may not be cached, like `messageDelete` or `messageReactionAdd`, pass a single object with the ids and whatever could be resolved, instead of a partial structure. `raw` receives every packet, including ones this version does not know yet.
218
+
219
+ An exception thrown inside a listener is emitted as `error`. With no `error` listener it is rethrown, so it is never silent.
220
+
221
+ ## Voice
222
+
223
+ Bots can join a voice channel and play audio but never listen. baff.js does not include a media library: `channel.join()` (or `client.voice.join(channelId)`) returns the LiveKit address and token, and you connect with LiveKit's own SDK. See [Voice for bots](https://docs.baff.gg/voice). `client.voice.leave(serverId)` takes the bot out again.
224
+
225
+ ## What bots cannot do
226
+
227
+ baff.js only wraps routes a bot may call. Friends, creating servers, joining by invite, reporting, group DM calls, the portal, forwarding messages and voting in polls are refused by the platform for bot tokens, so there are no methods for them. Stage channels, soundboard, scheduled-message and AutoMod routes are not wrapped in this version.
228
+
229
+ ## Versioning
230
+
231
+ baff.js follows semver. Until 1.0, minor versions may change types and method names; the [changelog](./CHANGELOG.md) lists every change. The API shapes come from the baff.gg docs, so a new field on the server shows up in `raw` events and in structures' `toJSON()` before it gets a typed property.
232
+
233
+ ## Development
234
+
235
+ ```bash
236
+ npm install
237
+ npm run typecheck # strict tsc over src and test, including the type tests
238
+ npm test # builds the tests and runs them against a fake gateway and REST server
239
+ npm run build # dist/esm and dist/cjs with declarations
240
+ ```
241
+
242
+ ## License
243
+
244
+ Free to use for building anything that works with baff.gg. Not open source: see the LICENSE file.
@@ -0,0 +1,51 @@
1
+ import type { APIEmbed } from '../types.js';
2
+ export declare const EmbedLimits: {
3
+ readonly embeds: 10;
4
+ readonly fields: 25;
5
+ readonly title: 256;
6
+ readonly description: 4096;
7
+ readonly fieldName: 256;
8
+ readonly fieldValue: 1024;
9
+ readonly footer: 2048;
10
+ readonly author: 256;
11
+ readonly total: 6000;
12
+ };
13
+ export type ColorResolvable = number | `#${string}` | readonly [number, number, number];
14
+ export declare function resolveColor(color: ColorResolvable): number;
15
+ export declare class EmbedBuilder {
16
+ readonly data: APIEmbed;
17
+ constructor(data?: APIEmbed);
18
+ setTitle(title: string | null): this;
19
+ setDescription(description: string | null): this;
20
+ setURL(value: string | null): this;
21
+ setColor(color: ColorResolvable | null): this;
22
+ setTimestamp(timestamp?: Date | number | string | null): this;
23
+ setAuthor(author: {
24
+ name: string;
25
+ url?: string;
26
+ iconURL?: string;
27
+ } | null): this;
28
+ setFooter(footer: {
29
+ text: string;
30
+ iconURL?: string;
31
+ } | null): this;
32
+ setThumbnail(imageURL: string | null): this;
33
+ setImage(imageURL: string | null): this;
34
+ addFields(...fields: {
35
+ name: string;
36
+ value: string;
37
+ inline?: boolean;
38
+ }[]): this;
39
+ setFields(...fields: {
40
+ name: string;
41
+ value: string;
42
+ inline?: boolean;
43
+ }[]): this;
44
+ spliceFields(index: number, deleteCount: number, ...fields: {
45
+ name: string;
46
+ value: string;
47
+ inline?: boolean;
48
+ }[]): this;
49
+ get length(): number;
50
+ toJSON(): APIEmbed;
51
+ }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EmbedBuilder = exports.EmbedLimits = void 0;
4
+ exports.resolveColor = resolveColor;
5
+ const BaffError_js_1 = require("../errors/BaffError.js");
6
+ const validate_js_1 = require("./validate.js");
7
+ exports.EmbedLimits = {
8
+ embeds: 10,
9
+ fields: 25,
10
+ title: 256,
11
+ description: 4096,
12
+ fieldName: 256,
13
+ fieldValue: 1024,
14
+ footer: 2048,
15
+ author: 256,
16
+ total: 6000
17
+ };
18
+ function resolveColor(color) {
19
+ if (typeof color === 'number')
20
+ return (0, validate_js_1.range)('INVALID_EMBED', 'color', color, 0, 0xffffff);
21
+ if (typeof color === 'string') {
22
+ if (!/^#[0-9a-f]{6}$/i.test(color))
23
+ return (0, validate_js_1.fail)('INVALID_EMBED', 'color', 'must be #rrggbb.');
24
+ return parseInt(color.slice(1), 16);
25
+ }
26
+ const [r, g, b] = color;
27
+ return ((0, validate_js_1.range)('INVALID_EMBED', 'color', r, 0, 255) << 16) | ((0, validate_js_1.range)('INVALID_EMBED', 'color', g, 0, 255) << 8) | (0, validate_js_1.range)('INVALID_EMBED', 'color', b, 0, 255);
28
+ }
29
+ class EmbedBuilder {
30
+ data;
31
+ constructor(data = {}) {
32
+ this.data = { ...data, fields: data.fields ? data.fields.map((f) => ({ ...f })) : undefined };
33
+ }
34
+ setTitle(title) {
35
+ this.data.title = title ?? undefined;
36
+ return this;
37
+ }
38
+ setDescription(description) {
39
+ this.data.description = description ?? undefined;
40
+ return this;
41
+ }
42
+ setURL(value) {
43
+ this.data.url = value ?? undefined;
44
+ return this;
45
+ }
46
+ setColor(color) {
47
+ this.data.color = color === null ? undefined : resolveColor(color);
48
+ return this;
49
+ }
50
+ setTimestamp(timestamp = new Date()) {
51
+ if (timestamp === null)
52
+ this.data.timestamp = undefined;
53
+ else {
54
+ const date = new Date(timestamp);
55
+ if (Number.isNaN(date.getTime()))
56
+ throw new BaffError_js_1.BaffError('INVALID_EMBED', 'timestamp is not a valid date.');
57
+ this.data.timestamp = date.toISOString();
58
+ }
59
+ return this;
60
+ }
61
+ setAuthor(author) {
62
+ this.data.author = author ? { name: author.name, url: author.url, icon_url: author.iconURL } : undefined;
63
+ return this;
64
+ }
65
+ setFooter(footer) {
66
+ this.data.footer = footer ? { text: footer.text, icon_url: footer.iconURL } : undefined;
67
+ return this;
68
+ }
69
+ setThumbnail(imageURL) {
70
+ this.data.thumbnail = imageURL ? { url: imageURL } : undefined;
71
+ return this;
72
+ }
73
+ setImage(imageURL) {
74
+ this.data.image = imageURL ? { url: imageURL } : undefined;
75
+ return this;
76
+ }
77
+ addFields(...fields) {
78
+ this.data.fields = [...(this.data.fields ?? []), ...fields];
79
+ return this;
80
+ }
81
+ setFields(...fields) {
82
+ this.data.fields = [...fields];
83
+ return this;
84
+ }
85
+ spliceFields(index, deleteCount, ...fields) {
86
+ const next = [...(this.data.fields ?? [])];
87
+ next.splice(index, deleteCount, ...fields);
88
+ this.data.fields = next;
89
+ return this;
90
+ }
91
+ get length() {
92
+ const d = this.data;
93
+ const size = (s) => [...(s ?? '')].length;
94
+ return size(d.title) + size(d.description) + size(d.footer?.text) + size(d.author?.name) + (d.fields ?? []).reduce((sum, f) => sum + size(f.name) + size(f.value), 0);
95
+ }
96
+ toJSON() {
97
+ const d = this.data;
98
+ const code = 'INVALID_EMBED';
99
+ const out = {};
100
+ const title = (0, validate_js_1.text)(code, 'title', d.title, exports.EmbedLimits.title, { optional: true });
101
+ const description = (0, validate_js_1.text)(code, 'description', d.description, exports.EmbedLimits.description, { optional: true });
102
+ if (title !== undefined)
103
+ out.title = title;
104
+ if (description !== undefined)
105
+ out.description = description;
106
+ const link = (0, validate_js_1.url)(code, 'url', d.url);
107
+ if (link)
108
+ out.url = link;
109
+ if (d.color !== undefined)
110
+ out.color = (0, validate_js_1.range)(code, 'color', d.color, 0, 0xffffff);
111
+ if (d.timestamp)
112
+ out.timestamp = d.timestamp;
113
+ if (d.footer) {
114
+ out.footer = { text: (0, validate_js_1.text)(code, 'footer.text', d.footer.text, exports.EmbedLimits.footer) };
115
+ const icon = (0, validate_js_1.url)(code, 'footer.iconURL', d.footer.icon_url);
116
+ if (icon)
117
+ out.footer.icon_url = icon;
118
+ }
119
+ if (d.author) {
120
+ out.author = { name: (0, validate_js_1.text)(code, 'author.name', d.author.name, exports.EmbedLimits.author) };
121
+ const authorUrl = (0, validate_js_1.url)(code, 'author.url', d.author.url);
122
+ const icon = (0, validate_js_1.url)(code, 'author.iconURL', d.author.icon_url);
123
+ if (authorUrl)
124
+ out.author.url = authorUrl;
125
+ if (icon)
126
+ out.author.icon_url = icon;
127
+ }
128
+ for (const key of ['image', 'thumbnail']) {
129
+ const media = d[key];
130
+ if (media)
131
+ out[key] = { url: (0, validate_js_1.url)(code, key, media.url, false) };
132
+ }
133
+ if (d.fields?.length) {
134
+ if (d.fields.length > exports.EmbedLimits.fields)
135
+ (0, validate_js_1.fail)(code, 'fields', `can have up to ${exports.EmbedLimits.fields} entries.`);
136
+ out.fields = d.fields.map((f, i) => ({
137
+ name: (0, validate_js_1.text)(code, `fields[${i}].name`, f.name, exports.EmbedLimits.fieldName),
138
+ value: (0, validate_js_1.text)(code, `fields[${i}].value`, f.value, exports.EmbedLimits.fieldValue),
139
+ inline: f.inline ?? false
140
+ }));
141
+ }
142
+ if (!Object.keys(out).filter((k) => k !== 'color' && k !== 'timestamp' && k !== 'url').length) {
143
+ (0, validate_js_1.fail)(code, 'embed', 'is empty: give it a title, description, fields, image, thumbnail, author or footer.');
144
+ }
145
+ if (this.length > exports.EmbedLimits.total)
146
+ (0, validate_js_1.fail)(code, 'embed', `has ${this.length} characters; the limit is ${exports.EmbedLimits.total} across all of a message's embeds.`);
147
+ return out;
148
+ }
149
+ }
150
+ exports.EmbedBuilder = EmbedBuilder;
@@ -0,0 +1,69 @@
1
+ import type { APICommand, APICommandOption } from '../types.js';
2
+ declare abstract class Named {
3
+ protected name?: string;
4
+ protected description?: string;
5
+ setName(name: string): this;
6
+ setDescription(description: string): this;
7
+ protected header(path: string): {
8
+ name: string;
9
+ description: string;
10
+ };
11
+ }
12
+ export declare abstract class OptionBuilder extends Named {
13
+ protected required: boolean;
14
+ abstract readonly type: string;
15
+ setRequired(required?: boolean): this;
16
+ toJSON(path?: string): APICommandOption;
17
+ }
18
+ export declare class StringOption extends OptionBuilder {
19
+ readonly type = "string";
20
+ }
21
+ export declare class IntegerOption extends OptionBuilder {
22
+ readonly type = "integer";
23
+ }
24
+ export declare class NumberOption extends OptionBuilder {
25
+ readonly type = "number";
26
+ }
27
+ export declare class BooleanOption extends OptionBuilder {
28
+ readonly type = "boolean";
29
+ }
30
+ export declare class UserOption extends OptionBuilder {
31
+ readonly type = "user";
32
+ }
33
+ export declare class ChannelOption extends OptionBuilder {
34
+ readonly type = "channel";
35
+ }
36
+ export declare class RoleOption extends OptionBuilder {
37
+ readonly type = "role";
38
+ }
39
+ type OptionInput<T> = T | ((option: T) => T);
40
+ declare class OptionHolder extends Named {
41
+ protected plain: OptionBuilder[];
42
+ addStringOption(option: OptionInput<StringOption>): this;
43
+ addIntegerOption(option: OptionInput<IntegerOption>): this;
44
+ addNumberOption(option: OptionInput<NumberOption>): this;
45
+ addBooleanOption(option: OptionInput<BooleanOption>): this;
46
+ addUserOption(option: OptionInput<UserOption>): this;
47
+ addChannelOption(option: OptionInput<ChannelOption>): this;
48
+ addRoleOption(option: OptionInput<RoleOption>): this;
49
+ }
50
+ export declare class SubcommandBuilder extends OptionHolder {
51
+ toJSON(path?: string): APICommandOption;
52
+ }
53
+ export declare class SubcommandGroupBuilder extends Named {
54
+ private subcommands;
55
+ addSubcommand(sub: OptionInput<SubcommandBuilder>): this;
56
+ toJSON(path?: string): APICommandOption;
57
+ }
58
+ export declare class SlashCommandBuilder extends OptionHolder {
59
+ private nested;
60
+ addSubcommand(sub: OptionInput<SubcommandBuilder>): this;
61
+ addSubcommandGroup(group: OptionInput<SubcommandGroupBuilder>): this;
62
+ toJSON(): APICommand;
63
+ }
64
+ export declare class PrefixCommandBuilder extends Named {
65
+ private prefix?;
66
+ setPrefix(prefix: string): this;
67
+ toJSON(): APICommand;
68
+ }
69
+ export {};