@gakr-gakr/discord 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 (353) hide show
  1. package/account-inspect-api.ts +6 -0
  2. package/action-runtime-api.ts +1 -0
  3. package/api.ts +130 -0
  4. package/autobot.plugin.json +15 -0
  5. package/channel-config-api.ts +1 -0
  6. package/channel-plugin-api.ts +3 -0
  7. package/config-api.ts +4 -0
  8. package/configured-state.ts +6 -0
  9. package/contract-api.ts +21 -0
  10. package/directory-contract-api.ts +4 -0
  11. package/doctor-contract-api.ts +1 -0
  12. package/index.ts +24 -0
  13. package/package.json +79 -0
  14. package/runtime-api.actions.ts +15 -0
  15. package/runtime-api.lookup.ts +22 -0
  16. package/runtime-api.monitor.ts +50 -0
  17. package/runtime-api.send.ts +79 -0
  18. package/runtime-api.threads.ts +31 -0
  19. package/runtime-api.ts +181 -0
  20. package/runtime-setter-api.ts +3 -0
  21. package/secret-contract-api.ts +4 -0
  22. package/security-audit-contract-api.ts +1 -0
  23. package/security-contract-api.ts +4 -0
  24. package/session-key-api.ts +1 -0
  25. package/setup-entry.ts +9 -0
  26. package/setup-plugin-api.ts +3 -0
  27. package/src/account-inspect.ts +131 -0
  28. package/src/accounts.ts +205 -0
  29. package/src/actions/handle-action.guild-admin.ts +421 -0
  30. package/src/actions/handle-action.ts +402 -0
  31. package/src/actions/runtime.guild.ts +446 -0
  32. package/src/actions/runtime.messaging.messages.ts +226 -0
  33. package/src/actions/runtime.messaging.reactions.ts +67 -0
  34. package/src/actions/runtime.messaging.runtime.ts +73 -0
  35. package/src/actions/runtime.messaging.send.ts +336 -0
  36. package/src/actions/runtime.messaging.shared.ts +97 -0
  37. package/src/actions/runtime.messaging.ts +37 -0
  38. package/src/actions/runtime.moderation-shared.ts +48 -0
  39. package/src/actions/runtime.moderation.ts +116 -0
  40. package/src/actions/runtime.presence.ts +117 -0
  41. package/src/actions/runtime.shared.ts +86 -0
  42. package/src/actions/runtime.ts +87 -0
  43. package/src/api.ts +219 -0
  44. package/src/approval-handler.runtime.ts +636 -0
  45. package/src/approval-native.ts +219 -0
  46. package/src/approval-runtime.ts +14 -0
  47. package/src/approval-shared.ts +56 -0
  48. package/src/audit-core.ts +178 -0
  49. package/src/audit.ts +32 -0
  50. package/src/channel-actions.runtime.ts +1 -0
  51. package/src/channel-actions.ts +254 -0
  52. package/src/channel-api.ts +29 -0
  53. package/src/channel.conversation.ts +159 -0
  54. package/src/channel.loaders.ts +50 -0
  55. package/src/channel.runtime.ts +1 -0
  56. package/src/channel.setup.ts +12 -0
  57. package/src/channel.ts +728 -0
  58. package/src/chunk.ts +321 -0
  59. package/src/client.ts +143 -0
  60. package/src/component-custom-id.ts +72 -0
  61. package/src/components-registry.ts +356 -0
  62. package/src/components.builders.ts +410 -0
  63. package/src/components.modal.ts +124 -0
  64. package/src/components.parse.ts +407 -0
  65. package/src/components.ts +54 -0
  66. package/src/components.types.ts +187 -0
  67. package/src/config-schema.ts +6 -0
  68. package/src/config-ui-hints.ts +354 -0
  69. package/src/conversation-identity.ts +58 -0
  70. package/src/delivery-retry.ts +56 -0
  71. package/src/directory-cache.ts +116 -0
  72. package/src/directory-config.ts +58 -0
  73. package/src/directory-live.ts +135 -0
  74. package/src/doctor-contract.ts +477 -0
  75. package/src/doctor-shared.ts +5 -0
  76. package/src/doctor.ts +340 -0
  77. package/src/draft-chunking.ts +43 -0
  78. package/src/draft-stream.ts +162 -0
  79. package/src/error-body.ts +38 -0
  80. package/src/exec-approvals.ts +110 -0
  81. package/src/gateway-logging.ts +67 -0
  82. package/src/group-policy.ts +113 -0
  83. package/src/guilds.ts +29 -0
  84. package/src/inbound-event-delivery.ts +135 -0
  85. package/src/interactive-dispatch.ts +104 -0
  86. package/src/internal/api.commands.ts +51 -0
  87. package/src/internal/api.guild.ts +164 -0
  88. package/src/internal/api.interactions.ts +53 -0
  89. package/src/internal/api.messages.ts +113 -0
  90. package/src/internal/api.reactions.ts +38 -0
  91. package/src/internal/api.ts +61 -0
  92. package/src/internal/api.users.ts +19 -0
  93. package/src/internal/api.webhooks.ts +13 -0
  94. package/src/internal/client.ts +310 -0
  95. package/src/internal/command-deploy.ts +352 -0
  96. package/src/internal/commands.ts +188 -0
  97. package/src/internal/components.base.ts +65 -0
  98. package/src/internal/components.message.ts +279 -0
  99. package/src/internal/components.modal.ts +95 -0
  100. package/src/internal/components.ts +31 -0
  101. package/src/internal/discord.ts +11 -0
  102. package/src/internal/embeds.ts +35 -0
  103. package/src/internal/entity-cache.ts +98 -0
  104. package/src/internal/event-queue.ts +185 -0
  105. package/src/internal/gateway-close-codes.ts +25 -0
  106. package/src/internal/gateway-dispatch.ts +96 -0
  107. package/src/internal/gateway-identify-limiter.ts +26 -0
  108. package/src/internal/gateway-lifecycle.ts +75 -0
  109. package/src/internal/gateway-rate-limit.ts +104 -0
  110. package/src/internal/gateway.ts +479 -0
  111. package/src/internal/interaction-dispatch.ts +162 -0
  112. package/src/internal/interaction-options.ts +98 -0
  113. package/src/internal/interaction-response.ts +53 -0
  114. package/src/internal/interactions.ts +378 -0
  115. package/src/internal/listeners.ts +91 -0
  116. package/src/internal/modal-fields.ts +95 -0
  117. package/src/internal/payload.ts +69 -0
  118. package/src/internal/rest-body.ts +115 -0
  119. package/src/internal/rest-errors.ts +88 -0
  120. package/src/internal/rest-routes.ts +50 -0
  121. package/src/internal/rest-scheduler.ts +557 -0
  122. package/src/internal/rest.ts +322 -0
  123. package/src/internal/schemas.ts +36 -0
  124. package/src/internal/structures.ts +280 -0
  125. package/src/internal/test-builders.test-support.ts +167 -0
  126. package/src/internal/voice.ts +49 -0
  127. package/src/media-detection.ts +28 -0
  128. package/src/mentions.ts +147 -0
  129. package/src/monitor/ack-reactions.ts +70 -0
  130. package/src/monitor/agent-components-auth.ts +7 -0
  131. package/src/monitor/agent-components-context.ts +154 -0
  132. package/src/monitor/agent-components-data.ts +224 -0
  133. package/src/monitor/agent-components-dm-auth.ts +177 -0
  134. package/src/monitor/agent-components-guild-auth.ts +322 -0
  135. package/src/monitor/agent-components-helpers.runtime.ts +3 -0
  136. package/src/monitor/agent-components-helpers.ts +34 -0
  137. package/src/monitor/agent-components-reply.ts +10 -0
  138. package/src/monitor/agent-components.deps.runtime.ts +2 -0
  139. package/src/monitor/agent-components.dispatch.ts +359 -0
  140. package/src/monitor/agent-components.handlers.ts +303 -0
  141. package/src/monitor/agent-components.modal.ts +160 -0
  142. package/src/monitor/agent-components.plugin-interactive.ts +187 -0
  143. package/src/monitor/agent-components.runtime.ts +14 -0
  144. package/src/monitor/agent-components.system-controls.ts +215 -0
  145. package/src/monitor/agent-components.ts +70 -0
  146. package/src/monitor/agent-components.types.ts +58 -0
  147. package/src/monitor/agent-components.wildcard-controls.ts +171 -0
  148. package/src/monitor/allow-list.ts +631 -0
  149. package/src/monitor/auto-presence.ts +356 -0
  150. package/src/monitor/channel-access.ts +102 -0
  151. package/src/monitor/commands.ts +9 -0
  152. package/src/monitor/dm-command-auth.ts +259 -0
  153. package/src/monitor/dm-command-decision.ts +49 -0
  154. package/src/monitor/exec-approvals.ts +161 -0
  155. package/src/monitor/format.ts +45 -0
  156. package/src/monitor/gateway-handle.ts +34 -0
  157. package/src/monitor/gateway-metadata.ts +298 -0
  158. package/src/monitor/gateway-plugin.ts +302 -0
  159. package/src/monitor/gateway-registry.ts +37 -0
  160. package/src/monitor/gateway-supervisor.ts +206 -0
  161. package/src/monitor/inbound-context.ts +95 -0
  162. package/src/monitor/inbound-dedupe.ts +79 -0
  163. package/src/monitor/inbound-job.ts +118 -0
  164. package/src/monitor/listeners.queue.ts +91 -0
  165. package/src/monitor/listeners.reactions.ts +594 -0
  166. package/src/monitor/listeners.ts +150 -0
  167. package/src/monitor/message-channel-info.ts +96 -0
  168. package/src/monitor/message-forwarded.ts +114 -0
  169. package/src/monitor/message-handler.batch-gate.ts +19 -0
  170. package/src/monitor/message-handler.context.ts +492 -0
  171. package/src/monitor/message-handler.dm-preflight.ts +119 -0
  172. package/src/monitor/message-handler.draft-preview.ts +436 -0
  173. package/src/monitor/message-handler.hydration.ts +198 -0
  174. package/src/monitor/message-handler.module-test-helpers.ts +31 -0
  175. package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
  176. package/src/monitor/message-handler.preflight-channel-context.ts +58 -0
  177. package/src/monitor/message-handler.preflight-context.ts +54 -0
  178. package/src/monitor/message-handler.preflight-helpers.ts +164 -0
  179. package/src/monitor/message-handler.preflight-history.ts +23 -0
  180. package/src/monitor/message-handler.preflight-logging.ts +36 -0
  181. package/src/monitor/message-handler.preflight-pluralkit.ts +28 -0
  182. package/src/monitor/message-handler.preflight-runtime.ts +28 -0
  183. package/src/monitor/message-handler.preflight-thread.ts +49 -0
  184. package/src/monitor/message-handler.preflight.ts +822 -0
  185. package/src/monitor/message-handler.preflight.types.ts +115 -0
  186. package/src/monitor/message-handler.process.ts +1033 -0
  187. package/src/monitor/message-handler.routing-preflight.ts +112 -0
  188. package/src/monitor/message-handler.ts +309 -0
  189. package/src/monitor/message-media.ts +536 -0
  190. package/src/monitor/message-run-queue.ts +101 -0
  191. package/src/monitor/message-text.ts +171 -0
  192. package/src/monitor/message-utils.ts +34 -0
  193. package/src/monitor/model-picker-preferences.ts +184 -0
  194. package/src/monitor/model-picker.state.ts +364 -0
  195. package/src/monitor/model-picker.test-utils.ts +26 -0
  196. package/src/monitor/model-picker.ts +38 -0
  197. package/src/monitor/model-picker.view.ts +722 -0
  198. package/src/monitor/native-command-agent-reply.ts +125 -0
  199. package/src/monitor/native-command-arg-ui.ts +233 -0
  200. package/src/monitor/native-command-auth.ts +309 -0
  201. package/src/monitor/native-command-bypass.ts +13 -0
  202. package/src/monitor/native-command-context.ts +109 -0
  203. package/src/monitor/native-command-dispatch.ts +35 -0
  204. package/src/monitor/native-command-model-picker-apply.ts +209 -0
  205. package/src/monitor/native-command-model-picker-interaction.ts +516 -0
  206. package/src/monitor/native-command-model-picker-ui.ts +357 -0
  207. package/src/monitor/native-command-reply.ts +185 -0
  208. package/src/monitor/native-command-route.ts +91 -0
  209. package/src/monitor/native-command-status.ts +76 -0
  210. package/src/monitor/native-command-ui.ts +26 -0
  211. package/src/monitor/native-command-ui.types.ts +20 -0
  212. package/src/monitor/native-command.args.ts +45 -0
  213. package/src/monitor/native-command.options.ts +153 -0
  214. package/src/monitor/native-command.runtime.ts +51 -0
  215. package/src/monitor/native-command.ts +747 -0
  216. package/src/monitor/native-command.types.ts +9 -0
  217. package/src/monitor/native-interaction-channel-context.ts +50 -0
  218. package/src/monitor/preflight-audio.runtime.ts +9 -0
  219. package/src/monitor/preflight-audio.ts +130 -0
  220. package/src/monitor/presence-cache.ts +61 -0
  221. package/src/monitor/presence.ts +50 -0
  222. package/src/monitor/provider-session.runtime.ts +12 -0
  223. package/src/monitor/provider.acp.ts +89 -0
  224. package/src/monitor/provider.allowlist.ts +398 -0
  225. package/src/monitor/provider.cleanup.ts +41 -0
  226. package/src/monitor/provider.commands.ts +129 -0
  227. package/src/monitor/provider.config-log.ts +45 -0
  228. package/src/monitor/provider.deploy-errors.ts +362 -0
  229. package/src/monitor/provider.deploy.ts +221 -0
  230. package/src/monitor/provider.interactions.ts +160 -0
  231. package/src/monitor/provider.lifecycle.ts +562 -0
  232. package/src/monitor/provider.runtime.ts +1 -0
  233. package/src/monitor/provider.startup-log.ts +32 -0
  234. package/src/monitor/provider.startup.ts +323 -0
  235. package/src/monitor/provider.ts +688 -0
  236. package/src/monitor/reply-context.ts +64 -0
  237. package/src/monitor/reply-delivery.ts +216 -0
  238. package/src/monitor/reply-safety.ts +96 -0
  239. package/src/monitor/rest-fetch.ts +97 -0
  240. package/src/monitor/route-resolution.ts +140 -0
  241. package/src/monitor/sender-identity.ts +81 -0
  242. package/src/monitor/startup-status.ts +10 -0
  243. package/src/monitor/status.ts +22 -0
  244. package/src/monitor/system-events.ts +55 -0
  245. package/src/monitor/thread-bindings.config.ts +35 -0
  246. package/src/monitor/thread-bindings.discord-api.ts +310 -0
  247. package/src/monitor/thread-bindings.lifecycle.ts +354 -0
  248. package/src/monitor/thread-bindings.manager.ts +554 -0
  249. package/src/monitor/thread-bindings.messages.ts +6 -0
  250. package/src/monitor/thread-bindings.persona.ts +25 -0
  251. package/src/monitor/thread-bindings.session-adapter.ts +229 -0
  252. package/src/monitor/thread-bindings.session-shared.ts +59 -0
  253. package/src/monitor/thread-bindings.session-updates.ts +35 -0
  254. package/src/monitor/thread-bindings.state.ts +540 -0
  255. package/src/monitor/thread-bindings.ts +48 -0
  256. package/src/monitor/thread-bindings.types.ts +83 -0
  257. package/src/monitor/thread-channel-context.ts +112 -0
  258. package/src/monitor/thread-session-close.ts +63 -0
  259. package/src/monitor/thread-title.ts +181 -0
  260. package/src/monitor/threading.auto-thread.ts +287 -0
  261. package/src/monitor/threading.cache.ts +45 -0
  262. package/src/monitor/threading.starter.ts +288 -0
  263. package/src/monitor/threading.ts +20 -0
  264. package/src/monitor/threading.types.ts +102 -0
  265. package/src/monitor/timeouts.ts +84 -0
  266. package/src/monitor/typing.ts +17 -0
  267. package/src/monitor.gateway.ts +75 -0
  268. package/src/monitor.ts +28 -0
  269. package/src/network-config.ts +79 -0
  270. package/src/normalize.ts +86 -0
  271. package/src/outbound-adapter.ts +327 -0
  272. package/src/outbound-approval.ts +29 -0
  273. package/src/outbound-components.ts +86 -0
  274. package/src/outbound-payload.ts +208 -0
  275. package/src/outbound-send-context.ts +92 -0
  276. package/src/outbound-session-route.ts +72 -0
  277. package/src/pluralkit.ts +58 -0
  278. package/src/preview-streaming.ts +18 -0
  279. package/src/probe.runtime.ts +1 -0
  280. package/src/probe.ts +237 -0
  281. package/src/proxy-fetch.ts +92 -0
  282. package/src/proxy-request-client.ts +21 -0
  283. package/src/recipient-resolution.ts +39 -0
  284. package/src/resolve-allowlist-common.ts +39 -0
  285. package/src/resolve-channels.ts +369 -0
  286. package/src/resolve-users.ts +184 -0
  287. package/src/retry.ts +98 -0
  288. package/src/runtime-api.ts +64 -0
  289. package/src/runtime-config.ts +16 -0
  290. package/src/runtime.ts +23 -0
  291. package/src/secret-config-contract.ts +140 -0
  292. package/src/security-audit.runtime.ts +1 -0
  293. package/src/security-audit.ts +208 -0
  294. package/src/security-contract.ts +47 -0
  295. package/src/security-doctor.ts +20 -0
  296. package/src/security.ts +60 -0
  297. package/src/send-target-parsing.ts +14 -0
  298. package/src/send.channels.ts +139 -0
  299. package/src/send.components.ts +391 -0
  300. package/src/send.emojis-stickers.ts +57 -0
  301. package/src/send.guild.ts +170 -0
  302. package/src/send.message-request.ts +112 -0
  303. package/src/send.messages.ts +229 -0
  304. package/src/send.outbound.ts +459 -0
  305. package/src/send.permissions.ts +283 -0
  306. package/src/send.reactions.ts +155 -0
  307. package/src/send.receipt.ts +69 -0
  308. package/src/send.shared.ts +469 -0
  309. package/src/send.ts +82 -0
  310. package/src/send.types.ts +191 -0
  311. package/src/send.typing.ts +9 -0
  312. package/src/send.voice.ts +140 -0
  313. package/src/send.webhook.ts +137 -0
  314. package/src/session-contract.ts +3 -0
  315. package/src/session-key-normalization.ts +47 -0
  316. package/src/setup-account-state.ts +144 -0
  317. package/src/setup-adapter.ts +14 -0
  318. package/src/setup-core.ts +215 -0
  319. package/src/setup-runtime-helpers.ts +10 -0
  320. package/src/setup-surface.ts +132 -0
  321. package/src/shared-interactive.ts +167 -0
  322. package/src/shared.ts +197 -0
  323. package/src/status-issues.ts +201 -0
  324. package/src/subagent-hooks.ts +232 -0
  325. package/src/target-parsing.ts +70 -0
  326. package/src/target-resolver.ts +129 -0
  327. package/src/targets.ts +12 -0
  328. package/src/token.ts +107 -0
  329. package/src/ui-colors.ts +27 -0
  330. package/src/ui.ts +20 -0
  331. package/src/voice/access.ts +126 -0
  332. package/src/voice/audio.ts +249 -0
  333. package/src/voice/capture-state.ts +120 -0
  334. package/src/voice/command.ts +284 -0
  335. package/src/voice/config.ts +8 -0
  336. package/src/voice/ingress.ts +164 -0
  337. package/src/voice/manager.runtime.ts +14 -0
  338. package/src/voice/manager.ts +1155 -0
  339. package/src/voice/prompt.ts +22 -0
  340. package/src/voice/realtime.ts +1370 -0
  341. package/src/voice/receive-recovery.ts +159 -0
  342. package/src/voice/sanitize.ts +29 -0
  343. package/src/voice/sdk-runtime.ts +14 -0
  344. package/src/voice/segment.ts +160 -0
  345. package/src/voice/session.ts +81 -0
  346. package/src/voice/speaker-context.ts +127 -0
  347. package/src/voice/tts.ts +151 -0
  348. package/src/voice-message.ts +474 -0
  349. package/subagent-hooks-api.ts +27 -0
  350. package/test-api.ts +4 -0
  351. package/thread-binding-api.ts +1 -0
  352. package/timeouts.ts +6 -0
  353. package/tsconfig.json +16 -0
@@ -0,0 +1,95 @@
1
+ import { type APIRole, type APIUser } from "discord-api-types/v10";
2
+ import { Role, User, type StructureClient } from "./structures.js";
3
+
4
+ type ModalResolvedData = {
5
+ roles?: Record<string, { id: string; name?: string }>;
6
+ users?: Record<string, { id: string; username?: string; discriminator?: string }>;
7
+ };
8
+
9
+ export function extractModalFields(components: unknown[]): Record<string, string | string[]> {
10
+ const out: Record<string, string | string[]> = {};
11
+ for (const component of flattenModalComponents(components)) {
12
+ const raw = component as { custom_id?: unknown; value?: unknown; values?: unknown };
13
+ if (typeof raw.custom_id !== "string") {
14
+ continue;
15
+ }
16
+ if (Array.isArray(raw.values)) {
17
+ out[raw.custom_id] = raw.values.map(String);
18
+ } else if (
19
+ typeof raw.value === "string" ||
20
+ typeof raw.value === "number" ||
21
+ typeof raw.value === "boolean"
22
+ ) {
23
+ out[raw.custom_id] = String(raw.value);
24
+ }
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function flattenModalComponents(components: unknown[]): unknown[] {
30
+ const out: unknown[] = [];
31
+ for (const entry of components) {
32
+ if (!entry || typeof entry !== "object") {
33
+ continue;
34
+ }
35
+ const component = entry as { component?: unknown; components?: unknown[] };
36
+ if (component.component && typeof component.component === "object") {
37
+ out.push(component.component);
38
+ }
39
+ if (Array.isArray(component.components)) {
40
+ out.push(...flattenModalComponents(component.components));
41
+ }
42
+ out.push(entry);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ export class ModalFields {
48
+ constructor(
49
+ private values: Record<string, string | string[]>,
50
+ private resolved?: ModalResolvedData,
51
+ private client?: StructureClient,
52
+ ) {}
53
+
54
+ private value(id: string, required: boolean): string | string[] | undefined {
55
+ const value = this.values[id];
56
+ if (required && (value === undefined || (Array.isArray(value) && value.length === 0))) {
57
+ throw new Error(`Missing required modal field ${id}`);
58
+ }
59
+ return value;
60
+ }
61
+
62
+ getText(id: string, required = false): string | null {
63
+ const value = this.value(id, required);
64
+ return typeof value === "string" ? value : null;
65
+ }
66
+
67
+ getStringSelect(id: string, required = false): string[] {
68
+ const value = this.value(id, required);
69
+ if (Array.isArray(value)) {
70
+ return value;
71
+ }
72
+ return typeof value === "string" ? [value] : [];
73
+ }
74
+
75
+ getRoleSelect(id: string, required = false): Role[] {
76
+ const values = this.getStringSelect(id, required);
77
+ return values.map((roleId) => {
78
+ const raw = this.resolved?.roles?.[roleId];
79
+ return raw
80
+ ? new Role(this.client!, { id: roleId, name: raw.name ?? "" } as APIRole)
81
+ : new Role<true>(this.client!, roleId);
82
+ });
83
+ }
84
+
85
+ getUserSelect(id: string, required = false): User[] {
86
+ const values = this.getStringSelect(id, required);
87
+ return values.map((userId) => {
88
+ const raw = this.resolved?.users?.[userId];
89
+ return new User(this.client!, {
90
+ id: userId,
91
+ username: raw?.username ?? "",
92
+ } as APIUser);
93
+ });
94
+ }
95
+ }
@@ -0,0 +1,69 @@
1
+ import { MessageFlags, type APIEmbed } from "discord-api-types/v10";
2
+ import { Embed } from "./embeds.js";
3
+
4
+ export type MessagePayloadFile = {
5
+ name: string;
6
+ data: Blob | Uint8Array | ArrayBuffer;
7
+ description?: string;
8
+ duration_secs?: number;
9
+ waveform?: string;
10
+ };
11
+ export type MessagePayloadObject = {
12
+ content?: string;
13
+ embeds?: Array<APIEmbed | Embed>;
14
+ components?: TopLevelComponents[];
15
+ allowedMentions?: unknown;
16
+ allowed_mentions?: unknown;
17
+ flags?: number;
18
+ tts?: boolean;
19
+ files?: MessagePayloadFile[];
20
+ poll?: unknown;
21
+ ephemeral?: boolean;
22
+ stickers?: [string, string, string] | [string, string] | [string];
23
+ };
24
+ export type MessagePayload = string | MessagePayloadObject;
25
+ export type TopLevelComponents = {
26
+ isV2?: boolean;
27
+ serialize: () => unknown;
28
+ };
29
+
30
+ function clean<T extends Record<string, unknown>>(value: T): T {
31
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as T;
32
+ }
33
+
34
+ function serializeAnyComponent(component: { serialize: () => unknown }): unknown {
35
+ return component.serialize();
36
+ }
37
+
38
+ function payloadHasV2Components(payload: MessagePayloadObject): boolean {
39
+ return Boolean(payload.components?.some((component) => component.isV2));
40
+ }
41
+
42
+ function normalizePayloadFlags(payload: MessagePayloadObject): number | undefined {
43
+ const flags = payload.ephemeral ? (payload.flags ?? 0) | MessageFlags.Ephemeral : payload.flags;
44
+ if (!payloadHasV2Components(payload)) {
45
+ return flags;
46
+ }
47
+ if (payload.content || payload.embeds?.length) {
48
+ throw new Error("Discord Components V2 payloads cannot include content or embeds");
49
+ }
50
+ return (flags ?? 0) | MessageFlags.IsComponentsV2;
51
+ }
52
+
53
+ export function serializePayload(payload: MessagePayload) {
54
+ if (typeof payload === "string") {
55
+ return { content: payload };
56
+ }
57
+ const flags = normalizePayloadFlags(payload);
58
+ return clean({
59
+ content: payload.content,
60
+ embeds: payload.embeds?.map((entry) => ("serialize" in entry ? entry.serialize() : entry)),
61
+ components: payload.components?.map((entry) => serializeAnyComponent(entry)),
62
+ allowed_mentions: payload.allowed_mentions ?? payload.allowedMentions,
63
+ flags,
64
+ tts: payload.tts,
65
+ files: payload.files,
66
+ poll: payload.poll,
67
+ sticker_ids: payload.stickers,
68
+ });
69
+ }
@@ -0,0 +1,115 @@
1
+ type RequestData = {
2
+ body?: unknown;
3
+ multipartStyle?: "message" | "form";
4
+ rawBody?: boolean;
5
+ headers?: Record<string, string>;
6
+ };
7
+
8
+ export function serializeRequestBody(
9
+ data: RequestData | undefined,
10
+ headers: Headers,
11
+ ): BodyInit | undefined {
12
+ if (data?.headers) {
13
+ for (const [key, value] of Object.entries(data.headers)) {
14
+ headers.set(key, value);
15
+ }
16
+ }
17
+ if (data?.body == null) {
18
+ return undefined;
19
+ }
20
+ if (typeof data.body === "object") {
21
+ const bodyObject = data.body as Record<string, unknown>;
22
+ const topLevelFiles = Array.isArray(bodyObject.files) ? bodyObject.files : undefined;
23
+ const nestedData =
24
+ bodyObject.data && typeof bodyObject.data === "object"
25
+ ? (bodyObject.data as Record<string, unknown>)
26
+ : undefined;
27
+ const nestedFiles =
28
+ nestedData && Array.isArray(nestedData.files) ? nestedData.files : undefined;
29
+ const files = topLevelFiles ?? nestedFiles;
30
+ const filesContainer = topLevelFiles ? bodyObject : nestedFiles ? nestedData : undefined;
31
+ if (files?.length && filesContainer) {
32
+ if (data.multipartStyle === "form") {
33
+ const formData = new FormData();
34
+ for (const [key, value] of Object.entries(filesContainer)) {
35
+ if (key === "files" || value === undefined || value === null) {
36
+ continue;
37
+ }
38
+ formData.append(key, typeof value === "string" ? value : JSON.stringify(value));
39
+ }
40
+ for (const file of files) {
41
+ const item = file as {
42
+ fieldName?: unknown;
43
+ name?: unknown;
44
+ data?: unknown;
45
+ contentType?: unknown;
46
+ };
47
+ const name = typeof item.name === "string" && item.name ? item.name : "file";
48
+ const blob =
49
+ item.data instanceof Blob
50
+ ? item.data
51
+ : new Blob([item.data as BlobPart], {
52
+ type: typeof item.contentType === "string" ? item.contentType : undefined,
53
+ });
54
+ formData.append(
55
+ typeof item.fieldName === "string" && item.fieldName ? item.fieldName : "file",
56
+ blob,
57
+ name,
58
+ );
59
+ }
60
+ return formData;
61
+ }
62
+ const payloadJson = topLevelFiles
63
+ ? { ...bodyObject }
64
+ : { ...bodyObject, data: { ...nestedData } };
65
+ const payloadFilesContainer = topLevelFiles
66
+ ? (payloadJson as Record<string, unknown>)
67
+ : ((payloadJson as { data: Record<string, unknown> }).data ?? {});
68
+ const formData = new FormData();
69
+ const existingAttachments = Array.isArray(payloadFilesContainer.attachments)
70
+ ? [...payloadFilesContainer.attachments]
71
+ : [];
72
+ const uploaded = files.map((file, index) => {
73
+ const item = file as {
74
+ name?: unknown;
75
+ data?: unknown;
76
+ contentType?: unknown;
77
+ description?: unknown;
78
+ duration_secs?: unknown;
79
+ waveform?: unknown;
80
+ };
81
+ const name = typeof item.name === "string" && item.name ? item.name : `file-${index}`;
82
+ const blob =
83
+ item.data instanceof Blob
84
+ ? item.data
85
+ : new Blob([item.data as BlobPart], {
86
+ type: typeof item.contentType === "string" ? item.contentType : undefined,
87
+ });
88
+ const id = existingAttachments.length + index;
89
+ formData.append(`files[${id}]`, blob, name);
90
+ const attachment: Record<string, unknown> = {
91
+ id,
92
+ filename: name,
93
+ };
94
+ if (typeof item.description === "string") {
95
+ attachment.description = item.description;
96
+ }
97
+ if (typeof item.duration_secs === "number") {
98
+ attachment.duration_secs = item.duration_secs;
99
+ }
100
+ if (typeof item.waveform === "string") {
101
+ attachment.waveform = item.waveform;
102
+ }
103
+ return attachment;
104
+ });
105
+ payloadFilesContainer.attachments = [...existingAttachments, ...uploaded];
106
+ delete payloadFilesContainer.files;
107
+ formData.append("payload_json", JSON.stringify(payloadJson));
108
+ return formData;
109
+ }
110
+ }
111
+ if (!data.rawBody) {
112
+ headers.set("Content-Type", "application/json");
113
+ }
114
+ return data.rawBody ? (data.body as BodyInit) : JSON.stringify(data.body);
115
+ }
@@ -0,0 +1,88 @@
1
+ export function readDiscordCode(body: unknown): number | undefined {
2
+ const value =
3
+ body && typeof body === "object" && "code" in body
4
+ ? (body as { code?: unknown }).code
5
+ : undefined;
6
+ if (typeof value === "number" && Number.isFinite(value)) {
7
+ return value;
8
+ }
9
+ if (typeof value === "string" && /^\d+$/.test(value)) {
10
+ return Number(value);
11
+ }
12
+ return undefined;
13
+ }
14
+
15
+ export function readDiscordMessage(body: unknown, fallback: string): string {
16
+ const value =
17
+ body && typeof body === "object" && "message" in body
18
+ ? (body as { message?: unknown }).message
19
+ : undefined;
20
+ return typeof value === "string" && value.trim() ? value : fallback;
21
+ }
22
+
23
+ function readRetryAfterHeader(value: string | null, now = Date.now()): number | undefined {
24
+ if (!value) {
25
+ return undefined;
26
+ }
27
+ const seconds = Number(value);
28
+ if (Number.isFinite(seconds)) {
29
+ return seconds;
30
+ }
31
+ const retryAt = Date.parse(value);
32
+ return Number.isFinite(retryAt) ? (retryAt - now) / 1000 : undefined;
33
+ }
34
+
35
+ function coerceRetryAfterSeconds(value: unknown): number | undefined {
36
+ if (typeof value !== "number" && typeof value !== "string") {
37
+ return undefined;
38
+ }
39
+ const seconds = typeof value === "number" ? value : Number(value);
40
+ return Number.isFinite(seconds) && seconds >= 0 ? Math.max(0, seconds) : undefined;
41
+ }
42
+
43
+ export function readRetryAfter(body: unknown, response: Response, fallbackSeconds = 0): number {
44
+ const bodyValue =
45
+ body && typeof body === "object" && "retry_after" in body
46
+ ? (body as { retry_after?: unknown }).retry_after
47
+ : undefined;
48
+ return (
49
+ coerceRetryAfterSeconds(bodyValue) ??
50
+ coerceRetryAfterSeconds(readRetryAfterHeader(response.headers.get("Retry-After"))) ??
51
+ fallbackSeconds
52
+ );
53
+ }
54
+
55
+ export class DiscordError extends Error {
56
+ readonly status: number;
57
+ readonly statusCode: number;
58
+ readonly rawBody: unknown;
59
+ readonly rawError: unknown;
60
+ discordCode?: number;
61
+
62
+ constructor(response: Response, body: unknown) {
63
+ super(readDiscordMessage(body, `Discord API request failed (${response.status})`));
64
+ this.name = "DiscordError";
65
+ this.status = response.status;
66
+ this.statusCode = response.status;
67
+ this.rawBody = body;
68
+ this.rawError = body;
69
+ this.discordCode = readDiscordCode(body);
70
+ }
71
+ }
72
+
73
+ export class RateLimitError extends DiscordError {
74
+ readonly retryAfter: number;
75
+ readonly scope: string | null;
76
+ readonly bucket: string | null;
77
+
78
+ constructor(
79
+ response: Response,
80
+ body: { message: string; retry_after: number; global: boolean; code?: number | string },
81
+ ) {
82
+ super(response, body);
83
+ this.name = "RateLimitError";
84
+ this.retryAfter = readRetryAfter(body, response, 1);
85
+ this.scope = body.global ? "global" : response.headers.get("X-RateLimit-Scope");
86
+ this.bucket = response.headers.get("X-RateLimit-Bucket");
87
+ }
88
+ }
@@ -0,0 +1,50 @@
1
+ type QueryValue = string | number | boolean;
2
+
3
+ export function createRouteKey(method: string, path: string): string {
4
+ return `${method.toUpperCase()} ${path.split("?")[0] ?? path}`;
5
+ }
6
+
7
+ function readTopLevelRouteKey(path: string): string {
8
+ const [pathname = path] = path.split("?");
9
+ const [first, id, token] = pathname.replace(/^\/+/, "").split("/");
10
+ if (!first || !id) {
11
+ return pathname;
12
+ }
13
+ if (first === "channels" || first === "guilds" || first === "webhooks") {
14
+ return first === "webhooks" && token ? `${first}/${id}/${token}` : `${first}/${id}`;
15
+ }
16
+ return first;
17
+ }
18
+
19
+ export function createBucketKey(bucket: string, path: string): string {
20
+ return `${bucket}:${readTopLevelRouteKey(path)}`;
21
+ }
22
+
23
+ export function readHeaderNumber(headers: Headers, name: string): number | undefined {
24
+ const value = headers.get(name);
25
+ if (!value) {
26
+ return undefined;
27
+ }
28
+ const parsed = Number(value);
29
+ return Number.isFinite(parsed) ? parsed : undefined;
30
+ }
31
+
32
+ export function readResetAt(response: Response): number | undefined {
33
+ const resetAfter = readHeaderNumber(response.headers, "X-RateLimit-Reset-After");
34
+ if (resetAfter !== undefined) {
35
+ return Date.now() + Math.max(0, resetAfter * 1000);
36
+ }
37
+ const reset = readHeaderNumber(response.headers, "X-RateLimit-Reset");
38
+ return reset !== undefined ? reset * 1000 : undefined;
39
+ }
40
+
41
+ export function appendQuery(path: string, query?: Record<string, QueryValue>): string {
42
+ if (!query || Object.keys(query).length === 0) {
43
+ return path;
44
+ }
45
+ const search = new URLSearchParams();
46
+ for (const [key, value] of Object.entries(query)) {
47
+ search.set(key, String(value));
48
+ }
49
+ return `${path}?${search.toString()}`;
50
+ }