oh-my-dws 0.0.1 → 0.0.3
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 +40 -8
- package/bin/oh-my-dws.js +2 -2
- package/docs//344/275/277/347/224/250/350/257/264/346/230/216.md +170 -11
- package/package.json +6 -2
- package/src/actions.js +590 -63
- package/src/ai.js +300 -0
- package/src/archive.js +17 -8
- package/src/auth.js +33 -1
- package/src/avatars.js +143 -0
- package/src/contacts.js +563 -0
- package/src/layout.js +63 -0
- package/src/listen.js +289 -0
- package/src/media.js +12 -1
- package/src/rev.js +23 -0
- package/src/server.js +385 -7
- package/src/settings.js +51 -0
- package/src/sync.js +213 -0
- package/web/index.html +2136 -74
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npx oh-my-dws start
|
|
|
15
15
|
|
|
16
16
|
# 或全局安装
|
|
17
17
|
npm i -g oh-my-dws
|
|
18
|
-
oh-my-dws start # 后台起服务并打开浏览器(默认
|
|
18
|
+
oh-my-dws start # 后台起服务并打开浏览器(默认 25259)
|
|
19
19
|
|
|
20
20
|
# 或从源码跑
|
|
21
21
|
git clone https://github.com/duzhenxun/oh-my-dws.git
|
|
@@ -27,7 +27,7 @@ macOS 也可以双击仓库里的 `启动.command`。
|
|
|
27
27
|
## 用法
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
oh-my-dws start # 后台启动(默认 http://127.0.0.1:
|
|
30
|
+
oh-my-dws start # 后台启动(默认 http://127.0.0.1:25259)
|
|
31
31
|
oh-my-dws status # 看 PID / 端口 / URL / 版本
|
|
32
32
|
oh-my-dws restart # 重启
|
|
33
33
|
oh-my-dws stop # 停止
|
|
@@ -46,7 +46,7 @@ oh-my-dws --help
|
|
|
46
46
|
| `stop` / `restart` | 停止 / 重启后台服务(先 SIGTERM,超时再 SIGKILL) |
|
|
47
47
|
| `status` | 运行状态(同时探 `/api/health`);退出码 `0` 在跑 / `3` 未跑 / `4` 进程在但无响应 |
|
|
48
48
|
| `update` | `npm i -g oh-my-dws@latest`,正在运行则自动重启 |
|
|
49
|
-
| `-p, --port <端口>` | 监听端口,默认 `
|
|
49
|
+
| `-p, --port <端口>` | 监听端口,默认 `25259` |
|
|
50
50
|
| `--data <目录>` | 聊天数据目录,默认 `~/.oh-my-dws` |
|
|
51
51
|
| `--no-open` | 不自动打开浏览器 |
|
|
52
52
|
| `-v, --version` / `-h, --help` | 版本 / 帮助 |
|
|
@@ -76,6 +76,9 @@ oh-my-dws/ # 代码(npm 包)
|
|
|
76
76
|
~/.oh-my-dws/ # 数据(聊天相关文件全在这里,可整体备份/迁移)
|
|
77
77
|
├── 群索引.md # 群名 → 群 ID / 成员数 / 文件位置(每次拉取后自动重建)
|
|
78
78
|
├── server.json # 后台服务的 PID/端口(`start` 写、`stop` 删)
|
|
79
|
+
├── 常用联系人.json # 常用联系人名单(可手工编辑/迁移)
|
|
80
|
+
├── 布局.json # 会话列表拖动顺序 / 置顶
|
|
81
|
+
├── ai.json # AI 接口配置(选填)
|
|
79
82
|
├── _运行/server.log # 后台服务日志
|
|
80
83
|
├── _原始数据/ # dws 原始 JSON(便于二次加工/复核)
|
|
81
84
|
│ ├── <群简称>_消息.json
|
|
@@ -92,10 +95,11 @@ oh-my-dws/ # 代码(npm 包)
|
|
|
92
95
|
|
|
93
96
|
## 功能
|
|
94
97
|
|
|
95
|
-
-
|
|
98
|
+
- **常用联系人(可收起,只放真实单聊)**:侧栏一栏列出你钉钉里的**单聊对象**(头像 + 姓名 + 最近时间 + **最后一条消息预览**,跟钉钉单聊列表一样);+ 从 `dws chat +recent-conversations` 的真实单聊里挑(后台抓取 + 缓存 30 分钟,本地已归档的单聊优先),也可手动填 `openConversationId`;**拖动可调整顺序**、悬停 ⇧ 一键置顶、🖼 可上传本地头像(钉钉接口不返回头像 URL)、× 移出;**点开即进入该单聊会话**(没归档会自动先归档),历史和发消息都在里面;只有 openDingTalkId 的手动联系人退化成跨群时间线。数据存 `~/.oh-my-dws/常用联系人.json`
|
|
99
|
+
- 左侧会话列表(可搜索),分 **@我 聚合 / 群聊** 两组(**单聊不单独列,统一在常用联系人里**),**每组标题可点击收起/展开**;支持**拖动排序**、**⇧ 置顶**、**☑ 多选批量删除**(顺序与置顶存 `布局.json`);右侧看某个会话的**全部消息**
|
|
96
100
|
- **侧栏默认收起**:收起后是 32px 窄条(`»` 展开、显示当前会话首字头像);展开后悬停侧栏会在右边缘出现 `«` 把手一点就收;**⌘/Ctrl + B** 随时切;**拖右边缘可调宽**(200–560px,双击回默认 310px),宽度和收起状态都记在 localStorage
|
|
97
101
|
- 侧栏圆点:只有**真有新消息**时才亮绿点并显示 `+N 新`;无新消息是空心灰圈(`complete=false` 才变红);打开该会话即自动清零已阅
|
|
98
|
-
-
|
|
102
|
+
- **默认倒序 + 只看今天 + 分页渲染**:打开默认「今天」、只渲染最近 10 条(范围内没消息会自动放开到全部时间)(`10+82 / 1392 条`),底部「加载更早的 20 条」按需追加;1300 条历史的单聊打开也就几十毫秒(工具栏可切「最早在前」/「全部时间」)
|
|
99
103
|
- 消息区渲染钉钉正文的 `**加粗**`、`` `代码` ``、链接、@人,按日期分段
|
|
100
104
|
- 每条消息带**头像**(按人员 ID 稳定配色 + 姓名首字;钉钉接口不返回真实头像 URL)
|
|
101
105
|
- **引用/回复消息显示成引用块**(灰底竖线),点击引用块直接跳到被引用的那条消息
|
|
@@ -106,6 +110,14 @@ oh-my-dws/ # 代码(npm 包)
|
|
|
106
110
|
- **拉取全部最新**(右键菜单「⬇️ 拉取全部最新」):所有会话依次增量刷新
|
|
107
111
|
- **右键会话**:成员 / 人员 ID、拉取最新消息、复制会话 ID、删除归档(移到 `_回收站/`,可恢复);下面隔开的全局项:搜索群、拉取全部最新
|
|
108
112
|
- **工具栏一行到底**:搜索框 + 发送者 / 来源群 / 时间范围(全部·今天·近 4 档)/ 排序(最新在前·最早在前)+ 条数
|
|
113
|
+
- **设置中心**:消息区右上角「⚙ 设置」弹窗,四个页签:AI 模型 / 监控与实时日志(含重启、兜底同步间隔)/ 进程与服务日志 / 通用偏好
|
|
114
|
+
- **真实头像**:钉钉接口不返回头像,所以做成本地头像库(按 `openDingTalkId`):**点任意头像**(消息里的或联系人里的)上传图片/贴图片链接,这个人在所有群、所有消息、联系人栏的头像一起变;存 `_媒体/头像/`
|
|
115
|
+
- **未读角标自动出现**:页面闲置时每 5 秒比一次数据版本号(`/api/ping`),有变化就刷新会话/联系人列表并点亮未读(私聊在联系人行、联系人栏、窄条都有角标);**自己发的消息不计未读**
|
|
116
|
+
- **实时监听(自动收新消息,零接口额度)**:服务拉起两个 `dws event +listen-im` 长连接进程(全部群消息 / 全部单聊),事件里的字段直接写入归档;账号面板可看连接状态、pid、事件数、最后事件时间,并能一键**重启/停止**;崩溃自动退避重连
|
|
117
|
+
- **AI 两个能力**(任意 OpenAI 兼容接口,界面里可配;没配则自动复用环境变量或 pi 的 provider):**✨ AI 帮我写**(发消息浮层里,按最近 24 条上下文起草回复,可直接改完再发)、**🧠 AI 总结**(消息区右上角,按当前筛选范围输出【结论】【待办】【风险/分歧】)
|
|
118
|
+
- **撤回标记**:消息被撤回后显示「已撤回(谁、几点)」,正文清空;别人的撤回走 `recall` 事件实时打标
|
|
119
|
+
- **撤回自己的消息**:消息上右键 →「↩️ 撤回」(只对自己发的消息出现,走 `dws chat message recall`,撤回后自动回读)
|
|
120
|
+
- **图文混排**:`⌘V` / 拖拽的图片先挂在输入框上方缩略条里(可多张、可删),写文字后一起发 → 合成**一条** markdown 消息(文字 + 内联图片);只发图也行。原理是先传图床取公网 URL 再发 markdown,钉钉里是真图片而非附件;原图留 `_媒体/已发/`
|
|
109
121
|
- **发消息默认收起**:右下角「✏️ 发消息」悬浮按钮,点开才展开浮层(`⌘/Ctrl + Enter` 发送,× 或 Esc 收起);有草稿自动展开;点发送即直接投递(不弹确认框),发送后自动回读归档确认落库;草稿按会话保存;可勾选是否带「AI 角标」(默认**不带**)
|
|
110
122
|
- **@我 聚合**:把「**已归档群聊**」里的 `@我` 消息汇总成一条时间线。侧栏在「@我的消息」下标出**来源群子级 + 条数**;每条消息带 📍 群名标签、左侧竖条按群配色;「来源群」页看群排行。**只扫已归档的群**,新增/删除群后点「重新汇总」修正
|
|
111
123
|
- 每条 @我 消息底部三个按钮:
|
|
@@ -157,11 +169,31 @@ dws chat +chat-messages --group "<cid>" --page-all --download-resources --output
|
|
|
157
169
|
## 开发
|
|
158
170
|
|
|
159
171
|
```bash
|
|
160
|
-
node bin/oh-my-dws.js --no-open --port
|
|
161
|
-
npm
|
|
162
|
-
npm
|
|
172
|
+
node bin/oh-my-dws.js --no-open --port 26000 # 本地调试(前台)
|
|
173
|
+
npm run check # 语法 + 网页 id 引用自检
|
|
174
|
+
npm pack --dry-run # 检查发布内容
|
|
163
175
|
```
|
|
164
176
|
|
|
177
|
+
## 发版(打 tag 自动发 npm)
|
|
178
|
+
|
|
179
|
+
仓库里已有 `.github/workflows/release.yml`:**推 `v*` tag 就自动 `npm publish`**。
|
|
180
|
+
|
|
181
|
+
1. 一次性(二选一):
|
|
182
|
+
- **可信发布(推荐)**:npm 包页面 `/access` → Trusted Publishers → Add publisher → GitHub Actions,
|
|
183
|
+
填 `duzhenxun` / `oh-my-dws` / `release.yml`,Environment 留空;不用任何 token
|
|
184
|
+
- **或用 token**:npm 建 **Automation** token,存进 GitHub Secrets,名字 `NPM_TOKEN`
|
|
185
|
+
2. 以后一条命令发版:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npm run release # 0.0.1 → 0.0.2(改版本 + commit + tag + push)
|
|
189
|
+
npm run release:minor # → 0.1.0
|
|
190
|
+
npm run release:major # → 1.0.0
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
CI 会校验 **tag 与 `package.json` 版本一致**、跑 `npm run check`、`npm pack --dry-run`、
|
|
194
|
+
已发布过则跳过,最后 `npm publish --access public` 并建 GitHub Release。
|
|
195
|
+
细节与报错处理见 [docs/使用说明.md](docs/使用说明.md)。
|
|
196
|
+
|
|
165
197
|
## 注意
|
|
166
198
|
|
|
167
199
|
- 图片消息正文只返回 `mediaId`,需要实际图片时由网页按需下载。
|
package/bin/oh-my-dws.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* oh-my-dws 命令行入口。
|
|
6
6
|
*
|
|
7
|
-
* 前台:oh-my-dws [--port
|
|
7
|
+
* 前台:oh-my-dws [--port 25259] [--data 目录] [--no-open]
|
|
8
8
|
* 后台:oh-my-dws start|stop|restart|status|update
|
|
9
9
|
*
|
|
10
10
|
* start 会以 detached 子进程跑 `__serve`,PID/端口写进 <数据目录>/server.json,
|
|
@@ -17,7 +17,7 @@ const { spawn, spawnSync } = require('node:child_process');
|
|
|
17
17
|
|
|
18
18
|
const pkg = require('../package.json');
|
|
19
19
|
|
|
20
|
-
const DEFAULT_PORT =
|
|
20
|
+
const DEFAULT_PORT = 25259;
|
|
21
21
|
const COMMANDS = ['start', 'stop', 'restart', 'status', 'update', 'serve', '__serve'];
|
|
22
22
|
|
|
23
23
|
const HELP = `oh-my-dws ${pkg.version} — 钉钉群消息归档(本地网页控制台)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- 版本:`0.0.1`
|
|
7
7
|
- 运行时:**Node.js ≥ 18**(零第三方依赖)
|
|
8
8
|
- 数据源:本机 `dws`(钉钉 CLI,需已登录 `dws auth login`)
|
|
9
|
-
- 默认地址:**http://127.0.0.1:
|
|
9
|
+
- 默认地址:**http://127.0.0.1:25259**
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -42,7 +42,7 @@ oh-my-dws start
|
|
|
42
42
|
|
|
43
43
|
常用选项:
|
|
44
44
|
|
|
45
|
-
- `-p, --port <端口>`:监听端口,默认 **
|
|
45
|
+
- `-p, --port <端口>`:监听端口,默认 **25259**
|
|
46
46
|
- `--data <目录>`:聊天数据目录,默认 `~/.oh-my-dws`(等价环境变量 `OH_MY_DWS_HOME`)
|
|
47
47
|
- `--no-open`:不自动打开浏览器(等价 `NO_OPEN=1`)
|
|
48
48
|
|
|
@@ -56,6 +56,90 @@ oh-my-dws start
|
|
|
56
56
|
- **收起**:展开状态下鼠标移到侧栏上,右边缘中部会出现 `«` 把手,点一下收起;或者按 **⌘/Ctrl + B**(随时可切,状态会记住)。
|
|
57
57
|
- **调宽**:拖侧栏右边缘(200–560px),双击边缘恢复默认 310px;宽度也记住。
|
|
58
58
|
- 搜索框不再常驻:**右键 →「🔍 搜索群…」** 才出现(按地名/会话 ID 过滤,Esc 或清空后收起)。
|
|
59
|
+
- **会话分组**:只有「@我 聚合 / 群聊」两组(**单聊统一走上面的常用联系人**),点标题就收/展,右侧是该组总条数,状态记在本机;点某个会话时若所在分组收起会自动展开。
|
|
60
|
+
- **拖动排序 / 置顶 / 多选删除**(会话列表):
|
|
61
|
+
- **拖动**条目即可换位置(只在同一分组内生效),顺序存 `布局.json`,刷新/重启保持
|
|
62
|
+
- 悬停 **⇧** 一键置顶到最前(置顶后名字旁有 📌,再点取消)
|
|
63
|
+
- 悬停 **☑** 进入多选:连续点选多个会话,侧栏底部出现「已选 N 个会话 · 删除 · 取消」,删除是一次确认后批量移到 `_回收站`
|
|
64
|
+
|
|
65
|
+
### 常用联系人(单聊)
|
|
66
|
+
|
|
67
|
+
侧栏账号胶囊下面那一栏是 **常用联系人**,里面放的是**你钉钉里真实的单聊对象**(不是群成员):
|
|
68
|
+
|
|
69
|
+
- **点标题 = 私聊聚合页**:点「常用联系人」这几个字,右侧打开一个**所有人的私聊时间线**(别人新发给你的消息带**红点 + 「未读」**,右上角「全部标为已读(N)」一次清掉;「全部来源人」下拉里也会显示每个人 `· N 未读`)(标题「私聊消息」,显示「N 个人私聊 / X 条 别人发我的」);每条**不重复显示人名**(发送人就是对方),下面直接给「🗄 到单聊定位 / 💬 打开钉钉 / 📋 复制定位信息」;点发送人名字可跳到与 TA 的单聊;工具栏可「全部来源人」筛选、搜索、调时间范围。左边小箭头才是收起/展开。
|
|
70
|
+
- **列表长这样**:头像 + 姓名 + 最近时间 + **最后一条消息预览**(和钉钉的单聊列表一致)。
|
|
71
|
+
- **收起 / 展开**:点标题栏就收,状态记在本机。
|
|
72
|
+
- **添加**:点右侧 **+**,弹窗里列出**你近一年有消息的钉钉单聊**(`dws chat +recent-conversations`,`type=direct`),带 cid、最近时间、是否已归档:
|
|
73
|
+
- 这个列表第一次拉要 1~2 分钟(钉钉分页慢),所以是**后台拉 + 缓存 30 分钟**:弹窗会显示「正在读取…」并自动刷新;也可以点「刷新列表」强制重读
|
|
74
|
+
- 本地已归档的单聊永远优先显示(带「已归档」标记和条数)
|
|
75
|
+
- 也能手动填:姓名 + 单聊 `openConversationId`;只填 `openDingTalkId` 也能发单聊,但没有会话历史
|
|
76
|
+
- **排序**:**直接拖动条目换位置**(顺序存在 `常用联系人.json` 里,刷新/重开都保持);悬停出现的 **⇧** 是一键置顶到最前(置顶后名字旁有 📌,再点取消标记)。
|
|
77
|
+
- **头像**:钉钉开放接口(成员 / 通讯录 `contact user search`、`get-self` / 好友列表 / 会话信息)**都不返回头像 URL**,所以默认是「姓名首字 + 稳定配色」的圆。想要真头像,三种方式任选:**点头像圈**、悬停点 **🖼**、或**把图片文件直接拖到条目上** —— 支持本地图片(≤3MB,存 `_媒体/头像/`)或粘贴一个 http(s) 图片链接。
|
|
78
|
+
- **移除**:鼠标悬停到某人上,右侧出现 **×**,只删常用名单,不动归档。
|
|
79
|
+
- **点一个人**:直接打开**这个单聊会话**(历史消息 + 发消息都在)。如果他还没归档,会自动先归档(拉一次历史)再打开。
|
|
80
|
+
- 只有 `openDingTalkId`、没有会话的手动联系人,点开是「跨群时间线」兜底视图,仍可发单聊(走 `--open-dingtalk-id`)。
|
|
81
|
+
|
|
82
|
+
名单存 `~/.oh-my-dws/常用联系人.json`;单聊列表和预览缓存分别在 `_运行/dm-cache.json`、`_运行/dm-preview.json`(删了会自动重建)。
|
|
83
|
+
|
|
84
|
+
### AI:帮我写消息 / 总结对话
|
|
85
|
+
|
|
86
|
+
两个功能都用**任意 OpenAI 兼容接口**(`POST /chat/completions`),没有绑定某一家。
|
|
87
|
+
|
|
88
|
+
- **✨ AI 帮我写**(发消息浮层里):点开后在输入框里写一句「想表达什么」(可留空),点「生成」→ AI 按**当前会话最近 24 条上下文**写一条回复,直接填进输入框,还能自己改完再发。
|
|
89
|
+
- **🧠 AI 总结**(消息区右上角):把**当前筛选范围内**的消息(最多最近 200 条)总结成三段:`【结论】【待办】【风险/分歧】`,弹窗里可复制 / 重新总结。
|
|
90
|
+
- **AI 设置**(点左上账号胶囊 → 面板底部「⚙ AI 设置」;发消息浮层的 ⚙ 是同一个):填接口地址、模型、API Key,另可写「额外人设 / 要求」(比如「我是研发,回复要短、给结论、别客套」)。「测试连接」可直接验证。
|
|
91
|
+
|
|
92
|
+
配置优先级:
|
|
93
|
+
|
|
94
|
+
1. `~/.oh-my-dws/ai.json`(界面里保存的,最优先)
|
|
95
|
+
2. 环境变量 `AI_BASE_URL` / `AI_API_KEY` / `AI_MODEL`(也认 `OPENAI_*`)
|
|
96
|
+
3. 本机 **pi 的配置**(`~/.pi/agent/models.json` + `settings.json` 的默认 provider/model)——装了 pi 的机器通常**零配置就能用**
|
|
97
|
+
|
|
98
|
+
### 头像(一次设置,到处生效)
|
|
99
|
+
|
|
100
|
+
钉钉 CLI **不返回人员头像**(通讯录 `contact user search/get-self`、群成员、好友、会话信息都没有 avatar 字段,二进制里也没有这类 API),所以头像是本地挂的:
|
|
101
|
+
|
|
102
|
+
- **点头像就能设**:常用联系人行的头像、以及**消息里发送者的头像**,点一下弹出「设置头像」→ 选本地图片(≤3MB)或粘贴 http(s) 图片链接,也可以清除。
|
|
103
|
+
- 作用域是**人**(按 `openDingTalkId`):给某人在任意一条消息上设一次,他在**所有群、所有消息、常用联系人**里的头像都会变。
|
|
104
|
+
- 存 `_媒体/头像/`:图片文件 + `avatars.json` 索引(`openId → 文件/外链`)。
|
|
105
|
+
- 没设头像的仍然是「姓名首字 + 稳定配色」的圆。
|
|
106
|
+
|
|
107
|
+
### 设置中心(消息区右上角「⚙ 设置」)
|
|
108
|
+
|
|
109
|
+
一个弹窗管所有设置,左边四个页签:
|
|
110
|
+
|
|
111
|
+
| 页签 | 内容 |
|
|
112
|
+
|---|---|
|
|
113
|
+
| **AI 模型** | 接口地址 / 模型 / API Key / 额外人设;「测试连接」「清空自定义」;顶部显示当前生效的模型与来源 |
|
|
114
|
+
| **监控 / 日志** | 两个长连接的状态(●已连接、pid、启动时间、累计事件/写入/忽略、最后事件时间)、**重启 / 停止 / 立即兜底同步**;兜底同步的开关与间隔;**实时日志(listen.log,每 3 秒刷新,能看到连接、ready、事件全过程)** |
|
|
115
|
+
| **进程** | 服务与两个监听的进程表(PID、父 PID、状态、启动时间)+ 版本 / Node / 运行时长 / 内存 / 数据目录 + 服务日志(server.log) |
|
|
116
|
+
| **通用** | 粘贴图片的发送方式(内联图片 / 文件)、侧栏与联系人默认收起、快捷键说明 |
|
|
117
|
+
|
|
118
|
+
账号胶囊面板底部的「⚙ 设置」也会直接打开这个弹窗(默认落在 AI 页签)。
|
|
119
|
+
|
|
120
|
+
### 实时监听(自动收新消息)
|
|
121
|
+
|
|
122
|
+
服务启动时会拉起**两个常驻子进程**(走钉钉 Stream 长连接,**不占消息接口额度**):
|
|
123
|
+
|
|
124
|
+
| 进程 | 作用 |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `dws event +listen-im --kind all-group` | 收全部群消息事件 |
|
|
127
|
+
| `dws event +listen-im --kind all-direct` | 收全部单聊事件 |
|
|
128
|
+
|
|
129
|
+
事件里自带 `conversation_id / message_id / content / sender / create_time`,所以**直接把新消息写进对应归档**,不用再调消息接口。
|
|
130
|
+
|
|
131
|
+
- **在哪看状态**:点左上账号胶囊 → 面板里「实时监听」区块:两个进程各一行(`●` 已连接 / `○` 重连中)、pid、已收事件数、写入条数、最后事件时间;下面还有「兜底同步」状态。每 5 秒自动刷新。
|
|
132
|
+
- **坏了怎么重启**:面板上的 **重启**(只重启这两个监听进程,不动服务);也可以用 **停止 / 启动** 单独开关;整个服务出问题就 `oh-my-dws restart`。
|
|
133
|
+
- **自动重连**:进程意外退出会按 2s / 5s / 15s / 30s / 60s 退避重连,日志在 `~/.oh-my-dws/_运行/listen.log`。
|
|
134
|
+
- **兜底同步**:另外每 60 秒查一次「钉钉未读会话」,只对已归档的做增量补拉,用来兜住长连接漏事件的情况(可以在 `/api/sync` 或代码里关掉)。
|
|
135
|
+
|
|
136
|
+
> 只有 AI 那两个按钮会产生费用;监听和拉取都不花钱,也不占消息接口额度。
|
|
137
|
+
|
|
138
|
+
**未读怎么算**
|
|
139
|
+
|
|
140
|
+
- 侧栏/联系人上的红色未读角标来自归档里的 `newCount`:**只有别人发的消息才计数**,你自己发的(不管是在钉钉里手动发,还是从本工具发)都不会点亮红点。
|
|
141
|
+
- 「我」的判定:`auth status` 拿 userId + 姓名 → `contact user search` 按 userId 对上取 `openDingTalkId`,进程内缓存。
|
|
142
|
+
- 页面每 5 秒查一次 `/api/ping`(只比一个版本号,本地开销很小):有变化就自动刷新左右两栏;正在看的会话只在顶部弹「↓ 有 N 条新消息,点击刷新」,不会自己跳走打乱你的位置。
|
|
59
143
|
|
|
60
144
|
### 右键菜单
|
|
61
145
|
|
|
@@ -75,12 +159,24 @@ oh-my-dws start
|
|
|
75
159
|
### 消息区
|
|
76
160
|
|
|
77
161
|
- **默认倒序**(最新在最上),工具栏可切「最早在前」;支持关键词、发送者、来源群、时间范围过滤。
|
|
162
|
+
- **秒开**:同一个会话第二次打开走内存缓存(10 分钟有效),**不再闪「加载中」**,通常 10ms 内就出来;后台再悄悄比对,内容没变就不重渲染(不会打断你的滚动位置)。
|
|
163
|
+
- **打开很快就到位**:时间范围默认 **今天**(如果这个会话/范围一条都没有,会自动放开到「全部时间」,不会一进来就空),首屏**只渲染最近 10 条**(右上角显示 `10+82 / 1392 条` 表示已显示 10、还有 82 条符合当前筛选),底部「加载更早的 20 条」按需追加——所以哪怕单聊有 1300 条历史也不会卡。
|
|
78
164
|
- 新拉到的消息会高亮,逐条带 `messageId`、时间、发送者头像(按人员 ID 稳定配色 + 姓名首字)。
|
|
79
165
|
- 引用消息显示成引用块,点一下跳到被引用的那条;`**加粗**`、`` `代码` ``、@人、链接都会渲染。
|
|
80
166
|
- **图片直接看**:`[图片消息]` 按需下载到 `_媒体/` 并内联显示,点开可放大预览(Esc 关闭)。
|
|
81
167
|
- **拉取最新消息**(消息区右上角):从最后一条时间 −1 分钟拉到当前,按 `messageId` 去重合并,回写 JSON + Markdown + 群索引。
|
|
82
|
-
- **发消息**:右下角「✏️ 发消息」,`⌘/Ctrl + Enter` 发送,草稿按会话保存;默认不带 AI
|
|
83
|
-
-
|
|
168
|
+
- **发消息**:右下角「✏️ 发消息」,`⌘/Ctrl + Enter` 发送,草稿按会话保存;默认不带 AI 角标;**输入框随内容自动变高**(最多 160px)。
|
|
169
|
+
- **撤回标记**:撤回的消息显示成虚线卡片「已撤回 + 谁撤的 + 时间」,正文清空。
|
|
170
|
+
- 你自己撤回:点完立刻打标,不等钉钉。
|
|
171
|
+
- 别人撤回:钉钉的 `recall` 事件只支持「指定群/指定单聊」订阅(`--kind all-*` 不支持 recall),所以这条走**拉取时识别**——下次拉取发现正文变成「撤回了一条消息」占位、或原本有正文现在被清空,就自动打标。
|
|
172
|
+
- **撤回自己的消息**:在消息上**右键**(只对自己发的消息生效,别人的消息不弹菜单)→「↩️ 撤回这条消息」/「📋 复制消息内容」。撤回走 `dws chat message recall`,钉钉里会显示「某某撤回了一条消息」,撤回后自动回读归档。
|
|
173
|
+
- **图文混排(一条消息里既有文字又有图片)**:`⌘V` 粘贴截图或拖图片进来后,图片会**挂在输入框上方的缩略条里**(可点 × 移除、可多张),然后正常写文字,点「发送」/`⌘/Ctrl+Enter` —— 文字和图片会合成**同一条** markdown 消息发出去(钉钉里就是截图那种"文字 + 图片"的气泡)。
|
|
174
|
+
- **只发图片**:不写文字,直接挂着图片点发送即可。
|
|
175
|
+
- 原理:`dws` 只能发「已有 mediaId 的图片」或「本地文件」,没有本地图片上传通道;所以先把图传到图床拿公网 URL,再用 markdown 图片 `` 发出去(图文混排就是 `文字\n\n`)。
|
|
176
|
+
- 图床脚本默认为 `~/.agents/skills/inke-image-upload/scripts/upload-image.mjs`,可用环境变量 `IMAGE_UPLOAD_SCRIPT` 指到别的实现;**上传失败会自动退回文件方式**(不会白粘一次)。
|
|
177
|
+
- 发出的原图同时留一份在 `_媒体/已发/`,归档里也能直接看到(文件名去掉了时间戳前缀,重名才加 `-2`)。
|
|
178
|
+
- 别人发的文件消息(带 `fileId`)无法下载:钉钉只开放 `mediaId` 下载,不开放钉盘 `fileId`。注意——**这是以你本人钉钉身份发出的真消息**。
|
|
179
|
+
- **@我 聚合**:「@我的消息」把所有 `@我` 汇总成一条时间线,来源群列在下面(**含未归档群**,点一下自动归档并定位);每条消息可「归档定位 / 打开钉钉 / 复制定位信息」。**从「起点日」往后累积**:第一次汇总是今天,之后新来的往上加,更早的历史不往回捞(卡片标题会显示「自 MM-DD」);想重来就右键 →「🧹 清空 @我 并从今天重新汇总」。
|
|
84
180
|
|
|
85
181
|
## 四、数据在哪、怎么备份
|
|
86
182
|
|
|
@@ -88,6 +184,9 @@ oh-my-dws start
|
|
|
88
184
|
~/.oh-my-dws/
|
|
89
185
|
├── 群索引.md 群名 → 群 ID / 成员数 / 文件位置(自动重建,别手改)
|
|
90
186
|
├── server.json 后台服务 PID / 端口(start 写、stop 删)
|
|
187
|
+
├── 常用联系人.json 你自己维护的常用联系人名单
|
|
188
|
+
├── 布局.json 会话列表的拖动顺序与置顶
|
|
189
|
+
├── ai.json AI 接口配置(模型/Key,选填)
|
|
91
190
|
├── _运行/server.log 后台服务日志
|
|
92
191
|
├── _原始数据/ dws 原始 JSON + dws-login.log
|
|
93
192
|
├── _媒体/ 图片/文件缓存(可删,会重新拉)
|
|
@@ -103,7 +202,7 @@ oh-my-dws start
|
|
|
103
202
|
|
|
104
203
|
## 五、常见问题
|
|
105
204
|
|
|
106
|
-
**端口被占用**:`oh-my-dws status` 看是不是已经在跑;换端口 `oh-my-dws restart --port
|
|
205
|
+
**端口被占用**:`oh-my-dws status` 看是不是已经在跑;换端口 `oh-my-dws restart --port 25260`。
|
|
107
206
|
|
|
108
207
|
**页面提示「dws 未就绪 / 未登录」**:`dws auth status` 确认;点胶囊里的「登录 dws」,或命令行 `dws auth login --recommend`。
|
|
109
208
|
|
|
@@ -113,17 +212,77 @@ oh-my-dws start
|
|
|
113
212
|
|
|
114
213
|
**macOS 双击 `启动.command`**:等价 `oh-my-dws start`(后台常驻 + 开浏览器),关掉终端窗口服务照旧跑;停用 `oh-my-dws stop`。
|
|
115
214
|
|
|
116
|
-
##
|
|
215
|
+
## 六、发版:打 tag 自动发 npm
|
|
216
|
+
|
|
217
|
+
发版已经交给 CI(`.github/workflows/release.yml`):**推 `v*` tag → GitHub Actions 自动 `npm publish`**。
|
|
218
|
+
|
|
219
|
+
### 一次性配置(二选一)
|
|
220
|
+
|
|
221
|
+
**方式 A:可信发布(推荐,不用管 token)**
|
|
222
|
+
|
|
223
|
+
在 https://www.npmjs.com/package/oh-my-dws/access → **Trusted Publishers** → Add publisher,
|
|
224
|
+
选 **GitHub Actions**,按这样填(大小写、名字必须一模一样):
|
|
225
|
+
|
|
226
|
+
| 字段 | 填什么 |
|
|
227
|
+
|---|---|
|
|
228
|
+
| Publisher | `GitHub Actions` |
|
|
229
|
+
| Organization or user | `duzhenxun` |
|
|
230
|
+
| Repository | `oh-my-dws` |
|
|
231
|
+
| Workflow filename | `release.yml` |
|
|
232
|
+
| Environment name | 留空 |
|
|
233
|
+
|
|
234
|
+
保存后 CI 里不需要任何 token;workflow 已带 `id-token: write` 并把 npm 升到 ≥ 11.5.1。
|
|
235
|
+
|
|
236
|
+
**方式 B:Automation token(旧做法)**
|
|
237
|
+
|
|
238
|
+
1. npmjs.com → Access Tokens → Generate New Token → **Automation**
|
|
239
|
+
2. 仓库 → Settings → Secrets and variables → Actions → New repository secret,名字 `NPM_TOKEN`
|
|
240
|
+
|
|
241
|
+
workflow 两种都兼容:有 `NPM_TOKEN` 就用它,没有就走 OIDC 可信发布。
|
|
242
|
+
|
|
243
|
+
> 别加 `--provenance`:provenance 只允许**公开仓库**,本仓库是 private,加了会 422。
|
|
244
|
+
|
|
245
|
+
### 以后发版
|
|
117
246
|
|
|
118
247
|
```bash
|
|
119
|
-
npm
|
|
120
|
-
npm
|
|
121
|
-
npm
|
|
248
|
+
npm run release # 补丁版 0.0.1 → 0.0.2(改 package.json + commit + tag + push)
|
|
249
|
+
npm run release:minor # 次版本 0.1.0
|
|
250
|
+
npm run release:major # 主版本 1.0.0
|
|
122
251
|
```
|
|
123
252
|
|
|
124
|
-
|
|
253
|
+
这三个脚本做的是 `npm version <x> -m "chore: release v%s"` + `git push --follow-tags`,
|
|
254
|
+
tag 推上去后 CI 自动跑完发布。想手动控制也可以:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npm version 0.0.3 -m "chore: release v%s"
|
|
258
|
+
git push --follow-tags
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### CI 做了什么
|
|
262
|
+
|
|
263
|
+
1. 校验 **tag 与 `package.json` 版本一致**(`v0.0.3` ↔ `0.0.3`,不一致直接失败,不会发错版本)
|
|
264
|
+
2. 跑 `npm run check`(全部 JS 语法 + 网页内联脚本 + `$("#id")` 引用是否都存在)
|
|
265
|
+
3. `npm pack --dry-run` 看打包内容
|
|
266
|
+
4. npm 上已有该版本则跳过(可安全重跑)
|
|
267
|
+
5. `npm publish --access public`(token 走 `NPM_TOKEN`)
|
|
268
|
+
6. 建同名 GitHub Release
|
|
269
|
+
|
|
270
|
+
### 发了之后
|
|
125
271
|
|
|
126
272
|
```bash
|
|
127
273
|
npm i -g oh-my-dws # 装
|
|
128
|
-
oh-my-dws update #
|
|
274
|
+
oh-my-dws update # 升级到 latest 并自动重启
|
|
129
275
|
```
|
|
276
|
+
|
|
277
|
+
### 常见错误
|
|
278
|
+
|
|
279
|
+
| 报错 | 原因 / 处理 |
|
|
280
|
+
|---|---|
|
|
281
|
+
| `EOTP` / 要求一次性验证码 | 本地发布才会遇到;CI 用可信发布或 Automation token 不会 |
|
|
282
|
+
| `E401` / `Unable to authenticate` | 可信发布里的 Workflow filename / repo 写错,或 `id-token: write` 没开 |
|
|
283
|
+
| `E403` 无权限发布 | token 没给这个包的读写权限,或包名被别人占 |
|
|
284
|
+
| `422 Unsupported ... visibility: private` | 给私有仓库加了 `--provenance`,去掉它 |
|
|
285
|
+
| `npm 版本太低` | 可信发布需要 npm ≥ 11.5.1,workflow 里已自动 `npm i -g npm@latest` |
|
|
286
|
+
| CI 报 tag 与版本不一致 | 先 `npm version` 再打 tag,不要直接手推 tag |
|
|
287
|
+
|
|
288
|
+
> 手动应急发布(不经过 CI):`npm publish --otp=<6位码>`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-dws",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "钉钉群消息归档:本地网页控制台浏览、增量拉取、按群归档与全文 Markdown 导出,数据存放于 ~/.oh-my-dws(支持 start/stop/restart/status/update)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dingtalk",
|
|
@@ -37,7 +37,11 @@
|
|
|
37
37
|
"stop": "node bin/oh-my-dws.js stop",
|
|
38
38
|
"status": "node bin/oh-my-dws.js status",
|
|
39
39
|
"restart": "node bin/oh-my-dws.js restart",
|
|
40
|
-
"prepublishOnly": "node -e \"require('fs').accessSync('web/index.html')\""
|
|
40
|
+
"prepublishOnly": "node -e \"require('fs').accessSync('web/index.html')\"",
|
|
41
|
+
"check": "node scripts/check.js",
|
|
42
|
+
"release": "npm version patch -m \"chore: release v%s\" && git push --follow-tags",
|
|
43
|
+
"release:minor": "npm version minor -m \"chore: release v%s\" && git push --follow-tags",
|
|
44
|
+
"release:major": "npm version major -m \"chore: release v%s\" && git push --follow-tags"
|
|
41
45
|
},
|
|
42
46
|
"engines": {
|
|
43
47
|
"node": ">=18"
|