feat: support msg_seq parameter in reply message construction

- Add optional 'seq' parameter to OB11MessageReply for using msg_seq
- Prioritize seq over id for querying reply messages
- Maintain backward compatibility with existing id parameter
- Update type definitions across backend and frontend
- Update validation schemas for message nodes

close #1523
This commit is contained in:
时瑾 2026-01-18 09:30:53 +08:00
parent 5284e0ac5a
commit b296d50d4a
No known key found for this signature in database
GPG Key ID: 023F70A1B8F8C196
4 changed files with 131 additions and 110 deletions

View File

@ -587,15 +587,33 @@ export class OneBotMsgApi {
return at(atQQ, uid, NTMsgAtType.ATTYPEONE, info.nick || ''); return at(atQQ, uid, NTMsgAtType.ATTYPEONE, info.nick || '');
}, },
[OB11MessageDataType.reply]: async ({ data: { id } }) => { [OB11MessageDataType.reply]: async ({ data: { id, seq } }, context) => {
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id)); let replyMsg: RawMessage | undefined;
if (!replyMsgM) { let replyMsgPeer: Peer | undefined;
this.core.context.logger.logWarn('回复消息不存在', id);
// 优先使用 seq
if (seq) {
const msgList = (await this.core.apis.MsgApi.getMsgsBySeqAndCount(
context.peer, seq.toString(), 1, true, true
)).msgList;
replyMsg = msgList[0];
replyMsgPeer = context.peer;
} else if (id) {
// 降级使用 id
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
if (!replyMsgM) {
this.core.context.logger.logWarn('回复消息不存在', id);
return undefined;
}
replyMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(
replyMsgM.Peer, [replyMsgM.MsgId])).msgList[0];
replyMsgPeer = replyMsgM.Peer;
} else {
this.core.context.logger.logWarn('回复消息缺少id或seq参数');
return undefined; return undefined;
} }
const replyMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(
replyMsgM.Peer, [replyMsgM.MsgId])).msgList[0]; return replyMsg && replyMsgPeer
return replyMsg
? { ? {
elementType: ElementType.REPLY, elementType: ElementType.REPLY,
elementId: '', elementId: '',
@ -605,7 +623,7 @@ export class OneBotMsgApi {
senderUin: replyMsg.senderUin, senderUin: replyMsg.senderUin,
senderUinStr: replyMsg.senderUin, senderUinStr: replyMsg.senderUin,
replyMsgClientSeq: replyMsg.clientSeq, replyMsgClientSeq: replyMsg.clientSeq,
_replyMsgPeer: replyMsgM.Peer, _replyMsgPeer: replyMsgPeer,
}, },
} }
: undefined; : undefined;

View File

@ -159,7 +159,8 @@ export interface OB11MessageAt {
export interface OB11MessageReply { export interface OB11MessageReply {
type: OB11MessageDataType.reply; type: OB11MessageDataType.reply;
data: { data: {
id: string; id?: string; // msg_id 的短ID映射
seq?: number; // msg_seq优先使用
}; };
} }

View File

@ -61,7 +61,8 @@ const messageNode = z.union([
.object({ .object({
type: z.literal('reply'), type: z.literal('reply'),
data: z.object({ data: z.object({
id: z.number(), id: z.number().optional(),
seq: z.number().optional(),
}), }),
}) })
.describe('回复消息'), .describe('回复消息'),

View File

@ -24,196 +24,197 @@ export type OB11SegmentType =
| 'file'; | 'file';
export interface Segment { export interface Segment {
type: OB11SegmentType type: OB11SegmentType;
} }
/** 纯文本 */ /** 纯文本 */
export interface TextSegment extends Segment { export interface TextSegment extends Segment {
type: 'text' type: 'text';
data: { data: {
text: string text: string;
} };
} }
/** QQ表情 */ /** QQ表情 */
export interface FaceSegment extends Segment { export interface FaceSegment extends Segment {
type: 'face' type: 'face';
data: { data: {
id: string id: string;
} };
} }
/** 图片消息段 */ /** 图片消息段 */
export interface ImageSegment extends Segment { export interface ImageSegment extends Segment {
type: 'image' type: 'image';
data: { data: {
file: string file: string;
type?: 'flash' type?: 'flash';
url?: string url?: string;
cache?: 0 | 1 cache?: 0 | 1;
proxy?: 0 | 1 proxy?: 0 | 1;
timeout?: number timeout?: number;
} };
} }
/** 语音消息段 */ /** 语音消息段 */
export interface RecordSegment extends Segment { export interface RecordSegment extends Segment {
type: 'record' type: 'record';
data: { data: {
file: string file: string;
magic?: 0 | 1 magic?: 0 | 1;
url?: string url?: string;
cache?: 0 | 1 cache?: 0 | 1;
proxy?: 0 | 1 proxy?: 0 | 1;
timeout?: number timeout?: number;
} };
} }
/** 短视频消息段 */ /** 短视频消息段 */
export interface VideoSegment extends Segment { export interface VideoSegment extends Segment {
type: 'video' type: 'video';
data: { data: {
file: string file: string;
url?: string url?: string;
cache?: 0 | 1 cache?: 0 | 1;
proxy?: 0 | 1 proxy?: 0 | 1;
timeout?: number timeout?: number;
} };
} }
/** @某人消息段 */ /** @某人消息段 */
export interface AtSegment extends Segment { export interface AtSegment extends Segment {
type: 'at' type: 'at';
data: { data: {
qq: string | 'all' qq: string | 'all';
name?: string name?: string;
} };
} }
/** 猜拳魔法表情消息段 */ /** 猜拳魔法表情消息段 */
export interface RpsSegment extends Segment { export interface RpsSegment extends Segment {
type: 'rps' type: 'rps';
} }
/** 掷骰子魔法表情消息段 */ /** 掷骰子魔法表情消息段 */
export interface DiceSegment extends Segment { export interface DiceSegment extends Segment {
type: 'dice' type: 'dice';
} }
/** 窗口抖动(戳一戳)消息段 */ /** 窗口抖动(戳一戳)消息段 */
export interface ShakeSegment extends Segment { export interface ShakeSegment extends Segment {
type: 'shake' type: 'shake';
data: object data: object;
} }
/** 戳一戳消息段 */ /** 戳一戳消息段 */
export interface PokeSegment extends Segment { export interface PokeSegment extends Segment {
type: 'poke' type: 'poke';
data: { data: {
type: string type: string;
id: string id: string;
name?: string name?: string;
} };
} }
/** 匿名发消息消息段 */ /** 匿名发消息消息段 */
export interface AnonymousSegment extends Segment { export interface AnonymousSegment extends Segment {
type: 'anonymous' type: 'anonymous';
data: { data: {
ignore?: 0 | 1 ignore?: 0 | 1;
} };
} }
/** 链接分享消息段 */ /** 链接分享消息段 */
export interface ShareSegment extends Segment { export interface ShareSegment extends Segment {
type: 'share' type: 'share';
data: { data: {
url: string url: string;
title: string title: string;
content?: string content?: string;
image?: string image?: string;
} };
} }
/** 推荐好友/群消息段 */ /** 推荐好友/群消息段 */
export interface ContactSegment extends Segment { export interface ContactSegment extends Segment {
type: 'contact' type: 'contact';
data: { data: {
type: 'qq' | 'group' type: 'qq' | 'group';
id: string id: string;
} };
} }
/** 位置消息段 */ /** 位置消息段 */
export interface LocationSegment extends Segment { export interface LocationSegment extends Segment {
type: 'location' type: 'location';
data: { data: {
lat: string lat: string;
lon: string lon: string;
title?: string title?: string;
content?: string content?: string;
} };
} }
/** 音乐分享消息段 */ /** 音乐分享消息段 */
export interface MusicSegment extends Segment { export interface MusicSegment extends Segment {
type: 'music' type: 'music';
data: { data: {
type: 'qq' | '163' | 'xm' type: 'qq' | '163' | 'xm';
id: string id: string;
} };
} }
/** 音乐自定义分享消息段 */ /** 音乐自定义分享消息段 */
export interface CustomMusicSegment extends Segment { export interface CustomMusicSegment extends Segment {
type: 'music' type: 'music';
data: { data: {
type: 'custom' type: 'custom';
url: string url: string;
audio: string audio: string;
title: string title: string;
content?: string content?: string;
image?: string image?: string;
} };
} }
/** 回复消息段 */ /** 回复消息段 */
export interface ReplySegment extends Segment { export interface ReplySegment extends Segment {
type: 'reply' type: 'reply';
data: { data: {
id: string id?: string; // msg_id 的短ID映射
} seq?: number; // msg_seq优先使用
};
} }
export interface FileSegment extends Segment { export interface FileSegment extends Segment {
type: 'file' type: 'file';
data: { data: {
file: string file: string;
} };
} }
/** 合并转发消息段 */ /** 合并转发消息段 */
export interface ForwardSegment extends Segment { export interface ForwardSegment extends Segment {
type: 'forward' type: 'forward';
data: { data: {
id: string id: string;
} };
} }
/** XML消息段 */ /** XML消息段 */
export interface XmlSegment extends Segment { export interface XmlSegment extends Segment {
type: 'xml' type: 'xml';
data: { data: {
data: string data: string;
} };
} }
/** JSON消息段 */ /** JSON消息段 */
export interface JsonSegment extends Segment { export interface JsonSegment extends Segment {
type: 'json' type: 'json';
data: { data: {
data: string data: string;
} };
} }
/** OneBot11消息段 */ /** OneBot11消息段 */
@ -242,23 +243,23 @@ export type OB11SegmentBase =
/** 合并转发已有消息节点消息段 */ /** 合并转发已有消息节点消息段 */
export interface DirectNodeSegment extends Segment { export interface DirectNodeSegment extends Segment {
type: 'node' type: 'node';
data: { data: {
id: string id: string;
} };
} }
/** 合并转发自定义节点消息段 */ /** 合并转发自定义节点消息段 */
export interface CustomNodeSegments extends Segment { export interface CustomNodeSegments extends Segment {
type: 'node' type: 'node';
data: { data: {
user_id: string user_id: string;
nickname: string nickname: string;
content: OB11Segment[] content: OB11Segment[];
prompt?: string prompt?: string;
summary?: string summary?: string;
source?: string source?: string;
} };
} }
/** 合并转发消息段 */ /** 合并转发消息段 */