DingTalk group chat bridge for Claude Code. Send messages, receive @mentions, auto-execute prompts via Stream SDK.
SaferSkills independently audited dingtalk-bridge (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Bridges Claude Code with DingTalk group chat via the DingTalk Stream SDK + OpenAPI.
| Feature | Description |
|---|---|
| Send Markdown | Send rich markdown messages to a DingTalk group |
| Send Text | Send plain text messages |
| Stream Bot | 24/7 listener: receive @mentions, execute via claude -p, reply with results |
| Auto Conv Discovery | Conversation ID auto-saved on first @mention |
| Keepalive | 20s WebSocket ping prevents DingTalk 30-min timeout |
| Crash Recovery | Exponential backoff (5s to 60s) on disconnect |
bash "$CLAUDE_SKILL_DIR/scripts/install.sh"The script will:
dingtalk_stream, websockets)config.jsondata/ directory for conversation stateSet credentials via environment variables (recommended):
export DINGTALK_APP_KEY="your_app_key"
export DINGTALK_APP_SECRET="your_app_secret"
export DINGTALK_WORKDIR="/path/to/your/project" # optionalOr edit config.json in the skill directory (see config.example.json).
The bot needs a conversation ID to send messages. Two ways:
Auto (recommended): Start the Stream bot, then @mention it in a DingTalk group. The conv ID is saved automatically.
Manual: If you already have the openConversationId and robotCode:
mkdir -p "$CLAUDE_SKILL_DIR/data"
echo '{"openConversationId":"YOUR_ID","robotCode":"YOUR_ROBOT_CODE"}' > "$CLAUDE_SKILL_DIR/data/conv.json"# Markdown (default)
python3 "$CLAUDE_SKILL_DIR/src/send.py" "**Bold** message with markdown"
# With custom title
python3 "$CLAUDE_SKILL_DIR/src/send.py" --title "Alert" "Server is down!"
# Plain text
python3 "$CLAUDE_SKILL_DIR/src/send.py" --text "Plain text message"python3 "$CLAUDE_SKILL_DIR/src/stream_bot.py"The bot will:
claude -p "<message>" --continueimport sys
sys.path.insert(0, "/path/to/dingtalk-bridge/src")
from send import send_markdown, send_text
send_markdown("Daily Report", "**Sent:** 50\n**Opened:** 18\n**Clicked:** 7")
send_text("Simple notification")| Config Key | Env Var | Default | Description |
|---|---|---|---|
app_key | DINGTALK_APP_KEY | (required) | DingTalk App Key |
app_secret | DINGTALK_APP_SECRET | (required) | DingTalk App Secret |
conv_file | DINGTALK_CONV_FILE | <skill>/data/conv.json | Conversation metadata path |
workdir | DINGTALK_WORKDIR | cwd | Working directory for claude CLI |
claude_bin | DINGTALK_CLAUDE_BIN | claude | Path to claude binary |
max_reply | DINGTALK_MAX_REPLY | 3000 | Max reply length (chars) |
keepalive | DINGTALK_KEEPALIVE | 20 | WebSocket keepalive interval (seconds) |
python3 "$CLAUDE_SKILL_DIR/tests/test_dingtalk.py"dingtalk-bridge/
├── SKILL.md # This file
├── config.example.json # Example configuration
├── config.json # Your config (gitignored)
├── data/
│ └── conv.json # Auto-saved conversation metadata
├── src/
│ ├── __init__.py
│ ├── config.py # Config loader (env > file > defaults)
│ ├── send.py # Send messages (OpenAPI)
│ └── stream_bot.py # Stream bot (receive + execute + reply)
├── scripts/
│ └── install.sh # One-command setup
└── tests/
└── test_dingtalk.py # Regression tests~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.