一个插件接入六种海外聊天平台并保留会话
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/2 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add monkey-sking/dsh-im-global该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
dsh 原生插件 · other
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 3 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 49 处,展示前 43 条)
- 读取本地文件lib/index.js:772
const data = JSON.parse(await readFile(localSecretsFile(), 'utf8')); - 写入本地文件lib/index.js:783
await mkdir(path.dirname(file), { recursive: true }); - 读取本地文件lib/index.js:786
data = JSON.parse(await readFile(file, 'utf8')) ?? {}; - 写入本地文件lib/index.js:792
await writeFile(tmp, JSON.stringify(data, null, 2), { mode: 0o600 }); - 写入本地文件lib/index.js:793
await chmod(tmp, 0o600); - 写入本地文件lib/index.js:794
await rename(tmp, file); - 发起网络请求lib/client.js:97
const response = await fetch(ROUTE, { - 发起网络请求lib/discord/api.js:55
response = await this.fetch(url, { - 发起网络请求lib/discord/api.js:99
this.me = await this.request('GET', '/users/@me'); - 发起网络请求lib/discord/api.js:109
return this.request('POST', `/channels/${channelId}/messages`, { body }); - 发起网络请求lib/discord/api.js:114
return this.request('PATCH', `/channels/${channelId}/messages/${messageId}`, { - 发起网络请求lib/discord/api.js:122
await this.request('POST', `/channels/${channelId}/typing`, { retries: 1 }); - 发起网络请求lib/discord/api.js:130
const response = await this.fetch(url, { headers: { 'User-Agent': USER_AGENT } }); - 读取本地文件lib/core/bindings.js:49
const raw = await readFile(this.file, 'utf8'); - 写入本地文件lib/core/bindings.js:130
await mkdir(path.dirname(this.file), { recursive: true }); - 写入本地文件lib/core/bindings.js:132
await writeFile(this.file, payload, 'utf8'); - 读取本地文件lib/settings-store.js:110
const raw = await readFile(this.file, 'utf8'); - 写入本地文件lib/settings-store.js:143
await mkdir(path.dirname(this.file), { recursive: true }); - 写入本地文件lib/settings-store.js:145
await writeFile(tmp, `${JSON.stringify(next, null, 2)}\n`, 'utf8'); - 写入本地文件lib/settings-store.js:146
await rename(tmp, this.file); - 发起网络请求lib/web-api.js:148
const response = await fetch(url, init); - 发起网络请求lib/adapters/matrix.js:38
const me = await this.request('GET', '/_matrix/client/v3/account/whoami'); - 发起网络请求lib/adapters/matrix.js:56
const response = await fetch(`${this.baseUrl}${path}`, { - 发起网络请求lib/adapters/matrix.js:86
const payload = await this.request( - 发起网络请求lib/adapters/matrix.js:119
const payload = await this.request( - 发起网络请求lib/adapters/matrix.js:191
const payload = await this.request( - 发起网络请求lib/adapters/matrix.js:201
return this.request( - 发起网络请求lib/adapters/mattermost.js:45
const me = await this.request('GET', '/users/me'); - 发起网络请求lib/adapters/mattermost.js:66
const response = await fetch(`${this.apiBase}${path}`, { - 发起网络请求lib/adapters/mattermost.js:139
const post = await this.request('GET', `/posts/${postId}`); - 发起网络请求lib/adapters/mattermost.js:202
const post = await this.request('POST', '/posts', { - 发起网络请求lib/adapters/mattermost.js:211
return this.request('PUT', `/posts/${messageId}`, { id: messageId, message: text }); - 发起网络请求lib/adapters/mattermost.js:216
await this.request('POST', '/users/me/typing', { channel_id: convId }); - 发起网络请求lib/adapters/slack.js:67
const response = await fetch(`${SLACK_API}/${method}`, { - 发起网络请求lib/adapters/slack.js:257
const response = await fetch(attachment.url, { - 发起网络请求lib/adapters/zulip.js:47
const me = await this.request('GET', '/users/me'); - 发起网络请求lib/adapters/zulip.js:65
const response = await fetch(`${this.apiBase}${path}`, { - 发起网络请求lib/adapters/zulip.js:87
const payload = await this.request('POST', '/register', { - 发起网络请求lib/adapters/zulip.js:133
const response = await fetch(`${this.apiBase}${path}`, { - 发起网络请求lib/adapters/zulip.js:186
const payload = await this.request('GET', `/streams/${id}`); - 发起网络请求lib/adapters/zulip.js:196
const payload = await this.request('POST', '/messages', { - 发起网络请求lib/telegram/api.js:35
response = await this.fetch(url, { - 发起网络请求lib/telegram/api.js:136
const response = await this.fetch(url);
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh-llm@deepseek-ai/dsh-agent给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成dsh-im-global
DeepSeek Harness 的全球 IM 渠道 —— 一个插件同时接入 Telegram、Discord、Slack、Mattermost、Zulip 和 Matrix,每个会话独立,重启后依然保留。
⚠️ 安全:默认关闭。 渠道没配白名单时,会拒收所有消息(fail-closed),并显示为锁定状态,直到你加上发送者的 user id。接机器人之前,先看 安全:白名单是 fail-closed 的。
它的结构和钉钉 / QQ / 微信 / 飞书 / 企业微信网关(@lijian-ui/dsh-im-gateway)一致:一个插件,一份 channels: [...] 列表,每个平台一个适配器。
为什么做这个
dsh-telegram 验证了这套模型,但它是第三方维护的单平台渠道:语言写死一种,一个包只支持一个平台,想加第二个平台就得再复制一份同样的会话管线。
展开完整文档(README 剩余 1 节)
dsh-im-global
DeepSeek Harness 的全球 IM 渠道 —— 一个插件同时接入 Telegram、Discord、Slack、Mattermost、Zulip 和 Matrix,每个会话独立,重启后依然保留。
⚠️ 安全:默认关闭。 渠道没配白名单时,会拒收所有消息(fail-closed),并显示为锁定状态,直到你加上发送者的 user id。接机器人之前,先看 安全:白名单是 fail-closed 的。
它的结构和钉钉 / QQ / 微信 / 飞书 / 企业微信网关(@lijian-ui/dsh-im-gateway)一致:一个插件,一份 channels: [...] 列表,每个平台一个适配器。
为什么做这个
dsh-telegram 验证了这套模型,但它是第三方维护的单平台渠道:语言写死一种,一个包只支持一个平台,想加第二个平台就得再复制一份同样的会话管线。
这个包把那套管线抽了出来:
lib/index.js plugin: config, channel lifecycle, routing, commands
lib/core/bindings.js conversation -> session (durable, restart-safe)
lib/core/reply.js turn -> message delivery (streaming, platform-agnostic)
lib/core/commands.js /new /status /help parsing
lib/adapters/telegram.js Bot API long polling
lib/adapters/discord.js Gateway WebSocket + REST
lib/adapters/slack.js Socket Mode + Web API
lib/adapters/mattermost.js WebSocket + REST (self-hosted)
lib/adapters/zulip.js event-queue long polling
lib/adapters/matrix.js client-server /sync long polling
加一个平台,只写一个适配器文件。core/ 里什么都不用改。
平台
| type | 入站方式 | 需要公网 URL? | 凭据 |
| telegram | Bot API 长轮询 | 不需要 | bot token(@BotFather) |
| discord | Gateway WebSocket | 不需要 | bot token |
| slack | Socket Mode WebSocket | 不需要 | bot token xoxb-… + app token xapp-… |
| matrix | /sync 长轮询 | 不需要 | access token(+ homeserver URL) |
| mattermost | WebSocket + REST | 不需要 | personal access token + base URL |
| zulip | 事件队列长轮询 | 否 | <bot email>:<api key> + base URL |
WhatsApp / Teams / LINE / Messenger 不在覆盖范围内:它们只把事件投递到公网 HTTPS 回调,这是另一种托管形态,而不是另一个适配器。
安装
从插件市场安装
在 Plugins → Discover → Manage index sources 下添加这个索引:
https://raw.githubusercontent.com/monkey-sking/dsh-im-global/main/index.json
从本地检出安装
plugin_manager({ action: "install_bundle", target: "/Users/you/.local/share/dsh-im-global" })
这个 bundle 会往配置档组合里插入一行:im-global。
配置(两种方式)
设置页(不写 YAML,不重启)
启用一次 bundle,然后打开 Settings → Chat channels。这个页面管理频道列表:添加/删除频道、选择类型、给凭据起名、设置服务器 URL、测试凭据能否连上平台,然后保存。保存会在原地重启受影响的频道——进程不重启。
凭据不在这里编辑。页面只存凭据的名字;密钥留在凭据存储里。
直接保存 token(没有凭据页的构建)
每张频道卡片还有一个 Token(可选,直接保存) 字段。粘贴 token 后按 保存 token——插件会把它写入 ~/.dsh/dsh-im-global/secrets.json(权限 0600),并在 ctx.credentials 和同名环境变量之后作为兜底解析。宿主机没有凭据界面时很有用:频道随后无需触碰宿主机凭据存储就能启动。
cordis.patch.yml
下面的 ## Configure 讲的是基于文件的方式,它始终是底线:在那里钉住的值会一直生效,直到设置页保存了自己的频道列表。
配置
token 放在 ctx.credentials 里,绝不写进配置文件。先创建它们——通过凭据设置页或宿主机的凭据存储——用频道引用的名字:
| platform | default credential | what to paste |
| Telegram | TELEGRAM_BOT_TOKEN | BotFather → /newbot → 它打印的 token |
| Discord | DISCORD_BOT_TOKEN | Bot → Token(Developer Portal → Bot → Reset Token) |
| Slack | SLACK_BOT_TOKEN | OAuth & Permissions → Bot User OAuth Token(xoxb-…) |
| Slack | SLACK_APP_TOKEN | Basic Information → App-Level Tokens(xapp-…,scope connections:write) |
| Matrix | MATRIX_ACCESS_TOKEN | 任意账号的 access token |
| Mattermost | MATTERMOST_TOKEN | Profile → Security → Personal Access Tokens |
| Zulip | ZULIP_BOT_TOKEN | Settings → Bots 里的 <bot email>:<api key> |
然后编辑配置档 cordis.patch.yml 里的 im-global 行:
- id: im-global
config:
channels:
- id: telegram-main
type: telegram
tokenRef: TELEGRAM_BOT_TOKEN
- id: discord-main
type: discord
tokenRef: DISCORD_BOT_TOKEN
cwd: /Users/you/dev/workspace
- id: slack-main
type: slack
tokenRef: SLACK_BOT_TOKEN
appTokenRef: SLACK_APP_TOKEN
- id: matrix-main
type: matrix
tokenRef: MATRIX_ACCESS_TOKEN
homeserverUrl: https://matrix.org
设置存储
设置页会写入 ~/.dsh/dsh-im-global/settings.json(先写临时文件,再原子重命名)。
这个文件存在时,其中的 channels 列表优先于组合配置里的列表;删掉它,就回退到 cordis.patch.yml。
安全:允许名单默认关闭
聊天桥接会把任何能触达机器人的人都变成智能体驱动者,所以这个插件默认关闭。没配允许名单的频道什么都不接受——连主人也不行——在设置页显示为已锁定,所有入站消息一律丢弃并记日志。
要打开某个频道,把发送者的平台用户 id 填进名单,可以在设置页填(允许的用户 id 字段,逗号分隔),也可以在组合配置里填:
- id: im-global
config:
allowedUsers: [123456789] # plugin-level default for every channel
channels:
- id: telegram-main
type: telegram
tokenRef: TELEGRAM_BOT_TOKEN
allowedUsers: [987654321] # per-channel list beats the plugin-level
频道级的 allowedUsers 优先;没配就用插件级的 allowedUsers;两者都没配,频道保持锁定。怎么找用户 id:Telegram = 机器人看到的数字 id,Discord = 数字 snowflake,Slack = 成员 id,Matrix = 完整的 @user:server,Mattermost/Zulip = 数字用户 id。机器人会从每条入站消息记录的 userId 里读到它们。
这是平台和你的智能体之间唯一的闸门。把它当防火墙规则对待:暴露机器人之前先配好,加用户时记得同步维护。
这道闸门覆盖聊天驱动智能体的两种途径:
- 键入的消息,在路由里、任何会话工作开始之前检查;
- 回答待答问题的按钮按下,在适配器里、选项发出之前检查。名单外的发送者按下按钮会被拒绝,提示「你不在允许名单上,无法回答」,永远到不了问题代理——否则群里任何人只要看到键盘就能替主人作答。
Telegram 专属说明
- 用 @BotFather 创建机器人,复制 token。
- 同一个 token 只能有一个进程长轮询。 第二个 getUpdates 会收到 409 Conflict,所以启动这个频道之前,先停用同一个机器人上的其他 Telegram 频道(配置档补丁用 - id: telegram / disabled: true 做到这点)。
- 从别的 Telegram 插件迁过来?把对话带过来:
node scripts/migrate-telegram-bindings.mjs telegram-main
它会复制旧的 chat -> session 映射,同一个聊天就能续上同一个 Harness 会话,不用从零开始。
Mattermost / Zulip 专属说明
- 两者都是自托管,所以 baseUrl 必填(https://chat.example.com)。
- Mattermost 需要 personal access token,socket 用它认证;适配器每 30 秒 ping 一次,否则 socket 会静默。
- Zulip 的事件队列按自己的节奏过期;遇到 BAD_EVENT_QUEUE_ID 时重新注册,而不是抛出错误。
- Zulip 的回复发到同一个 stream 和 topic;私信则回复给发送者。
Discord 注意事项
- 启用 Message Content 特权 intent(Bot → Privileged Gateway Intents)。不启用的话,机器人收到的每条消息 content 都是空的。
- 用 bot scope 邀请机器人,并授予 Send Messages / Read Message History / Attach Files 权限。
- 在聊天里用 !new:开头的 / 被 Discord 自己的斜杠命令 UI 占用,所以 /new 可能被客户端吞掉。!new、!status、!help 在任何地方都能用;私信和其他平台也接受 /…。
Slack 注意事项
- 启用 Socket Mode,创建带 connections:write 的 app-level token。
- 事件订阅:message.channels、message.im(私有频道再加 message.groups)。
- 改完 scope 要重装应用,否则机器人会静默地收不到事件。
Matrix 注意事项
- 任何账号都行;机器人在它能读取的房间里都会回复。
- 适配器会把 since 往前带,所以重启不会重放历史消息。
聊天内命令
| 命令 | 作用 |
| !new / /new | 切换到新会话(旧会话在 Web UI 里仍可读) |
| !status | 会话 id、状态、工作目录 |
| !help | 命令列表 |
普通消息直接发给智能体。平台允许适配器下载图片时,图片会作为附件交给模型。
设计说明
- 会话身份就是平台身份。 会话 id 由 type:conversationId 推导而来(discord:1234…),所以重启后会接着同一个对话,而不是开一个空白会话。
- 创建撞车时回退到恢复。 如果日志已在磁盘上、但存在性探测没发现,创建路径会改走恢复,而不是让每条消息都失败——正是这个 bug 弄坏了 Telegram 频道。
- 每轮只发一条预览消息。 流式输出按 1.6 秒的节奏原地编辑同一条消息,而不是每个 token 发一条。
- 机器人从不回复自己。 适配器在路由前会丢弃 bot 作者、webhook 和自己的用户 id。
- 符合平台习惯的 markdown。 表格转成对齐的等宽块,标题转成加粗,大规模提及会被中和——手机上直接显示原始竖线表格,看起来就像乱码。
引用消息
回复中引用了更早的消息时,会作为短前缀传给模型,这样「这个怎么改」就有指代对象:
[引用 你 的消息] 配置文件在 ~/.dsh/ 下面这个怎么改
| 平台 | 传输中的引用内容 | 状态 |
| Telegram | reply_to_message(完整消息) | ✅ 内联 |
| Discord | referenced_message(完整消息) | ✅ 内联 |
| Slack | 不内联;thread_ts → conversations.replies | ✅ 线程父消息(1 次调用,缓存 2 分钟) |
| Matrix | 只有 event id;m.in_reply_to → /context | ✅ 精确引用的事件(缓存 2 分钟) |
| Mattermost | root_id → GET /posts/{id} | ✅ 线程根消息(缓存 2 分钟) |
| Zulip | 正文里的 markdown 引用块 | ✅ 内联解析,无需额外调用 |
Telegram、Discord 和 Zulip 不需要额外请求。Slack、Matrix 和 Mattermost 会拉取一次被引用的消息,并缓存两分钟,因为它们的协议只携带 id。
交互式提问
智能体提问时(ask_user_question),问题在聊天里作答,而不是只在 Web UI 里:harness 通过 user-questions/request 瀑布流暴露问题,本渠道认领属于自己会话的问题(其余问题落到 Web UI)。
各平台对交互式作答的支持:
| 平台 | 原生提供的控件 | 本插件中的支持 |
| Telegram | 内联键盘(回调按钮、URL 按钮、web_app)。每行 ≤8 个,≤100 个按钮 | ✅ 按钮 —— callback_query + answerCallbackQuery,作答后收起键盘 |
| Discord | 消息组件:按钮(每行 ≤5 个,每条消息 ≤25 个)、选择菜单、模态框。回复需在 3 秒内完成交互 ACK | ⏳ 文本回退(1) … 2) …) |
| Slack | Block Kit actions:按钮(每个 block ≤25 个)、选择菜单、overflow、模态框。经 Socket Mode 到达 —— 无需公网 URL | ⏳ 文本回退 |
| Mattermost | 交互式消息附件(actions),但点击会 POST 到集成 URL | ⏳ 文本回退 |
| Matrix | 无按钮;emoji 回应或纯文本 | ⏳ 文本回退 |
| Zulip | 无按钮;回应和纯文本 | ⏳ 文本回退 |
文本回退到处都能用,且不依赖平台提供任何东西:问题带编号发出,回复 2(或选项标签)即可作答。问题未关闭时,它占用下一条消息 —— 输入 2 是作答,而不是开启新一轮。
审批(user-approval)是独立服务,尚未路由到聊天;需要审批的工具仍会出现在 Web UI 里。
可靠性说明
有四种失败模式做了显式处理,因为从聊天侧看,每一种都像是「机器人坏了」:
- 已归档会话。 内核拒绝已归档会话的每一步(archived-session-gate),表现为 blocked 轮次:消息被接受,却以沉默作答。渠道检测到后,会取消归档该会话;只有取消归档被拒时,才回退到新会话。
- 长时间使用工具的轮次。 本 harness 发布的是已完成的步骤消息,而非原始增量,所以流式预览由两者共同驱动。否则多步轮次在结束前什么都不显示。
- 插件重载。 disposer 同步注册,并在停止适配器前把实例标记为已死。异步副作用把 cordis 的启动完成之后才释放,所以启动期间重载会让旧轮询器继续存活:同一个 bot token 上两个轮询器争抢 getUpdates(409 Conflict),还会悄悄吞掉对方的消息。
- 模型接线缺失。 只配 agentOptions,智能体在组装提示词时没有服务商和模型;installModelSelection 把它同时接到组装和请求路由上。缺了它,一轮对话几毫秒就结束。
测试
node --test test/*.test.js预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
monkey-sking/dsh-im-global
一个插件接入六种海外聊天平台并保留会话
相关插件 · 其他
模型破甲清洗HunYiSage/dsh-purge
⚔️ DeepSeek Harness 网络安全红队工具dsh-purge
★ 16,993已汉化DeepSeek 客户端
73
女娲蒸馏alchaincyf/nuwa-skill
输入名人名字,提炼其思维模型与决策方式供你调用
★ 37,308已汉化DSH
72
逆向分析桥接Fanqi89/dsh-rea
REA(Reverse Engineer Anything)MCP 桥接,用来把 REA 逆向工具接入 DSH 插件
★ 51已汉化DSH
72
语音女友beiyege-01/dsh-voice-ai-girlfriend
语音 AI 女友Voice AI girlfriend for DeepSeek Harness:Whisper…
★ 888已汉化DSH
71.6
红队越狱测试套件Minglink/dsh-infinite-gen-5
DeepSeek v4.1 flash 逆向工具无限五代 求 Star 收藏 ⭐欢迎大家提交项目的改进
★ 3,759已汉化DSH
71.4
协同办公空间dream-num/univer-workspace
一个让人类与 AI Agent 共同创作、协作和审阅的开源 Office 工作空间。
★ 2,269已汉化DSH
70.8
这是你的仓库?认领条目