Open-source project
xmanrui/dsh-im avatar
xmanrui/dsh-im

dsh-im bridges twelve IM channels into DeepSeek Harness, and freshly scanned Feishu and QQ bots start wide open

Connects IM bots to DeepSeek Harness via QR code or credentials, supporting Lark, WeChat, DingTalk, WeCom, QQ, Slack, Telegram, Discord and WhatsApp (9 channels).

2,328 stars239 forksJavaScriptMIT

At a glance

What is it?
xmanrui/dsh-im is a plugin, not a server: it needs a local DeepSeek Harness host and then wires twelve chat platforms into one agent, each with its own transport, its own proxy rules and its own attachment limits. It also ships the access defaults, the attachment ceilings and the encryption caveat that decide whether a given channel is usable for you.
Who is it for?
This is a well-specified integration layer for one specific host. If you run DeepSeek Harness locally and want a chat channel that keeps working after a restart, dsh-im covers twelve platforms with the per-channel fallbacks you would otherwise spend weeks writing.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • xmanrui/dsh-im2,328 ★ · +1,936
Star history
Repository2026-08-212026-10-22
xmanrui/dsh-im3922.3k

1,936 Stars in the last 62 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Twelve channels, and the answer comes back twelve different ways

Each channel decides for itself how an answer arrives. Feishu receives over a long connection and can present the work as a native live view, a single real-time process card, or step-by-step messages. DingTalk uses a Stream long connection and streams through an AI Card. The WeCom bot path uses the official WebSocket long connection with a native thinking indicator and tool progress. The WeCom self-built app path is different again: an HTTP callback, configured with the enterprise ID, AgentId, Secret, Token and EncodingAESKey, plus an optional proxy address. Slack runs Socket Mode and prefers the official streaming messages API. Discord connects on Gateway v10 and, in a server text or announcement channel, creates a native thread on the first mention, so later turns in that thread need no new mention.

Then the polling and editing half. WeChat receives over Tencent iLink long polling, shows a typing indicator while it waits, and splits the final reply into segments of 1,800 characters. Telegram uses Bot API long polling, shows progress by editing a placeholder message, and completes the rich text reply in place, falling back to plain text where the platform will not carry rich text. WhatsApp runs a WhatsApp Web long connection, reports read receipts and typing, and edits the same message once a second to show tool progress and the answer as it generates, splitting long replies and falling back to a full text reply when an edit fails. Matrix polls the CS API. iMessage goes through the native Messages.app on macOS, one identity per macOS user account.

Two installers, and the GitHub one asks pnpm for permission

There are two ways in, and the second one is not free.

The stable path installs the published package:

sh
dsh plugin --profile web add -w @xmanrui/dsh-im

Then `dsh web` is restarted, the browser refreshed, and the settings page opened to the IM bots entry. That entry sits at order: 21, pushed as late as it can go in the top-level settings menu, right after Agent Presets, and the plugin page itself no longer keeps the older entry point. Upgrading from an older build leaves existing bots, credentials, workspaces, Agent Presets and session bindings alone.

The source path exists for trying code that has not reached npm:

sh
npx -y github:xmanrui/dsh-im install

That one pulls Git dependencies and builds them directly, and pnpm 10 or later can require the profile's pnpm-workspace.yaml to allow that dependency to run build scripts. The project says plainly that ordinary users should prefer the npm release. Read the second installer as a developer convenience with a configuration step attached, not as an equivalent path. Either way, secrets and tokens go only to the local Harness Host and into protected credential storage; status endpoints and bot lists do not hand them back.

Proxy setup differs per channel, and the long connections skip two env vars

Proxy configuration is three separate mechanisms that do not agree with each other. For Feishu behind a forward proxy, HTTPS_PROXY must be set to a proxy URL that includes the scheme before `dsh web` starts; lowercase https_proxy is also read, and HTTP_PROXY serves as a fallback. Feishu registration and credential verification reuse the SDK's proxy-aware HTTP client, and the message long connection opens its WebSocket through that proxy explicitly. The long connection does not read ALL_PROXY or NO_PROXY at all, so a site that relies on a proxy bypass list has nowhere to put it for that connection.

Telegram takes a different route. If the machine cannot reach the Bot API directly, the requirement is Node.js 22.21 or newer plus Node's own environment proxy support switched on, and the README's example begins:

sh
NODE_USE_ENV_PROXY=1
HTTPS_PROXY=http://proxy:8080

The HTTP_PROXY line that would follow is cut off partway through in the README, so the third line is not spelled out anywhere in it. Whatever a host sets here takes effect only after the Host restarts, which makes proxy changes a restart rather than a reload. The two channels also disagree on scope: Feishu covers both its short requests and its long connection, Telegram covers its polling client, and the README says nothing about proxy variables for the remaining channels.

Newly scanned Feishu and QQ bots accept commands from anyone

Access defaults deserve a careful read. Bots created by QR scan in Feishu and QQ allow every user, in both direct and group chats, to chat and to run built-in commands. The allowlist and the command permissions can be tightened afterwards in the bot's own access settings, and bots that already existed keep whatever they had. So on a fresh Feishu or QQ install the first change worth making is the access settings, before anyone finds the bot.

Feishu group chats add a second filter on top. A group message is taken when another bot explicitly mentions the current one, with no extra switch to enable; messages with no mention, messages that mention only other members or everyone, the bot's own messages, and bot-to-bot direct messages are ignored even when the group response mode is set to answer everything. Those messages stay bound by the group allowlist and the command permissions. The tenant permission behind this is im:message.group_at_msg.include_bot:readonly, requested by default for scanned apps. For an app that already exists or was bound by hand, the missing scope is added through the button labeled 补全权限 or the private-chat command /repair, and it takes effect only after the scan and the publishing approval Feishu requires. Telegram's per-bot direct-message allowlist mode is off unless you turn it on, and WhatsApp answers only its own self-chat by default.

Matrix encryption is labelled non-sensitive-test only by the project itself

Matrix is the channel the project marks as experimental, and its encryption note is unusually blunt. Setup is a homeserver address with an access token, or a user ID and password. Two modes exist. In optional mode the plugin tries to start the crypto engine and, if it fails to start, skips the encrypted messages it receives rather than refusing to run. In required mode a failed engine start means no connection at all.

What is missing is the part that carries the secrecy. Interactive device verification is not implemented, key backup is not implemented, SSSS is not implemented, and media attachment content is not encrypted. The project also states that this release did not verify interop against a real homeserver or against Element, and that the channel should not be treated as a complete end-to-end confidentiality guarantee. So the honest reading is that Matrix here is a convenient way to drive a test room and nothing more. A room whose contents you would not want in a server log is exactly the case this channel does not cover, and the label is the project's own, not an outside opinion.

Deferred delivery brings back the text and nothing else

Timeouts are handled by a shared task tracker rather than by dropping the answer. After a channel reports that the wait for a model reply timed out, the plugin keeps checking the original task and, once it completes, sends the final text to the chat or thread it came from. The same checking resumes after a plugin restart or a reconnection, so a crash in the middle of a long answer does not lose the result. /stop cancels only the turn submitted for the current chat, and once the session has switched the plugin stops sending old-session results back to that chat.

The limits are stated precisely, and they are the sensible ones. Re-sending still obeys each channel's send permissions and quotas. A definite send failure is retried at most three times; when the send result is uncertain the record is kept and automatic retry stops, which is the right call since a blind resend is how a chat ends up with the same answer twice. And the mechanism restores text results and terminal notifications only. It does not replay the question, an approval prompt, or a file tool call, so an interrupted session never silently re-asks you to confirm something. The behaviour is documented in two top-level files, PROACTIVE_DELIVERY.md and PROACTIVE_DELIVERY.en.md, with docs/deferred-delivery.md behind it.

Four adjustable limits inbound on images, none at all outbound on files

Attachments are the one place with real numbers, and the numbers run in one direction only. Inbound, the built-in channels that accept images take JPEG, PNG, WebP, and GIF sent as an image file, with an optional text caption. Each original image may be up to 30 MB and each message up to 20 images. Originals are saved into the session workspace, while the copy handed to the model is scaled or compressed to at most 5 MB per image and 20 MB in total. Past those limits, or on a model without image support, the image is passed as a file instead. All four limits are adjustable under General Settings, then Attachments, then Image input, and a change applies to later messages only. Raising them grows request size, processing time and model cost, and the platform and host still cap you. Feishu needs the tenant permission im:message:readonly to download images or files out of user messages, and it has no narrower image-only permission for that interface.

Outbound, result files have no boundaries at all. The plugin adds no rules on file source, creation time, workspace boundary, extension, content, count, size or validity. A file only has to exist and be readable, whether it was already there or the current task produced it, and the ability is on for every connected bot with no switch and no allowlist. Images go out as native image messages where possible and fall back to a file attachment when a channel refuses them, with no resend when the outcome is uncertain.

Three source trees ship in one package, and the host lives elsewhere

The package is @xmanrui/dsh-im at version 4.34.2, declared as an ES module, with one binary, dsh-im, pointing at bin/dsh-im.mjs. The published file list is unusual in a useful way: lib/, src/ and plugin-src/ all travel together, so the compiled output and two source trees ship in the same tarball. Also included are cordis.patch.yml, seven scripts/verify-*.mjs checks covering interface language, injected context, LAN management, model setting, package contents, session channel logos and session title prefixes, and docs/client-integration.md. The repository root adds worker/ with wrangler.jsonc, a Workers deployment alongside the Node plugin, plus poll.mjs and pollnow.mjs. The README itself is the Simplified Chinese one, with README.en.md linked beside it, and the plugin notes a DeepSeek Harness token sponsorship valued at 2,000 yuan while stating that development and maintenance are done independently by community developers.

What matters more is what the plugin is not. It has no server of its own. The local `dsh web` and DSH Desktop reuse the current Host's internal services: an older Harness through apiProxy, a newer one automatically through Typert Gateway, the Session Controller and the Workspace Controller, so no Harness address needs configuring and nothing detours through a local HTTP port. A harnessBaseUrl set in channel config exists only for old remote HTTP and WebSocket Harnesses, and a failed internal call does not quietly reconnect to a different Host. Each channel can hold several bots with independent connection state, workspace, model and session bindings, and iMessage is the exception because it borrows the local Messages.app identity of one macOS account. Three tags, v4.34.0, v4.34.1 and v4.34.2, carry dates of 2026-10-01 and 2026-10-02 while the last push on the default branch is dated 2026-09-25.

Editorial conclusion

This is a well-specified integration layer for one specific host. If you run DeepSeek Harness locally and want a chat channel that keeps working after a restart, dsh-im covers twelve platforms with the per-channel fallbacks you would otherwise spend weeks writing. It is the wrong tool when you need Matrix confidentiality, because the project says so itself, when you need a Feishu or QQ bot that does not answer strangers, because freshly scanned bots accept anyone until you change the access settings, or when your egress relies on ALL_PROXY or NO_PROXY for the Feishu long connection. Before installing, read the Matrix encryption note, the attachment limit table, and the line about tokens being kept only on the local Host.

Frequently asked questions

How many IM channels does dsh-im support, and how does each one connect?

Twelve built-in channels: Feishu, WeChat, DingTalk, WeCom bot, WeCom self-built app, QQ, Slack, Telegram, Discord, WhatsApp, iMessage and Matrix. Each uses its own transport, from Socket Mode and Gateway v10 long connections to Tencent iLink long polling and the native macOS Messages.app. Matrix is marked experimental.

How do I install dsh-im?

The published package installs with dsh plugin --profile web add -w @xmanrui/dsh-im, after which dsh web is restarted and the settings page opened to the IM bots entry. Unreleased code can be installed with npx -y github:xmanrui/dsh-im install, which builds Git dependencies directly and with pnpm 10 or later can need the profile's pnpm-workspace.yaml to allow that dependency to run build scripts.

Can dsh-im work behind an HTTP proxy?

For Feishu, set HTTPS_PROXY to a proxy URL that includes the scheme before starting dsh web, with lowercase https_proxy and HTTP_PROXY also accepted, and note that the message long connection does not read ALL_PROXY or NO_PROXY. For Telegram you need Node.js 22.21 or newer and NODE_USE_ENV_PROXY=1.

What are the default access permissions on a new dsh-im bot?

Bots created by QR scan in Feishu and QQ let every user chat in direct and group chats and run built-in commands until you change the allowlist and command permissions in the bot's access settings. Bots that already existed keep the settings they had.

Is dsh-im's Matrix message encryption safe for sensitive conversations?

The project does not claim it is. Optional mode skips encrypted messages when the crypto engine fails to start and required mode refuses to connect, but interactive device verification, key backup, SSSS and media attachment content encryption are all unimplemented, and this release did not verify interop against a real homeserver or Element.

What happens in dsh-im when a model reply times out?

The plugin keeps checking the original task and sends the final text to the chat or thread once it completes, including after a restart or a reconnect. A definite send failure is retried up to three times and an uncertain result stops automatic retrying. Questions, approval prompts and file tool calls are not replayed.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/xmanrui-dsh-im.svg)](https://hysenlabs.com/projects/xmanrui-dsh-im)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/xmanrui-dsh-im.svg)](https://hysenlabs.com/projects/xmanrui-dsh-im)