@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,86 @@
1
+ import { parseDiscordTarget } from "./target-parsing.js";
2
+
3
+ export function normalizeDiscordMessagingTarget(raw: string): string | undefined {
4
+ // Default bare IDs to channels so routing is stable across tool actions.
5
+ const target = parseDiscordTarget(raw, { defaultKind: "channel" });
6
+ return target?.normalized;
7
+ }
8
+
9
+ /**
10
+ * Normalize a Discord outbound target for delivery. Bare numeric IDs are
11
+ * prefixed with "channel:" to avoid the ambiguous-target error in
12
+ * parseDiscordTarget, unless the ID is explicitly configured as an allowed DM
13
+ * sender. All other formats pass through unchanged.
14
+ */
15
+ export function normalizeDiscordOutboundTarget(
16
+ to?: string,
17
+ allowFrom?: readonly string[],
18
+ ): { ok: true; to: string } | { ok: false; error: Error } {
19
+ const trimmed = to?.trim();
20
+ if (!trimmed) {
21
+ return {
22
+ ok: false,
23
+ error: new Error(
24
+ 'Discord recipient is required. Use "channel:<id>" for channels or "user:<id>" for DMs.',
25
+ ),
26
+ };
27
+ }
28
+ if (/^\d+$/.test(trimmed)) {
29
+ if (allowFromContainsDiscordUserId(allowFrom, trimmed)) {
30
+ return { ok: true, to: `user:${trimmed}` };
31
+ }
32
+ return { ok: true, to: `channel:${trimmed}` };
33
+ }
34
+ return { ok: true, to: trimmed };
35
+ }
36
+
37
+ export function allowFromContainsDiscordUserId(
38
+ allowFrom: readonly string[] | undefined,
39
+ userId: string,
40
+ ): boolean {
41
+ const normalizedUserId = userId.trim();
42
+ if (!normalizedUserId) {
43
+ return false;
44
+ }
45
+ return (allowFrom ?? []).some(
46
+ (entry) => normalizeAllowFromDiscordUserId(entry) === normalizedUserId,
47
+ );
48
+ }
49
+
50
+ function normalizeAllowFromDiscordUserId(entry: string): string | undefined {
51
+ const trimmed = entry.trim().toLowerCase();
52
+ if (!trimmed || trimmed === "*") {
53
+ return undefined;
54
+ }
55
+ const mentionMatch = /^<@!?(\d+)>$/.exec(trimmed);
56
+ if (mentionMatch) {
57
+ return mentionMatch[1];
58
+ }
59
+ // Accept both current and legacy allowFrom forms for Discord user IDs.
60
+ const prefixedMatch = /^(?:discord:)?user:(\d+)$/.exec(trimmed);
61
+ if (prefixedMatch) {
62
+ return prefixedMatch[1];
63
+ }
64
+ const discordMatch = /^discord:(\d+)$/.exec(trimmed);
65
+ if (discordMatch) {
66
+ return discordMatch[1];
67
+ }
68
+ return /^\d+$/.test(trimmed) ? trimmed : undefined;
69
+ }
70
+
71
+ export function looksLikeDiscordTargetId(raw: string): boolean {
72
+ const trimmed = raw.trim();
73
+ if (!trimmed) {
74
+ return false;
75
+ }
76
+ if (/^<@!?\d+>$/.test(trimmed)) {
77
+ return true;
78
+ }
79
+ if (/^(user|channel|discord):/i.test(trimmed)) {
80
+ return true;
81
+ }
82
+ if (/^\d{6,}$/.test(trimmed)) {
83
+ return true;
84
+ }
85
+ return false;
86
+ }
@@ -0,0 +1,327 @@
1
+ import {
2
+ type ChannelOutboundAdapter,
3
+ createAttachedChannelResultAdapter,
4
+ } from "autobot/plugin-sdk/channel-send-result";
5
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
6
+ import type { OutboundIdentity } from "autobot/plugin-sdk/outbound-runtime";
7
+ import { resolveOutboundSendDep } from "autobot/plugin-sdk/outbound-send-deps";
8
+ import {
9
+ normalizeOptionalString,
10
+ normalizeOptionalStringifiedId,
11
+ } from "autobot/plugin-sdk/string-coerce-runtime";
12
+ import { chunkDiscordTextWithMode } from "./chunk.js";
13
+ import { withDiscordDeliveryRetry } from "./delivery-retry.js";
14
+ import { notifyDiscordInboundEventOutboundPayloadSuccess } from "./inbound-event-delivery.js";
15
+ import { isLikelyDiscordVideoMedia } from "./media-detection.js";
16
+ import type { ThreadBindingRecord } from "./monitor/thread-bindings.js";
17
+ import { normalizeDiscordOutboundTarget } from "./normalize.js";
18
+ import { normalizeDiscordApprovalPayload } from "./outbound-approval.js";
19
+ import { buildDiscordPresentationPayload } from "./outbound-components.js";
20
+ import { sendDiscordOutboundPayload } from "./outbound-payload.js";
21
+ import {
22
+ loadDiscordSendRuntime,
23
+ resolveDiscordFormattingOptions,
24
+ resolveDiscordOutboundTarget,
25
+ type DiscordSendFn,
26
+ type DiscordVoiceSendFn,
27
+ } from "./outbound-send-context.js";
28
+
29
+ export const DISCORD_TEXT_CHUNK_LIMIT = 2000;
30
+ const DISCORD_INTERNAL_RUNTIME_SCAFFOLDING_BLOCK_RE =
31
+ /<\s*(system-reminder|previous_response)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi;
32
+ const DISCORD_INTERNAL_RUNTIME_SCAFFOLDING_SELF_CLOSING_RE =
33
+ /<\s*(?:system-reminder|previous_response)\b[^>]*\/\s*>/gi;
34
+ const DISCORD_INTERNAL_RUNTIME_SCAFFOLDING_TAG_RE =
35
+ /<\s*\/?\s*(?:system-reminder|previous_response)\b[^>]*>/gi;
36
+
37
+ function stripDiscordInternalRuntimeScaffolding(text: string): string {
38
+ return text
39
+ .replace(DISCORD_INTERNAL_RUNTIME_SCAFFOLDING_BLOCK_RE, "")
40
+ .replace(DISCORD_INTERNAL_RUNTIME_SCAFFOLDING_SELF_CLOSING_RE, "")
41
+ .replace(DISCORD_INTERNAL_RUNTIME_SCAFFOLDING_TAG_RE, "");
42
+ }
43
+
44
+ type DiscordThreadBindingsModule = typeof import("./monitor/thread-bindings.js");
45
+
46
+ let discordThreadBindingsPromise: Promise<DiscordThreadBindingsModule> | undefined;
47
+
48
+ function loadDiscordThreadBindings(): Promise<DiscordThreadBindingsModule> {
49
+ discordThreadBindingsPromise ??= import("./monitor/thread-bindings.js");
50
+ return discordThreadBindingsPromise;
51
+ }
52
+
53
+ function resolveDiscordWebhookIdentity(params: {
54
+ identity?: OutboundIdentity;
55
+ binding: ThreadBindingRecord;
56
+ }): { username?: string; avatarUrl?: string } {
57
+ const usernameRaw = normalizeOptionalString(params.identity?.name);
58
+ const fallbackUsername = normalizeOptionalString(params.binding.label) ?? params.binding.agentId;
59
+ const username = (usernameRaw || fallbackUsername || "").slice(0, 80) || undefined;
60
+ const avatarUrl = normalizeOptionalString(params.identity?.avatarUrl);
61
+ return { username, avatarUrl };
62
+ }
63
+
64
+ async function maybeSendDiscordWebhookText(params: {
65
+ cfg: AutoBotConfig;
66
+ text: string;
67
+ threadId?: string | number | null;
68
+ accountId?: string | null;
69
+ identity?: OutboundIdentity;
70
+ replyToId?: string | null;
71
+ }): Promise<{ messageId: string; channelId: string } | null> {
72
+ if (params.threadId == null) {
73
+ return null;
74
+ }
75
+ const threadId = normalizeOptionalStringifiedId(params.threadId) ?? "";
76
+ if (!threadId) {
77
+ return null;
78
+ }
79
+ const { getThreadBindingManager } = await loadDiscordThreadBindings();
80
+ const manager = getThreadBindingManager(params.accountId ?? undefined);
81
+ if (!manager) {
82
+ return null;
83
+ }
84
+ const binding = manager.getByThreadId(threadId);
85
+ if (!binding?.webhookId || !binding?.webhookToken) {
86
+ return null;
87
+ }
88
+ const persona = resolveDiscordWebhookIdentity({
89
+ identity: params.identity,
90
+ binding,
91
+ });
92
+ const { sendWebhookMessageDiscord } = await loadDiscordSendRuntime();
93
+ const result = await sendWebhookMessageDiscord(params.text, {
94
+ webhookId: binding.webhookId,
95
+ webhookToken: binding.webhookToken,
96
+ accountId: binding.accountId,
97
+ threadId: binding.threadId,
98
+ cfg: params.cfg,
99
+ replyTo: params.replyToId ?? undefined,
100
+ username: persona.username,
101
+ avatarUrl: persona.avatarUrl,
102
+ });
103
+ return result;
104
+ }
105
+
106
+ export const discordOutbound: ChannelOutboundAdapter = {
107
+ deliveryMode: "direct",
108
+ chunker: (text, limit, ctx) =>
109
+ chunkDiscordTextWithMode(text, {
110
+ maxChars: limit,
111
+ maxLines: ctx?.formatting?.maxLinesPerMessage,
112
+ }),
113
+ textChunkLimit: DISCORD_TEXT_CHUNK_LIMIT,
114
+ sanitizeText: ({ text }) => stripDiscordInternalRuntimeScaffolding(text),
115
+ pollMaxOptions: 10,
116
+ normalizePayload: ({ payload }) => normalizeDiscordApprovalPayload(payload),
117
+ presentationCapabilities: {
118
+ supported: true,
119
+ buttons: true,
120
+ selects: true,
121
+ context: true,
122
+ divider: true,
123
+ limits: {
124
+ actions: {
125
+ maxActions: 25,
126
+ maxActionsPerRow: 5,
127
+ maxRows: 5,
128
+ maxLabelLength: 80,
129
+ supportsDisabled: true,
130
+ },
131
+ selects: {
132
+ maxOptions: 25,
133
+ maxLabelLength: 100,
134
+ maxValueBytes: 100,
135
+ },
136
+ text: {
137
+ maxLength: DISCORD_TEXT_CHUNK_LIMIT,
138
+ encoding: "characters",
139
+ markdownDialect: "discord-markdown",
140
+ },
141
+ },
142
+ },
143
+ deliveryCapabilities: {
144
+ durableFinal: {
145
+ text: true,
146
+ media: true,
147
+ payload: true,
148
+ silent: true,
149
+ replyTo: true,
150
+ thread: true,
151
+ messageSendingHooks: true,
152
+ },
153
+ },
154
+ renderPresentation: async ({ payload, presentation }) => {
155
+ return await buildDiscordPresentationPayload({
156
+ payload,
157
+ presentation,
158
+ });
159
+ },
160
+ resolveTarget: ({ to, allowFrom }) => normalizeDiscordOutboundTarget(to, allowFrom),
161
+ sendPayload: async (ctx) =>
162
+ await sendDiscordOutboundPayload({
163
+ ctx,
164
+ fallbackAdapter: discordOutbound,
165
+ }),
166
+ ...createAttachedChannelResultAdapter({
167
+ channel: "discord",
168
+ sendText: async ({
169
+ cfg,
170
+ to,
171
+ text,
172
+ accountId,
173
+ deps,
174
+ replyToId,
175
+ threadId,
176
+ identity,
177
+ silent,
178
+ formatting,
179
+ }) => {
180
+ if (!silent) {
181
+ const webhookResult = await maybeSendDiscordWebhookText({
182
+ cfg,
183
+ text,
184
+ threadId,
185
+ accountId,
186
+ identity,
187
+ replyToId,
188
+ }).catch(() => null);
189
+ if (webhookResult) {
190
+ return webhookResult;
191
+ }
192
+ }
193
+ const send =
194
+ resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
195
+ (await loadDiscordSendRuntime()).sendMessageDiscord;
196
+ return await withDiscordDeliveryRetry({
197
+ cfg,
198
+ accountId,
199
+ fn: async () =>
200
+ await send(resolveDiscordOutboundTarget({ to, threadId }), text, {
201
+ verbose: false,
202
+ replyTo: replyToId ?? undefined,
203
+ accountId: accountId ?? undefined,
204
+ silent: silent ?? undefined,
205
+ cfg,
206
+ ...resolveDiscordFormattingOptions({ formatting }),
207
+ }),
208
+ });
209
+ },
210
+ sendMedia: async ({
211
+ cfg,
212
+ to,
213
+ text,
214
+ mediaUrl,
215
+ audioAsVoice,
216
+ mediaAccess,
217
+ mediaLocalRoots,
218
+ mediaReadFile,
219
+ accountId,
220
+ deps,
221
+ replyToId,
222
+ threadId,
223
+ silent,
224
+ formatting,
225
+ }) => {
226
+ const send =
227
+ resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
228
+ (await loadDiscordSendRuntime()).sendMessageDiscord;
229
+ const target = resolveDiscordOutboundTarget({ to, threadId });
230
+ const formattingOptions = resolveDiscordFormattingOptions({ formatting });
231
+ if (audioAsVoice && mediaUrl) {
232
+ const sendVoice =
233
+ resolveOutboundSendDep<DiscordVoiceSendFn>(deps, "discordVoice") ??
234
+ (await loadDiscordSendRuntime()).sendVoiceMessageDiscord;
235
+ return await withDiscordDeliveryRetry({
236
+ cfg,
237
+ accountId,
238
+ fn: async () =>
239
+ await sendVoice(target, mediaUrl, {
240
+ cfg,
241
+ replyTo: replyToId ?? undefined,
242
+ accountId: accountId ?? undefined,
243
+ silent: silent ?? undefined,
244
+ }),
245
+ });
246
+ }
247
+ if (text.trim() && mediaUrl && isLikelyDiscordVideoMedia(mediaUrl)) {
248
+ await withDiscordDeliveryRetry({
249
+ cfg,
250
+ accountId,
251
+ fn: async () =>
252
+ await send(target, text, {
253
+ verbose: false,
254
+ replyTo: replyToId ?? undefined,
255
+ accountId: accountId ?? undefined,
256
+ silent: silent ?? undefined,
257
+ cfg,
258
+ ...formattingOptions,
259
+ }),
260
+ });
261
+ return await withDiscordDeliveryRetry({
262
+ cfg,
263
+ accountId,
264
+ fn: async () =>
265
+ await send(target, "", {
266
+ verbose: false,
267
+ mediaUrl,
268
+ mediaAccess,
269
+ mediaLocalRoots,
270
+ mediaReadFile,
271
+ accountId: accountId ?? undefined,
272
+ silent: silent ?? undefined,
273
+ cfg,
274
+ ...formattingOptions,
275
+ }),
276
+ });
277
+ }
278
+ return await withDiscordDeliveryRetry({
279
+ cfg,
280
+ accountId,
281
+ fn: async () =>
282
+ await send(target, text, {
283
+ verbose: false,
284
+ mediaUrl,
285
+ mediaAccess,
286
+ mediaLocalRoots,
287
+ mediaReadFile,
288
+ replyTo: replyToId ?? undefined,
289
+ accountId: accountId ?? undefined,
290
+ silent: silent ?? undefined,
291
+ cfg,
292
+ ...formattingOptions,
293
+ }),
294
+ });
295
+ },
296
+ sendPoll: async ({ cfg, to, poll, accountId, threadId, silent }) =>
297
+ await withDiscordDeliveryRetry({
298
+ cfg,
299
+ accountId,
300
+ fn: async () =>
301
+ await (
302
+ await loadDiscordSendRuntime()
303
+ ).sendPollDiscord(resolveDiscordOutboundTarget({ to, threadId }), poll, {
304
+ accountId: accountId ?? undefined,
305
+ silent: silent ?? undefined,
306
+ cfg,
307
+ }),
308
+ }),
309
+ }),
310
+ afterDeliverPayload: async ({ target, payload }) => {
311
+ notifyDiscordInboundEventOutboundPayloadSuccess({
312
+ payload,
313
+ to: resolveDiscordOutboundTarget({ to: target.to, threadId: target.threadId }),
314
+ accountId: target.accountId,
315
+ });
316
+ const threadId = normalizeOptionalStringifiedId(target.threadId);
317
+ if (!threadId) {
318
+ return;
319
+ }
320
+ const { getThreadBindingManager } = await loadDiscordThreadBindings();
321
+ const manager = getThreadBindingManager(target.accountId ?? undefined);
322
+ if (!manager?.getByThreadId(threadId)) {
323
+ return;
324
+ }
325
+ manager.touchThread({ threadId });
326
+ },
327
+ };
@@ -0,0 +1,29 @@
1
+ function hasApprovalChannelData(payload: { channelData?: unknown }): boolean {
2
+ const channelData = payload.channelData;
3
+ if (!channelData || typeof channelData !== "object" || Array.isArray(channelData)) {
4
+ return false;
5
+ }
6
+ return Boolean((channelData as { execApproval?: unknown }).execApproval);
7
+ }
8
+
9
+ function neutralizeDiscordApprovalMentions(value: string): string {
10
+ return value
11
+ .replace(/@everyone/gi, "@\u200beveryone")
12
+ .replace(/@here/gi, "@\u200bhere")
13
+ .replace(/<@/g, "<@\u200b")
14
+ .replace(/<#/g, "<#\u200b");
15
+ }
16
+
17
+ export function normalizeDiscordApprovalPayload<
18
+ T extends {
19
+ text?: string;
20
+ channelData?: unknown;
21
+ },
22
+ >(payload: T): T {
23
+ return hasApprovalChannelData(payload) && payload.text
24
+ ? {
25
+ ...payload,
26
+ text: neutralizeDiscordApprovalMentions(payload.text),
27
+ }
28
+ : payload;
29
+ }
@@ -0,0 +1,86 @@
1
+ import type { ChannelOutboundAdapter } from "autobot/plugin-sdk/channel-send-result";
2
+ import { readDiscordComponentSpec, type DiscordComponentMessageSpec } from "./components.js";
3
+
4
+ type DiscordComponentSendFn = typeof import("./send.components.js").sendDiscordComponentMessage;
5
+ type DiscordSharedInteractiveModule = typeof import("./shared-interactive.js");
6
+ type OutboundPayload = Parameters<NonNullable<ChannelOutboundAdapter["sendPayload"]>>[0]["payload"];
7
+
8
+ let discordComponentSendPromise: Promise<DiscordComponentSendFn> | undefined;
9
+ let discordSharedInteractivePromise: Promise<DiscordSharedInteractiveModule> | undefined;
10
+
11
+ export async function sendDiscordComponentMessageLazy(
12
+ ...args: Parameters<DiscordComponentSendFn>
13
+ ): ReturnType<DiscordComponentSendFn> {
14
+ discordComponentSendPromise ??= import("./send.components.js").then(
15
+ (module) => module.sendDiscordComponentMessage,
16
+ );
17
+ return await (
18
+ await discordComponentSendPromise
19
+ )(...args);
20
+ }
21
+
22
+ function loadDiscordSharedInteractive(): Promise<DiscordSharedInteractiveModule> {
23
+ discordSharedInteractivePromise ??= import("./shared-interactive.js");
24
+ return discordSharedInteractivePromise;
25
+ }
26
+
27
+ function addPayloadTextFallback(
28
+ spec: DiscordComponentMessageSpec,
29
+ payload: Pick<OutboundPayload, "text">,
30
+ ): DiscordComponentMessageSpec {
31
+ return spec.text
32
+ ? spec
33
+ : {
34
+ ...spec,
35
+ text: payload.text?.trim() ? payload.text : undefined,
36
+ };
37
+ }
38
+
39
+ export async function buildDiscordPresentationPayload(params: {
40
+ payload: Parameters<NonNullable<ChannelOutboundAdapter["renderPresentation"]>>[0]["payload"];
41
+ presentation: Parameters<
42
+ NonNullable<ChannelOutboundAdapter["renderPresentation"]>
43
+ >[0]["presentation"];
44
+ }): Promise<typeof params.payload | null> {
45
+ const componentSpec = (await loadDiscordSharedInteractive()).buildDiscordPresentationComponents(
46
+ params.presentation,
47
+ );
48
+ if (!componentSpec) {
49
+ return null;
50
+ }
51
+ return {
52
+ ...params.payload,
53
+ channelData: {
54
+ ...params.payload.channelData,
55
+ discord: {
56
+ ...(params.payload.channelData?.discord as Record<string, unknown> | undefined),
57
+ presentationComponents: componentSpec,
58
+ },
59
+ },
60
+ };
61
+ }
62
+
63
+ export async function resolveDiscordComponentSpec(
64
+ payload: OutboundPayload,
65
+ ): Promise<DiscordComponentMessageSpec | undefined> {
66
+ const discordData = payload.channelData?.discord as
67
+ | { components?: unknown; presentationComponents?: DiscordComponentMessageSpec }
68
+ | undefined;
69
+ const rawComponentSpec =
70
+ discordData?.presentationComponents ??
71
+ (discordData?.components &&
72
+ typeof discordData.components === "object" &&
73
+ !Array.isArray(discordData.components)
74
+ ? readDiscordComponentSpec(discordData.components)
75
+ : null);
76
+ if (rawComponentSpec) {
77
+ return addPayloadTextFallback(rawComponentSpec, payload);
78
+ }
79
+ if (!payload.interactive) {
80
+ return undefined;
81
+ }
82
+ const interactiveSpec = (await loadDiscordSharedInteractive()).buildDiscordInteractiveComponents(
83
+ payload.interactive,
84
+ );
85
+ return interactiveSpec ? addPayloadTextFallback(interactiveSpec, payload) : undefined;
86
+ }