dsh-qzone-echo · QQ说说收集
把你自己 QQ 空间发过的说说收进本地库,分成四栏:全部 / 个人思考·感想 / 梦记 / 介绍·推荐。
自动分栏靠规则,规则一定会有判错的时候 —— 所以三栏都能逐条手改:认错了点一下就好,改错了再点一下撤销, 撤销只影响那一栏,不会把你在另一栏的手改连带清掉,具体见下面「判错了怎么办」。
怎么用
- 设置 → 左侧导航「QQ说说收集」
- 浏览器登录 https://user.qzone.qq.com → F12 → Network → 随便点一条说说列表请求 →
复制请求头里整段 Cookie(必须有
uin=o0xxxx和p_skey),粘进设置页存下来 - 点「全量同步」,翻页拉全部说说(首次按
maxPerSync封顶) - 每条会按规则打分(
lib/classify.mjs),≥ 分数线(默认 45)的进「思考」那一栏; 带「梦见 / 梦到 / 做梦 / 梦里…」的另外进「梦记」那一栏; 书评、绘本介绍、荐物这类「讲别人作品」的单独进「介绍/推荐」那一栏(不算思考,但一条不丢) - 四栏可以分别翻、分别导出;改分数线即时重算
- 想要图:设置页「⑤ 配图存到本地」点一下,或对话里
qzone_echo_images
判错了怎么办(人工纠正)
规则永远会误判(比如只是发张图、只是转述绘本简介)。三栏都能手改:
- 思考:「算思考」/「不算思考」
- 梦记:被认成梦了但其实是随手记的 —— 「不是梦」
- 介绍/推荐:其实是在说自己的感受 —— 「不是推荐」
⚠️ 要点两下才生效:第一次点只是「上膛」(按钮变成黄色「再点一下确认」),
再点一次才真的写进 corrections.jsonl;点了别处、或等 5 秒,上膛状态自己泄掉。
这样手滑一下不会把数据改坏。
改错了怎么办?设置页有一栏 「⑧ 你手动改过的」:
- 每条改过的都列在那儿(改的是哪一栏 + 时间 + 正文),点「撤销」回到按规则自动判
- 撤销是按栏算的:撤掉某条的「梦记」判定,不会顺手把你给同一条打的「算思考」一起抹掉
- 整个改歪了就点「全部撤销(回到自动判)」,
corrections.jsonl清空 - 网页版上改错了,卡片上会多一个「恢复思考/梦记/推荐自动判定」的按钮,点它就行(也是只撤那一栏)
对话里也能用 qzone_echo_fix(tid + category(thought/dream/review)+ value(true/false)改;
undo: true 撤单条(默认只撤 category 那一栏,不传 category 才三栏全撤);undoAll: true 全撤;
旧的 tid + thought 写法照样能用)。
网页版(看的时候最舒服)
设置页「⑦ 看 / 搜 / 导出」右边有个 「网页版 ↗」,点开是 http://<宿主地址>/api/qzone-echo/ui
(也有个短链:http://<宿主地址>/qzone)。
- 一个自包含页面,不开新端口、不另起服务,走宿主自己的 webServer
- 深色底、大图、表情按 QQ 原样还原(
[em]eNNN[/em]→qzonestyle的真表情图) - 四栏切换(全部 / 思考 / 梦记 / 介绍推荐)+ 年份与月份筛 + 关键词搜索(正文、评论、转发全文一起搜,多词空格分隔)
- 字数统计(按年 / 按月)、配图补全按钮也在
- 纠正按钮也在(同样是两下确认),改完就地更新、不会跳回第一屏(翻到第几屏就还在第几屏)
- 只读本地
$DSH_HOME/qzone-echo/的 jsonl,不碰网络、不碰 Cookie
给网页版上锁(口令)
设置页 「⑩ 网页版要不要口令」:填一个(≥4 位)→ 点「设口令」。也可以点「随机一个」让它生成。
| 状态 | 会怎样 |
|---|---|
| 没设口令 | 谁打开这个地址都能看(本机自用最省事) |
| 设了口令 | 除登录框外什么都看不到:/items /status /export /img 一律 401 |
几个要点:
- 口令只存 scrypt 派生值(盐 + hash),在
$DSH_HOME/qzone-echo/webpass.json,权限 600; 明文和 hash 永不出现在任何接口返回里 - 登录成功发一个随机 token,存 HttpOnly Cookie(7 天)。服务端只存它的 sha256 哈希
(
$DSH_HOME/qzone-echo/websessions.json,权限 600)—— 插件热重载不会把已登录的浏览器踢下线; 改口令也不会(改之前在设置页已经验过旧口令,那个人就是老大自己) - 只有宿主重启(进程内存全没)才需要重新输一次;想立刻踢掉所有设备就用「撤掉口令」再重设
- 连错 8 次歇 30 秒(本机自用,够挡住手贱)
- 设了口令后设置页也读不到数据了(同一个接口,不然直接打接口就绕过去了); 设置页会摆一个「去网页版输口令」的按钮,并且每 3 秒自己重试一次、切回窗口也会重试, 所以网页版输完切回来它就自己好了,不用手动刷新
- 忘了口令:找不回来(只存派生值)。删掉
$DSH_HOME/qzone-echo/webpass.json再重载插件即可 - ⚠️ 这是门锁不是加密:防的是「同一台机器上别人顺手点开这个网址」。 真要防硬盘被拿走,请用系统级磁盘加密
别人搜得到吗?
搜不到,三层都不露:
- 宿主只监听
127.0.0.1—— 外网、局域网都连不到,「搜到」的前提都没有 - 页面带
<meta name="robots" content="noindex,nofollow,noarchive,nosnippet">与X-Robots-Tag响应头 GET /api/qzone-echo/robots.txt返回User-agent: */Disallow: /
(这三点是防「以后万一换了反向代理/公网域名」时被索引,本机自用其实第 1 条就够了。)
网址也能藏(随机前缀)
设置页 「⑪ 网页版网址(要不要藏)」:
| 状态 | /qzone |
/api/qzone-echo/ui |
|---|---|---|
| 没设前缀(默认) | ✅ 302 到网页版 | ✅ 能开 |
| 设了前缀 | ❌ 404 | ❌ 404(要先走过 /qz/<前缀> 拿到通行 Cookie) |
- 点「随机一个」自动生成 10 位前缀;也能自己填(字母数字
-_,4~40 位) - 设完入口变成
http://<宿主>/qz/<前缀>,旧地址立刻 404 - 走一次前缀会下发
qz_gateCookie(30 天),之后/ui才肯给你 - 点「撤掉前缀」回到
/qzone - ⚠️ 藏网址不是加密,只挡瞎猜的人;真正挡数据的是口令,两个都设才稳
搜索
设置页第⑦栏多了一行搜索框,网页版在顶栏:
- 打关键词 → 回车或点「搜」
- 搜的是正文 + 评论(含回复)+ 转发原文 + 分享标题
- 多个词用空格分隔,是「全都要包含」的关系
三种收法(历史多了就靠它们)
| 口径 | 怎么点 | 什么时候用 |
|---|---|---|
| 全量 | 「全量同步」 | 第一次收,或怀疑中间漏了 |
| 按月 | 填 2021-06 → 「只收这个月」 |
号老、条数多:一个月一个月搬,稳,进度看得见 |
| 增量 | 「只补新的」 | 平时补新发的:翻到库里已有的就停,最省时间 |
自动增量:设置页「④ 不断收新发的」打开后,后台每 N 小时(默认 6)自己跑一次增量,
只补新发的、顺手分类。开关关着时定时器什么都不做。
⚠️ 接口不认 begintime/endtime(实测传了也原样返回全部),所以「按月」是客户端切的:
从最新往前翻,翻到下界以下的立刻停 —— 因此越早的月份翻得越久。
号特别老、条数特别多时的两条实战经验
- 每页最多 40 条:接口
num传 41 以上就是不报错地只给 10 条(看起来像「到底了」), 插件已经硬性夹到 40,不用管。 - 接着往前挖要带
startPos:库已经收到 2024 年了,想再往 2023/2022 挖, 从 0 开始等于把已有的几千条重翻一遍(又慢又白白多撞限流)。 传startPos: <库里已有的条数>就能从那个位置接着翻。 - 翻几千条会撞腾讯限流(
使用人数过多,请稍后再试)或偶发HTTP 500 空响应。 这两种都不是 Cookie 问题、不是封号:已经抓到的照样入库(返回里partial: true+nextPos告诉下次从哪接着翻),歇一阵再来就行。
长说说补全:腾讯只给前 500 字,插件会自己补回全文
腾讯的说说接口有个硬上限:正文只返回前 500 字左右,超长说说默认只给开头一截,
接口同时用 has_more_con=1 告诉你「后面还有」。
插件做了什么:
- 同步时带
not_trunc_con=1去要正文,能一次拿到全文 - 万一还是短的(老数据、接口改口径),就交给后台断点修复(设置页「长文补全」,或
qzone_echo_repair_longtext):- 优先挑「刚好卡在 500 字」「断在半句没句号」「接口明说还有更多」的帖子
- 逐条打详情接口核对;只补长、不缩短,同长度就不动
- 每条间隔 3.5 秒、每批 5 条;撞限流自动暂停并写冷却时间,醒了接着来
- 修复前自动备份
feed.before-longtext-repair-<时间戳>.jsonl - 断点在
longtext-repair-state.json,随时可以停、随时可以接着跑
- 网页版和
qzone_echo_list会如实标出contentIncomplete(网页上是橙色的「正文可能不全」), 不会把截断的正文伪装成原文
⚠️ 这条链路的判据是「启发式」:短帖(< 300 字)、句末有标点、详情接口已确认过的都不进队列, 所以偶尔会把一条本来完整的帖子也拿去核对一遍 —— 核对结果是「一样长」就只打上「已核对」标记,正文不动。
配图下载(不烧 token)
配图默认只存链接;要留档得自己下:
- 设置页点「把配图下载到本地」,或对话里
qzone_echo_images(可带scope=thoughts/dreams、year、limit) - 开关「同步后自动下图」打开后,每次同步完自动补没下过的
- 不烧 token:下载是纯本地 I/O,图片字节不经过模型,只有你把图喂给模型看才会花
- 落地到
~/.dsh/qzone-echo/images/<年>/<url 的 sha1 前 16 位>.<ext> - 同一个 URL 只存一份;已经有的跳过(
force: true可重下);下不下来的只记在errors里,下次还能补 - 单张超过 20 MB 直接跳过(防坏链塞爆磁盘)
- 结果回写到
feed.jsonl的localImages(与images一一对齐,失败的位置是null) - 二次同步不会把已落地的
localImages冲掉(mergeMoments里专门保了这一列) - 图数上限在腾讯那边,不在我们这:说说接口的
pic数组一条最多给 9 张(自己的), 转发时原帖的图在rt_con.pic里、也最多 9 张,所以库里最多 18 张。 原帖实际有几张由接口的pictotal给,存在picTotal字段里 —— 网页版看到原帖共 50 张,QQ 接口一次只给到 9 张就是这个原因,不是丢图。 代码这边不再对images做任何截断:接口给多少存多少、给多少显示多少。
多号与清理(拿别的号测试时用)
库里的每条说说都带 owner(哪个 QQ 号),id 是 owner:tid:
- 换号同步不会互相覆盖(tid 是空间侧的,不同号之间撞了也不会串)
- 设置页「⑨ 清掉某个号的数据」或对话里
qzone_echo_purge:- 第一次点 = 预览(只说会删几条),确认后点「确定删掉」才真删
- 删完
thoughts.jsonl/dreams.jsonl跟着重算,不同步、不残留 - 那个号独占的配图文件一起清掉;两个号共用的图保留
- 老数据(
owner还没加的那批)算在unknown名下,用uin: "unknown"清
栏与栏的边界(重要)
梦记 ≠ 思考 ≠ 介绍/推荐,三者互斥:一条说说只会落在一栏里。
梦想、梦寐以求、同床异梦、红楼梦这类固定搭配不算梦记(先抹掉再看)- 「做梦」单独两个字也算 —— 醒来只记一句,往往就是这么短
- 梦记不看分数线:它靠关键词命中,不靠分数
- 介绍/推荐(
kind=review):开头是「今日绘本NN / 书评 / 荐书 / 安利…」这种系列标题, 或者正文有书名号《…》并且带「图:/文:/译:/作者:」这类出版信息行 —— 主体是「讲别人的作品」,所以不算个人思考,但也不丢,单列一栏能翻到
数据在哪
~/.dsh/qzone-echo/feed.jsonl—— 全部说说(原始)~/.dsh/qzone-echo/thoughts.jsonl—— 筛出来的「个人思考/感想」~/.dsh/qzone-echo/dreams.jsonl—— 筛出来的「梦记」~/.dsh/qzone-echo/reviews.jsonl—— 筛出来的「介绍/推荐」(书评绘本这类)~/.dsh/qzone-echo/corrections.jsonl—— 人工纠正(一条一行,永远压过自动打分)~/.dsh/qzone-echo/images/<年>/—— 下载下来的配图(如果点了下载)~/.dsh/qzone-echo/credential.json—— Cookie(只在本机,等于账号钥匙,别外传)~/.dsh/qzone-echo/config.json—— 分数线、每页条数、要不要自动下图这些~/.dsh/qzone-echo/webpass.json—— 网页版口令的 scrypt 派生值(删掉它就等于撤口令)~/.dsh/qzone-echo/websessions.json—— 已登录浏览器的 token 哈希 + 过期时间(删掉 = 踢掉所有设备)
工具(对话里也能直接用)
| 工具 | 干什么 |
|---|---|
qzone_echo_login |
存 Cookie(顺手验证一次) |
qzone_echo_sync |
同步(同时更新思考 + 梦记 + 介绍/推荐三栏);startPos 可从第 N 条接着往前挖 |
qzone_echo_repair_longtext |
补全被腾讯截断的长说说(action=start/status/pause;断点续跑、限流自动冷却) |
qzone_echo_status |
看状态(条数、年份、各栏多少、补全进度) |
qzone_echo_list |
按年份 / 关键词 / 类型翻(kind=dream 只翻梦记,kind=review 只翻介绍/推荐) |
qzone_echo_thoughts |
只拿「思考」候选(分数从高到低) |
qzone_echo_dreams |
只拿「梦记」(时间倒序) |
qzone_echo_fix |
人工纠正一条的思考 / 梦记 / 推荐分类(category + value),或撤销(undo / undoAll) |
qzone_echo_export |
导出 Markdown / JSON(scope=thoughts/dreams/reviews/all) |
qzone_echo_images |
把配图下到本地(dryRun: true 只统计不下) |
qzone_echo_image_job |
后台慢慢补配图,断点续跑(start / pause / status) |
qzone_echo_complete_images |
补全一条说说里存漏了的图(一次一条,tid 指定) |
qzone_echo_purge |
删掉某个号的全部说说(不传 apply 只预览) |
导出的两种格式看什么
| 格式 | 长什么样 | 拿它干嘛 |
|---|---|---|
Markdown(.md) |
按年分组的人话:# 我的 QQ 空间说说 → ## 2026(22 条) → ### 时间,评论排成 - 昵称(时间):内容,QQ 表情转成  |
丢进笔记软件、Obsidian、Word —— 直接读 |
JSON(.json) |
原始条目数组,每条一堆字段:tid / ts / time / content / images / localImages / comments / owner / share / forwarded … |
给程序吃、自己再加工;不是给人读的 |
两个都在 ~/.dsh/qzone-echo/out/ 里,按钮点了会提示完整路径。
⚠️ Markdown 里的表情是网络图(
qzonestyle.gtimg.cn),编辑器要能联网才显示得出来。 本地离线看请看网页版,那里的图和表情都是同源/本地直出。
只读,不动你的号
插件只会 GET 空间的说说列表接口,没有任何发说说、评论、点赞、转发、删除的调用;
配图下载也是对腾讯 CDN 的 GET;
也不往外发任何东西,数据只落在这台机器的家目录里。
打分是规则不是模型
lib/classify.mjs 用的是词表 + 结构信号(第一人称、情绪词、反思句式、长度、有无图/转发)。
所以 qzone_echo_thoughts 给的是候选:分数只能保证"像",要下判断还得读原文。
分享的歌 / 视频 / 网页
说说里的「分享了一首歌」不是普通文本:正文常常是空的,内容藏在 source_appid /
source_name / source_url 里(转发的话在 rt_source_* 上)。2026-10-22 之前这几种字段没解析,
结果分享歌的说说在库里长得像「一条空说说」——已经补上:
- 解析后存成
share: { appid, name, url, music, cardText },kind记成share - 只认真有
appid或跳转链接的,光有正文的普通说说不会被误判成分享 - 歌判定:
appid=233(QQ音乐)或链接命中网易云/酷狗/酷我等域名 - 打分里分享卡片降权 26 分:这是「我分享了它」,不是「我在想什么」,不该冒充个人思考
- 导出 Markdown 时写成
> 🎵 分享了一首歌:晴天 — https://... - 同步不会因为解析规则升级而漏更新:
share变了也会重新入库
一点坑(写在这里免得下次再踩)
- 接口是
emotion_cgi_msglist_v6,g_tk由p_skey算(skey也行),Cookie 缺p_skey直接 401。 - 判断「这个号有没有内容」别信
conlist:老说说(type=2)conlist有条目,分享类说说(纯卡片)反而一条都没有, 所以conlist.length === 0不代表空说说。 - Cookie 里的
xxx_totalcount是浏览器留下的旧计数(测试号显示 6,接口实际 12 条),以接口total为准。 apply(ctx, config)传进来的 config 字段值不保证是标量(宿主可能把 schemastery 的字段对象原样透传), 所以index.js里一律走normalizeCfg()按DEFAULT_CONFIG的类型挑一遍再合并。- 工具注册时的
parameters必须带additionalProperties: false,缺了宿主校验直接抛错,整条插件判 failed。 - 改
lib/*.mjs的 import 之后记得跑一遍selftest.mjs—— 2026-10-22 就漏掉过recomputeThoughts, 宿主里不报错但改阈值那条路会 500。
No comments yet. Be the first to write one.