Compare commits

..

10 Commits

Author SHA1 Message Date
手瓜一十雪
6c3d737219 fix: 合并reply 2024-08-06 16:00:25 +08:00
手瓜一十雪
a1f38fed7a release: v1.8.1 2024-08-06 15:55:59 +08:00
手瓜一十雪
c36bb77286 feat: 合并消息初步解析完成 2024-08-06 14:48:20 +08:00
手瓜一十雪
1a1acdc3c9 feat: 转发着色 2024-08-06 11:40:36 +08:00
手瓜一十雪
ef8e60c405 build: 180test 2024-08-06 11:31:48 +08:00
手瓜一十雪
31c1cc47bf build: 180 2024-08-06 11:30:35 +08:00
手瓜一十雪
351fed7359 fix #102 2024-08-06 11:30:12 +08:00
手瓜一十雪
f49e7cbe57 chore: log 2024-08-06 11:15:44 +08:00
手瓜一十雪
7da8ea5e99 style:lint 2024-08-06 11:12:25 +08:00
手瓜一十雪
8fa6a12a7c build: 181test 2024-08-06 11:07:37 +08:00
32 changed files with 176 additions and 107 deletions

View File

@@ -1,21 +0,0 @@
# v1.8.0
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
## 启动的方式
Way03/Way05
## 新增与调整
1. 消息ID映射到UINT32空间
2. 回复ID验证与修复
3. 新API /fetch_emoji_like
```json5
{
"message_id": 1557274996,//消息ID
"emojiType": "1",//可以从event事件获取 一般为1或者2
"emojiId": "76"//可以从event事件获取 一个表情对应一个ID
}
```
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,13 @@
# v1.8.1
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
## 启动的方式
Way03/Way05
## 新增与调整
1. 多层转发消息解析
2. 撤回消息附带反馈
3. 文件上传兼容性提升
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "1.8.0",
"version": "1.8.1",
"scripts": {
"watch:dev": "vite --mode development",
"watch:prod": "vite --mode production",

View File

@@ -6,15 +6,15 @@ import AppidTable from '@/core/external/appid.json';
import { log } from './log';
//基础目录获取
export let QQMainPath = process.execPath;
export let QQPackageInfoPath: string = path.join(path.dirname(QQMainPath), 'resources', 'app', 'package.json');
export let QQVersionConfigPath: string | undefined = getQQVersionConfigPath(QQMainPath);
export const QQMainPath = process.execPath;
export const QQPackageInfoPath: string = path.join(path.dirname(QQMainPath), 'resources', 'app', 'package.json');
export const QQVersionConfigPath: string | undefined = getQQVersionConfigPath(QQMainPath);
//基础信息获取 无快更则启用默认模板填充
export let isQuickUpdate: boolean = !!QQVersionConfigPath;
export let QQVersionConfig: QQVersionConfigType = isQuickUpdate ? JSON.parse(fs.readFileSync(QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
export let QQPackageInfo: QQPackageInfoType = JSON.parse(fs.readFileSync(QQPackageInfoPath).toString());
export let { appid: QQVersionAppid, qua: QQVersionQua } = getAppidV2();
export const isQuickUpdate: boolean = !!QQVersionConfigPath;
export const QQVersionConfig: QQVersionConfigType = isQuickUpdate ? JSON.parse(fs.readFileSync(QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
export const QQPackageInfo: QQPackageInfoType = JSON.parse(fs.readFileSync(QQPackageInfoPath).toString());
export const { appid: QQVersionAppid, qua: QQVersionQua } = getAppidV2();
//基础函数
export function getQQBuildStr() {
@@ -31,17 +31,18 @@ export function getQUAInternal() {
return systemPlatform === 'linux' ? `V1_LNX_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B` : `V1_WIN_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B`;
}
export function getAppidV2(): { appid: string, qua: string } {
let appidTbale = AppidTable as unknown as QQAppidTableType;
const appidTbale = AppidTable as unknown as QQAppidTableType;
try {
let data = appidTbale[getFullQQVesion()];
const data = appidTbale[getFullQQVesion()];
if (data) {
return data;
}
}
catch (e) {
log('[QQ版本兼容性检测] 版本兼容性不佳,可能会导致一些功能无法正常使用', e);
log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
}
// 以下是兜底措施
log(`[QQ版本兼容性检测] ${getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`);
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: getQUAInternal() };
}
// platform_type: 3,

View File

@@ -181,7 +181,7 @@ type Uri2LocalRes = {
isLocal: boolean
}
export async function uri2local(uri: string, fileName: string | null = null): Promise<Uri2LocalRes> {
export async function uri2local(UriOrPath: string, fileName: string | null = null): Promise<Uri2LocalRes> {
const res = {
success: false,
errMsg: '',
@@ -190,26 +190,29 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
path: '',
isLocal: false
};
if (!fileName) {
fileName = randomUUID();
}
let filePath = path.join(getTempDir(), fileName);
if (!fileName) fileName = randomUUID();
let filePath = path.join(getTempDir(), fileName);//临时目录
let url = null;
//区分path和uri
try {
url = new URL(uri);
} catch (e: any) {
res.errMsg = `uri ${uri} 解析失败,` + e.toString() + ` 可能${uri}不存在`;
if (fs.existsSync(UriOrPath)) url = new URL('file://' + UriOrPath);
} catch (error: any) { }
try {
url = new URL(UriOrPath);
} catch (error: any) { }
//验证url
if (!url) {
res.errMsg = `UriOrPath ${UriOrPath} 解析失败,可能${UriOrPath}不存在`;
return res;
}
// log("uri protocol", url.protocol, uri);
if (url.protocol == 'base64:') {
// base64转成文件
const base64Data = uri.split('base64://')[1];
const base64Data = UriOrPath.split('base64://')[1];
try {
const buffer = Buffer.from(base64Data, 'base64');
fs.writeFileSync(filePath, buffer);
} catch (e: any) {
res.errMsg = 'base64文件下载失败,' + e.toString();
return res;
@@ -218,7 +221,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
// 下载文件
let buffer: Buffer | null = null;
try {
buffer = await httpDownload(uri);
buffer = await httpDownload(UriOrPath);
} catch (e: any) {
res.errMsg = `${url}下载失败,` + e.toString();
return res;
@@ -252,6 +255,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
}
}
else {
// 26702执行forword file文件操作 不应该在这里乱来
// const cache = await dbUtil.getFileCacheByName(uri);
// if (cache) {
// filePath = cache.path;
@@ -259,7 +263,6 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
// filePath = uri;
// }
}
res.isLocal = true;
}
// else{

View File

@@ -360,12 +360,12 @@ export async function promisePipeline(promises: Promise<any>[], callback: (resul
callbackCalled = callback(result);
}
} catch (error) {
console.error("Error in promise pipeline:", error);
console.error('Error in promise pipeline:', error);
}
}
}
export function getQQVersionConfigPath(exePath: string = ""): string | undefined {
export function getQQVersionConfigPath(exePath: string = ''): string | undefined {
let configVersionInfoPath;
if (os.platform() !== 'linux') {
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');

View File

@@ -67,6 +67,9 @@ setTimeout(() => {
// }, 25000)
export class NTQQMsgApi {
static async FetchLongMsg(peer: Peer, msgId: string) {
return napCatCore.session.getMsgService().fetchLongMsg(peer, msgId);
}
static async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
//console.log(peer, msgSeq, emojiId, emojiType, count);
return napCatCore.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, "", false, 20)
@@ -100,7 +103,7 @@ export class NTQQMsgApi {
filterMsgFromTime: '0',
isReverseOrder: false,
isIncludeCurrent: true,
pageLimit: 1,
pageLimit: count,
});
return ret;
}

View File

@@ -731,10 +731,12 @@ export interface MultiForwardMsgElement {
}
export interface RawMessage {
parentMsgPeer: Peer;
parentMsgIdList:string[];
id?: number;//扩展字段 用于处理OB11 ID
guildId: string;
msgRandom: string;
// int32, 自己维护的消息id
id?: number;
msgId: string;

View File

@@ -11,6 +11,10 @@
"appid": 537237950,
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
},
"3.2.12-26740": {
"appid": 537237950,
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B"
},
"9.9.11-24815": {
"appid": 537226656,
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
@@ -38,5 +42,9 @@
"9.9.15-26702": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26740": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
}
}

View File

@@ -5,28 +5,28 @@ import { ActionName } from '../types';
import { NTQQMsgApi } from '@/core/apis';
import { MessageUnique } from '@/common/utils/MessageUnique';
const SchemaData = {
type: 'object',
properties: {
user_id: { type: 'string' },
group_id: { type: 'string' },
emojiId: { type: 'string' },
emojiType: { type: 'string' },
message_id: { type: ['string', 'number'] },
count: { type: 'number' }
},
required: ['emojiId', 'emojiType', 'message_id']
type: 'object',
properties: {
user_id: { type: 'string' },
group_id: { type: 'string' },
emojiId: { type: 'string' },
emojiType: { type: 'string' },
message_id: { type: ['string', 'number'] },
count: { type: 'number' }
},
required: ['emojiId', 'emojiType', 'message_id']
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class FetchEmojioLike extends BaseAction<Payload, any> {
actionName = ActionName.FetchEmojioLike;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
let msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
if(!msgIdPeer) throw new Error('消息不存在');
let msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count);
return ret;
}
actionName = ActionName.FetchEmojioLike;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
if(!msgIdPeer) throw new Error('消息不存在');
const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count);
return ret;
}
}

View File

@@ -20,8 +20,8 @@ export class OCRImage extends BaseAction<Payload, any> {
actionName = ActionName.OCRImage;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
const { path, isLocal, errMsg } = (await uri2local(payload.image));
if (errMsg) {
const { path, isLocal, errMsg,success } = (await uri2local(payload.image));
if (!success) {
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
}
if (path) {

View File

@@ -26,8 +26,8 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
};
}
protected async _handle(payload: Payload): Promise<any> {
const { path, isLocal, errMsg } = (await uri2local(payload.file));
if (errMsg) {
const { path, isLocal, errMsg,success } = (await uri2local(payload.file));
if (!success) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
}
if (path) {

View File

@@ -24,8 +24,8 @@ export default class SetAvatar extends BaseAction<Payload, null> {
};
}
protected async _handle(payload: Payload): Promise<null> {
const { path, isLocal, errMsg } = (await uri2local(payload.file));
if (errMsg) {
const { path, isLocal, errMsg,success } = (await uri2local(payload.file));
if (!success) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
}
if (path) {

View File

@@ -37,7 +37,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
peerUid: group.groupCode
};
let msgList: RawMessage[];
if (!payload.message_seq) {
if (!payload.message_seq || payload.message_seq === 0) {
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, count)).msgList;
} else {
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0';

View File

@@ -7,7 +7,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: [ 'number' , 'string' ] },
group_id: { type: ['number', 'string'] },
content: { type: 'string' },
image: { type: 'string' },
pinned: { type: 'number' },
@@ -24,8 +24,8 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
let UploadImage: { id: string, width: number, height: number } | undefined = undefined;
if (payload.image) {
//公告图逻辑
const { errMsg, path, isLocal } = (await uri2local(payload.image));
if (errMsg) {
const { errMsg, path, isLocal, success } = (await uri2local(payload.image));
if (!success) {
throw `群公告${payload.image}设置失败,image字段可能格式不正确`;
}
if (!path) {

View File

@@ -34,7 +34,7 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
file = `file://${file}`;
}
const downloadResult = await uri2local(file);
if (downloadResult.errMsg) {
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id);

View File

@@ -41,7 +41,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
file = `file://${file}`;
}
const downloadResult = await uri2local(file);
if (downloadResult.errMsg) {
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name);

View File

@@ -19,7 +19,7 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
actionName = ActionName.GetGroupList;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
let groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
const groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
return OB11Constructor.groups(groupList);
}
}

View File

@@ -26,11 +26,11 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
let uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
if (!uid) {
throw (`Uin2Uid Error ${payload.user_id}不存在`);
}
let member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
const member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
if (!member) {
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
}
@@ -44,7 +44,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
const date = Math.round(Date.now() / 1000);
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
if (!requireMinNTQQBuild('26702')) {
let SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
const SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
let isPrivilege = false;
if (SelfInfoInGroup) {
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;

View File

@@ -28,7 +28,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
if (requireMinNTQQBuild('26702')) {
let V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
const V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
if (V2Data) {
return OB11Constructor.groupMember(payload.group_id.toString(), V2Data);
} else {

View File

@@ -30,7 +30,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
if (!group) {
throw (`${payload.group_id}不存在`);
}
let groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
const groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
let _groupMembers = Array.from(groupMembers.values())
.map(item => { return OB11Constructor.groupMember(group.groupCode, item); });

View File

@@ -3,12 +3,15 @@ import { ActionName } from '../types';
import BaseAction from '../BaseAction';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { sleep } from '@/common/utils/helper';
import { NTEventDispatch } from '@/common/utils/EventTask';
import { NodeIKernelMsgListener } from '@/core';
const SchemaData = {
type: 'object',
properties: {
message_id: {
oneOf:[
oneOf: [
{ type: 'number' },
{ type: 'string' }
]
@@ -23,9 +26,27 @@ class DeleteMsg extends BaseAction<Payload, void> {
actionName = ActionName.DeleteMsg;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
const msg = await MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id));
const msg = MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id));
if (msg) {
let ret = NTEventDispatch.RegisterListen<NodeIKernelMsgListener['onMsgInfoListUpdate']>
(
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
5000,
(msgs) => {
if (msgs.find(m => m.msgId === msg.MsgId && m.recallTime !== '0')) {
return true;
}
return false;
}
).catch(e => new Promise<undefined>((resolve, reject) => { resolve(undefined) }));
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
let data = await ret;
if (!data) {
throw new Error('Recall failed');
}
//await sleep(100);
//await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId]);
}
}
}

View File

@@ -27,9 +27,9 @@ async function handleOb11FileLikeMessage(
{ deleteAfterSentFiles }: MessageContext
) {
//有的奇怪的框架将url作为参数 而不是file 此时优先url
const { path, isLocal, fileName, errMsg } = (await uri2local(inputdata?.url || inputdata.file));
const { path, isLocal, fileName, errMsg,success } = (await uri2local(inputdata?.url || inputdata.file));
if (errMsg) {
if (!success) {
logError('文件下载失败', errMsg);
throw Error('文件下载失败' + errMsg);
}

View File

@@ -103,5 +103,5 @@ export enum ActionName {
FetchCustomFace = 'fetch_custom_face',
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
TestApi01 = 'test_api_01',
FetchEmojioLike = "fetch_emoji_like"
FetchEmojioLike = 'fetch_emoji_like'
}

View File

@@ -18,12 +18,12 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
actionName = ActionName.GetRecentContact;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
let ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
let data = await Promise.all(ret.info.changedList.map(async (t) => {
let FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
const ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
const data = await Promise.all(ret.info.changedList.map(async (t) => {
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
if (FastMsg.msgList.length > 0) {
//扩展ret.info.changedList
let lastestMsg = await OB11Constructor.message(FastMsg.msgList[0]);
const lastestMsg = await OB11Constructor.message(FastMsg.msgList[0]);
return {
lastestMsg: lastestMsg,
peerUin: t.peerUin,

View File

@@ -65,7 +65,7 @@ export class OB11Constructor {
real_id: msg.id!,
message_type: msg.chatType == ChatType.group ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin!),
user_id: parseInt(msg.senderUin || '0'),
nickname: msg.sendNickName,
card: msg.sendMemberName || '',
},
@@ -165,8 +165,10 @@ export class OB11Constructor {
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
}
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
if (msg.peerUin == '284840486') {
//合并消息内侧 消息具体定位不到
}
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
throw new Error('回复消息消息验证失败');
}
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString();
@@ -221,6 +223,9 @@ export class OB11Constructor {
const videoElement: VideoElement = element.videoElement;
//读取视频链接并兜底
let videoUrl;//Array
if (msg.peerUin = '284840486') {
//合并消息内部 应该进行特殊处理
}
try {
videoUrl = await NTQQFileApi.getVideoUrl({
chatType: msg.chatType,
@@ -327,6 +332,27 @@ export class OB11Constructor {
else if (element.multiForwardMsgElement) {
message_data['type'] = OB11MessageDataType.forward;
message_data['data']['id'] = msg.msgId;
const ParentMsgPeer = msg.parentMsgPeer ?? { chatType: msg.chatType, guildId: '', peerUid: msg.peerUid };
//判断是否在合并消息内
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
//首次列表不存在则开始创建
msg.parentMsgIdList.push(msg.msgId);
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
//加入自身MsgId
let MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
//拉取下级消息
if (!MultiMsgs) continue;
//拉取失败则跳过
message_data['data']['content'] = [];
for (let MultiMsg of MultiMsgs) {
//对每条拉取的消息传递ParentMsgPeer修正Peer
MultiMsg.parentMsgPeer = ParentMsgPeer;
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId);//该ID仅用查看 无法调用
let msgList = await OB11Constructor.message(MultiMsg);
message_data['data']['content'].push(msgList);
//console.log("合并消息", msgList);
}
}
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);

View File

@@ -2,7 +2,7 @@ import { OB11Message } from '@/onebot11/types';
import { log } from '@/common/utils/log';
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
import exp from 'constants';
import { Group } from '@/core';
import { Group, NTQQMsgApi } from '@/core';
import chalk from 'chalk';
const spSegColor = chalk.blue;// for special segment
@@ -24,8 +24,13 @@ export async function logMessage(ob11Message: OB11Message) {
}
}
if (ob11Message.message_type === 'group') {
group = await getGroup(ob11Message.group_id!);
prefix += `群[${group?.groupName}(${ob11Message.group_id})] `;
if (ob11Message.group_id == 284840486) {
group = await getGroup(ob11Message.group_id!);
prefix += `转发消息[外部来源] `;
} else {
group = await getGroup(ob11Message.group_id!);
prefix += `群[${group?.groupName}(${ob11Message.group_id})] `;
}
}
let msgChain = '';
if (Array.isArray(ob11Message.message)) {
@@ -66,6 +71,13 @@ export async function logMessage(ob11Message: OB11Message) {
else if (segment.type === 'video') {
msgParts.push(spSegColor(`[视频|${segment.data.url}]`));
}
else if (segment.type === 'forward') {
msgParts.push(spSegColor(`[转发|${segment.data.id}|消息开始]`));
segment.data.content.forEach((msg) => {
logMessage(msg);
});
msgParts.push(spSegColor(`[转发|${segment.data.id}|消息结束]`));
}
else {
msgParts.push(spSegColor(`[未实现|${JSON.stringify(segment)}]`));
}

View File

@@ -11,7 +11,7 @@ class OB11HTTPServer extends HttpServerBase {
name = 'OneBot V11 server';
handleFailed(res: Response, payload: any, e: Error) {
res.send(OB11Response.error(e?.stack?.toString() || e.message || "Error Handle", 200));
res.send(OB11Response.error(e?.stack?.toString() || e.message || 'Error Handle', 200));
}
protected listen(port: number, host: string) {

View File

@@ -181,7 +181,8 @@ export interface OB11MessageMarkdown {
export interface OB11MessageForward {
type: OB11MessageDataType.forward
data: {
id: string
id: string,
content: OB11Message[]
}
}

View File

@@ -1 +1 @@
export const version = '1.8.0';
export const version = '1.8.1';

View File

@@ -29,7 +29,7 @@ async function onSettingWindowCreated(view: Element) {
SettingItem(
'<span id="napcat-update-title">Napcat</span>',
undefined,
SettingButton('V1.8.0', 'napcat-update-button', 'secondary')
SettingButton('V1.8.1', 'napcat-update-button', 'secondary')
),
]),
SettingList([

View File

@@ -163,7 +163,7 @@ async function onSettingWindowCreated(view) {
SettingItem(
'<span id="napcat-update-title">Napcat</span>',
void 0,
SettingButton("V1.8.0", "napcat-update-button", "secondary")
SettingButton("V1.8.1", "napcat-update-button", "secondary")
)
]),
SettingList([