@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,167 @@
1
+ import { ComponentType, InteractionType } from "discord-api-types/v10";
2
+ import { vi, type Mock } from "vitest";
3
+ import { Client, type ClientOptions } from "./client.js";
4
+ import type { BaseCommand } from "./commands.js";
5
+ import type { RawInteraction } from "./interactions.js";
6
+ import type { QueuedRequest, RequestClient, RequestData } from "./rest.js";
7
+
8
+ type RestMock = Partial<Record<"get" | "post" | "patch" | "put" | "delete", Mock>>;
9
+ type RestMethod = "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
10
+ type RawInteractionOverrides = Omit<Partial<RawInteraction>, "data" | "type"> &
11
+ Pick<RawInteraction, "id" | "token"> & {
12
+ data?: Record<string, unknown>;
13
+ };
14
+
15
+ type FakeRestCall = {
16
+ method: RestMethod;
17
+ path: string;
18
+ data?: RequestData;
19
+ query?: QueuedRequest["query"];
20
+ };
21
+
22
+ type FakeRestClient = RequestClient & {
23
+ calls: FakeRestCall[];
24
+ enqueueResponse: (value: unknown) => void;
25
+ };
26
+
27
+ export function createDeferred<T>() {
28
+ let resolve: ((value: T) => void) | undefined;
29
+ const promise = new Promise<T>((res) => {
30
+ resolve = res;
31
+ });
32
+ return { promise, resolve: resolve! };
33
+ }
34
+
35
+ export function createJsonResponse(body: unknown, init?: ResponseInit): Response {
36
+ return new Response(JSON.stringify(body), {
37
+ status: 200,
38
+ ...init,
39
+ });
40
+ }
41
+
42
+ export function createAbortableFetchMock() {
43
+ let receivedSignal: AbortSignal | undefined;
44
+ const fetch = vi.fn(
45
+ (_input: string | URL | Request, init?: RequestInit) =>
46
+ new Promise<Response>((_resolve, reject) => {
47
+ receivedSignal = init?.signal ?? undefined;
48
+ init?.signal?.addEventListener("abort", () => {
49
+ reject(new DOMException("The operation was aborted.", "AbortError"));
50
+ });
51
+ }),
52
+ );
53
+ return {
54
+ fetch,
55
+ get receivedSignal() {
56
+ return receivedSignal;
57
+ },
58
+ };
59
+ }
60
+
61
+ export function createInternalTestClient(
62
+ commands: BaseCommand[] = [],
63
+ options?: Partial<ClientOptions>,
64
+ ): Client {
65
+ return new Client(
66
+ {
67
+ baseUrl: "http://localhost",
68
+ clientId: "app1",
69
+ publicKey: "public",
70
+ token: "token",
71
+ ...options,
72
+ },
73
+ { commands },
74
+ );
75
+ }
76
+
77
+ function createRestMock(overrides: RestMock = {}): RestMock & RequestClient {
78
+ return {
79
+ get: vi.fn(async () => undefined),
80
+ post: vi.fn(async () => undefined),
81
+ patch: vi.fn(async () => undefined),
82
+ put: vi.fn(async () => undefined),
83
+ delete: vi.fn(async () => undefined),
84
+ ...overrides,
85
+ } as RestMock & RequestClient;
86
+ }
87
+
88
+ export function attachRestMock(client: Client, rest: RestMock): RestMock & RequestClient {
89
+ const mock = createRestMock(rest);
90
+ client.rest = mock;
91
+ return mock;
92
+ }
93
+
94
+ export function createFakeRestClient(responses: unknown[] = []): FakeRestClient {
95
+ const calls: FakeRestCall[] = [];
96
+ const queued = [...responses];
97
+ const request = async (
98
+ method: RestMethod,
99
+ path: string,
100
+ data?: RequestData,
101
+ query?: QueuedRequest["query"],
102
+ ) => {
103
+ calls.push({ method, path, data, query });
104
+ return queued.shift();
105
+ };
106
+ return {
107
+ calls,
108
+ enqueueResponse: (value: unknown) => {
109
+ queued.push(value);
110
+ },
111
+ get: async (path, query) => await request("GET", path, undefined, query),
112
+ post: async (path, data, query) => await request("POST", path, data, query),
113
+ patch: async (path, data, query) => await request("PATCH", path, data, query),
114
+ put: async (path, data, query) => await request("PUT", path, data, query),
115
+ delete: async (path, data, query) => await request("DELETE", path, data, query),
116
+ } as FakeRestClient;
117
+ }
118
+
119
+ export function createInternalInteractionPayload(
120
+ overrides: Partial<RawInteraction> & Pick<RawInteraction, "id" | "token">,
121
+ ): RawInteraction {
122
+ return {
123
+ application_id: "app1",
124
+ type: InteractionType.ApplicationCommand,
125
+ version: 1,
126
+ data: {
127
+ id: "command1",
128
+ name: "test",
129
+ type: 1,
130
+ },
131
+ ...overrides,
132
+ } as unknown as RawInteraction;
133
+ }
134
+
135
+ export function createInternalComponentInteractionPayload(
136
+ overrides: RawInteractionOverrides,
137
+ ): RawInteraction {
138
+ const { data, ...rest } = overrides;
139
+ return {
140
+ application_id: "app1",
141
+ version: 1,
142
+ data: {
143
+ component_type: ComponentType.Button,
144
+ custom_id: "component1",
145
+ ...data,
146
+ },
147
+ ...rest,
148
+ type: InteractionType.MessageComponent,
149
+ } as unknown as RawInteraction;
150
+ }
151
+
152
+ export function createInternalModalInteractionPayload(
153
+ overrides: RawInteractionOverrides,
154
+ ): RawInteraction {
155
+ const { data, ...rest } = overrides;
156
+ return {
157
+ application_id: "app1",
158
+ version: 1,
159
+ data: {
160
+ custom_id: "modal1",
161
+ components: [],
162
+ ...data,
163
+ },
164
+ ...rest,
165
+ type: InteractionType.ModalSubmit,
166
+ } as unknown as RawInteraction;
167
+ }
@@ -0,0 +1,49 @@
1
+ import type {
2
+ DiscordGatewayAdapterCreator,
3
+ DiscordGatewayAdapterLibraryMethods,
4
+ } from "@discordjs/voice";
5
+ import type { GatewaySendPayload } from "discord-api-types/v10";
6
+ import { Plugin, type Client } from "./client.js";
7
+ import type { GatewayPlugin } from "./gateway.js";
8
+
9
+ export class VoicePlugin extends Plugin {
10
+ readonly id = "voice";
11
+ protected client?: Client;
12
+ readonly adapters = new Map<string, DiscordGatewayAdapterLibraryMethods>();
13
+ private gatewayPlugin?: GatewayPlugin;
14
+
15
+ override registerClient(client: Client): void {
16
+ this.client = client;
17
+ this.gatewayPlugin = client.getPlugin<GatewayPlugin>("gateway");
18
+ if (!this.gatewayPlugin) {
19
+ throw new Error("Discord voice cannot be used without a gateway connection.");
20
+ }
21
+ }
22
+
23
+ getGateway(_guildId: string): GatewayPlugin | undefined {
24
+ return this.gatewayPlugin;
25
+ }
26
+
27
+ getGatewayAdapterCreator(guildId: string): DiscordGatewayAdapterCreator {
28
+ const gateway = this.getGateway(guildId);
29
+ if (!gateway) {
30
+ throw new Error("Discord voice cannot be used without a gateway connection.");
31
+ }
32
+ return (methods) => {
33
+ this.adapters.set(guildId, methods);
34
+ return {
35
+ sendPayload(payload) {
36
+ try {
37
+ gateway.send(payload as GatewaySendPayload, true);
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ },
43
+ destroy: () => {
44
+ this.adapters.delete(guildId);
45
+ },
46
+ };
47
+ };
48
+ }
49
+ }
@@ -0,0 +1,28 @@
1
+ import { normalizeLowercaseStringOrEmpty } from "autobot/plugin-sdk/string-coerce-runtime";
2
+
3
+ const DISCORD_VIDEO_MEDIA_EXTENSIONS = new Set([".avi", ".m4v", ".mkv", ".mov", ".mp4", ".webm"]);
4
+
5
+ function normalizeMediaPathForExtension(mediaUrl: string): string {
6
+ const trimmed = mediaUrl.trim();
7
+ if (!trimmed) {
8
+ return "";
9
+ }
10
+ try {
11
+ const parsed = new URL(trimmed);
12
+ return normalizeLowercaseStringOrEmpty(parsed.pathname);
13
+ } catch {
14
+ const withoutHash = trimmed.split("#", 1)[0] ?? trimmed;
15
+ const withoutQuery = withoutHash.split("?", 1)[0] ?? withoutHash;
16
+ return normalizeLowercaseStringOrEmpty(withoutQuery);
17
+ }
18
+ }
19
+
20
+ export function isLikelyDiscordVideoMedia(mediaUrl: string): boolean {
21
+ const normalized = normalizeMediaPathForExtension(mediaUrl);
22
+ for (const ext of DISCORD_VIDEO_MEDIA_EXTENSIONS) {
23
+ if (normalized.endsWith(ext)) {
24
+ return true;
25
+ }
26
+ }
27
+ return false;
28
+ }
@@ -0,0 +1,147 @@
1
+ import {
2
+ normalizeLowercaseStringOrEmpty,
3
+ normalizeOptionalString,
4
+ normalizeOptionalStringifiedId,
5
+ } from "autobot/plugin-sdk/string-coerce-runtime";
6
+ import { resolveDiscordDirectoryUserId } from "./directory-cache.js";
7
+
8
+ type DiscordMentionAliasesConfig = Record<string, string>;
9
+
10
+ const MARKDOWN_CODE_SEGMENT_PATTERN = /```[\s\S]*?```|`[^`\n]*`/g;
11
+ const MENTION_CANDIDATE_PATTERN = /(^|[\s([{"'.,;:!?])@([a-z0-9_.-]{2,32}(?:#[0-9]{4})?)/gi;
12
+ const DISCORD_RESERVED_MENTIONS = new Set(["everyone", "here"]);
13
+ const DISCORD_DISCRIMINATOR_SUFFIX = /#\d{4}$/;
14
+
15
+ function normalizeSnowflake(value: string | number | bigint): string | null {
16
+ const text = normalizeOptionalStringifiedId(value) ?? "";
17
+ if (!/^\d+$/.test(text)) {
18
+ return null;
19
+ }
20
+ return text;
21
+ }
22
+
23
+ export function formatMention(params: {
24
+ userId?: string | number | bigint | null;
25
+ roleId?: string | number | bigint | null;
26
+ channelId?: string | number | bigint | null;
27
+ }): string {
28
+ const userId = params.userId == null ? null : normalizeSnowflake(params.userId);
29
+ const roleId = params.roleId == null ? null : normalizeSnowflake(params.roleId);
30
+ const channelId = params.channelId == null ? null : normalizeSnowflake(params.channelId);
31
+ const values = [
32
+ userId ? { kind: "user" as const, id: userId } : null,
33
+ roleId ? { kind: "role" as const, id: roleId } : null,
34
+ channelId ? { kind: "channel" as const, id: channelId } : null,
35
+ ].filter((entry): entry is { kind: "user" | "role" | "channel"; id: string } => Boolean(entry));
36
+ if (values.length !== 1) {
37
+ throw new Error("formatMention requires exactly one of userId, roleId, or channelId");
38
+ }
39
+ const target = values[0];
40
+ if (target.kind === "user") {
41
+ return `<@${target.id}>`;
42
+ }
43
+ if (target.kind === "role") {
44
+ return `<@&${target.id}>`;
45
+ }
46
+ return `<#${target.id}>`;
47
+ }
48
+
49
+ function normalizeHandleKey(raw: string): string | null {
50
+ let handle = normalizeOptionalString(raw) ?? "";
51
+ if (!handle) {
52
+ return null;
53
+ }
54
+ if (handle.startsWith("@")) {
55
+ handle = normalizeOptionalString(handle.slice(1)) ?? "";
56
+ }
57
+ if (!handle || /\s/.test(handle)) {
58
+ return null;
59
+ }
60
+ return normalizeLowercaseStringOrEmpty(handle);
61
+ }
62
+
63
+ function resolveConfiguredMentionAlias(
64
+ handle: string,
65
+ mentionAliases?: DiscordMentionAliasesConfig | null,
66
+ ): string | undefined {
67
+ const key = normalizeHandleKey(handle);
68
+ if (!key || !mentionAliases) {
69
+ return undefined;
70
+ }
71
+ const withoutDiscriminator = key.replace(DISCORD_DISCRIMINATOR_SUFFIX, "");
72
+ for (const [rawAlias, rawUserId] of Object.entries(mentionAliases)) {
73
+ const alias = normalizeHandleKey(rawAlias);
74
+ if (!alias) {
75
+ continue;
76
+ }
77
+ const aliasWithoutDiscriminator = alias.replace(DISCORD_DISCRIMINATOR_SUFFIX, "");
78
+ if (
79
+ alias === key ||
80
+ (withoutDiscriminator && withoutDiscriminator !== key && alias === withoutDiscriminator) ||
81
+ (aliasWithoutDiscriminator &&
82
+ aliasWithoutDiscriminator !== alias &&
83
+ aliasWithoutDiscriminator === key)
84
+ ) {
85
+ const userId = normalizeSnowflake(rawUserId);
86
+ if (userId) {
87
+ return userId;
88
+ }
89
+ }
90
+ }
91
+ return undefined;
92
+ }
93
+
94
+ function rewritePlainTextMentions(
95
+ text: string,
96
+ params: {
97
+ accountId?: string | null;
98
+ mentionAliases?: DiscordMentionAliasesConfig | null;
99
+ },
100
+ ): string {
101
+ if (!text.includes("@")) {
102
+ return text;
103
+ }
104
+ return text.replace(MENTION_CANDIDATE_PATTERN, (match, prefix, rawHandle) => {
105
+ const handle = normalizeOptionalString(rawHandle) ?? "";
106
+ if (!handle) {
107
+ return match;
108
+ }
109
+ const lookup = normalizeLowercaseStringOrEmpty(handle);
110
+ if (DISCORD_RESERVED_MENTIONS.has(lookup)) {
111
+ return match;
112
+ }
113
+ const userId =
114
+ resolveConfiguredMentionAlias(handle, params.mentionAliases) ??
115
+ resolveDiscordDirectoryUserId({
116
+ accountId: params.accountId,
117
+ handle,
118
+ });
119
+ if (!userId) {
120
+ return match;
121
+ }
122
+ return `${String(prefix ?? "")}${formatMention({ userId })}`;
123
+ });
124
+ }
125
+
126
+ export function rewriteDiscordKnownMentions(
127
+ text: string,
128
+ params: {
129
+ accountId?: string | null;
130
+ mentionAliases?: DiscordMentionAliasesConfig | null;
131
+ },
132
+ ): string {
133
+ if (!text.includes("@")) {
134
+ return text;
135
+ }
136
+ let rewritten = "";
137
+ let offset = 0;
138
+ MARKDOWN_CODE_SEGMENT_PATTERN.lastIndex = 0;
139
+ for (const match of text.matchAll(MARKDOWN_CODE_SEGMENT_PATTERN)) {
140
+ const matchIndex = match.index ?? 0;
141
+ rewritten += rewritePlainTextMentions(text.slice(offset, matchIndex), params);
142
+ rewritten += match[0];
143
+ offset = matchIndex + match[0].length;
144
+ }
145
+ rewritten += rewritePlainTextMentions(text.slice(offset), params);
146
+ return rewritten;
147
+ }
@@ -0,0 +1,70 @@
1
+ import {
2
+ createStatusReactionController,
3
+ logAckFailure,
4
+ type StatusReactionAdapter,
5
+ } from "autobot/plugin-sdk/channel-feedback";
6
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
7
+ import { logVerbose } from "autobot/plugin-sdk/runtime-env";
8
+ import { createDiscordRuntimeAccountContext } from "../client.js";
9
+ import type { RequestClient } from "../internal/discord.js";
10
+ import { reactMessageDiscord, removeReactionDiscord } from "../send.js";
11
+ import type { DiscordReactionRuntimeContext } from "../send.types.js";
12
+
13
+ export function createDiscordAckReactionContext(params: {
14
+ rest: RequestClient;
15
+ cfg: AutoBotConfig;
16
+ accountId: string;
17
+ }): DiscordReactionRuntimeContext {
18
+ return {
19
+ rest: params.rest,
20
+ ...createDiscordRuntimeAccountContext({
21
+ cfg: params.cfg,
22
+ accountId: params.accountId,
23
+ }),
24
+ };
25
+ }
26
+
27
+ export function createDiscordAckReactionAdapter(params: {
28
+ channelId: string;
29
+ messageId: string;
30
+ reactionContext: DiscordReactionRuntimeContext;
31
+ }): StatusReactionAdapter {
32
+ return {
33
+ setReaction: async (emoji) => {
34
+ await reactMessageDiscord(params.channelId, params.messageId, emoji, params.reactionContext);
35
+ },
36
+ removeReaction: async (emoji) => {
37
+ await removeReactionDiscord(
38
+ params.channelId,
39
+ params.messageId,
40
+ emoji,
41
+ params.reactionContext,
42
+ );
43
+ },
44
+ };
45
+ }
46
+
47
+ export function queueInitialDiscordAckReaction(params: {
48
+ enabled: boolean;
49
+ shouldSendAckReaction: boolean;
50
+ ackReaction: string | undefined;
51
+ statusReactions: ReturnType<typeof createStatusReactionController>;
52
+ reactionAdapter: StatusReactionAdapter;
53
+ target: string;
54
+ }) {
55
+ if (params.enabled) {
56
+ void params.statusReactions.setQueued();
57
+ return;
58
+ }
59
+ if (!params.shouldSendAckReaction || !params.ackReaction) {
60
+ return;
61
+ }
62
+ void params.reactionAdapter.setReaction(params.ackReaction).catch((err) => {
63
+ logAckFailure({
64
+ log: logVerbose,
65
+ channel: "discord",
66
+ target: params.target,
67
+ error: err,
68
+ });
69
+ });
70
+ }
@@ -0,0 +1,7 @@
1
+ export { resolveInteractionContextWithDmAuth } from "./agent-components-dm-auth.js";
2
+ export {
3
+ ensureAgentComponentInteractionAllowed,
4
+ ensureComponentUserAllowed,
5
+ resolveAuthorizedComponentInteraction,
6
+ resolveComponentCommandAuthorized,
7
+ } from "./agent-components-guild-auth.js";
@@ -0,0 +1,154 @@
1
+ import { ChannelType } from "discord-api-types/v10";
2
+ import { logError } from "autobot/plugin-sdk/logging-core";
3
+ import { resolveAgentRoute } from "autobot/plugin-sdk/routing";
4
+ import {
5
+ type AgentComponentContext,
6
+ type AgentComponentInteraction,
7
+ type AgentComponentMessageInteraction,
8
+ type ComponentInteractionContext,
9
+ type DiscordChannelContext,
10
+ } from "./agent-components.types.js";
11
+ import { normalizeDiscordDisplaySlug, normalizeDiscordSlug } from "./allow-list.js";
12
+ import { resolveDiscordChannelInfoSafe } from "./channel-access.js";
13
+
14
+ function formatUsername(user: { username: string; discriminator?: string | null }): string {
15
+ if (user.discriminator && user.discriminator !== "0") {
16
+ return `${user.username}#${user.discriminator}`;
17
+ }
18
+ return user.username;
19
+ }
20
+
21
+ function isThreadChannelType(channelType: number | undefined): boolean {
22
+ return (
23
+ channelType === ChannelType.PublicThread ||
24
+ channelType === ChannelType.PrivateThread ||
25
+ channelType === ChannelType.AnnouncementThread
26
+ );
27
+ }
28
+
29
+ export function resolveAgentComponentRoute(params: {
30
+ ctx: AgentComponentContext;
31
+ rawGuildId: string | undefined;
32
+ memberRoleIds: string[];
33
+ isDirectMessage: boolean;
34
+ isGroupDm: boolean;
35
+ userId: string;
36
+ channelId: string;
37
+ parentId: string | undefined;
38
+ }) {
39
+ return resolveAgentRoute({
40
+ cfg: params.ctx.cfg,
41
+ channel: "discord",
42
+ accountId: params.ctx.accountId,
43
+ guildId: params.rawGuildId,
44
+ memberRoleIds: params.memberRoleIds,
45
+ peer: {
46
+ kind: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
47
+ id: params.isDirectMessage ? params.userId : params.channelId,
48
+ },
49
+ parentPeer: params.parentId ? { kind: "channel", id: params.parentId } : undefined,
50
+ });
51
+ }
52
+
53
+ export async function ackComponentInteraction(params: {
54
+ interaction: AgentComponentInteraction;
55
+ replyOpts: { ephemeral?: boolean };
56
+ label: string;
57
+ }) {
58
+ try {
59
+ await params.interaction.reply({
60
+ content: "✓",
61
+ ...params.replyOpts,
62
+ });
63
+ } catch (err) {
64
+ logError(`${params.label}: failed to acknowledge interaction: ${String(err)}`);
65
+ }
66
+ }
67
+
68
+ export function resolveDiscordChannelContext(
69
+ interaction: AgentComponentInteraction,
70
+ ): DiscordChannelContext {
71
+ const channel = interaction.channel;
72
+ const channelInfo = resolveDiscordChannelInfoSafe(channel);
73
+ const channelName = channelInfo.name;
74
+ const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
75
+ const displayChannelSlug = channelName ? normalizeDiscordDisplaySlug(channelName) : "";
76
+ const channelType = channelInfo.type;
77
+ const isThread = isThreadChannelType(channelType);
78
+
79
+ let parentId: string | undefined;
80
+ let parentName: string | undefined;
81
+ let parentSlug = "";
82
+ if (isThread) {
83
+ parentId = channelInfo.parentId;
84
+ parentName = channelInfo.parentName;
85
+ if (parentName) {
86
+ parentSlug = normalizeDiscordSlug(parentName);
87
+ }
88
+ }
89
+
90
+ return {
91
+ channelName,
92
+ channelSlug,
93
+ displayChannelSlug,
94
+ channelType,
95
+ isThread,
96
+ parentId,
97
+ parentName,
98
+ parentSlug,
99
+ };
100
+ }
101
+
102
+ export async function resolveComponentInteractionContext(params: {
103
+ interaction: AgentComponentInteraction;
104
+ label: string;
105
+ defer?: boolean;
106
+ }): Promise<ComponentInteractionContext | null> {
107
+ const { interaction, label } = params;
108
+ const channelId = interaction.rawData.channel_id;
109
+ if (!channelId) {
110
+ logError(`${label}: missing channel_id in interaction`);
111
+ return null;
112
+ }
113
+
114
+ const user = interaction.user;
115
+ if (!user) {
116
+ logError(`${label}: missing user in interaction`);
117
+ return null;
118
+ }
119
+
120
+ const shouldDefer = params.defer !== false && "defer" in interaction;
121
+ let didDefer = false;
122
+ if (shouldDefer) {
123
+ try {
124
+ await (interaction as AgentComponentMessageInteraction).defer({ ephemeral: true });
125
+ didDefer = true;
126
+ } catch (err) {
127
+ logError(`${label}: failed to defer interaction: ${String(err)}`);
128
+ }
129
+ }
130
+ const replyOpts = didDefer ? {} : { ephemeral: true };
131
+
132
+ const username = formatUsername(user);
133
+ const userId = user.id;
134
+ const rawGuildId = interaction.rawData.guild_id;
135
+ const channelType = resolveDiscordChannelContext(interaction).channelType;
136
+ const isGroupDm = channelType === ChannelType.GroupDM;
137
+ const isDirectMessage =
138
+ channelType === ChannelType.DM || (!rawGuildId && !isGroupDm && channelType == null);
139
+ const memberRoleIds = Array.isArray(interaction.rawData.member?.roles)
140
+ ? interaction.rawData.member.roles.map((roleId: string) => roleId)
141
+ : [];
142
+
143
+ return {
144
+ channelId,
145
+ user,
146
+ username,
147
+ userId,
148
+ replyOpts,
149
+ rawGuildId,
150
+ isDirectMessage,
151
+ isGroupDm,
152
+ memberRoleIds,
153
+ };
154
+ }