🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

橘波特 std-microblock/orange-bot

DeepSeek Harness
std-microblock10 天前有提交已提供中文文档扫描:含敏感能力

橘波特(Orange):一套 QQ 群聊机器人技术栈,用于 DeepSeek Harness,包含 dsh 插件、对接官方 QQ 机器人的 koishi 桥接,以及给 userbot 用的 crossgram 扩展。单仓库。

36.5/100
综合分
5
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/24 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add std-microblock/orange-bot
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · platform
装得上吗
作者未把包发布到 npm,只能用源码安装
插件本身没问题,是分发方式不同
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 10 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包orange-bot(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/28 06:43:16

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 27 处,展示前 27 条)
  • 读取本地文件
    packages/dsh-plugin-orange/src/backfill.ts:12
    for (const ws of await readdir(sessionsRoot).catch(() => [] as string[])) {
  • 读取本地文件
    packages/dsh-plugin-orange/src/backfill.ts:13
    for (const id of await readdir(join(sessionsRoot, ws)).catch(() => [] as string[])) {
  • 执行外部命令固定命令
    packages/dsh-plugin-orange/src/backfill.ts:19
    const text = execFileSync('zstdcat', [join(sessionsRoot, ws, id, 'session.v4.jsonl.zstd')], { maxBuffer: 512 * 1024 * 1024 }).toString('utf8')
  • 读取本地文件
    packages/dsh-plugin-orange/src/bot.ts:2374
    if (url.startsWith('/')) return { path: url, data: new Uint8Array(await readFile(url)) }
  • 写入本地文件
    packages/dsh-plugin-orange/src/bot.ts:2379
    await mkdir(dir, { recursive: true })
  • 读取本地文件
    packages/dsh-plugin-orange/src/bot.ts:2382
    try { await stat(cached); return { path: cached, data: new Uint8Array(await readFile(cached)) } } catch {}
  • 读取本地文件
    packages/dsh-plugin-orange/src/bot.ts:2382
    try { await stat(cached); return { path: cached, data: new Uint8Array(await readFile(cached)) } } catch {}
  • 发起网络请求
    packages/dsh-plugin-orange/src/bot.ts:2383
    const response = await fetch(url, { signal: AbortSignal.timeout(60_000) })
  • 写入本地文件
    packages/dsh-plugin-orange/src/bot.ts:2389
    await writeFile(cached, data)
  • 写入本地文件
    packages/dsh-plugin-orange/src/bot.ts:2390
    if (path !== cached) await writeFile(path, data)
  • 读取本地文件
    packages/dsh-plugin-orange/src/bot.ts:2404
    const data = await readFile(src)
  • 发起网络请求
    packages/dsh-plugin-orange/src/llm.ts:81
    response = await fetch(`${cfg.baseURL.replace(/ $/, '')}/chat/completions`, {
  • 写入本地文件
    packages/dsh-plugin-orange/src/log.ts:11
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    packages/dsh-plugin-orange/src/log.ts:17
    if ((statSync(file, { throwIfNoEntry: false })?.size ?? 0) > 20 * 1024 * 1024) renameSync(file, file + '.1')
  • 读取本地文件
    packages/dsh-plugin-orange/src/log.ts:17
    if ((statSync(file, { throwIfNoEntry: false })?.size ?? 0) > 20 * 1024 * 1024) renameSync(file, file + '.1')
  • 写入本地文件
    packages/dsh-plugin-orange/src/log.ts:18
    appendFileSync(file, line)
  • 发起网络请求
    packages/dsh-plugin-orange/src/satori.ts:233
    const response = await fetch(`${this.cfg.endpoint}/v1/${method}`, {
  • 发起网络请求
    packages/dsh-plugin-orange/src/satori.ts:253
    const response = await fetch(`${this.cfg.endpoint}/orange/${method}`, {
  • 删除本地文件
    packages/dsh-plugin-orange/src/stickers.ts:167
    if (path) await unlink(path).catch(() => {})
  • 读取本地文件
    packages/dsh-plugin-orange/src/stickers.ts:460
    const raw = await readFile(path).catch(() => undefined)
  • 写入本地文件
    packages/dsh-plugin-orange/src/stickers.ts:474
    await mkdir(this.thumbDir, { recursive: true })
  • 写入本地文件
    packages/dsh-plugin-orange/src/stickers.ts:475
    await writeFile(join(this.dir, file), input.data)
  • 写入本地文件
    packages/dsh-plugin-orange/src/stickers.ts:494
    await writeFile(join(this.thumbDir, name), out.stdout).catch(() => {})
  • 发起网络请求
    packages/dsh-plugin-orange/src/stickers.ts:710
    const response = await fetch(url, { signal: AbortSignal.timeout(60_000) })
  • 执行外部命令来自配置/变量
    packages/dsh-plugin-orange/src/stickers.ts:731
    child = spawn(bin, args, { stdio: ['ignore', 'pipe', 'pipe'] })
  • 写入本地文件
    packages/dsh-plugin-orange/src/store.ts:128
    mkdirSync(dataDir, { recursive: true })
  • 读取本地文件
    packages/dsh-plugin-orange/src/web.ts:241
    const file = await readFile(bot.stickers.filePath(row, thumb)).catch(() => undefined)
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

6 张 · 点击查看大图

README

由 DeepSeek 最新模型翻译生成
orange-bot(橘波特) Orange (橘波特):一个 QQ 群聊机器人,跑在 DeepSeek Harness 上。它盯着同一个 QQ 群的两个视图——官方 bot(koishi)和 userbot(crossgram)——自己判断什么时候该开口,把真正的活儿交给后台任务智能体,再用一个 Web 面板把看到和做过的一切都管起来。 橘波特不是一个只会复读的命令行机器人。它在群里像个人:有人 @ 它、有人接它的话、或者群里在聊它主人维护的项目的 bug 时,它自己判断要不要插一句;需要查证的事情它不瞎猜,而是派给后台任务智能体去翻源码、跑命令、提 PR。它同时接两个平台(QQ 官方 bot,以及和主人共号的 userbot),把同一个物理群的两行视图配对成一行,再在这上面做触发判定、会话、任务和面板。 这个仓库是它的全部代码:一个 dsh 插件 + 两个挂在 Satori 服务上的同伴插件。
展开完整文档(README 剩余 1 节)
orange-bot(橘波特)

Orange (橘波特):一个 QQ 群聊机器人,跑在 DeepSeek Harness 上。它盯着同一个 QQ 群的两个视图——官方 bot(koishi)和 userbot(crossgram)——自己判断什么时候该开口,把真正的活儿交给后台任务智能体,再用一个 Web 面板把看到和做过的一切都管起来。

橘波特不是一个只会复读的命令行机器人。它在群里像个人:有人 @ 它、有人接它的话、或者群里在聊它主人维护的项目的 bug 时,它自己判断要不要插一句;需要查证的事情它不瞎猜,而是派给后台任务智能体去翻源码、跑命令、提 PR。它同时接两个平台(QQ 官方 bot,以及和主人共号的 userbot),把同一个物理群的两行视图配对成一行,再在这上面做触发判定、会话、任务和面板。

这个仓库是它的全部代码:一个 dsh 插件 + 两个挂在 Satori 服务上的同伴插件。

三个包

| 包 | 跑在哪 | 提供什么 |
| packages/dsh-plugin-orange | DeepSeek Harness(dsh web profile) | 机器人本体:双平台接入、消息库、触发合并与分类器、chat 智能体、后台任务 worker、智能体工具、Web 面板 |
| packages/koishi-plugin-orange-bridge | koishi(官方 bot 侧) | 官方 bot 那几个 Satori 表达不了的 HTTP 面:C2C 私聊发送、被动回复、群名、成员角色、撤回、指令表 |
| packages/orange-ext | crossgram(userbot 侧) | 全量消息 WS、历史、合并转发展开、reaction、撤回、成员角色、消息查询、表情(收藏夹) |

orange-ext 是 crossgram monorepo(@mtproto-relay/)里的插件,依赖那边的 @mtproto-relay/bridge 类型,所以它不在本仓库的 pnpm workspace 里:源码放在这儿,装法见它自己的 README。

拓扑

QQ 群 │ ├─ 官方 bot ─ koishi ────────────┐ /satori/v1/ 事件流(Satori op:3 identify) │ + orange-bridge ───┤ /satori/orange/* send / guild / member / recall / commands │ │ └─ userbot ─ crossgram ──────────┤ /satori/v1/message.create 出站发送
+ orange-ext ───────┤ /satori/orange/events (WS) 全量消息流(含自己发的) │ /satori/orange/* history / forward / reaction / sticker / member.roles / …
▼
反向代理(例如 Caddy,两条 HTTPS 入口) │ ▼ dsh(web profile)+ dsh-plugin-orange ├─ SQLite:消息 / 响应 / 分类 / 任务 / 记忆 / 用量 / 表情 / 群角色 ├─ chat 智能体:每个会话一个 session ├─ 后台 task worker:带完整 shell,会开子智能体、会提 PR └─ Web 面板:/orange-api/(会话、响应记录、任务、表情、用量、设置)

截图

面板(dsh web 侧栏「🍊 Orange Bot」里的会话页:左侧是见过的所有群,右侧是两侧合一的消息流 + 触发原因):

会话

| 响应记录:每条回复的触发方式、dry run、这次抽到的子风格 | 后台任务:worker 实时状态与 PR/issue |
| 响应记录 | 后台任务 |

| 记忆与用户:长期记忆、用户标签(谁是开发者) | 用量:按类别与模型计的 token 与花销 |
| 记忆与用户 | 用量 |

| 设置:说话风格、表情、主人项目、旁听主题 | 表情库:类型筛选 / 搜索 / 权重 |
| 设置 | 表情库 |

表情库的导入(从 QQ 收藏夹导入,视觉模型逐张自动分类并写一句话描述):

| 导入进度 | 批量操作 | 表情设置 |
| 导入 | 批量 | 表情设置 |

消息渲染:存下来的文本是给模型看的(<quote>…</quote>、<forward …>、[图片 #12]),面板负责把它摊平成给人看的样子——下面这组是修这个 bug 前后面板画面的对比:

| 修之前:引用里的合并转发直接把 XML 画出来 | 修之后:引用块 + 转发卡片 |
| before | after |

| 引用里的图真的出图 | 我们自己写的那行 > 名字:… 也画成引用块 |
| 引用里的图 | 自渲染引用行 |

装起来

0. 需要什么

- 一台能跑 dsh 的机器(Node ≥ 22);
- 一个 koishi(官方 bot 侧,装 @satorijs/adapter-qq)+ 本仓库的 koishi-plugin-orange-bridge;
- 一个 crossgram(userbot 侧)+ 本仓库的 orange-ext;
- 一个 OpenAI 兼容的模型端点(分类器、聊天、任务、表情分类、风格润色都走它);
- 两条能到上面两个 Satori 服务的 HTTPS 入口(同一域名不同端口也行),因为两边都靠 Bearer token 认证。

1. 把同伴插件装到两侧

koishi 侧:

在 koishi 的 app 目录里
npm install /path/to/orange-bot/packages/koishi-plugin-orange-bridge

koishi.yml
plugins:
orange-bridge:
path: /satori/orange
token: <和 dsh 侧 ORANGE_KOISHI_TOKEN 相同>

crossgram 侧:把那 packages/orange-ext 放进 crossgram monorepo 的 packages 下并加进它的 workspace 依赖,配置 path: /satori/orange、token: <和 ORANGE_CROSSGRAM_TOKEN 相同>、platformId: qqnt;细节见 packages/orange-ext/README.md。

2. 反向代理

/etc/caddy/Caddyfile
https://bot.example.com:41008 { # koishi
@satori path /satori/v1/ /satori/orange/*
handle @satori { reverse_proxy 127.0.0.1:5140 }
respond 404
}

https://bot.example.com:41009 { # crossgram
@satori path /satori/v1/* /satori/orange/*
handle @satori { reverse_proxy 127.0.0.1:3140 }
respond 404
}

3. 挂到 dsh

git clone https://github.com/std-microblock/orange-bot.git
cd orange-bot/packages/dsh-plugin-orange && npm install # prepare 会顺手 build 一次

然后让 dsh web profile 依赖它并把包名加进 bundle 列表(完整步骤与配置表见 packages/dsh-plugin-orange/README.md):

{
"name": "dsh-profile-web",
"private": true,
"dependencies": { "dsh-plugin-orange": "link:/path/to/orange-bot/packages/dsh-plugin-orange" },
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-plugin-orange"],
"patchReload": "live"
}
}
}

4. 首次运行

新装的插件谁都不回答、什么群都不存:admins、白名单、errorGroup 默认都是空的。按顺序:

1. 起 dsh web,在启动日志里拿到带 token 的面板地址,侧栏会出现「🍊 Orange Bot」;
2. 把主人写进 admins(qq:<user_openid> / cg:<uid>),否则群里没人有管理权限;
3. 面板「会话」页把要聊的群加白名单(或管理员在群里 /ai.white add <群号>);
4. 新群先开 dry run:照常思考、照常出响应记录,但不真的发出去。

端点契约

要做别的客户端、或者只想接一侧的话,这张表是全部要求( 是配置里的 endpoint,例如 https://bot.example.com:41008/satori):

| 端点 | 谁提供 | 说明 |
| /v1/ | koishi 自带的 Satori 服务 | 官方 bot 的事件流(op:3 identify)与其余 Satori API |
| /orange/send | orange-bridge | 复用 koishi 的消息编码器发消息:C2C 私聊、被动回复(同一个 msg_id 的 msg_seq 递增)|
| /orange/guild | orange-bridge | 群名/群标签(官方群事件里只有不透明的 group_openid)|
| /orange/member | orange-bridge | 单个群成员的角色(事件没带 member_role 时的兜底)|
| /orange/recall | orange-bridge | 撤回自己发的消息 |
| /orange/commands | orange-bridge | koishi 已注册的指令别名,用来对 koishi 自己的指令保持沉默 |
| /v1/message.create | crossgram 的 satori-exporter | userbot 侧的出站发送 |
| /orange/events(WS) | orange-ext | 全量消息流,含账号自己发出的;连上后第一帧发 {"token":…} 认证,回 {"type":"ready","selfId","loginId"} |
| /orange/message.get message.recall history.list forward.get forward.send reaction.set reaction.catalog member.roles conversation.get sticker.packs sticker.list sticker.asset debug | orange-ext | 见 packages/orange-ext/README.md 的路由表 |

自查(两个 token 分别填):

curl -sS -X POST -H "Authorization: Bearer $ORANGE_CROSSGRAM_TOKEN" \
-H 'Content-Type: application/json' -d '{}' \
https://bot.example.com:41009/satori/orange/debug

开发

pnpm install
pnpm build # 只 build dsh 插件(node 半 + 面板 bundle)
pnpm typecheck # tsc --noEmit

- packages/dsh-plugin-orange:TypeScript + esbuild;改 src/client/ 之后要重新 build 才会生效(面板 bundle 由 dsh 的模块加载器接管,react 与 @deepseek-ai/dsh-client-ui- 由宿主提供)。
- packages/koishi-plugin-orange-bridge:一个 CommonJS 文件,没有构建步骤;改完重启 koishi。
- packages/orange-ext:TypeScript,由 crossgram 自己加载(main 指向 src/index.ts);它依赖 crossgram 的工作区,单独装不起来,改完重启 crossgram。
- 源码注释里出现的 vm/debug/*-test.ts(润色、@ 解析、面板分词、表情…)是作者部署仓库里的离线纯逻辑断言脚本,没有随本仓库发布,因此在本仓库里跑不了。

隐私与安全

- 只存该存的:白名单群(及其配对的对端行)才落消息;非白名单群只留群号/群名;除主人↔机器人的私聊,别人的私聊连会话行都不建。
- 工具按会话收敛:历史、搜索、转发、图片、记忆只覆盖本会话服务的群和它的对端行;跨群要管理员触发,后台任务继承发起者的权限。
- 工具守卫:任何 orange 会话都不许读机器人自己的数据库、凭据与会话记录(~/.orange/、orange.db、/etc/orange、~/.dsh/sessions、进程环境变量里的 token),命中直接拒绝并写日志。
- 入站审查:非管理员触发的消息先过一遍(关键词 + 一次小模型调用,带缓存):套别群/私聊内容、要机器人骂人或网暴、辱骂挑衅、涉政 → 完全不回应,也不进 agent 的上下文,只在响应记录里留一条审计。

许可

MIT © 2026 MicroBlock
预览分享卡片长什么样 ↓std-microblock/orange-bot
分享 · DeepSeek Harness Hub
std-microblock/orange-bot

橘波特(Orange):一套 QQ 群聊机器人技术栈,用于 DeepSeek Harness,包含 dsh 插件、对接官方 QQ 机器人的 koishi 桥接,以及给 userbot 用的 crossgram 扩展。单仓库。

回到顶部