Skip to content

feat(core): 可选地将合并转发聊天记录 ID 附加到上下文,并追加[聊天记录]标记#714

Merged
dingyi222666 merged 5 commits into
ChatLunaLab:v1-devfrom
CookSleep:feat/attach-forward-msg-id-context
Feb 13, 2026
Merged

feat(core): 可选地将合并转发聊天记录 ID 附加到上下文,并追加[聊天记录]标记#714
dingyi222666 merged 5 commits into
ChatLunaLab:v1-devfrom
CookSleep:feat/attach-forward-msg-id-context

Conversation

@CookSleep
Copy link
Copy Markdown
Member

背景

在使用 Napcat/合并转发聊天记录时,ChatLuna 的输入消息转换链路可能会过滤掉转发记录元素,导致上下文中无法保留“存在聊天记录”这一信息。

改动

  • 新增配置项 attachForwardMsgIdToContext(位于“对话行为选项”末尾,默认关闭)。
  • 开启后:
    • 检测到消息元素中存在合并转发/转发记录元素时,会递归收集可能的消息 ID 字段(idmessage_idmessageIdres_idresIdforward_idforwardId)。
    • 将收集到的 ID 数组写入 inputMessage.additional_kwargs.forwardMessageIds
    • 向输入消息内容中追加一段文本标记 [聊天记录],避免在过滤后消息为空而被中间件直接丢弃。

兼容性

  • 默认关闭,不影响现有行为。
  • 仅在检测到转发记录元素时才追加标记和附加 IDs。

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

在配置接口中新增布尔字段 attachForwardMsgIdToContext(默认 false)。聊天读取中间件改为在已转换消息的 additional_kwargs 中使用内部键保存转发历史(取代外部 WeakMap),新增转发检测与收集钩子与辅助函数;当配置启用且存在转发历史时,将收集到的 ID 写入 additional_kwargs.forwardMessageIds 并附加 “[聊天记录]” 标签,然后移除内部键。

Changes

Cohort / File(s) Summary
配置扩展
packages/core/src/config.ts
Config 接口新增布尔字段 attachForwardMsgIdToContext,并在导出的 Config schema 中加入 attachForwardMsgIdToContext: Schema.boolean().default(false)
消息处理中间件增强(核心逻辑)
packages/core/src/middlewares/chat/read_chat_message.ts
改用 transformedMessage.additional_kwargs[forwardHistoryInternalKey] 存储 per-message 转发状态;新增 ctx.chatluna.messageTransformer.forward.message 拦截钩子以初始化/更新状态并收集转发 ID;在最终转换时(若 config 启用且有 ID)将规范化后的 ID 列表写入 additional_kwargs.forwardMessageIds、附加 “[聊天记录]” 并移除内部键;引入辅助函数用于检测与提取转发元素与 ID。
工具函数新增
packages/core/src/utils/koishi.ts
新增并导出 pickForwardMessageIdisForwardMessageElementnormalizeForwardMessageId 用于从元素中识别转发消息并规范化 ID(支持 message_id / messageId / 数字)。

Sequence Diagram(s)

sequenceDiagram
  participant Client as Incoming Message
  participant Middleware as read_chat_message
  participant Transformer as MessageTransformer
  participant Message as transformedMessage

  Client->>Middleware: 发送含转发元素的原始消息
  Middleware->>Transformer: 注册/触发 forward/message 拦截钩子
  Transformer->>Middleware: 初始化或更新内部 state.ids
  Middleware->>Middleware: 遍历元素,调用 isForwardMessageElement -> pickForwardMessageId -> normalizeForwardMessageId,收集 state.ids
  alt config.attachForwardMsgIdToContext && state.ids 非空
    Middleware->>Message: 确保 additional_kwargs 存在
    Middleware->>Message: 写入 additional_kwargs.forwardMessageIds = state.ids
    Middleware->>Message: 调用 addMessageContent("[聊天记录]")
    Middleware->>Message: 删除内部键 forwardHistoryInternalKey
  end
  Middleware->>Transformer: 将处理后的 transformedMessage 继续下游
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰✨
我把转发的足迹一颗颗串起,
藏进上下文里当成温柔的记忆,
贴上“小小标签”让它不会迷路,
翻过代码的田野,轻轻一跳,藏好这些光。

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 拉取请求标题清楚地总结了主要变更:添加可选的前向消息ID附加到上下文并追加[聊天记录]标记,与代码改动完全相关。
Description check ✅ Passed 拉取请求描述详细说明了背景、改动内容、兼容性考虑,与代码改动完全相关且提供了充分的上下文。
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into v1-dev

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
packages/core/src/utils/koishi.ts (1)

173-184: 注意大整数消息 ID 的精度问题。

部分平台(如 QQ/OneBot)的消息 ID 可能超过 Number.MAX_SAFE_INTEGER,如果上游以 number 类型传入,String(value) 转换后会丢失精度。不过这属于上游 JSON 解析阶段的问题,此函数本身处理逻辑没有问题,仅提醒关注。

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @CookSleep, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在解决 ChatLuna 在处理合并转发聊天记录时可能丢失关键信息的问题。通过引入一个可选的配置项,它允许系统在检测到转发消息时,将其相关的 ID 附加到消息上下文中,并向消息内容追加一个 [聊天记录] 标记。这确保了即使消息内容被转换或过滤,关于转发记录的存在性及其关联 ID 的信息也能被保留,从而提高了系统处理此类消息的鲁棒性和准确性。此功能默认关闭,不影响现有行为。

Highlights

  • 新增配置项: 引入了一个新的配置项 attachForwardMsgIdToContext,默认关闭。此选项控制是否将合并转发消息的 ID 附加到消息上下文中。
  • 转发消息ID收集: 当 attachForwardMsgIdToContext 启用且检测到消息中包含合并转发/转发记录元素时,系统会递归收集消息 ID(包括 id, message_id, messageId, res_id, resId, forward_id, forwardId 等字段)。
  • 上下文增强: 收集到的转发消息 ID 数组将被写入 inputMessage.additional_kwargs.forwardMessageIds,从而在上下文中保留这些关键信息。
  • 消息内容标记: 为了避免在消息内容被过滤后导致消息为空而被中间件丢弃,当检测到转发记录时,会在输入消息内容中追加一个 [聊天记录] 的文本标记。
  • 辅助函数: 新增了 hasForwardMessageElementcollectForwardMessageIdsisForwardMessageElementnormalizeForwardMessageId 等辅助函数,用于检测和处理转发消息元素。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/core/src/config.ts
    • Config 接口中添加了 attachForwardMsgIdToContext: boolean 属性。
    • 在配置 Schema 中添加了 attachForwardMsgIdToContext 字段,类型为布尔值,默认值为 false
  • packages/core/src/locales/en-US.schema.yml
    • attachForwardMsgIdToContext 配置项添加了英文本地化描述。
  • packages/core/src/locales/zh-CN.schema.yml
    • attachForwardMsgIdToContext 配置项添加了中文本地化描述。
  • packages/core/src/middlewares/chat/read_chat_message.ts
    • 引入了根据 config.attachForwardMsgIdToContexthasForwardMessageElement 判断是否存在转发历史的逻辑。
    • 在存在转发历史时,调用 collectForwardMessageIds 收集消息 ID。
    • 修改了 transformedMessage,在存在转发历史时,将其 additional_kwargs.forwardMessageIds 设置为收集到的 ID,并向内容追加 [聊天记录] 标记。
    • 新增了四个辅助函数:hasForwardMessageElementcollectForwardMessageIdsisForwardMessageElementnormalizeForwardMessageId,用于处理转发消息元素的检测和 ID 提取。
Activity
  • 自创建以来,此拉取请求尚未有评论或审查活动。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 引入了一项处理转发聊天记录的新功能,这是一个很有价值的补充。实现结构清晰,通过增加新的配置项和相应的逻辑来检测、处理和标记转发消息。代码总体上是清晰的,并且遵循了 PR 描述中的逻辑。

我的主要反馈与改进新添加的辅助函数中的 TypeScript 类型有关。为消息元素使用 Koishi 的正确类型将增强类型安全性和代码可读性。对此我提供了一些具体的建议。

总的来说,这是一次不错的贡献。

Comment thread packages/core/src/middlewares/chat/read_chat_message.ts Outdated
Comment thread packages/core/src/middlewares/chat/read_chat_message.ts Outdated
Comment thread packages/core/src/middlewares/chat/read_chat_message.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/core/src/middlewares/chat/read_chat_message.ts`:
- Around line 449-467: 当前采集逻辑在 collectForwardMessageIds(使用
normalizeForwardMessageId)里无差别地遍历包括 'id' 的所有键,导致当元素同时存在 forward=true 与自身 id
时可能误取元素自身 ID;为修复,要改为优先使用专用键
['forward_id','forwardId','res_id','resId','message_id','messageId'] 并在找到第一个有效
normalizedId 后停止(或仅在上述专用键全部缺失时才回退到 'id'),或者直接移除 'id' 并在 isForwardMessageElement
中同步将 'id' 纳入判断以保持语义一致;定位到使用 normalizeForwardMessageId 的循环并调整键优先级与短路逻辑(或移除 'id'
并更新 isForwardMessageElement)以避免采集到错误的 ID。
🧹 Nitpick comments (2)
packages/core/src/middlewares/chat/read_chat_message.ts (2)

29-35: 两次递归遍历可合并为一次。

hasForwardMessageElementcollectForwardMessageIds 都递归遍历同一棵元素树。可以只调用 collectForwardMessageIds,根据返回结果是否为空来判断 hasForwardHistory,避免重复遍历。

♻️ 建议合并
-            const hasForwardHistory =
-                config.attachForwardMsgIdToContext &&
-                hasForwardMessageElement(message as h[])
-
-            const forwardMessageIds = hasForwardHistory
-                ? collectForwardMessageIds(message as h[])
-                : []
+            const forwardMessageIds = config.attachForwardMsgIdToContext
+                ? collectForwardMessageIds(message as h[])
+                : []
+
+            const hasForwardHistory = forwardMessageIds.length > 0

注意:此修改会改变语义——如果存在转发元素但未收集到任何 ID,hasForwardHistory 将为 false[聊天记录] 标记也不会被追加。如果希望"只要存在转发元素就追加标记",可以改为使用 collectForwardMessageIds 内部同时返回一个布尔标志。


434-438: children 访问方式可以简化。

koishi 的 h 类型本身就有 children: h[] 属性,无需通过 unknown 中转。当前写法可以正常工作,但不够简洁。

♻️ 简化 children 访问
-        const children = (element as unknown as { children?: unknown }).children
-        return (
-            Array.isArray(children) &&
-            children.length > 0 &&
-            hasForwardMessageElement(children as h[])
-        )
+        return (
+            element.children?.length > 0 &&
+            hasForwardMessageElement(element.children)
+        )

collectForwardMessageIds 中同理:

-        const children = (element as unknown as { children?: unknown }).children
-        if (Array.isArray(children) && children.length > 0) {
-            ;(children as h[]).forEach(visit)
-        }
+        if (element.children?.length > 0) {
+            element.children.forEach(visit)
+        }

Also applies to: 470-473

Comment thread packages/core/src/middlewares/chat/read_chat_message.ts Outdated
Copy link
Copy Markdown
Member

@dingyi222666 dingyi222666 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用 https://chatluna.chat/development/connect-to-core-services/message-read.html 相关 API 实现,稍加修改后,伪装也能支持读取。

ctx.chatluna.messageTransformer.intercept(
    'at',
    async (session, element, message) => {
        const name = element.attrs['name']
        const id = element.attrs['id']

        if (id !== session.bot.selfId) {
            addMessageContent(
                message,
                `<at ${name != null ? `name="${name}"` : ''} id="${id}"/>`
            )
        }
    }
)```

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/core/src/middlewares/chat/read_chat_message.ts`:
- Around line 467-478: pickForwardMessageId currently only looks for
message_id/messageId while isForwardMessageElement treats
forward_id/forwardId/res_id/resId as valid forward markers; update
pickForwardMessageId to check the full set of keys (forward_id, forwardId,
res_id, resId, then fallback to message_id, messageId) in that priority order
and keep using normalizeForwardMessageId for each candidate; also apply the same
key-range change to the other similar extractor referenced around the 480-494
block so forward-detected elements have their IDs collected into state.ids
consistently with isForwardMessageElement.

Comment thread packages/core/src/middlewares/chat/read_chat_message.ts Outdated
CookSleep and others added 3 commits February 8, 2026 22:58
Extract pickForwardMessageId, isForwardMessageElement, and normalizeForwardMessageId functions from read_chat_message.ts to koishi.ts utils module. This allows these utilities to be reused across different middleware and modules.

The functions are now exported from the shared utils module and imported where needed.
@dingyi222666 dingyi222666 merged commit 7a46ce0 into ChatLunaLab:v1-dev Feb 13, 2026
4 of 5 checks passed
@CookSleep CookSleep deleted the feat/attach-forward-msg-id-context branch February 17, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants