Skip to content

chore: update version to 7.12.11#756

Merged
idranme merged 4 commits into
mainfrom
dev
May 3, 2026
Merged

chore: update version to 7.12.11#756
idranme merged 4 commits into
mainfrom
dev

Conversation

@idranme
Copy link
Copy Markdown
Collaborator

@idranme idranme commented May 3, 2026

Summary by Sourcery

为行内键盘按钮点击添加支持,改进 AI 群聊记录发送以返回实际消息 ID,并修复群撤回事件中的发送者识别问题。

New Features:

  • 暴露新的消息服务 API,用于处理行内键盘按钮点击事件并提供即时反馈。
  • 更新群组 AI 记录发送动作,以解析并返回实际创建的消息 ID,而非占位符。

Bug Fixes:

  • 修正群撤回通知事件中发送者和操作者的识别问题,当原始发送者 UIN 被报告为 0 时也能正确识别。

Enhancements:

  • 确保 AI 群聊记录生成时使用并返回一致的客户端消息随机 ID,以便关联已创建的消息。

Chores:

  • 将应用版本号提升至 7.12.11,并更新分发元数据。
Original summary in English

Summary by Sourcery

Add support for inline keyboard button clicks, improve AI group record sending to return actual message IDs, and fix sender identification for group recall events.

New Features:

  • Expose a new message service API for handling inline keyboard button click events with prompt feedback.
  • Update the group AI record sending action to resolve and return the created message ID instead of a placeholder.

Bug Fixes:

  • Correct sender and operator identification in group recall notice events when the original sender UIN is reported as zero.

Enhancements:

  • Ensure AI group record generation uses and returns a consistent client message random ID for correlating created messages.

Chores:

  • Bump the application version to 7.12.11 and update distribution metadata.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 3, 2026

审阅者指南

为消息服务新增内联键盘点击 API,增强 AI 群记录发送逻辑,通过基于 msgRandom 关联的方式返回创建出的消息 ID;改进当 senderUin 为 0 时群撤回通知中消息发送方与操作者 UIN 的解析逻辑;并将项目版本号提升至 7.12.11。

通过 msgRandom 解析 SendGroupAiRecord 消息 ID 的时序图

sequenceDiagram
  actor OneBotClient
  participant SendGroupAiRecord
  participant Context
  participant PMHQ as PMHQMessageMixin
  participant OidbSvc as OidbSvcTrpcTcp
  participant EventBus
  participant Store

  OneBotClient->>SendGroupAiRecord: _handle(payload)
  SendGroupAiRecord->>Context: access pmhq
  SendGroupAiRecord->>PMHQ: getGroupGenerateAiRecord(groupId, character, text, chatType)
  PMHQ->>PMHQ: generate msgRandom
  PMHQ->>OidbSvc: httpSendPB(OidbSvcTrpcTcp.0x929b_0, encodedBody)
  OidbSvc-->>PMHQ: ack
  PMHQ-->>SendGroupAiRecord: { msgRandom }
  SendGroupAiRecord->>Context: on(nt/message-created, handler)
  Note over SendGroupAiRecord,EventBus: handler compares msg.msgRandom with target msgRandom
  EventBus-->>Context: nt/message-created(msg)
  Context-->>SendGroupAiRecord: invoke handler(msg)
  SendGroupAiRecord->>Store: createMsgShortId(msg)
  Store-->>SendGroupAiRecord: shortId
  SendGroupAiRecord-->>OneBotClient: { message_id: shortId }
Loading

更新后的 NodeIKernelMsgService 内联键盘 API 类图

classDiagram
  class NodeIKernelMsgService {
    <<interface>>
    setContactLocalTop(peer: Peer, isTop: boolean) Promise~GeneralCallResult~
    sendShowInputStatusReq(chatType: ChatType, eventType: number, toUid: string) Promise~GeneralCallResult~
    clickInlineKeyboardButton(inlineKeyboardClickInfo: InlineKeyboardClickInfo) Promise~InlineKeyboardClickResult~
  }

  class InlineKeyboardClickInfo {
    +string guildId
    +number dmFlag
    +string peerId
    +ChatType chatType
    +string botAppid
    +string msgSeq
    +string buttonId
    +string callback_data
  }

  class GeneralCallResult {
  }

  class InlineKeyboardClickResult {
    +number status
    +string promptText
    +number promptType
    +number promptIcon
  }

  NodeIKernelMsgService --> InlineKeyboardClickInfo : uses
  NodeIKernelMsgService --> InlineKeyboardClickResult : returns
  InlineKeyboardClickResult --|> GeneralCallResult
Loading

SendGroupAiRecord 与 PMHQ 消息 mixin 变更的类图

classDiagram
  class PMHQBase {
  }

  class MessageMixin {
    +getGroupGenerateAiRecord(groupId: number, character: string, text: string, chatType: number) Promise~GetGroupGenerateAiRecordResult~
  }

  class GetGroupGenerateAiRecordResult {
    +number msgRandom
  }

  class Context {
    +PMHQBase pmhq
    +Store store
    +EventBus on(eventName: string, handler: MessageCreatedHandler) Dispose
  }

  class Store {
    +createMsgShortId(message: Message) number
  }

  class EventBus {
    +on(eventName: string, handler: MessageCreatedHandler) Dispose
  }

  class Dispose {
    +invoke() void
  }

  class Message {
    +string msgRandom
  }

  class BaseAction~Payload,Response~ {
    +Context ctx
  }

  class SendGroupAiRecord {
    +_handle(payload: Payload) Promise~Response~
  }

  class Payload {
    +string group_id
    +string character
    +string text
    +string chat_type
  }

  class Response {
    +number message_id
  }

  MessageMixin ..|> PMHQBase
  Context --> PMHQBase : has
  Context --> Store : has
  Context --> EventBus : has
  SendGroupAiRecord --|> BaseAction
  SendGroupAiRecord --> Context : uses
  SendGroupAiRecord --> Payload : parameter
  SendGroupAiRecord --> Response : returns
  PMHQBase <.. MessageMixin : mixin
  PMHQBase --> GetGroupGenerateAiRecordResult : returns
  Store --> Message : uses
Loading

更新后的 OB11GroupRecallNoticeEvent 构造逻辑类图

classDiagram
  class OB11Entities {
  }

  class OB11GroupRecallNoticeEvent {
    +OB11GroupRecallNoticeEvent(groupId: number, senderUin: number, operatorUin: number, messageId: number)
  }

  class RevokeElement {
    +string operatorUid
    +string origMsgSenderUid
  }

  class NtUserApi {
    +getUinByUid(uid: string) Promise~string~
  }

  class OB11Context {
    +NtUserApi ntUserApi
    +Logger logger
  }

  class Logger {
    +warn(message: string) void
  }

  class MessageRecord {
    +string peerUid
    +string senderUin
  }

  OB11Entities ..> OB11GroupRecallNoticeEvent : creates
  OB11Entities ..> NtUserApi : uses
  OB11Entities ..> RevokeElement : uses
  OB11Entities ..> MessageRecord : uses
  OB11Context --> NtUserApi : has
  OB11Context --> Logger : has
Loading

文件级变更

变更 详情 文件
在消息服务 API 中暴露一个用于处理内联键盘按钮点击的新方法。
  • 在 NodeIKernelMsgService 接口中扩展 clickInlineKeyboardButton 方法,用于接收内联键盘点击元数据。
  • 将该方法返回类型定义为在 GeneralCallResult 基础上扩展了提示/状态字段的类型,以便用于 UI 反馈。
src/ntqqapi/services/NodeIKernelMsgService.ts
让 SendGroupAiRecord 通过关联 AI 记录请求和后续的 message-created 事件,返回实际创建的消息 ID。
  • 从 getGroupGenerateAiRecord 的返回结果中获取 msgRandom,并转换为字符串用于与发出的消息进行比较。
  • 使用 Promise.withResolvers 创建一个延迟的 Response Promise。
  • 订阅 nt/message-created 事件,根据 msgRandom 匹配目标消息;在匹配到时释放监听器,并使用 store 中生成的短消息 ID 进行 resolve。
  • 将 _handle 的返回值改为该延迟 Promise,而不再返回固定的 message_id: 0。
src/onebot11/action/llbot/msg/SendGroupAiRecord.ts
src/main/pmhq/mixins/message.ts
调整群撤回通知事件的构造逻辑,在 senderUin 为 0 时正确解析消息发送方和操作者的 UIN。
  • 引入局部变量 senderUin,以 msg.senderUin 初始化。
  • 当 msg.senderUin 为 '0' 时,通过 ntUserApi.getUinByUid 使用 origMsgSenderUid 解析出 senderUin。
  • 如果撤回者与原消息发送者为同一人,则将 operatorUin 与解析得到的 senderUin 对齐。
  • 在构造 OB11GroupRecallNoticeEvent 时使用解析后的 senderUin,而不是原始的 msg.senderUin。
src/onebot11/entities.ts
从 AI 群记录生成辅助方法返回 msgRandom,以支持下游的关联。
  • 对每个请求生成一次 4 字节随机的 msgRandom,并在 clientMsgInfo 中重复使用。
  • 在发送 OIDB 请求后返回一个包含 msgRandom 的对象。
src/main/pmhq/mixins/message.ts
将应用/包版本号提升到 7.12.11,并调整相关的元数据/文档路径。
  • 把导出的版本常量从 7.12.10 更新为 7.12.11。
  • 修改包分发元数据(以及可能的文档路径重命名)以反映新版本。
src/version.ts
package-dist.json
a/doc/检验规范.txt
b/doc/检验规范.txt

提示与命令

与 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 来在任意时间(重新)生成总结。
  • 生成审阅者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可在任意时间(重新)生成审阅者指南。
  • 批量解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,以标记所有 Sourcery 评论为已解决。适用于你已经处理完所有评论且不希望再看到它们的时候。
  • 忽略所有现有的 Sourcery 审查: 在 Pull Request 中评论 @sourcery-ai dismiss,以忽略所有已有的 Sourcery 审查结果。特别适合你希望从头开始新的审查时使用——别忘了随后再评论 @sourcery-ai review 来触发新的审查!

自定义使用体验

访问你的 控制面板 以:

  • 启用或禁用审查功能,例如 Sourcery 自动生成的 Pull Request 总结、审阅者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查说明。
  • 调整其他审查相关设置。

获取帮助

Original review guide in English

Reviewer's Guide

Adds a new inline keyboard click API to the message service, enhances AI group record sending to return the created message ID by correlating on msgRandom, improves group recall notice sender/operator resolution when senderUin is zero, and bumps the project version to 7.12.11.

Sequence diagram for SendGroupAiRecord message ID resolution via msgRandom

sequenceDiagram
  actor OneBotClient
  participant SendGroupAiRecord
  participant Context
  participant PMHQ as PMHQMessageMixin
  participant OidbSvc as OidbSvcTrpcTcp
  participant EventBus
  participant Store

  OneBotClient->>SendGroupAiRecord: _handle(payload)
  SendGroupAiRecord->>Context: access pmhq
  SendGroupAiRecord->>PMHQ: getGroupGenerateAiRecord(groupId, character, text, chatType)
  PMHQ->>PMHQ: generate msgRandom
  PMHQ->>OidbSvc: httpSendPB(OidbSvcTrpcTcp.0x929b_0, encodedBody)
  OidbSvc-->>PMHQ: ack
  PMHQ-->>SendGroupAiRecord: { msgRandom }
  SendGroupAiRecord->>Context: on(nt/message-created, handler)
  Note over SendGroupAiRecord,EventBus: handler compares msg.msgRandom with target msgRandom
  EventBus-->>Context: nt/message-created(msg)
  Context-->>SendGroupAiRecord: invoke handler(msg)
  SendGroupAiRecord->>Store: createMsgShortId(msg)
  Store-->>SendGroupAiRecord: shortId
  SendGroupAiRecord-->>OneBotClient: { message_id: shortId }
Loading

Class diagram for updated NodeIKernelMsgService inline keyboard API

classDiagram
  class NodeIKernelMsgService {
    <<interface>>
    setContactLocalTop(peer: Peer, isTop: boolean) Promise~GeneralCallResult~
    sendShowInputStatusReq(chatType: ChatType, eventType: number, toUid: string) Promise~GeneralCallResult~
    clickInlineKeyboardButton(inlineKeyboardClickInfo: InlineKeyboardClickInfo) Promise~InlineKeyboardClickResult~
  }

  class InlineKeyboardClickInfo {
    +string guildId
    +number dmFlag
    +string peerId
    +ChatType chatType
    +string botAppid
    +string msgSeq
    +string buttonId
    +string callback_data
  }

  class GeneralCallResult {
  }

  class InlineKeyboardClickResult {
    +number status
    +string promptText
    +number promptType
    +number promptIcon
  }

  NodeIKernelMsgService --> InlineKeyboardClickInfo : uses
  NodeIKernelMsgService --> InlineKeyboardClickResult : returns
  InlineKeyboardClickResult --|> GeneralCallResult
Loading

Class diagram for SendGroupAiRecord and PMHQ message mixin changes

classDiagram
  class PMHQBase {
  }

  class MessageMixin {
    +getGroupGenerateAiRecord(groupId: number, character: string, text: string, chatType: number) Promise~GetGroupGenerateAiRecordResult~
  }

  class GetGroupGenerateAiRecordResult {
    +number msgRandom
  }

  class Context {
    +PMHQBase pmhq
    +Store store
    +EventBus on(eventName: string, handler: MessageCreatedHandler) Dispose
  }

  class Store {
    +createMsgShortId(message: Message) number
  }

  class EventBus {
    +on(eventName: string, handler: MessageCreatedHandler) Dispose
  }

  class Dispose {
    +invoke() void
  }

  class Message {
    +string msgRandom
  }

  class BaseAction~Payload,Response~ {
    +Context ctx
  }

  class SendGroupAiRecord {
    +_handle(payload: Payload) Promise~Response~
  }

  class Payload {
    +string group_id
    +string character
    +string text
    +string chat_type
  }

  class Response {
    +number message_id
  }

  MessageMixin ..|> PMHQBase
  Context --> PMHQBase : has
  Context --> Store : has
  Context --> EventBus : has
  SendGroupAiRecord --|> BaseAction
  SendGroupAiRecord --> Context : uses
  SendGroupAiRecord --> Payload : parameter
  SendGroupAiRecord --> Response : returns
  PMHQBase <.. MessageMixin : mixin
  PMHQBase --> GetGroupGenerateAiRecordResult : returns
  Store --> Message : uses
Loading

Class diagram for updated OB11GroupRecallNoticeEvent creation logic

classDiagram
  class OB11Entities {
  }

  class OB11GroupRecallNoticeEvent {
    +OB11GroupRecallNoticeEvent(groupId: number, senderUin: number, operatorUin: number, messageId: number)
  }

  class RevokeElement {
    +string operatorUid
    +string origMsgSenderUid
  }

  class NtUserApi {
    +getUinByUid(uid: string) Promise~string~
  }

  class OB11Context {
    +NtUserApi ntUserApi
    +Logger logger
  }

  class Logger {
    +warn(message: string) void
  }

  class MessageRecord {
    +string peerUid
    +string senderUin
  }

  OB11Entities ..> OB11GroupRecallNoticeEvent : creates
  OB11Entities ..> NtUserApi : uses
  OB11Entities ..> RevokeElement : uses
  OB11Entities ..> MessageRecord : uses
  OB11Context --> NtUserApi : has
  OB11Context --> Logger : has
Loading

File-Level Changes

Change Details Files
Expose a new method for handling inline keyboard button clicks in the message service API.
  • Extend NodeIKernelMsgService interface with clickInlineKeyboardButton method accepting inline keyboard click metadata.
  • Define the method return type as GeneralCallResult extended with prompt/status fields for UI feedback.
src/ntqqapi/services/NodeIKernelMsgService.ts
Make SendGroupAiRecord return the actual created message ID by correlating the AI record request with the subsequent message-created event.
  • Capture msgRandom from getGroupGenerateAiRecord result and convert it to string for comparison with emitted messages.
  • Use Promise.withResolvers to create a deferred Response promise.
  • Subscribe to nt/message-created events, match on msgRandom, dispose the listener when matched, then resolve with a short message ID from the store.
  • Change _handle to return the deferred promise instead of a static message_id: 0.
src/onebot11/action/llbot/msg/SendGroupAiRecord.ts
src/main/pmhq/mixins/message.ts
Adjust group recall notice event construction to correctly resolve sender and operator UINs when senderUin is reported as zero.
  • Introduce a local senderUin variable initialized from msg.senderUin.
  • When msg.senderUin is '0', resolve senderUin from origMsgSenderUid via ntUserApi.getUinByUid.
  • If the revoker is also the original sender, align operatorUin with the resolved senderUin.
  • Use the resolved senderUin when constructing OB11GroupRecallNoticeEvent instead of the raw msg.senderUin.
src/onebot11/entities.ts
Return msgRandom from the AI group record generation helper to support downstream correlation.
  • Generate a 4-byte random msgRandom value once per request and reuse it in clientMsgInfo.
  • Return an object containing msgRandom after sending the OIDB request.
src/main/pmhq/mixins/message.ts
Bump the application/package version to 7.12.11 and adjust related metadata/docs paths.
  • Update exported version constant from 7.12.10 to 7.12.11.
  • Modify package distribution metadata (and possibly doc path renames) to reflect the new version.
src/version.ts
package-dist.json
a/doc/检验规范.txt
b/doc/检验规范.txt

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
Copy Markdown
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 - 我发现了 1 个问题,并给出了一些整体性的反馈:

  • SendGroupAiRecord._handle 中,如果目标 msgRandom 对应的 nt/message-created 事件从未触发,那么返回的 promise 将永远不会被解决;建议添加超时或兜底逻辑,确保在超时后能 resolve/reject,并且始终正确清理事件监听器。
  • SendGroupAiRecord 中使用 Promise.withResolvers 依赖于相对较新的运行时支持;如果这段代码需要在较老的环境中运行,建议包一层显式的 new Promise 来避免兼容性问题。
  • OB11Entities 中更新后的撤回处理逻辑里,之前对假值 msg.senderUin!msg.senderUin)的分支被移除了;如果 senderUin 在某些情况下可能为空或为 undefined,可能需要保留对应场景的处理或日志记录,以避免生成无效的 Number(senderUin)
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
-`SendGroupAiRecord._handle` 中,如果目标 `msgRandom` 对应的 `nt/message-created` 事件从未触发,那么返回的 promise 将永远不会被解决;建议添加超时或兜底逻辑,确保在超时后能 resolve/reject,并且始终正确清理事件监听器。
-`SendGroupAiRecord` 中使用 `Promise.withResolvers` 依赖于相对较新的运行时支持;如果这段代码需要在较老的环境中运行,建议包一层显式的 `new Promise` 来避免兼容性问题。
-`OB11Entities` 中更新后的撤回处理逻辑里,之前对假值 `msg.senderUin``!msg.senderUin`)的分支被移除了;如果 `senderUin` 在某些情况下可能为空或为 undefined,可能需要保留对应场景的处理或日志记录,以避免生成无效的 `Number(senderUin)`## Individual Comments

### Comment 1
<location path="src/onebot11/action/llbot/msg/SendGroupAiRecord.ts" line_range="26-35" />
<code_context>
-    await this.ctx.pmhq.getGroupGenerateAiRecord(+payload.group_id, payload.character, payload.text, +payload.chat_type)
-    return { message_id: 0 }
+    const res = await this.ctx.pmhq.getGroupGenerateAiRecord(+payload.group_id, payload.character, payload.text, +payload.chat_type)
+    const targetMsgRandom = res.msgRandom.toString()
+    const { promise, resolve } = Promise.withResolvers<Response>()
+    const dispose = this.ctx.on('nt/message-created', (msg) => {
+      if (msg.msgRandom === targetMsgRandom) {
+        dispose()
+        const shortId = this.ctx.store.createMsgShortId(msg)
+        resolve({ message_id: shortId })
+      }
+    })
+    return promise
   }
 }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 如果对应的 `nt/message-created` 事件未被触发,这个 promise 可能永远不会被 settle。

如果 AI 记录消息从未被创建,或者事件被错过(例如网络问题、服务错误、进程重启),这个 promise 将既不会 resolve 也不会 reject,使得该操作无限期地挂起。建议添加超时和/或显式的错误路径(例如在 N 秒后 reject 并清理监听器),这样调用方可以处理失败,而不是一直被挂住。
</issue_to_address>

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

Hey - I've found 1 issue, and left some high level feedback:

  • In SendGroupAiRecord._handle, the returned promise will never resolve if the nt/message-created event with the target msgRandom is not emitted; consider adding a timeout or fallback to resolve/reject and ensure the event listener is always cleaned up.
  • The use of Promise.withResolvers in SendGroupAiRecord relies on relatively new runtime support; if this code must run in older environments, consider an explicit new Promise wrapper to avoid compatibility issues.
  • In the updated recall handling in OB11Entities, the previous branch for a falsy msg.senderUin (!msg.senderUin) was removed; if senderUin can be empty/undefined in some cases, you may want to preserve handling or logging for that scenario to avoid producing an invalid Number(senderUin).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `SendGroupAiRecord._handle`, the returned promise will never resolve if the `nt/message-created` event with the target `msgRandom` is not emitted; consider adding a timeout or fallback to resolve/reject and ensure the event listener is always cleaned up.
- The use of `Promise.withResolvers` in `SendGroupAiRecord` relies on relatively new runtime support; if this code must run in older environments, consider an explicit `new Promise` wrapper to avoid compatibility issues.
- In the updated recall handling in `OB11Entities`, the previous branch for a falsy `msg.senderUin` (`!msg.senderUin`) was removed; if `senderUin` can be empty/undefined in some cases, you may want to preserve handling or logging for that scenario to avoid producing an invalid `Number(senderUin)`.

## Individual Comments

### Comment 1
<location path="src/onebot11/action/llbot/msg/SendGroupAiRecord.ts" line_range="26-35" />
<code_context>
-    await this.ctx.pmhq.getGroupGenerateAiRecord(+payload.group_id, payload.character, payload.text, +payload.chat_type)
-    return { message_id: 0 }
+    const res = await this.ctx.pmhq.getGroupGenerateAiRecord(+payload.group_id, payload.character, payload.text, +payload.chat_type)
+    const targetMsgRandom = res.msgRandom.toString()
+    const { promise, resolve } = Promise.withResolvers<Response>()
+    const dispose = this.ctx.on('nt/message-created', (msg) => {
+      if (msg.msgRandom === targetMsgRandom) {
+        dispose()
+        const shortId = this.ctx.store.createMsgShortId(msg)
+        resolve({ message_id: shortId })
+      }
+    })
+    return promise
   }
 }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The promise may never settle if the corresponding `nt/message-created` event is not emitted.

If the AI record message is never created or the event is missed (e.g., network issue, service error, process restart), this promise will never resolve or reject, leaving the action hanging indefinitely. Consider adding a timeout and/or explicit error path (e.g., reject after N seconds and dispose the listener) so callers can handle failures instead of hanging forever.
</issue_to_address>

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.

Comment on lines +26 to +35
const targetMsgRandom = res.msgRandom.toString()
const { promise, resolve } = Promise.withResolvers<Response>()
const dispose = this.ctx.on('nt/message-created', (msg) => {
if (msg.msgRandom === targetMsgRandom) {
dispose()
const shortId = this.ctx.store.createMsgShortId(msg)
resolve({ message_id: shortId })
}
})
return promise
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): 如果对应的 nt/message-created 事件未被触发,这个 promise 可能永远不会被 settle。

如果 AI 记录消息从未被创建,或者事件被错过(例如网络问题、服务错误、进程重启),这个 promise 将既不会 resolve 也不会 reject,使得该操作无限期地挂起。建议添加超时和/或显式的错误路径(例如在 N 秒后 reject 并清理监听器),这样调用方可以处理失败,而不是一直被挂住。

Original comment in English

suggestion (bug_risk): The promise may never settle if the corresponding nt/message-created event is not emitted.

If the AI record message is never created or the event is missed (e.g., network issue, service error, process restart), this promise will never resolve or reject, leaving the action hanging indefinitely. Consider adding a timeout and/or explicit error path (e.g., reject after N seconds and dispose the listener) so callers can handle failures instead of hanging forever.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

Test Report

Job Status
unit-test ✅ success
e2e-test ✅ success

✅ All tests passed

@idranme idranme merged commit a5d9d2f into main May 3, 2026
6 checks passed
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.

1 participant