@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,191 @@
1
+ import type { MessageReceipt } from "autobot/plugin-sdk/channel-message";
2
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
3
+ import type { RetryConfig } from "autobot/plugin-sdk/retry-runtime";
4
+ import type { RequestClient } from "./internal/discord.js";
5
+
6
+ export class DiscordSendError extends Error {
7
+ kind?: "missing-permissions" | "dm-blocked";
8
+ channelId?: string;
9
+ missingPermissions?: string[];
10
+ discordCode?: number;
11
+ status?: number;
12
+
13
+ constructor(message: string, opts?: Partial<DiscordSendError>) {
14
+ super(message);
15
+ this.name = "DiscordSendError";
16
+ if (opts) {
17
+ Object.assign(this, opts);
18
+ }
19
+ }
20
+
21
+ override toString() {
22
+ return this.message;
23
+ }
24
+ }
25
+
26
+ export const DISCORD_MAX_EMOJI_BYTES = 256 * 1024;
27
+ export const DISCORD_MAX_STICKER_BYTES = 512 * 1024;
28
+ export const DISCORD_MAX_EVENT_COVER_BYTES = 8 * 1024 * 1024;
29
+
30
+ export type DiscordSendResult = {
31
+ messageId: string;
32
+ channelId: string;
33
+ receipt: MessageReceipt;
34
+ };
35
+
36
+ export type DiscordRuntimeAccountContext = {
37
+ cfg: AutoBotConfig;
38
+ accountId: string;
39
+ };
40
+
41
+ export type DiscordReactOpts = {
42
+ cfg: AutoBotConfig;
43
+ accountId?: string;
44
+ token?: string;
45
+ rest?: RequestClient;
46
+ verbose?: boolean;
47
+ retry?: RetryConfig;
48
+ };
49
+
50
+ export type DiscordReactionRuntimeContext = DiscordRuntimeAccountContext & {
51
+ rest: RequestClient;
52
+ };
53
+
54
+ export type DiscordReactionUser = {
55
+ id: string;
56
+ username?: string;
57
+ tag?: string;
58
+ };
59
+
60
+ export type DiscordReactionSummary = {
61
+ emoji: { id?: string | null; name?: string | null; raw: string };
62
+ count: number;
63
+ users: DiscordReactionUser[];
64
+ };
65
+
66
+ export type DiscordPermissionsSummary = {
67
+ channelId: string;
68
+ guildId?: string;
69
+ permissions: string[];
70
+ raw: string;
71
+ isDm: boolean;
72
+ channelType?: number;
73
+ };
74
+
75
+ export type DiscordMessageQuery = {
76
+ limit?: number;
77
+ before?: string;
78
+ after?: string;
79
+ around?: string;
80
+ };
81
+
82
+ export type DiscordMessageEdit = {
83
+ content?: string;
84
+ flags?: number;
85
+ };
86
+
87
+ export type DiscordThreadCreate = {
88
+ messageId?: string;
89
+ name: string;
90
+ autoArchiveMinutes?: number;
91
+ content?: string;
92
+ /** Discord thread type (default: PublicThread for standalone threads). */
93
+ type?: number;
94
+ /** Tag IDs to apply when creating a forum/media thread (Discord `applied_tags`). */
95
+ appliedTags?: string[];
96
+ };
97
+
98
+ export type DiscordThreadList = {
99
+ guildId: string;
100
+ channelId?: string;
101
+ includeArchived?: boolean;
102
+ before?: string;
103
+ limit?: number;
104
+ };
105
+
106
+ export type DiscordSearchQuery = {
107
+ guildId: string;
108
+ content: string;
109
+ channelIds?: string[];
110
+ authorIds?: string[];
111
+ limit?: number;
112
+ };
113
+
114
+ export type DiscordRoleChange = {
115
+ guildId: string;
116
+ userId: string;
117
+ roleId: string;
118
+ };
119
+
120
+ export type DiscordModerationTarget = {
121
+ guildId: string;
122
+ userId: string;
123
+ reason?: string;
124
+ };
125
+
126
+ export type DiscordTimeoutTarget = DiscordModerationTarget & {
127
+ until?: string;
128
+ durationMinutes?: number;
129
+ };
130
+
131
+ export type DiscordEmojiUpload = {
132
+ guildId: string;
133
+ name: string;
134
+ mediaUrl: string;
135
+ roleIds?: string[];
136
+ };
137
+
138
+ export type DiscordStickerUpload = {
139
+ guildId: string;
140
+ name: string;
141
+ description: string;
142
+ tags: string;
143
+ mediaUrl: string;
144
+ };
145
+
146
+ export type DiscordChannelCreate = {
147
+ guildId: string;
148
+ name: string;
149
+ type?: number;
150
+ parentId?: string;
151
+ topic?: string;
152
+ position?: number;
153
+ nsfw?: boolean;
154
+ };
155
+
156
+ type DiscordForumTag = {
157
+ id?: string;
158
+ name: string;
159
+ moderated?: boolean;
160
+ emoji_id?: string | null;
161
+ emoji_name?: string | null;
162
+ };
163
+
164
+ export type DiscordChannelEdit = {
165
+ channelId: string;
166
+ name?: string;
167
+ topic?: string;
168
+ position?: number;
169
+ parentId?: string | null;
170
+ nsfw?: boolean;
171
+ rateLimitPerUser?: number;
172
+ archived?: boolean;
173
+ locked?: boolean;
174
+ autoArchiveDuration?: number;
175
+ availableTags?: DiscordForumTag[];
176
+ };
177
+
178
+ export type DiscordChannelMove = {
179
+ guildId: string;
180
+ channelId: string;
181
+ parentId?: string | null;
182
+ position?: number;
183
+ };
184
+
185
+ export type DiscordChannelPermissionSet = {
186
+ channelId: string;
187
+ targetId: string;
188
+ targetType: 0 | 1;
189
+ allow?: string;
190
+ deny?: string;
191
+ };
@@ -0,0 +1,9 @@
1
+ import { resolveDiscordRest } from "./client.js";
2
+ import { sendChannelTyping } from "./internal/discord.js";
3
+ import type { DiscordReactOpts } from "./send.types.js";
4
+
5
+ export async function sendTypingDiscord(channelId: string, opts: DiscordReactOpts) {
6
+ const rest = resolveDiscordRest(opts);
7
+ await sendChannelTyping(rest, channelId);
8
+ return { ok: true, channelId };
9
+ }
@@ -0,0 +1,140 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { recordChannelActivity } from "autobot/plugin-sdk/channel-activity-runtime";
4
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
5
+ import {
6
+ extensionForMime,
7
+ maxBytesForKind,
8
+ unlinkIfExists,
9
+ } from "autobot/plugin-sdk/media-runtime";
10
+ import { requireRuntimeConfig } from "autobot/plugin-sdk/plugin-config-runtime";
11
+ import type { RetryConfig } from "autobot/plugin-sdk/retry-runtime";
12
+ import { tempWorkspace, resolvePreferredAutoBotTmpDir } from "autobot/plugin-sdk/temp-path";
13
+ import { loadWebMediaRaw } from "autobot/plugin-sdk/web-media";
14
+ import { resolveDiscordAccount } from "./accounts.js";
15
+ import type { RequestClient } from "./internal/discord.js";
16
+ import { parseAndResolveRecipient } from "./recipient-resolution.js";
17
+ import { createDiscordSendResult } from "./send.receipt.js";
18
+ import { buildDiscordSendError, createDiscordClient, resolveChannelId } from "./send.shared.js";
19
+ import type { DiscordSendResult } from "./send.types.js";
20
+ import {
21
+ ensureOggOpus,
22
+ getVoiceMessageMetadata,
23
+ sendDiscordVoiceMessage,
24
+ } from "./voice-message.js";
25
+
26
+ type VoiceMessageOpts = {
27
+ cfg: AutoBotConfig;
28
+ token?: string;
29
+ accountId?: string;
30
+ verbose?: boolean;
31
+ rest?: RequestClient;
32
+ replyTo?: string;
33
+ retry?: RetryConfig;
34
+ silent?: boolean;
35
+ };
36
+
37
+ function toDiscordSendResult(
38
+ result: { id?: string | null; channel_id?: string | null },
39
+ fallbackChannelId: string,
40
+ ): DiscordSendResult {
41
+ return createDiscordSendResult({
42
+ result,
43
+ fallbackChannelId,
44
+ kind: "voice",
45
+ });
46
+ }
47
+
48
+ async function materializeVoiceMessageInput(
49
+ mediaUrl: string,
50
+ ): Promise<{ filePath: string; cleanup: () => Promise<void> }> {
51
+ // Security: reuse the standard media loader so we apply SSRF guards + allowed-local-root checks.
52
+ // Then write to a private temp file so ffmpeg/ffprobe never sees the original URL/path string.
53
+ const media = await loadWebMediaRaw(mediaUrl, maxBytesForKind("audio"));
54
+ const extFromName = media.fileName ? path.extname(media.fileName) : "";
55
+ const extFromMime = media.contentType ? extensionForMime(media.contentType) : "";
56
+ const ext = extFromName || extFromMime || ".bin";
57
+ const workspace = await tempWorkspace({
58
+ rootDir: resolvePreferredAutoBotTmpDir(),
59
+ prefix: "voice-src-",
60
+ });
61
+ const filePath = await workspace.write(`input${ext}`, media.buffer);
62
+ return { filePath, cleanup: async () => await workspace.cleanup() };
63
+ }
64
+
65
+ /**
66
+ * Send a voice message to Discord.
67
+ *
68
+ * Voice messages are a special Discord feature that displays audio with a waveform
69
+ * visualization. They require OGG/Opus format and cannot include text content.
70
+ *
71
+ * @param to - Recipient (user ID for DM or channel ID)
72
+ * @param audioPath - Path to local audio file (will be converted to OGG/Opus if needed)
73
+ * @param opts - Send options
74
+ */
75
+ export async function sendVoiceMessageDiscord(
76
+ to: string,
77
+ audioPath: string,
78
+ opts: VoiceMessageOpts,
79
+ ): Promise<DiscordSendResult> {
80
+ const { filePath: localInputPath, cleanup: cleanupLocalInput } =
81
+ await materializeVoiceMessageInput(audioPath);
82
+ let oggPath: string | null = null;
83
+ let oggCleanup = false;
84
+ let token: string | undefined;
85
+ let rest: RequestClient | undefined;
86
+ let channelId: string | undefined;
87
+ const cfg = requireRuntimeConfig(opts.cfg, "Discord voice send");
88
+
89
+ try {
90
+ const accountInfo = resolveDiscordAccount({
91
+ cfg,
92
+ accountId: opts.accountId,
93
+ });
94
+ const client = createDiscordClient({ ...opts, cfg });
95
+ token = client.token;
96
+ rest = client.rest;
97
+ const request = client.request;
98
+ const recipient = await parseAndResolveRecipient(to, cfg, opts.accountId);
99
+ channelId = (await resolveChannelId(rest, recipient, request)).channelId;
100
+
101
+ const ogg = await ensureOggOpus(localInputPath);
102
+ oggPath = ogg.path;
103
+ oggCleanup = ogg.cleanup;
104
+
105
+ const metadata = await getVoiceMessageMetadata(oggPath);
106
+ const audioBuffer = await fs.readFile(oggPath);
107
+ const result = await sendDiscordVoiceMessage(
108
+ rest,
109
+ channelId,
110
+ audioBuffer,
111
+ metadata,
112
+ opts.replyTo,
113
+ request,
114
+ opts.silent,
115
+ token,
116
+ );
117
+
118
+ recordChannelActivity({
119
+ channel: "discord",
120
+ accountId: accountInfo.accountId,
121
+ direction: "outbound",
122
+ });
123
+
124
+ return toDiscordSendResult(result, channelId);
125
+ } catch (err) {
126
+ if (channelId && rest && token) {
127
+ throw await buildDiscordSendError(err, {
128
+ channelId,
129
+ cfg,
130
+ rest,
131
+ token,
132
+ hasMedia: true,
133
+ });
134
+ }
135
+ throw err;
136
+ } finally {
137
+ await unlinkIfExists(oggCleanup ? oggPath : null);
138
+ await cleanupLocalInput();
139
+ }
140
+ }
@@ -0,0 +1,137 @@
1
+ import { recordChannelActivity } from "autobot/plugin-sdk/channel-activity-runtime";
2
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
3
+ import { normalizeOptionalString } from "autobot/plugin-sdk/string-coerce-runtime";
4
+ import { resolveDiscordClientAccountContext } from "./client.js";
5
+ import {
6
+ DiscordError,
7
+ RateLimitError,
8
+ readDiscordCode,
9
+ readDiscordMessage,
10
+ readRetryAfter,
11
+ } from "./internal/rest-errors.js";
12
+ import { rewriteDiscordKnownMentions } from "./mentions.js";
13
+ import { createDiscordSendResult } from "./send.receipt.js";
14
+ import type { DiscordSendResult } from "./send.types.js";
15
+
16
+ type DiscordWebhookSendOpts = {
17
+ cfg: AutoBotConfig;
18
+ webhookId: string;
19
+ webhookToken: string;
20
+ accountId?: string;
21
+ threadId?: string | number;
22
+ replyTo?: string;
23
+ username?: string;
24
+ avatarUrl?: string;
25
+ wait?: boolean;
26
+ };
27
+
28
+ function resolveWebhookExecutionUrl(params: {
29
+ webhookId: string;
30
+ webhookToken: string;
31
+ threadId?: string | number;
32
+ wait?: boolean;
33
+ }) {
34
+ const baseUrl = new URL(
35
+ `https://discord.com/api/v10/webhooks/${encodeURIComponent(params.webhookId)}/${encodeURIComponent(params.webhookToken)}`,
36
+ );
37
+ baseUrl.searchParams.set("wait", params.wait === false ? "false" : "true");
38
+ if (params.threadId !== undefined && params.threadId !== null && params.threadId !== "") {
39
+ baseUrl.searchParams.set("thread_id", String(params.threadId));
40
+ }
41
+ return baseUrl.toString();
42
+ }
43
+
44
+ function coerceWebhookErrorBody(raw: string): unknown {
45
+ if (!raw) {
46
+ return undefined;
47
+ }
48
+ try {
49
+ return JSON.parse(raw);
50
+ } catch {
51
+ return { message: raw.slice(0, 200) };
52
+ }
53
+ }
54
+
55
+ async function throwWebhookResponseError(response: Response): Promise<never> {
56
+ const raw = await response.text().catch(() => "");
57
+ const parsed = coerceWebhookErrorBody(raw);
58
+ if (response.status === 429) {
59
+ throw new RateLimitError(response, {
60
+ message: readDiscordMessage(parsed, "Rate limited"),
61
+ retry_after: readRetryAfter(parsed, response, 1),
62
+ code: readDiscordCode(parsed),
63
+ global:
64
+ parsed && typeof parsed === "object" && "global" in parsed
65
+ ? Boolean((parsed as { global?: unknown }).global)
66
+ : false,
67
+ });
68
+ }
69
+ throw new DiscordError(response, parsed);
70
+ }
71
+
72
+ export async function sendWebhookMessageDiscord(
73
+ text: string,
74
+ opts: DiscordWebhookSendOpts,
75
+ ): Promise<DiscordSendResult> {
76
+ const webhookId = normalizeOptionalString(opts.webhookId) ?? "";
77
+ const webhookToken = normalizeOptionalString(opts.webhookToken) ?? "";
78
+ if (!webhookId || !webhookToken) {
79
+ throw new Error("Discord webhook id/token are required");
80
+ }
81
+
82
+ const replyTo = normalizeOptionalString(opts.replyTo) ?? "";
83
+ const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
84
+ const { account, proxyFetch } = resolveDiscordClientAccountContext({
85
+ cfg: opts.cfg,
86
+ accountId: opts.accountId,
87
+ });
88
+ const rewrittenText = rewriteDiscordKnownMentions(text, {
89
+ accountId: account.accountId,
90
+ mentionAliases: account.config.mentionAliases,
91
+ });
92
+
93
+ const response = await (proxyFetch ?? fetch)(
94
+ resolveWebhookExecutionUrl({
95
+ webhookId,
96
+ webhookToken,
97
+ threadId: opts.threadId,
98
+ wait: opts.wait,
99
+ }),
100
+ {
101
+ method: "POST",
102
+ headers: {
103
+ "content-type": "application/json",
104
+ },
105
+ body: JSON.stringify({
106
+ content: rewrittenText,
107
+ username: normalizeOptionalString(opts.username),
108
+ avatar_url: normalizeOptionalString(opts.avatarUrl),
109
+ ...(messageReference ? { message_reference: messageReference } : {}),
110
+ }),
111
+ },
112
+ );
113
+ if (!response.ok) {
114
+ await throwWebhookResponseError(response);
115
+ }
116
+
117
+ const payload = (await response.json().catch(() => ({}))) as {
118
+ id?: string;
119
+ channel_id?: string;
120
+ };
121
+ try {
122
+ recordChannelActivity({
123
+ channel: "discord",
124
+ accountId: account.accountId,
125
+ direction: "outbound",
126
+ });
127
+ } catch {
128
+ // Best-effort telemetry only.
129
+ }
130
+ return createDiscordSendResult({
131
+ result: payload,
132
+ fallbackChannelId: opts.threadId ? String(opts.threadId) : "",
133
+ kind: "text",
134
+ ...(opts.threadId != null ? { threadId: opts.threadId } : {}),
135
+ ...(replyTo ? { replyToId: replyTo } : {}),
136
+ });
137
+ }
@@ -0,0 +1,3 @@
1
+ export function deriveLegacySessionChatType(sessionKey: string): "channel" | undefined {
2
+ return /^discord:(?:[^:]+:)?guild-[^:]+:channel-[^:]+$/.test(sessionKey) ? "channel" : undefined;
3
+ }
@@ -0,0 +1,47 @@
1
+ import { normalizeLowercaseStringOrEmpty } from "autobot/plugin-sdk/string-coerce-runtime";
2
+
3
+ type DiscordSessionKeyContext = {
4
+ ChatType?: string;
5
+ From?: string;
6
+ SenderId?: string;
7
+ };
8
+
9
+ function normalizeDiscordChatType(raw?: string): "direct" | "group" | "channel" | undefined {
10
+ const normalized = normalizeLowercaseStringOrEmpty(raw);
11
+ if (!normalized) {
12
+ return undefined;
13
+ }
14
+ if (normalized === "dm") {
15
+ return "direct";
16
+ }
17
+ if (normalized === "group" || normalized === "channel" || normalized === "direct") {
18
+ return normalized;
19
+ }
20
+ return undefined;
21
+ }
22
+
23
+ export function normalizeExplicitDiscordSessionKey(
24
+ sessionKey: string,
25
+ ctx: DiscordSessionKeyContext,
26
+ ): string {
27
+ let normalized = normalizeLowercaseStringOrEmpty(sessionKey);
28
+ if (normalizeDiscordChatType(ctx.ChatType) !== "direct") {
29
+ return normalized;
30
+ }
31
+
32
+ normalized = normalized.replace(/^(discord:)dm:/, "$1direct:");
33
+ normalized = normalized.replace(/^(agent:[^:]+:discord:)dm:/, "$1direct:");
34
+ const match = normalized.match(/^((?:agent:[^:]+:)?)discord:channel:([^:]+)$/);
35
+ if (!match) {
36
+ return normalized;
37
+ }
38
+
39
+ const from = normalizeLowercaseStringOrEmpty(ctx.From);
40
+ const senderId = normalizeLowercaseStringOrEmpty(ctx.SenderId);
41
+ const fromDiscordId =
42
+ from.startsWith("discord:") && !from.includes(":channel:") && !from.includes(":group:")
43
+ ? from.slice("discord:".length)
44
+ : "";
45
+ const directId = senderId || fromDiscordId;
46
+ return directId && directId === match[2] ? `${match[1]}discord:direct:${match[2]}` : normalized;
47
+ }
@@ -0,0 +1,144 @@
1
+ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "autobot/plugin-sdk/account-id";
2
+ import { listCombinedAccountIds } from "autobot/plugin-sdk/account-resolution";
3
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
4
+ import {
5
+ hasConfiguredSecretInput,
6
+ normalizeSecretInputString,
7
+ } from "autobot/plugin-sdk/secret-input";
8
+ import { resolveDefaultDiscordAccountId } from "./accounts.js";
9
+ import { mergeDiscordAccountConfig, resolveDiscordAccountConfig } from "./accounts.js";
10
+ import type { DiscordAccountConfig } from "./runtime-api.js";
11
+ import { resolveDiscordToken } from "./token.js";
12
+
13
+ type InspectedDiscordSetupAccount = {
14
+ accountId: string;
15
+ enabled: boolean;
16
+ token: string;
17
+ tokenSource: "env" | "config" | "none";
18
+ tokenStatus: "available" | "configured_unavailable" | "missing";
19
+ configured: boolean;
20
+ config: DiscordAccountConfig;
21
+ };
22
+
23
+ function inspectConfiguredToken(value: unknown): {
24
+ token: string;
25
+ tokenSource: "config";
26
+ tokenStatus: "available" | "configured_unavailable";
27
+ } | null {
28
+ const normalized = normalizeSecretInputString(value);
29
+ if (normalized) {
30
+ return {
31
+ token: normalized.replace(/^Bot\s+/i, ""),
32
+ tokenSource: "config",
33
+ tokenStatus: "available",
34
+ };
35
+ }
36
+ if (hasConfiguredSecretInput(value)) {
37
+ return {
38
+ token: "",
39
+ tokenSource: "config",
40
+ tokenStatus: "configured_unavailable",
41
+ };
42
+ }
43
+ return null;
44
+ }
45
+
46
+ export function listDiscordSetupAccountIds(cfg: AutoBotConfig): string[] {
47
+ const accounts = cfg.channels?.discord?.accounts;
48
+ return listCombinedAccountIds({
49
+ configuredAccountIds:
50
+ accounts && typeof accounts === "object" && !Array.isArray(accounts)
51
+ ? Object.keys(accounts).map((accountId) => normalizeAccountId(accountId))
52
+ : [],
53
+ implicitAccountId: DEFAULT_ACCOUNT_ID,
54
+ });
55
+ }
56
+
57
+ export function resolveDefaultDiscordSetupAccountId(cfg: AutoBotConfig): string {
58
+ return resolveDefaultDiscordAccountId(cfg);
59
+ }
60
+
61
+ export function resolveDiscordSetupAccountConfig(params: {
62
+ cfg: AutoBotConfig;
63
+ accountId?: string | null;
64
+ }): { accountId: string; config: DiscordAccountConfig } {
65
+ const accountId = normalizeAccountId(
66
+ params.accountId ?? resolveDefaultDiscordSetupAccountId(params.cfg),
67
+ );
68
+ return {
69
+ accountId,
70
+ config: mergeDiscordAccountConfig(params.cfg, accountId),
71
+ };
72
+ }
73
+
74
+ export function inspectDiscordSetupAccount(params: {
75
+ cfg: AutoBotConfig;
76
+ accountId?: string | null;
77
+ }): InspectedDiscordSetupAccount {
78
+ const { accountId, config } = resolveDiscordSetupAccountConfig(params);
79
+ const enabled = params.cfg.channels?.discord?.enabled !== false && config.enabled !== false;
80
+ const accountConfig = resolveDiscordAccountConfig(params.cfg, accountId);
81
+ const hasAccountToken = Boolean(
82
+ accountConfig &&
83
+ Object.prototype.hasOwnProperty.call(accountConfig as Record<string, unknown>, "token"),
84
+ );
85
+ const accountToken = inspectConfiguredToken(accountConfig?.token);
86
+ if (accountToken) {
87
+ return {
88
+ accountId,
89
+ enabled,
90
+ token: accountToken.token,
91
+ tokenSource: accountToken.tokenSource,
92
+ tokenStatus: accountToken.tokenStatus,
93
+ configured: true,
94
+ config,
95
+ };
96
+ }
97
+ if (hasAccountToken) {
98
+ return {
99
+ accountId,
100
+ enabled,
101
+ token: "",
102
+ tokenSource: "none",
103
+ tokenStatus: "missing",
104
+ configured: false,
105
+ config,
106
+ };
107
+ }
108
+
109
+ const channelToken = inspectConfiguredToken(params.cfg.channels?.discord?.token);
110
+ if (channelToken) {
111
+ return {
112
+ accountId,
113
+ enabled,
114
+ token: channelToken.token,
115
+ tokenSource: channelToken.tokenSource,
116
+ tokenStatus: channelToken.tokenStatus,
117
+ configured: true,
118
+ config,
119
+ };
120
+ }
121
+
122
+ const tokenResolution = resolveDiscordToken(params.cfg, { accountId });
123
+ if (tokenResolution.token) {
124
+ return {
125
+ accountId,
126
+ enabled,
127
+ token: tokenResolution.token,
128
+ tokenSource: tokenResolution.source,
129
+ tokenStatus: "available",
130
+ configured: true,
131
+ config,
132
+ };
133
+ }
134
+
135
+ return {
136
+ accountId,
137
+ enabled,
138
+ token: "",
139
+ tokenSource: "none",
140
+ tokenStatus: "missing",
141
+ configured: false,
142
+ config,
143
+ };
144
+ }
@@ -0,0 +1,14 @@
1
+ import {
2
+ createEnvPatchedAccountSetupAdapter,
3
+ type ChannelSetupAdapter,
4
+ } from "autobot/plugin-sdk/setup-runtime";
5
+
6
+ const channel = "discord" as const;
7
+
8
+ export const discordSetupAdapter: ChannelSetupAdapter = createEnvPatchedAccountSetupAdapter({
9
+ channelKey: channel,
10
+ defaultAccountOnlyEnvError: "DISCORD_BOT_TOKEN can only be used for the default account.",
11
+ missingCredentialError: "Discord requires token (or --use-env).",
12
+ hasCredentials: (input) => Boolean(input.token),
13
+ buildPatch: (input) => (input.token ? { token: input.token } : {}),
14
+ });