chapterjs 0.1.2 → 0.2.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 (318) hide show
  1. package/dist/assets/asset.d.ts +46 -0
  2. package/dist/assets/asset.js +86 -0
  3. package/dist/assets/public.d.ts +19 -0
  4. package/dist/assets/public.js +120 -0
  5. package/dist/cache/cache.d.ts +62 -0
  6. package/dist/cache/cache.js +80 -0
  7. package/dist/cache/store.d.ts +45 -0
  8. package/dist/cache/store.js +53 -0
  9. package/dist/cli/build.d.ts +41 -0
  10. package/dist/cli/build.js +205 -0
  11. package/dist/cli/cli.js +108 -25
  12. package/dist/cli/cluster.d.ts +70 -0
  13. package/dist/cli/cluster.js +213 -0
  14. package/dist/cli/dev.d.ts +18 -1
  15. package/dist/cli/dev.js +253 -198
  16. package/dist/cli/env.d.ts +31 -0
  17. package/dist/cli/env.js +93 -0
  18. package/dist/cli/log.d.ts +13 -0
  19. package/dist/cli/log.js +17 -0
  20. package/dist/cli/node-version.d.ts +8 -0
  21. package/dist/cli/node-version.js +13 -0
  22. package/dist/cli/preflight.d.ts +36 -0
  23. package/dist/cli/preflight.js +122 -0
  24. package/dist/cli/project.d.ts +135 -0
  25. package/dist/cli/project.js +517 -0
  26. package/dist/cli/start.d.ts +25 -0
  27. package/dist/cli/start.js +225 -0
  28. package/dist/commands/autocomplete.d.ts +13 -0
  29. package/dist/commands/autocomplete.js +159 -0
  30. package/dist/commands/command.d.ts +360 -0
  31. package/dist/commands/command.js +28 -0
  32. package/dist/commands/declaration.d.ts +77 -0
  33. package/dist/commands/declaration.js +492 -0
  34. package/dist/commands/register.d.ts +18 -0
  35. package/dist/commands/register.js +42 -0
  36. package/dist/commands/router.d.ts +20 -0
  37. package/dist/commands/router.js +228 -0
  38. package/dist/commands/tree.d.ts +47 -0
  39. package/dist/commands/tree.js +216 -0
  40. package/dist/commands/twins.d.ts +15 -0
  41. package/dist/commands/twins.js +78 -0
  42. package/dist/components/button.d.ts +79 -0
  43. package/dist/components/button.js +106 -0
  44. package/dist/components/component.d.ts +140 -0
  45. package/dist/components/component.js +39 -0
  46. package/dist/components/custom-id.d.ts +72 -0
  47. package/dist/components/custom-id.js +128 -0
  48. package/dist/components/declaration.d.ts +42 -0
  49. package/dist/components/declaration.js +342 -0
  50. package/dist/components/declared.d.ts +29 -0
  51. package/dist/components/declared.js +49 -0
  52. package/dist/components/embed.d.ts +29 -0
  53. package/dist/components/embed.js +11 -0
  54. package/dist/components/instance.d.ts +100 -0
  55. package/dist/components/instance.js +86 -0
  56. package/dist/components/layout.d.ts +103 -0
  57. package/dist/components/layout.js +279 -0
  58. package/dist/components/modal.d.ts +245 -0
  59. package/dist/components/modal.js +398 -0
  60. package/dist/components/options.d.ts +41 -0
  61. package/dist/components/options.js +101 -0
  62. package/dist/components/render.d.ts +12 -0
  63. package/dist/components/render.js +58 -0
  64. package/dist/components/router.d.ts +19 -0
  65. package/dist/components/router.js +251 -0
  66. package/dist/components/select.d.ts +146 -0
  67. package/dist/components/select.js +179 -0
  68. package/dist/core/bot.d.ts +99 -0
  69. package/dist/core/bot.js +174 -0
  70. package/dist/core/memory.d.ts +66 -0
  71. package/dist/core/memory.js +115 -0
  72. package/dist/discord/api.d.ts +112 -0
  73. package/dist/discord/api.js +115 -0
  74. package/dist/discord/cdn.d.ts +67 -0
  75. package/dist/discord/cdn.js +100 -0
  76. package/dist/discord/codes.d.ts +171 -0
  77. package/dist/discord/codes.js +421 -0
  78. package/dist/discord/endpoint.d.ts +56 -0
  79. package/dist/discord/endpoint.js +53 -0
  80. package/dist/discord/endpoints.d.ts +1529 -0
  81. package/dist/discord/endpoints.js +1139 -0
  82. package/dist/discord/formatting.d.ts +67 -0
  83. package/dist/discord/formatting.js +87 -0
  84. package/dist/discord/index.d.ts +10 -0
  85. package/dist/discord/index.js +13 -0
  86. package/dist/discord/intents.d.ts +48 -0
  87. package/dist/discord/intents.js +128 -0
  88. package/dist/discord/permissions.d.ts +94 -0
  89. package/dist/discord/permissions.js +185 -0
  90. package/dist/discord/snowflake.d.ts +21 -0
  91. package/dist/discord/snowflake.js +36 -0
  92. package/dist/discord/types/application-command.d.ts +332 -0
  93. package/dist/discord/types/application-command.js +48 -0
  94. package/dist/discord/types/application-role-connection-metadata.d.ts +42 -0
  95. package/dist/discord/types/application-role-connection-metadata.js +22 -0
  96. package/dist/discord/types/application.d.ts +208 -0
  97. package/dist/discord/types/application.js +48 -0
  98. package/dist/discord/types/audit-log.d.ts +255 -0
  99. package/dist/discord/types/audit-log.js +144 -0
  100. package/dist/discord/types/auto-moderation.d.ts +167 -0
  101. package/dist/discord/types/auto-moderation.js +52 -0
  102. package/dist/discord/types/channel.d.ts +557 -0
  103. package/dist/discord/types/channel.js +88 -0
  104. package/dist/discord/types/common.d.ts +54 -0
  105. package/dist/discord/types/common.js +38 -0
  106. package/dist/discord/types/component.d.ts +775 -0
  107. package/dist/discord/types/component.js +76 -0
  108. package/dist/discord/types/emoji.d.ts +74 -0
  109. package/dist/discord/types/emoji.js +1 -0
  110. package/dist/discord/types/entitlement.d.ts +84 -0
  111. package/dist/discord/types/entitlement.js +22 -0
  112. package/dist/discord/types/gateway-events.d.ts +1148 -0
  113. package/dist/discord/types/gateway-events.js +54 -0
  114. package/dist/discord/types/gateway.d.ts +38 -0
  115. package/dist/discord/types/gateway.js +1 -0
  116. package/dist/discord/types/guild-scheduled-event.d.ts +257 -0
  117. package/dist/discord/types/guild-scheduled-event.js +68 -0
  118. package/dist/discord/types/guild-template.d.ts +51 -0
  119. package/dist/discord/types/guild-template.js +1 -0
  120. package/dist/discord/types/guild.d.ts +910 -0
  121. package/dist/discord/types/guild.js +153 -0
  122. package/dist/discord/types/index.d.ts +31 -0
  123. package/dist/discord/types/index.js +31 -0
  124. package/dist/discord/types/interaction.d.ts +321 -0
  125. package/dist/discord/types/interaction.js +47 -0
  126. package/dist/discord/types/invite.d.ts +158 -0
  127. package/dist/discord/types/invite.js +39 -0
  128. package/dist/discord/types/message.d.ts +834 -0
  129. package/dist/discord/types/message.js +159 -0
  130. package/dist/discord/types/oauth2.d.ts +22 -0
  131. package/dist/discord/types/oauth2.js +1 -0
  132. package/dist/discord/types/permissions.d.ts +183 -0
  133. package/dist/discord/types/permissions.js +118 -0
  134. package/dist/discord/types/poll.d.ts +106 -0
  135. package/dist/discord/types/poll.js +8 -0
  136. package/dist/discord/types/sku.d.ts +47 -0
  137. package/dist/discord/types/sku.js +26 -0
  138. package/dist/discord/types/soundboard.d.ts +71 -0
  139. package/dist/discord/types/soundboard.js +1 -0
  140. package/dist/discord/types/stage-instance.d.ts +58 -0
  141. package/dist/discord/types/stage-instance.js +10 -0
  142. package/dist/discord/types/sticker.d.ts +115 -0
  143. package/dist/discord/types/sticker.js +20 -0
  144. package/dist/discord/types/subscription.d.ts +54 -0
  145. package/dist/discord/types/subscription.js +12 -0
  146. package/dist/discord/types/team.d.ts +55 -0
  147. package/dist/discord/types/team.js +21 -0
  148. package/dist/discord/types/user.d.ts +273 -0
  149. package/dist/discord/types/user.js +83 -0
  150. package/dist/discord/types/voice.d.ts +72 -0
  151. package/dist/discord/types/voice.js +1 -0
  152. package/dist/discord/types/webhook.d.ts +184 -0
  153. package/dist/discord/types/webhook.js +12 -0
  154. package/dist/events/declaration.d.ts +16 -0
  155. package/dist/events/declaration.js +80 -0
  156. package/dist/events/event.d.ts +45 -0
  157. package/dist/events/event.js +34 -0
  158. package/dist/events/registry.d.ts +697 -0
  159. package/dist/events/registry.js +839 -0
  160. package/dist/events/router.d.ts +46 -0
  161. package/dist/events/router.js +133 -0
  162. package/dist/gateway/errors.d.ts +18 -0
  163. package/dist/gateway/errors.js +47 -0
  164. package/dist/gateway/gateway.d.ts +64 -0
  165. package/dist/gateway/gateway.js +117 -0
  166. package/dist/gateway/identify-queue.d.ts +25 -0
  167. package/dist/gateway/identify-queue.js +29 -0
  168. package/dist/gateway/shard.d.ts +94 -0
  169. package/dist/gateway/shard.js +414 -0
  170. package/dist/gateway/state.d.ts +11 -0
  171. package/dist/gateway/state.js +184 -0
  172. package/dist/index.d.ts +57 -18
  173. package/dist/index.js +40 -12
  174. package/dist/interactions/cooldown.d.ts +33 -0
  175. package/dist/interactions/cooldown.js +74 -0
  176. package/dist/interactions/dispatch.d.ts +91 -0
  177. package/dist/interactions/dispatch.js +110 -0
  178. package/dist/loader/generated.d.ts +10 -0
  179. package/dist/loader/generated.js +80 -0
  180. package/dist/loader/hot.d.ts +13 -0
  181. package/dist/loader/hot.js +85 -0
  182. package/dist/loader/loader.d.ts +85 -0
  183. package/dist/loader/loader.js +119 -0
  184. package/dist/loader/locate.d.ts +10 -0
  185. package/dist/loader/locate.js +36 -0
  186. package/dist/loader/watch.d.ts +17 -0
  187. package/dist/loader/watch.js +66 -0
  188. package/dist/main.d.ts +2 -0
  189. package/dist/main.js +5 -0
  190. package/dist/messages/commands.d.ts +23 -0
  191. package/dist/messages/commands.js +142 -0
  192. package/dist/messages/declaration.d.ts +89 -0
  193. package/dist/messages/declaration.js +455 -0
  194. package/dist/messages/messages.d.ts +241 -0
  195. package/dist/messages/messages.js +31 -0
  196. package/dist/messages/phrases.d.ts +23 -0
  197. package/dist/messages/phrases.js +44 -0
  198. package/dist/messages/translate.d.ts +100 -0
  199. package/dist/messages/translate.js +249 -0
  200. package/dist/presence/declaration.d.ts +19 -0
  201. package/dist/presence/declaration.js +118 -0
  202. package/dist/presence/presence.d.ts +57 -0
  203. package/dist/presence/presence.js +27 -0
  204. package/dist/rest/errors.d.ts +55 -0
  205. package/dist/rest/errors.js +92 -0
  206. package/dist/rest/rate-limiter.d.ts +46 -0
  207. package/dist/rest/rate-limiter.js +153 -0
  208. package/dist/rest/rest.d.ts +63 -0
  209. package/dist/rest/rest.js +194 -0
  210. package/dist/structures/base.d.ts +48 -0
  211. package/dist/structures/base.js +84 -0
  212. package/dist/structures/channel.d.ts +380 -0
  213. package/dist/structures/channel.js +627 -0
  214. package/dist/structures/context.d.ts +30 -0
  215. package/dist/structures/context.js +7 -0
  216. package/dist/structures/emoji.d.ts +46 -0
  217. package/dist/structures/emoji.js +65 -0
  218. package/dist/structures/entities.d.ts +62 -0
  219. package/dist/structures/entities.js +226 -0
  220. package/dist/structures/guild.d.ts +348 -0
  221. package/dist/structures/guild.js +466 -0
  222. package/dist/structures/interaction.d.ts +197 -0
  223. package/dist/structures/interaction.js +396 -0
  224. package/dist/structures/invite.d.ts +38 -0
  225. package/dist/structures/invite.js +57 -0
  226. package/dist/structures/known.d.ts +28 -0
  227. package/dist/structures/known.js +58 -0
  228. package/dist/structures/member.d.ts +140 -0
  229. package/dist/structures/member.js +260 -0
  230. package/dist/structures/message.d.ts +197 -0
  231. package/dist/structures/message.js +268 -0
  232. package/dist/structures/payload.d.ts +91 -0
  233. package/dist/structures/payload.js +184 -0
  234. package/dist/structures/role.d.ts +69 -0
  235. package/dist/structures/role.js +112 -0
  236. package/dist/structures/user.d.ts +59 -0
  237. package/dist/structures/user.js +94 -0
  238. package/dist/structures/webhook.d.ts +46 -0
  239. package/dist/structures/webhook.js +79 -0
  240. package/dist/tasks/declaration.d.ts +16 -0
  241. package/dist/tasks/declaration.js +56 -0
  242. package/dist/tasks/schedule.d.ts +54 -0
  243. package/dist/tasks/schedule.js +236 -0
  244. package/dist/tasks/scheduler.d.ts +35 -0
  245. package/dist/tasks/scheduler.js +107 -0
  246. package/dist/tasks/task.d.ts +75 -0
  247. package/dist/tasks/task.js +27 -0
  248. package/dist/util/case.d.ts +13 -0
  249. package/dist/util/case.js +54 -0
  250. package/dist/util/duration.d.ts +8 -0
  251. package/dist/util/duration.js +29 -0
  252. package/dist/util/websocket.d.ts +9 -0
  253. package/dist/util/websocket.js +31 -0
  254. package/dist/voice/connection.d.ts +90 -0
  255. package/dist/voice/connection.js +494 -0
  256. package/dist/voice/dave.d.ts +109 -0
  257. package/dist/voice/dave.js +245 -0
  258. package/dist/voice/opus.d.ts +19 -0
  259. package/dist/voice/opus.js +273 -0
  260. package/dist/voice/session.d.ts +55 -0
  261. package/dist/voice/session.js +405 -0
  262. package/dist/voice/source.d.ts +22 -0
  263. package/dist/voice/source.js +195 -0
  264. package/dist/voice/transport.d.ts +43 -0
  265. package/dist/voice/transport.js +140 -0
  266. package/dist/voice/usage.d.ts +4 -0
  267. package/dist/voice/usage.js +33 -0
  268. package/package.json +26 -7
  269. package/vendor/dave/LICENSE +231 -0
  270. package/vendor/dave/libdave.d.mts +99 -0
  271. package/vendor/dave/libdave.mjs +2 -0
  272. package/vendor/dave/libdave.wasm +0 -0
  273. package/dist/components.d.ts +0 -97
  274. package/dist/components.js +0 -260
  275. package/dist/context.d.ts +0 -72
  276. package/dist/context.js +0 -273
  277. package/dist/discord/client.d.ts +0 -35
  278. package/dist/discord/client.js +0 -66
  279. package/dist/discord/constants.d.ts +0 -49
  280. package/dist/discord/constants.js +0 -61
  281. package/dist/discord/events.d.ts +0 -129
  282. package/dist/discord/events.js +0 -102
  283. package/dist/discord/gateway.d.ts +0 -61
  284. package/dist/discord/gateway.js +0 -320
  285. package/dist/discord/payloads.d.ts +0 -275
  286. package/dist/discord/payloads.js +0 -3
  287. package/dist/discord/presence.d.ts +0 -42
  288. package/dist/discord/presence.js +0 -51
  289. package/dist/discord/rest.d.ts +0 -49
  290. package/dist/discord/rest.js +0 -271
  291. package/dist/discord/types.d.ts +0 -334
  292. package/dist/discord/types.js +0 -4
  293. package/dist/events.d.ts +0 -94
  294. package/dist/events.js +0 -255
  295. package/dist/presence.d.ts +0 -92
  296. package/dist/presence.js +0 -250
  297. package/dist/structures/Channel.d.ts +0 -110
  298. package/dist/structures/Channel.js +0 -171
  299. package/dist/structures/Guild.d.ts +0 -70
  300. package/dist/structures/Guild.js +0 -105
  301. package/dist/structures/Interaction.d.ts +0 -113
  302. package/dist/structures/Interaction.js +0 -274
  303. package/dist/structures/Member.d.ts +0 -70
  304. package/dist/structures/Member.js +0 -124
  305. package/dist/structures/Message.d.ts +0 -87
  306. package/dist/structures/Message.js +0 -182
  307. package/dist/structures/Reaction.d.ts +0 -38
  308. package/dist/structures/Reaction.js +0 -64
  309. package/dist/structures/Role.d.ts +0 -43
  310. package/dist/structures/Role.js +0 -77
  311. package/dist/structures/User.d.ts +0 -41
  312. package/dist/structures/User.js +0 -79
  313. package/dist/structures/utils.d.ts +0 -72
  314. package/dist/structures/utils.js +0 -59
  315. package/dist/utils/errors.d.ts +0 -5
  316. package/dist/utils/errors.js +0 -28
  317. package/dist/utils/modules.d.ts +0 -39
  318. package/dist/utils/modules.js +0 -156
@@ -0,0 +1,251 @@
1
+ // Runs the component someone used: reads the id the framework wrote in it,
2
+ // finds its declaration, turns what Discord sent into what `run` receives,
3
+ // and makes sure the person always gets an answer.
4
+ import { ComponentType, } from '../discord/types/component.js';
5
+ import { InteractionType, } from '../discord/types/interaction.js';
6
+ import { MessageFlags } from '../discord/types/message.js';
7
+ import { authorOf, placeContext, refuse, resolvePlace, runInteraction, } from '../interactions/dispatch.js';
8
+ import { audienceLocale, translation } from '../messages/translate.js';
9
+ import { ComponentInteraction, ModalInteraction, } from '../structures/interaction.js';
10
+ import { remember } from '../structures/known.js';
11
+ import { toCamelCase } from '../util/case.js';
12
+ import { decodeCustomId, readData } from './custom-id.js';
13
+ import { SELECT_TYPES } from './select.js';
14
+ /** What a component is called, for the person and the developer. */
15
+ const WHAT_OF = {
16
+ button: 'button',
17
+ select: 'menu',
18
+ modal: 'form',
19
+ };
20
+ export class ComponentRouter {
21
+ #components = new Map();
22
+ #options;
23
+ constructor(options) {
24
+ this.#options = options;
25
+ }
26
+ /** Replaces every component (after a load or a reload). */
27
+ set(entries) {
28
+ const components = new Map();
29
+ for (const entry of entries) {
30
+ if (entry.component.kind !== 'embed') {
31
+ components.set(`${entry.component.kind}:${entry.component.name}`, entry);
32
+ }
33
+ }
34
+ this.#components = components;
35
+ }
36
+ /** Handles an Interaction Create event, if it is a component or a form. */
37
+ dispatch(ctx, raw) {
38
+ const isComponent = raw.type === InteractionType.MessageComponent;
39
+ const isModal = raw.type === InteractionType.ModalSubmit;
40
+ if (!isComponent && !isModal)
41
+ return;
42
+ const data = raw.data;
43
+ if (!data || typeof data.custom_id !== 'string')
44
+ return;
45
+ const author = authorOf(raw);
46
+ if (!author)
47
+ return;
48
+ const user = ctx.entities.user(author.user);
49
+ const { user: _user, member: _member, message: rawMessage, ...rest } = raw;
50
+ const message = rawMessage
51
+ ? ctx.entities.message(rawMessage, raw.guild_id)
52
+ : null;
53
+ // Only the person sees an ephemeral message: what answers a component
54
+ // on it (an update of that message, a reply) stays with them, whatever
55
+ // the file says, so `t` speaks their language.
56
+ // https://docs.discord.com/developers/resources/message#message-object-message-flags
57
+ const isPrivate = ((rawMessage?.flags ?? 0) &
58
+ MessageFlags.Ephemeral) !==
59
+ 0;
60
+ const ephemeralOf = (component) => isPrivate || ('ephemeral' in component && component.ephemeral);
61
+ const { name, parts } = decodeCustomId(data.custom_id);
62
+ // A button, a menu and a form may share a name: what was used says
63
+ // which one is meant.
64
+ const kind = isModal
65
+ ? 'modal'
66
+ : data.component_type ===
67
+ ComponentType.Button
68
+ ? 'button'
69
+ : 'select';
70
+ const entry = this.#components.get(`${kind}:${name}`);
71
+ const what = WHAT_OF[kind];
72
+ const interaction = isModal
73
+ ? new ModalInteraction(ctx, rest, user, {
74
+ ephemeral: entry ? ephemeralOf(entry.component) : isPrivate,
75
+ message,
76
+ })
77
+ : new ComponentInteraction(ctx, rest, user, {
78
+ ephemeral: entry ? ephemeralOf(entry.component) : isPrivate,
79
+ message: message,
80
+ });
81
+ if (isComponent && !message)
82
+ return;
83
+ // Embeds are never in the map: what is left has a `run`.
84
+ if (!entry ||
85
+ entry.component.kind === 'embed' ||
86
+ (entry.component.kind === 'select' &&
87
+ SELECT_TYPES[entry.component.type] !==
88
+ data.component_type)) {
89
+ // Sent before the export was renamed, removed or changed of kind.
90
+ refuse(interaction, 'gone', { what });
91
+ return;
92
+ }
93
+ const { component, file } = entry;
94
+ const values = readData(component.data, parts);
95
+ if (!values) {
96
+ // The data of the component changed since the message was sent.
97
+ refuse(interaction, 'outdated', { what });
98
+ return;
99
+ }
100
+ const placed = resolvePlace(ctx, raw, user, author.member, component.where, warning => this.#options.onWarning(file, `${name} ${warning}`));
101
+ if (!placed.place) {
102
+ refuse(interaction, placed.refusal, { what });
103
+ return;
104
+ }
105
+ const { place } = placed;
106
+ if (component.kind !== 'modal' &&
107
+ component.who === 'author' &&
108
+ rawMessage?.interaction_metadata?.user.id !== user.id) {
109
+ refuse(interaction, 'authorOnly', { what });
110
+ return;
111
+ }
112
+ remember(interaction, {
113
+ guild: place.guild ?? undefined,
114
+ member: place.member ?? undefined,
115
+ channel: place.channel,
116
+ });
117
+ let specific;
118
+ try {
119
+ specific =
120
+ component.kind === 'modal'
121
+ ? {
122
+ fields: this.#readFields(ctx, raw, component.fields, data),
123
+ }
124
+ : component.kind === 'select'
125
+ ? this.#readValues(ctx, raw, component.type, data)
126
+ : {};
127
+ }
128
+ catch (error) {
129
+ this.#options.onError(file, error);
130
+ refuse(interaction, 'failed', { what });
131
+ return;
132
+ }
133
+ const context = Object.freeze({
134
+ interaction,
135
+ user,
136
+ message,
137
+ data: Object.freeze(values),
138
+ ...specific,
139
+ ...placeContext(place, component.where),
140
+ // `t` speaks the language of who will read the answer.
141
+ ...translation(ctx, audienceLocale({
142
+ person: raw.locale,
143
+ guild: place.guild,
144
+ ephemeral: ephemeralOf(component),
145
+ })),
146
+ });
147
+ runInteraction({
148
+ interaction,
149
+ file,
150
+ name,
151
+ what,
152
+ reporter: this.#options,
153
+ // Nothing changes on screen while a component is answered; a form
154
+ // sent is like a command used.
155
+ defer: () => interaction instanceof ComponentInteraction
156
+ ? interaction.deferUpdate()
157
+ : interaction.defer(),
158
+ unanswered: `finished without answering: the person sees "This interaction failed". Call interaction.update() or interaction.reply() in run${component.kind === 'modal' ? '' : ', or interaction.deferUpdate() to change nothing'}.`,
159
+ run: () => component.run(context),
160
+ });
161
+ }
162
+ /** What the person picked in a menu, as structures where it applies. */
163
+ #readValues(ctx, raw, type, data) {
164
+ const values = resolveValues(ctx, raw, type, data.values ?? [], data.resolved ?? {});
165
+ return { values, value: values[0] };
166
+ }
167
+ /** What the person filled in, field by field. */
168
+ #readFields(ctx, raw, fields, data) {
169
+ const sent = new Map();
170
+ for (const top of data.components ?? []) {
171
+ if (top.type === ComponentType.Label && top.component?.custom_id) {
172
+ sent.set(top.component.custom_id, top.component);
173
+ }
174
+ }
175
+ const resolved = data.resolved ?? {};
176
+ const result = {};
177
+ for (const { name, field, required } of fields) {
178
+ if (field.type === 'note')
179
+ continue;
180
+ const input = sent.get(name);
181
+ switch (field.type) {
182
+ case 'text': {
183
+ const text = input && 'value' in input ? input.value : '';
184
+ result[name] = text === '' && !required ? undefined : text;
185
+ break;
186
+ }
187
+ case 'radio': {
188
+ const value = input && 'value' in input ? input.value : null;
189
+ result[name] = value === null || value === '' ? undefined : value;
190
+ break;
191
+ }
192
+ case 'checkbox':
193
+ result[name] =
194
+ input && 'value' in input ? input.value === true : false;
195
+ break;
196
+ case 'select':
197
+ case 'checkboxes':
198
+ result[name] =
199
+ input && 'values' in input ? [...input.values] : [];
200
+ break;
201
+ case 'files':
202
+ result[name] = (input && 'values' in input ? input.values : [])
203
+ .map(id => {
204
+ const attachment = resolved.attachments?.[id];
205
+ return attachment ? toCamelCase(attachment) : undefined;
206
+ })
207
+ .filter(one => one !== undefined);
208
+ break;
209
+ default:
210
+ result[name] = resolveValues(ctx, raw, field.type, input && 'values' in input ? input.values : [], resolved);
211
+ }
212
+ }
213
+ return result;
214
+ }
215
+ }
216
+ /** The things picked in a menu of the server, as structures. */
217
+ function resolveValues(ctx, raw, type, ids, resolved) {
218
+ if (type === 'string')
219
+ return [...ids];
220
+ const guildId = raw.guild_id;
221
+ const userOf = (id) => {
222
+ const rawUser = resolved.users?.[id];
223
+ if (!rawUser)
224
+ return undefined;
225
+ const member = resolved.members?.[id];
226
+ if (member && guildId) {
227
+ ctx.entities.member(guildId, member, rawUser);
228
+ }
229
+ return ctx.entities.user(rawUser);
230
+ };
231
+ const roleOf = (id) => {
232
+ const role = resolved.roles?.[id];
233
+ return role && guildId ? ctx.entities.role(guildId, role) : undefined;
234
+ };
235
+ const channelOf = (id) => {
236
+ const partial = resolved.channels?.[id];
237
+ return (ctx.cache.channels.get(id) ??
238
+ (partial
239
+ ? ctx.entities.channel(partial, guildId)
240
+ : undefined));
241
+ };
242
+ return ids
243
+ .map(id => type === 'user'
244
+ ? userOf(id)
245
+ : type === 'role'
246
+ ? roleOf(id)
247
+ : type === 'channel'
248
+ ? channelOf(id)
249
+ : (userOf(id) ?? roleOf(id)))
250
+ .filter(one => one !== undefined);
251
+ }
@@ -0,0 +1,146 @@
1
+ import type { Snowflake } from '../discord/types/common.js';
2
+ import { ChannelType } from '../discord/types/channel.js';
3
+ import { type RawSelectMenu } from '../discord/types/component.js';
4
+ import type { Channel } from '../structures/channel.js';
5
+ import type { ComponentInteraction } from '../structures/interaction.js';
6
+ import type { Role } from '../structures/role.js';
7
+ import type { User } from '../structures/user.js';
8
+ import type { TranslationContext } from '../messages/messages.js';
9
+ import { type DynamicText } from './component.js';
10
+ import type { ComponentWhere, InteractiveConfig, InteractiveContext, PlaceOf } from './component.js';
11
+ import { type DataShape, type DataInputOf } from './custom-id.js';
12
+ import type { Rendered, SelectComponent } from './instance.js';
13
+ import { type LoadedOption, type OptionValue, type SelectOptions } from './options.js';
14
+ /** The kinds of menus: texts you declare, or things of the server. */
15
+ export type SelectType = 'string' | 'user' | 'role' | 'mentionable' | 'channel';
16
+ /** The kinds of menus whose options are things of the server. */
17
+ export type EntitySelectType = Exclude<SelectType, 'string'>;
18
+ export declare const SELECT_TYPES: Record<SelectType, RawSelectMenu['type']>;
19
+ /** What `run` receives for one picked thing, by kind of menu. */
20
+ interface EntityValues {
21
+ user: User;
22
+ role: Role;
23
+ mentionable: User | Role;
24
+ channel: Channel;
25
+ }
26
+ /** What a select menu looks like. */
27
+ export interface SelectLook<Data extends DataShape = DataShape> {
28
+ /**
29
+ * The text shown when nothing is picked (150 characters at most):
30
+ * written as is, or a function of `t` and the data, run when the
31
+ * message is sent.
32
+ */
33
+ placeholder?: DynamicText<Data>;
34
+ /** Shows the menu greyed out, impossible to use. */
35
+ disabled?: boolean;
36
+ }
37
+ /** What a message can change when it shows a menu. */
38
+ export interface SelectInstanceOptions extends SelectLook {
39
+ /**
40
+ * What is picked when the menu appears: the values of the options for a
41
+ * menu of texts, the ids (or the things themselves) for the others.
42
+ */
43
+ defaults?: readonly (string | {
44
+ id: Snowflake;
45
+ })[];
46
+ }
47
+ /** What every select file declares, whatever its kind. */
48
+ interface SelectConfigBase<Data extends DataShape, Where extends ComponentWhere> extends SelectLook<Data>, InteractiveConfig<Data, Where> {
49
+ /** How many things must be picked at least (0 to 25). 1 by default. */
50
+ min?: number;
51
+ /** How many things can be picked at most (1 to 25). 1 by default. */
52
+ max?: number;
53
+ }
54
+ /** What `run` receives when something is picked. */
55
+ export type SelectContext<Picked, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> = InteractiveContext<Data> & TranslationContext & {
56
+ /** What was picked, in the order of the menu. */
57
+ values: Picked[];
58
+ /** The first thing picked; `undefined` when nothing was (a menu with `min: 0`). */
59
+ value: Picked | undefined;
60
+ } & PlaceOf<Where, ComponentInteraction>;
61
+ /** A menu of texts you declare. */
62
+ export interface StringSelectConfig<Options extends SelectOptions, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> extends SelectConfigBase<Data, Where> {
63
+ /** The kind of menu: texts you declare, which is the default. */
64
+ type?: 'string';
65
+ /**
66
+ * The options (25 at most): a list of texts, an object from what is
67
+ * shown to what your code receives, or a list of `RichOption`.
68
+ */
69
+ options: Options;
70
+ /** What to do when something is picked. */
71
+ run: (context: SelectContext<OptionValue<Options>, Data, Where>) => unknown;
72
+ }
73
+ /** A menu of users, roles, users and roles, or channels of the server. */
74
+ export interface EntitySelectConfig<Type extends EntitySelectType, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> extends SelectConfigBase<Data, Where> {
75
+ /** The kind of menu: what Discord lists. */
76
+ type: Type;
77
+ /** For a menu of channels: the kinds of channels listed. All by default. */
78
+ channelTypes?: readonly ChannelType[];
79
+ /** What to do when something is picked. */
80
+ run: (context: SelectContext<EntityValues[Type], Data, Where>) => unknown;
81
+ }
82
+ /** What a select file gives to `select()`: a menu of texts, or of things of the server. */
83
+ export type SelectConfig<Options extends SelectOptions = SelectOptions, Type extends EntitySelectType = EntitySelectType, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> = StringSelectConfig<Options, Data, Where> | EntitySelectConfig<Type, Data, Where>;
84
+ /**
85
+ * What `select()` returns: a declaration the framework finds in the exports
86
+ * of a file. Import it where you send a message, and call it with the data
87
+ * it carries; a menu without data is used as is, or called with what to
88
+ * show.
89
+ */
90
+ export type SelectDeclaration<Data extends DataShape = {}> = {
91
+ /** What the file gave to `select()`, not checked yet. */
92
+ readonly config: unknown;
93
+ } & ({} extends Data ? ((options?: SelectInstanceOptions) => SelectComponent) & SelectComponent : {} extends DataInputOf<Data> ? ((data?: DataInputOf<Data>, options?: SelectInstanceOptions) => SelectComponent) & SelectComponent : (data: DataInputOf<Data>, options?: SelectInstanceOptions) => SelectComponent);
94
+ /**
95
+ * Declares a select menu. Export the result from any file of `src/`: the
96
+ * name of the export is what tells the menu apart, so you never write an
97
+ * id.
98
+ *
99
+ * ```ts
100
+ * import { select } from 'chapterjs';
101
+ *
102
+ * export const color = select({
103
+ * placeholder: 'Pick a color',
104
+ * options: { Red: 'red', Blue: 'blue' },
105
+ * async run({ interaction, value }) {
106
+ * await interaction.update({ content: `You picked ${value}.`, components: [] });
107
+ * },
108
+ * });
109
+ * ```
110
+ */
111
+ export declare function select<const Options extends SelectOptions, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: StringSelectConfig<Options, Data, Where>): SelectDeclaration<Data>;
112
+ export declare function select<const Type extends EntitySelectType, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: EntitySelectConfig<Type, Data, Where>): SelectDeclaration<Data>;
113
+ /** A select menu, checked. */
114
+ export interface LoadedSelect {
115
+ readonly kind: 'select';
116
+ /** The name of the export that declares it: its id for Discord. */
117
+ readonly name: string;
118
+ readonly type: SelectType;
119
+ /** The options of a menu of texts, their texts maybe computed when sent. */
120
+ readonly options: readonly LoadedOption[];
121
+ readonly channelTypes: readonly ChannelType[] | undefined;
122
+ readonly look: SelectLook;
123
+ readonly min: number;
124
+ readonly max: number;
125
+ readonly data: DataShape;
126
+ readonly where: ComponentWhere;
127
+ readonly who: 'everyone' | 'author';
128
+ readonly ephemeral: boolean;
129
+ readonly run: (context: SelectContext<unknown, DataShape, ComponentWhere>) => unknown;
130
+ }
131
+ /** Checks `min` and `max` of a menu, a group of checkboxes or a file upload. */
132
+ export declare function readRange(what: string, given: {
133
+ min?: unknown;
134
+ max?: unknown;
135
+ }, limit: number, defaults: {
136
+ min: number;
137
+ max: number;
138
+ }): {
139
+ min: number;
140
+ max: number;
141
+ };
142
+ export declare function readPlaceholder(what: string, value: unknown): DynamicText | undefined;
143
+ export declare function readChannelTypes(what: string, value: unknown): ChannelType[] | undefined;
144
+ /** The menu as Discord takes it, for the data and the options given. */
145
+ export declare function renderSelect(loaded: LoadedSelect, args: readonly unknown[]): Rendered<'select'>;
146
+ export {};
@@ -0,0 +1,179 @@
1
+ // `select()`, as the files of `src/components/selects/` import it from
2
+ // 'chapterjs': a menu of texts you declare, or of users, roles, channels
3
+ // of the server, and what to do with what is picked.
4
+ // https://docs.discord.com/developers/components/reference#string-select
5
+ import { Limits } from '../discord/api.js';
6
+ import { ChannelType } from '../discord/types/channel.js';
7
+ import { ComponentType, } from '../discord/types/component.js';
8
+ import { isDynamic, resolveText, } from './component.js';
9
+ import { checkData, encodeCustomId, } from './custom-id.js';
10
+ import { createComponent } from './declared.js';
11
+ import { hasDynamicOption, resolveOptions, } from './options.js';
12
+ export const SELECT_TYPES = {
13
+ string: ComponentType.StringSelect,
14
+ user: ComponentType.UserSelect,
15
+ role: ComponentType.RoleSelect,
16
+ mentionable: ComponentType.MentionableSelect,
17
+ channel: ComponentType.ChannelSelect,
18
+ };
19
+ export function select(config) {
20
+ return createComponent('select', config, {
21
+ asPiece: true,
22
+ pieceKind: 'select',
23
+ });
24
+ }
25
+ const fail = (message) => {
26
+ throw new TypeError(message);
27
+ };
28
+ /** Checks `min` and `max` of a menu, a group of checkboxes or a file upload. */
29
+ export function readRange(what, given, limit, defaults) {
30
+ const read = (key, fallback, low) => {
31
+ const value = given[key];
32
+ if (value === undefined)
33
+ return fallback;
34
+ if (!Number.isInteger(value) ||
35
+ value < low ||
36
+ value > limit) {
37
+ return fail(`"${key}" of ${what} is a whole number between ${low} and ${limit}, got ${JSON.stringify(value) ?? typeof value}.`);
38
+ }
39
+ return value;
40
+ };
41
+ const min = read('min', defaults.min, 0);
42
+ const max = read('max', defaults.max, 1);
43
+ if (min > max) {
44
+ fail(`${what} can never be used: its "min" (${min}) is greater than its "max" (${max}).`);
45
+ }
46
+ return { min, max };
47
+ }
48
+ export function readPlaceholder(what, value) {
49
+ if (value === undefined)
50
+ return undefined;
51
+ // A function is checked when the message is sent.
52
+ if (isDynamic(value))
53
+ return value;
54
+ if (typeof value !== 'string' ||
55
+ value.trim() === '' ||
56
+ value.length > Limits.SelectPlaceholder) {
57
+ return fail(`The placeholder of ${what} is a text of 1 to ${Limits.SelectPlaceholder} characters.`);
58
+ }
59
+ return value;
60
+ }
61
+ export function readChannelTypes(what, value) {
62
+ if (value === undefined)
63
+ return undefined;
64
+ const known = Object.values(ChannelType);
65
+ if (!Array.isArray(value) ||
66
+ value.length === 0 ||
67
+ !value.every(one => known.includes(one))) {
68
+ return fail(`"channelTypes" of ${what} is a list of ChannelType values, like [ChannelType.GuildText].`);
69
+ }
70
+ return value;
71
+ }
72
+ /** Checks what a message gives to change a menu. */
73
+ function readInstanceOptions(loaded, name, given) {
74
+ if (given === undefined)
75
+ return { look: {}, defaults: undefined };
76
+ if (typeof given !== 'object' || given === null || Array.isArray(given)) {
77
+ fail(`The options of ${name} are an object like { placeholder: '...', defaults: [...] }.`);
78
+ }
79
+ const options = given;
80
+ for (const key of Object.keys(options)) {
81
+ if (!['placeholder', 'disabled', 'defaults'].includes(key)) {
82
+ fail(`"${key}" is not something ${name} takes when shown. It can take: placeholder, disabled, defaults.`);
83
+ }
84
+ }
85
+ const look = {};
86
+ const placeholder = readPlaceholder(name, options.placeholder);
87
+ if (placeholder !== undefined)
88
+ look.placeholder = placeholder;
89
+ if (options.disabled !== undefined) {
90
+ if (typeof options.disabled !== 'boolean')
91
+ fail(`"disabled" of ${name} is true or false.`);
92
+ look.disabled = options.disabled;
93
+ }
94
+ if (options.defaults === undefined)
95
+ return { look, defaults: undefined };
96
+ if (!Array.isArray(options.defaults)) {
97
+ fail(`"defaults" of ${name} is a list of what is picked when the menu appears.`);
98
+ }
99
+ const ids = options.defaults.map(item => {
100
+ const id = typeof item === 'string' ? item : item?.id;
101
+ if (typeof id !== 'string') {
102
+ return fail(`A default of ${name} is a text, or something with an id, got ${JSON.stringify(item) ?? typeof item}.`);
103
+ }
104
+ return id;
105
+ });
106
+ if (ids.length > loaded.max) {
107
+ fail(`${name} has ${ids.length} defaults, but ${loaded.max} can be picked at most.`);
108
+ }
109
+ if (loaded.type === 'string') {
110
+ for (const value of ids) {
111
+ if (!loaded.options.some(option => option.value === value)) {
112
+ fail(`${JSON.stringify(value)} is not an option of ${name}. Its options are: ${loaded.options.map(option => option.value).join(', ')}.`);
113
+ }
114
+ }
115
+ return { look, defaults: ids };
116
+ }
117
+ // Discord wants to know what each default is; a mentionable may be either,
118
+ // and a role id is also a guild id for @everyone: users first.
119
+ const kind = loaded.type === 'mentionable' ? 'user' : loaded.type;
120
+ return { look, defaults: ids.map(id => ({ id, type: kind })) };
121
+ }
122
+ /** The menu as Discord takes it, for the data and the options given. */
123
+ export function renderSelect(loaded, args) {
124
+ const hasData = Object.keys(loaded.data).length > 0;
125
+ const { name } = loaded;
126
+ if (args.length > (hasData ? 2 : 1)) {
127
+ fail(`${name} takes ${hasData ? 'its data, then options' : 'options'} at most.`);
128
+ }
129
+ const values = checkData(name, loaded.data, hasData ? args[0] : undefined);
130
+ const { look, defaults } = readInstanceOptions(loaded, name, hasData ? args[1] : args[0]);
131
+ const merged = { ...loaded.look, ...look };
132
+ const dynamic = isDynamic(merged.placeholder) || hasDynamicOption(loaded.options);
133
+ const make = (context) => {
134
+ const placeholder = resolveText(`The placeholder of ${name}`, merged.placeholder, context, { max: Limits.SelectPlaceholder });
135
+ const base = {
136
+ custom_id: encodeCustomId(loaded.name, loaded.data, values),
137
+ ...(placeholder !== undefined ? { placeholder } : {}),
138
+ ...(loaded.min !== 1 ? { min_values: loaded.min } : {}),
139
+ ...(loaded.max !== 1 ? { max_values: loaded.max } : {}),
140
+ ...(merged.disabled ? { disabled: true } : {}),
141
+ };
142
+ if (loaded.type !== 'string') {
143
+ return {
144
+ type: SELECT_TYPES[loaded.type],
145
+ ...base,
146
+ ...(loaded.channelTypes
147
+ ? { channel_types: [...loaded.channelTypes] }
148
+ : {}),
149
+ ...(defaults
150
+ ? { default_values: defaults }
151
+ : {}),
152
+ };
153
+ }
154
+ const picked = defaults;
155
+ return {
156
+ type: ComponentType.StringSelect,
157
+ ...base,
158
+ options: resolveOptions(name, loaded.options, context)
159
+ .map(option => picked
160
+ ? {
161
+ ...option,
162
+ ...(picked.includes(option.value)
163
+ ? { default: true }
164
+ : { default: undefined }),
165
+ }
166
+ : option)
167
+ .map(option => option.default === undefined
168
+ ? (({ default: _d, ...rest }) => rest)(option)
169
+ : option),
170
+ };
171
+ };
172
+ // Texts computed when the message is sent wait for who will read it.
173
+ return {
174
+ kind: 'select',
175
+ raw: dynamic
176
+ ? t => make({ t, data: values })
177
+ : make({ t: undefined, data: values }),
178
+ };
179
+ }
@@ -0,0 +1,99 @@
1
+ import { type CacheOptions } from '../cache/cache.js';
2
+ import type { Snowflake } from '../discord/types/common.js';
3
+ import type { GatewayDispatchEventName, GatewayDispatchEvents, RawGatewayPresenceUpdate } from '../discord/types/gateway-events.js';
4
+ import { Gateway, type GatewayOptions } from '../gateway/gateway.js';
5
+ import type { ShardEvent } from '../gateway/shard.js';
6
+ import { type RestOptions } from '../rest/rest.js';
7
+ import type { Context } from '../structures/context.js';
8
+ /** What a dispatched event is, besides its data. */
9
+ export interface DispatchInfo {
10
+ shardId: number;
11
+ /**
12
+ * For Guild Create: `true` when the bot just joined the server, `false`
13
+ * when it was already in it and Discord is only sending its data (after
14
+ * connecting, or after an outage).
15
+ */
16
+ joined: boolean;
17
+ /** What `beforeDispatch` returned for this event. */
18
+ before: unknown;
19
+ }
20
+ export type BotEvent = ShardEvent
21
+ /** Every server the bot is in has sent its data (or took too long). */
22
+ | {
23
+ type: 'guildsReady';
24
+ }
25
+ /** Applying an event to the cache failed: the event is still delivered. */
26
+ | {
27
+ type: 'stateError';
28
+ event: GatewayDispatchEventName;
29
+ error: unknown;
30
+ }
31
+ /** Something went wrong with a voice connection, which goes on or ends. */
32
+ | {
33
+ type: 'voiceWarning';
34
+ message: string;
35
+ };
36
+ export interface BotOptions {
37
+ token: string;
38
+ /** The version of the framework. */
39
+ version: string;
40
+ intents: number;
41
+ presence?: RawGatewayPresenceUpdate | undefined;
42
+ cache?: CacheOptions;
43
+ shards?: GatewayOptions['shards'];
44
+ identifyGate?: GatewayOptions['identifyGate'];
45
+ /** Called for every event, once the cache reflects it. */
46
+ onDispatch?: <E extends GatewayDispatchEventName>(event: E, data: GatewayDispatchEvents[E], info: DispatchInfo) => void;
47
+ /**
48
+ * Called for every event before the cache reflects it: the last moment
49
+ * to read what the event removes. What it returns is given back to
50
+ * `onDispatch`.
51
+ */
52
+ beforeDispatch?: <E extends GatewayDispatchEventName>(event: E, data: GatewayDispatchEvents[E]) => unknown;
53
+ onEvent?: (event: BotEvent) => void;
54
+ /**
55
+ * The servers the bot pays attention to. Events of the others are dropped
56
+ * before anything else, as if the bot was not in them: this is how a dev
57
+ * bot only sees its dev server. What belongs to no server is decided by
58
+ * `privateEvents`.
59
+ */
60
+ guildFilter?: (guildId: Snowflake) => boolean;
61
+ /**
62
+ * Whether what happens outside servers (private messages, commands used
63
+ * there) is for this bot. Default: true. Discord sends it to every
64
+ * process of the bot that runs its first shard: when a dev bot and a
65
+ * production bot share a token, only one may answer.
66
+ */
67
+ privateEvents?: boolean;
68
+ /**
69
+ * How long to wait for the data of every server after connecting, in
70
+ * milliseconds, before going on without the ones that did not come
71
+ * (an outage of Discord). Default: 15000.
72
+ */
73
+ guildsTimeout?: number;
74
+ /** Only tests change these. */
75
+ rest?: Partial<RestOptions>;
76
+ gateway?: Pick<GatewayOptions, 'backoff' | 'identifyInterval'>;
77
+ voice?: {
78
+ secure?: boolean;
79
+ };
80
+ }
81
+ export interface Bot {
82
+ readonly ctx: Context;
83
+ readonly gateway: Gateway;
84
+ /**
85
+ * Connects to Discord, and resolves once every shard is ready and every
86
+ * server has sent its data.
87
+ */
88
+ connect(): Promise<void>;
89
+ /** Disconnects cleanly: the bot goes offline right away. */
90
+ close(): Promise<void>;
91
+ /**
92
+ * Changes what the bot shows under its name (its status and activity)
93
+ * on every shard of this process. Discord's limit on presence updates is
94
+ * respected: when it is reached, the latest presence is sent as soon as
95
+ * it allows.
96
+ */
97
+ setPresence(presence: RawGatewayPresenceUpdate): void;
98
+ }
99
+ export declare function createBot(options: BotOptions): Bot;