Bridge any DingTalk group @-mention to a local Claude Code or Codex CLI session — no public IP, no webhook callback, just DingTalk's Stream WebSocket.
把钉钉群里 @机器人 的消息转发到本机的 Claude Code / Codex CLI(可选 Hermes),把 AI 回复发回群里。
入站走钉钉官方 Stream 模式(WebSocket),不需要公网 IP / webhook 回调地址。
- 🤖 One bot per agent — Claude, Codex, Hermes each get their own DingTalk robot (own AppKey/Secret).
@claude-botruns Claude,@codex-botruns Codex. - 🔁 Session continuity — per-(conversation, sender, agent) sessions are persisted as JSON and resumed across restarts (Claude
--resume, Codexexec resume <thread_id>). - 🧠 Echo mode — switch to
BRIDGE_BRAIN=echoto validate the inbound pipe without invoking any agent. - 😊 Emoji progress —
🤔Thinking→🥳Done/😖Failedreactions mark task progress on the original @-message (best-effort, never blocks). - 🔒 Sender allowlist — gate execution to specific DingTalk
staff_ids; empty = open (not recommended). - 🛡️ No
dangerously-skip-permissions— the default Claude permission mode is the strictest one; expand capabilities only throughallowedTools.
DingTalk group @bot ──① Stream WS ──▶ bridge.py ──② spawn agent CLI ──▶ Claude / Codex
▲ │
└──────────────── ③ reply via session_webhook ──────┘
+ optional emoji reactions
| File | Role |
|---|---|
bridge.py |
Stream client per bot, message routing, command parsing |
config.py |
Immutable .env loading + multi-agent config |
brain.py |
EchoBrain / ClaudeBrain / CodexBrain / HermesBrain — one Brain per agent |
reactions.py |
Emoji progress via DingTalk OpenAPI (robot_1_0) |
session_store.py |
Thread-safe JSON persistence of (agent, conv, sender) → session_id |
task_io.py |
TaskInput / TaskOutput contract shared by every brain |
selftest.py |
Offline unit tests (run with python selftest.py) |
run.sh |
One-shot launcher (creates venv, installs deps, runs bridge) |
- DingTalk Open Platform → App Development → Enterprise Internal App → create one app per agent.
- In each app → Credentials → copy
AppKey/AppSecret. - Robot → enable Robot capability → Message receiving mode: Stream (NOT HTTP callback).
- Add the robot to your target group (Group Settings → Smart Assistants → Add Robot).
- Publish the app.
git clone https://github.com/trustchain-ai/dingtalk-bridge.git
cd dingtalk-bridge
cp .env.example .env
# fill in CLAUDE_APP_KEY / CLAUDE_APP_SECRET (and CODEX_*, HERMES_* as needed).env is git-ignored. Never commit AppKey/Secret.
./run.sh
# first run: creates .venv and installs requirements.txtIn your DingTalk group:
@claude-bot 你好 # EchoBrain confirms the pipe (BRIDGE_BRAIN=echo)
# then switch BRIDGE_BRAIN=claude to actually execute
@codex-bot /status # Show session state for this (conv, sender)
@claude-bot /reset # Wipe this bot's session for this (conv, sender)
@claude-bot /reset all # Wipe sessions across all agents
Each agent expects its CLI binary on PATH:
| Agent | Binary (default) | Headless flag | Resume flag |
|---|---|---|---|
| claude | /opt/homebrew/bin/claude |
-p <task> --output-format json |
--resume <session_id> |
| codex | /opt/homebrew/bin/codex |
exec --skip-git-repo-check --sandbox read-only --json |
exec resume <thread_id> |
| hermes | /Users/lihu/.local/bin/hermes |
-z <task> |
--continue <session_name> |
Override via CLAUDE_BIN, CODEX_BIN, HERMES_BIN in .env.
Claude honors CLAUDE_PERMISSION_MODE (default / acceptEdits / plan). Do not set dangerously-skip-permissions — the bridge intentionally refuses to bypass Claude's safety gate so a group member can't drive your machine unchecked.
BRIDGE_BRAIN=claudemeans any allowed sender can run Claude/Codex with your credentials on your machine. Always setALLOWED_SENDERS=<your_staff_id>.- Sender staff IDs are printed in startup logs at first run — copy yours into
.env. - AppKey / AppSecret live only in
.env(git-ignored). Rotate immediately if exposed. - Bridge never echoes credentials into chat; replies are clipped to
MAX_REPLY_CHARS.
| Command | Effect |
|---|---|
<free text> |
Route the task to the agent of the @-mentioned bot |
/reset |
Wipe this bot's session for this (conv, sender) |
/reset <agent> |
If invoked on a different bot, tells you which bot to use |
/reset all |
Wipe every agent's session for this (conv, sender) |
/status |
Print session state for every agent in this (conv, sender) |
./.venv/bin/python selftest.py # offline unit tests (default; SKIP_LIVE=1 skips live CLI tests)
./.venv/bin/python -m bridge # run bridge directly (use ./run.sh in production)The brain interface is intentionally narrow (run(TaskInput) -> TaskOutput) — adding a new agent is a single new class in brain.py plus a build_brain branch.
# Adding "my-agent":
class MyAgentBrain:
name = "my-agent"
def run(self, ti: TaskInput) -> TaskOutput: ...MIT — see LICENSE.
trustchain-ai · issues and PRs welcome.