Skip to content

refactor(bots): extract gateway bridge base classes and rename simple-bridge to telegram-bridge - #3076

Merged
Astro-Han merged 4 commits into
apache:mainfrom
chinawch007:refactor/bot-gateway-bridge-base
Aug 17, 2026
Merged

Astro-Han merged 4 commits into
apache:mainfrom
chinawch007:refactor/bot-gateway-bridge-base

Conversation

@chinawch007

@chinawch007 chinawch007 commented Aug 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes the "Bot gateway bridge base" lane. Extracts the gateway/WS plumbing that qq-bridge.ts, discord-bridge.ts, and dingtalk-bridge.ts had duplicated verbatim, and completes the follow-up cleanup of simple-bridge.ts. Two commits, fully behavior-preserving.

1. refactor(bots): extract ws/gateway bridge base classes for qq/discord/dingtalk

  • WsBridgeBase (packages/runtime/src/bots/ws-bridge-base.ts) — the WebSocket transport lifecycle all three bridges shared byte-for-byte: start() credential gate, stop(), connect() with listener wiring, handleClose(), scheduleReconnect(), sendWs(), and the shared reconnectBackoffMs (the three per-file copies were identical, including their RECONNECT_DELAY_* constants; the constants are now exported once).
  • GatewayBridgeBase (extends WsBridgeBase) — the Discord/QQ-shaped protocol layer: the eight opcode constants and the handleWsMessage dispatch switch, heartbeat scheduling with missed-ack reconnect, seq/sessionId tracking, the identify/resume fork at HELLO, forceReconnect(), and promoteToOperational() for READY/RESUMED.
  • QQ and Discord now override exactly the platform seams the lane named: credentials/auth scheme (QQBot <access_token> with refresh vs static Bot <token>), buildIdentifyPayload()/buildResumePayload(), dispatch event names (AT_MESSAGE_CREATE/GROUP_AT_MESSAGE_CREATE/C2C_MESSAGE_CREATE/... vs MESSAGE_CREATE), and the REST send routes. qq-bridge.ts 741→504 lines, discord-bridge.ts 629→385.
  • DingTalk extends only WsBridgeBase: its Stream protocol has no opcodes/heartbeat/identify/resume (frames carry headers/messageId/topic and every delivery must be acked), so a single all-three GatewayBridgeBase would have given it dead session machinery. It shares the transport lifecycle (connect/close policy/backoff/stop, with a closeReasonPrefix hook for its stream-closed-* reasons) and keeps its frame protocol in the subclass. dingtalk-bridge.ts 561→464.
  • New gateway-bridge-base.test.ts (17 tests, 315 lines) — the connect/heartbeat/reconnect paths had zero behavioral coverage before (all existing bridge tests only exercise pure helpers). Uses a FakeWebSocket injected through a createWebSocket() seam to drive HELLO→identify, READY→resume, missed-ack force-reconnect, INVALID_SESSION resumable/non-resumable semantics, fatal/resumable/clean close policy, and the shared backoff curve.
  • Net: the three gateway bridges dropped from 1931 to 1353 lines (−578) with zero behavior change — every lifted block was character-identical across the originals, all reason strings (gateway-closed-*, stream-closed-*, gateway-bot-*, connections-open-*, getAppAccessToken-*) are preserved verbatim, and every file's __TEST__ export surface is untouched.

2. refactor(bots): rename simple-bridge to telegram-bridge, drop dead feishu/discord branches

  • BotRegistry only ever constructed SimpleBotBridge for telegram (feishu and discord route to their dedicated bridges), so the feishu start branch — plus startFeishu(), feishuTenantAccessToken(), and FEISHU_REQUEST_TIMEOUT_MS — the discord scaffold-only branch, and the unreachable unimplemented fallback were dead code. All deleted.
  • Renamed simple-bridge.ts → telegram-bridge.ts and SimpleBotBridge → TelegramBotBridge to match the file's real responsibility; connectionKind() simplifies to a constant 'polling'. Callers updated: bot-registry.ts and six test files (import paths only; telegram-reply-stream.test.ts also picks up the new class name).

Refs: #1404

Verification

  • npm run format:check — clean (8 formatting nits auto-fixed and folded in)
  • npm run lint — 2304 files, no issues
  • npm run typecheck
  • npm --workspace @maka/bots run build — clean
  • npm --workspace @maka/bots run test:dist — 92 tests, 92 pass

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: ZCode (GLM) prepared the entire change under human
direction — the base-class extraction, the qq/discord/dingtalk bridge
migrations, the telegram-bridge rename, the review-driven hardening
fix, and the new tests. The contributor of record scoped the task,
approved the plan, triaged the review findings, and made every commit
decision. All commits carry Generated-by: ZCode (GLM) trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • [] Yes — described under Summary above
  • [x ] No

…/dingtalk

Collapse the connect/heartbeat/identify/resume/reconnect plumbing that
qq-bridge, discord-bridge, and dingtalk-bridge duplicated verbatim:
WsBridgeBase owns the WebSocket transport lifecycle (connect, close
policy, reconnect backoff, stop) and GatewayBridgeBase layers the
Discord/QQ-shaped opcode protocol (HELLO/heartbeat/seq/session/
identify/resume) on top; DingTalk's Stream protocol extends the
transport base directly. Pure helpers and __TEST__ exports are
unchanged; gateway-bridge-base.test.ts adds behavior coverage for the
previously untested connect/heartbeat/reconnect paths.

Generated-by: ZCode (GLM)
…ishu/discord branches

The registry only ever constructs this bridge for telegram — feishu
and discord route to their dedicated bridges — so the feishu start
branch (plus feishuTenantAccessToken and its timeout constant), the
discord scaffold branch, and the unreachable 'unimplemented' fallback
were dead. Rename the file and class to match the real responsibility
and simplify connectionKind to a constant 'polling'.

Generated-by: ZCode (GLM)
@coderabbitai

coderabbitai Bot commented Aug 15, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dc186018-0d3e-4f2f-9fa2-8bf9367ab4a5

📥 Commits

Reviewing files that changed from the base of the PR and between ea620e2 and 8986366.

📒 Files selected for processing (1)
  • packages/runtime/src/bots/__tests__/gateway-bridge-base.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/runtime/src/bots/tests/gateway-bridge-base.test.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 1 remains after this review.


📝 Walkthrough

Summary

This PR removes duplicated WebSocket and gateway lifecycle logic from the bot bridges. WsBridgeBase centralizes connection, reconnection, close handling, cleanup, stopping, and status updates. GatewayBridgeBase centralizes heartbeat, session, identify/resume, dispatch, and gateway reconnect handling. Discord, QQ, and DingTalk retain platform-specific behavior through protected hooks.

The PR also renames SimpleBotBridge to TelegramBotBridge and removes obsolete Feishu, Discord scaffold, and fallback branches. Close-policy tests now stop bridges so scheduled reconnect timers cannot affect later tests.

Review assessment

  • Problem solved: The bridges previously duplicated lifecycle, reconnect, heartbeat, and session logic. The shared bases provide one implementation and consistent behavior.
  • Source of truth: The PR extends the existing bridge architecture. It does not create a parallel runtime path.
  • Solution size: The change is the smallest coherent solution for shared behavior across three WebSocket transports and two gateway implementations. The abstract hooks, shared state, and focused lifecycle tests are necessary to preserve platform-specific behavior.
  • Possible simplification: The removed local lifecycle and backoff code should remain deleted. The gateway tests should remain because they cover behavior moved into GatewayBridgeBase. No further safe deletion is evident.
  • Validation: The commit history confirms the refactor and the follow-up timer-cleanup test change. The PR summary reports formatting, lint, typecheck, build, and 92 distribution tests passing. Required-check status remains unverified without direct check evidence.

Review-relevant risks

The inheritance changes and the SimpleBotBridge to TelegramBotBridge rename may affect integration-facing contracts. The shared lifecycle changes may affect connection, reconnect, heartbeat, session, close, and status behavior. Material changes in these areas require independent human review under repository policy.

No protected-area effect was identified for security, licensing, releases, or governance in the current diff. The person performing the merge must review the final diff, and a maintainer makes the final determination.

Walkthrough

The PR adds shared WebSocket and gateway lifecycle bases, migrates Discord, QQ, and DingTalk bridges to them, and renames and narrows the Telegram bridge. Gateway tests cover connection, heartbeat, reconnect, session, and close behavior.

Changes

Shared WebSocket bridge lifecycle

Layer / File(s) Summary
Shared WebSocket lifecycle
packages/runtime/src/bots/ws-bridge-base.ts
WsBridgeBase centralizes socket events, lifecycle state, close handling, status updates, sending, and reconnect backoff.
Gateway protocol lifecycle
packages/runtime/src/bots/gateway-bridge-base.ts
GatewayBridgeBase handles gateway frames, sessions, identify/resume payloads, heartbeats, reconnects, and protocol cleanup.
Discord and QQ gateway adapters
packages/runtime/src/bots/discord-bridge.ts, packages/runtime/src/bots/qq-bridge.ts
Discord and QQ provide platform-specific gateway authentication, payloads, close decisions, readiness promotion, and event mapping.
DingTalk WebSocket adapter
packages/runtime/src/bots/dingtalk-bridge.ts
DingTalk provides credential checks, Stream connection setup, close decisions, frame handling, acknowledgements, and readiness behavior through WsBridgeBase.
Gateway lifecycle validation
packages/runtime/src/bots/__tests__/gateway-bridge-base.test.ts, packages/runtime/src/bots/__tests__/discord-bridge.test.ts
Tests cover startup gates, gateway frames, heartbeats, reconnects, close policies, session reset, timer cleanup, and detached socket events.

Telegram bridge separation

Layer / File(s) Summary
Telegram bridge rename and wiring
packages/runtime/src/bots/telegram-bridge.ts, packages/runtime/src/bots/bot-registry.ts, packages/runtime/src/bots/__tests__/*telegram*, packages/runtime/src/bots/__tests__/bot-user-allowlist.test.ts
SimpleBotBridge becomes TelegramBotBridge. Telegram startup and polling status are selected directly. Feishu code is removed. Registry and tests use the Telegram bridge module.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: ⚪ Minimal · up to 89863

The PR centralizes shared gateway transport behavior and renames the Telegram bridge without a reported behavior change; the supplied checks pass, and no actionable merge-blocking risk remains after normal review.

Sequence Diagram(s)

sequenceDiagram
  participant DiscordBotBridge
  participant GatewayBridgeBase
  participant WebSocket
  DiscordBotBridge->>GatewayBridgeBase: openConnection()
  GatewayBridgeBase->>WebSocket: connect to gateway URL
  WebSocket-->>GatewayBridgeBase: HELLO
  GatewayBridgeBase->>DiscordBotBridge: request identify or resume payload
  GatewayBridgeBase->>WebSocket: send gateway payload
  WebSocket-->>GatewayBridgeBase: dispatch or heartbeat acknowledgement
  GatewayBridgeBase->>DiscordBotBridge: onDispatch()
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the gateway base-class extraction and Telegram bridge rename.
Description check ✅ Passed The description covers the required summary, verification, AI-use declaration, checklist, issue reference, and behavior statement.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Ai Use Disclosure ✅ Passed The PR description selects substantive generative tooling, names ZCode (GLM) and its scope, and all four PR commits have matching standalone Generated-by: ZCode (GLM) trailers.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a250ef1-ab98-4ede-bbb1-be709b63a797

📥 Commits

Reviewing files that changed from the base of the PR and between 64dfd6d and f10c646.

📒 Files selected for processing (13)
  • packages/runtime/src/bots/__tests__/bot-user-allowlist.test.ts
  • packages/runtime/src/bots/__tests__/gateway-bridge-base.test.ts
  • packages/runtime/src/bots/__tests__/telegram-ratelimit-retry.test.ts
  • packages/runtime/src/bots/__tests__/telegram-reply-stream.test.ts
  • packages/runtime/src/bots/__tests__/telegram-reply-to.test.ts
  • packages/runtime/src/bots/__tests__/telegram-utf16.test.ts
  • packages/runtime/src/bots/bot-registry.ts
  • packages/runtime/src/bots/dingtalk-bridge.ts
  • packages/runtime/src/bots/discord-bridge.ts
  • packages/runtime/src/bots/gateway-bridge-base.ts
  • packages/runtime/src/bots/qq-bridge.ts
  • packages/runtime/src/bots/telegram-bridge.ts
  • packages/runtime/src/bots/ws-bridge-base.ts

Comment thread packages/runtime/src/bots/discord-bridge.ts
Comment thread packages/runtime/src/bots/gateway-bridge-base.ts Outdated
Comment thread packages/runtime/src/bots/gateway-bridge-base.ts
Comment thread packages/runtime/src/bots/gateway-bridge-base.ts
Comment thread packages/runtime/src/bots/ws-bridge-base.ts
Address five review findings on the extracted base classes. All five
were pre-existing behaviors carried over verbatim from the per-bridge
implementations; fixing them in the base covers QQ and Discord at once.

- discord: clear resumeGatewayUrl in resetSession — Discord's resume
  URL is only valid while its session lives, never for a fresh identify
- gateway: validate HELLO heartbeat_interval and fall back to the
  default instead of scheduling on NaN (a ~1ms heartbeat loop)
- gateway: force-reconnect when the identify/resume payload is null so
  the backoff path owns the retry instead of leaving an un-identified
  socket idle until the remote gateway closes it
- gateway: track the heartbeat jitter timeout alongside the interval so
  stop()/close cancel it, and never start the interval on a dead socket
  (a stale jitter could otherwise orphan the live interval and run two
  heartbeat loops)
- ws: ignore close events from sockets the bridge already detached —
  a late close after forceReconnect double-incremented reconnect
  attempts and could drop a session the reconnect meant to keep

The test FakeWebSocket now emits an async close event from close() so
the detached-socket path is exercised; nine tests cover the fixes.

Generated-by: ZCode (GLM)
@chinawch007
chinawch007 force-pushed the refactor/bot-gateway-bridge-base branch from f10c646 to ea620e2 Compare August 16, 2026 02:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9bac9a63-df57-4133-9d66-a866444f8789

📥 Commits

Reviewing files that changed from the base of the PR and between f10c646 and ea620e2.

📒 Files selected for processing (5)
  • packages/runtime/src/bots/__tests__/discord-bridge.test.ts
  • packages/runtime/src/bots/__tests__/gateway-bridge-base.test.ts
  • packages/runtime/src/bots/discord-bridge.ts
  • packages/runtime/src/bots/gateway-bridge-base.ts
  • packages/runtime/src/bots/ws-bridge-base.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/runtime/src/bots/ws-bridge-base.ts
  • packages/runtime/src/bots/gateway-bridge-base.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment thread packages/runtime/src/bots/__tests__/gateway-bridge-base.test.ts
… timers

Two close-policy tests ended with a scheduled reconnect timer still
armed; stop the bridge like every other test in the file so the timer
cannot fire during later tests and resurrect the connection.

Generated-by: ZCode (GLM)
@Astro-Han

Astro-Han commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for the refactor — the extraction boundary is right (two layers: transport WsBridgeBase shared with DingTalk, protocol GatewayBridgeBase for QQ/Discord), the moved code is byte-identical to the originals, the rename is complete with no residue, and the dead feishu/discord/unimplemented branches you removed are genuinely unreachable given the closed BotProvider union. CI is green.

Conclusion: PASS — no blocking findings; the refactor is good to merge as-is.

For the record (P3, non-blocking — all nice-to-haves):

P3-1 — the PR description/checklist says "fully behavior-preserving / No behavior change", but the branch intentionally changes 5 runtime behaviors. The body says "Two commits, fully behavior-preserving" and the checklist ticks [x] No, but the branch has 4 commits and ea620e2ad ("harden gateway bridge lifecycle per review") deliberately changes 5 observable edge-case behaviors (all with tests, all fixes): (1) onHello — a malformed HELLO missing heartbeat_interval no longer schedules a NaN→~1ms heartbeat storm; (2) sendIdentify/sendResume — a null auth payload now forceReconnect()s instead of idling the socket until a remote timeout; (3) scheduleHeartbeat — the jitter timer is now tracked/cancelled on stop (old code leaked an interval) and no interval starts on a dead socket; (4) ws-bridge-base.ts:124-128 — a late close from an already-replaced socket no longer double-increments reconnect attempts, so running/readiness stay stable during the forced-reconnect window; (5) discord-bridge.ts resetSession() — clears resumeGatewayUrl (old code re-identified with a stale resume URL after a fatal close). The code is fine; this is only about keeping the record accurate for future archaeology — if you touch the description anyway, flipping the checklist to "Yes — edge-case hardening (5 items, all tested)" and fixing "Two commits" → 4 and "17 tests, 315 lines" → 25/441 would help.

P3-2 — QQ and DingTalk subclass↔base wiring has zero integration tests. gateway-bridge-base.test.ts drives a synthetic subclass only; the sole subclass-level test is the Discord READY/resume lifecycle. Deferral is defensible (pre-existing gap + verbatim moves + typecheck constraint on overrides); a follow-up adding QQ HELLO→identify and DingTalk open→operational+ack tests would anchor the riskiest part of the refactor.

P3-3 — telegram-bridge.ts:296/351/378 still guard this.platform !== 'telegram' although the registry is the only constructor and only ever passes 'telegram' (the same dead-branch cleanup in commit 3 didn't reach these three); createWebSocket() is a test seam in production code (fine, worth a comment); sleep()/token-refresh/send-retry blocks remain duplicated across the four bridges (acceptable scope cut, could be a follow-up).


AI-assisted review disclosure: this review was produced with AI assistance (two pi review subagents on ollama-cloud/deepseek-v4-flash). Subagent 1 built and ran the affected test files locally (68/68 pass) and grepped the full repo for rename residue; subagent 2 diffed each of the 4 commits and verified the refactor commit is byte-equivalent. The behavior-change claims are from commit-level diffing, not runtime observation. Please weigh these findings with your own judgment.

中文摘要(AI 辅助审查)

结论:PASS,无阻塞项,可直接合并。refactor 质量高:两层抽象边界正确(传输层 WsBridgeBase 含 DingTalk、协议层 GatewayBridgeBase 给 QQ/Discord)、搬移代码与原文件逐字节一致、重命名彻底零残留、删除的死分支经 BotProvider 闭集核实确实不可达,本地实跑 68/68 测试通过。均为 P3(nice to have,不阻塞):(1) PR 正文声称 "fully behavior-preserving / No behavior change" 与事实不符——实际 4 个 commit,hardening commit (ea620e2) 有意改 5 处运行时行为(畸形 HELLO 心跳风暴、null 认证 payload 空转、jitter 定时器泄漏、迟到 close 双调度、Discord 陈旧 resume URL 重新认证——均带测试的正确修复),代码无问题,仅建议顺手更新描述与 checklist 保持记录准确;(2) QQ/DingTalk 子类↔base 集成零测试(存量缺口+逐字搬移+类型约束,可延后,建议 follow-up 补测试);(3) telegram-bridge 三处 platform guard 死条件残留、createWebSocket 测试 seam、sleep/token-refresh/send-retry 四桥重复(范围裁剪可接受)。

@Astro-Han
Astro-Han merged commit 9c5455b into apache:main Aug 17, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants