Telegram duty gateway: phone-message task loop on a dedicated duty session, global approval forwarding with inline approve/reject buttons, telegram_ask choice questions, duty/local toggle with a web duty banner, zh/en messages, zero idle tokens.
Install
# from npm (prebuilt)
dsh plugin --profile web add @luzhengyangtx/dsh-telegram-duty
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:luzhengyangtx/dsh-telegram-duty
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
Turn DeepSeek Harness (DSH) into your always-on pocket duty office: message a Telegram bot from your phone, the duty agent gets to work, and the result comes back to your phone. Idle cost is zero tokens — the plugin long-polls getUpdates and only wakes an agent when a message arrives.
Requirements
DeepSeek Harness ≥ 0.1.2-rc.1 is required. Since v0.5.0 the plugin reads session history through the
session.snapshotEvents()API, which does not exist in 0.1.1-rc.2 and older (it was introduced indsh-v0.1.2-rc.1). If you are on 0.1.1-rc.2 or older, stay on npm0.4.0— the latest version will fail to load there.
Quick Start (≈10 minutes)
Create a bot — chat with @BotFather, send
/newbot, and copy the token (looks like123456:ABC...).Find your chat id — send any message to @userinfobot (or to your new bot) and read the numeric
id.Install the plugin — from inside a DeepSeek Harness checkout:
dsh plugin --profile web add @luzhengyangtx/dsh-telegram-dutyConfigure — add to
C:\Users\<you>\.dsh\profiles\web\cordis.patch.yml:- id: telegram-duty config: token: '123456:YOUR-BOT-TOKEN' # step 1 chatId: 123456789 # step 2 (whitelist) language: zh # or enRestart
dsh weband send any message to your bot from the phone. Done — the duty agent answers.
While on duty, the web UI shows a banner telling you approvals are forwarded to your phone, with a one-click switch back.
Features
- 📱 Task loop — Telegram message → dedicated "duty" DSH session (its agent has the standard tool set) → final reply back to Telegram; long replies are split under Telegram's message limit.
- ⏳ Instant feedback — every task message is acknowledged immediately ("📨 Received, generating…") and the phone keeps showing the "typing…" animation until the result arrives; errors report a clear "⚠️ Processing error".
- 🎯 Targeted sessions —
/sessionslists your workspace sessions (live and offline, matching the web sidebar; blank drafts are hidden) with numbered buttons: tap one to route following messages there. Prefix one message with#Nto send just that one to session N./dutyreturns to the default duty route. Offline sessions are woken automatically on the first targeted message. - 🔐 Whitelist — only your own chat id is served; other senders are logged and ignored.
- ✅ Global approval forwarding — in duty mode, approval requests from all sessions go to your phone with [✅ Approve] [⛔ Reject] buttons (tap to answer; typing
3 approve / 3 rejectstill works). Unanswered approvals time out to rejected (fail-closed, 10 minutes by default)./awaywarns when the web UI still holds unanswered approvals, and/unblockcancels turns stuck on them. - ❓ telegram_ask tool — when an agent needs you to choose between options, confirm something, or supply missing information, it pushes the question to your phone with one button per option; the duty agent is prompted to prefer it.
- 📣 telegram_notify tool — any session's agent can proactively push a message to your phone (reports, reminders, notifications), no waiting required.
- 🔀 Duty / local toggle
- Enter duty: send any phone message, send
/away, or flipwatchModein the web settings. - Back to local: send any message in the web UI, or send
/back. - In local mode approvals stay in the web popup; in duty mode the popup is paused.
- Enter duty: send any phone message, send
- 🚩 Duty banner — while on duty, a frame-wide web banner shows "approvals are on your phone" with a one-click switch back.
- 📱 Sidebar duty button — a "Duty" action beside Settings opens the duty session in one click (creating/waking it when needed), with a live status dot (amber on duty, gray locally).
- 🗂 Durable — the
getUpdatescursor and the watch mode persist across restarts; backlog is fast-forwarded on the very first run only. - 🌐 English & Chinese — every Telegram message respects the
languagesetting (zh|en, defaulten). - 🌍 Direct-connection friendly — connects directly by default; configure
proxyonly when Telegram is blocked in your network (see the table below).
Configuration
All fields live in the telegram-duty settings namespace (the web Settings page shows it, or put them under the patch row's config:).
| Field | Default | Meaning |
|---|---|---|
token |
— | Bot token (marked secret; never shown in settings UIs). Alternatively set credentialsFile to a JSON file with token / chat_id / proxy. |
chatId |
— | Whitelisted chat id. |
proxy |
(empty) | HTTP proxy used for Bot API calls. Leave empty for a direct connection — only set a local proxy (e.g. http://127.0.0.1:7890) when Telegram is blocked in your network, and keep that proxy running. |
sessionId |
telegram-duty |
Stable DSH session id; created on first message. |
dutyCwd |
process cwd | Workspace directory of the duty session. |
approvalTimeoutMinutes |
10 |
Unanswered approvals are rejected after this. |
watchMode |
local |
local (web popups) or duty (approvals to Telegram). |
language |
en |
Language of all Telegram messages: en or zh. |
dataDir |
<DSH_HOME>/storages/telegram-duty |
Offset persistence directory. |
credentialsFile |
— | Optional JSON file { token, chat_id, proxy } for credentials defaults. |
replyChunkChars |
3800 |
Split threshold for long replies (Telegram limit 4096). |
Phone commands
/help— usage/sessions— list your workspace sessions (live + offline) with numbered buttons; tap a number to route following messages there#N message— send just this one message to session N (numbering from the latest/sessions; expires after 30 minutes)/duty— back to the default duty-session route/away— enter duty mode (approvals go to the phone); warns about approvals the web UI still holds/unblock— cancel turns stuck on unanswered web approvals (resend the task afterwards)/back— return to local mode3 approve/3 reject— answer approval #3; with a single pending approval, bareapprove/rejectworks too.
FAQ
- My bot doesn't reply. Check in order: ① token and chatId are correct; ② you have sent
/startto the bot at least once (bots cannot message first); ③ the DSH log shows notelegram-dutyerrors; ④watchMode/language behave as expected. - How do I find my chat id? Send any message to @userinfobot; it replies with your numeric id.
- Do I need a proxy? No — the plugin connects directly by default. Set
proxy(e.g.http://127.0.0.1:7890for Clash) only when your network blocks Telegram, and keep that proxy running while DSH is up. - Does it cost tokens while idle? No. The plugin long-polls
getUpdates; an agent is woken only when a message arrives. - Does it work when my computer is off? No — the plugin runs together with DSH. Nothing is lost across restarts (the update cursor is persisted to disk).
- What arrives on my phone? Task results with instant acks and a live typing indicator, approval requests from any session (in duty mode), agent questions (
telegram_ask), and proactive agent messages (telegram_notify). - Will the bot answer messages in groups? No — the whitelist only serves your own chat id; everything else is logged and ignored.
- Why does
/sessionsnot list all my sessions? It lists your workspace sessions exactly like the web sidebar: blank drafts are hidden, and sessions appear live (空闲/忙碌) or offline (离线 — woken on the first targeted message). The internal duty session is never listed; the default route is the duty session anyway. - How do I update the plugin? Run
dsh plugin --profile web add @luzhengyangtx/dsh-telegram-duty@<version>(or bump the dependency in your profile'spackage.json).
Roadmap
| Status | Item | Tier |
|---|---|---|
| ✅ shipped (v0.4.0) | targeted sessions (/sessions / #N / /duty) · instant ack + typing · telegram_notify · approval recovery (/unblock) · sidebar duty button · bigger banner · zh/en |
free |
| 🚧 planned (v1.x) | voice-message notes, more handy utilities | free |
| 🔭 preview only | multi-platform (Feishu / WhatsApp / …), team & multi-user mode, cloud hosting, priority support | pro (future) |
Free forever: Telegram duty, global approval forwarding, duty/local toggle, web duty banner, zh/en. The pro row is only a preview — no pro code exists yet; it marks the direction of a future commercial edition.
Custom Services
Need help onboarding, configuration, connecting other platforms, or bespoke features? Email dsh-telegram-duty@outlook.com. Typical engagements start at ¥500 (integration + configuration + Q&A); complex work is quoted case by case. Scope of delivery (working setup + docs + basic Q&A) is agreed before the work starts.
Community & Support
- 💬 Telegram group: https://t.me/+w8w7kAnGniRhZTJk
- 💝 Sponsor on Ko-fi: https://ko-fi.com/luzhengyangtx
- 💝 Sponsor on 爱发电 (afdian): https://afdian.com/a/luzhengyangtx
- ⭐ GitHub: https://github.com/luzhengyangtx/dsh-telegram-duty
Dev
pnpm --filter "@luzhengyangtx/dsh-telegram-duty" run typecheck
node_modules\.bin\vitest.cmd run packages/interaction/telegram-duty/tests
# channel self-check against a real bot:
$env:TG_CREDS='C:\path\to\config.json'
node --import tsx/esm packages/interaction/telegram-duty/scripts/check-telegram.ts
License
MIT. The session-driving pattern (resume/create + summarize) follows @kriskwok/dsh-feishu-gateway (MIT) — see LICENSE.
Links
More in this category
xmanrui/dsh-im★ 1525
Connect IM bots to DeepSeek Harness via QR codes or bot credentials (9 channels: Feishu, WeChat, DingTalk, WeCom, QQ, Slack, Telegram, Discord, and WhatsApp).
shaobeichen/dsh-pocket★ 1396
Remote phone access to the DSH Web UI: scan a QR code for LAN or public (cloudflared tunnel) access with real-time sync, a mobile-adaptive layout, and a settings tab.
inclusionAI/Avernet#deepseek-harness-channel-bcn★ 572
Connects DeepSeek Harness to Avernet's Bot Collaboration Network over WebSocket V2, with automatic onboarding, isolated agent sessions, tool-call events, and multi-bot routing tools.
omdsh-dev/dsh-notification★ 85
Desktop notifications for turn completions, with per-outcome controls and keyword rules.
whyihaveyou/dsh-suite#plugin-notify★ 57
IM webhook and local notifications on turn completion, errors, or approval (Feishu/WeCom/DingTalk/Slack/Discord/custom).
omdsh-dev/dsh-lark★ 55
Lark/Feishu bot channel for DeepSeek Harness: each chat drives its own agent, and tool approvals, model questions, and plan reviews return as cards answered by a button or a reply. Switch workspace and model from the chat (`/cd`, `/model`, `/new`), and run several bots that keep separate sessions and can hand turns to each other in one group.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.