@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,283 @@
1
+ import type { APIChannel, APIGuild, APIGuildMember, APIRole } from "discord-api-types/v10";
2
+ import { ChannelType, PermissionFlagsBits } from "discord-api-types/v10";
3
+ import { resolveDiscordRest } from "./client.js";
4
+ import {
5
+ getChannel,
6
+ getCurrentUser,
7
+ getGuild,
8
+ getGuildMember,
9
+ type RequestClient,
10
+ } from "./internal/discord.js";
11
+ import type { DiscordPermissionsSummary, DiscordReactOpts } from "./send.types.js";
12
+
13
+ const PERMISSION_ENTRIES = Object.entries(PermissionFlagsBits).filter(
14
+ ([, value]) => typeof value === "bigint",
15
+ );
16
+ const ALL_PERMISSIONS = PERMISSION_ENTRIES.reduce((acc, [, value]) => acc | value, 0n);
17
+ const ADMINISTRATOR_BIT = PermissionFlagsBits.Administrator;
18
+
19
+ function addPermissionBits(base: bigint, add?: string) {
20
+ if (!add) {
21
+ return base;
22
+ }
23
+ return base | BigInt(add);
24
+ }
25
+
26
+ function removePermissionBits(base: bigint, deny?: string) {
27
+ if (!deny) {
28
+ return base;
29
+ }
30
+ return base & ~BigInt(deny);
31
+ }
32
+
33
+ function bitfieldToPermissions(bitfield: bigint) {
34
+ return PERMISSION_ENTRIES.filter(([, value]) => (bitfield & value) === value)
35
+ .map(([name]) => name)
36
+ .toSorted();
37
+ }
38
+
39
+ function hasAdministrator(bitfield: bigint) {
40
+ return (bitfield & ADMINISTRATOR_BIT) === ADMINISTRATOR_BIT;
41
+ }
42
+
43
+ function hasPermissionBit(bitfield: bigint, permission: bigint) {
44
+ return (bitfield & permission) === permission;
45
+ }
46
+
47
+ export function isThreadChannelType(channelType?: number) {
48
+ return (
49
+ channelType === ChannelType.GuildNewsThread ||
50
+ channelType === ChannelType.GuildPublicThread ||
51
+ channelType === ChannelType.GuildPrivateThread
52
+ );
53
+ }
54
+
55
+ async function fetchBotUserId(rest: RequestClient) {
56
+ const me = await getCurrentUser(rest);
57
+ if (!me?.id) {
58
+ throw new Error("Failed to resolve bot user id");
59
+ }
60
+ return me.id;
61
+ }
62
+
63
+ function resolveMemberGuildPermissionBits(params: {
64
+ guild: Pick<APIGuild, "id" | "roles">;
65
+ member: Pick<APIGuildMember, "roles">;
66
+ }) {
67
+ const rolesById = new Map<string, APIRole>(
68
+ (params.guild.roles ?? []).map((role) => [role.id, role]),
69
+ );
70
+ const everyoneRole = rolesById.get(params.guild.id);
71
+ let permissions = 0n;
72
+ if (everyoneRole?.permissions) {
73
+ permissions = addPermissionBits(permissions, everyoneRole.permissions);
74
+ }
75
+ for (const roleId of params.member.roles ?? []) {
76
+ const role = rolesById.get(roleId);
77
+ if (role?.permissions) {
78
+ permissions = addPermissionBits(permissions, role.permissions);
79
+ }
80
+ }
81
+ return permissions;
82
+ }
83
+
84
+ function resolveMemberChannelPermissionBits(params: {
85
+ guildId: string;
86
+ userId: string;
87
+ guild: Pick<APIGuild, "id" | "roles">;
88
+ member: Pick<APIGuildMember, "roles">;
89
+ channel: APIChannel;
90
+ }) {
91
+ let permissions = resolveMemberGuildPermissionBits({
92
+ guild: params.guild,
93
+ member: params.member,
94
+ });
95
+
96
+ if (hasAdministrator(permissions)) {
97
+ return ALL_PERMISSIONS;
98
+ }
99
+
100
+ const overwrites =
101
+ "permission_overwrites" in params.channel ? (params.channel.permission_overwrites ?? []) : [];
102
+ for (const overwrite of overwrites) {
103
+ if (overwrite.id === params.guildId) {
104
+ permissions = removePermissionBits(permissions, overwrite.deny ?? "0");
105
+ permissions = addPermissionBits(permissions, overwrite.allow ?? "0");
106
+ }
107
+ }
108
+ let roleDeny = 0n;
109
+ let roleAllow = 0n;
110
+ for (const overwrite of overwrites) {
111
+ if (params.member.roles?.includes(overwrite.id)) {
112
+ roleDeny = addPermissionBits(roleDeny, overwrite.deny ?? "0");
113
+ roleAllow = addPermissionBits(roleAllow, overwrite.allow ?? "0");
114
+ }
115
+ }
116
+ permissions = permissions & ~roleDeny;
117
+ permissions = permissions | roleAllow;
118
+ for (const overwrite of overwrites) {
119
+ if (overwrite.id === params.userId) {
120
+ permissions = removePermissionBits(permissions, overwrite.deny ?? "0");
121
+ permissions = addPermissionBits(permissions, overwrite.allow ?? "0");
122
+ }
123
+ }
124
+
125
+ return permissions;
126
+ }
127
+
128
+ /**
129
+ * Fetch guild-level permissions for a user. This does not include channel-specific overwrites.
130
+ */
131
+ export async function fetchMemberGuildPermissionsDiscord(
132
+ guildId: string,
133
+ userId: string,
134
+ opts: DiscordReactOpts,
135
+ ): Promise<bigint | null> {
136
+ const rest = resolveDiscordRest(opts);
137
+ try {
138
+ const [guild, member] = await Promise.all([
139
+ getGuild(rest, guildId),
140
+ getGuildMember(rest, guildId, userId),
141
+ ]);
142
+ return resolveMemberGuildPermissionBits({ guild, member });
143
+ } catch {
144
+ // Not a guild member, guild not found, or API failure.
145
+ return null;
146
+ }
147
+ }
148
+
149
+ export async function canViewDiscordGuildChannel(
150
+ guildId: string,
151
+ channelId: string,
152
+ userId: string,
153
+ opts: DiscordReactOpts,
154
+ ): Promise<boolean> {
155
+ const rest = resolveDiscordRest(opts);
156
+ try {
157
+ const channel = await getChannel(rest, channelId);
158
+ const channelGuildId = "guild_id" in channel ? channel.guild_id : undefined;
159
+ if (channelGuildId !== guildId) {
160
+ return false;
161
+ }
162
+ const [guild, member] = await Promise.all([
163
+ getGuild(rest, guildId),
164
+ getGuildMember(rest, guildId, userId),
165
+ ]);
166
+ const permissions = resolveMemberChannelPermissionBits({
167
+ guildId,
168
+ userId,
169
+ guild,
170
+ member,
171
+ channel,
172
+ });
173
+ return hasPermissionBit(permissions, PermissionFlagsBits.ViewChannel);
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Returns true when the user has ADMINISTRATOR or required permission bits
181
+ * matching the provided predicate.
182
+ */
183
+ async function hasGuildPermissionsDiscord(
184
+ guildId: string,
185
+ userId: string,
186
+ requiredPermissions: bigint[],
187
+ check: (permissions: bigint, requiredPermissions: bigint[]) => boolean,
188
+ opts: DiscordReactOpts,
189
+ ): Promise<boolean> {
190
+ const permissions = await fetchMemberGuildPermissionsDiscord(guildId, userId, opts);
191
+ if (permissions === null) {
192
+ return false;
193
+ }
194
+ if (hasAdministrator(permissions)) {
195
+ return true;
196
+ }
197
+ return check(permissions, requiredPermissions);
198
+ }
199
+
200
+ /**
201
+ * Returns true when the user has ADMINISTRATOR or any required permission bit.
202
+ */
203
+ export async function hasAnyGuildPermissionDiscord(
204
+ guildId: string,
205
+ userId: string,
206
+ requiredPermissions: bigint[],
207
+ opts: DiscordReactOpts,
208
+ ): Promise<boolean> {
209
+ return await hasGuildPermissionsDiscord(
210
+ guildId,
211
+ userId,
212
+ requiredPermissions,
213
+ (permissions, required) =>
214
+ required.some((permission) => hasPermissionBit(permissions, permission)),
215
+ opts,
216
+ );
217
+ }
218
+
219
+ /**
220
+ * Returns true when the user has ADMINISTRATOR or all required permission bits.
221
+ */
222
+ export async function hasAllGuildPermissionsDiscord(
223
+ guildId: string,
224
+ userId: string,
225
+ requiredPermissions: bigint[],
226
+ opts: DiscordReactOpts,
227
+ ): Promise<boolean> {
228
+ return await hasGuildPermissionsDiscord(
229
+ guildId,
230
+ userId,
231
+ requiredPermissions,
232
+ (permissions, required) =>
233
+ required.every((permission) => hasPermissionBit(permissions, permission)),
234
+ opts,
235
+ );
236
+ }
237
+
238
+ /**
239
+ * @deprecated Prefer hasAnyGuildPermissionDiscord or hasAllGuildPermissionsDiscord for clarity.
240
+ */
241
+ export const hasGuildPermissionDiscord = hasAnyGuildPermissionDiscord;
242
+
243
+ export async function fetchChannelPermissionsDiscord(
244
+ channelId: string,
245
+ opts: DiscordReactOpts,
246
+ ): Promise<DiscordPermissionsSummary> {
247
+ const rest = resolveDiscordRest(opts);
248
+ const channel = await getChannel(rest, channelId);
249
+ const channelType = "type" in channel ? channel.type : undefined;
250
+ const guildId = "guild_id" in channel ? channel.guild_id : undefined;
251
+ if (!guildId) {
252
+ return {
253
+ channelId,
254
+ permissions: [],
255
+ raw: "0",
256
+ isDm: true,
257
+ channelType,
258
+ };
259
+ }
260
+
261
+ const botId = await fetchBotUserId(rest);
262
+ const [guild, member] = await Promise.all([
263
+ getGuild(rest, guildId),
264
+ getGuildMember(rest, guildId, botId),
265
+ ]);
266
+
267
+ const permissions = resolveMemberChannelPermissionBits({
268
+ guildId,
269
+ userId: botId,
270
+ guild,
271
+ member,
272
+ channel,
273
+ });
274
+
275
+ return {
276
+ channelId,
277
+ guildId,
278
+ permissions: bitfieldToPermissions(permissions),
279
+ raw: permissions.toString(),
280
+ isDm: false,
281
+ channelType,
282
+ };
283
+ }
@@ -0,0 +1,155 @@
1
+ import { requireRuntimeConfig } from "autobot/plugin-sdk/plugin-config-runtime";
2
+ import {
3
+ createOwnMessageReaction,
4
+ deleteOwnMessageReaction,
5
+ getChannelMessage,
6
+ listMessageReactionUsers,
7
+ } from "./internal/discord.js";
8
+ import {
9
+ buildReactionIdentifier,
10
+ createDiscordClient,
11
+ formatReactionEmoji,
12
+ normalizeReactionEmoji,
13
+ } from "./send.shared.js";
14
+ import type {
15
+ DiscordReactionRuntimeContext,
16
+ DiscordReactionSummary,
17
+ DiscordReactOpts,
18
+ } from "./send.types.js";
19
+
20
+ function createDiscordReactionRuntimeClient(opts: DiscordReactionRuntimeContext) {
21
+ return createDiscordClient(opts);
22
+ }
23
+
24
+ function resolveDiscordReactionClient(opts: DiscordReactOpts) {
25
+ if (!opts.cfg) {
26
+ throw new Error(
27
+ "Discord reactions requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.",
28
+ );
29
+ }
30
+ const cfg = requireRuntimeConfig(opts.cfg, "Discord reactions");
31
+ return createDiscordClient({ ...opts, cfg });
32
+ }
33
+
34
+ function isDiscordReactionRuntimeContext(
35
+ opts: DiscordReactOpts,
36
+ ): opts is DiscordReactionRuntimeContext {
37
+ return Boolean(opts.rest && opts.cfg && opts.accountId);
38
+ }
39
+
40
+ export async function reactMessageDiscord(
41
+ channelId: string,
42
+ messageId: string,
43
+ emoji: string,
44
+ opts: DiscordReactOpts,
45
+ ) {
46
+ const { rest, request } = isDiscordReactionRuntimeContext(opts)
47
+ ? createDiscordReactionRuntimeClient(opts)
48
+ : resolveDiscordReactionClient(opts);
49
+ const encoded = normalizeReactionEmoji(emoji);
50
+ await request(() => createOwnMessageReaction(rest, channelId, messageId, encoded), "react");
51
+ return { ok: true };
52
+ }
53
+
54
+ export async function removeReactionDiscord(
55
+ channelId: string,
56
+ messageId: string,
57
+ emoji: string,
58
+ opts: DiscordReactOpts,
59
+ ) {
60
+ const { rest } = isDiscordReactionRuntimeContext(opts)
61
+ ? createDiscordReactionRuntimeClient(opts)
62
+ : resolveDiscordReactionClient(opts);
63
+ const encoded = normalizeReactionEmoji(emoji);
64
+ await deleteOwnMessageReaction(rest, channelId, messageId, encoded);
65
+ return { ok: true };
66
+ }
67
+
68
+ export async function removeOwnReactionsDiscord(
69
+ channelId: string,
70
+ messageId: string,
71
+ opts: DiscordReactOpts,
72
+ ): Promise<{ ok: true; removed: string[] }> {
73
+ const { rest } = isDiscordReactionRuntimeContext(opts)
74
+ ? createDiscordReactionRuntimeClient(opts)
75
+ : resolveDiscordReactionClient(opts);
76
+ const message = (await getChannelMessage(rest, channelId, messageId)) as {
77
+ reactions?: Array<{ emoji: { id?: string | null; name?: string | null } }>;
78
+ };
79
+ const identifiers = new Set<string>();
80
+ for (const reaction of message.reactions ?? []) {
81
+ const identifier = buildReactionIdentifier(reaction.emoji);
82
+ if (identifier) {
83
+ identifiers.add(identifier);
84
+ }
85
+ }
86
+ if (identifiers.size === 0) {
87
+ return { ok: true, removed: [] };
88
+ }
89
+ const removed: string[] = [];
90
+ await Promise.allSettled(
91
+ Array.from(identifiers, (identifier) => {
92
+ removed.push(identifier);
93
+ return deleteOwnMessageReaction(
94
+ rest,
95
+ channelId,
96
+ messageId,
97
+ normalizeReactionEmoji(identifier),
98
+ );
99
+ }),
100
+ );
101
+ return { ok: true, removed };
102
+ }
103
+
104
+ export async function fetchReactionsDiscord(
105
+ channelId: string,
106
+ messageId: string,
107
+ opts: DiscordReactOpts & { limit?: number },
108
+ ): Promise<DiscordReactionSummary[]> {
109
+ const { rest } = isDiscordReactionRuntimeContext(opts)
110
+ ? createDiscordReactionRuntimeClient(opts)
111
+ : resolveDiscordReactionClient(opts);
112
+ const message = (await getChannelMessage(rest, channelId, messageId)) as {
113
+ reactions?: Array<{
114
+ count: number;
115
+ emoji: { id?: string | null; name?: string | null };
116
+ }>;
117
+ };
118
+ const reactions = message.reactions ?? [];
119
+ if (reactions.length === 0) {
120
+ return [];
121
+ }
122
+ const limit =
123
+ typeof opts.limit === "number" && Number.isFinite(opts.limit)
124
+ ? Math.min(Math.max(Math.floor(opts.limit), 1), 100)
125
+ : 100;
126
+
127
+ const summaries: DiscordReactionSummary[] = [];
128
+ for (const reaction of reactions) {
129
+ const identifier = buildReactionIdentifier(reaction.emoji);
130
+ if (!identifier) {
131
+ continue;
132
+ }
133
+ const encoded = encodeURIComponent(identifier);
134
+ const users = await listMessageReactionUsers(rest, channelId, messageId, encoded, {
135
+ limit,
136
+ });
137
+ summaries.push({
138
+ emoji: {
139
+ id: reaction.emoji.id ?? null,
140
+ name: reaction.emoji.name ?? null,
141
+ raw: formatReactionEmoji(reaction.emoji),
142
+ },
143
+ count: reaction.count,
144
+ users: users.map((user) => ({
145
+ id: user.id,
146
+ username: user.username,
147
+ tag:
148
+ user.username && user.discriminator
149
+ ? `${user.username}#${user.discriminator}`
150
+ : user.username,
151
+ })),
152
+ });
153
+ }
154
+ return summaries;
155
+ }
@@ -0,0 +1,69 @@
1
+ import {
2
+ createMessageReceiptFromOutboundResults,
3
+ type MessageReceipt,
4
+ type MessageReceiptPartKind,
5
+ type MessageReceiptSourceResult,
6
+ } from "autobot/plugin-sdk/channel-message";
7
+ import type { DiscordSendResult } from "./send.types.js";
8
+
9
+ export type DiscordReceiptResultSource = {
10
+ id?: string | null;
11
+ channel_id?: string | null;
12
+ platformMessageIds?: readonly string[];
13
+ };
14
+
15
+ export function createDiscordSendReceipt(params: {
16
+ platformMessageIds: readonly string[];
17
+ channelId?: string;
18
+ kind: MessageReceiptPartKind;
19
+ threadId?: string;
20
+ replyToId?: string;
21
+ }): MessageReceipt {
22
+ const platformMessageIds = params.platformMessageIds
23
+ .map((messageId) => messageId.trim())
24
+ .filter((messageId) => messageId && messageId !== "unknown");
25
+ return createMessageReceiptFromOutboundResults({
26
+ results: platformMessageIds.map((messageId) => {
27
+ const result: MessageReceiptSourceResult = {
28
+ channel: "discord",
29
+ messageId,
30
+ };
31
+ if (params.channelId) {
32
+ result.channelId = params.channelId;
33
+ }
34
+ return result;
35
+ }),
36
+ kind: params.kind,
37
+ threadId: params.threadId,
38
+ replyToId: params.replyToId,
39
+ });
40
+ }
41
+
42
+ export function createDiscordSendResult(params: {
43
+ result: DiscordReceiptResultSource;
44
+ fallbackChannelId: string;
45
+ kind: MessageReceiptPartKind;
46
+ threadId?: string | number;
47
+ replyToId?: string;
48
+ }): DiscordSendResult {
49
+ const messageId = params.result.id || "unknown";
50
+ const channelId = params.result.channel_id ?? params.fallbackChannelId;
51
+ const receiptParams: Parameters<typeof createDiscordSendReceipt>[0] = {
52
+ platformMessageIds: params.result.platformMessageIds?.length
53
+ ? params.result.platformMessageIds
54
+ : [messageId],
55
+ channelId,
56
+ kind: params.kind,
57
+ };
58
+ if (params.threadId != null) {
59
+ receiptParams.threadId = String(params.threadId);
60
+ }
61
+ if (params.replyToId) {
62
+ receiptParams.replyToId = params.replyToId;
63
+ }
64
+ return {
65
+ messageId,
66
+ channelId,
67
+ receipt: createDiscordSendReceipt(receiptParams),
68
+ };
69
+ }