@openclaw/discord 2026.3.13 → 2026.5.2-beta.1

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 (502) hide show
  1. package/account-inspect-api.ts +6 -0
  2. package/action-runtime-api.ts +1 -0
  3. package/api.ts +132 -0
  4. package/channel-config-api.ts +1 -0
  5. package/channel-plugin-api.ts +3 -0
  6. package/config-api.ts +4 -0
  7. package/configured-state.ts +6 -0
  8. package/contract-api.ts +21 -0
  9. package/directory-contract-api.ts +4 -0
  10. package/doctor-contract-api.ts +1 -0
  11. package/index.test.ts +13 -0
  12. package/index.ts +18 -13
  13. package/openclaw.plugin.json +3326 -1
  14. package/package.json +68 -2
  15. package/runtime-api.actions.ts +15 -0
  16. package/runtime-api.lookup.ts +22 -0
  17. package/runtime-api.monitor.ts +50 -0
  18. package/runtime-api.send.ts +79 -0
  19. package/runtime-api.threads.ts +30 -0
  20. package/runtime-api.ts +180 -0
  21. package/runtime-setter-api.ts +3 -0
  22. package/secret-contract-api.ts +4 -0
  23. package/security-audit-contract-api.ts +1 -0
  24. package/security-contract-api.ts +4 -0
  25. package/session-key-api.ts +1 -0
  26. package/setup-entry.ts +9 -0
  27. package/setup-plugin-api.ts +3 -0
  28. package/src/account-inspect.test.ts +126 -0
  29. package/src/account-inspect.ts +132 -0
  30. package/src/accounts.test.ts +247 -0
  31. package/src/accounts.ts +196 -0
  32. package/src/actions/handle-action.guild-admin.ts +411 -0
  33. package/src/actions/handle-action.test.ts +306 -0
  34. package/src/actions/handle-action.ts +372 -0
  35. package/src/actions/runtime.guild.ts +446 -0
  36. package/src/actions/runtime.messaging.messages.ts +205 -0
  37. package/src/actions/runtime.messaging.reactions.ts +67 -0
  38. package/src/actions/runtime.messaging.runtime.ts +69 -0
  39. package/src/actions/runtime.messaging.send.ts +248 -0
  40. package/src/actions/runtime.messaging.shared.ts +97 -0
  41. package/src/actions/runtime.messaging.ts +37 -0
  42. package/src/actions/runtime.moderation-shared.ts +48 -0
  43. package/src/actions/runtime.moderation.authz.test.ts +151 -0
  44. package/src/actions/runtime.moderation.ts +116 -0
  45. package/src/actions/runtime.presence.test.ts +160 -0
  46. package/src/actions/runtime.presence.ts +117 -0
  47. package/src/actions/runtime.shared.ts +83 -0
  48. package/src/actions/runtime.test.ts +1087 -0
  49. package/src/actions/runtime.ts +87 -0
  50. package/src/api-barrel.test.ts +80 -0
  51. package/src/api.test.ts +130 -0
  52. package/src/api.ts +169 -0
  53. package/src/approval-handler.runtime.test.ts +41 -0
  54. package/src/approval-handler.runtime.ts +632 -0
  55. package/src/approval-native.test.ts +330 -0
  56. package/src/approval-native.ts +219 -0
  57. package/src/approval-runtime.ts +14 -0
  58. package/src/approval-shared.ts +53 -0
  59. package/src/audit-core.ts +141 -0
  60. package/src/audit.test.ts +145 -0
  61. package/src/audit.ts +32 -0
  62. package/src/channel-actions.contract.test.ts +45 -0
  63. package/src/channel-actions.runtime.ts +1 -0
  64. package/src/channel-actions.test.ts +275 -0
  65. package/src/channel-actions.ts +203 -0
  66. package/src/channel-api.ts +29 -0
  67. package/src/channel.conversation.ts +159 -0
  68. package/src/channel.loaders.ts +47 -0
  69. package/src/channel.runtime.ts +1 -0
  70. package/src/channel.setup.ts +12 -0
  71. package/src/channel.test.ts +547 -12
  72. package/src/channel.ts +597 -430
  73. package/src/chunk.test.ts +157 -0
  74. package/src/chunk.ts +321 -0
  75. package/src/client.proxy.test.ts +176 -0
  76. package/src/client.test.ts +76 -0
  77. package/src/client.ts +132 -0
  78. package/src/component-custom-id.ts +72 -0
  79. package/src/components-registry.ts +356 -0
  80. package/src/components.builders.ts +409 -0
  81. package/src/components.modal.ts +124 -0
  82. package/src/components.parse.ts +407 -0
  83. package/src/components.test.ts +312 -0
  84. package/src/components.ts +54 -0
  85. package/src/components.types.ts +187 -0
  86. package/src/config-schema.test.ts +325 -0
  87. package/src/config-schema.ts +6 -0
  88. package/src/config-ui-hints.ts +249 -0
  89. package/src/conversation-identity.ts +58 -0
  90. package/src/delivery-retry.ts +56 -0
  91. package/src/directory-cache.ts +116 -0
  92. package/src/directory-config.ts +58 -0
  93. package/src/directory-contract.test.ts +129 -0
  94. package/src/directory-live.test.ts +126 -0
  95. package/src/directory-live.ts +135 -0
  96. package/src/doctor-contract.ts +477 -0
  97. package/src/doctor-shared.ts +5 -0
  98. package/src/doctor.test.ts +405 -0
  99. package/src/doctor.ts +340 -0
  100. package/src/draft-chunking.test.ts +64 -0
  101. package/src/draft-chunking.ts +43 -0
  102. package/src/draft-stream.test.ts +159 -0
  103. package/src/draft-stream.ts +154 -0
  104. package/src/error-body.ts +38 -0
  105. package/src/exec-approvals.test.ts +88 -0
  106. package/src/exec-approvals.ts +110 -0
  107. package/src/gateway-logging.test.ts +98 -0
  108. package/src/gateway-logging.ts +67 -0
  109. package/src/group-policy.ts +113 -0
  110. package/src/guilds.ts +29 -0
  111. package/src/inbound-context.contract.test.ts +11 -0
  112. package/src/interactive-dispatch.ts +104 -0
  113. package/src/internal/api.commands.ts +51 -0
  114. package/src/internal/api.guild.ts +164 -0
  115. package/src/internal/api.interactions.ts +53 -0
  116. package/src/internal/api.messages.ts +113 -0
  117. package/src/internal/api.reactions.ts +38 -0
  118. package/src/internal/api.test.ts +262 -0
  119. package/src/internal/api.ts +61 -0
  120. package/src/internal/api.users.ts +19 -0
  121. package/src/internal/api.webhooks.ts +13 -0
  122. package/src/internal/client.test.ts +408 -0
  123. package/src/internal/client.ts +308 -0
  124. package/src/internal/command-deploy.ts +237 -0
  125. package/src/internal/commands.ts +188 -0
  126. package/src/internal/components.base.ts +65 -0
  127. package/src/internal/components.message.ts +279 -0
  128. package/src/internal/components.modal.ts +95 -0
  129. package/src/internal/components.ts +31 -0
  130. package/src/internal/discord.ts +11 -0
  131. package/src/internal/embeds.ts +35 -0
  132. package/src/internal/entity-cache.ts +98 -0
  133. package/src/internal/event-queue.ts +162 -0
  134. package/src/internal/gateway-close-codes.ts +25 -0
  135. package/src/internal/gateway-dispatch.ts +96 -0
  136. package/src/internal/gateway-identify-limiter.ts +26 -0
  137. package/src/internal/gateway-lifecycle.ts +61 -0
  138. package/src/internal/gateway-rate-limit.ts +104 -0
  139. package/src/internal/gateway.test.ts +603 -0
  140. package/src/internal/gateway.ts +476 -0
  141. package/src/internal/interaction-dispatch.test.ts +148 -0
  142. package/src/internal/interaction-dispatch.ts +162 -0
  143. package/src/internal/interaction-options.ts +98 -0
  144. package/src/internal/interaction-response.ts +53 -0
  145. package/src/internal/interactions.test.ts +325 -0
  146. package/src/internal/interactions.ts +378 -0
  147. package/src/internal/listeners.ts +85 -0
  148. package/src/internal/live-smoke.live.test.ts +26 -0
  149. package/src/internal/modal-fields.ts +95 -0
  150. package/src/internal/payload.ts +69 -0
  151. package/src/internal/rest-body.ts +115 -0
  152. package/src/internal/rest-errors.ts +88 -0
  153. package/src/internal/rest-routes.ts +50 -0
  154. package/src/internal/rest-scheduler.ts +557 -0
  155. package/src/internal/rest.test.ts +673 -0
  156. package/src/internal/rest.ts +322 -0
  157. package/src/internal/schemas.ts +36 -0
  158. package/src/internal/structures.test.ts +43 -0
  159. package/src/internal/structures.ts +280 -0
  160. package/src/internal/test-builders.test-support.ts +163 -0
  161. package/src/internal/voice.ts +49 -0
  162. package/src/media-detection.ts +28 -0
  163. package/src/mentions.test.ts +111 -0
  164. package/src/mentions.ts +147 -0
  165. package/src/monitor/access-groups.ts +55 -0
  166. package/src/monitor/ack-reactions.ts +70 -0
  167. package/src/monitor/acp-bind-here.integration.test.ts +211 -0
  168. package/src/monitor/agent-components-auth.ts +7 -0
  169. package/src/monitor/agent-components-context.ts +154 -0
  170. package/src/monitor/agent-components-data.ts +224 -0
  171. package/src/monitor/agent-components-dm-auth.ts +221 -0
  172. package/src/monitor/agent-components-guild-auth.ts +322 -0
  173. package/src/monitor/agent-components-helpers.runtime.ts +5 -0
  174. package/src/monitor/agent-components-helpers.ts +34 -0
  175. package/src/monitor/agent-components-reply.ts +10 -0
  176. package/src/monitor/agent-components.deps.runtime.ts +2 -0
  177. package/src/monitor/agent-components.dispatch.ts +366 -0
  178. package/src/monitor/agent-components.handlers.ts +303 -0
  179. package/src/monitor/agent-components.modal.ts +160 -0
  180. package/src/monitor/agent-components.plugin-interactive.ts +187 -0
  181. package/src/monitor/agent-components.runtime.ts +14 -0
  182. package/src/monitor/agent-components.system-controls.ts +211 -0
  183. package/src/monitor/agent-components.ts +70 -0
  184. package/src/monitor/agent-components.types.ts +58 -0
  185. package/src/monitor/agent-components.wildcard-controls.ts +168 -0
  186. package/src/monitor/agent-components.wildcard.test.ts +71 -0
  187. package/src/monitor/allow-list.test.ts +14 -0
  188. package/src/monitor/allow-list.ts +633 -0
  189. package/src/monitor/auto-presence.test.ts +156 -0
  190. package/src/monitor/auto-presence.ts +356 -0
  191. package/src/monitor/channel-access.test.ts +99 -0
  192. package/src/monitor/channel-access.ts +102 -0
  193. package/src/monitor/commands.test.ts +24 -0
  194. package/src/monitor/commands.ts +9 -0
  195. package/src/monitor/dm-command-auth.test.ts +197 -0
  196. package/src/monitor/dm-command-auth.ts +158 -0
  197. package/src/monitor/dm-command-decision.test.ts +113 -0
  198. package/src/monitor/dm-command-decision.ts +49 -0
  199. package/src/monitor/exec-approvals.test.ts +226 -0
  200. package/src/monitor/exec-approvals.ts +158 -0
  201. package/src/monitor/format.ts +45 -0
  202. package/src/monitor/gateway-handle.ts +34 -0
  203. package/src/monitor/gateway-metadata.test.ts +29 -0
  204. package/src/monitor/gateway-metadata.ts +298 -0
  205. package/src/monitor/gateway-plugin.test.ts +297 -0
  206. package/src/monitor/gateway-plugin.ts +294 -0
  207. package/src/monitor/gateway-registry.ts +37 -0
  208. package/src/monitor/gateway-supervisor.test.ts +150 -0
  209. package/src/monitor/gateway-supervisor.ts +206 -0
  210. package/src/monitor/inbound-context.test-helpers.ts +37 -0
  211. package/src/monitor/inbound-context.test.ts +106 -0
  212. package/src/monitor/inbound-context.ts +103 -0
  213. package/src/monitor/inbound-dedupe.ts +79 -0
  214. package/src/monitor/inbound-job.test.ts +203 -0
  215. package/src/monitor/inbound-job.ts +118 -0
  216. package/src/monitor/listeners.queue.ts +91 -0
  217. package/src/monitor/listeners.reactions.ts +610 -0
  218. package/src/monitor/listeners.test.ts +200 -0
  219. package/src/monitor/listeners.ts +150 -0
  220. package/src/monitor/message-channel-info.ts +96 -0
  221. package/src/monitor/message-forwarded.ts +107 -0
  222. package/src/monitor/message-handler.batch-gate.test.ts +22 -0
  223. package/src/monitor/message-handler.batch-gate.ts +19 -0
  224. package/src/monitor/message-handler.bot-self-filter.test.ts +68 -0
  225. package/src/monitor/message-handler.context.ts +406 -0
  226. package/src/monitor/message-handler.dm-preflight.ts +123 -0
  227. package/src/monitor/message-handler.draft-preview.ts +246 -0
  228. package/src/monitor/message-handler.hydration.test.ts +80 -0
  229. package/src/monitor/message-handler.hydration.ts +198 -0
  230. package/src/monitor/message-handler.inbound-context.test.ts +59 -0
  231. package/src/monitor/message-handler.module-test-helpers.ts +31 -0
  232. package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
  233. package/src/monitor/message-handler.preflight-channel-context.test.ts +18 -0
  234. package/src/monitor/message-handler.preflight-channel-context.ts +58 -0
  235. package/src/monitor/message-handler.preflight-context.ts +54 -0
  236. package/src/monitor/message-handler.preflight-helpers.ts +164 -0
  237. package/src/monitor/message-handler.preflight-history.ts +23 -0
  238. package/src/monitor/message-handler.preflight-logging.ts +36 -0
  239. package/src/monitor/message-handler.preflight-pluralkit.ts +26 -0
  240. package/src/monitor/message-handler.preflight-runtime.ts +28 -0
  241. package/src/monitor/message-handler.preflight-thread.ts +49 -0
  242. package/src/monitor/message-handler.preflight.acp-bindings.test.ts +369 -0
  243. package/src/monitor/message-handler.preflight.test-helpers.ts +111 -0
  244. package/src/monitor/message-handler.preflight.test.ts +1623 -0
  245. package/src/monitor/message-handler.preflight.ts +679 -0
  246. package/src/monitor/message-handler.preflight.types.ts +110 -0
  247. package/src/monitor/message-handler.process.test.ts +1369 -0
  248. package/src/monitor/message-handler.process.ts +686 -0
  249. package/src/monitor/message-handler.queue.test.ts +496 -0
  250. package/src/monitor/message-handler.routing-preflight.ts +112 -0
  251. package/src/monitor/message-handler.test-harness.ts +99 -0
  252. package/src/monitor/message-handler.test-helpers.ts +75 -0
  253. package/src/monitor/message-handler.ts +274 -0
  254. package/src/monitor/message-media.ts +509 -0
  255. package/src/monitor/message-run-queue.ts +101 -0
  256. package/src/monitor/message-text.ts +171 -0
  257. package/src/monitor/message-utils.test.ts +1157 -0
  258. package/src/monitor/message-utils.ts +32 -0
  259. package/src/monitor/model-picker-preferences.test.ts +67 -0
  260. package/src/monitor/model-picker-preferences.ts +184 -0
  261. package/src/monitor/model-picker.state.ts +364 -0
  262. package/src/monitor/model-picker.test-utils.ts +26 -0
  263. package/src/monitor/model-picker.test.ts +794 -0
  264. package/src/monitor/model-picker.ts +38 -0
  265. package/src/monitor/model-picker.view.ts +695 -0
  266. package/src/monitor/monitor.agent-components.test.ts +375 -0
  267. package/src/monitor/monitor.test.ts +849 -0
  268. package/src/monitor/monitor.threading-utils.test.ts +598 -0
  269. package/src/monitor/native-command-agent-reply.ts +125 -0
  270. package/src/monitor/native-command-arg-ui.ts +233 -0
  271. package/src/monitor/native-command-auth.ts +308 -0
  272. package/src/monitor/native-command-bypass.ts +13 -0
  273. package/src/monitor/native-command-context.test.ts +98 -0
  274. package/src/monitor/native-command-context.ts +103 -0
  275. package/src/monitor/native-command-dispatch.ts +35 -0
  276. package/src/monitor/native-command-model-picker-apply.ts +177 -0
  277. package/src/monitor/native-command-model-picker-interaction.ts +461 -0
  278. package/src/monitor/native-command-model-picker-ui.ts +368 -0
  279. package/src/monitor/native-command-reply.test.ts +68 -0
  280. package/src/monitor/native-command-reply.ts +185 -0
  281. package/src/monitor/native-command-route.ts +91 -0
  282. package/src/monitor/native-command-status.ts +76 -0
  283. package/src/monitor/native-command-ui.ts +26 -0
  284. package/src/monitor/native-command-ui.types.ts +20 -0
  285. package/src/monitor/native-command.args.ts +45 -0
  286. package/src/monitor/native-command.command-arg.test.ts +99 -0
  287. package/src/monitor/native-command.commands-allowfrom.test.ts +490 -0
  288. package/src/monitor/native-command.model-picker.test.ts +767 -0
  289. package/src/monitor/native-command.options.test.ts +369 -0
  290. package/src/monitor/native-command.options.ts +153 -0
  291. package/src/monitor/native-command.plugin-dispatch.test.ts +961 -0
  292. package/src/monitor/native-command.runtime.ts +50 -0
  293. package/src/monitor/native-command.status-direct.test.ts +272 -0
  294. package/src/monitor/native-command.test-helpers.ts +64 -0
  295. package/src/monitor/native-command.think-autocomplete.test.ts +416 -0
  296. package/src/monitor/native-command.ts +700 -0
  297. package/src/monitor/native-command.types.ts +9 -0
  298. package/src/monitor/native-interaction-channel-context.ts +50 -0
  299. package/src/monitor/preflight-audio.runtime.ts +9 -0
  300. package/src/monitor/preflight-audio.test.ts +157 -0
  301. package/src/monitor/preflight-audio.ts +130 -0
  302. package/src/monitor/presence-cache.ts +61 -0
  303. package/src/monitor/presence.test.ts +44 -0
  304. package/src/monitor/presence.ts +50 -0
  305. package/src/monitor/provider-session.runtime.ts +12 -0
  306. package/src/monitor/provider.acp.ts +89 -0
  307. package/src/monitor/provider.allowlist.test.ts +149 -0
  308. package/src/monitor/provider.allowlist.ts +394 -0
  309. package/src/monitor/provider.cleanup.ts +41 -0
  310. package/src/monitor/provider.commands.ts +129 -0
  311. package/src/monitor/provider.config-log.ts +45 -0
  312. package/src/monitor/provider.deploy-errors.ts +362 -0
  313. package/src/monitor/provider.deploy.ts +221 -0
  314. package/src/monitor/provider.interactions.ts +160 -0
  315. package/src/monitor/provider.lifecycle.test.ts +713 -0
  316. package/src/monitor/provider.lifecycle.ts +552 -0
  317. package/src/monitor/provider.proxy.test.ts +745 -0
  318. package/src/monitor/provider.rest-proxy.test.ts +121 -0
  319. package/src/monitor/provider.runtime.ts +1 -0
  320. package/src/monitor/provider.skill-dedupe.test.ts +42 -0
  321. package/src/monitor/provider.startup-log.ts +32 -0
  322. package/src/monitor/provider.startup.test.ts +426 -0
  323. package/src/monitor/provider.startup.ts +323 -0
  324. package/src/monitor/provider.test.ts +1111 -0
  325. package/src/monitor/provider.ts +713 -0
  326. package/src/monitor/reply-context.ts +64 -0
  327. package/src/monitor/reply-delivery.test.ts +244 -0
  328. package/src/monitor/reply-delivery.ts +203 -0
  329. package/src/monitor/rest-fetch.ts +43 -0
  330. package/src/monitor/route-resolution.test.ts +204 -0
  331. package/src/monitor/route-resolution.ts +140 -0
  332. package/src/monitor/sender-identity.ts +81 -0
  333. package/src/monitor/startup-status.test.ts +30 -0
  334. package/src/monitor/startup-status.ts +10 -0
  335. package/src/monitor/status.ts +22 -0
  336. package/src/monitor/system-events.ts +55 -0
  337. package/src/monitor/thread-bindings.config.ts +35 -0
  338. package/src/monitor/thread-bindings.discord-api.test.ts +229 -0
  339. package/src/monitor/thread-bindings.discord-api.ts +310 -0
  340. package/src/monitor/thread-bindings.lifecycle.test.ts +1871 -0
  341. package/src/monitor/thread-bindings.lifecycle.ts +354 -0
  342. package/src/monitor/thread-bindings.manager.ts +553 -0
  343. package/src/monitor/thread-bindings.messages.ts +6 -0
  344. package/src/monitor/thread-bindings.persona.test.ts +34 -0
  345. package/src/monitor/thread-bindings.persona.ts +25 -0
  346. package/src/monitor/thread-bindings.session-adapter.ts +229 -0
  347. package/src/monitor/thread-bindings.session-shared.ts +59 -0
  348. package/src/monitor/thread-bindings.session-updates.ts +35 -0
  349. package/src/monitor/thread-bindings.shared-state.test.ts +36 -0
  350. package/src/monitor/thread-bindings.state.ts +540 -0
  351. package/src/monitor/thread-bindings.ts +48 -0
  352. package/src/monitor/thread-bindings.types.ts +83 -0
  353. package/src/monitor/thread-channel-context.ts +112 -0
  354. package/src/monitor/thread-session-close.test.ts +180 -0
  355. package/src/monitor/thread-session-close.ts +63 -0
  356. package/src/monitor/thread-title.generate.test.ts +197 -0
  357. package/src/monitor/thread-title.test.ts +31 -0
  358. package/src/monitor/thread-title.ts +181 -0
  359. package/src/monitor/threading.auto-thread.test.ts +327 -0
  360. package/src/monitor/threading.auto-thread.ts +287 -0
  361. package/src/monitor/threading.cache.ts +45 -0
  362. package/src/monitor/threading.parent-info.test.ts +156 -0
  363. package/src/monitor/threading.starter.test.ts +260 -0
  364. package/src/monitor/threading.starter.ts +287 -0
  365. package/src/monitor/threading.ts +20 -0
  366. package/src/monitor/threading.types.ts +102 -0
  367. package/src/monitor/timeouts.ts +84 -0
  368. package/src/monitor/typing.test.ts +42 -0
  369. package/src/monitor/typing.ts +17 -0
  370. package/src/monitor.gateway.test.ts +187 -0
  371. package/src/monitor.gateway.ts +75 -0
  372. package/src/monitor.test.ts +1397 -0
  373. package/src/monitor.ts +28 -0
  374. package/src/normalize.test.ts +56 -0
  375. package/src/normalize.ts +86 -0
  376. package/src/outbound-adapter.interactive-order.test.ts +64 -0
  377. package/src/outbound-adapter.test-harness.ts +207 -0
  378. package/src/outbound-adapter.test.ts +696 -0
  379. package/src/outbound-adapter.ts +291 -0
  380. package/src/outbound-approval.ts +29 -0
  381. package/src/outbound-components.ts +81 -0
  382. package/src/outbound-payload.contract.test.ts +38 -0
  383. package/src/outbound-payload.ts +134 -0
  384. package/src/outbound-send-context.ts +92 -0
  385. package/src/outbound-session-route.test.ts +34 -0
  386. package/src/outbound-session-route.ts +72 -0
  387. package/src/pluralkit.test.ts +67 -0
  388. package/src/pluralkit.ts +58 -0
  389. package/src/preview-streaming.ts +32 -0
  390. package/src/probe.intents.test.ts +94 -0
  391. package/src/probe.parse-token.test.ts +43 -0
  392. package/src/probe.runtime.ts +1 -0
  393. package/src/probe.ts +237 -0
  394. package/src/proxy-fetch.ts +92 -0
  395. package/src/proxy-request-client.test.ts +78 -0
  396. package/src/proxy-request-client.ts +21 -0
  397. package/src/recipient-resolution.ts +39 -0
  398. package/src/resolve-allowlist-common.test.ts +36 -0
  399. package/src/resolve-allowlist-common.ts +39 -0
  400. package/src/resolve-channels.test.ts +340 -0
  401. package/src/resolve-channels.ts +369 -0
  402. package/src/resolve-users.test.ts +222 -0
  403. package/src/resolve-users.ts +184 -0
  404. package/src/retry.test.ts +83 -0
  405. package/src/retry.ts +98 -0
  406. package/src/runtime-api.ts +64 -0
  407. package/src/runtime.ts +22 -5
  408. package/src/secret-config-contract.ts +140 -0
  409. package/src/security-audit.runtime.ts +1 -0
  410. package/src/security-audit.test.ts +246 -0
  411. package/src/security-audit.ts +208 -0
  412. package/src/security-contract.ts +47 -0
  413. package/src/security-doctor.test.ts +25 -0
  414. package/src/security-doctor.ts +20 -0
  415. package/src/security.ts +60 -0
  416. package/src/send-target-parsing.ts +14 -0
  417. package/src/send.channels.ts +139 -0
  418. package/src/send.components.test.ts +275 -0
  419. package/src/send.components.ts +381 -0
  420. package/src/send.creates-thread.test.ts +643 -0
  421. package/src/send.emojis-stickers.ts +57 -0
  422. package/src/send.guild.ts +170 -0
  423. package/src/send.message-request.ts +97 -0
  424. package/src/send.messages.test.ts +53 -0
  425. package/src/send.messages.ts +225 -0
  426. package/src/send.outbound.ts +413 -0
  427. package/src/send.permissions.authz.test.ts +188 -0
  428. package/src/send.permissions.ts +283 -0
  429. package/src/send.reactions.ts +155 -0
  430. package/src/send.sends-basic-channel-messages.test.ts +941 -0
  431. package/src/send.shared.ts +447 -0
  432. package/src/send.test-harness.ts +56 -0
  433. package/src/send.ts +82 -0
  434. package/src/send.types.ts +188 -0
  435. package/src/send.typing.test.ts +41 -0
  436. package/src/send.typing.ts +9 -0
  437. package/src/send.voice.ts +134 -0
  438. package/src/send.webhook-activity.test.ts +105 -0
  439. package/src/send.webhook.proxy.test.ts +191 -0
  440. package/src/send.webhook.ts +133 -0
  441. package/src/session-contract.ts +3 -0
  442. package/src/session-key-normalization.test.ts +44 -0
  443. package/src/session-key-normalization.ts +47 -0
  444. package/src/setup-account-state.test.ts +91 -0
  445. package/src/setup-account-state.ts +144 -0
  446. package/src/setup-adapter.ts +12 -0
  447. package/src/setup-core.ts +212 -0
  448. package/src/setup-runtime-helpers.ts +10 -0
  449. package/src/setup-surface.test.ts +137 -0
  450. package/src/setup-surface.ts +129 -0
  451. package/src/shared-interactive.test.ts +153 -0
  452. package/src/shared-interactive.ts +124 -0
  453. package/src/shared.test.ts +165 -0
  454. package/src/shared.ts +190 -0
  455. package/src/status-issues.test.ts +70 -0
  456. package/src/status-issues.ts +169 -0
  457. package/src/subagent-hooks.test.ts +130 -81
  458. package/src/subagent-hooks.ts +184 -122
  459. package/src/target-parsing.ts +53 -0
  460. package/src/target-resolver.ts +129 -0
  461. package/src/targets.test.ts +367 -0
  462. package/src/targets.ts +12 -0
  463. package/src/test-http-helpers.ts +10 -0
  464. package/src/test-support/component-runtime.ts +190 -0
  465. package/src/test-support/config.ts +7 -0
  466. package/src/test-support/configured-binding-runtime.ts +29 -0
  467. package/src/test-support/partial-channel.ts +26 -0
  468. package/src/test-support/provider.test-support.ts +545 -0
  469. package/src/token.test.ts +107 -0
  470. package/src/token.ts +60 -0
  471. package/src/ui-colors.ts +27 -0
  472. package/src/ui.ts +20 -0
  473. package/src/voice/access.test.ts +217 -0
  474. package/src/voice/access.ts +124 -0
  475. package/src/voice/audio.ts +173 -0
  476. package/src/voice/capture-state.test.ts +48 -0
  477. package/src/voice/capture-state.ts +120 -0
  478. package/src/voice/command.test.ts +164 -0
  479. package/src/voice/command.ts +283 -0
  480. package/src/voice/config.ts +8 -0
  481. package/src/voice/manager.e2e.test.ts +928 -0
  482. package/src/voice/manager.ready-listener.test.ts +37 -0
  483. package/src/voice/manager.runtime.ts +11 -0
  484. package/src/voice/manager.ts +691 -0
  485. package/src/voice/prompt.test.ts +16 -0
  486. package/src/voice/prompt.ts +17 -0
  487. package/src/voice/receive-recovery.test.ts +79 -0
  488. package/src/voice/receive-recovery.ts +159 -0
  489. package/src/voice/sanitize.test.ts +34 -0
  490. package/src/voice/sanitize.ts +32 -0
  491. package/src/voice/sdk-runtime.ts +14 -0
  492. package/src/voice/segment.ts +156 -0
  493. package/src/voice/session.ts +50 -0
  494. package/src/voice/speaker-context.ts +127 -0
  495. package/src/voice/tts.ts +125 -0
  496. package/src/voice-message.test.ts +234 -0
  497. package/src/voice-message.ts +444 -0
  498. package/subagent-hooks-api.ts +27 -0
  499. package/test-api.ts +4 -0
  500. package/thread-binding-api.ts +1 -0
  501. package/timeouts.ts +6 -0
  502. package/tsconfig.json +16 -0
@@ -0,0 +1,322 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { inspect } from "node:util";
3
+ import { serializeRequestBody } from "./rest-body.js";
4
+ import {
5
+ DiscordError,
6
+ RateLimitError,
7
+ readDiscordCode,
8
+ readDiscordMessage,
9
+ readRetryAfter,
10
+ } from "./rest-errors.js";
11
+ import { appendQuery, createRouteKey } from "./rest-routes.js";
12
+ import {
13
+ RestScheduler,
14
+ type RequestPriority as RestRequestPriority,
15
+ type RequestQuery,
16
+ } from "./rest-scheduler.js";
17
+ import { isDiscordRateLimitBody } from "./schemas.js";
18
+
19
+ export { DiscordError, RateLimitError } from "./rest-errors.js";
20
+
21
+ export type RuntimeProfile = "serverless" | "persistent";
22
+ export type RequestPriority = RestRequestPriority;
23
+ export type RequestSchedulerOptions = {
24
+ lanes?: Partial<
25
+ Record<RequestPriority, { maxQueueSize?: number; staleAfterMs?: number; weight?: number }>
26
+ >;
27
+ maxConcurrency?: number;
28
+ maxRateLimitRetries?: number;
29
+ };
30
+
31
+ export type RequestClientOptions = {
32
+ tokenHeader?: "Bot" | "Bearer";
33
+ baseUrl?: string;
34
+ apiVersion?: number;
35
+ userAgent?: string;
36
+ timeout?: number;
37
+ queueRequests?: boolean;
38
+ maxQueueSize?: number;
39
+ runtimeProfile?: RuntimeProfile;
40
+ scheduler?: RequestSchedulerOptions;
41
+ fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
42
+ };
43
+
44
+ export type RequestData = {
45
+ body?: unknown;
46
+ multipartStyle?: "message" | "form";
47
+ rawBody?: boolean;
48
+ headers?: Record<string, string>;
49
+ };
50
+
51
+ export type QueuedRequest = {
52
+ method: string;
53
+ path: string;
54
+ data?: RequestData;
55
+ query?: RequestQuery;
56
+ resolve: (value?: unknown) => void;
57
+ reject: (reason?: unknown) => void;
58
+ routeKey: string;
59
+ };
60
+
61
+ const defaultOptions = {
62
+ tokenHeader: "Bot" as const,
63
+ baseUrl: "https://discord.com/api",
64
+ apiVersion: 10,
65
+ userAgent: "OpenClaw Discord",
66
+ timeout: 15_000,
67
+ queueRequests: true,
68
+ maxQueueSize: 1000,
69
+ runtimeProfile: "persistent" as RuntimeProfile,
70
+ };
71
+
72
+ const DEFAULT_MAX_CONCURRENT_WORKERS = 4;
73
+ const defaultLaneOptions: Record<RestRequestPriority, { staleAfterMs?: number; weight: number }> = {
74
+ critical: { weight: 6 },
75
+ standard: { weight: 3 },
76
+ background: { staleAfterMs: 20_000, weight: 1 },
77
+ };
78
+
79
+ function coerceResponseBody(raw: string): unknown {
80
+ if (!raw) {
81
+ return undefined;
82
+ }
83
+ try {
84
+ return JSON.parse(raw);
85
+ } catch {
86
+ return raw;
87
+ }
88
+ }
89
+
90
+ function escapeMultipartQuotedValue(value: string): string {
91
+ return value.replace(/["\r\n]/g, (ch) => (ch === '"' ? "%22" : ch === "\r" ? "%0D" : "%0A"));
92
+ }
93
+
94
+ async function formDataToMultipartBody(body: FormData, headers: Headers): Promise<BodyInit> {
95
+ const boundary = `----openclaw-discord-${randomBytes(12).toString("hex")}`;
96
+ headers.set("Content-Type", `multipart/form-data; boundary=${boundary}`);
97
+ const chunks: Buffer[] = [];
98
+ const push = (value: string | Buffer) => {
99
+ chunks.push(typeof value === "string" ? Buffer.from(value) : value);
100
+ };
101
+ for (const [key, value] of body.entries()) {
102
+ push(`--${boundary}\r\n`);
103
+ const escapedKey = escapeMultipartQuotedValue(key);
104
+ if (typeof value === "string") {
105
+ push(`Content-Disposition: form-data; name="${escapedKey}"\r\n\r\n`);
106
+ push(value);
107
+ push("\r\n");
108
+ continue;
109
+ }
110
+ const filename = (value as Blob & { name?: unknown }).name;
111
+ const escapedFilename = escapeMultipartQuotedValue(
112
+ typeof filename === "string" && filename.length > 0 ? filename : "blob",
113
+ );
114
+ push(`Content-Disposition: form-data; name="${escapedKey}"; filename="${escapedFilename}"\r\n`);
115
+ if (value.type) {
116
+ push(`Content-Type: ${value.type}\r\n`);
117
+ }
118
+ push("\r\n");
119
+ push(Buffer.from(await value.arrayBuffer()));
120
+ push("\r\n");
121
+ }
122
+ push(`--${boundary}--\r\n`);
123
+ return Buffer.concat(chunks) as unknown as BodyInit;
124
+ }
125
+
126
+ async function normalizeFetchBody(
127
+ body: BodyInit | undefined,
128
+ headers: Headers,
129
+ ): Promise<BodyInit | undefined> {
130
+ if (body instanceof FormData) {
131
+ return await formDataToMultipartBody(body, headers);
132
+ }
133
+ return body;
134
+ }
135
+
136
+ export class RequestClient {
137
+ readonly options: RequestClientOptions;
138
+ protected token: string;
139
+ protected customFetch: RequestClientOptions["fetch"];
140
+ protected requestControllers = new Set<AbortController>();
141
+ private scheduler: RestScheduler<RequestData>;
142
+
143
+ constructor(token: string, options?: RequestClientOptions) {
144
+ this.token = token.replace(/^Bot\s+/i, "");
145
+ this.customFetch = options?.fetch;
146
+ this.options = { ...defaultOptions, ...options };
147
+ this.scheduler = new RestScheduler<RequestData>(
148
+ {
149
+ lanes: normalizeSchedulerLanes(
150
+ this.options.maxQueueSize ?? defaultOptions.maxQueueSize,
151
+ this.options.scheduler?.lanes,
152
+ ),
153
+ maxConcurrency: this.options.scheduler?.maxConcurrency ?? DEFAULT_MAX_CONCURRENT_WORKERS,
154
+ maxQueueSize: this.options.maxQueueSize ?? defaultOptions.maxQueueSize,
155
+ maxRateLimitRetries: this.options.scheduler?.maxRateLimitRetries ?? 3,
156
+ },
157
+ async (request) =>
158
+ await this.executeRequest(
159
+ request.method,
160
+ request.path,
161
+ { data: request.data, query: request.query },
162
+ request.routeKey,
163
+ ),
164
+ );
165
+ }
166
+
167
+ async get(path: string, query?: QueuedRequest["query"]): Promise<unknown> {
168
+ return await this.request("GET", path, { query });
169
+ }
170
+
171
+ async post(path: string, data?: RequestData, query?: QueuedRequest["query"]): Promise<unknown> {
172
+ return await this.request("POST", path, { data, query });
173
+ }
174
+
175
+ async patch(path: string, data?: RequestData, query?: QueuedRequest["query"]): Promise<unknown> {
176
+ return await this.request("PATCH", path, { data, query });
177
+ }
178
+
179
+ async put(path: string, data?: RequestData, query?: QueuedRequest["query"]): Promise<unknown> {
180
+ return await this.request("PUT", path, { data, query });
181
+ }
182
+
183
+ async delete(path: string, data?: RequestData, query?: QueuedRequest["query"]): Promise<unknown> {
184
+ return await this.request("DELETE", path, { data, query });
185
+ }
186
+
187
+ protected async request(
188
+ method: string,
189
+ path: string,
190
+ params: { data?: RequestData; query?: QueuedRequest["query"] },
191
+ ): Promise<unknown> {
192
+ const routeKey = createRouteKey(method, path);
193
+ if (!this.options.queueRequests) {
194
+ return await this.executeRequest(method, path, params, routeKey);
195
+ }
196
+ return await this.scheduler.enqueue({
197
+ method,
198
+ path,
199
+ priority: getRequestPriority(method, path),
200
+ ...params,
201
+ });
202
+ }
203
+
204
+ protected async executeRequest(
205
+ method: string,
206
+ path: string,
207
+ params: { data?: RequestData; query?: QueuedRequest["query"] },
208
+ routeKey = createRouteKey(method, path),
209
+ ): Promise<unknown> {
210
+ const url = `${this.options.baseUrl}/v${this.options.apiVersion}${appendQuery(path, params.query)}`;
211
+ const headers = new Headers({
212
+ "User-Agent": this.options.userAgent ?? defaultOptions.userAgent,
213
+ });
214
+ if (this.token !== "webhook") {
215
+ headers.set("Authorization", `${this.options.tokenHeader ?? "Bot"} ${this.token}`);
216
+ }
217
+ const body = serializeRequestBody(params.data, headers);
218
+ const controller = new AbortController();
219
+ const timeout = setTimeout(() => controller.abort(), this.options.timeout ?? 15_000);
220
+ timeout.unref?.();
221
+ this.requestControllers.add(controller);
222
+ try {
223
+ const response = await (this.customFetch ?? fetch)(url, {
224
+ method,
225
+ headers,
226
+ body: await normalizeFetchBody(body, headers),
227
+ signal: controller.signal,
228
+ });
229
+ const text = await response.text();
230
+ const parsed = coerceResponseBody(text);
231
+ this.scheduler.recordResponse(routeKey, path, response, parsed);
232
+ if (response.status === 204) {
233
+ return undefined;
234
+ }
235
+ if (response.status === 429) {
236
+ const rateLimitBody = isDiscordRateLimitBody(parsed) ? parsed : undefined;
237
+ throw new RateLimitError(response, {
238
+ message: readDiscordMessage(rateLimitBody, "Rate limited"),
239
+ retry_after: readRetryAfter(rateLimitBody, response, 1),
240
+ code: readDiscordCode(rateLimitBody),
241
+ global: Boolean(rateLimitBody?.global),
242
+ });
243
+ }
244
+ if (!response.ok) {
245
+ throw new DiscordError(response, parsed);
246
+ }
247
+ return parsed;
248
+ } catch (error) {
249
+ if (error instanceof DOMException && error.name === "AbortError") {
250
+ throw error;
251
+ }
252
+ if (error instanceof Error) {
253
+ throw error;
254
+ }
255
+ throw new Error(`Discord request failed: ${inspect(error)}`, { cause: error });
256
+ } finally {
257
+ clearTimeout(timeout);
258
+ this.requestControllers.delete(controller);
259
+ }
260
+ }
261
+
262
+ clearQueue(): void {
263
+ this.scheduler.clearQueue();
264
+ }
265
+
266
+ get queueSize(): number {
267
+ return this.scheduler.queueSize;
268
+ }
269
+
270
+ getSchedulerMetrics() {
271
+ return this.scheduler.getMetrics();
272
+ }
273
+
274
+ abortAllRequests(): void {
275
+ this.scheduler.abortPending();
276
+ for (const controller of this.requestControllers) {
277
+ controller.abort();
278
+ }
279
+ this.requestControllers.clear();
280
+ }
281
+ }
282
+
283
+ function normalizeSchedulerLanes(
284
+ maxQueueSize: number,
285
+ lanes?: RequestSchedulerOptions["lanes"],
286
+ ): Record<RestRequestPriority, { maxQueueSize: number; staleAfterMs?: number; weight: number }> {
287
+ const fallbackMaxQueueSize = Math.max(1, Math.floor(maxQueueSize));
288
+ return {
289
+ critical: normalizeSchedulerLane("critical", fallbackMaxQueueSize, lanes?.critical),
290
+ standard: normalizeSchedulerLane("standard", fallbackMaxQueueSize, lanes?.standard),
291
+ background: normalizeSchedulerLane("background", fallbackMaxQueueSize, lanes?.background),
292
+ };
293
+ }
294
+
295
+ function normalizeSchedulerLane(
296
+ lane: RestRequestPriority,
297
+ maxQueueSize: number,
298
+ options?: { maxQueueSize?: number; staleAfterMs?: number; weight?: number },
299
+ ): { maxQueueSize: number; staleAfterMs?: number; weight: number } {
300
+ const defaults = defaultLaneOptions[lane];
301
+ return {
302
+ maxQueueSize:
303
+ options?.maxQueueSize !== undefined
304
+ ? Math.max(1, Math.floor(options.maxQueueSize))
305
+ : maxQueueSize,
306
+ staleAfterMs:
307
+ options?.staleAfterMs !== undefined
308
+ ? Math.max(0, Math.floor(options.staleAfterMs))
309
+ : defaults.staleAfterMs,
310
+ weight:
311
+ options?.weight !== undefined ? Math.max(1, Math.floor(options.weight)) : defaults.weight,
312
+ };
313
+ }
314
+
315
+ function getRequestPriority(method: string, path: string): RestRequestPriority {
316
+ const normalizedMethod = method.toUpperCase();
317
+ const normalizedPath = path.toLowerCase();
318
+ if (/^\/interactions\/\d+\/[^/]+\/callback$/.test(normalizedPath)) {
319
+ return "critical";
320
+ }
321
+ return normalizedMethod === "GET" ? "background" : "standard";
322
+ }
@@ -0,0 +1,36 @@
1
+ import { Type } from "typebox";
2
+ import { Check } from "typebox/value";
3
+
4
+ const discordInteractionPayloadSchema = Type.Object(
5
+ {
6
+ id: Type.String({ minLength: 1 }),
7
+ token: Type.String({ minLength: 1 }),
8
+ type: Type.Number(),
9
+ },
10
+ { additionalProperties: true },
11
+ );
12
+
13
+ const discordRateLimitBodySchema = Type.Object(
14
+ {
15
+ message: Type.Optional(Type.String()),
16
+ retry_after: Type.Optional(Type.Union([Type.Number(), Type.String()])),
17
+ global: Type.Optional(Type.Boolean()),
18
+ code: Type.Optional(Type.Union([Type.Number(), Type.String()])),
19
+ },
20
+ { additionalProperties: true },
21
+ );
22
+
23
+ export function assertDiscordInteractionPayload(value: unknown): void {
24
+ if (!Check(discordInteractionPayloadSchema, value)) {
25
+ throw new Error("Invalid Discord interaction payload");
26
+ }
27
+ }
28
+
29
+ export function isDiscordRateLimitBody(value: unknown): value is {
30
+ message?: string;
31
+ retry_after?: number | string;
32
+ global?: boolean;
33
+ code?: number | string;
34
+ } {
35
+ return Check(discordRateLimitBodySchema, value);
36
+ }
@@ -0,0 +1,43 @@
1
+ import { ChannelType } from "discord-api-types/v10";
2
+ import { describe, expect, it } from "vitest";
3
+ import { channelFactory, type StructureClient } from "./structures.js";
4
+
5
+ const client: StructureClient = {
6
+ rest: {} as StructureClient["rest"],
7
+ async fetchUser() {
8
+ throw new Error("not used");
9
+ },
10
+ };
11
+
12
+ describe("channelFactory", () => {
13
+ it("maps Discord API thread owner and parent fields to camelCase aliases", () => {
14
+ const channel = channelFactory(client, {
15
+ id: "thread-1",
16
+ type: ChannelType.PublicThread,
17
+ guild_id: "guild-1",
18
+ name: "support",
19
+ owner_id: "owner-1",
20
+ parent_id: "parent-1",
21
+ last_message_id: null,
22
+ rate_limit_per_user: 0,
23
+ thread_metadata: {
24
+ archived: false,
25
+ auto_archive_duration: 60,
26
+ locked: false,
27
+ archive_timestamp: new Date(0).toISOString(),
28
+ },
29
+ message_count: 1,
30
+ member_count: 1,
31
+ total_message_sent: 1,
32
+ });
33
+
34
+ expect(channel.parentId).toBe("parent-1");
35
+ expect(channel.ownerId).toBe("owner-1");
36
+ expect(
37
+ channel.rawData && "parent_id" in channel.rawData ? channel.rawData.parent_id : undefined,
38
+ ).toBe("parent-1");
39
+ expect(
40
+ channel.rawData && "owner_id" in channel.rawData ? channel.rawData.owner_id : undefined,
41
+ ).toBe("owner-1");
42
+ });
43
+ });
@@ -0,0 +1,280 @@
1
+ import {
2
+ type APIChannel,
3
+ type APIEmbed,
4
+ type APIGuild,
5
+ type APIGuildMember,
6
+ type APIMessage,
7
+ type APIRole,
8
+ type APIUser,
9
+ type MessageType,
10
+ } from "discord-api-types/v10";
11
+ import {
12
+ createChannelMessage,
13
+ createUserDmChannel,
14
+ deleteChannelMessage,
15
+ editChannelMessage,
16
+ getChannelMessage,
17
+ pinChannelMessage,
18
+ unpinChannelMessage,
19
+ } from "./api.js";
20
+ import { serializePayload, type MessagePayload } from "./payload.js";
21
+ import type { RequestClient } from "./rest.js";
22
+
23
+ type RawOrId<T> = T | string | { id: string; channelId?: string };
24
+ export type StructureClient = {
25
+ rest: RequestClient;
26
+ fetchUser(id: string): Promise<User>;
27
+ };
28
+
29
+ export class Base {
30
+ constructor(protected client: StructureClient) {}
31
+ }
32
+
33
+ export class User<IsPartial extends boolean = false> extends Base {
34
+ protected _rawData: APIUser | null;
35
+ readonly id: string;
36
+
37
+ constructor(client: StructureClient, rawDataOrId: IsPartial extends true ? string : APIUser) {
38
+ super(client);
39
+ this._rawData = typeof rawDataOrId === "string" ? null : rawDataOrId;
40
+ this.id = typeof rawDataOrId === "string" ? rawDataOrId : rawDataOrId.id;
41
+ }
42
+
43
+ get rawData(): Readonly<APIUser> {
44
+ if (!this._rawData) {
45
+ throw new Error("Partial Discord user has no raw data");
46
+ }
47
+ return this._rawData;
48
+ }
49
+ get partial(): IsPartial {
50
+ return (this._rawData === null) as IsPartial;
51
+ }
52
+ get username() {
53
+ return this._rawData?.username ?? "";
54
+ }
55
+ get globalName() {
56
+ return this._rawData?.global_name;
57
+ }
58
+ get discriminator() {
59
+ return this._rawData?.discriminator;
60
+ }
61
+ get bot() {
62
+ return this._rawData?.bot;
63
+ }
64
+ get avatar() {
65
+ return this._rawData?.avatar;
66
+ }
67
+ get avatarUrl() {
68
+ return this.avatar ? `https://cdn.discordapp.com/avatars/${this.id}/${this.avatar}.png` : null;
69
+ }
70
+ toString(): string {
71
+ return `<@${this.id}>`;
72
+ }
73
+ async fetch(): Promise<User> {
74
+ return this.client.fetchUser(this.id);
75
+ }
76
+ async createDm() {
77
+ return await createUserDmChannel(this.client.rest, this.id);
78
+ }
79
+ async send(data: MessagePayload): Promise<Message> {
80
+ const dm = await this.createDm();
81
+ const message = await createChannelMessage(this.client.rest, dm.id, {
82
+ body: serializePayload(data),
83
+ });
84
+ return new Message(this.client, message);
85
+ }
86
+ }
87
+
88
+ export class Role<IsPartial extends boolean = false> extends Base {
89
+ protected _rawData: APIRole | null;
90
+ readonly id: string;
91
+ constructor(client: StructureClient, rawDataOrId: IsPartial extends true ? string : APIRole) {
92
+ super(client);
93
+ this._rawData = typeof rawDataOrId === "string" ? null : rawDataOrId;
94
+ this.id = typeof rawDataOrId === "string" ? rawDataOrId : rawDataOrId.id;
95
+ }
96
+ get name() {
97
+ return this._rawData?.name ?? "";
98
+ }
99
+ }
100
+
101
+ export class Guild<IsPartial extends boolean = false> extends Base {
102
+ protected _rawData: APIGuild | null;
103
+ readonly id: string;
104
+ constructor(client: StructureClient, rawDataOrId: IsPartial extends true ? string : APIGuild) {
105
+ super(client);
106
+ this._rawData = typeof rawDataOrId === "string" ? null : rawDataOrId;
107
+ this.id = typeof rawDataOrId === "string" ? rawDataOrId : rawDataOrId.id;
108
+ }
109
+ get name() {
110
+ return this._rawData?.name ?? "";
111
+ }
112
+ }
113
+
114
+ export class GuildMember extends Base {
115
+ constructor(
116
+ client: StructureClient,
117
+ public rawData: APIGuildMember,
118
+ ) {
119
+ super(client);
120
+ }
121
+ get user() {
122
+ return this.rawData.user ? new User(this.client, this.rawData.user) : null;
123
+ }
124
+ get roles() {
125
+ return (this.rawData.roles ?? []) as Array<string | Role>;
126
+ }
127
+ get nickname() {
128
+ return this.rawData.nick ?? undefined;
129
+ }
130
+ }
131
+
132
+ export class Message<IsPartial extends boolean = false> extends Base {
133
+ protected _rawData: APIMessage | null;
134
+ readonly id: string;
135
+ readonly channelId: string;
136
+
137
+ constructor(client: StructureClient, rawDataOrIds: RawOrId<APIMessage>) {
138
+ super(client);
139
+ this._rawData =
140
+ typeof rawDataOrIds === "string" || !("author" in rawDataOrIds) ? null : rawDataOrIds;
141
+ this.id = typeof rawDataOrIds === "string" ? rawDataOrIds : rawDataOrIds.id;
142
+ this.channelId =
143
+ typeof rawDataOrIds === "string"
144
+ ? ""
145
+ : "channel_id" in rawDataOrIds
146
+ ? rawDataOrIds.channel_id
147
+ : (rawDataOrIds.channelId ?? "");
148
+ }
149
+
150
+ get rawData(): Readonly<APIMessage> {
151
+ if (!this._rawData) {
152
+ throw new Error("Partial Discord message has no raw data");
153
+ }
154
+ return this._rawData;
155
+ }
156
+ get partial(): IsPartial {
157
+ return (this._rawData === null) as IsPartial;
158
+ }
159
+ get message(): Message<IsPartial> {
160
+ return this;
161
+ }
162
+ get channel_id() {
163
+ return this.channelId;
164
+ }
165
+ get guild_id() {
166
+ return (this._rawData as { guild_id?: string } | null)?.guild_id;
167
+ }
168
+ get guild() {
169
+ return this.guild_id ? new Guild<true>(this.client, this.guild_id) : null;
170
+ }
171
+ get webhookId() {
172
+ return this.webhook_id;
173
+ }
174
+ get webhook_id() {
175
+ return (this._rawData as { webhook_id?: string | null } | null)?.webhook_id ?? null;
176
+ }
177
+ get member() {
178
+ const member = (this._rawData as { member?: APIGuildMember } | null)?.member;
179
+ return member ? new GuildMember(this.client, member) : null;
180
+ }
181
+ get rawMember() {
182
+ return (this._rawData as { member?: APIGuildMember } | null)?.member;
183
+ }
184
+ get content() {
185
+ return this._rawData?.content ?? "";
186
+ }
187
+ get author() {
188
+ return this._rawData?.author ? new User(this.client, this._rawData.author) : null;
189
+ }
190
+ get embeds(): APIEmbed[] {
191
+ return this._rawData?.embeds ?? [];
192
+ }
193
+ get attachments() {
194
+ return this._rawData?.attachments ?? [];
195
+ }
196
+ get stickers() {
197
+ return this._rawData?.sticker_items ?? [];
198
+ }
199
+ get mentionedUsers() {
200
+ return (this._rawData?.mentions ?? []).map((user) => new User(this.client, user));
201
+ }
202
+ get mentionedRoles() {
203
+ return this._rawData?.mention_roles ?? [];
204
+ }
205
+ get mentionedEveryone() {
206
+ return this._rawData?.mention_everyone ?? false;
207
+ }
208
+ get timestamp() {
209
+ return this._rawData?.timestamp;
210
+ }
211
+ get type(): MessageType | undefined {
212
+ return this._rawData?.type;
213
+ }
214
+ get messageReference() {
215
+ return this._rawData?.message_reference;
216
+ }
217
+ get referencedMessage() {
218
+ return this._rawData?.referenced_message
219
+ ? new Message(this.client, this._rawData.referenced_message)
220
+ : null;
221
+ }
222
+ get thread() {
223
+ return this._rawData?.thread ? channelFactory(this.client, this._rawData.thread) : null;
224
+ }
225
+ async fetch(): Promise<Message> {
226
+ const raw = await getChannelMessage(this.client.rest, this.channelId, this.id);
227
+ return new Message(this.client, raw);
228
+ }
229
+ async delete(): Promise<void> {
230
+ await deleteChannelMessage(this.client.rest, this.channelId, this.id);
231
+ }
232
+ async edit(data: MessagePayload): Promise<Message> {
233
+ const raw = await editChannelMessage(this.client.rest, this.channelId, this.id, {
234
+ body: serializePayload(data),
235
+ });
236
+ return new Message(this.client, raw);
237
+ }
238
+ async reply(data: MessagePayload): Promise<Message> {
239
+ const raw = await createChannelMessage(this.client.rest, this.channelId, {
240
+ body: {
241
+ ...serializePayload(data),
242
+ message_reference: { message_id: this.id, fail_if_not_exists: false },
243
+ },
244
+ });
245
+ return new Message(this.client, raw);
246
+ }
247
+ async pin(): Promise<void> {
248
+ await pinChannelMessage(this.client.rest, this.channelId, this.id);
249
+ }
250
+ async unpin(): Promise<void> {
251
+ await unpinChannelMessage(this.client.rest, this.channelId, this.id);
252
+ }
253
+ }
254
+
255
+ export type DiscordChannel = APIChannel & {
256
+ rawData?: APIChannel;
257
+ guildId?: string;
258
+ guild?: Guild;
259
+ name?: string;
260
+ parentId?: string | null;
261
+ ownerId?: string | null;
262
+ };
263
+
264
+ export function channelFactory(
265
+ _client: StructureClient,
266
+ channelData: APIChannel,
267
+ _partial?: boolean,
268
+ ): DiscordChannel {
269
+ return {
270
+ ...channelData,
271
+ rawData: channelData,
272
+ guildId: "guild_id" in channelData ? channelData.guild_id : undefined,
273
+ guild:
274
+ "guild_id" in channelData && typeof channelData.guild_id === "string"
275
+ ? new Guild<true>(_client, channelData.guild_id)
276
+ : undefined,
277
+ parentId: "parent_id" in channelData ? channelData.parent_id : undefined,
278
+ ownerId: "owner_id" in channelData ? channelData.owner_id : undefined,
279
+ } as DiscordChannel;
280
+ }