Skip to content

Conversation

@clansty
Copy link
Contributor

@clansty clansty commented Jan 6, 2026

Summary by Sourcery

为私聊、群聊以及 go-cqhttp OneBot 发送流程中的合并转发消息提供可自定义的展示元数据支持。

新功能:

  • 允许调用方通过 API 负载和 OneBot go-cqhttp 动作,为发送的合并转发消息指定标题/来源、预览/资讯、摘要以及提示等元数据。

增强改进:

  • 在转换编码器中传递转发消息的展示元数据,使生成的多消息负载和卡片提示能够使用调用方提供的值,并在缺省情况下采用合理的回退值,而不是固定的「[聊天记录]」文本。
Original summary in English

Summary by Sourcery

Support customizable display metadata for merged forward messages across private, group, and go-cqhttp OneBot send flows.

New Features:

  • Allow callers to specify title/source, preview/news, summary, and prompt metadata for outgoing merged forward messages via API payloads and OneBot go-cqhttp actions.

Enhancements:

  • Propagate forward message display metadata through transform encoders so that generated multi-message payloads and card prompts use caller-provided values with sensible fallbacks instead of fixed '[聊天记录]' text.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 6, 2026

Reviewer's Guide

为合并/转发消息添加可自定义展示元数据(标题/来源、预览/新闻、摘要、提示文案),覆盖 Milky APIs 和 OneBot11 go-cqhttp actions,在整个 transform/encoding 管线中传递这些选项,并在最终消息元素中使用计算得到的提示文案(prompt),而不是固定占位符。

可自定义转发消息元数据流程序列图

sequenceDiagram
  actor BotDeveloper
  participant OB11Client as SendForwardMsg
  participant Encoder as MessageEncoder
  participant PMHQ as PMHQService
  participant App as AppService

  BotDeveloper->>OB11Client: SendForwardMsg(payload with source, news, summary, prompt)
  OB11Client->>Encoder: generate(nodes, options)
  Note over Encoder: options.source, options.news,
  Note over Encoder: options.summary, options.prompt
  Encoder-->>OB11Client: raw { multiMsgItems, tsum, source, summary, news, prompt }
  OB11Client->>PMHQ: uploadForward(peer, raw.multiMsgItems)
  PMHQ-->>OB11Client: resid
  OB11Client->>OB11Client: prompt = options.prompt or "[聊天记录]"
  OB11Client->>App: sendMessage(ctx, peer, forward_element with desc=prompt, prompt=prompt)
  App-->>BotDeveloper: Response with message id
Loading

Milky API 转发消息元数据传递时序图

sequenceDiagram
  participant API as SendPrivateMessage
  participant Transform as transformOutgoingForwardMessages
  participant FEncoder as ForwardMessageEncoder
  participant PMHQ as PMHQService
  participant App as AppService

  API->>Transform: transformOutgoingForwardMessages(ctx, messages, peer, options)
  Transform->>FEncoder: generate(messages, options)
  FEncoder-->>Transform: raw { multiMsgItems, tsum, source, summary, news, prompt }
  Transform-->>API: raw
  API->>PMHQ: uploadForward(peer, raw.multiMsgItems)
  PMHQ-->>API: resid
  API->>API: prompt = raw.prompt
  API->>App: sendMessage(ctx, peer, forward_element with desc=prompt, prompt=prompt)
  App-->>API: RawMessage result
Loading

更新后的转发消息编码器与 action 类图

classDiagram
  class SendForwardMsg {
    +execute(payload: Payload) Promise~Response~
    -handleFakeForwardNode(peer: Peer, nodes: OB11MessageNode[], options: ForwardOptions) Promise~Response~
    -handleForwardNode(peer: Peer, nodes: OB11MessageNode[], msgInfos: MsgInfo[]) Promise~Response~
  }

  class Payload {
    +messages: OB11MessageNode[]
    +message: OB11MessageNode[]
    +message_type: string
    +source: string
    +news: NewsItem[]
    +summary: string
    +prompt: string
  }

  class ForwardOptions {
    +source: string
    +news: NewsItem[]
    +summary: string
    +prompt: string
  }

  class NewsItem {
    +text: string
  }

  class MessageEncoder {
    -ctx: Context
    -peer: Peer
    -tsum: number
    -isGroup: boolean
    -news: NewsItem[]
    +render(content: OB11MessageData[]) Promise~void~
    +generate(content: OB11MessageData[], options: EncoderOptions) Promise~EncodedForward~
  }

  class EncoderOptions {
    +source: string
    +news: NewsItem[]
    +summary: string
    +prompt: string
  }

  class ForwardMessageEncoder {
    -ctx: Context
    -peer: Peer
    -tsum: number
    -isGroup: boolean
    -news: NewsItem[]
    +render(content: OutgoingForwardedMessage[]) Promise~void~
    +generate(content: OutgoingForwardedMessage[], options: ForwardMetaOptions) Promise~EncodedForward~
  }

  class ForwardMetaOptions {
    +title: string
    +preview: NewsItem[]
    +summary: string
    +prompt: string
  }

  class EncodedForward {
    +multiMsgItems: any[]
    +tsum: number
    +source: string
    +summary: string
    +news: NewsItem[]
    +prompt: string
  }

  class transformOutgoingForwardMessages {
    +transformOutgoingForwardMessages(ctx: Context, messages: OutgoingForwardedMessage[], peer: Peer, options: ForwardMetaOptions) Promise~EncodedForward~
  }

  SendForwardMsg --> MessageEncoder : uses
  SendForwardMsg --> ForwardOptions : passes
  MessageEncoder --> EncoderOptions : uses
  ForwardMessageEncoder --> ForwardMetaOptions : uses
  transformOutgoingForwardMessages --> ForwardMessageEncoder : uses
  MessageEncoder --> EncodedForward : returns
  ForwardMessageEncoder --> EncodedForward : returns
Loading

文件级变更

Change Details Files
通过 Milky 私聊/群聊发送 API 贯穿可自定义转发消息展示元数据,并在发送元素中使用编码器提供的 prompt。
  • 重构 SendPrivateMessage 和 SendGroupMessage 中的转发负载处理,首先提取一个类型化的 forwardData 对象,其中可以包含 title、preview、summary 和 prompt 字段。
  • 扩展 transformOutgoingForwardMessages,使其接受一个可选的 options 对象(包含 title、preview、summary 和 prompt),并将其传递给 ForwardMessageEncoder.generate。
  • 使用转发消息编码器返回的 prompt 作为外发 RawMessage 中富文本元素的 desc 和 prompt 字段,而不是固定的字符串 '[聊天记录]'。
src/milky/api/message.ts
src/milky/transform/message/outgoing.ts
增强 OneBot11 go-cqhttp SendForwardMsg,使其接受自定义转发展示元数据,并将其传递到假转发节点编码和最终消息的 prompt/desc 中。
  • 扩展 SendForwardMsg 的 payload 和 OB11PostSendMsg 类型,支持 source、news、summary 和 prompt 属性,用于自定义转发展示。
  • 更新 SendForwardMsg.execute,在使用假转发时将这些新选项传递给 handleFakeForwardNode。
  • 修改 handleFakeForwardNode 以接受一个 options 对象,将其传递给 MessageEncoder.generate,根据结果推导 prompt(回退到 '[聊天记录]'),并在外发消息卡片中同时用于 desc 和 prompt。
src/onebot11/action/go-cqhttp/SendForwardMsg.ts
src/onebot11/types.ts
更新消息编码器,从 options 中生成带合理默认值的转发元数据,并向下游暴露 prompt 供使用。
  • 扩展 ForwardMessageEncoder.generate,使其接受一个 options 对象,在提供 title、preview、summary 和 prompt 时使用这些值,否则回退到之前的行为(根据群聊/私聊决定 source、自动生成摘要、计算预览/news,以及默认的 '[聊天记录]' prompt)。
  • 扩展用于 OneBot11 多消息创建的 MessageEncoder.generate,使其接受一个 options 对象,并以同样的方式,用提供的值覆盖 source、news、summary 和 prompt,否则回退到现有行为。
src/milky/transform/message/outgoing.ts
src/onebot11/helper/createMultiMessage.ts

Tips and commands

Interacting with Sourcery

  • 触发新评审: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 在某条评审评论下回复,要求 Sourcery 基于该评论创建 issue。你也可以直接回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文的任意位置写上 @sourcery-ai summary,即可在该位置生成 PR 摘要。你也可以评论 @sourcery-ai summary 来在任意时间(重新)生成摘要。
  • 生成 reviewer's guide: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成 reviewer's guide。
  • 批量解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,以将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • 批量撤销所有 Sourcery 评审: 在 pull request 中评论 @sourcery-ai dismiss,以撤销所有现有的 Sourcery 评审。特别适用于你想从头开始一次全新评审的场景——别忘了再评论 @sourcery-ai review 来触发新的评审!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用评审功能,例如 Sourcery 生成的 pull request 摘要、reviewer's guide 等。
  • 更改评审语言。
  • 添加、删除或编辑自定义评审指令。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Adds support for customizable display metadata (title/source, preview/news, summary, prompt) for merged/forwarded messages across Milky APIs and OneBot11 go-cqhttp actions, threading these options through the transform/encoding pipeline and using the computed prompt in the final message elements instead of a fixed placeholder.

Sequence diagram for customizable forward message metadata flow

sequenceDiagram
  actor BotDeveloper
  participant OB11Client as SendForwardMsg
  participant Encoder as MessageEncoder
  participant PMHQ as PMHQService
  participant App as AppService

  BotDeveloper->>OB11Client: SendForwardMsg(payload with source, news, summary, prompt)
  OB11Client->>Encoder: generate(nodes, options)
  Note over Encoder: options.source, options.news,
  Note over Encoder: options.summary, options.prompt
  Encoder-->>OB11Client: raw { multiMsgItems, tsum, source, summary, news, prompt }
  OB11Client->>PMHQ: uploadForward(peer, raw.multiMsgItems)
  PMHQ-->>OB11Client: resid
  OB11Client->>OB11Client: prompt = options.prompt or "[聊天记录]"
  OB11Client->>App: sendMessage(ctx, peer, forward_element with desc=prompt, prompt=prompt)
  App-->>BotDeveloper: Response with message id
Loading

Sequence diagram for Milky API forward message metadata propagation

sequenceDiagram
  participant API as SendPrivateMessage
  participant Transform as transformOutgoingForwardMessages
  participant FEncoder as ForwardMessageEncoder
  participant PMHQ as PMHQService
  participant App as AppService

  API->>Transform: transformOutgoingForwardMessages(ctx, messages, peer, options)
  Transform->>FEncoder: generate(messages, options)
  FEncoder-->>Transform: raw { multiMsgItems, tsum, source, summary, news, prompt }
  Transform-->>API: raw
  API->>PMHQ: uploadForward(peer, raw.multiMsgItems)
  PMHQ-->>API: resid
  API->>API: prompt = raw.prompt
  API->>App: sendMessage(ctx, peer, forward_element with desc=prompt, prompt=prompt)
  App-->>API: RawMessage result
Loading

Class diagram for updated forward message encoders and action

classDiagram
  class SendForwardMsg {
    +execute(payload: Payload) Promise~Response~
    -handleFakeForwardNode(peer: Peer, nodes: OB11MessageNode[], options: ForwardOptions) Promise~Response~
    -handleForwardNode(peer: Peer, nodes: OB11MessageNode[], msgInfos: MsgInfo[]) Promise~Response~
  }

  class Payload {
    +messages: OB11MessageNode[]
    +message: OB11MessageNode[]
    +message_type: string
    +source: string
    +news: NewsItem[]
    +summary: string
    +prompt: string
  }

  class ForwardOptions {
    +source: string
    +news: NewsItem[]
    +summary: string
    +prompt: string
  }

  class NewsItem {
    +text: string
  }

  class MessageEncoder {
    -ctx: Context
    -peer: Peer
    -tsum: number
    -isGroup: boolean
    -news: NewsItem[]
    +render(content: OB11MessageData[]) Promise~void~
    +generate(content: OB11MessageData[], options: EncoderOptions) Promise~EncodedForward~
  }

  class EncoderOptions {
    +source: string
    +news: NewsItem[]
    +summary: string
    +prompt: string
  }

  class ForwardMessageEncoder {
    -ctx: Context
    -peer: Peer
    -tsum: number
    -isGroup: boolean
    -news: NewsItem[]
    +render(content: OutgoingForwardedMessage[]) Promise~void~
    +generate(content: OutgoingForwardedMessage[], options: ForwardMetaOptions) Promise~EncodedForward~
  }

  class ForwardMetaOptions {
    +title: string
    +preview: NewsItem[]
    +summary: string
    +prompt: string
  }

  class EncodedForward {
    +multiMsgItems: any[]
    +tsum: number
    +source: string
    +summary: string
    +news: NewsItem[]
    +prompt: string
  }

  class transformOutgoingForwardMessages {
    +transformOutgoingForwardMessages(ctx: Context, messages: OutgoingForwardedMessage[], peer: Peer, options: ForwardMetaOptions) Promise~EncodedForward~
  }

  SendForwardMsg --> MessageEncoder : uses
  SendForwardMsg --> ForwardOptions : passes
  MessageEncoder --> EncoderOptions : uses
  ForwardMessageEncoder --> ForwardMetaOptions : uses
  transformOutgoingForwardMessages --> ForwardMessageEncoder : uses
  MessageEncoder --> EncodedForward : returns
  ForwardMessageEncoder --> EncodedForward : returns
Loading

File-Level Changes

Change Details Files
Thread customizable forward-message display metadata through Milky private/group send APIs and use encoder-provided prompt in sent elements.
  • Refactor forward payload handling in SendPrivateMessage and SendGroupMessage to first extract a typed forwardData object that may include title, preview, summary, and prompt fields.
  • Extend transformOutgoingForwardMessages to accept an optional options object with title, preview, summary, and prompt and pass it into ForwardMessageEncoder.generate.
  • Use the prompt returned from the forward-message encoder as both the rich element description (desc) and prompt fields in the outgoing RawMessage instead of the fixed '[聊天记录]' string.
src/milky/api/message.ts
src/milky/transform/message/outgoing.ts
Enhance OneBot11 go-cqhttp SendForwardMsg to accept custom forward display metadata and propagate it into fake forward node encoding and final message prompt/desc.
  • Extend SendForwardMsg payload and OB11PostSendMsg types to support source, news, summary, and prompt properties for custom forward display.
  • Update SendForwardMsg.execute to pass these new options into handleFakeForwardNode when using fake forwarding.
  • Modify handleFakeForwardNode to accept an options object, pass it to MessageEncoder.generate, derive the prompt (falling back to '[聊天记录]'), and use it for both desc and prompt in the outgoing message card.
src/onebot11/action/go-cqhttp/SendForwardMsg.ts
src/onebot11/types.ts
Update message encoders to generate forward metadata from options with sensible defaults and expose prompt for downstream use.
  • Extend ForwardMessageEncoder.generate to take an options object and use provided title, preview, summary, and prompt when present, otherwise fall back to previous behavior (group vs private source, auto-generated summary, computed preview/news, and default '[聊天记录]' prompt).
  • Extend MessageEncoder.generate for OneBot11 multi-message creation to accept an options object and similarly override source, news, summary, and prompt with provided values or default to existing behavior.
src/milky/transform/message/outgoing.ts
src/onebot11/helper/createMultiMessage.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我提供了一些整体性的反馈:

  • 用于转发元数据(title/source、preview/news、summary、prompt)的内联选项类型在多个位置重复出现;建议抽取为共享的 interface/type,这样可以保持结构一致并更易于维护。
  • 默认的 prompt 字符串“[聊天记录]”在多个实现中重复使用(ForwardMessageEncoder、MessageEncoder、handleFakeForwardNode);将其集中到一个共享常量中可以降低不同实现之间不一致的风险。
给 AI Agents 的提示词
Please address the comments from this code review:

## Overall Comments
- The inline option types for forward metadata (title/source, preview/news, summary, prompt) are duplicated in several places; consider extracting shared interfaces/types so the shape stays consistent and easier to maintain.
- The default prompt string '[聊天记录]' is repeated in multiple implementations (ForwardMessageEncoder, MessageEncoder, handleFakeForwardNode); centralizing this into a shared constant would reduce the risk of divergence.

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续的代码评审。
Original comment in English

Hey - I've left some high level feedback:

  • The inline option types for forward metadata (title/source, preview/news, summary, prompt) are duplicated in several places; consider extracting shared interfaces/types so the shape stays consistent and easier to maintain.
  • The default prompt string '[聊天记录]' is repeated in multiple implementations (ForwardMessageEncoder, MessageEncoder, handleFakeForwardNode); centralizing this into a shared constant would reduce the risk of divergence.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The inline option types for forward metadata (title/source, preview/news, summary, prompt) are duplicated in several places; consider extracting shared interfaces/types so the shape stays consistent and easier to maintain.
- The default prompt string '[聊天记录]' is repeated in multiple implementations (ForwardMessageEncoder, MessageEncoder, handleFakeForwardNode); centralizing this into a shared constant would reduce the risk of divergence.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@clansty
Copy link
Contributor Author

clansty commented Jan 6, 2026

已增加对嵌套合并转发的支持,API 调用方法和 napcat 大致相同,只是 node 的发送者字段在这里是 name 和 uin,而不是 username 和 user_id。这是 LuckyLilliaBot 本来就这样的,onebot 方言,没办法

@linyuchen
Copy link
Collaborator

能否拆成两个 PR,由于 Milky 那边可能需要标准定下来才能 merge,OneBot 这个可以先 merge

@clansty
Copy link
Contributor Author

clansty commented Jan 7, 2026

好的

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