npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

koishi-plugin-dcqq-bridge

v0.4.0

Published

Discord 频道与 QQ 群(OneBot)之间的消息转发:完整渲染 embed、换算时间码、@全体、防 ping、失败降级、可选的机器翻译与 EVE 术语表

Readme

koishi-plugin-dcqq-bridge

A Koishi plugin that bridges Discord channels and QQ groups (OneBot v11, e.g. LLBot): full embed rendering, Discord timestamp conversion, real @everyone → QQ @全体 mapping with safety checks, ping-safe relaying to Discord (allowed_mentions), reply mapping, retries with graceful fallback, backfill after gateway reconnects, and optional machine translation (any OpenAI-compatible API) with keyword/moderation filtering and an EVE Online glossary. Everything optional is off by default.


在 Discord 频道 和 QQ 群 之间转发消息(双向或单向)。

功能

  • 一行一个桥:一个 Discord 频道 ↔ 一个 QQ 群,方向可选 双向 / Discord→QQ / QQ→Discord。两个机器人自动识别。
  • Discord → QQ 完整渲染:直接读 Discord 网关的原始数据,不经过适配器的解析(适配器会把 <t:…> 时间码、< > 之间的文字吞掉)。
    • 用户、角色、频道提及显示成名字;自定义表情显示成 [名字];Markdown 标记去掉、文字保留。
    • embed 的作者、标题、描述、所有字段、页脚、时间戳、图片全部转发。
    • 时间码 9 种样式全部换算成设定的时区。
    • 贴纸、转发的消息、投票、组件消息都有去处。
  • QQ → Discord:插件自己调用 Discord API。
    • 默认用 webhook 显示 QQ 发送者的名字和头像。
    • 每个请求都带 allowed_mentions: { parse: [] },QQ 用户手打 <@ID> 也 ping 不到任何人。
    • 图片先下载再上传(QQ 图片地址会过期);文件、表情包、合并转发、小程序卡片都显示成文字,不会静默消失。
  • 回复:两边互相回复转过来的消息时,尽量变成真正的回复;做不到时加一行 ↪ 回复 名字:内容。
  • @全体(默认关闭,按桥打开):只有 Discord 上真正的 @everyone / @here 才会在 QQ 上 @全体。
    • 发送前检查机器人是不是管理员、今天还剩几次;支持冷却时间、每日上限、给人工管理员预留次数。
    • 查询失败时一律不 @全体,改成在消息前加文字。
  • 失败处理:
    • 一张图下载失败只换成 [图片],其余照发。
    • 文件太大被 Discord 拒收时,这一批文件换成占位文字再发一次;用户的原文一个字都不截,占位文字放不下时合成一句「[另有 N 个文件过大未发送]」或另发一条。
    • 连接失败会重试;被 Discord 限流时整个队列一起等。
    • 可能已经发出去的请求不重发,避免重复消息。
    • 超长消息自动分段。
  • 断线补发:插件运行期间 Discord 网关断线、重新建立会话后,补发断线期间漏掉的消息(最多往回 6 小时、每个频道最多 200 条),前缀标 (补发),补发的消息不会 @全体。插件自己停用、重载或 Koishi 关机后再启动时不补发(避免把旧插件已经转过的消息再转一遍)。
  • 屏蔽词:每个桥单独设置,检查正文、embed 和文件名。
  • 自动翻译(默认关闭,按桥打开):英译中、中译英,原文和【机翻】译文在同一条消息里;支持任何 OpenAI 兼容接口,不预设服务商。翻译失败、超时、被过滤时只发原文。
  • 过滤(默认关闭):关键词表,外加可选的 OpenAI 审核接口;命中就不附译文,原文照常转发。
  • EVE 术语表(默认关闭):插件自带从 CCP 官方数据生成的物品、组别、星系、星域中英文名;可以加载自己的黑话表文件,黑话表和官方名称表也可以填网址、定时在线更新。星系写成 Jita(吉他),1DQ1-A 这类代号星系永远不翻。
  • 管理命令:查看状态、暂停(可以只暂停翻译)、恢复、重新读取词表、从旧插件导入配置。

安装

  1. 在 Koishi 控制台的插件市场搜索 dcqq-bridge 并安装。
  2. 需要先装好并启用:数据库插件(例如 database-sqlite)、Discord 适配器(@satorijs/adapter-discord)、OneBot 适配器(koishi-plugin-adapter-onebot)。
  3. Discord 开发者后台里给机器人打开 Message Content Intent,否则收到的消息是空的。
  4. 在要转发的 Discord 频道里,给机器人「查看频道」「发送消息」「附加文件」「管理 Webhook」权限。

配置

所有配置都在 Koishi 控制台的插件配置页里改,保存后立即生效。某一项写错时,只会影响那一项,不会让整个插件停掉:写错的桥会被跳过,并在 bridge.status 里标出来。

基本

| 配置项 | 默认 | 说明 | |---|---|---| | discordSelfId | 空 | 用哪个 Discord 机器人。只有一个时留空 | | qqSelfId | 空 | 用哪个 QQ 机器人。只有一个时留空 | | timezone | Asia/Shanghai | Discord 时间码换算到哪个时区。写错时改用 UTC | | discordAsWebhook | 开 | 发到 Discord 时用 webhook 显示 QQ 发送者的名字和头像。关掉后由机器人发,前面加 [桥名 - 名字]。webhook 用不了时也会自动改由机器人发 | | keepDays | 7 | 回复对应表保留几天 | | authority | 4 | 管理命令需要的 Koishi 权限等级 | | qqReorderMs | 0 | QQ 带回复的消息可能比后面的消息晚一点到(适配器要先查被回复的消息)。填 1000 左右时,每条 QQ 消息先等这么久,按 QQ 的消息序号排好再转发。0 = 不等待 | | maxQueueAgeMinutes | 15 | 消息在队列里等太久(例如 Discord 长时间连不上)就丢掉不发,单位分钟;0 = 不限制 |

桥(表格,一行一个)

| 列 | 默认 | 说明 | |---|---|---| | 桥名 | 空 | 显示在前缀里,可以空 | | Discord 频道 ID | 空 | 纯数字 | | QQ 群号 | 空 | 纯数字 | | 方向 | 双向 | 双向 / Discord → QQ / QQ → Discord | | 启用 | 开 | | | @全体 | 关 | 只对 Discord → QQ 方向有效 | | 屏蔽词 | 空 | 每一条是一个正则表达式(普通的词直接写),不区分大小写,多条用 ;; 分隔。命中就不转发这条消息 | | 翻译 | 关 | 还要打开下面「翻译」里的总开关才生效 |

  • 一个 Discord 频道要发往两个 QQ 群,就写两行。
  • 同一对频道和群写了两行、方向正好相反时,两行都照常工作,bridge.status 会建议合成一行「双向」。
  • 直接编辑 koishi.yml 时,ID 要加引号(discord: '123…'),否则会被当成数字并丢掉精度。
  • 某一行的方向、启用、@全体、翻译写成了别的值(例如 D2Q、no)时,这一行整行无效,其他行照常工作。无效的行(包括 ID 写错、重复的行)会在 bridge.status 的最上面提示「第 N 行桥……已跳过(到控制台表格里检查第 N 行)」,在群里发、私聊不加 -a 时也能看到。

@全体

| 配置项 | 默认 | 说明 | |---|---|---| | fallbackText | 【全体通知】 | 没能 @全体 时加在消息最前面的文字 | | reserve | 0 | 给人工管理员留几次:群里当天剩余次数不超过这个数时,插件不再 @全体 | | dailyCap | 0 | 每个 QQ 群每天插件最多用几次,0 = 不另外限制 | | cooldownMinutes | 0 | 同一个 QQ 群两次 @全体 之间至少隔几分钟 | | maxAgeMinutes | 10 | Discord 消息发出超过这么多分钟就不再 @全体 |

@全体 只在下面这些条件全部满足时才会发生:

  • 这个桥打开了 @全体,方向包含 Discord → QQ;
  • Discord 上是真正 ping 了所有人的 @everyone 或 @here(只写了文字、没有权限 ping 的不算;「静默」发送的也不算;角色 ping 永远只显示成文字);
  • 不是补发的旧消息;
  • 没有在冷却中,也没到每日上限;
  • 机器人在这个 QQ 群里是群主或管理员;
  • QQ 显示还有剩余次数,并且剩余次数大于 reserve。

上面这些查询最多等 15 秒,并且最晚到这条消息 60 秒发送期限之前 10 秒;查不完就按「查询失败」改发带 fallbackText 的文字,保证文字版能发出去。

翻译

| 配置项 | 默认 | 说明 | |---|---|---| | enabled | 关 | 总开关 | | baseURL | 空 | OpenAI 兼容接口的地址(见下表) | | apiKey | 空 | API key | | model | 空 | 模型名。推荐不带推理(思考)的模型,例如 gpt-4.1-mini、gpt-4o-mini | | label | 【机翻】 | 译文前的标注。不能为空,为空时自动用「【机翻】」 | | timeoutMs | 6000 | 翻译请求最多等多少毫秒,超过就只发原文 | | maxPerHour | 0 | 每小时最多请求几次,0 = 不限 | | extraBody | 空 | 可选:额外合并进请求体的字段,写成 JSON 对象(见下) | | notCommands | help | 这些词开头的消息不当作命令,照常翻译;多个用 ;; 分隔,不区分大小写 | | keepValueLabels | 空 | 这些标签后面的内容原样保留、不翻译;多个用 ;; 分隔,不区分大小写(见下) | | context | 空 | 可选:给模型的背景说明,多行文字,原样加在系统提示词的最后(见下) | | protectMemberNames | 开 | 中译英时,QQ 群成员名片里的名字原样保留、不翻译(见下) |

总开关打开但没填 baseURL 或 model 时,翻译按关闭处理,bridge.status 会显示原因。

服务商示例(只是例子,插件不预设任何一家;地址和模型名以各家官方文档为准):

| 服务商 | baseURL | model 例子 | |---|---|---| | OpenAI | https://api.openai.com/v1 | gpt-4.1-mini、gpt-4o-mini | | DeepSeek | https://api.deepseek.com/v1 | deepseek-chat | | 通义千问 | https://dashscope.aliyuncs.com/compatible-mode/v1 | qwen-plus |

模型选择:聊天转发要的是快,推荐不带推理(思考)的模型,例如 gpt-4.1-mini、gpt-4o-mini。o 系列、R1 这类推理模型要先「想」一阵才出译文,常常超过 timeoutMs,结果只发原文。

extraBody:服务商需要额外参数时用,写成一个 JSON 对象,原样合并进请求体,例如 {"max_tokens": 1000}。model 和 messages 由插件决定,写了也会被忽略。JSON 写错或不是对象(例如数组)时整项忽略,并在 bridge.status 里提示。

keepValueLabels:人名、舰队名、语音频道名被翻译或音译后,成员就没法在游戏里搜到了。某一行以「标签 + 冒号(: 或 :)」开头、并且标签在这个列表里时,冒号后面到行尾的内容不发给模型,译文里原样放回。标签前后可以有空格,也可以是 Discord 粗体(**FC Name:** 某人、**FC Name**: 某人 都算),行首可以有列表符号 -;标签不在行首的不算。这条规则只管像 ping 的消息:同一条消息里至少有 2 行以这些标签开头,或者冒号后面的内容很短(不超过 5 个词、40 个字符;英文按空格分词,一个汉字算一个词)。所以 FC Name: 某人 单独一行也保护,FC: everyone align to the sun and wait for warp 这种普通聊天照常翻译。Discord embed 的字段名在列表里时,不管长短,整个字段值都不翻译。AA 舰队 ping 的参考写法:

FC;;FC Name;;Fleet Commander;;Fleet Name;;Comms;;Formup Location

context:告诉模型这是什么场景的聊天、常见说法是什么意思。不为空时原样加在系统提示词的最后,英译中、中译英都加。一个字的黑话(例如「怪」「洞」)进不了术语表,靠这里说明。改了以后翻译缓存自动失效,同一句话会重新翻译。插件本身不内置任何游戏的内容,EVE 的参考写法(复制进配置,按需要增删):

这是 EVE Online 玩家之间的聊天和舰队通知。常见说法:怪 = NPC 海盗(rats),刷怪 = ratting;洞 = 虫洞;扫 = 扫描;蛋 = 太空舱(pod);军团 = corp;旗舰 = capital;远征 = escalation;交易语境里「收」= 买(WTB)、「出」= 卖(WTS);刷怪语境里的「6/10」「10/10」是异常空间的等级;「in 5」「bridge in 5」这类说法里的数字一般指分钟。

背景说明会随每次翻译请求一起发给服务商,算进用量;写得越长,每次请求越贵。

建筑通知里的名字:Alliance Auth 的 Structures 插件发到 Discord 的建筑通知里,建筑名、军团名、联盟名翻译以后成员就没法在游戏里搜到了。翻译时(只影响发给模型的文字,转发的原文不变):

  • 文字里有 https://evemaps.dotlan.net/corp/<名字> 或 …/alliance/<名字> 这样的链接时,把链接里的名字还原(_ 换成空格,URL 解码),文字里原样出现的这个名字和网址一样换成占位符,例如 belonging to Example Corp https://evemaps.dotlan.net/corp/Example_Corp 里的 Example Corp;
  • The <建筑类型> <建筑名> in <星系> 这种句式里,建筑类型是官方名称表里的建筑(Astrahus、Fortizar、Keepstar、Ansiblex Jump Bridge 等)时,中间的建筑名换成占位符,例如 The Astrahus Example Keep in Jita 里的 Example Keep。The Astrahus in Jita is reinforced 没有名字,照常翻译。建筑类型从插件自带的官方名称表取,不打开「EVE 官方名称表」也有效。

protectMemberNames:QQ 里直接写群友的名字(不是 @)时,名字容易被翻译或音译。打开后(默认开),中译英时从这个 QQ 群的成员名片(没有名片时用 QQ 昵称)里取名字,在原文里出现的原样保留。名片按常见写法拆开:去掉 []、【】 里的简称,按 -、_、|、/、空格分段,每段单独算一个名字;只收 3 个字及以上、含汉字的段(2 个字的太容易和普通词重合,例如「咖啡」),和术语表原文相同的段也不收。例如名片 [ABC]Some Pilot-小鱼干,「把小鱼干搞大」里的「小鱼干」不发给模型。成员名片每个群缓存 1 小时,在后台更新,不会让转发等待;插件刚启动后的头几条消息可能还没有名片(照常翻译),查询失败时也照常翻译。

另外,提示词里要求模型不翻译、不音译玩家和角色名、军团和联盟名及其简称、舰队制式名、语音频道名;这只是要求,模型不一定每次都照做,固定格式的 ping 请用上面的标签。

同时进行的翻译请求最多 4 个,其余排队;排队的时间也算在 timeoutMs 里,等不到就按超时处理(不发请求),只发原文。

必须用服务商的 API key。Claude、ChatGPT 这类聊天订阅不能拿来当机器人的后端。

哪些不翻译:

  • 太短的(例如 gg、@张三 ok);
  • 已经是目标语言的;
  • Koishi 命令(例如查价命令):第一个词(去掉命令前缀后)是本机的命令、不在 notCommands 里,并且整条消息按空格分不超过 3 段。所以 jita plex 不翻译,Help needed in Jita, we are tackled 照常翻译。只用来分组、自己没有功能的命令(例如 bridge.status 的上一级 bridge)不算,所以 bridge is up 照常翻译;
  • 命中关键词的;
  • 去掉网址、提及、术语等之后没有文字的(只有术语时,能直接换成目标语言的术语就本地生成译文,不请求服务商);
  • 译文和原文一样的(不区分大小写和空白)不附;
  • 断线后补发的消息。

不会发给翻译服务商的内容:用户名、QQ 号、群号、频道名、文件名。提及、@everyone、@here、网址、时间、代号星系、ISK 数字、keepValueLabels 标签后面的内容、建筑通知里的建筑名和军团名、群友名片里的名字会先换成占位符,翻译后再换回来。

embed 和术语的排版:embed 的字段在翻译时和转发的原文一样,一个字段一行「字段名:值」。英译中时术语表换进去的中文,挨着汉字或中文标点的一侧不留空格(例如「当心,墩子在我们的本星系」);两个中文术语之间只隔着空格、并且挨着空格的两个字都是汉字时,空格也去掉(「长须鲸级被抓」),有一边不是汉字时保留(「FRT 舰队」)。中译英时换进去的英文紧挨着英文字母或数字时补一个空格(「AAA燃料块」→ AAA Fuel Block),后面是复数词尾 s、es 时不补(Fuel Blocks)。

译文里的网址:网址换回来以后,后面紧跟的不是空格或行尾(英文标点除外)时补一个空格,前面紧挨着汉字或全角标点时也补一个空格,免得 QQ 把旁边的汉字、「)」也算进链接。译文每行末尾的空白会去掉。

图片的位置:发到 QQ 的消息一条放得下时,顺序是「原文 → 图片 → 译文」;太长要分几条发时,是「原文 → 译文」,图片跟在最后一条。Discord 消息里只有空白的行(例如 AA ping 里用来空一行的 ** **)转发后是空行。

零宽字符:有的 ping 工具会在消息里插入大量看不见的零宽字符(U+200B、U+200C、U+200D、U+2060、U+FEFF),Discord → QQ 转发和翻译之前都会删掉,免得术语匹配不上。组合 emoji(例如 👨‍👩‍👧)里夹在两个 emoji 之间的 U+200D 保留。

过滤

| 配置项 | 默认 | 说明 | |---|---|---| | keywords | 空 | 关键词,一行一个,不区分大小写。re: 开头的按正则 | | keywordFile | 空 | 可选的关键词文件(相对 Koishi 实例目录),格式同上,# 开头是注释 | | moderation | 关 | 用 OpenAI 审核接口检查译文 | | moderationBaseURL | https://api.openai.com/v1 | 审核接口地址 | | moderationApiKey | 空 | 审核接口的 key。留空时,只有翻译接口地址和审核接口地址完全相同(协议、主机、端口、路径,末尾的 / 不算)才借用翻译的 key;插件绝不会把别家的 key 发给 OpenAI |

  • 原文命中关键词:不翻译;译文命中关键词:不附译文。原文照常转发。
  • 英文关键词建议写成 re:\bword\b,否则 ass 会命中 class。
  • 审核接口管暴力、仇恨、色情这类内容,不懂中国的政治敏感词,那部分只能靠关键词表。
  • 审核出错、超时或没有可用的 key 时,译文一律不附(宁可不翻,不发出没经过检查的译文)。
  • 仓库里不附任何关键词表,由使用者自己维护。

术语表

| 配置项 | 默认 | 说明 | |---|---|---| | eve | 关 | 使用插件自带的 EVE 官方名称表(data/eve-glossary.json) | | systemStyle | en(zh) | 有名字的星系、星域、星座在英译中时怎么写:Jita(吉他) / Jita / 吉他 | | slangFile | 空 | 黑话表文件(YAML,相对 Koishi 实例目录);多个文件用 ;; 分隔,见下面「多个黑话表文件」 | | officialUrl | 空 | 在线的官方名称表网址(JSON),\|\| 分隔备用网址;空 = 用插件自带的表。见下面「在线黑话表和官方名称表」 | | refreshHours | 6 | 在线词表每隔几小时检查一次更新,最长 168(一周),填更大的按 168 算;0 = 只在插件启动和 bridge.reload 时检查 | | overrides | 空 | 自己加的词条(表格),优先级最高(和群里 纠错 命令加的词条同级,同一个原文以这里为准) |

黑话表格式见插件自带的 data/eve-slang.example.yaml。每一条:

  • en、zh:标准写法;
  • mode:keep 原样保留 / force 一定换成对应的词 / hint 只作为参考交给模型(参考词条是否被采用由模型决定;想要固定的译法,请用 force);
  • dir:both / en2zh / zh2en;
  • en_aliases、zh_aliases:其他写法,匹配到时输出标准写法;
  • category、note、confidence:只给人看。

黑话表写错时只在日志和 bridge.status 里提示,照常转发。改了文件后发送 bridge.reload 重新读取。

多个黑话表文件:slangFile 里可以写多个路径,用 ;; 分隔,例如公开的黑话表加上自己联盟专用的几条:

data/eve-cn-slang/glossary.yaml;;data/dcqq-bridge/local-slang.yaml
  • 按顺序加载,后面文件里同一个原文的词条覆盖前面的。「同一个原文」按方向看:英译中(en2zh)比 en(不区分大小写),中译英(zh2en)比 zh;两条都是 both 时,en 或 zh 相同都算。方向不同的两条只在共有的方向上比较,例如后面文件里的 en2zh 词条只覆盖前面的 en2zh 和 both 词条;前面的 both 词条只被覆盖了一个方向时,保留另一个方向(被 en2zh 覆盖后只剩中译英)。所以只改 logistics → 后勤 时,同样译成「后勤」的 logi 不受影响。同一个文件里的重复还是按原来的规则处理。
  • 每个文件单独读:一个文件读不到或 YAML 写错,只在 bridge.status 和日志里提示这个文件,其他文件照常加载。
  • bridge.reload 的回复里按文件列出条数,例如「黑话表:glossary.yaml 301 条,local-slang.yaml 2 条」。
  • 只写一个路径时和以前完全一样。
  • 可以直接用开源的中文黑话表:github.com/yilifaer/eve-cn-slang(glossary.yaml),克隆或下载到 Koishi 实例目录下,把路径写进 slangFile;也可以直接写网址,见下一节。

在线黑话表和官方名称表

黑话表和官方名称表可以放在 GitHub 等网站上,插件定时下载。改了数据仓库里的文件,各个机器人过几小时自己就用上新的,不用发新版插件、也不用每台机器手动更新。默认不开(网址都是空的)。

设置步骤(以 eve-cn-slang 为例,换成自己的仓库时把网址里的用户名、仓库名、分支名改掉):

  1. 打开插件配置的「术语表」,打开 eve(EVE 官方名称表)。

  2. slangFile 填黑话表的网址;|| 后面是备用网址(GitHub 打不开时用 jsDelivr 的镜像)。还想加自己联盟专用的几条,就用 ;; 再接一个本地文件,写在后面的覆盖前面的:

    https://raw.githubusercontent.com/yilifaer/eve-cn-slang/main/glossary.yaml || https://cdn.jsdelivr.net/gh/yilifaer/eve-cn-slang@main/glossary.yaml ;; data/dcqq-bridge/local-slang.yaml
  3. officialUrl 填官方名称表的网址,同样可以加备用网址:

    https://raw.githubusercontent.com/yilifaer/eve-cn-slang/main/official/eve-official.json || https://cdn.jsdelivr.net/gh/yilifaer/eve-cn-slang@main/official/eve-official.json
  4. refreshHours 默认 6 小时检查一次,一般不用改(最长 168 小时,也就是一周)。保存配置。

  5. 私聊机器人发 bridge.reload,回复里应该有「glossary.yaml(在线)N 条,更新于 …」和「官方名称表:eve-official.json(在线)build N,N 条,更新于 …」。

规则:

  • slangFile 里网址和本地文件可以混着写,按顺序合并,覆盖规则和多个本地文件完全一样(见上一节)。一项里用 || 分隔的网址按顺序试,第一个下载成功的为准。
  • 下载用 Koishi 的 HTTP 服务,每次最多等 30 秒;黑话表最大 5 MB,官方名称表最大 20 MB。服务器回复错误(404、500 等)时不读错误页的内容,直接算这个网址下载失败。支持 ETag / Last-Modified,文件没变时服务器只回一句「没变」,不重新下载整份。
  • 内容没变就什么都不做;变了才重新加载术语表,翻译缓存随之失效(同一句话按新词条重新翻译)。
  • 下载到的内容会先检查:黑话表必须是 YAML 列表,并且至少有一条能用的词条(拿到错误页、空文件、只有注释的文件、空列表就不用;本地黑话表文件不受这一条影响);官方名称表必须有 buildNumber 和 entries,每一条都有 kind、en、zh,写了 count 时要和条数一样,条数至少是插件自带表的一半。检查不通过就继续用原来的版本,并在 bridge.status 里写原因。
  • eve-cn-slang 的官方名称表已经自己写了 cat 字段(区分舰船、建筑),插件直接用文件里的,所以「末日沙场」这种不带「级」的船名照样认得。只有整份表一条 cat 都没有时(例如自己做的、没写 cat 的表),插件才按英文名从自带的表补上。
  • 在线官方名称表的 build 比插件自带的旧时(例如断网很久、只剩旧缓存,插件又升级了),用插件自带的。
  • bridge.status 显示正在用的官方名称表(在线还是插件自带、build 号)和每个在线黑话表最近一次下载成功的时间;下载失败时写原因和时间。

断网或网站打不开时:

  • 每次下载成功都存一份在 Koishi 实例目录的 data/dcqq-bridge/cache/ 下。插件启动时先用这份缓存,再在后台下载,启动和转发都不等网络。
  • 下载失败就继续用手上的版本:这次运行里下载过的 → 缓存 → 都没有时官方名称表用插件自带的、在线黑话表当作读不到(其他黑话表文件照常)。连续失败只在日志里写一次,恢复时再写一次。
  • bridge.reload 会马上把所有在线词表下载一次,并逐个报告,例如「glossary.yaml(在线)读不到(raw.githubusercontent.com 超时;cdn.jsdelivr.net 超时),用的是 10/05 08:00 (UTC+8) 的缓存,301 条」。

缓存只留一份:每个网址(按 || 前面的第一个网址算)只留一份内容和一个记录文件(ETag 等),新版本下载好后先写临时文件再替换旧的,不会越存越多。内容写进去了才更新记录;记录里存着内容的校验值,对不上(例如上次写到一半)时重新下载整份,不会把旧内容当成最新的。插件启动和 bridge.reload 时删掉已经不在配置里的网址的缓存、以及上次没写完留下的临时文件。改了第一个网址或者调换了 || 前后的顺序时,只要新旧网址里有一个相同,旧的缓存改成新的文件名接着用(断网时把镜像调到前面也不会丢),不算「不在配置里」。只删插件自己按固定格式命名的文件,缓存目录里别的文件不动。

版权:eve-cn-slang 的 official/ 目录(官方名称表)和插件自带的 data/eve-glossary.json 一样来自 CCP 的游戏数据,按 EVE 开发者许可协议使用(仅限非商业、非营利用途),不适用那个仓库的 CC BY 4.0;声明见该仓库的 official/NOTICE.md 和本插件的 data/NOTICE。

匹配规则:

  • 最长的优先;
  • 英文要求前后是词的边界,允许末尾多一个 s;
  • 只有 3 个字母以内的英文词(例如 FC、o7、x up)要求大小写完全一致;
  • 官方名称里是常用英语单词的(例如星域 Catch、Domain),只作参考,并且只在首字母大写、不在句首时匹配;
  • 两个字以内的官方中文名(例如「吉他」)在中译英时只作参考;2 个字的组别、大类名(例如「建筑」「其他」「工具」)多是普通词,中译英时不用;
  • 舰船名在中译英时也认不带「级」的写法(「末日沙场」= 末日沙场级 = Armageddon),只加去掉「级」后 3 个字及以上的;2 个字的(「灾难」「挑战」)多是普通词,不自动加,需要的话写进黑话表;和别的官方名称或黑话表词条重复时不加,以它们为准。

在群里直接纠错:看到翻错了,在桥接的 QQ 群或 Discord 频道里发 纠错 命令(需要 authority 级权限),马上生效,不用打开控制台,也不会让插件重载。

| 命令 | 作用 | |---|---| | 纠错 原文 = 译法 | 添加或修改一条词条,默认强制替换(force)。例如 纠错 standing fleet = 值守舰队,回复「已添加:standing fleet → 值守舰队(强制替换,英译中)」 | | 纠错 -h 原文 = 译法 | 同上,但只作参考(hint),由模型决定用不用 | | 纠错 -k 原文 | 原样保留,不翻译(keep) | | 纠错 列表、纠错 列表 关键词 | 列出用命令加的词条(带关键词时只列包含它的);太长时分几条发 | | 纠错 删除 原文 | 删除一条(两个方向都有时一起删) | | 纠错 导出 | 把全部词条写成黑话表格式的 YAML,存到 Koishi 实例目录的 data/dcqq-bridge/fixes-<时间>.yaml,回复文件路径,方便以后并进黑话表 |

  • 原文、译法里可以有空格;等号写全角 = 也行。英文名是 bridge.fix。
  • 原文、译法被一对 <…>、〈…〉、《…》 整个包住时,括号会被去掉:纠错 <afk cloaker> = <墩子> 加的是「afk cloaker → 墩子」,纠错 删除 <afk cloaker> 也能删掉它(沙盒这类会把 <…> 当成标签的平台也一样)。
  • 方向自动判断:原文有英文字母、译法有汉字 → 英译中;原文有汉字、译法有英文字母 → 中译英;判断不出来(例如两边都是英文)就回复用法,不添加。-k 只看原文:有汉字 → 中译英(中英混写的词也算中文),只有英文字母 → 英译中。
  • 同一个方向、同一个原文再加一次就覆盖旧的(3 个字母以上不分大小写)。
  • 加的时候和黑话表走同样的检查:原文、译法至少 2 个字;3 个字母以内的英文只匹配大小写完全一致的写法;原文是常用英语单词、又是强制替换时,回复里会提醒「可能误伤普通句子」,但照样加上,不想要就改用 -h 再加一次;用 -k 原样保留常用英语单词(例如 纠错 -k fleet)时也会提醒「普通句子里的 fleet 也不会被翻译」,同样照样加上。
  • 优先级和控制台的 overrides 相同,高于黑话表和官方名称表。控制台 overrides 里有同一个原文时以控制台为准,纠错 列表 里会标出来。
  • 词条存在数据库里(表 dcqqbridge_glossary),不写进 koishi.yml。每次增删后翻译缓存都会失效,同一句话下次会按新词条重新翻译。
  • 回复一条转发过来的消息再发纠错:加完词条后,插件从那条消息里取回原文(去掉开头的 [桥名 - 名字] 和后面的译文),用新词条重新翻译一次,把新译文回复在你发命令的群或频道里(不转发到对面),方便马上确认效果。重新翻译失败不影响词条本身。
  • 纠错命令和机器人的回复都不会转发到对面。

管理命令

所有命令都需要达到 authority 设置的权限等级。任何达到这个等级的人都能暂停、恢复、导入;如果别的插件也需要给人这个等级,考虑把本插件的 authority 调高。

| 命令 | 作用 | |---|---| | bridge.status [桥](桥接状态) | 每个桥的状态:启用、暂停或无效,最近一次转发时间,24 小时转发数和失败数,最近一次失败的原因。@全体 桥还显示今天用了几次、剩余次数(每次执行命令时实时向 QQ 查询,最多等 8 秒;查不到时写明原因,例如「查询失败:超时」「QQ 机器人不在线」)、改发文字的次数和原因。在群里只显示和这个群有关的桥;私聊时加 -a 连无效、未启用的行也显示 | | bridge.pause [桥](桥接暂停) | 暂停。不带桥 = 全局暂停,所有转发立即停止。重启后仍然有效。加 -t 只暂停翻译,转发照常 | | bridge.resume [桥](桥接恢复) | 恢复;加 -t 只恢复翻译 | | bridge.reload | 重新读取关键词文件和黑话表;在线黑话表、官方名称表马上下载一次 | | bridge.import | 从 @myrtus/forward 的配置生成桥(只能私聊使用,只输出、不改任何配置) | | bridge.fix(纠错) | 在群里加、改、删术语词条,马上生效,见上面「术语表」一节 |

「桥」可以写成:bridge.status 里的编号、Discord频道ID:QQ群号、或桥名。

在桥接的群或频道里发的本插件命令(包括中文别名、带不带命令前缀、bridge status 这种空格写法)不会被转发到对面,机器人的回复也不转发;别的插件的命令照常转发。只有 bridge 一个词(例如「bridge is down」)不算命令,照常转发。

统计数字只保存在内存里,插件重载后清零(在控制台保存一次配置也算重载);bridge.status 第一行会写「统计从 … 重载后开始」。暂停状态保存在数据库里。

bridge.status 里的时间按 timezone 显示,后面标上时区,例如 09/30 12:38 (UTC+8);电脑本机的时区和它不同时不会看错。

给账号设权限(例如在 Discord 上也能用命令)

权限等级是 Koishi 自己管的,本插件不做特殊处理。QQ 号和 Discord 账号在 Koishi 里是两个不同的用户,QQ 号有 4 级权限,不代表 Discord 账号也有;新账号默认是 1 级。要在 Discord 频道里用 纠错、bridge.status 这些命令,需要给 Discord 账号单独设成 4 级(或者 authority 设置的等级)。

先让这个 Discord 账号在机器人能看到的频道里发一句话,Koishi 才会给它建用户记录。然后任选一种方法:

方法一:在控制台的「数据库」页面改(需要启用 dataview 插件,控制台左边栏叫「数据库」)

  1. 打开 binding 表,找到 platform 是 discord、pid 是你的 Discord 用户 ID 的那一行,记下 aid 那一格的数字。(Discord 用户 ID:Discord 设置 → 高级 → 打开「开发者模式」,然后右键自己的头像 →「复制用户 ID」。)
  2. 打开 user 表,找到 id 等于刚才那个数字的一行,把 authority 改成 4,保存。

方法二:用 authorize 命令(admin 插件提供)

  • 由一个 5 级的账号发送:authorize 4 -u @discord:你的Discord用户ID,例如 authorize 4 -u @discord:123456789012345678(这里的数字是编造的)。
  • Koishi 规定只能给别人设比自己低的等级,所以 4 级的账号不能用这个命令把别人设成 4 级;手头没有 5 级账号时用方法一。

改完后在 Discord 频道里发 bridge.status 试一下:有状态回复就说明设好了;权限还不够时 Koishi 会回复「权限不足。」(或者什么都不回)。

从 @myrtus/forward 迁移

  1. 私聊机器人发送 bridge.import,核对它给出的报告:生成了几个桥、跳过了哪些、为什么跳过,以及行为变化(例如屏蔽词现在不区分大小写、也检查 embed)。
  2. 把回复里的 bridges: 配置粘进本插件的配置。这份配置也保存在 Koishi 实例目录的 data/dcqq-bridge/import-<时间>.yaml。
  3. 在同一次保存里停用 @myrtus/forward、启用本插件。两个插件绝不能同时转发同一个频道,否则每条消息会发两次。
  4. 出问题时停用本插件、重新启用旧插件即可。旧插件的配置和数据表本插件都没动过。

常见问题

  • QQ 机器人离线,转发不出去:OneBot 适配器用「正向 WebSocket」连接时,LLBot 重启或断开后不会自动重连。bridge.status 会显示「QQ 机器人离线」。解决办法:在 Koishi 控制台重载 onebot 适配器;或者把连接方式改成「反向 WebSocket」(由 LLBot 负责重连)。
  • Discord → QQ 收不到:确认 Discord 开发者后台打开了 Message Content Intent,并且机器人在频道里有「查看频道」权限。
  • QQ → Discord 显示的是机器人自己的名字:机器人没有「管理 Webhook」权限,插件已自动改由机器人发送。日志里每小时会提醒一次。
  • QQ 带回复的消息顺序偶尔颠倒:见 qqReorderMs。

隐私与合规提示

以下不是法律意见。

  • 为稳妥起见,译文都带「机器翻译」标注:《人工智能生成合成内容标识办法》2025-09-01 起施行,是否适用于群聊翻译存在争议,所以 label 不能为空。
  • DeepSeek 开放平台协议 §3.3 把 API 开发者视为服务提供者,§3.4 要求对输入和输出做关键词过滤,关键词表就是用来满足这一条的。打开翻译但关键词表为空时,插件会在启动时提醒。
  • 个人信息:把 QQ 群消息发到境外(Discord;打开翻译后还有翻译服务商,开启审核时还有 OpenAI)可能涉及个人信息出境,需要告知群成员并取得单独同意。建议在群公告里列出所有接收方。
  • 使用 OpenAI 时,注意它的「支持的国家和地区」条款。
  • webhook 模式会把 QQ 头像地址显示在 Discord 上,这个地址里带有 QQ 号。
  • QQ 的用户协议禁止第三方客户端和自动发送消息,机器人账号本身就有被限制或封禁的风险。消息越多、@全体 越频繁,风险越高。

开发

npm ci
npm run typecheck
npm test
npm run build

测试用真实的 Koishi、Discord 适配器、OneBot 适配器,加上模拟的 Discord 服务器和模拟的 LLBot,不访问外网。实测步骤见 docs/测试步骤.md,决策记录见 DECISIONS.md。

数据来源与许可

代码使用 MIT 许可。以下两个数据文件不属于 MIT,详见 data/NOTICE:

  • data/eve-glossary.json:从 CCP 的 EVE Online 静态数据生成,按 EVE 开发者许可协议 使用(仅限非商业、非营利用途)。用 npm run build:glossary 可以从官方最新数据重新生成。

    © 2014 CCP hf. All rights reserved. "EVE", "EVE Online", "CCP", and all related logos and images are trademarks or registered trademarks of CCP hf.

  • 在线官方名称表(officialUrl,例如 eve-cn-slang 的 official/eve-official.json)同样是 CCP 游戏数据的衍生物,许可和版权声明同上;下载后的缓存也一样。

  • data/common-words.txt:常用英语单词表,取自 SCOWL(Spell Checker Oriented Word Lists,经 npm 包 wordlist-english)的第 10、20、35 级,按 SCOWL 的许可使用,版权声明原文见 data/NOTICE。

致谢

  • Koishi 和 Satori 适配器。
  • 特别感谢 @myrtus/koishi-plugin-forward 的作者。我们的 Discord 频道和 QQ 群一直靠它互通,它是这个插件的起点。后来因为我们的游戏(EVE Online)有一些特殊需要,例如舰队通知里的 @全体、embed 排版、中英机翻和游戏术语,才另外写了这个插件。开发时阅读过它的代码来理解原来的行为,但没有复制它的任何代码(它是 AGPL-3.0 许可)。向原作者致敬!

许可证

MIT