@zaofan/dsh-qqbot 1.5.25 → 1.5.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/README.md.bak-mobile-1790816881789 +281 -0
- package/README_EN.md +23 -0
- package/client/qqbot-settings.js +32 -14
- package/client/qqbot-settings.js.bak-mobile-css-1790817989692 +4912 -0
- package/client/qqbot-settings.js.bak-touch-1790818115035 +4912 -0
- package/client/qqbot-settings.js.bak-touch2-1790818130561 +4912 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,6 +44,9 @@
|
|
|
44
44
|
**📦 干净又利落**
|
|
45
45
|
发图/撤消息用纯文本就能驱动(回复里写 `[MEDIA:image|路径]` / `[RECALL]`);仓库不含任何机器人凭据与隐私。
|
|
46
46
|
|
|
47
|
+
**📱 不用开电脑 —— 手机也能 7×24 挂着**
|
|
48
|
+
配合社区的 [dsh-mobile-apk](https://github.com/kelai141/dsh-mobile-apk)(**dsh 安卓壳 APK**:WebView UI + 内嵌 Termux 运行时快照,解压即跑),把 dsh 和这只鲸鱼一起装进手机,就是一台**随身在线**的 QQ 机器人 —— 不用为了挂机常开一台电脑。(安卓上通常没有 ONNX 运行时,记得在设置里勾上「**程序兜底(不依赖小模型)**」,见下方说明。)
|
|
49
|
+
|
|
47
50
|
### 📸 效果展示
|
|
48
51
|
|
|
49
52
|
图①:dsh 运行后台——思考过程、工具调用、Token 用量一目了然(配合「回复闸门 reply_gate」可让机器人自主判断该开口还是静默吃瓜);
|
|
@@ -142,6 +145,27 @@ npm install --legacy-peer-deps # 仅安装期解析策略, 不改运行行
|
|
|
142
145
|
|
|
143
146
|
> 跟踪中: 上游 #37 根治后此段可删(版本线收敛后 npm 不再报错)。
|
|
144
147
|
|
|
148
|
+
### 📱 装到手机上(安卓)—— 随身挂机方案
|
|
149
|
+
|
|
150
|
+
[dsh-mobile-apk](https://github.com/kelai141/dsh-mobile-apk) 是社区做的 **dsh 安卓壳 APK**
|
|
151
|
+
(WebView UI + 内嵌 Termux 运行时快照,解压即跑,为 dsh 本地运行设计)。
|
|
152
|
+
把 dsh 装进去之后,再按本文档把本插件装进那个 dsh —— 你就得到一台**不用电脑托管的、7×24 在线的 QQ 机器人**。
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
下载 APK: https://github.com/kelai141/dsh-mobile-apk/releases
|
|
156
|
+
(按手机 CPU 选 arm64 / x86_64)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
装好之后,插件安装方式与电脑上一致(插件页填包名或 GitHub 地址,见上)。
|
|
160
|
+
|
|
161
|
+
> ⚠️ **安卓上务必打开这两个开关**(否则机器人半天不理人):
|
|
162
|
+
>
|
|
163
|
+
> 1. **设置 → 智能回复 → 勾选「程序兜底(不依赖小模型)」**
|
|
164
|
+
> 手机环境通常拿不到 ONNX 运行时,真实小模型会加载失败;勾上程序兜底后改用
|
|
165
|
+
> **字符 n-gram 字面相似**(零依赖、零 token),智能回复照常工作。
|
|
166
|
+
> 2. **系统设置 → 电池 → 允许后台运行 / 加入省电白名单**
|
|
167
|
+
> 否则系统会在息屏后杀掉 Termux 进程,机器人就离线了。
|
|
168
|
+
|
|
145
169
|
### 首次启动与绑定
|
|
146
170
|
|
|
147
171
|
启动 `dsh web` 后,若未配置凭据会自动进入**扫码引导**:终端输出二维码 → 手机 QQ 扫码绑定 → 凭据自动保存,重启不丢(设置面板里也可随时「扫码绑定」/改账号)。
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# @zaofan/dsh-qqbot
|
|
2
|
+
|
|
3
|
+
[](./LICENSE) -blue)
|
|
4
|
+
|
|
5
|
+
基于 [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) 的 QQ Bot IM 插件**增强 fork**:将 QQ 消息平台作为 dsh agent 的前端协议驱动,并加入表情包图库、富媒体收发、定时任务、多实例人格、好感度系统、可视化设置面板等能力。
|
|
6
|
+
|
|
7
|
+
📦 仓库: [gcry13067381632-jpg/dsh-qqbot](https://github.com/gcry13067381632-jpg/dsh-qqbot)(fork 自 [tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot);改动都在这一侧,升级/重装上游会被冲掉)
|
|
8
|
+
|
|
9
|
+
> ⭐ **用得顺手的话,麻烦点一下右上角的 Star** —— 它是这个项目"有人在用"的唯一可见信号,也是继续更新的动力。
|
|
10
|
+
|
|
11
|
+
中文 | [English](./README_EN.md) | 📖 **[用户手册](./docs/USER-GUIDE.md)**(配置项 / 内置命令 / 自定义扩展 / 群管理 / 智能回复 / 架构…)
|
|
12
|
+
|
|
13
|
+
## 🐋 她能帮你……
|
|
14
|
+
|
|
15
|
+
**一句话**:把 dsh 的 QQ 机器人养成"活鱼"——会存表情包、会挑图回你、到点自己开口、记得住谁跟它亲疏远近,一台电脑还能同时养好几条性格不同的鲸鱼。
|
|
16
|
+
|
|
17
|
+
**🤳 群里发的图,她偷偷全存进小图库**
|
|
18
|
+
自动去重、分「待整理/收藏/回收站」;你说一句"发个开心点的图",她自己搜库、自己挑、自己发,还会挑场合出手(冷场不发、刷屏限量、同图不连发)。
|
|
19
|
+
|
|
20
|
+
**⏰ 到点她自己会开口**
|
|
21
|
+
"每天早 9 点去群里说早安""30 秒后提醒我喝水"——她说到做到,准点冒泡。
|
|
22
|
+
|
|
23
|
+
**🧑🤝🧑 一个电脑,N 条鲸鱼同时在线**
|
|
24
|
+
每条号独立 AppID、独立人格、独立图库/定时/闸门,互不串号;Web 页「扫码绑定」手机一扫就上岗。
|
|
25
|
+
|
|
26
|
+
**💗 她记得住谁亲谁疏**(v1.5.1)
|
|
27
|
+
两个维度分开算:**熟识度**(她把你记得多牢——来过几天、说过多少、被点名、接话,慢变量)与**好感度**(她对你什么态度——随每次互动可升可降)。面板里一行一个人,还能**一键导出 Excel** 拿去分享。
|
|
28
|
+
- 判定用一个跑在本机的小模型读她的「思考(内心)」与「正文(说出口的话)」:**心里不肯但话仍照顾 = 让步**、**心里亲近而嘴上冷淡 = 不算数**(扣不扣看内心,不看表面)
|
|
29
|
+
- 好感度只影响"她愿不愿意自己开口"的松紧:**负好感也只是少主动,绝不冷落、阴阳、攻击**
|
|
30
|
+
- 群消息聚合时按「价值 × 好感」**加权平均**综合判断,不会被一句话钓走
|
|
31
|
+
|
|
32
|
+
**💬 悬浮球 dock:她的随身控制台**
|
|
33
|
+
设置面板右下角的小球,点开就是一整个操作台:**💬 聊天**(像 QQ 一样回放群/私聊记录,气泡+头像,图能放大、本地视频能播、SILK 语音转 mp3 直接听、文件出下载卡,还能在聊天框里直接发文字/插图/发文件——长文本自动拆条连发不被吞);**📥 入群审批**、**🔇 禁言**(机器人为群管理员时,有人申请进群她会提醒你,回句"通过/拒绝"就批);**⚙️ 出站**(适配主动:刚收到真人消息时前几条带引用回你、连发自动转独立消息,定时/后台推送不打扰)。
|
|
34
|
+
|
|
35
|
+
**🛡️ 群主/群管好帮手**
|
|
36
|
+
入群审批 + 禁言 + 查成员,全走官方接口,出错给"人话"(不是管理员/不能禁群主……都告诉你为什么)。
|
|
37
|
+
|
|
38
|
+
**🗂️ 归档了也会自己回家**
|
|
39
|
+
不小心把她的会话点了「归档」藏进侧边栏深处?不用满世界找——那个会话**下次在 QQ 里被消息触发时**,会自己从归档里摘出来回到侧边栏(你主动归档的其它会话不受影响)。
|
|
40
|
+
|
|
41
|
+
**🖥️ 不碰配置文件,设置面板点点点**
|
|
42
|
+
怎么回、能发什么图、什么时候开口、什么人格——面板上改完保存即生效(只有增删账号才要重启)。还有 ✏️ 预设人格编辑器,直接在网页里改她的"性格文件"。
|
|
43
|
+
|
|
44
|
+
**📦 干净又利落**
|
|
45
|
+
发图/撤消息用纯文本就能驱动(回复里写 `[MEDIA:image|路径]` / `[RECALL]`);仓库不含任何机器人凭据与隐私。
|
|
46
|
+
|
|
47
|
+
### 📸 效果展示
|
|
48
|
+
|
|
49
|
+
图①:dsh 运行后台——思考过程、工具调用、Token 用量一目了然(配合「回复闸门 reply_gate」可让机器人自主判断该开口还是静默吃瓜);
|
|
50
|
+
图②:QQ 群里的抓鬼游戏互动——该回就回、该藏就藏,角色扮演全自动;
|
|
51
|
+
图③:斗图实战——机器人用自己收藏的表情包接招回击,图、文分开两条连发。
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
图④:群管理·入群审批全流程——【群管·入群申请】事件注入 + 【审批轮询】双链路触发,AI 核对 openid 台账后自动放行;
|
|
60
|
+
图⑤:QQ 群内放行成功效果——小号申请入群被自动审批通过。
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+

|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 安装
|
|
69
|
+
|
|
70
|
+
> ## ⚠️ 必须装到 `web` profile
|
|
71
|
+
>
|
|
72
|
+
> 本插件的**前端设置面板**(dock 球 + 「设置 → QQ 机器人」整页)只在 **`web` profile** 里生效。
|
|
73
|
+
> 装到别的 profile 会得到一个 **没有设置面板的裸环境** —— dock 球和设置页都不会出现,
|
|
74
|
+
> 表现为"插件装了但看不到任何界面"。
|
|
75
|
+
>
|
|
76
|
+
> **正常使用 `dsh web` 的用户不用关心**(`dsh web` 默认就是 `web` profile,插件页/插件市场装机也落在它上面)。
|
|
77
|
+
> 只有手动指定过 `--profile <其它名字>` 的场景才需要注意。
|
|
78
|
+
> 也不要 `add @tencent-connect/dsh-qqbot`——那会装上游官方版(无本 fork 增强功能)。
|
|
79
|
+
>
|
|
80
|
+
> ✅ **单包自含,装一个就全有**:QQ 机器人 + Web 可视化设置面板(host 桥 + 设置页 UI)都打包在
|
|
81
|
+
> 这一个包内——装完它,dsh Web「设置」里就会出现「QQ 机器人 (im-qqbot)」面板(多账号时每个实例各一页),
|
|
82
|
+
> **无需再单独安装 dsh-qqbot-settings**。
|
|
83
|
+
|
|
84
|
+
### 方式一(发布到 npm 后):一条命令
|
|
85
|
+
|
|
86
|
+
```powershell
|
|
87
|
+
npx @deepseek-ai/dsh plugin --profile web add @zaofan/dsh-qqbot
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> 尚未发布到 npm 前,请用下面的方式二。
|
|
91
|
+
|
|
92
|
+
> ⚠️ **从 GitHub 直装(`github:gcry13067381632-jpg/dsh-qqbot`)需要编译产物 `dist/`,而仓库不提交 `dist`。**
|
|
93
|
+
> 包已声明 `prepare` 脚本,pnpm 安装时会自动编译;
|
|
94
|
+
> 若你的包管理器没自动跑(或报 `failed to import` / 找不到 `dist/index.js`),
|
|
95
|
+
> 手动在插件目录执行一次 `npm run build` 即可。**最省事的做法是用 npm 上的发布版**(`@zaofan/dsh-qqbot`)。
|
|
96
|
+
|
|
97
|
+
### 方式二:源码分发(当前推荐)
|
|
98
|
+
|
|
99
|
+
**Windows(一键脚本)**:
|
|
100
|
+
|
|
101
|
+
```powershell
|
|
102
|
+
git clone https://github.com/gcry13067381632-jpg/dsh-qqbot.git
|
|
103
|
+
cd dsh-qqbot
|
|
104
|
+
.\install.ps1 # 自动 install/build → pack → add tarball → 输出重启指引
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> 若系统禁止运行脚本,改用:`powershell -ExecutionPolicy Bypass -File .\install.ps1`
|
|
108
|
+
|
|
109
|
+
**macOS / Linux(手动)**:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
git clone https://github.com/gcry13067381632-jpg/dsh-qqbot.git
|
|
113
|
+
cd dsh-qqbot
|
|
114
|
+
npm install && npm run build
|
|
115
|
+
pnpm pack --pack-destination /tmp
|
|
116
|
+
npx @deepseek-ai/dsh plugin --profile web add /tmp/zaofan-dsh-qqbot-0.4.0.tgz
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> 💡 为什么打 tarball、而不是 `add` 源码目录?实测教训:
|
|
120
|
+
> ① 目录路径含空格时 Windows 会把参数在空格处拆碎(pnpm 报 `- isn't supported`);
|
|
121
|
+
> ② `add` 目录 = pnpm link(junction),插件无法按"代码位置"反推 profile → 扫码凭据落不了盘,只能走环境变量。
|
|
122
|
+
|
|
123
|
+
### 构建与部署(源码)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm install # 安装依赖(peer 依赖由 dsh 宿主解析)
|
|
127
|
+
npm run build # 或: node node_modules/typescript/lib/tsc.js -p tsconfig.json
|
|
128
|
+
npm run check:package # 发布前自检(单包四项家当齐全)
|
|
129
|
+
pnpm pack # 打 tarball(供 dsh plugin add 安装)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
仓库根的 `install.ps1` 提供 Windows 一键安装(自动 pack 到无空格目录 → add → 重启提示)。
|
|
133
|
+
|
|
134
|
+
### 排障: npm 安装报 ERESOLVE(2026-09-06 移植上游 PR #42)
|
|
135
|
+
|
|
136
|
+
首次 `npm install` 可能报 `ERESOLVE could not resolve`——原因: `@deepseek-ai/dsh-tools`/`dsh-agent` 等 peer 依赖仍在 prerelease(-rc) 版本线,npm 7+ 严格解析拒绝不相交组合。**这是上游版本线问题,不是插件 bug**,两条绕过路:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm install --legacy-peer-deps # 仅安装期解析策略, 不改运行行为
|
|
140
|
+
# 或: 装完依赖后手动 build + pack(peer 由 dsh 宿主解析, 不受影响)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
> 跟踪中: 上游 #37 根治后此段可删(版本线收敛后 npm 不再报错)。
|
|
144
|
+
|
|
145
|
+
### 首次启动与绑定
|
|
146
|
+
|
|
147
|
+
启动 `dsh web` 后,若未配置凭据会自动进入**扫码引导**:终端输出二维码 → 手机 QQ 扫码绑定 → 凭据自动保存,重启不丢(设置面板里也可随时「扫码绑定」/改账号)。
|
|
148
|
+
|
|
149
|
+

|
|
150
|
+
|
|
151
|
+
> **提示**:建议使用 `0.4.0` 以上版本扫码,支持点击链接在浏览器打开,避免部分终端二维码渲染错位的问题。
|
|
152
|
+
|
|
153
|
+
### 还没有 QQ 机器人?先注册一个(拿 AppID / AppSecret)
|
|
154
|
+
|
|
155
|
+
1. 打开 [QQ 开放平台](https://q.qq.com),用 QQ 号登录;
|
|
156
|
+
2. 进入「机器人」→「创建机器人」,填好名称、头像、简介;
|
|
157
|
+
3. 创建完成后在机器人详情页拿到 **AppID** 与 **AppSecret**;
|
|
158
|
+
4. 在 dsh Web → 设置 →「QQ 机器人」→「账号与预设」里填入并保存
|
|
159
|
+
(或设为环境变量 `QQBOT_APPID` / `QQBOT_SECRET`);
|
|
160
|
+
5. 按需在平台开通**单聊/群聊**消息权限(群聊一般需要提交用途审核)。
|
|
161
|
+
|
|
162
|
+
> 💡 更省事:机器人建好即可,首次启动直接**扫码绑定**,不用手抄凭据。
|
|
163
|
+
|
|
164
|
+
## 常见问题
|
|
165
|
+
|
|
166
|
+
### 升级 dsh 0.1.6 后,恢复会话报 `workflow-worker-thread ... cannot be resolved`?
|
|
167
|
+
|
|
168
|
+
dsh **0.1.6** 把工作流执行器插件**改了名,且不保留别名**:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
@deepseek-ai/dsh-workflow-worker-thread → @deepseek-ai/dsh-workflow-ptc
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- **用官方预设的人不受影响** —— 官方预设文件随 dsh 升级一起更新(0.1.6 里已是 `workflow-ptc`)。
|
|
175
|
+
- **从旧版官方预设「复制」出来的自定义预设会挂** —— 副本不会跟着升级走,仍写着旧名,于是报:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
preset "xxx" failed to mount: row "workflow-worker-thread"
|
|
179
|
+
names a plugin that cannot be resolved: @deepseek-ai/dsh-workflow-worker-thread
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**修复**:把预设目录 `~/.dsh/.agent-presets/<预设名>/agent.cordis.yml` 里的旧名改成新名(`id` 与 `name` 各一处),或直接跑仓库里的一键脚本:
|
|
183
|
+
|
|
184
|
+
```powershell
|
|
185
|
+
# 先检查(不改任何文件)
|
|
186
|
+
pwsh -File scripts/fix-dsh-016-presets.ps1
|
|
187
|
+
|
|
188
|
+
# 确认后修复(自动备份为 *.bak-dsh016-<时间戳>)
|
|
189
|
+
pwsh -File scripts/fix-dsh-016-presets.ps1 -Apply
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
改完重启 dsh 让预设重新挂载。
|
|
193
|
+
|
|
194
|
+
> 同一批变更里还有几个已改名/移除的包,脚本会一并检测:
|
|
195
|
+
>
|
|
196
|
+
> | 旧名 | 处理 |
|
|
197
|
+
> |---|---|
|
|
198
|
+
> | `@deepseek-ai/dsh-code-runtime` | 改名为 `@deepseek-ai/dsh-ptc-runtime` |
|
|
199
|
+
> | `dsh-code-runtime-worker-thread` | 已移除,需删行 |
|
|
200
|
+
> | `dsh-tool-subagent-report` | 已移除,需删行 |
|
|
201
|
+
> | `dsh-agent-spine-demo` | 已移除,需删行 |
|
|
202
|
+
>
|
|
203
|
+
> ⚠️ 注意:**注释里**提到旧名字不会导致挂载失败(脚本也只检查非注释行)。
|
|
204
|
+
|
|
205
|
+
### 默认模型是哪个?
|
|
206
|
+
|
|
207
|
+
dsh 0.1.6 起默认模型为 `deepseek-official/deepseek-flash`(旧的 `deepseek-v4-flash` 已移出默认模型列表)。插件在「什么都没配」时的兜底也已同步为该值;你在设置里显式指定的 provider/model 优先级更高。
|
|
208
|
+
|
|
209
|
+
### 升级 dsh 0.1.7 后,恢复会话报「预设缺失」/ 机器人不理人?
|
|
210
|
+
|
|
211
|
+
dsh **0.1.7** 把 Agent 预设从「目录里的 yml 文件」换成了「**profile 配置里的声明行**」:
|
|
212
|
+
由 `@deepseek-ai/dsh-agent-preset-registry` + 每个预设一行 `@deepseek-ai/dsh-agent-preset` 组成。
|
|
213
|
+
官方明确:**注册表不扫描目录、不接受 preset 路径、没有任何接口接受 YAML 写回** —— 所以旧目录预设**必须迁移**,否则会话日志里记着的 preset ID 找不到定义,**恢复会话会被拒绝**。
|
|
214
|
+
|
|
215
|
+
**一键迁移**(仓库里 `scripts/migrate-presets-017.ps1`):
|
|
216
|
+
|
|
217
|
+
```powershell
|
|
218
|
+
# ① 先看看会迁移哪些预设(不写任何文件)
|
|
219
|
+
pwsh -File scripts/migrate-presets-017.ps1
|
|
220
|
+
|
|
221
|
+
# ② 生成「原 patch + 迁移片段」的合并版,核对一眼
|
|
222
|
+
pwsh -File scripts/migrate-presets-017.ps1 -Merge -Out "$env:TEMP\cordis.patch.yml.0.1.7-ready"
|
|
223
|
+
|
|
224
|
+
# ③ 升级 dsh 到 ≥0.1.7 之后启用(⚠️ 顺序不可反!0.1.6 读不了新声明会加载失败)
|
|
225
|
+
cd ~/.dsh/profiles/web
|
|
226
|
+
Copy-Item cordis.patch.yml cordis.patch.yml.bak-0.1.6
|
|
227
|
+
Move-Item "$env:TEMP\cordis.patch.yml.0.1.7-ready" cordis.patch.yml -Force
|
|
228
|
+
# 然后重启 dsh
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> ⚠️ 两个坑(脚本已自动处理):patch 顶层若是空数组 `[]` 要**先删掉**再粘;新增条目必须用 `- insert:`(写成 `- id:` 会报 `entry not found`)。
|
|
232
|
+
> ⚠️ 0.1.7 会把**会话日志升级为 V4(不可回退)**、`settings.yaml` 也只导入一次 —— 升级前记得备份整个 `~/.dsh`。
|
|
233
|
+
|
|
234
|
+
### 升级 dsh 0.1.7 后,某些插件被「禁用 / 跳过」?
|
|
235
|
+
|
|
236
|
+
0.1.7 新增了**插件版本兼容检查**(比对各插件的 `peerDependencies`)。钉死在旧次版本的包会被跳过,或整行被禁用:
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
dsh: disabling profile plugin row "mcp-chrome": Plugin ... is incompatible with dsh 0.1.7-rc.1
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
常见两例:
|
|
243
|
+
|
|
244
|
+
- `@deepseek-ai/dsh-mcp-client@0.0.1-rc.1` → 让所有 `mcp-*` 行失效。**注意官方这个包的 `latest` 标签还停在旧版,`next` 才是新版** → 要显式装 `@deepseek-ai/dsh-mcp-client@0.1.7-rc.1`
|
|
245
|
+
- `@dhicoc/dsh-reverse-skill@1.0.5` → 跳过(作者尚未适配 0.1.7)
|
|
246
|
+
|
|
247
|
+
想强行运行,可用官方提供的**确切版本例外**(`dsh plugin allow-version`,或插件管理页里授予),但官方警告"可能崩溃或数据丢失",自行权衡。
|
|
248
|
+
|
|
249
|
+
## 🚫 无上下文模式(按会话省 token)
|
|
250
|
+
|
|
251
|
+
让某个 QQ 会话**每轮只记得最近几条对话**,更早的历史自动折叠 —— 群聊省 token 利器。
|
|
252
|
+
|
|
253
|
+
**怎么开**:dock 面板(右下角 🛡)→「⚙ 单会话设置」→ 子标签「🚫 无上下文」→ 勾选 + 填「带 @ 前 N 条」→ 保存。
|
|
254
|
+
|
|
255
|
+
**几点先说清**(细节见手册):
|
|
256
|
+
- **web 记录不删** —— 只多一行「上下文已压缩」折叠标记
|
|
257
|
+
- **N 计的是 dsh 侧的消息条数**,1 条可能含多条 QQ 消息(入站时会聚合)
|
|
258
|
+
- **零额外模型调用**(替身文本写死)
|
|
259
|
+
- **从下一轮起生效**(压缩发生在回合开始)
|
|
260
|
+
|
|
261
|
+
📖 完整说明(含易误解点、全局开关、存储位置)→ **[用户手册](docs/USER-GUIDE.md#无上下文模式按会话省-token)**
|
|
262
|
+
|
|
263
|
+
## ✅ QQ 审批卡片:谁可以点
|
|
264
|
+
|
|
265
|
+
发起审批的卡片**发到发起者所在的会话**(群里就发到群里),但**只有下列人能点按钮**:
|
|
266
|
+
**① 发起者本人 ② 主人白名单**(`groupAdmin.owners`)。
|
|
267
|
+
|
|
268
|
+
> ⚠️ **群聊场景一定要填白名单**:设置 → QQ 机器人 →「允许操作的主人 openid(逗号分隔,可留空=不校验)」。
|
|
269
|
+
> 否则别人发起的审批,主人点了没反应。
|
|
270
|
+
|
|
271
|
+
📖 细节 → **[用户手册](docs/USER-GUIDE.md#qq-审批卡片谁可以点)**
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 支持这个项目
|
|
276
|
+
|
|
277
|
+
如果这个插件帮你省了 token、或者让你家的鲸鱼更活蹦乱跳 —— **给个 ⭐ Star** 就是最实在的支持;有 bug / 想要的功能,欢迎开 [Issue](https://github.com/gcry13067381632-jpg/dsh-qqbot/issues)。
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
[MIT](./LICENSE)
|
package/README_EN.md
CHANGED
|
@@ -30,6 +30,9 @@ The little ball at the corner of the settings panel opens a whole control deck:
|
|
|
30
30
|
**🖥️ No config-file surgery** — reply pacing, sticker gates, scheduled wake-ups, outbound mode and per-bot personas are all in the panel; saving applies live (only adding/removing bots needs a restart). There is even a ✏️ persona editor to tweak her "personality file" right in the browser.
|
|
31
31
|
|
|
32
32
|
**📦 Clean & lean** — drive image sending / recall from plain text (`[MEDIA:image|path]` / `[RECALL]`); the repo contains no bot credentials or private data.
|
|
33
|
+
|
|
34
|
+
**📱 No PC needed — she runs 24/7 on your phone**
|
|
35
|
+
Pair her with the community [dsh-mobile-apk](https://github.com/kelai141/dsh-mobile-apk) (an **Android shell APK for dsh**: WebView UI + a bundled Termux runtime snapshot, unzip-and-run) and you get a QQ bot that stays online from your pocket — no dedicated computer to keep running. (Android usually has no ONNX runtime, so remember to tick **“Program fallback (no small model)”** in Settings — see the Android section below.)
|
|
33
36
|
|
|
34
37
|
### 📸 Showcase
|
|
35
38
|
|
|
@@ -146,6 +149,26 @@ npm install --legacy-peer-deps # install-time resolution only; runtime behav
|
|
|
146
149
|
|
|
147
150
|
> Tracked: once upstream #37 is fixed and version lines converge, this section can be removed.
|
|
148
151
|
|
|
152
|
+
### 📱 Deploy on Android — the pocket 24/7 setup
|
|
153
|
+
|
|
154
|
+
[dsh-mobile-apk](https://github.com/kelai141/dsh-mobile-apk) is a community-built **Android shell APK for dsh**
|
|
155
|
+
(WebView UI + a bundled Termux runtime snapshot — unzip and run). Install dsh there, then install this plugin
|
|
156
|
+
into that dsh exactly as documented above, and you end up with a QQ bot that runs **24/7 from your phone**,
|
|
157
|
+
with no PC left on.
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Download APK: https://github.com/kelai141/dsh-mobile-apk/releases
|
|
161
|
+
(pick arm64 / x86_64 to match your phone's CPU)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
> ⚠️ **Two switches you must turn on for Android** (otherwise she silently stops replying):
|
|
165
|
+
>
|
|
166
|
+
> 1. **Settings → Smart reply → tick “Program fallback (no small model)”**
|
|
167
|
+
> Phones usually have no ONNX runtime, so the real embedding model fails to load. With the fallback on,
|
|
168
|
+
> scoring switches to **character n-gram lexical similarity** (zero dependencies, zero tokens) and works.
|
|
169
|
+
> 2. **System settings → Battery → allow background activity / add to the power-saving whitelist**
|
|
170
|
+
> Otherwise the OS kills the Termux process once the screen is off and the bot goes offline.
|
|
171
|
+
|
|
149
172
|
### First launch & binding
|
|
150
173
|
|
|
151
174
|
Start `dsh web`. If credentials are missing, the **QR flow** starts automatically: a QR code is printed in the
|
package/client/qqbot-settings.js
CHANGED
|
@@ -1718,7 +1718,7 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
1718
1718
|
// · 群管理(发消息/审批入群/禁言) → 悬浮球点开成操作台
|
|
1719
1719
|
var DOCK_CSS = "#qqs-dock-wrap{position:fixed;right:18px;bottom:190px;z-index:9997;font-family:-apple-system,'PingFang SC','Microsoft YaHei',sans-serif}#qqs-dock-ball{width:52px;height:52px;border-radius:50%;background:linear-gradient(160deg,#7c6cf0,#5b4fd8);color:#fff;font-size:24px;line-height:52px;text-align:center;cursor:pointer;box-shadow:0 6px 20px rgba(90,70,220,.4);user-select:none;transition:transform .12s,box-shadow .12s;position:relative}#qqs-dock-ball:hover{transform:scale(1.06)}#qqs-dock-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;border-radius:9px;background:#ff4d4f;color:#fff;font-size:11px;font-weight:700;line-height:18px;padding:0 4px;box-sizing:border-box;text-align:center;display:none}#qqs-dock-panel{position:fixed;right:18px;bottom:190px;z-index:9998;width:min(720px,94vw);max-height:68vh;display:none;flex-direction:column;background:#fff;border:1px solid #d9c6ff;border-radius:16px;box-shadow:0 12px 40px rgba(60,40,140,.25);overflow:hidden;font-family:-apple-system,'PingFang SC','Microsoft YaHei',sans-serif}#qqs-dock-panel .dk-h{display:flex;align-items:center;gap:8px;padding:10px 14px;background:linear-gradient(90deg,#7c6cf01f,#7c6cf008);font-size:14px;font-weight:700;color:#4a3a9f;border-bottom:1px solid #efe8ff}#qqs-dock-panel .dk-b{padding:10px 14px;overflow:auto;font-size:13px;color:#1f2329}#qqs-dock-panel .dk-tab{display:flex;gap:4px;border-bottom:1px solid #eee;margin-bottom:10px}#qqs-dock-panel .dk-tab button{font:inherit;font-size:13px;padding:6px 14px;border:none;background:none;cursor:pointer;color:#666;border-bottom:2px solid transparent}#qqs-dock-panel .dk-tab button.on{color:#4a3a9f;font-weight:700;border-bottom-color:#7c6cf0}#qqs-dock-panel select.qqs-sel,#qqs-dock-panel input.qqs-txt,#qqs-dock-panel textarea.qqs-txt{font:inherit;color:#1f2329;background:#fff;border:1px solid #d0d5dd;border-radius:8px;padding:5px 8px;outline:none}#qqs-dock-panel .dk-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:6px 0}#qqs-dock-panel .dk-btn{font:inherit;font-size:13px;padding:5px 12px;border-radius:8px;cursor:pointer;border:1px solid #d9c6ff;background:#f1ecff;color:#4a3a9f}#qqs-dock-panel .dk-btn.ok{background:#e6f7ec;color:#187a3d;border-color:#b8e6c8}#qqs-dock-panel .dk-btn.no{background:#fdeeee;color:#c23131;border-color:#f3c4c4}#qqs-dock-panel .dk-btn:disabled{opacity:.5;cursor:default}#qqs-dock-panel .dk-msg{color:#888;font-size:12px;padding:2px 0}#qqs-dock-panel .dk-list{max-height:34vh;overflow:auto;border:1px solid #f0ecff;border-radius:10px;padding:4px}#qqs-dock-panel .dk-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-bottom:1px solid #f5f2ff;flex-wrap:wrap;font-size:13px}#qqs-dock-panel .dk-item:last-child{border-bottom:none}#qqs-dock-panel .dk-empty{color:#aaa;text-align:center;padding:18px 0;font-size:12px}"
|
|
1720
1720
|
// ── 💬 聊天视图样式(dock 追加段, 2026-09-07): QQ 风格气泡, 群友左(bot)右 ──
|
|
1721
|
-
var DOCK_CSS2 = "#qqs-dock-panel .dk-chat-head{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 6px}#qqs-dock-panel .dk-chat-box{overflow-y:auto;overscroll-behavior:contain;background:#f5f6f8;border:1px solid #e6e8ec;border-radius:10px;padding:10px 12px;box-sizing:border-box;height:min(36vh,300px);min-height:140px;scroll-behavior:auto}#qqs-dock-panel .dk-chat-box::-webkit-scrollbar{width:6px}#qqs-dock-panel .dk-chat-box::-webkit-scrollbar-thumb{background:#d3d7dd;border-radius:3px}#qqs-dock-panel .dk-crow{display:flex;gap:8px;align-items:flex-start;margin:0 0 12px}#qqs-dock-panel .dk-crow.out{flex-direction:row-reverse}#qqs-dock-panel .dk-ava{width:32px;height:32px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:#fff;overflow:hidden;user-select:none;background:linear-gradient(150deg,#8fb3e8,#5f8fd9)}#qqs-dock-panel .dk-crow.out .dk-ava{background:linear-gradient(150deg,#5ec7f2,#3b8fe0)}#qqs-dock-panel .dk-cmain{display:flex;flex-direction:column;max-width:calc(100% - 40px);min-width:0}#qqs-dock-panel .dk-crow.in .dk-cmain{align-items:flex-start}#qqs-dock-panel .dk-crow.out .dk-cmain{align-items:flex-end}#qqs-dock-panel .dk-cmeta{font-size:11px;color:#9aa0a8;margin:0 6px 2px;max-width:100%;display:flex;align-items:center;gap:5px;flex-wrap:wrap}#qqs-dock-panel .dk-crow.out .dk-cmeta{flex-direction:row-reverse}#qqs-dock-panel .dk-cbubble{padding:7px 11px;font-size:13px;line-height:1.55;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;box-shadow:0 1px 2px rgba(20,30,60,.06);max-width:100%}#qqs-dock-panel .dk-cbubble a.dk-link{color:#1a66d6;text-decoration:underline;word-break:break-all;cursor:pointer}#qqs-dock-panel .dk-crow.out .dk-cbubble a.dk-link{color:#eaf3ff}#qqs-dock-panel .dk-crow.in .dk-cbubble{background:#fff;border:1px solid #e3e6ea;color:#1f2329;border-radius:3px 10px 10px 10px}#qqs-dock-panel .dk-crow.out .dk-cbubble{background:linear-gradient(180deg,#69a6ff,#3d7df5);color:#fff;border-radius:10px 3px 10px 10px}#qqs-dock-panel .dk-img{display:block;max-width:min(230px,52vw);max-height:200px;border-radius:6px;margin:0 0 3px;object-fit:cover;cursor:zoom-in}#qqs-dock-panel .dk-audio{display:block;max-width:min(260px,60vw);width:100%;height:34px;margin:0 0 2px}#qqs-dock-panel .dk-file{display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:6px 12px;border-radius:8px;background:#f0f6ff;border:1px solid #cfe0fa;color:#2b6bd8;font-size:13px;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#qqs-dock-panel .dk-file:hover{background:#e2edff}#qqs-dock-panel .dk-video{display:block;max-width:min(260px,60vw);max-height:180px;border-radius:6px;margin:0 0 2px}#qqs-dock-panel .dk-ctag{display:inline-block;font-size:10px;color:#5b8ff0;background:#eaf2ff;border:1px solid #d4e3fd;border-radius:8px;padding:0 6px}#qqs-dock-panel .dk-chat-top{text-align:center;color:#b0b4bb;font-size:11px;padding:2px 0 6px;user-select:none}#qqs-dock-panel .dk-chat-bottom{text-align:center;color:#c3c7cd;font-size:11px;padding:6px 0 0}#qqs-dock-panel .dk-composer{margin-top:8px;border:1px solid #e3e6ea;border-radius:10px;background:#fff;overflow:visible}#qqs-dock-panel .dk-composer textarea{width:100%;box-sizing:border-box;border:none;outline:none;resize:none;font:inherit;font-size:13px;color:#1f2329;background:transparent;padding:8px 10px 4px;line-height:1.5;max-height:120px}#qqs-dock-panel .dk-cbar{display:flex;align-items:center;gap:4px;padding:4px 8px 6px;flex-wrap:wrap}#qqs-dock-panel .dk-cbar .dk-btn{padding:3px 10px;font-size:12px;border-radius:7px}#qqs-dock-panel .dk-cbar .dk-send{background:linear-gradient(180deg,#69a6ff,#3d7df5);color:#fff;border:none;border-radius:8px;padding:5px 18px;font-size:13px;font-weight:600;cursor:pointer}#qqs-dock-panel .dk-cbar .dk-send:disabled{opacity:.5;cursor:default}#qqs-lightbox{position:fixed;inset:0;z-index:2147483000;background:rgba(8,10,18,.82);display:flex;align-items:center;justify-content:center;cursor:zoom-out}#qqs-lightbox img{max-width:92vw;max-height:92vh;border-radius:8px;box-shadow:0 10px 60px rgba(0,0,0,.6)}#qqs-dock-panel .dk-composer{position:relative}#qqs-dock-panel .dk-at-pop{position:absolute;left:6px;bottom:calc(100% - 4px);z-index:30;min-width:200px;max-width:90%;max-height:190px;overflow-y:auto;background:#fff;border:1px solid #e0e4ea;border-radius:10px;box-shadow:0 8px 24px rgba(30,40,80,.16);padding:4px;display:none}#qqs-dock-panel .dk-at-item{display:flex;align-items:center;gap:6px;padding:5px 9px;border-radius:7px;cursor:pointer;font-size:12px;color:#1f2329;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#qqs-dock-panel .dk-at-item.on,#qqs-dock-panel .dk-at-item:hover{background:#eef3ff;color:#2b5fd0}#qqs-dock-panel .dk-at-empty{color:#aaa;font-size:12px;padding:6px 9px}#qqs-lightbox .lb-x{position:fixed;right:16px;top:10px;color:#fff;font-size:30px;cursor:pointer;line-height:1;padding:6px}#qqs-dock-panel.dk-full{left:0!important;top:0!important;right:0!important;bottom:0!important;width:100vw!important;max-width:100vw!important;height:100vh!important;max-height:100vh!important;border-radius:0;z-index:2147482000;display:flex;flex-direction:column}#qqs-dock-panel.dk-full .dk-h,#qqs-dock-panel.dk-full .dk-detect{flex:none}#qqs-dock-panel.dk-full .dk-b{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column;padding:8px 14px 6px}#qqs-dock-panel.dk-full .dk-chat-wrap{display:flex;flex-direction:column;flex:1;min-height:0}#qqs-dock-panel.dk-full .dk-chat-head{flex:none}#qqs-dock-panel.dk-full .dk-chat-box{flex:1;height:auto!important;min-height:0!important;max-height:none!important;overflow-y:auto;overscroll-behavior:contain}#qqs-dock-panel.dk-full .dk-composer{flex:none;margin-top:6px}"
|
|
1721
|
+
var DOCK_CSS2 = "#qqs-dock-panel .dk-chat-head{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 6px}#qqs-dock-panel .dk-chat-box{overflow-y:auto;overscroll-behavior:contain;background:#f5f6f8;border:1px solid #e6e8ec;border-radius:10px;padding:10px 12px;box-sizing:border-box;height:min(36vh,300px);min-height:140px;scroll-behavior:auto}#qqs-dock-panel .dk-chat-box::-webkit-scrollbar{width:6px}#qqs-dock-panel .dk-chat-box::-webkit-scrollbar-thumb{background:#d3d7dd;border-radius:3px}#qqs-dock-panel .dk-crow{display:flex;gap:8px;align-items:flex-start;margin:0 0 12px}#qqs-dock-panel .dk-crow.out{flex-direction:row-reverse}#qqs-dock-panel .dk-ava{width:32px;height:32px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:#fff;overflow:hidden;user-select:none;background:linear-gradient(150deg,#8fb3e8,#5f8fd9)}#qqs-dock-panel .dk-crow.out .dk-ava{background:linear-gradient(150deg,#5ec7f2,#3b8fe0)}#qqs-dock-panel .dk-cmain{display:flex;flex-direction:column;max-width:calc(100% - 40px);min-width:0}#qqs-dock-panel .dk-crow.in .dk-cmain{align-items:flex-start}#qqs-dock-panel .dk-crow.out .dk-cmain{align-items:flex-end}#qqs-dock-panel .dk-cmeta{font-size:11px;color:#9aa0a8;margin:0 6px 2px;max-width:100%;display:flex;align-items:center;gap:5px;flex-wrap:wrap}#qqs-dock-panel .dk-crow.out .dk-cmeta{flex-direction:row-reverse}#qqs-dock-panel .dk-cbubble{padding:7px 11px;font-size:13px;line-height:1.55;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;box-shadow:0 1px 2px rgba(20,30,60,.06);max-width:100%}#qqs-dock-panel .dk-cbubble a.dk-link{color:#1a66d6;text-decoration:underline;word-break:break-all;cursor:pointer}#qqs-dock-panel .dk-crow.out .dk-cbubble a.dk-link{color:#eaf3ff}#qqs-dock-panel .dk-crow.in .dk-cbubble{background:#fff;border:1px solid #e3e6ea;color:#1f2329;border-radius:3px 10px 10px 10px}#qqs-dock-panel .dk-crow.out .dk-cbubble{background:linear-gradient(180deg,#69a6ff,#3d7df5);color:#fff;border-radius:10px 3px 10px 10px}#qqs-dock-panel .dk-img{display:block;max-width:min(230px,52vw);max-height:200px;border-radius:6px;margin:0 0 3px;object-fit:cover;cursor:zoom-in}#qqs-dock-panel .dk-audio{display:block;max-width:min(260px,60vw);width:100%;height:34px;margin:0 0 2px}#qqs-dock-panel .dk-file{display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:6px 12px;border-radius:8px;background:#f0f6ff;border:1px solid #cfe0fa;color:#2b6bd8;font-size:13px;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#qqs-dock-panel .dk-file:hover{background:#e2edff}#qqs-dock-panel .dk-video{display:block;max-width:min(260px,60vw);max-height:180px;border-radius:6px;margin:0 0 2px}#qqs-dock-panel .dk-ctag{display:inline-block;font-size:10px;color:#5b8ff0;background:#eaf2ff;border:1px solid #d4e3fd;border-radius:8px;padding:0 6px}#qqs-dock-panel .dk-chat-top{text-align:center;color:#b0b4bb;font-size:11px;padding:2px 0 6px;user-select:none}#qqs-dock-panel .dk-chat-bottom{text-align:center;color:#c3c7cd;font-size:11px;padding:6px 0 0}#qqs-dock-panel .dk-composer{margin-top:8px;border:1px solid #e3e6ea;border-radius:10px;background:#fff;overflow:visible}#qqs-dock-panel .dk-composer textarea{width:100%;box-sizing:border-box;border:none;outline:none;resize:none;font:inherit;font-size:13px;color:#1f2329;background:transparent;padding:8px 10px 4px;line-height:1.5;max-height:120px}#qqs-dock-panel .dk-cbar{display:flex;align-items:center;gap:4px;padding:4px 8px 6px;flex-wrap:wrap}#qqs-dock-panel .dk-cbar .dk-btn{padding:3px 10px;font-size:12px;border-radius:7px}#qqs-dock-panel .dk-cbar .dk-send{background:linear-gradient(180deg,#69a6ff,#3d7df5);color:#fff;border:none;border-radius:8px;padding:5px 18px;font-size:13px;font-weight:600;cursor:pointer}#qqs-dock-panel .dk-cbar .dk-send:disabled{opacity:.5;cursor:default}#qqs-lightbox{position:fixed;inset:0;z-index:2147483000;background:rgba(8,10,18,.82);display:flex;align-items:center;justify-content:center;cursor:zoom-out}#qqs-lightbox img{max-width:92vw;max-height:92vh;border-radius:8px;box-shadow:0 10px 60px rgba(0,0,0,.6)}#qqs-dock-panel .dk-composer{position:relative}#qqs-dock-panel .dk-at-pop{position:absolute;left:6px;bottom:calc(100% - 4px);z-index:30;min-width:200px;max-width:90%;max-height:190px;overflow-y:auto;background:#fff;border:1px solid #e0e4ea;border-radius:10px;box-shadow:0 8px 24px rgba(30,40,80,.16);padding:4px;display:none}#qqs-dock-panel .dk-at-item{display:flex;align-items:center;gap:6px;padding:5px 9px;border-radius:7px;cursor:pointer;font-size:12px;color:#1f2329;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#qqs-dock-panel .dk-at-item.on,#qqs-dock-panel .dk-at-item:hover{background:#eef3ff;color:#2b5fd0}#qqs-dock-panel .dk-at-empty{color:#aaa;font-size:12px;padding:6px 9px}#qqs-lightbox .lb-x{position:fixed;right:16px;top:10px;color:#fff;font-size:30px;cursor:pointer;line-height:1;padding:6px}#qqs-dock-panel.dk-full{left:0!important;top:0!important;right:0!important;bottom:0!important;width:100vw!important;max-width:100vw!important;height:100vh!important;max-height:100vh!important;border-radius:0;z-index:2147482000;display:flex;flex-direction:column}#qqs-dock-panel.dk-full .dk-h,#qqs-dock-panel.dk-full .dk-detect{flex:none}#qqs-dock-panel.dk-full .dk-b{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column;padding:8px 14px 6px}#qqs-dock-panel.dk-full .dk-chat-wrap{display:flex;flex-direction:column;flex:1;min-height:0}#qqs-dock-panel.dk-full .dk-chat-head{flex:none}#qqs-dock-panel.dk-full .dk-chat-box{flex:1;height:auto!important;min-height:0!important;max-height:none!important;overflow-y:auto;overscroll-behavior:contain}#qqs-dock-panel.dk-full .dk-composer{flex:none;margin-top:6px}#qqs-dock-panel .dk-tab{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none}#qqs-dock-panel .dk-tab::-webkit-scrollbar{display:none}#qqs-dock-panel .dk-tab button{white-space:nowrap;flex:0 0 auto}@media (max-width:560px){#qqs-dock-panel:not(.dk-full){left:8px!important;right:8px!important;width:auto!important;max-width:calc(100vw - 16px)!important;bottom:96px!important}#qqs-dock-panel .dk-h{font-size:13px;padding:8px 10px;gap:6px}#qqs-dock-panel .dk-b{padding:8px 10px;font-size:13px}#qqs-dock-panel .dk-tab button{font-size:12px;padding:7px 10px}#qqs-dock-panel .dk-btn{font-size:12px;padding:6px 10px}#qqs-dock-panel .dk-row{gap:6px;margin:5px 0}#qqs-dock-panel select.qqs-sel,#qqs-dock-panel input.qqs-txt,#qqs-dock-panel textarea.qqs-txt{padding:7px 9px;font-size:16px}#qqs-dock-panel .dk-list{max-height:46vh}#qqs-dock-panel .dk-item{padding:8px 6px;gap:6px}}@media (max-width:560px){#qqs-dock-panel.dk-full{border-radius:0}#qqs-dock-panel.dk-full .dk-b{padding-bottom:calc(8px + env(safe-area-inset-bottom,0px))}#qqs-dock-panel.dk-full .dk-tab{position:sticky;top:0;background:#fff;z-index:2}}@media (max-width:560px){#qqs-dock-ball{width:46px;height:46px;line-height:46px;font-size:21px}}"
|
|
1722
1722
|
function ensureDockCss2() { try { if (!document.getElementById('qqs-dock-css2')) { var st = document.createElement('style'); st.id = 'qqs-dock-css2'; st.textContent = DOCK_CSS2; document.head.appendChild(st) } } catch (e) {} };
|
|
1723
1723
|
function ensureDockCss() { try { if (!document.getElementById('qqs-dock-css')) { var st = document.createElement('style'); st.id = 'qqs-dock-css'; st.textContent = DOCK_CSS; document.head.appendChild(st) } } catch (e) {} }
|
|
1724
1724
|
function startQqDock(sessionsSvc) {
|
|
@@ -1751,11 +1751,18 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
1751
1751
|
var _y = Math.max(0, Math.min(Math.max(0, window.innerHeight - _bh), Number(pos.y) || 0))
|
|
1752
1752
|
wrap.style.left = _x + 'px'; wrap.style.top = _y + 'px'; wrap.style.right = 'auto'; wrap.style.bottom = 'auto'
|
|
1753
1753
|
var dragState = null
|
|
1754
|
-
|
|
1755
|
-
|
|
1754
|
+
// ⚠️ 2026-10-01 修(安卓上拖不动悬浮球):原用 mousedown/mousemove/mouseup ——
|
|
1755
|
+
// 那是【鼠标事件】,触摸屏按住不会触发 move(浏览器只在 tap 时合成 click),
|
|
1756
|
+
// 所以在 dsh-mobile-apk 这类安卓 WebView 里球完全拖不动。
|
|
1757
|
+
// 改用 Pointer Events:鼠标 / 触摸 / 触控笔一套通吃。
|
|
1758
|
+
var lastDragEndedAt = 0 // 拖完的短暂屏蔽窗,避免"松手顺手把面板也打开"
|
|
1759
|
+
try { ball.style.touchAction = 'none' } catch (eT) {} // 别让浏览器把触摸当滚动手势
|
|
1760
|
+
ball.addEventListener('pointerdown', function (e) {
|
|
1761
|
+
dragState = { sx: e.clientX, sy: e.clientY, ox: wrap.offsetLeft, oy: wrap.offsetTop, moved: false, pid: e.pointerId }
|
|
1762
|
+
try { ball.setPointerCapture(e.pointerId) } catch (eC) {} // 手指移出球体也不丢事件
|
|
1756
1763
|
e.preventDefault()
|
|
1757
1764
|
})
|
|
1758
|
-
document.addEventListener('
|
|
1765
|
+
document.addEventListener('pointermove', function (e) {
|
|
1759
1766
|
if (!dragState) return
|
|
1760
1767
|
var dx = e.clientX - dragState.sx, dy = e.clientY - dragState.sy
|
|
1761
1768
|
if (Math.abs(dx) + Math.abs(dy) > 3) dragState.moved = true
|
|
@@ -1764,16 +1771,21 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
1764
1771
|
wrap.style.top = Math.max(0, Math.min(window.innerHeight - 60, dragState.oy + dy)) + 'px'
|
|
1765
1772
|
wrap.style.right = 'auto'; wrap.style.bottom = 'auto'
|
|
1766
1773
|
syncPanelPos()
|
|
1774
|
+
e.preventDefault()
|
|
1767
1775
|
})
|
|
1768
|
-
|
|
1776
|
+
function endBallDrag() {
|
|
1769
1777
|
if (!dragState) return
|
|
1770
1778
|
var moved = dragState.moved
|
|
1779
|
+
var pid = dragState.pid
|
|
1771
1780
|
dragState = null
|
|
1781
|
+
try { if (pid != null) ball.releasePointerCapture(pid) } catch (eR) {}
|
|
1772
1782
|
if (moved) {
|
|
1773
|
-
|
|
1783
|
+
lastDragEndedAt = Date.now()
|
|
1784
|
+
try { localStorage.setItem('qqs-dock-pos', JSON.stringify({ x: wrap.offsetLeft, y: wrap.offsetTop })) } catch (eS) {}
|
|
1774
1785
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1786
|
+
}
|
|
1787
|
+
document.addEventListener('pointerup', endBallDrag)
|
|
1788
|
+
document.addEventListener('pointercancel', endBallDrag) // 触摸被系统打断也要收尾
|
|
1777
1789
|
function syncPanelPos() {
|
|
1778
1790
|
if (!panel || !open) return
|
|
1779
1791
|
layoutPanel()
|
|
@@ -1823,8 +1835,10 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
1823
1835
|
if (!panel || !open) return
|
|
1824
1836
|
var h = panel.querySelector('.dk-h')
|
|
1825
1837
|
if (!h) return
|
|
1826
|
-
|
|
1827
|
-
|
|
1838
|
+
// ⚠️ 2026-10-01 修:改 Pointer Events —— 触摸屏(安卓 WebView)也能拖动面板
|
|
1839
|
+
try { h.style.touchAction = 'none' } catch (eT) {}
|
|
1840
|
+
h.onpointerdown = function (e) {
|
|
1841
|
+
if (e.pointerType === 'mouse' && e.button !== 0) return
|
|
1828
1842
|
var t = e.target
|
|
1829
1843
|
if (t && t.closest && t.closest('button,select,input,textarea,a,.qqs-sel,.qqs-txt')) return
|
|
1830
1844
|
e.preventDefault()
|
|
@@ -1840,12 +1854,14 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
1840
1854
|
panel.style.bottom = 'auto'
|
|
1841
1855
|
}
|
|
1842
1856
|
var mu = function () {
|
|
1843
|
-
document.removeEventListener('
|
|
1844
|
-
document.removeEventListener('
|
|
1857
|
+
document.removeEventListener('pointermove', mm)
|
|
1858
|
+
document.removeEventListener('pointerup', mu)
|
|
1859
|
+
document.removeEventListener('pointercancel', mu)
|
|
1845
1860
|
if (moved) manualPanelPos = { x: panel.offsetLeft, y: panel.offsetTop }
|
|
1846
1861
|
}
|
|
1847
|
-
document.addEventListener('
|
|
1848
|
-
document.addEventListener('
|
|
1862
|
+
document.addEventListener('pointermove', mm)
|
|
1863
|
+
document.addEventListener('pointerup', mu)
|
|
1864
|
+
document.addEventListener('pointercancel', mu)
|
|
1849
1865
|
}
|
|
1850
1866
|
// 双击头部 → 取消手动固定, 恢复自动贴球定位
|
|
1851
1867
|
h.ondblclick = function (e) {
|
|
@@ -1861,6 +1877,8 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
1861
1877
|
ball.style.display = 'block'
|
|
1862
1878
|
}
|
|
1863
1879
|
function togglePanel() {
|
|
1880
|
+
// 刚拖拽过 → 这次 click 只是"松手",不要顺手把面板打开
|
|
1881
|
+
if (Date.now() - (typeof lastDragEndedAt === 'number' ? lastDragEndedAt : 0) < 350) return
|
|
1864
1882
|
if (dragState && dragState.moved) return
|
|
1865
1883
|
if (open) { closePanel(); return }
|
|
1866
1884
|
if (!panel) {
|