@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,259 @@
1
+ import {
2
+ type AccessGroupMembershipFact,
3
+ type ChannelIngressEventInput,
4
+ type ChannelIngressIdentifierKind,
5
+ createChannelIngressResolver,
6
+ defineStableChannelIngressIdentity,
7
+ type ChannelIngressIdentitySubjectInput,
8
+ type ResolveChannelMessageIngressParams,
9
+ } from "autobot/plugin-sdk/channel-ingress-runtime";
10
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
11
+ import { logVerbose } from "autobot/plugin-sdk/runtime-env";
12
+ import type { RequestClient } from "../internal/discord.js";
13
+ import { canViewDiscordGuildChannel } from "../send.permissions.js";
14
+ import { normalizeDiscordAllowList } from "./allow-list.js";
15
+
16
+ const DISCORD_ALLOW_LIST_PREFIXES = ["discord:", "user:", "pk:"];
17
+ const DISCORD_CHANNEL_ID = "discord";
18
+ const DISCORD_USER_ID_KIND = "stable-id" satisfies ChannelIngressIdentifierKind;
19
+ const DISCORD_USER_NAME_KIND = "username" satisfies ChannelIngressIdentifierKind;
20
+
21
+ export type DiscordDmPolicy = "open" | "pairing" | "allowlist" | "disabled";
22
+
23
+ function normalizeDiscordIdEntry(entry: string): string | null {
24
+ const text = entry.trim();
25
+ if (!text) {
26
+ return null;
27
+ }
28
+ const maybeId = text.replace(/^<@!?/, "").replace(/>$/, "");
29
+ if (/^\d+$/.test(maybeId)) {
30
+ return maybeId;
31
+ }
32
+ const prefix = DISCORD_ALLOW_LIST_PREFIXES.find((entryPrefix) => text.startsWith(entryPrefix));
33
+ if (prefix) {
34
+ const candidate = text.slice(prefix.length).trim();
35
+ return candidate || null;
36
+ }
37
+ return null;
38
+ }
39
+
40
+ function normalizeDiscordNameEntry(entry: string): string | null {
41
+ const text = entry.trim();
42
+ if (!text || text === "*" || normalizeDiscordIdEntry(text)) {
43
+ return null;
44
+ }
45
+ const nameSlug = normalizeDiscordAllowList([text], DISCORD_ALLOW_LIST_PREFIXES)
46
+ ?.names.values()
47
+ .next().value;
48
+ return typeof nameSlug === "string" && nameSlug ? nameSlug : null;
49
+ }
50
+
51
+ function normalizeDiscordNameSubject(value: string): string | null {
52
+ const nameSlug = normalizeDiscordAllowList([value], DISCORD_ALLOW_LIST_PREFIXES)
53
+ ?.names.values()
54
+ .next().value;
55
+ return typeof nameSlug === "string" && nameSlug ? nameSlug : null;
56
+ }
57
+
58
+ const discordIngressIdentity = defineStableChannelIngressIdentity({
59
+ key: "discordUserId",
60
+ kind: DISCORD_USER_ID_KIND,
61
+ normalizeEntry: normalizeDiscordIdEntry,
62
+ normalizeSubject: (value) => value.trim() || null,
63
+ sensitivity: "pii",
64
+ aliases: (
65
+ [
66
+ ["discordUserName", normalizeDiscordNameEntry],
67
+ ["discordUserTag", () => null],
68
+ ] as const
69
+ ).map(([key, normalizeEntry]) => ({
70
+ key,
71
+ kind: DISCORD_USER_NAME_KIND,
72
+ normalizeEntry,
73
+ normalizeSubject: normalizeDiscordNameSubject,
74
+ dangerous: true,
75
+ sensitivity: "pii",
76
+ })),
77
+ });
78
+
79
+ function createDiscordDmIngressSubject(sender: {
80
+ id: string;
81
+ name?: string;
82
+ tag?: string;
83
+ }): ChannelIngressIdentitySubjectInput {
84
+ return {
85
+ stableId: sender.id,
86
+ aliases: {
87
+ discordUserName: sender.name,
88
+ discordUserTag: sender.tag,
89
+ },
90
+ };
91
+ }
92
+
93
+ function createDiscordDynamicAccessGroupResolver(params: {
94
+ cfg?: AutoBotConfig;
95
+ token?: string;
96
+ rest?: RequestClient;
97
+ }): ResolveChannelMessageIngressParams["resolveAccessGroupMembership"] {
98
+ if (!params.cfg) {
99
+ return undefined;
100
+ }
101
+ const cfg = params.cfg;
102
+ return async ({ name, group, accountId, subject }) => {
103
+ if (group.type !== "discord.channelAudience") {
104
+ return false;
105
+ }
106
+ const senderId = String(subject.stableId ?? "").trim();
107
+ if (!senderId) {
108
+ return false;
109
+ }
110
+ const membership = group.membership ?? "canViewChannel";
111
+ if (membership !== "canViewChannel") {
112
+ return false;
113
+ }
114
+ try {
115
+ return await canViewDiscordGuildChannel(group.guildId, group.channelId, senderId, {
116
+ cfg,
117
+ accountId,
118
+ token: params.token,
119
+ rest: params.rest,
120
+ });
121
+ } catch (err) {
122
+ logVerbose(`discord: accessGroup:${name} lookup failed for user ${senderId}: ${String(err)}`);
123
+ throw err;
124
+ }
125
+ };
126
+ }
127
+
128
+ function createDiscordIngressResolver(params: {
129
+ accountId: string;
130
+ cfg?: AutoBotConfig;
131
+ token?: string;
132
+ rest?: RequestClient;
133
+ readStoreAllowFrom?: ResolveChannelMessageIngressParams["readStoreAllowFrom"];
134
+ useDefaultPairingStore?: boolean;
135
+ }) {
136
+ return createChannelIngressResolver({
137
+ channelId: DISCORD_CHANNEL_ID,
138
+ accountId: params.accountId,
139
+ identity: discordIngressIdentity,
140
+ cfg: params.cfg,
141
+ resolveAccessGroupMembership: createDiscordDynamicAccessGroupResolver({
142
+ cfg: params.cfg,
143
+ token: params.token,
144
+ rest: params.rest,
145
+ }),
146
+ ...(params.readStoreAllowFrom ? { readStoreAllowFrom: params.readStoreAllowFrom } : {}),
147
+ ...(params.useDefaultPairingStore !== undefined
148
+ ? { useDefaultPairingStore: params.useDefaultPairingStore }
149
+ : {}),
150
+ });
151
+ }
152
+
153
+ function syntheticAccessGroupMembership(
154
+ groupName: string,
155
+ allowed: boolean,
156
+ ): AccessGroupMembershipFact {
157
+ return allowed
158
+ ? {
159
+ kind: "matched",
160
+ groupName,
161
+ source: "dynamic",
162
+ matchedEntryIds: [groupName],
163
+ }
164
+ : {
165
+ kind: "not-matched",
166
+ groupName,
167
+ source: "dynamic",
168
+ };
169
+ }
170
+
171
+ export async function resolveDiscordDmCommandAccess(params: {
172
+ accountId: string;
173
+ dmPolicy: DiscordDmPolicy;
174
+ configuredAllowFrom: string[];
175
+ sender: { id: string; name?: string; tag?: string };
176
+ allowNameMatching: boolean;
177
+ cfg?: AutoBotConfig;
178
+ token?: string;
179
+ rest?: RequestClient;
180
+ readStoreAllowFrom?: ResolveChannelMessageIngressParams["readStoreAllowFrom"];
181
+ eventKind?: ChannelIngressEventInput["kind"];
182
+ }) {
183
+ return await createDiscordIngressResolver({
184
+ accountId: params.accountId,
185
+ cfg: params.cfg,
186
+ token: params.token,
187
+ rest: params.rest,
188
+ readStoreAllowFrom: params.readStoreAllowFrom,
189
+ useDefaultPairingStore: params.readStoreAllowFrom == null,
190
+ }).message({
191
+ subject: createDiscordDmIngressSubject(params.sender),
192
+ conversation: {
193
+ kind: "direct",
194
+ id: params.sender.id,
195
+ },
196
+ event: {
197
+ kind: params.eventKind ?? "native-command",
198
+ authMode: "inbound",
199
+ mayPair: true,
200
+ },
201
+ dmPolicy: params.dmPolicy,
202
+ groupPolicy: "disabled",
203
+ policy: {
204
+ mutableIdentifierMatching: params.allowNameMatching ? "enabled" : "disabled",
205
+ },
206
+ allowFrom: params.configuredAllowFrom,
207
+ command: {
208
+ hasControlCommand: false,
209
+ modeWhenAccessGroupsOff: "configured",
210
+ },
211
+ });
212
+ }
213
+
214
+ export async function resolveDiscordTextCommandAccess(params: {
215
+ accountId: string;
216
+ sender: { id: string; name?: string; tag?: string };
217
+ ownerAllowFrom?: string[];
218
+ memberAccessConfigured: boolean;
219
+ memberAllowed: boolean;
220
+ allowNameMatching: boolean;
221
+ allowTextCommands: boolean;
222
+ hasControlCommand: boolean;
223
+ cfg?: AutoBotConfig;
224
+ token?: string;
225
+ rest?: RequestClient;
226
+ }) {
227
+ const ownerAllowFrom = (params.ownerAllowFrom ?? []).filter((entry) => entry.trim() !== "*");
228
+ const memberAccessGroup = "discord-member-access";
229
+ const commandGroup = params.memberAccessConfigured ? [`accessGroup:${memberAccessGroup}`] : [];
230
+ const accessGroupMembership = params.memberAccessConfigured
231
+ ? [syntheticAccessGroupMembership(memberAccessGroup, params.memberAllowed)]
232
+ : [];
233
+ const result = await createDiscordIngressResolver({
234
+ accountId: params.accountId,
235
+ cfg: params.cfg,
236
+ token: params.token,
237
+ rest: params.rest,
238
+ }).command({
239
+ subject: createDiscordDmIngressSubject(params.sender),
240
+ conversation: {
241
+ kind: "group",
242
+ id: "discord-command",
243
+ },
244
+ accessGroupMembership,
245
+ dmPolicy: "allowlist",
246
+ groupPolicy: "allowlist",
247
+ policy: {
248
+ mutableIdentifierMatching: params.allowNameMatching ? "enabled" : "disabled",
249
+ },
250
+ allowFrom: ownerAllowFrom,
251
+ groupAllowFrom: commandGroup,
252
+ command: {
253
+ allowTextCommands: params.allowTextCommands,
254
+ hasControlCommand: params.hasControlCommand,
255
+ modeWhenAccessGroupsOff: "configured",
256
+ },
257
+ });
258
+ return result.commandAccess;
259
+ }
@@ -0,0 +1,49 @@
1
+ import type { ResolvedChannelMessageIngress } from "autobot/plugin-sdk/channel-ingress-runtime";
2
+ import { createChannelPairingChallengeIssuer } from "autobot/plugin-sdk/channel-pairing";
3
+ import { upsertChannelPairingRequest } from "autobot/plugin-sdk/conversation-runtime";
4
+
5
+ export async function handleDiscordDmCommandDecision(params: {
6
+ senderAccess: Pick<ResolvedChannelMessageIngress["senderAccess"], "decision">;
7
+ accountId: string;
8
+ sender: {
9
+ id: string;
10
+ tag?: string;
11
+ name?: string;
12
+ };
13
+ onPairingCreated: (code: string) => Promise<void>;
14
+ onUnauthorized: () => Promise<void>;
15
+ upsertPairingRequest?: typeof upsertChannelPairingRequest;
16
+ }): Promise<boolean> {
17
+ if (params.senderAccess.decision === "allow") {
18
+ return true;
19
+ }
20
+
21
+ if (params.senderAccess.decision === "pairing") {
22
+ const upsertPairingRequest = params.upsertPairingRequest ?? upsertChannelPairingRequest;
23
+ const result = await createChannelPairingChallengeIssuer({
24
+ channel: "discord",
25
+ upsertPairingRequest: async ({ id, meta }) =>
26
+ await upsertPairingRequest({
27
+ channel: "discord",
28
+ id,
29
+ accountId: params.accountId,
30
+ meta,
31
+ }),
32
+ })({
33
+ senderId: params.sender.id,
34
+ senderIdLine: `Your Discord user id: ${params.sender.id}`,
35
+ meta: {
36
+ tag: params.sender.tag,
37
+ name: params.sender.name,
38
+ },
39
+ sendPairingReply: async () => {},
40
+ });
41
+ if (result.created && result.code) {
42
+ await params.onPairingCreated(result.code);
43
+ }
44
+ return false;
45
+ }
46
+
47
+ await params.onUnauthorized();
48
+ return false;
49
+ }
@@ -0,0 +1,161 @@
1
+ import { ButtonStyle } from "discord-api-types/v10";
2
+ import { resolveApprovalOverGateway } from "autobot/plugin-sdk/approval-gateway-runtime";
3
+ import type { ExecApprovalDecision } from "autobot/plugin-sdk/approval-runtime";
4
+ import type {
5
+ DiscordExecApprovalConfig,
6
+ AutoBotConfig,
7
+ } from "autobot/plugin-sdk/config-contracts";
8
+ import { Button, type ButtonInteraction, type ComponentData } from "../internal/discord.js";
9
+ export { buildExecApprovalCustomId } from "../approval-handler.runtime.js";
10
+ import { getDiscordExecApprovalApprovers } from "../exec-approvals.js";
11
+
12
+ export { extractDiscordChannelId } from "../approval-native.js";
13
+ function decodeCustomIdValue(value: string): string {
14
+ try {
15
+ return decodeURIComponent(value);
16
+ } catch {
17
+ return value;
18
+ }
19
+ }
20
+
21
+ export function parseExecApprovalData(
22
+ data: ComponentData,
23
+ ): { approvalId: string; action: ExecApprovalDecision } | null {
24
+ if (!data || typeof data !== "object") {
25
+ return null;
26
+ }
27
+ const coerce = (value: unknown) =>
28
+ typeof value === "string" || typeof value === "number" ? String(value) : "";
29
+ const rawId = coerce(data.id);
30
+ const rawAction = coerce(data.action);
31
+ if (!rawId || !rawAction) {
32
+ return null;
33
+ }
34
+ const action = rawAction as ExecApprovalDecision;
35
+ if (action !== "allow-once" && action !== "allow-always" && action !== "deny") {
36
+ return null;
37
+ }
38
+ return {
39
+ approvalId: decodeCustomIdValue(rawId),
40
+ action,
41
+ };
42
+ }
43
+
44
+ type ExecApprovalButtonContext = {
45
+ getApprovers: () => string[];
46
+ resolveApproval: (
47
+ approvalId: string,
48
+ decision: ExecApprovalDecision,
49
+ ) => Promise<ExecApprovalResolveResult>;
50
+ };
51
+
52
+ type ExecApprovalResolveResult = { ok: true } | { ok: false; reason: "error" | "not-found" };
53
+
54
+ function isStructuredApprovalNotFoundError(err: unknown): boolean {
55
+ if (!err || typeof err !== "object") {
56
+ return false;
57
+ }
58
+ const record = err as {
59
+ gatewayCode?: unknown;
60
+ details?: { reason?: unknown } | null;
61
+ };
62
+ if (record.gatewayCode === "APPROVAL_NOT_FOUND") {
63
+ return true;
64
+ }
65
+ return (
66
+ record.gatewayCode === "INVALID_REQUEST" && record.details?.reason === "APPROVAL_NOT_FOUND"
67
+ );
68
+ }
69
+
70
+ export class ExecApprovalButton extends Button {
71
+ override label = "execapproval";
72
+ customId = "execapproval:seed=1";
73
+ override style = ButtonStyle.Primary;
74
+
75
+ constructor(private readonly ctx: ExecApprovalButtonContext) {
76
+ super();
77
+ }
78
+
79
+ override async run(interaction: ButtonInteraction, data: ComponentData): Promise<void> {
80
+ const parsed = parseExecApprovalData(data);
81
+ if (!parsed) {
82
+ try {
83
+ await interaction.reply({
84
+ content: "This approval is no longer valid.",
85
+ ephemeral: true,
86
+ });
87
+ } catch {}
88
+ return;
89
+ }
90
+
91
+ const approvers = this.ctx.getApprovers();
92
+ const userId = interaction.userId;
93
+ if (!approvers.some((id) => id === userId)) {
94
+ try {
95
+ await interaction.reply({
96
+ content: "⛔ You are not authorized to approve exec requests.",
97
+ ephemeral: true,
98
+ });
99
+ } catch {}
100
+ return;
101
+ }
102
+
103
+ const decisionLabel =
104
+ parsed.action === "allow-once"
105
+ ? "Allowed (once)"
106
+ : parsed.action === "allow-always"
107
+ ? "Allowed (always)"
108
+ : "Denied";
109
+
110
+ try {
111
+ await interaction.acknowledge();
112
+ } catch {}
113
+
114
+ const result = await this.ctx.resolveApproval(parsed.approvalId, parsed.action);
115
+ if (!result.ok && result.reason !== "not-found") {
116
+ try {
117
+ await interaction.followUp({
118
+ content: `Failed to submit approval decision for **${decisionLabel}**. The request may have expired or already been resolved.`,
119
+ ephemeral: true,
120
+ });
121
+ } catch {}
122
+ }
123
+ }
124
+ }
125
+
126
+ export function createExecApprovalButton(ctx: ExecApprovalButtonContext): Button {
127
+ return new ExecApprovalButton(ctx);
128
+ }
129
+
130
+ export function createDiscordExecApprovalButtonContext(params: {
131
+ cfg: AutoBotConfig;
132
+ accountId: string;
133
+ config: DiscordExecApprovalConfig;
134
+ gatewayUrl?: string;
135
+ }): ExecApprovalButtonContext {
136
+ return {
137
+ getApprovers: () =>
138
+ getDiscordExecApprovalApprovers({
139
+ cfg: params.cfg,
140
+ accountId: params.accountId,
141
+ configOverride: params.config,
142
+ }),
143
+ resolveApproval: async (approvalId, decision) => {
144
+ try {
145
+ await resolveApprovalOverGateway({
146
+ cfg: params.cfg,
147
+ approvalId,
148
+ decision,
149
+ gatewayUrl: params.gatewayUrl,
150
+ clientDisplayName: `Discord approval (${params.accountId})`,
151
+ });
152
+ return { ok: true };
153
+ } catch (err) {
154
+ return {
155
+ ok: false,
156
+ reason: isStructuredApprovalNotFoundError(err) ? "not-found" : "error",
157
+ };
158
+ }
159
+ },
160
+ };
161
+ }
@@ -0,0 +1,45 @@
1
+ import type { Guild, User } from "../internal/discord.js";
2
+
3
+ export function resolveDiscordSystemLocation(params: {
4
+ isDirectMessage: boolean;
5
+ isGroupDm: boolean;
6
+ guild?: Guild;
7
+ channelName: string;
8
+ }) {
9
+ const { isDirectMessage, isGroupDm, guild, channelName } = params;
10
+ if (isDirectMessage) {
11
+ return "DM";
12
+ }
13
+ if (isGroupDm) {
14
+ return `Group DM #${channelName}`;
15
+ }
16
+ return guild?.name ? `${guild.name} #${channelName}` : `#${channelName}`;
17
+ }
18
+
19
+ export function formatDiscordReactionEmoji(emoji: { id?: string | null; name?: string | null }) {
20
+ if (emoji.id && emoji.name) {
21
+ // Custom guild emoji in Discord-renderable form.
22
+ return `<:${emoji.name}:${emoji.id}>`;
23
+ }
24
+ if (emoji.id) {
25
+ // Keep id visible even when name is missing (instead of opaque "emoji").
26
+ return `emoji:${emoji.id}`;
27
+ }
28
+ return emoji.name ?? "emoji";
29
+ }
30
+
31
+ export function formatDiscordUserTag(user: User) {
32
+ const discriminator = (user.discriminator ?? "").trim();
33
+ if (discriminator && discriminator !== "0") {
34
+ return `${user.username}#${discriminator}`;
35
+ }
36
+ return user.username ?? user.id;
37
+ }
38
+
39
+ export function resolveTimestampMs(timestamp?: string | null) {
40
+ if (!timestamp) {
41
+ return undefined;
42
+ }
43
+ const parsed = Date.parse(timestamp);
44
+ return Number.isNaN(parsed) ? undefined : parsed;
45
+ }
@@ -0,0 +1,34 @@
1
+ import type { EventEmitter } from "node:events";
2
+ import type { GatewayPlugin } from "../internal/gateway.js";
3
+
4
+ export const DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT =
5
+ "autobot:discord-gateway-transport-activity";
6
+
7
+ export type DiscordGatewayHandle = Pick<GatewayPlugin, "disconnect"> & {
8
+ emitter?: EventEmitter;
9
+ };
10
+
11
+ type GatewaySocketListener = (...args: unknown[]) => void;
12
+
13
+ type DiscordGatewaySocket = {
14
+ on: (event: "close" | "error", listener: GatewaySocketListener) => unknown;
15
+ listeners: (event: "close" | "error") => GatewaySocketListener[];
16
+ removeListener: (event: "close" | "error", listener: GatewaySocketListener) => unknown;
17
+ terminate?: () => void;
18
+ };
19
+
20
+ export type MutableDiscordGateway = GatewayPlugin & {
21
+ emitter?: EventEmitter;
22
+ options: Record<string, unknown> & {
23
+ reconnect?: {
24
+ maxAttempts?: number;
25
+ };
26
+ };
27
+ state?: {
28
+ sessionId?: string | null;
29
+ resumeGatewayUrl?: string | null;
30
+ sequence?: number | null;
31
+ };
32
+ sequence?: number | null;
33
+ ws?: DiscordGatewaySocket | null;
34
+ };