DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

nineoooo /

nineoooo/dsh-qzone-echo

Verified

A DSH plugin that archives your own QQ Zone posts into a local library, auto-sorted into thoughts, dreams and reviews — with a private web viewer and offline image backup. Cookie stays on your machine.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@d8e9c5d2

dsh-qzone-echo · QQ说说收集

把你自己 QQ 空间发过的说说收进本地库,分成四栏:全部 / 个人思考·感想 / 梦记 / 介绍·推荐。

自动分栏靠规则,规则一定会有判错的时候 —— 所以三栏都能逐条手改:认错了点一下就好,改错了再点一下撤销, 撤销只影响那一栏,不会把你在另一栏的手改连带清掉,具体见下面「判错了怎么办」。

怎么用

  1. 设置 → 左侧导航「QQ说说收集」
  2. 浏览器登录 https://user.qzone.qq.com → F12 → Network → 随便点一条说说列表请求 → 复制请求头里整段 Cookie(必须有 uin=o0xxxx 和 p_skey),粘进设置页存下来
  3. 点「全量同步」,翻页拉全部说说(首次按 maxPerSync 封顶)
  4. 每条会按规则打分(lib/classify.mjs),≥ 分数线(默认 45)的进「思考」那一栏; 带「梦见 / 梦到 / 做梦 / 梦里…」的另外进「梦记」那一栏; 书评、绘本介绍、荐物这类「讲别人作品」的单独进「介绍/推荐」那一栏(不算思考,但一条不丢)
  5. 四栏可以分别翻、分别导出;改分数线即时重算
  6. 想要图:设置页「⑤ 配图存到本地」点一下,或对话里 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 再重载插件即可
  • ⚠️ 这是门锁不是加密:防的是「同一台机器上别人顺手点开这个网址」。 真要防硬盘被拿走,请用系统级磁盘加密

别人搜得到吗?

搜不到,三层都不露:

  1. 宿主只监听 127.0.0.1 —— 外网、局域网都连不到,「搜到」的前提都没有
  2. 页面带 <meta name="robots" content="noindex,nofollow,noarchive,nosnippet"> 与 X-Robots-Tag 响应头
  3. 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_gate Cookie(30 天),之后 /ui 才肯给你
  • 点「撤掉前缀」回到 /qzone
  • ⚠️ 藏网址不是加密,只挡瞎猜的人;真正挡数据的是口令,两个都设才稳

搜索

设置页第⑦栏多了一行搜索框,网页版在顶栏:

  • 打关键词 → 回车或点「搜」
  • 搜的是正文 + 评论(含回复)+ 转发原文 + 分享标题
  • 多个词用空格分隔,是「全都要包含」的关系

三种收法(历史多了就靠它们)

口径 怎么点 什么时候用
全量 「全量同步」 第一次收,或怀疑中间漏了
按月 填 2021-06 → 「只收这个月」 号老、条数多:一个月一个月搬,稳,进度看得见
增量 「只补新的」 平时补新发的:翻到库里已有的就停,最省时间

自动增量:设置页「④ 不断收新发的」打开后,后台每 N 小时(默认 6)自己跑一次增量, 只补新发的、顺手分类。开关关着时定时器什么都不做。 ⚠️ 接口不认 begintime/endtime(实测传了也原样返回全部),所以「按月」是客户端切的: 从最新往前翻,翻到下界以下的立刻停 —— 因此越早的月份翻得越久。

号特别老、条数特别多时的两条实战经验

  1. 每页最多 40 条:接口 num 传 41 以上就是不报错地只给 10 条(看起来像「到底了」), 插件已经硬性夹到 40,不用管。
  2. 接着往前挖要带 startPos:库已经收到 2024 年了,想再往 2023/2022 挖, 从 0 开始等于把已有的几千条重翻一遍(又慢又白白多撞限流)。 传 startPos: <库里已有的条数> 就能从那个位置接着翻。
  3. 翻几千条会撞腾讯限流(使用人数过多,请稍后再试)或偶发 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。
—/ 5

No ratings yet

Verified DSH bundle

Commit d8e9c5d233b2

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout