@poolzin/pool-bot 2026.2.21 → 2026.2.22
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.
- package/CHANGELOG.md +17 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { buildChannelKeyCandidates, resolveChannelEntryMatchWithFallback, resolveChannelMatchConfig, } from "../../channels/channel-config.js";
|
|
2
2
|
import { formatDiscordUserTag } from "./format.js";
|
|
3
3
|
export function normalizeDiscordAllowList(raw, prefixes) {
|
|
4
|
-
if (!raw || raw.length === 0)
|
|
4
|
+
if (!raw || raw.length === 0) {
|
|
5
5
|
return null;
|
|
6
|
+
}
|
|
6
7
|
const ids = new Set();
|
|
7
8
|
const names = new Set();
|
|
8
9
|
const allowAll = raw.some((entry) => String(entry).trim() === "*");
|
|
9
10
|
for (const entry of raw) {
|
|
10
11
|
const text = String(entry).trim();
|
|
11
|
-
if (!text || text === "*")
|
|
12
|
+
if (!text || text === "*") {
|
|
12
13
|
continue;
|
|
14
|
+
}
|
|
13
15
|
const normalized = normalizeDiscordSlug(text);
|
|
14
16
|
const maybeId = text.replace(/^<@!?/, "").replace(/>$/, "");
|
|
15
17
|
if (/^\d+$/.test(maybeId)) {
|
|
@@ -19,8 +21,9 @@ export function normalizeDiscordAllowList(raw, prefixes) {
|
|
|
19
21
|
const prefix = prefixes.find((entry) => text.startsWith(entry));
|
|
20
22
|
if (prefix) {
|
|
21
23
|
const candidate = text.slice(prefix.length);
|
|
22
|
-
if (candidate)
|
|
24
|
+
if (candidate) {
|
|
23
25
|
ids.add(candidate);
|
|
26
|
+
}
|
|
24
27
|
continue;
|
|
25
28
|
}
|
|
26
29
|
if (normalized) {
|
|
@@ -38,15 +41,19 @@ export function normalizeDiscordSlug(value) {
|
|
|
38
41
|
.replace(/^-+|-+$/g, "");
|
|
39
42
|
}
|
|
40
43
|
export function allowListMatches(list, candidate) {
|
|
41
|
-
if (list.allowAll)
|
|
44
|
+
if (list.allowAll) {
|
|
42
45
|
return true;
|
|
43
|
-
|
|
46
|
+
}
|
|
47
|
+
if (candidate.id && list.ids.has(candidate.id)) {
|
|
44
48
|
return true;
|
|
49
|
+
}
|
|
45
50
|
const slug = candidate.name ? normalizeDiscordSlug(candidate.name) : "";
|
|
46
|
-
if (slug && list.names.has(slug))
|
|
51
|
+
if (slug && list.names.has(slug)) {
|
|
47
52
|
return true;
|
|
48
|
-
|
|
53
|
+
}
|
|
54
|
+
if (candidate.tag && list.names.has(normalizeDiscordSlug(candidate.tag))) {
|
|
49
55
|
return true;
|
|
56
|
+
}
|
|
50
57
|
return false;
|
|
51
58
|
}
|
|
52
59
|
export function resolveDiscordAllowListMatch(params) {
|
|
@@ -69,25 +76,62 @@ export function resolveDiscordAllowListMatch(params) {
|
|
|
69
76
|
}
|
|
70
77
|
export function resolveDiscordUserAllowed(params) {
|
|
71
78
|
const allowList = normalizeDiscordAllowList(params.allowList, ["discord:", "user:", "pk:"]);
|
|
72
|
-
if (!allowList)
|
|
79
|
+
if (!allowList) {
|
|
73
80
|
return true;
|
|
81
|
+
}
|
|
74
82
|
return allowListMatches(allowList, {
|
|
75
83
|
id: params.userId,
|
|
76
84
|
name: params.userName,
|
|
77
85
|
tag: params.userTag,
|
|
78
86
|
});
|
|
79
87
|
}
|
|
80
|
-
export function
|
|
81
|
-
|
|
88
|
+
export function resolveDiscordRoleAllowed(params) {
|
|
89
|
+
// Role allowlists accept role IDs only. Names are ignored.
|
|
90
|
+
const allowList = normalizeDiscordAllowList(params.allowList, ["role:"]);
|
|
91
|
+
if (!allowList) {
|
|
82
92
|
return true;
|
|
83
|
-
|
|
84
|
-
if (
|
|
93
|
+
}
|
|
94
|
+
if (allowList.allowAll) {
|
|
85
95
|
return true;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
}
|
|
97
|
+
return params.memberRoleIds.some((roleId) => allowList.ids.has(roleId));
|
|
98
|
+
}
|
|
99
|
+
export function resolveDiscordMemberAllowed(params) {
|
|
100
|
+
const hasUserRestriction = Array.isArray(params.userAllowList) && params.userAllowList.length > 0;
|
|
101
|
+
const hasRoleRestriction = Array.isArray(params.roleAllowList) && params.roleAllowList.length > 0;
|
|
102
|
+
if (!hasUserRestriction && !hasRoleRestriction) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
const userOk = hasUserRestriction
|
|
106
|
+
? resolveDiscordUserAllowed({
|
|
107
|
+
allowList: params.userAllowList,
|
|
108
|
+
userId: params.userId,
|
|
109
|
+
userName: params.userName,
|
|
110
|
+
userTag: params.userTag,
|
|
111
|
+
})
|
|
112
|
+
: false;
|
|
113
|
+
const roleOk = hasRoleRestriction
|
|
114
|
+
? resolveDiscordRoleAllowed({
|
|
115
|
+
allowList: params.roleAllowList,
|
|
116
|
+
memberRoleIds: params.memberRoleIds,
|
|
117
|
+
})
|
|
118
|
+
: false;
|
|
119
|
+
return userOk || roleOk;
|
|
120
|
+
}
|
|
121
|
+
export function resolveDiscordMemberAccessState(params) {
|
|
122
|
+
const channelUsers = params.channelConfig?.users ?? params.guildInfo?.users;
|
|
123
|
+
const channelRoles = params.channelConfig?.roles ?? params.guildInfo?.roles;
|
|
124
|
+
const hasAccessRestrictions = (Array.isArray(channelUsers) && channelUsers.length > 0) ||
|
|
125
|
+
(Array.isArray(channelRoles) && channelRoles.length > 0);
|
|
126
|
+
const memberAllowed = resolveDiscordMemberAllowed({
|
|
127
|
+
userAllowList: channelUsers,
|
|
128
|
+
roleAllowList: channelRoles,
|
|
129
|
+
memberRoleIds: params.memberRoleIds,
|
|
130
|
+
userId: params.sender.id,
|
|
131
|
+
userName: params.sender.name,
|
|
132
|
+
userTag: params.sender.tag,
|
|
90
133
|
});
|
|
134
|
+
return { channelUsers, channelRoles, hasAccessRestrictions, memberAllowed };
|
|
91
135
|
}
|
|
92
136
|
export function resolveDiscordOwnerAllowFrom(params) {
|
|
93
137
|
const rawAllowList = params.channelConfig?.users ?? params.guildInfo?.users;
|
|
@@ -111,21 +155,39 @@ export function resolveDiscordOwnerAllowFrom(params) {
|
|
|
111
155
|
}
|
|
112
156
|
return [match.matchKey];
|
|
113
157
|
}
|
|
158
|
+
export function resolveDiscordCommandAuthorized(params) {
|
|
159
|
+
if (!params.isDirectMessage) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
const allowList = normalizeDiscordAllowList(params.allowFrom, ["discord:", "user:", "pk:"]);
|
|
163
|
+
if (!allowList) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return allowListMatches(allowList, {
|
|
167
|
+
id: params.author.id,
|
|
168
|
+
name: params.author.username,
|
|
169
|
+
tag: formatDiscordUserTag(params.author),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
114
172
|
export function resolveDiscordGuildEntry(params) {
|
|
115
173
|
const guild = params.guild;
|
|
116
174
|
const entries = params.guildEntries;
|
|
117
|
-
if (!guild || !entries)
|
|
175
|
+
if (!guild || !entries) {
|
|
118
176
|
return null;
|
|
177
|
+
}
|
|
119
178
|
const byId = entries[guild.id];
|
|
120
|
-
if (byId)
|
|
179
|
+
if (byId) {
|
|
121
180
|
return { ...byId, id: guild.id };
|
|
181
|
+
}
|
|
122
182
|
const slug = normalizeDiscordSlug(guild.name ?? "");
|
|
123
183
|
const bySlug = entries[slug];
|
|
124
|
-
if (bySlug)
|
|
184
|
+
if (bySlug) {
|
|
125
185
|
return { ...bySlug, id: guild.id, slug: slug || bySlug.slug };
|
|
186
|
+
}
|
|
126
187
|
const wildcard = entries["*"];
|
|
127
|
-
if (wildcard)
|
|
188
|
+
if (wildcard) {
|
|
128
189
|
return { ...wildcard, id: guild.id, slug: slug || wildcard.slug };
|
|
190
|
+
}
|
|
129
191
|
return null;
|
|
130
192
|
}
|
|
131
193
|
function buildDiscordChannelKeys(params) {
|
|
@@ -142,6 +204,9 @@ function resolveDiscordChannelEntryMatch(channels, params, parentParams) {
|
|
|
142
204
|
wildcardKey: "*",
|
|
143
205
|
});
|
|
144
206
|
}
|
|
207
|
+
function hasConfiguredDiscordChannels(channels) {
|
|
208
|
+
return Boolean(channels && Object.keys(channels).length > 0);
|
|
209
|
+
}
|
|
145
210
|
function resolveDiscordChannelConfigEntry(entry) {
|
|
146
211
|
const resolved = {
|
|
147
212
|
allowed: entry.allow !== false,
|
|
@@ -149,6 +214,7 @@ function resolveDiscordChannelConfigEntry(entry) {
|
|
|
149
214
|
skills: entry.skills,
|
|
150
215
|
enabled: entry.enabled,
|
|
151
216
|
users: entry.users,
|
|
217
|
+
roles: entry.roles,
|
|
152
218
|
systemPrompt: entry.systemPrompt,
|
|
153
219
|
includeThreadStarter: entry.includeThreadStarter,
|
|
154
220
|
autoThread: entry.autoThread,
|
|
@@ -158,8 +224,9 @@ function resolveDiscordChannelConfigEntry(entry) {
|
|
|
158
224
|
export function resolveDiscordChannelConfig(params) {
|
|
159
225
|
const { guildInfo, channelId, channelName, channelSlug } = params;
|
|
160
226
|
const channels = guildInfo?.channels;
|
|
161
|
-
if (!channels)
|
|
227
|
+
if (!hasConfiguredDiscordChannels(channels)) {
|
|
162
228
|
return null;
|
|
229
|
+
}
|
|
163
230
|
const match = resolveDiscordChannelEntryMatch(channels, {
|
|
164
231
|
id: channelId,
|
|
165
232
|
name: channelName,
|
|
@@ -171,8 +238,9 @@ export function resolveDiscordChannelConfig(params) {
|
|
|
171
238
|
export function resolveDiscordChannelConfigWithFallback(params) {
|
|
172
239
|
const { guildInfo, channelId, channelName, channelSlug, parentId, parentName, parentSlug, scope, } = params;
|
|
173
240
|
const channels = guildInfo?.channels;
|
|
174
|
-
if (!channels)
|
|
241
|
+
if (!hasConfiguredDiscordChannels(channels)) {
|
|
175
242
|
return null;
|
|
243
|
+
}
|
|
176
244
|
const resolvedParentSlug = parentSlug ?? (parentName ? normalizeDiscordSlug(parentName) : "");
|
|
177
245
|
const match = resolveDiscordChannelEntryMatch(channels, {
|
|
178
246
|
id: channelId,
|
|
@@ -189,39 +257,48 @@ export function resolveDiscordChannelConfigWithFallback(params) {
|
|
|
189
257
|
return resolveChannelMatchConfig(match, resolveDiscordChannelConfigEntry) ?? { allowed: false };
|
|
190
258
|
}
|
|
191
259
|
export function resolveDiscordShouldRequireMention(params) {
|
|
192
|
-
if (!params.isGuildMessage)
|
|
260
|
+
if (!params.isGuildMessage) {
|
|
193
261
|
return false;
|
|
262
|
+
}
|
|
194
263
|
// Only skip mention requirement in threads created by the bot (when autoThread is enabled).
|
|
195
264
|
const isBotThread = params.isAutoThreadOwnedByBot ?? isDiscordAutoThreadOwnedByBot(params);
|
|
196
|
-
if (isBotThread)
|
|
265
|
+
if (isBotThread) {
|
|
197
266
|
return false;
|
|
267
|
+
}
|
|
198
268
|
return params.channelConfig?.requireMention ?? params.guildInfo?.requireMention ?? true;
|
|
199
269
|
}
|
|
200
270
|
export function isDiscordAutoThreadOwnedByBot(params) {
|
|
201
|
-
if (!params.isThread)
|
|
271
|
+
if (!params.isThread) {
|
|
202
272
|
return false;
|
|
203
|
-
|
|
273
|
+
}
|
|
274
|
+
if (!params.channelConfig?.autoThread) {
|
|
204
275
|
return false;
|
|
276
|
+
}
|
|
205
277
|
const botId = params.botId?.trim();
|
|
206
278
|
const threadOwnerId = params.threadOwnerId?.trim();
|
|
207
279
|
return Boolean(botId && threadOwnerId && botId === threadOwnerId);
|
|
208
280
|
}
|
|
209
281
|
export function isDiscordGroupAllowedByPolicy(params) {
|
|
210
282
|
const { groupPolicy, guildAllowlisted, channelAllowlistConfigured, channelAllowed } = params;
|
|
211
|
-
if (groupPolicy === "disabled")
|
|
283
|
+
if (groupPolicy === "disabled") {
|
|
212
284
|
return false;
|
|
213
|
-
|
|
285
|
+
}
|
|
286
|
+
if (groupPolicy === "open") {
|
|
214
287
|
return true;
|
|
215
|
-
|
|
288
|
+
}
|
|
289
|
+
if (!guildAllowlisted) {
|
|
216
290
|
return false;
|
|
217
|
-
|
|
291
|
+
}
|
|
292
|
+
if (!channelAllowlistConfigured) {
|
|
218
293
|
return true;
|
|
294
|
+
}
|
|
219
295
|
return channelAllowed;
|
|
220
296
|
}
|
|
221
297
|
export function resolveGroupDmAllow(params) {
|
|
222
298
|
const { channels, channelId, channelName, channelSlug } = params;
|
|
223
|
-
if (!channels || channels.length === 0)
|
|
299
|
+
if (!channels || channels.length === 0) {
|
|
224
300
|
return true;
|
|
301
|
+
}
|
|
225
302
|
const allowList = new Set(channels.map((entry) => normalizeDiscordSlug(String(entry))));
|
|
226
303
|
const candidates = [
|
|
227
304
|
normalizeDiscordSlug(channelId),
|
|
@@ -232,17 +309,20 @@ export function resolveGroupDmAllow(params) {
|
|
|
232
309
|
}
|
|
233
310
|
export function shouldEmitDiscordReactionNotification(params) {
|
|
234
311
|
const mode = params.mode ?? "own";
|
|
235
|
-
if (mode === "off")
|
|
312
|
+
if (mode === "off") {
|
|
236
313
|
return false;
|
|
237
|
-
|
|
314
|
+
}
|
|
315
|
+
if (mode === "all") {
|
|
238
316
|
return true;
|
|
317
|
+
}
|
|
239
318
|
if (mode === "own") {
|
|
240
319
|
return Boolean(params.botId && params.messageAuthorId === params.botId);
|
|
241
320
|
}
|
|
242
321
|
if (mode === "allowlist") {
|
|
243
322
|
const list = normalizeDiscordAllowList(params.allowlist, ["discord:", "user:", "pk:"]);
|
|
244
|
-
if (!list)
|
|
323
|
+
if (!list) {
|
|
245
324
|
return false;
|
|
325
|
+
}
|
|
246
326
|
return allowListMatches(list, {
|
|
247
327
|
id: params.userId,
|
|
248
328
|
name: params.userName,
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { ChannelType, MessageCreateListener, MessageReactionAddListener, MessageReactionRemoveListener, PresenceUpdateListener, } from "@buape/carbon";
|
|
2
2
|
import { danger } from "../../globals.js";
|
|
3
|
-
import { formatDurationSeconds } from "../../infra/format-duration.js";
|
|
3
|
+
import { formatDurationSeconds } from "../../infra/format-time/format-duration.js";
|
|
4
4
|
import { enqueueSystemEvent } from "../../infra/system-events.js";
|
|
5
|
-
import { setPresence } from "./presence-cache.js";
|
|
6
5
|
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
|
7
6
|
import { resolveAgentRoute } from "../../routing/resolve-route.js";
|
|
8
7
|
import { normalizeDiscordSlug, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, shouldEmitDiscordReactionNotification, } from "./allow-list.js";
|
|
9
8
|
import { formatDiscordReactionEmoji, formatDiscordUserTag } from "./format.js";
|
|
10
9
|
import { resolveDiscordChannelInfo } from "./message-utils.js";
|
|
10
|
+
import { setPresence } from "./presence-cache.js";
|
|
11
11
|
const DISCORD_SLOW_LISTENER_THRESHOLD_MS = 30_000;
|
|
12
12
|
const discordEventQueueLog = createSubsystemLogger("discord/event-queue");
|
|
13
13
|
function logSlowDiscordListener(params) {
|
|
14
|
-
if (params.durationMs < DISCORD_SLOW_LISTENER_THRESHOLD_MS)
|
|
14
|
+
if (params.durationMs < DISCORD_SLOW_LISTENER_THRESHOLD_MS) {
|
|
15
15
|
return;
|
|
16
|
+
}
|
|
16
17
|
const duration = formatDurationSeconds(params.durationMs, {
|
|
17
18
|
decimals: 1,
|
|
18
19
|
unit: "seconds",
|
|
@@ -67,27 +68,14 @@ export class DiscordReactionListener extends MessageReactionAddListener {
|
|
|
67
68
|
this.params = params;
|
|
68
69
|
}
|
|
69
70
|
async handle(data, client) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
botUserId: this.params.botUserId,
|
|
79
|
-
guildEntries: this.params.guildEntries,
|
|
80
|
-
logger: this.params.logger,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
logSlowDiscordListener({
|
|
85
|
-
logger: this.params.logger,
|
|
86
|
-
listener: this.constructor.name,
|
|
87
|
-
event: this.type,
|
|
88
|
-
durationMs: Date.now() - startedAt,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
71
|
+
await runDiscordReactionHandler({
|
|
72
|
+
data,
|
|
73
|
+
client,
|
|
74
|
+
action: "added",
|
|
75
|
+
handlerParams: this.params,
|
|
76
|
+
listener: this.constructor.name,
|
|
77
|
+
event: this.type,
|
|
78
|
+
});
|
|
91
79
|
}
|
|
92
80
|
}
|
|
93
81
|
export class DiscordReactionRemoveListener extends MessageReactionRemoveListener {
|
|
@@ -97,69 +85,200 @@ export class DiscordReactionRemoveListener extends MessageReactionRemoveListener
|
|
|
97
85
|
this.params = params;
|
|
98
86
|
}
|
|
99
87
|
async handle(data, client) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
88
|
+
await runDiscordReactionHandler({
|
|
89
|
+
data,
|
|
90
|
+
client,
|
|
91
|
+
action: "removed",
|
|
92
|
+
handlerParams: this.params,
|
|
93
|
+
listener: this.constructor.name,
|
|
94
|
+
event: this.type,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function runDiscordReactionHandler(params) {
|
|
99
|
+
const startedAt = Date.now();
|
|
100
|
+
try {
|
|
101
|
+
await handleDiscordReactionEvent({
|
|
102
|
+
data: params.data,
|
|
103
|
+
client: params.client,
|
|
104
|
+
action: params.action,
|
|
105
|
+
cfg: params.handlerParams.cfg,
|
|
106
|
+
accountId: params.handlerParams.accountId,
|
|
107
|
+
botUserId: params.handlerParams.botUserId,
|
|
108
|
+
guildEntries: params.handlerParams.guildEntries,
|
|
109
|
+
logger: params.handlerParams.logger,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
logSlowDiscordListener({
|
|
114
|
+
logger: params.handlerParams.logger,
|
|
115
|
+
listener: params.listener,
|
|
116
|
+
event: params.event,
|
|
117
|
+
durationMs: Date.now() - startedAt,
|
|
118
|
+
});
|
|
121
119
|
}
|
|
122
120
|
}
|
|
123
121
|
async function handleDiscordReactionEvent(params) {
|
|
124
122
|
try {
|
|
125
123
|
const { data, client, action, botUserId, guildEntries } = params;
|
|
126
|
-
if (!("user" in data))
|
|
124
|
+
if (!("user" in data)) {
|
|
127
125
|
return;
|
|
126
|
+
}
|
|
128
127
|
const user = data.user;
|
|
129
|
-
if (!user || user.bot)
|
|
128
|
+
if (!user || user.bot) {
|
|
130
129
|
return;
|
|
131
|
-
|
|
130
|
+
}
|
|
131
|
+
// Early exit: skip bot's own reactions before expensive network calls
|
|
132
|
+
if (botUserId && user.id === botUserId) {
|
|
132
133
|
return;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
}
|
|
135
|
+
const isGuildMessage = Boolean(data.guild_id);
|
|
136
|
+
const guildInfo = isGuildMessage
|
|
137
|
+
? resolveDiscordGuildEntry({
|
|
138
|
+
guild: data.guild ?? undefined,
|
|
139
|
+
guildEntries,
|
|
140
|
+
})
|
|
141
|
+
: null;
|
|
142
|
+
if (isGuildMessage && guildEntries && Object.keys(guildEntries).length > 0 && !guildInfo) {
|
|
138
143
|
return;
|
|
139
144
|
}
|
|
140
145
|
const channel = await client.fetchChannel(data.channel_id);
|
|
141
|
-
if (!channel)
|
|
146
|
+
if (!channel) {
|
|
142
147
|
return;
|
|
148
|
+
}
|
|
143
149
|
const channelName = "name" in channel ? (channel.name ?? undefined) : undefined;
|
|
144
150
|
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
|
145
151
|
const channelType = "type" in channel ? channel.type : undefined;
|
|
152
|
+
const isDirectMessage = channelType === ChannelType.DM;
|
|
153
|
+
const isGroupDm = channelType === ChannelType.GroupDM;
|
|
146
154
|
const isThreadChannel = channelType === ChannelType.PublicThread ||
|
|
147
155
|
channelType === ChannelType.PrivateThread ||
|
|
148
156
|
channelType === ChannelType.AnnouncementThread;
|
|
149
157
|
let parentId = "parentId" in channel ? (channel.parentId ?? undefined) : undefined;
|
|
150
158
|
let parentName;
|
|
151
159
|
let parentSlug = "";
|
|
152
|
-
|
|
160
|
+
const memberRoleIds = Array.isArray(data.rawMember?.roles)
|
|
161
|
+
? data.rawMember.roles.map((roleId) => String(roleId))
|
|
162
|
+
: [];
|
|
163
|
+
let reactionBase = null;
|
|
164
|
+
const resolveReactionBase = () => {
|
|
165
|
+
if (reactionBase) {
|
|
166
|
+
return reactionBase;
|
|
167
|
+
}
|
|
168
|
+
const emojiLabel = formatDiscordReactionEmoji(data.emoji);
|
|
169
|
+
const actorLabel = formatDiscordUserTag(user);
|
|
170
|
+
const guildSlug = guildInfo?.slug ||
|
|
171
|
+
(data.guild?.name
|
|
172
|
+
? normalizeDiscordSlug(data.guild.name)
|
|
173
|
+
: (data.guild_id ?? (isGroupDm ? "group-dm" : "dm")));
|
|
174
|
+
const channelLabel = channelSlug
|
|
175
|
+
? `#${channelSlug}`
|
|
176
|
+
: channelName
|
|
177
|
+
? `#${normalizeDiscordSlug(channelName)}`
|
|
178
|
+
: `#${data.channel_id}`;
|
|
179
|
+
const baseText = `Discord reaction ${action}: ${emojiLabel} by ${actorLabel} on ${guildSlug} ${channelLabel} msg ${data.message_id}`;
|
|
180
|
+
const contextKey = `discord:reaction:${action}:${data.message_id}:${user.id}:${emojiLabel}`;
|
|
181
|
+
reactionBase = { baseText, contextKey };
|
|
182
|
+
return reactionBase;
|
|
183
|
+
};
|
|
184
|
+
const emitReaction = (text, parentPeerId) => {
|
|
185
|
+
const { contextKey } = resolveReactionBase();
|
|
186
|
+
const route = resolveAgentRoute({
|
|
187
|
+
cfg: params.cfg,
|
|
188
|
+
channel: "discord",
|
|
189
|
+
accountId: params.accountId,
|
|
190
|
+
guildId: data.guild_id ?? undefined,
|
|
191
|
+
memberRoleIds,
|
|
192
|
+
peer: {
|
|
193
|
+
kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
|
|
194
|
+
id: isDirectMessage ? user.id : data.channel_id,
|
|
195
|
+
},
|
|
196
|
+
parentPeer: parentPeerId ? { kind: "channel", id: parentPeerId } : undefined,
|
|
197
|
+
});
|
|
198
|
+
enqueueSystemEvent(text, {
|
|
199
|
+
sessionKey: route.sessionKey,
|
|
200
|
+
contextKey,
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
const shouldNotifyReaction = (options) => shouldEmitDiscordReactionNotification({
|
|
204
|
+
mode: options.mode,
|
|
205
|
+
botId: botUserId,
|
|
206
|
+
messageAuthorId: options.messageAuthorId,
|
|
207
|
+
userId: user.id,
|
|
208
|
+
userName: user.username,
|
|
209
|
+
userTag: formatDiscordUserTag(user),
|
|
210
|
+
allowlist: guildInfo?.users,
|
|
211
|
+
});
|
|
212
|
+
const emitReactionWithAuthor = (message) => {
|
|
213
|
+
const { baseText } = resolveReactionBase();
|
|
214
|
+
const authorLabel = message?.author ? formatDiscordUserTag(message.author) : undefined;
|
|
215
|
+
const text = authorLabel ? `${baseText} from ${authorLabel}` : baseText;
|
|
216
|
+
emitReaction(text, parentId);
|
|
217
|
+
};
|
|
218
|
+
const loadThreadParentInfo = async () => {
|
|
153
219
|
if (!parentId) {
|
|
154
|
-
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const parentInfo = await resolveDiscordChannelInfo(client, parentId);
|
|
223
|
+
parentName = parentInfo?.name;
|
|
224
|
+
parentSlug = parentName ? normalizeDiscordSlug(parentName) : "";
|
|
225
|
+
};
|
|
226
|
+
const resolveThreadChannelConfig = () => resolveDiscordChannelConfigWithFallback({
|
|
227
|
+
guildInfo,
|
|
228
|
+
channelId: data.channel_id,
|
|
229
|
+
channelName,
|
|
230
|
+
channelSlug,
|
|
231
|
+
parentId,
|
|
232
|
+
parentName,
|
|
233
|
+
parentSlug,
|
|
234
|
+
scope: "thread",
|
|
235
|
+
});
|
|
236
|
+
// Parallelize async operations for thread channels
|
|
237
|
+
if (isThreadChannel) {
|
|
238
|
+
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
|
239
|
+
// Early exit: skip fetching message if notifications are off
|
|
240
|
+
if (reactionMode === "off") {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const channelInfoPromise = parentId
|
|
244
|
+
? Promise.resolve({ parentId })
|
|
245
|
+
: resolveDiscordChannelInfo(client, data.channel_id);
|
|
246
|
+
// Fast path: for "all" and "allowlist" modes, we don't need to fetch the message
|
|
247
|
+
if (reactionMode === "all" || reactionMode === "allowlist") {
|
|
248
|
+
const channelInfo = await channelInfoPromise;
|
|
155
249
|
parentId = channelInfo?.parentId;
|
|
250
|
+
await loadThreadParentInfo();
|
|
251
|
+
const channelConfig = resolveThreadChannelConfig();
|
|
252
|
+
if (channelConfig?.allowed === false) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// For allowlist mode, check if user is in allowlist first
|
|
256
|
+
if (reactionMode === "allowlist") {
|
|
257
|
+
if (!shouldNotifyReaction({ mode: reactionMode })) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const { baseText } = resolveReactionBase();
|
|
262
|
+
emitReaction(baseText, parentId);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
// For "own" mode, we need to fetch the message to check the author
|
|
266
|
+
const messagePromise = data.message.fetch().catch(() => null);
|
|
267
|
+
const [channelInfo, message] = await Promise.all([channelInfoPromise, messagePromise]);
|
|
268
|
+
parentId = channelInfo?.parentId;
|
|
269
|
+
await loadThreadParentInfo();
|
|
270
|
+
const channelConfig = resolveThreadChannelConfig();
|
|
271
|
+
if (channelConfig?.allowed === false) {
|
|
272
|
+
return;
|
|
156
273
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
parentSlug = parentName ? normalizeDiscordSlug(parentName) : "";
|
|
274
|
+
const messageAuthorId = message?.author?.id ?? undefined;
|
|
275
|
+
if (!shouldNotifyReaction({ mode: reactionMode, messageAuthorId })) {
|
|
276
|
+
return;
|
|
161
277
|
}
|
|
278
|
+
emitReactionWithAuthor(message);
|
|
279
|
+
return;
|
|
162
280
|
}
|
|
281
|
+
// Non-thread channel path
|
|
163
282
|
const channelConfig = resolveDiscordChannelConfigWithFallback({
|
|
164
283
|
guildInfo,
|
|
165
284
|
channelId: data.channel_id,
|
|
@@ -168,48 +287,35 @@ async function handleDiscordReactionEvent(params) {
|
|
|
168
287
|
parentId,
|
|
169
288
|
parentName,
|
|
170
289
|
parentSlug,
|
|
171
|
-
scope:
|
|
290
|
+
scope: "channel",
|
|
172
291
|
});
|
|
173
|
-
if (channelConfig?.allowed === false)
|
|
174
|
-
return;
|
|
175
|
-
if (botUserId && user.id === botUserId)
|
|
292
|
+
if (channelConfig?.allowed === false) {
|
|
176
293
|
return;
|
|
294
|
+
}
|
|
177
295
|
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
|
296
|
+
// Early exit: skip fetching message if notifications are off
|
|
297
|
+
if (reactionMode === "off") {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// Fast path: for "all" and "allowlist" modes, we don't need to fetch the message
|
|
301
|
+
if (reactionMode === "all" || reactionMode === "allowlist") {
|
|
302
|
+
// For allowlist mode, check if user is in allowlist first
|
|
303
|
+
if (reactionMode === "allowlist") {
|
|
304
|
+
if (!shouldNotifyReaction({ mode: reactionMode })) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const { baseText } = resolveReactionBase();
|
|
309
|
+
emitReaction(baseText, parentId);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
// For "own" mode, we need to fetch the message to check the author
|
|
178
313
|
const message = await data.message.fetch().catch(() => null);
|
|
179
314
|
const messageAuthorId = message?.author?.id ?? undefined;
|
|
180
|
-
|
|
181
|
-
mode: reactionMode,
|
|
182
|
-
botId: botUserId,
|
|
183
|
-
messageAuthorId,
|
|
184
|
-
userId: user.id,
|
|
185
|
-
userName: user.username,
|
|
186
|
-
userTag: formatDiscordUserTag(user),
|
|
187
|
-
allowlist: guildInfo?.users,
|
|
188
|
-
});
|
|
189
|
-
if (!shouldNotify)
|
|
315
|
+
if (!shouldNotifyReaction({ mode: reactionMode, messageAuthorId })) {
|
|
190
316
|
return;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const guildSlug = guildInfo?.slug || (data.guild?.name ? normalizeDiscordSlug(data.guild.name) : data.guild_id);
|
|
194
|
-
const channelLabel = channelSlug
|
|
195
|
-
? `#${channelSlug}`
|
|
196
|
-
: channelName
|
|
197
|
-
? `#${normalizeDiscordSlug(channelName)}`
|
|
198
|
-
: `#${data.channel_id}`;
|
|
199
|
-
const authorLabel = message?.author ? formatDiscordUserTag(message.author) : undefined;
|
|
200
|
-
const baseText = `Discord reaction ${action}: ${emojiLabel} by ${actorLabel} on ${guildSlug} ${channelLabel} msg ${data.message_id}`;
|
|
201
|
-
const text = authorLabel ? `${baseText} from ${authorLabel}` : baseText;
|
|
202
|
-
const route = resolveAgentRoute({
|
|
203
|
-
cfg: params.cfg,
|
|
204
|
-
channel: "discord",
|
|
205
|
-
accountId: params.accountId,
|
|
206
|
-
guildId: data.guild_id ?? undefined,
|
|
207
|
-
peer: { kind: "channel", id: data.channel_id },
|
|
208
|
-
});
|
|
209
|
-
enqueueSystemEvent(text, {
|
|
210
|
-
sessionKey: route.sessionKey,
|
|
211
|
-
contextKey: `discord:reaction:${action}:${data.message_id}:${user.id}:${emojiLabel}`,
|
|
212
|
-
});
|
|
317
|
+
}
|
|
318
|
+
emitReactionWithAuthor(message);
|
|
213
319
|
}
|
|
214
320
|
catch (err) {
|
|
215
321
|
params.logger.error(danger(`discord reaction handler failed: ${String(err)}`));
|
|
@@ -228,8 +334,9 @@ export class DiscordPresenceListener extends PresenceUpdateListener {
|
|
|
228
334
|
const userId = "user" in data && data.user && typeof data.user === "object" && "id" in data.user
|
|
229
335
|
? String(data.user.id)
|
|
230
336
|
: undefined;
|
|
231
|
-
if (!userId)
|
|
337
|
+
if (!userId) {
|
|
232
338
|
return;
|
|
339
|
+
}
|
|
233
340
|
setPresence(this.accountId, userId, data);
|
|
234
341
|
}
|
|
235
342
|
catch (err) {
|