Skip to content

dsh-meme-qq

Verified

dsh-meme-qq · v0.2.1 · MIT

DSH 插件:把表情包图库接到 QQ,给一句描述就自己找图并发裸图(不套气泡、不出现 [表情: ...] 标记)

Install

dsh plugin add dsh-meme-qq

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

dsh-meme-qq

DSH 的表情包图库 + QQ 机器人接口。给一句描述或情绪,插件自己找图,直接把裸图发到 QQ(不套文字气泡、正文里不出现 [表情: ...] 这类标记)。

为什么需要它:dsh-meme 是在回复里写 [表情: 描述],由网页端渲染成图。QQ 通道没有渲染层,那边永远只收到一行字。这个插件补上最后一步。

更新日志

  • 0.2.1 — 修掉 0.2.0 的致命 bug:成功返回值里带了一个 undefined 字段,宿主把它判成 value is not lossless JSON,于是每次调用都失败(图可能已经发出去了,模型却收到报错)。现在所有返回值出门前都过一遍无损 JSON 安检,并补了回归测试。
  • 0.2.0 — 全面配置化(图库/凭证/目标)、多目标与群聊、token 缓存、超时重试、错误归一、reindex / stats 动作。

装上

npm i dsh-meme-qq

或者在 DSH 里:设置 → 插件 → 添加插件,填 dsh-meme-qq(也支持 GitHub 地址、本地目录)。装完必须重启 DSH——插件只在进程启动时装载。重启后 meme_qq 会出现在工具列表里。

自己改代码调试时,直接把目录挂进 profile 也行:

{
  "dependencies": { "dsh-meme-qq": "file:D:/path/to/dsh-meme-qq" },
  "dsh": { "profile": { "bundles": ["...", "dsh-meme-qq"] } }
}

图库从哪来

插件只认 <packsDir>/index.json 这一个扁平索引,它由各个图库包的 index.db(sqlite)汇总而来。三种拿到方式:

  1. 已经装了 dsh-meme —— 它自带的图库包会被自动扫进来(includeBundledPack: true)。
  2. 自己的图 —— 按 packsDir/<pack>/memes/<tag>/*.gif + packsDir/<pack>/index.db 放好,然后调 meme_qq { "action": "reindex" }。
  3. 别人的 index.json —— 直接放到 packsDir 下也能用(但图得在对应路径上)。

index.db 表结构:

create table memes(path text, tag text, file_name text, file_hash text,
                   caption text, keywords text, mtime integer, captioned_at integer);
-- path 是相对 pack 根的路径,用 / 分隔,例如 memes/happy/笑.gif

需要什么

  • Node 22.5+(重建索引用的 node:sqlite;不重建索引也能正常发图)
  • 一个自己的 QQ 机器人(QQ 开放平台的 appId / appSecret),并且它跟目标说过话(否则拿不到 openid / group_openid)。插件不提供机器人,也不带任何人的凭证。

配置

配置写在 cordis patch 里那条 entry 上,全部可省。默认值假设 DSH 标准目录($DSH_HOME,默认 ~/.dsh)。

配置项 默认 说明
packsDir $DSH_HOME/meme-packs 图库根目录(每个子目录是一个 pack)
indexPath <packsDir>/index.json 扁平索引文件,可用 action=reindex 重建
usageFile <packsDir>/usage.json 发图账本(发得多往后排)
credentialsFile $DSH_HOME/.credentials.yaml 从这里读 appId / appSecret / userOpenid
appId / appSecret 空 直接填就覆盖凭证文件里的值
targets 空 = 用凭证里的 userOpenid 发私聊 目标列表,见下
defaultTarget 空 不指定目标时发给谁(name / id / 序号)
extraPackDirs [] 额外图库根目录(多个库不同位置时用)
includeBundledPack true 是否把 dsh-meme 自带图库也算进来
tagAliases {} 自定义中文 → tag 映射,叠加在内置别名上
timeoutMs 15000 单次请求超时
retries 2 网络/429/5xx 重试次数
maxFileBytes 8388608 图片上限,超了直接给可读错误
usageDecay 6 每发过一次扣多少匹配分
jitter 12 随机扰动幅度(让相近的图轮着出)
debug false 打开后往 DSH 日志里写每一步

targets

targets:
  - { type: c2c, openid: "USER_OPENID", name: 我 }
  - { type: group, groupOpenid: "GROUP_OPENID", name: 群聊 }

也接受字符串:"c2c:OPENID"、"group:GROUP_OPENID"、直接写 "OPENID"。

工具

meme_qq,三个动作:

{ "description": "累瘫", "tag": "sad" }                    // 默认 action=send
{ "action": "reindex" }                                   // 扫 index.db 重建 index.json
{ "action": "stats" }                                     // 一共多少张、各情绪多少、哪几张发得最多
  • tag 不填也行,会按描述文字匹配;给错了不崩,退回同情绪或全库。
  • 发过的图会降权,所以同一句话连发两次大概率换图。
  • 返回值是 { ok, sent, caption, tag, target };失败是 { ok: false, error: "一句话" }。

收新图

# 1. 图放进 packsDir/<pack>/memes/<tag>/
# 2. 往该 pack 的 index.db 插一行(表结构见上)
# 3. 调 meme_qq { "action": "reindex" }

出错时

  • meme_qq 根本不在工具列表:多半是 package.json 少了 engines.dsh(宿主会静默跳过),或者没重启 DSH。
  • 没读到 QQ 机器人凭证:检查 credentialsFile,或直接在配置里填 appId / appSecret。
  • QQ 接口 401:appId / appSecret 不配套,或 token 失效(插件会自动重取一次)。
  • QQ 接口 403:机器人没开通对应权限(C2C 私聊 / 群消息)。
  • QQ 接口 404:openid 不对,或机器人还没跟这个人/群说过话。
  • 索引里的图文件都不在了:图库搬过家,跑一次 action=reindex。

开发

node --test test/meme-qq.test.mjs

测试不碰真网络(fetch 全被 stub),也不需要真图库:临时目录里现造 sqlite 和假图片。覆盖配置归一、凭证解析、tag 映射、选图排序、目标选择、reindex、send 全链路、错误归一。

设计取舍

  • 零运行时依赖:不出于省事,是因为插件在宿主进程里跑,多一个依赖就多一个装不上的理由。
  • srv_send_msg=true 直接发:先上传再发消息那条路走过四轮,QQ 要求文本消息 content 非空,气泡里必定带一行字或一个零宽空格,最后都会在图上套个大泡泡。
  • 不导出 Config schema:配置写错时降级,比让整条插件不激活要好。
  • 错误只回一句话:模型不需要看 JSON 堆栈。