opensquid 0.5.441 → 0.5.447
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/README.md +1 -0
- package/dist/functions/arm_scope.d.ts +27 -0
- package/dist/functions/arm_scope.d.ts.map +1 -0
- package/dist/functions/arm_scope.js +52 -0
- package/dist/functions/arm_scope.js.map +1 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -0
- package/dist/functions/index.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +2 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/handoff/render.d.ts +5 -4
- package/dist/runtime/handoff/render.d.ts.map +1 -1
- package/dist/runtime/handoff/render.js +7 -7
- package/dist/runtime/handoff/render.js.map +1 -1
- package/dist/runtime/hooks/active_task_mirror.js +0 -0
- package/dist/runtime/hooks/apply_patch.js +0 -0
- package/dist/runtime/hooks/dispatch.js +0 -0
- package/dist/runtime/hooks/hook_output.js +0 -0
- package/dist/runtime/hooks/memory_reconcile.js +0 -0
- package/dist/runtime/hooks/new_project_detect.js +0 -0
- package/dist/runtime/hooks/profession_resolver.js +0 -0
- package/dist/runtime/hooks/scope_intent.js +0 -0
- package/dist/runtime/hooks/session_id.js +0 -0
- package/dist/runtime/hooks/session_liveness.js +0 -0
- package/dist/runtime/hooks/stop_drive.js +0 -0
- package/dist/runtime/hooks/stop_stream.js +0 -0
- package/dist/runtime/hooks/subagent_guard.js +0 -0
- package/dist/runtime/hooks/transcript.js +0 -0
- package/dist/runtime/hooks/transcript_tasks.js +0 -0
- package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
- package/dist/runtime/ralph/orchestrator.js +2 -1
- package/dist/runtime/ralph/orchestrator.js.map +1 -1
- package/dist/setup/cli/limits_state.d.ts.map +1 -1
- package/dist/setup/cli/limits_state.js +6 -40
- package/dist/setup/cli/limits_state.js.map +1 -1
- package/dist/setup/cli/pack_walk.d.ts +32 -0
- package/dist/setup/cli/pack_walk.d.ts.map +1 -0
- package/dist/setup/cli/pack_walk.js +76 -0
- package/dist/setup/cli/pack_walk.js.map +1 -0
- package/dist/setup/cli/permissions_state.d.ts.map +1 -1
- package/dist/setup/cli/permissions_state.js +6 -37
- package/dist/setup/cli/permissions_state.js.map +1 -1
- package/dist/setup/cli/triggers_state.d.ts.map +1 -1
- package/dist/setup/cli/triggers_state.js +3 -29
- package/dist/setup/cli/triggers_state.js.map +1 -1
- package/dist/workgraph/events.d.ts.map +1 -1
- package/dist/workgraph/events.js +10 -0
- package/dist/workgraph/events.js.map +1 -1
- package/dist/workgraph/store.d.ts.map +1 -1
- package/dist/workgraph/store.js +5 -0
- package/dist/workgraph/store.js.map +1 -1
- package/dist/workgraph/types.d.ts +2 -1
- package/dist/workgraph/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +268 -0
- package/package.json +5 -3
- package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
- package/dist/anti-drift/evaluator.d.ts +0 -88
- package/dist/anti-drift/evaluator.d.ts.map +0 -1
- package/dist/anti-drift/evaluator.js +0 -417
- package/dist/anti-drift/evaluator.js.map +0 -1
- package/dist/anti-drift/evaluator.test.js +0 -78
- package/dist/anti-drift/rules.d.ts +0 -80
- package/dist/anti-drift/rules.d.ts.map +0 -1
- package/dist/anti-drift/rules.js +0 -368
- package/dist/anti-drift/rules.js.map +0 -1
- package/dist/anti-drift/rules.test.js +0 -213
- package/dist/anti-drift/state.d.ts +0 -107
- package/dist/anti-drift/state.d.ts.map +0 -1
- package/dist/anti-drift/state.js +0 -177
- package/dist/anti-drift/state.js.map +0 -1
- package/dist/anti-drift/state.test.js +0 -120
- package/dist/chat/adapters/discord.d.ts +0 -41
- package/dist/chat/adapters/discord.d.ts.map +0 -1
- package/dist/chat/adapters/discord.js +0 -176
- package/dist/chat/adapters/discord.js.map +0 -1
- package/dist/chat/adapters/discord.test.js +0 -25
- package/dist/chat/adapters/slack.d.ts +0 -43
- package/dist/chat/adapters/slack.d.ts.map +0 -1
- package/dist/chat/adapters/slack.js +0 -172
- package/dist/chat/adapters/slack.js.map +0 -1
- package/dist/chat/adapters/slack.test.js +0 -30
- package/dist/chat/adapters/telegram.d.ts +0 -148
- package/dist/chat/adapters/telegram.d.ts.map +0 -1
- package/dist/chat/adapters/telegram.js +0 -498
- package/dist/chat/adapters/telegram.js.map +0 -1
- package/dist/chat/adapters/telegram.test.js +0 -94
- package/dist/chat/config.d.ts +0 -98
- package/dist/chat/config.d.ts.map +0 -1
- package/dist/chat/config.js +0 -185
- package/dist/chat/config.js.map +0 -1
- package/dist/chat/daemon/active-project.d.ts +0 -17
- package/dist/chat/daemon/active-project.d.ts.map +0 -1
- package/dist/chat/daemon/active-project.js +0 -23
- package/dist/chat/daemon/active-project.js.map +0 -1
- package/dist/chat/daemon/autospawn.d.ts +0 -40
- package/dist/chat/daemon/autospawn.d.ts.map +0 -1
- package/dist/chat/daemon/autospawn.js +0 -129
- package/dist/chat/daemon/autospawn.js.map +0 -1
- package/dist/chat/daemon/autospawn.test.js +0 -112
- package/dist/chat/daemon/cli.d.ts +0 -18
- package/dist/chat/daemon/cli.d.ts.map +0 -1
- package/dist/chat/daemon/cli.js +0 -71
- package/dist/chat/daemon/cli.js.map +0 -1
- package/dist/chat/daemon/collisions.js +0 -384
- package/dist/chat/daemon/health-check.d.ts +0 -69
- package/dist/chat/daemon/health-check.d.ts.map +0 -1
- package/dist/chat/daemon/health-check.js +0 -112
- package/dist/chat/daemon/health-check.js.map +0 -1
- package/dist/chat/daemon/inbox-read.d.ts +0 -35
- package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
- package/dist/chat/daemon/inbox-read.js +0 -75
- package/dist/chat/daemon/inbox-read.js.map +0 -1
- package/dist/chat/daemon/inbox-read.test.js +0 -97
- package/dist/chat/daemon/inbox.d.ts +0 -63
- package/dist/chat/daemon/inbox.d.ts.map +0 -1
- package/dist/chat/daemon/inbox.js +0 -56
- package/dist/chat/daemon/inbox.js.map +0 -1
- package/dist/chat/daemon/inbox.test.js +0 -110
- package/dist/chat/daemon/lifecycle.d.ts +0 -71
- package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
- package/dist/chat/daemon/lifecycle.js +0 -221
- package/dist/chat/daemon/lifecycle.js.map +0 -1
- package/dist/chat/daemon/lifecycle.test.js +0 -163
- package/dist/chat/daemon/protocol.d.ts +0 -107
- package/dist/chat/daemon/protocol.d.ts.map +0 -1
- package/dist/chat/daemon/protocol.js +0 -54
- package/dist/chat/daemon/protocol.js.map +0 -1
- package/dist/chat/daemon/routing.d.ts +0 -140
- package/dist/chat/daemon/routing.d.ts.map +0 -1
- package/dist/chat/daemon/routing.js +0 -198
- package/dist/chat/daemon/routing.js.map +0 -1
- package/dist/chat/daemon/routing.test.js +0 -259
- package/dist/chat/daemon/rpc-client.d.ts +0 -45
- package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-client.js +0 -133
- package/dist/chat/daemon/rpc-client.js.map +0 -1
- package/dist/chat/daemon/rpc-server.d.ts +0 -39
- package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-server.js +0 -385
- package/dist/chat/daemon/rpc-server.js.map +0 -1
- package/dist/chat/daemon/rpc.test.js +0 -177
- package/dist/chat/daemon/subscribers.js +0 -257
- package/dist/chat/daemon/worker.d.ts +0 -27
- package/dist/chat/daemon/worker.d.ts.map +0 -1
- package/dist/chat/daemon/worker.js +0 -313
- package/dist/chat/daemon/worker.js.map +0 -1
- package/dist/chat/daemon/workspace-topic.js +0 -324
- package/dist/chat/env-token.d.ts +0 -60
- package/dist/chat/env-token.d.ts.map +0 -1
- package/dist/chat/env-token.js +0 -137
- package/dist/chat/env-token.js.map +0 -1
- package/dist/chat/env-token.test.js +0 -160
- package/dist/chat/factory.d.ts +0 -30
- package/dist/chat/factory.d.ts.map +0 -1
- package/dist/chat/factory.js +0 -50
- package/dist/chat/factory.js.map +0 -1
- package/dist/chat/factory.test.js +0 -55
- package/dist/chat/gateway.d.ts +0 -176
- package/dist/chat/gateway.d.ts.map +0 -1
- package/dist/chat/gateway.js +0 -146
- package/dist/chat/gateway.js.map +0 -1
- package/dist/chat/gateway.test.js +0 -192
- package/dist/claude-md.d.ts +0 -39
- package/dist/claude-md.d.ts.map +0 -1
- package/dist/claude-md.js +0 -113
- package/dist/claude-md.js.map +0 -1
- package/dist/claude-md.test.js +0 -91
- package/dist/codex/activate.d.ts +0 -66
- package/dist/codex/activate.d.ts.map +0 -1
- package/dist/codex/activate.js +0 -329
- package/dist/codex/activate.js.map +0 -1
- package/dist/codex/activate.test.js +0 -229
- package/dist/codex/bundled-default/bundled-default.test.js +0 -161
- package/dist/codex/cli-publish.test.js +0 -133
- package/dist/codex/cli.d.ts +0 -35
- package/dist/codex/cli.d.ts.map +0 -1
- package/dist/codex/cli.js +0 -554
- package/dist/codex/cli.js.map +0 -1
- package/dist/codex/cli.test.js +0 -277
- package/dist/codex/import-skill-md.d.ts +0 -53
- package/dist/codex/import-skill-md.d.ts.map +0 -1
- package/dist/codex/import-skill-md.js +0 -236
- package/dist/codex/import-skill-md.js.map +0 -1
- package/dist/codex/import-skill-md.test.js +0 -225
- package/dist/codex/loader.d.ts +0 -27
- package/dist/codex/loader.d.ts.map +0 -1
- package/dist/codex/loader.js +0 -86
- package/dist/codex/loader.js.map +0 -1
- package/dist/codex/loader.test.js +0 -75
- package/dist/codex/parse.d.ts +0 -28
- package/dist/codex/parse.d.ts.map +0 -1
- package/dist/codex/parse.js +0 -309
- package/dist/codex/parse.js.map +0 -1
- package/dist/codex/parse.test.js +0 -241
- package/dist/codex/store.d.ts +0 -87
- package/dist/codex/store.d.ts.map +0 -1
- package/dist/codex/store.js +0 -205
- package/dist/codex/store.js.map +0 -1
- package/dist/codex/store.test.js +0 -242
- package/dist/codex/types.d.ts +0 -398
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -21
- package/dist/codex/types.js.map +0 -1
- package/dist/config.d.ts +0 -53
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -202
- package/dist/config.js.map +0 -1
- package/dist/config.test.js +0 -117
- package/dist/engine/cli.d.ts +0 -14
- package/dist/engine/cli.d.ts.map +0 -1
- package/dist/engine/cli.js +0 -171
- package/dist/engine/cli.js.map +0 -1
- package/dist/engine/client.d.ts +0 -219
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js +0 -312
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/config.d.ts +0 -62
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js +0 -223
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/index.d.ts +0 -17
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -16
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/resolver.d.ts +0 -62
- package/dist/engine/resolver.d.ts.map +0 -1
- package/dist/engine/resolver.js +0 -103
- package/dist/engine/resolver.js.map +0 -1
- package/dist/engine/singleton.d.ts +0 -95
- package/dist/engine/singleton.d.ts.map +0 -1
- package/dist/engine/singleton.js +0 -325
- package/dist/engine/singleton.js.map +0 -1
- package/dist/engine/types.d.ts +0 -402
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -22
- package/dist/engine/types.js.map +0 -1
- package/dist/engine-binary-resolver.js +0 -110
- package/dist/engine-binary-resolver.test.js +0 -61
- package/dist/engine-cli.js +0 -60
- package/dist/engine-client.js +0 -301
- package/dist/engine-client.test.js +0 -118
- package/dist/functions/chain_state.d.ts +0 -51
- package/dist/functions/chain_state.d.ts.map +0 -1
- package/dist/functions/chain_state.js +0 -59
- package/dist/functions/chain_state.js.map +0 -1
- package/dist/hooks/drift-catalog.d.ts +0 -68
- package/dist/hooks/drift-catalog.d.ts.map +0 -1
- package/dist/hooks/drift-catalog.js +0 -184
- package/dist/hooks/drift-catalog.js.map +0 -1
- package/dist/hooks/drift-catalog.test.js +0 -154
- package/dist/hooks/drift-patterns.d.ts +0 -110
- package/dist/hooks/drift-patterns.d.ts.map +0 -1
- package/dist/hooks/drift-patterns.js +0 -289
- package/dist/hooks/drift-patterns.js.map +0 -1
- package/dist/hooks/drift-patterns.test.js +0 -325
- package/dist/hooks/engine-vocab-gate.d.ts +0 -108
- package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
- package/dist/hooks/engine-vocab-gate.js +0 -225
- package/dist/hooks/engine-vocab-gate.js.map +0 -1
- package/dist/hooks/engine-vocab-gate.test.js +0 -170
- package/dist/hooks/heartbeat.d.ts +0 -107
- package/dist/hooks/heartbeat.d.ts.map +0 -1
- package/dist/hooks/heartbeat.js +0 -316
- package/dist/hooks/heartbeat.js.map +0 -1
- package/dist/hooks/heartbeat.test.js +0 -393
- package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
- package/dist/hooks/honesty-ledger.d.ts +0 -123
- package/dist/hooks/honesty-ledger.d.ts.map +0 -1
- package/dist/hooks/honesty-ledger.js +0 -226
- package/dist/hooks/honesty-ledger.js.map +0 -1
- package/dist/hooks/honesty-ledger.test.js +0 -466
- package/dist/hooks/inline-report-check.d.ts +0 -63
- package/dist/hooks/inline-report-check.d.ts.map +0 -1
- package/dist/hooks/inline-report-check.js +0 -88
- package/dist/hooks/inline-report-check.js.map +0 -1
- package/dist/hooks/inline-report-check.test.js +0 -96
- package/dist/hooks/pre-tool-use.d.ts +0 -62
- package/dist/hooks/pre-tool-use.d.ts.map +0 -1
- package/dist/hooks/pre-tool-use.js +0 -342
- package/dist/hooks/pre-tool-use.js.map +0 -1
- package/dist/hooks/pre-tool-use.test.js +0 -134
- package/dist/hooks/session-end.d.ts +0 -15
- package/dist/hooks/session-end.d.ts.map +0 -1
- package/dist/hooks/session-end.js +0 -60
- package/dist/hooks/session-end.js.map +0 -1
- package/dist/hooks/session-end.test.js +0 -52
- package/dist/hooks/stop.d.ts +0 -35
- package/dist/hooks/stop.d.ts.map +0 -1
- package/dist/hooks/stop.js +0 -136
- package/dist/hooks/stop.js.map +0 -1
- package/dist/hooks/transcript-active-task.test.js +0 -342
- package/dist/hooks/transcript.d.ts +0 -26
- package/dist/hooks/transcript.d.ts.map +0 -1
- package/dist/hooks/transcript.js +0 -266
- package/dist/hooks/transcript.js.map +0 -1
- package/dist/hooks/transcript.test.js +0 -103
- package/dist/hooks/user-prompt-submit.d.ts +0 -74
- package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
- package/dist/hooks/user-prompt-submit.js +0 -256
- package/dist/hooks/user-prompt-submit.js.map +0 -1
- package/dist/hooks/user-prompt-submit.test.js +0 -118
- package/dist/hooks/versioning-gate.d.ts +0 -101
- package/dist/hooks/versioning-gate.d.ts.map +0 -1
- package/dist/hooks/versioning-gate.js +0 -245
- package/dist/hooks/versioning-gate.js.map +0 -1
- package/dist/hooks/versioning-gate.test.js +0 -368
- package/dist/hooks/workflow-gate.d.ts +0 -64
- package/dist/hooks/workflow-gate.d.ts.map +0 -1
- package/dist/hooks/workflow-gate.js +0 -152
- package/dist/hooks/workflow-gate.js.map +0 -1
- package/dist/hooks/workflow-gate.test.js +0 -197
- package/dist/hooks-cli.d.ts +0 -25
- package/dist/hooks-cli.d.ts.map +0 -1
- package/dist/hooks-cli.js +0 -286
- package/dist/hooks-cli.js.map +0 -1
- package/dist/hooks-cli.test.js +0 -148
- package/dist/origin.d.ts +0 -16
- package/dist/origin.d.ts.map +0 -1
- package/dist/origin.js +0 -92
- package/dist/origin.js.map +0 -1
- package/dist/packs/seed_lessons_ingest.d.ts +0 -30
- package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
- package/dist/packs/seed_lessons_ingest.js +0 -107
- package/dist/packs/seed_lessons_ingest.js.map +0 -1
- package/dist/project-cli.d.ts +0 -7
- package/dist/project-cli.d.ts.map +0 -1
- package/dist/project-cli.js +0 -145
- package/dist/project-cli.js.map +0 -1
- package/dist/project.d.ts +0 -127
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -281
- package/dist/project.js.map +0 -1
- package/dist/project.test.js +0 -287
- package/dist/rag/backends/loop_engine.d.ts +0 -61
- package/dist/rag/backends/loop_engine.d.ts.map +0 -1
- package/dist/rag/backends/loop_engine.js +0 -160
- package/dist/rag/backends/loop_engine.js.map +0 -1
- package/dist/recall.d.ts +0 -82
- package/dist/recall.d.ts.map +0 -1
- package/dist/recall.js +0 -81
- package/dist/recall.js.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
- package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.js +0 -251
- package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
- package/dist/runtime/chain_state.d.ts +0 -124
- package/dist/runtime/chain_state.d.ts.map +0 -1
- package/dist/runtime/chain_state.js +0 -189
- package/dist/runtime/chain_state.js.map +0 -1
- package/dist/runtime/hooks/permission_decision.d.ts +0 -34
- package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
- package/dist/runtime/hooks/permission_decision.js +0 -39
- package/dist/runtime/hooks/permission_decision.js.map +0 -1
- package/dist/runtime/workflow_fsm.d.ts +0 -21
- package/dist/runtime/workflow_fsm.d.ts.map +0 -1
- package/dist/runtime/workflow_fsm.js +0 -25
- package/dist/runtime/workflow_fsm.js.map +0 -1
- package/dist/runtime/workflow_map.d.ts +0 -26
- package/dist/runtime/workflow_map.d.ts.map +0 -1
- package/dist/runtime/workflow_map.js +0 -38
- package/dist/runtime/workflow_map.js.map +0 -1
- package/dist/scope.d.ts +0 -48
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -111
- package/dist/scope.js.map +0 -1
- package/dist/setup/cli/topic_create_step.d.ts +0 -84
- package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
- package/dist/setup/cli/topic_create_step.js +0 -213
- package/dist/setup/cli/topic_create_step.js.map +0 -1
- package/dist/system-export.d.ts +0 -65
- package/dist/system-export.d.ts.map +0 -1
- package/dist/system-export.js +0 -194
- package/dist/system-export.js.map +0 -1
- package/dist/utterance/classifier.d.ts +0 -53
- package/dist/utterance/classifier.d.ts.map +0 -1
- package/dist/utterance/classifier.js +0 -184
- package/dist/utterance/classifier.js.map +0 -1
- package/dist/utterance/classifier.test.js +0 -147
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Telegram adapter — long-polling via the official Bot API (v0.7a).
|
|
3
|
-
*
|
|
4
|
-
* Rebuild path when you edit this file: `pnpm build` does NOT recompile
|
|
5
|
-
* src.legacy/ (rootDir=src per tsconfig.build.json), but the chat-daemon
|
|
6
|
-
* worker loads `dist/chat/adapters/telegram.js` at runtime. After
|
|
7
|
-
* editing, regenerate the dist file with:
|
|
8
|
-
*
|
|
9
|
-
* pnpm exec tsc src.legacy/chat/adapters/telegram.ts \
|
|
10
|
-
* --outDir dist --rootDir src.legacy \
|
|
11
|
-
* --module NodeNext --moduleResolution NodeNext --target ES2022 \
|
|
12
|
-
* --esModuleInterop --skipLibCheck --noEmitOnError false
|
|
13
|
-
*
|
|
14
|
-
* Then `node ./dist/index.js chat-daemon restart` to load the new code.
|
|
15
|
-
* Without the restart, sends keep using the old in-memory adapter.
|
|
16
|
-
*
|
|
17
|
-
* SDK: `grammy` (modern, TS-first, actively maintained — recommended
|
|
18
|
-
* over `telegraf` which is frozen at v4.16 from Feb 2024 and over
|
|
19
|
-
* `node-telegram-bot-api` which is untyped).
|
|
20
|
-
*
|
|
21
|
-
* Connection: long-polling via `bot.start()` — no public webhook URL
|
|
22
|
-
* required. The bot opens an outbound HTTPS connection to
|
|
23
|
-
* `api.telegram.org` and polls for updates. Works behind any NAT.
|
|
24
|
-
*
|
|
25
|
-
* Why `grammy` is loaded via dynamic import: opensquid declares all
|
|
26
|
-
* three chat SDKs as optionalDependencies. Users with no Telegram
|
|
27
|
-
* config (and possibly `npm --omit=optional`) shouldn't pay the install
|
|
28
|
-
* cost. The dynamic import only runs in `start()` when this adapter
|
|
29
|
-
* is actually being activated — and produces a clear "install grammy"
|
|
30
|
-
* error if the dep is missing.
|
|
31
|
-
*
|
|
32
|
-
* Gotcha: only ONE polling consumer per token at a time. A second
|
|
33
|
-
* opensquid process with the same token will collide with 409 Conflict.
|
|
34
|
-
* Surface that error clearly on start.
|
|
35
|
-
*/
|
|
36
|
-
import { ChatGatewayError, formatChannelId, } from "../gateway.js";
|
|
37
|
-
export class TelegramAdapter {
|
|
38
|
-
config;
|
|
39
|
-
platform = "telegram";
|
|
40
|
-
bot = null;
|
|
41
|
-
handlers = [];
|
|
42
|
-
botUsername = "";
|
|
43
|
-
botId = "";
|
|
44
|
-
startPromise = null;
|
|
45
|
-
/**
|
|
46
|
-
* 0.7.4 (#147): true when the long-poll lost to a 409 Conflict
|
|
47
|
-
* (another consumer holds the token — typically the Claude Code
|
|
48
|
-
* `plugin:telegram` bun bot). Outbound sendMessage still works via
|
|
49
|
-
* HTTPS, only inbound is dead. A periodic retry attempts to reclaim.
|
|
50
|
-
*/
|
|
51
|
-
outboundOnly = false;
|
|
52
|
-
/** 0.7.4 (#147): handle for the periodic long-poll retry timer. */
|
|
53
|
-
retryTimer = null;
|
|
54
|
-
/** Retry cadence — long enough that flapping doesn't burn API quota. */
|
|
55
|
-
static RETRY_INTERVAL_MS = 60_000;
|
|
56
|
-
/**
|
|
57
|
-
* 0.5.90 (TG.3): chat_ids for which we've already logged an allowlist
|
|
58
|
-
* drop this process. The adapter silently drops messages from non-
|
|
59
|
-
* allowlisted chats (correct policy — never echo policy decisions back
|
|
60
|
-
* to the sender), but operators need a one-time-per-chat log line so
|
|
61
|
-
* they can diagnose "why isn't my message routing?" without reading
|
|
62
|
-
* source. Tracked as a Set keyed by chatIdStr; resets on restart.
|
|
63
|
-
*/
|
|
64
|
-
allowlistDropLogged = new Set();
|
|
65
|
-
constructor(config) {
|
|
66
|
-
this.config = config;
|
|
67
|
-
if (!config.bot_token?.trim()) {
|
|
68
|
-
throw new ChatGatewayError("telegram adapter: bot_token is required", "set chat_connections.telegram.bot_token in ~/.opensquid/config.json");
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async start() {
|
|
72
|
-
if (this.bot)
|
|
73
|
-
return;
|
|
74
|
-
let Bot;
|
|
75
|
-
try {
|
|
76
|
-
const grammy = (await import("grammy"));
|
|
77
|
-
Bot = grammy.Bot;
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
// v0.7 audit fix (M5): distinguish "not installed" from "installed
|
|
81
|
-
// but threw on load" — different remediation each case.
|
|
82
|
-
const code = err?.code;
|
|
83
|
-
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
84
|
-
throw new ChatGatewayError("telegram adapter: 'grammy' SDK not installed", "run `npm install grammy` (or reinstall opensquid without --omit=optional)");
|
|
85
|
-
}
|
|
86
|
-
throw new ChatGatewayError(`telegram adapter: 'grammy' SDK failed to load: ${err instanceof Error ? err.message : String(err)}`, "the SDK is installed but threw on import — check node version compatibility");
|
|
87
|
-
}
|
|
88
|
-
const bot = new Bot(this.config.bot_token);
|
|
89
|
-
this.bot = bot;
|
|
90
|
-
// Probe identity + token validity. Throws on bad token.
|
|
91
|
-
const me = await bot.api.getMe();
|
|
92
|
-
this.botUsername = me.username ?? "";
|
|
93
|
-
this.botId = String(me.id);
|
|
94
|
-
bot.on("message", async (ctx) => {
|
|
95
|
-
const m = ctx.message;
|
|
96
|
-
if (!m?.text)
|
|
97
|
-
return; // ignore non-text in v0.7
|
|
98
|
-
const chatIdStr = String(m.chat.id);
|
|
99
|
-
// Allowlist enforcement.
|
|
100
|
-
if (this.config.allowlist_chat_ids &&
|
|
101
|
-
this.config.allowlist_chat_ids.length > 0 &&
|
|
102
|
-
!this.config.allowlist_chat_ids.includes(chatIdStr)) {
|
|
103
|
-
// Silently drop on the chat side (correct — never echo policy back
|
|
104
|
-
// to the sender). But log once per chat_id per process lifetime
|
|
105
|
-
// (0.5.90 / TG.3) so operators can diagnose "why isn't my message
|
|
106
|
-
// routing?" without reading source. The first message from a
|
|
107
|
-
// newly-talking chat surfaces the drop with a hint to fix.
|
|
108
|
-
if (!this.allowlistDropLogged.has(chatIdStr)) {
|
|
109
|
-
this.allowlistDropLogged.add(chatIdStr);
|
|
110
|
-
const chatType = m.chat.type;
|
|
111
|
-
const hint = "add this chat_id to chat_connections.telegram.allowlist_chat_ids in ~/.opensquid/config.json to enable inbound routing";
|
|
112
|
-
// eslint-disable-next-line no-console
|
|
113
|
-
console.error(`[telegram adapter] dropped inbound from non-allowlisted chat ${chatIdStr} (type=${chatType}); ${hint}`);
|
|
114
|
-
}
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const normalized = {
|
|
118
|
-
id: String(m.message_id),
|
|
119
|
-
threadId: m.message_thread_id !== undefined ? String(m.message_thread_id) : undefined,
|
|
120
|
-
platform: "telegram",
|
|
121
|
-
channel: formatChannelId("telegram", chatIdStr),
|
|
122
|
-
sender: m.from?.username ?? m.from?.first_name ?? String(m.from?.id ?? "unknown"),
|
|
123
|
-
senderId: String(m.from?.id ?? ""),
|
|
124
|
-
text: m.text,
|
|
125
|
-
receivedAt: new Date(m.date * 1000),
|
|
126
|
-
mentionsBot: detectBotMention(m.text, m.entities, this.botUsername),
|
|
127
|
-
};
|
|
128
|
-
for (const h of this.handlers) {
|
|
129
|
-
try {
|
|
130
|
-
await h(normalized);
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
134
|
-
// eslint-disable-next-line no-console
|
|
135
|
-
console.error(`[telegram adapter] handler error: ${msg}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
// `bot.start()` resolves only on bot.stop(). Fire-and-track via a
|
|
140
|
-
// promise we keep around for shutdown, but don't await it here.
|
|
141
|
-
// 0.7.4 (#147) fix: rejection handler delegates to
|
|
142
|
-
// handleStartRejection() so tests can exercise the 409 path
|
|
143
|
-
// without spinning up grammy.
|
|
144
|
-
this.startPromise = bot.start().catch((err) => this.handleStartRejection(err));
|
|
145
|
-
// Yield once so the polling loop has a tick to register before the
|
|
146
|
-
// gateway moves on to dispatch outbound messages.
|
|
147
|
-
await new Promise((r) => setImmediate(r));
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* 0.7.4 (#147): handle a rejection from `bot.start()`. Extracted
|
|
151
|
-
* from inline catch handler so tests can simulate 409 without
|
|
152
|
-
* needing a live grammy + colliding bot. EXPORTED VIA PROTECTED for
|
|
153
|
-
* test-only direct invocation; not part of the public adapter API.
|
|
154
|
-
*/
|
|
155
|
-
handleStartRejection(err) {
|
|
156
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
157
|
-
const is409 = /409|Conflict/.test(msg);
|
|
158
|
-
if (is409) {
|
|
159
|
-
// eslint-disable-next-line no-console
|
|
160
|
-
console.error(`[telegram adapter] long-poll lost to 409 Conflict — degrading to OUTBOUND-ONLY (outbound sendMessage still works); periodic retry every ${TelegramAdapter.RETRY_INTERVAL_MS / 1000}s`);
|
|
161
|
-
this.outboundOnly = true;
|
|
162
|
-
this.startPromise = null;
|
|
163
|
-
this.scheduleRetry();
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
// eslint-disable-next-line no-console
|
|
167
|
-
console.error(`[telegram adapter] long-poll loop errored: ${msg}`);
|
|
168
|
-
this.bot = null;
|
|
169
|
-
this.startPromise = null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 0.7.4 (#147): test-only seed — install a fake bot reference + mark
|
|
174
|
-
* outbound-only so tests can verify isOutboundOnly() + retry timer
|
|
175
|
-
* without spinning up grammy. Must be called before any
|
|
176
|
-
* handleStartRejection in test context.
|
|
177
|
-
*/
|
|
178
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
|
-
_testSeed(fakeBot) {
|
|
180
|
-
this.bot = fakeBot;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* 0.7.4 (#147): test-only — clear the retry timer so tests don't
|
|
184
|
-
* leak intervals after a 409 simulation.
|
|
185
|
-
*/
|
|
186
|
-
_testClearRetryTimer() {
|
|
187
|
-
if (this.retryTimer) {
|
|
188
|
-
clearInterval(this.retryTimer);
|
|
189
|
-
this.retryTimer = null;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* 0.7.4 (#147): periodically retry the long-poll while in outbound-
|
|
194
|
-
* only mode. If the competing consumer disconnects, we reclaim
|
|
195
|
-
* inbound transparently.
|
|
196
|
-
*/
|
|
197
|
-
scheduleRetry() {
|
|
198
|
-
if (this.retryTimer)
|
|
199
|
-
return;
|
|
200
|
-
this.retryTimer = setInterval(() => {
|
|
201
|
-
void this.tryReclaim();
|
|
202
|
-
}, TelegramAdapter.RETRY_INTERVAL_MS);
|
|
203
|
-
}
|
|
204
|
-
async tryReclaim() {
|
|
205
|
-
if (!this.outboundOnly || !this.bot)
|
|
206
|
-
return;
|
|
207
|
-
const bot = this.bot;
|
|
208
|
-
// Fire bot.start() again; same rejection handling as the initial
|
|
209
|
-
// start. If 409, stay outbound-only. If success (no rejection
|
|
210
|
-
// observable yet — start() resolves only on stop()), clear the
|
|
211
|
-
// outboundOnly flag and stop the retry timer.
|
|
212
|
-
const next = bot.start().catch((err) => {
|
|
213
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
214
|
-
const is409 = /409|Conflict/.test(msg);
|
|
215
|
-
if (!is409) {
|
|
216
|
-
// eslint-disable-next-line no-console
|
|
217
|
-
console.error(`[telegram adapter] retry start() errored: ${msg}`);
|
|
218
|
-
}
|
|
219
|
-
// Stay outbound-only; retry timer keeps running.
|
|
220
|
-
return;
|
|
221
|
-
});
|
|
222
|
-
// Yield to let the long-poll register if it's going to succeed.
|
|
223
|
-
await new Promise((r) => setImmediate(r));
|
|
224
|
-
// If the bot reference is still alive and no rejection fired yet,
|
|
225
|
-
// assume the long-poll is back. Reclaim inbound.
|
|
226
|
-
if (this.bot && this.outboundOnly) {
|
|
227
|
-
// Heuristic check: getMe still succeeds (the bot can still talk
|
|
228
|
-
// to the API). If 409 already rejected `next`, that handler will
|
|
229
|
-
// have run by now. We can't directly observe "start succeeded"
|
|
230
|
-
// because start() doesn't resolve on success — it stays pending.
|
|
231
|
-
// So we look for the absence of a fresh 409.
|
|
232
|
-
try {
|
|
233
|
-
await bot.api.getMe();
|
|
234
|
-
this.outboundOnly = false;
|
|
235
|
-
this.startPromise = next;
|
|
236
|
-
if (this.retryTimer) {
|
|
237
|
-
clearInterval(this.retryTimer);
|
|
238
|
-
this.retryTimer = null;
|
|
239
|
-
}
|
|
240
|
-
// eslint-disable-next-line no-console
|
|
241
|
-
console.error(`[telegram adapter] long-poll RECLAIMED — inbound restored`);
|
|
242
|
-
}
|
|
243
|
-
catch {
|
|
244
|
-
// getMe failed → keep outbound-only, retry on next tick
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
async shutdown() {
|
|
249
|
-
if (!this.bot)
|
|
250
|
-
return;
|
|
251
|
-
// 0.7.4 (#147): stop the retry timer first so it doesn't fire
|
|
252
|
-
// mid-shutdown and resurrect a dead adapter.
|
|
253
|
-
if (this.retryTimer) {
|
|
254
|
-
clearInterval(this.retryTimer);
|
|
255
|
-
this.retryTimer = null;
|
|
256
|
-
}
|
|
257
|
-
try {
|
|
258
|
-
await this.bot.stop();
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
// best-effort — if grammy already torn down, nothing to do
|
|
262
|
-
}
|
|
263
|
-
if (this.startPromise) {
|
|
264
|
-
// bot.start()'s promise should resolve after stop. Don't hang
|
|
265
|
-
// forever though — race against a short timeout.
|
|
266
|
-
await Promise.race([this.startPromise, new Promise((r) => setTimeout(r, 2000))]);
|
|
267
|
-
}
|
|
268
|
-
this.bot = null;
|
|
269
|
-
this.startPromise = null;
|
|
270
|
-
this.outboundOnly = false;
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* 0.7.4 (#147): introspection accessor for tests + the
|
|
274
|
-
* `chat_daemon_status` MCP tool to surface "outbound-only" state to
|
|
275
|
-
* operators trying to diagnose "where did my message go?"
|
|
276
|
-
*/
|
|
277
|
-
isOutboundOnly() {
|
|
278
|
-
return this.outboundOnly;
|
|
279
|
-
}
|
|
280
|
-
onMessage(handler) {
|
|
281
|
-
this.handlers.push(handler);
|
|
282
|
-
}
|
|
283
|
-
async send(message) {
|
|
284
|
-
if (!this.bot) {
|
|
285
|
-
throw new ChatGatewayError("telegram adapter: not started", "call gateway.start() before send()");
|
|
286
|
-
}
|
|
287
|
-
const { chatId, threadId: channelThreadId } = parseTelegramChannel(message.channel);
|
|
288
|
-
const opts = {};
|
|
289
|
-
if (message.replyTo) {
|
|
290
|
-
const n = Number(message.replyTo);
|
|
291
|
-
if (Number.isFinite(n))
|
|
292
|
-
opts.reply_to_message_id = n;
|
|
293
|
-
}
|
|
294
|
-
// Explicit `message.threadId` from the caller wins over a thread id
|
|
295
|
-
// embedded in the channel string. The embedded form exists so that
|
|
296
|
-
// composite channel literals echoed from `chat_poll_inbox`
|
|
297
|
-
// (e.g. `telegram:-1001234567890:15`) can be passed back to
|
|
298
|
-
// `chat_send` verbatim without the caller having to split them.
|
|
299
|
-
const effectiveThreadId = message.threadId ?? channelThreadId;
|
|
300
|
-
if (effectiveThreadId !== undefined) {
|
|
301
|
-
const n = Number(effectiveThreadId);
|
|
302
|
-
if (Number.isFinite(n))
|
|
303
|
-
opts.message_thread_id = n;
|
|
304
|
-
}
|
|
305
|
-
// TPS.7 (v0.5.130) — detect topic-gone failure and re-throw as a
|
|
306
|
-
// typed error so the daemon's send-RPC handler can clear the stale
|
|
307
|
-
// binding + notify the operator. Other Bot API failures propagate
|
|
308
|
-
// unchanged (the caller's existing error path stays in charge).
|
|
309
|
-
let sent;
|
|
310
|
-
try {
|
|
311
|
-
sent = await this.bot.api.sendMessage(chatId, message.text, opts);
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
if (isTopicGoneError(err) && opts.message_thread_id !== undefined) {
|
|
315
|
-
throw new TopicGoneError(extractGrammyDescription(err) ?? "topic gone", chatId, opts.message_thread_id, err);
|
|
316
|
-
}
|
|
317
|
-
throw err;
|
|
318
|
-
}
|
|
319
|
-
return {
|
|
320
|
-
platform: "telegram",
|
|
321
|
-
messageId: String(sent.message_id),
|
|
322
|
-
deliveredAt: new Date(sent.date * 1000),
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* v0.7.2 — Create a forum topic in a supergroup. Requires the bot
|
|
327
|
-
* to be admin with "Manage Topics" permission and the supergroup to
|
|
328
|
-
* have Topics enabled in settings. Returns the topic's
|
|
329
|
-
* `message_thread_id` for storage in chat-routing.json.
|
|
330
|
-
*/
|
|
331
|
-
async createTopic(chatId, name, options = {}) {
|
|
332
|
-
if (!this.bot) {
|
|
333
|
-
throw new ChatGatewayError("telegram adapter: not started", "call gateway.start() before createTopic()");
|
|
334
|
-
}
|
|
335
|
-
const apiOpts = {};
|
|
336
|
-
if (options.iconColor !== undefined)
|
|
337
|
-
apiOpts.icon_color = options.iconColor;
|
|
338
|
-
if (options.iconCustomEmojiId !== undefined)
|
|
339
|
-
apiOpts.icon_custom_emoji_id = options.iconCustomEmojiId;
|
|
340
|
-
const res = await this.bot.api.createForumTopic(chatId, name, apiOpts);
|
|
341
|
-
return { message_thread_id: res.message_thread_id, name: res.name };
|
|
342
|
-
}
|
|
343
|
-
async identity() {
|
|
344
|
-
if (!this.bot) {
|
|
345
|
-
throw new ChatGatewayError("telegram adapter: not started");
|
|
346
|
-
}
|
|
347
|
-
return { username: this.botUsername, nativeId: this.botId };
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
// ---------------------------------------------------------------------
|
|
351
|
-
// Helpers
|
|
352
|
-
// ---------------------------------------------------------------------
|
|
353
|
-
/**
|
|
354
|
-
* Parse a Telegram channel id (with optional embedded forum-topic
|
|
355
|
-
* thread id) into its native chat_id + message_thread_id parts.
|
|
356
|
-
*
|
|
357
|
-
* Wire format (canonical, mirrors `chat_poll_inbox` output):
|
|
358
|
-
* - `telegram:<chat_id>` → general topic, no thread
|
|
359
|
-
* - `telegram:<chat_id>:<thread_id>` → forum topic (supergroup with
|
|
360
|
-
* Topics enabled)
|
|
361
|
-
*
|
|
362
|
-
* Examples:
|
|
363
|
-
* - `telegram:-1001234567890` → `{ chatId: "-1001234567890" }`
|
|
364
|
-
* - `telegram:-1001234567890:15` → `{ chatId: "-1001234567890",
|
|
365
|
-
* threadId: "15" }`
|
|
366
|
-
* - `telegram:8075471258` → `{ chatId: "8075471258" }` (DM)
|
|
367
|
-
*
|
|
368
|
-
* Why the parser lives in the adapter (not in `gateway.ts`): Slack uses
|
|
369
|
-
* a different colon-in-native-id convention (`slack:C012345:1234.5678`
|
|
370
|
-
* where the trailing segment is `thread_ts`, an opaque part of the
|
|
371
|
-
* native id). Telegram's `<chat_id>:<thread_id>` semantic is
|
|
372
|
-
* platform-specific and shouldn't leak into the cross-platform
|
|
373
|
-
* `nativeIdFromChannel` helper.
|
|
374
|
-
*
|
|
375
|
-
* Exported for unit testing.
|
|
376
|
-
*/
|
|
377
|
-
export function parseTelegramChannel(channel) {
|
|
378
|
-
const colon = channel.indexOf(":");
|
|
379
|
-
if (colon === -1) {
|
|
380
|
-
throw new ChatGatewayError(`malformed channel id '${channel}'`, "telegram channel ids must be 'telegram:<chat_id>' or 'telegram:<chat_id>:<thread_id>'");
|
|
381
|
-
}
|
|
382
|
-
if (channel.slice(0, colon) !== "telegram") {
|
|
383
|
-
throw new ChatGatewayError(`telegram adapter received non-telegram channel: '${channel}'`);
|
|
384
|
-
}
|
|
385
|
-
const rest = channel.slice(colon + 1);
|
|
386
|
-
if (rest.length === 0) {
|
|
387
|
-
throw new ChatGatewayError(`malformed channel id '${channel}': empty chat_id`);
|
|
388
|
-
}
|
|
389
|
-
// chat_id is always the first segment after `telegram:`. If a second
|
|
390
|
-
// colon-segment is present, it's the forum-topic message_thread_id.
|
|
391
|
-
const sep = rest.indexOf(":");
|
|
392
|
-
if (sep === -1) {
|
|
393
|
-
return { chatId: rest };
|
|
394
|
-
}
|
|
395
|
-
const chatId = rest.slice(0, sep);
|
|
396
|
-
const threadId = rest.slice(sep + 1);
|
|
397
|
-
if (chatId.length === 0) {
|
|
398
|
-
throw new ChatGatewayError(`malformed channel id '${channel}': empty chat_id`);
|
|
399
|
-
}
|
|
400
|
-
if (threadId.length === 0) {
|
|
401
|
-
throw new ChatGatewayError(`malformed channel id '${channel}': empty thread_id after second colon`);
|
|
402
|
-
}
|
|
403
|
-
if (!/^\d+$/.test(threadId)) {
|
|
404
|
-
throw new ChatGatewayError(`malformed channel id '${channel}': thread_id must be all-digits, got '${threadId}'`);
|
|
405
|
-
}
|
|
406
|
-
return { chatId, threadId };
|
|
407
|
-
}
|
|
408
|
-
// ---------------------------------------------------------------------
|
|
409
|
-
// TPS.7 (v0.5.130) — typed stale-topic error
|
|
410
|
-
// ---------------------------------------------------------------------
|
|
411
|
-
/**
|
|
412
|
-
* Thrown by `TelegramAdapter.send` when the Telegram Bot API rejects an
|
|
413
|
-
* outbound `sendMessage` because the target `message_thread_id` no
|
|
414
|
-
* longer exists in the supergroup. The daemon's RPC `send` handler
|
|
415
|
-
* catches this, clears the workspace's stale binding via
|
|
416
|
-
* `clearBinding` (TPS.3), and surfaces the staleness through TPS.5's
|
|
417
|
-
* collision channel.
|
|
418
|
-
*
|
|
419
|
-
* Carries the underlying `GrammyError` (as `underlying: unknown`) for
|
|
420
|
-
* diagnostics — callers shouldn't peek into it but the daemon logs it.
|
|
421
|
-
*
|
|
422
|
-
* Why a typed error vs. introspecting the original GrammyError at the
|
|
423
|
-
* RPC layer: keeps grammy out of `rpc-server.ts` (which doesn't import
|
|
424
|
-
* the SDK) and makes the contract obvious at every call site.
|
|
425
|
-
*/
|
|
426
|
-
export class TopicGoneError extends Error {
|
|
427
|
-
chatId;
|
|
428
|
-
threadId;
|
|
429
|
-
underlying;
|
|
430
|
-
platform = "telegram";
|
|
431
|
-
constructor(message, chatId, threadId, underlying) {
|
|
432
|
-
super(message);
|
|
433
|
-
this.chatId = chatId;
|
|
434
|
-
this.threadId = threadId;
|
|
435
|
-
this.underlying = underlying;
|
|
436
|
-
this.name = "TopicGoneError";
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Match Bot API 400 responses whose description matches one of the
|
|
441
|
-
* known "thread not found" shapes. Defensive against Telegram changing
|
|
442
|
-
* the description string: matches "message thread not found" with
|
|
443
|
-
* either casing, both underscores or spaces, and the legacy CAPS form.
|
|
444
|
-
*
|
|
445
|
-
* Exported for unit testing.
|
|
446
|
-
*/
|
|
447
|
-
export function isTopicGoneError(err) {
|
|
448
|
-
if (!(err instanceof Error))
|
|
449
|
-
return false;
|
|
450
|
-
const code = err.error_code;
|
|
451
|
-
if (code !== 400)
|
|
452
|
-
return false;
|
|
453
|
-
const desc = extractGrammyDescription(err);
|
|
454
|
-
if (desc === undefined)
|
|
455
|
-
return false;
|
|
456
|
-
return /message[_ ]thread[_ ]not[_ ]found/i.test(desc);
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Pull the human-readable description out of a GrammyError-shaped value,
|
|
460
|
-
* falling back to the Error.message. Returns undefined if neither yields
|
|
461
|
-
* a usable string.
|
|
462
|
-
*
|
|
463
|
-
* Exported for unit testing.
|
|
464
|
-
*/
|
|
465
|
-
export function extractGrammyDescription(err) {
|
|
466
|
-
if (typeof err !== "object" || err === null)
|
|
467
|
-
return undefined;
|
|
468
|
-
const desc = err.description;
|
|
469
|
-
if (typeof desc === "string" && desc.length > 0)
|
|
470
|
-
return desc;
|
|
471
|
-
if (err instanceof Error && err.message.length > 0)
|
|
472
|
-
return err.message;
|
|
473
|
-
return undefined;
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Detect whether a message text contains an @-mention of this bot, or
|
|
477
|
-
* uses one of telegram's bot_command entities pointing at the bot
|
|
478
|
-
* (`/cmd@my_bot`).
|
|
479
|
-
*
|
|
480
|
-
* Exported for unit testing.
|
|
481
|
-
*/
|
|
482
|
-
export function detectBotMention(text, entities, botUsername) {
|
|
483
|
-
if (!botUsername)
|
|
484
|
-
return false;
|
|
485
|
-
const lower = `@${botUsername.toLowerCase()}`;
|
|
486
|
-
if (text.toLowerCase().includes(lower))
|
|
487
|
-
return true;
|
|
488
|
-
if (!entities)
|
|
489
|
-
return false;
|
|
490
|
-
for (const ent of entities) {
|
|
491
|
-
if (ent.type !== "mention" && ent.type !== "bot_command")
|
|
492
|
-
continue;
|
|
493
|
-
const slice = text.slice(ent.offset, ent.offset + ent.length).toLowerCase();
|
|
494
|
-
if (slice.includes(lower))
|
|
495
|
-
return true;
|
|
496
|
-
}
|
|
497
|
-
return false;
|
|
498
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../src.legacy/chat/adapters/telegram.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { ChatGatewayError } from "../gateway.js";
|
|
3
|
-
import { TelegramAdapter, detectBotMention } from "./telegram.js";
|
|
4
|
-
describe("TelegramAdapter constructor", () => {
|
|
5
|
-
it("rejects empty bot_token", () => {
|
|
6
|
-
expect(() => new TelegramAdapter({ bot_token: "" })).toThrow(ChatGatewayError);
|
|
7
|
-
});
|
|
8
|
-
it("rejects whitespace-only bot_token", () => {
|
|
9
|
-
expect(() => new TelegramAdapter({ bot_token: " " })).toThrow(ChatGatewayError);
|
|
10
|
-
});
|
|
11
|
-
it("accepts a real-shaped token", () => {
|
|
12
|
-
// Don't actually call start() — that would try to load grammy.
|
|
13
|
-
const a = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
14
|
-
expect(a.platform).toBe("telegram");
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
describe("detectBotMention", () => {
|
|
18
|
-
it("returns false when botUsername is empty", () => {
|
|
19
|
-
expect(detectBotMention("@somebot hello", [], "")).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
it("detects plain @-mention", () => {
|
|
22
|
-
expect(detectBotMention("hey @mybot do x", [], "mybot")).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
it("is case-insensitive", () => {
|
|
25
|
-
expect(detectBotMention("hey @MyBot", [], "mybot")).toBe(true);
|
|
26
|
-
expect(detectBotMention("hey @mybot", [], "MyBot")).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
it("returns false when text mentions someone else", () => {
|
|
29
|
-
expect(detectBotMention("hey @otherbot", [], "mybot")).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
it("detects /cmd@mybot entity (bot_command)", () => {
|
|
32
|
-
const text = "/start@mybot please";
|
|
33
|
-
const entities = [{ type: "bot_command", offset: 0, length: 14 }];
|
|
34
|
-
expect(detectBotMention(text, entities, "mybot")).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
it("ignores entities of unrelated types", () => {
|
|
37
|
-
const text = "see https://mybot.example.com";
|
|
38
|
-
const entities = [{ type: "url", offset: 4, length: 25 }];
|
|
39
|
-
expect(detectBotMention(text, entities, "mybot")).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe("TelegramAdapter — 409 outbound-only fallback (#147)", () => {
|
|
43
|
-
let adapter;
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
if (adapter) {
|
|
46
|
-
adapter._testClearRetryTimer();
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
it("starts in long-poll mode (isOutboundOnly false on a fresh adapter)", () => {
|
|
50
|
-
adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
51
|
-
expect(adapter.isOutboundOnly()).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
it("degrades to outbound-only on a 409 Conflict error (does NOT null the bot)", () => {
|
|
54
|
-
adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
55
|
-
// Seed a fake bot so handleStartRejection can verify the bot
|
|
56
|
-
// reference is preserved through the 409 path.
|
|
57
|
-
const fakeBot = { api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } };
|
|
58
|
-
adapter._testSeed(fakeBot);
|
|
59
|
-
adapter.handleStartRejection(new Error("Call to 'getUpdates' failed! (409: Conflict: terminated by other getUpdates)"));
|
|
60
|
-
expect(adapter.isOutboundOnly()).toBe(true);
|
|
61
|
-
// Outbound bot reference still alive (we can't directly check
|
|
62
|
-
// this.bot from outside, but the absence of a "telegram adapter:
|
|
63
|
-
// not started" error on a hypothetical send() is the contract;
|
|
64
|
-
// verified indirectly via isOutboundOnly + clean shutdown below).
|
|
65
|
-
});
|
|
66
|
-
it("matches the 409 detection across both 'Conflict' and '409' substrings", () => {
|
|
67
|
-
adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
68
|
-
adapter._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
|
|
69
|
-
adapter.handleStartRejection(new Error("HTTP 409 Conflict"));
|
|
70
|
-
expect(adapter.isOutboundOnly()).toBe(true);
|
|
71
|
-
const a2 = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
72
|
-
a2._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
|
|
73
|
-
a2.handleStartRejection(new Error("Some random Conflict happened"));
|
|
74
|
-
expect(a2.isOutboundOnly()).toBe(true);
|
|
75
|
-
a2._testClearRetryTimer();
|
|
76
|
-
});
|
|
77
|
-
it("non-409 errors still tear down the bot (treats as genuine failure)", () => {
|
|
78
|
-
adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
79
|
-
adapter._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
|
|
80
|
-
adapter.handleStartRejection(new Error("ECONNREFUSED"));
|
|
81
|
-
// outboundOnly stays false; the bot was nulled
|
|
82
|
-
expect(adapter.isOutboundOnly()).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
it("schedules a periodic retry timer when entering outbound-only mode", () => {
|
|
85
|
-
adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
|
|
86
|
-
adapter._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
|
|
87
|
-
expect(adapter.isOutboundOnly()).toBe(false);
|
|
88
|
-
adapter.handleStartRejection(new Error("409 Conflict"));
|
|
89
|
-
expect(adapter.isOutboundOnly()).toBe(true);
|
|
90
|
-
// Retry timer is private; we can't directly assert it exists, but
|
|
91
|
-
// _testClearRetryTimer() should successfully clear it (verified by
|
|
92
|
-
// the afterEach not throwing on subsequent runs).
|
|
93
|
-
});
|
|
94
|
-
});
|