Compare commits

...

13 Commits

Author SHA1 Message Date
手瓜一十雪
3bb12e3f45 fix: #572 2024-11-28 10:56:57 +08:00
手瓜一十雪
1dc2f7e5a2 style: lint 2024-11-28 10:46:14 +08:00
手瓜一十雪
2531b08538 refactor: 提高解析兼容 2024-11-28 10:41:51 +08:00
手瓜一十雪
9fcfb5493c fix: #571 2024-11-28 10:27:04 +08:00
Mlikiowa
4576354c51 release: v4.2.3 2024-11-28 01:54:43 +00:00
手瓜一十雪
1dcf2ef0c6 fix: error handle 2024-11-28 09:53:50 +08:00
Mlikiowa
3642c65e8c release: v4.2.2 2024-11-27 12:39:03 +00:00
手瓜一十雪
40e105994a fix: pic size 2024-11-27 20:38:39 +08:00
Mlikiowa
f2ee973882 release: v4.2.1 2024-11-27 11:07:43 +00:00
手瓜一十雪
3aa30792bf Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-11-27 19:07:11 +08:00
手瓜一十雪
6e336fa78e fix: 合并丢失 2024-11-27 19:07:01 +08:00
Mlikiowa
900027a6b7 release: v4.2.0 2024-11-27 10:55:36 +00:00
手瓜一十雪
38bdca2409 fix: qrcode login 2024-11-27 18:51:54 +08:00
14 changed files with 100 additions and 79 deletions

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "4.1.21", "version": "4.2.3",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -58,6 +58,9 @@ let qrcodeUrl: string = '';
const selectAccount = async (accountName: string): Promise<void> => { const selectAccount = async (accountName: string): Promise<void> => {
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName); const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
if (result) { if (result) {
if (heartBeatTimer) {
clearInterval(heartBeatTimer);
}
await MessagePlugin.success('登录成功即将跳转'); await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' }); await router.push({ path: '/dashboard/basic-info' });
} else { } else {
@@ -85,10 +88,11 @@ const HeartBeat = async (): Promise<void> => {
if (heartBeatTimer) { if (heartBeatTimer) {
clearInterval(heartBeatTimer); clearInterval(heartBeatTimer);
} }
//判断是否已经调转 // //判断是否已经调转
if (router.currentRoute.value.path !== '/dashboard/basic-info') { // if (router.currentRoute.value.path !== '/dashboard/basic-info') {
return; // return;
} // }
await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' }); await router.push({ path: '/dashboard/basic-info' });
} else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) { } else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) {
qrcodeUrl = isLogined.qrcodeurl; qrcodeUrl = isLogined.qrcodeurl;

View File

@@ -12,6 +12,9 @@
<t-form-item label="启用本地文件到URL" name="enableLocalFile2Url" class="form-item"> <t-form-item label="启用本地文件到URL" name="enableLocalFile2Url" class="form-item">
<t-switch v-model="otherConfig.enableLocalFile2Url" /> <t-switch v-model="otherConfig.enableLocalFile2Url" />
</t-form-item> </t-form-item>
<t-form-item label="启用上报解析合并消息" name="parseMultMsg" class="form-item">
<t-switch v-model="otherConfig.parseMultMsg" />
</t-form-item>
</t-form> </t-form>
<div class="button-container"> <div class="button-container">
<t-button @click="saveConfig">保存</t-button> <t-button @click="saveConfig">保存</t-button>
@@ -30,6 +33,7 @@ import { QQLoginManager } from '@/backend/shell';
const otherConfig = ref<Partial<OneBotConfig>>({ const otherConfig = ref<Partial<OneBotConfig>>({
musicSignUrl: '', musicSignUrl: '',
enableLocalFile2Url: false, enableLocalFile2Url: false,
parseMultMsg: true
}); });
const labelAlign = ref<string>(); const labelAlign = ref<string>();
@@ -59,6 +63,7 @@ const loadConfig = async () => {
if (userConfig) { if (userConfig) {
otherConfig.value.musicSignUrl = userConfig.musicSignUrl; otherConfig.value.musicSignUrl = userConfig.musicSignUrl;
otherConfig.value.enableLocalFile2Url = userConfig.enableLocalFile2Url; otherConfig.value.enableLocalFile2Url = userConfig.enableLocalFile2Url;
otherConfig.value.parseMultMsg = userConfig.parseMultMsg;
} }
} catch (error) { } catch (error) {
console.error('Error loading config:', error); console.error('Error loading config:', error);
@@ -71,6 +76,7 @@ const saveConfig = async () => {
if (userConfig) { if (userConfig) {
userConfig.musicSignUrl = otherConfig.value.musicSignUrl || ''; userConfig.musicSignUrl = otherConfig.value.musicSignUrl || '';
userConfig.enableLocalFile2Url = otherConfig.value.enableLocalFile2Url ?? false; userConfig.enableLocalFile2Url = otherConfig.value.enableLocalFile2Url ?? false;
userConfig.parseMultMsg = otherConfig.value.parseMultMsg ?? true;
const success = await setOB11Config(userConfig); const success = await setOB11Config(userConfig);
if (success) { if (success) {
MessagePlugin.success('配置保存成功'); MessagePlugin.success('配置保存成功');

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "4.1.21", "version": "4.2.3",
"scripts": { "scripts": {
"build:framework": "npm run build:webui && vite build --mode framework || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1",
"build:shell": "npm run build:webui && vite build --mode shell || exit 1", "build:shell": "npm run build:webui && vite build --mode shell || exit 1",

View File

@@ -190,16 +190,27 @@ export async function checkUriType(Uri: string) {
}, Uri); }, Uri);
if (LocalFileRet) return LocalFileRet; if (LocalFileRet) return LocalFileRet;
const OtherFileRet = await solveProblem((uri: string) => { const OtherFileRet = await solveProblem((uri: string) => {
//再判断是否是Http // 再判断是否是Http
if (uri.startsWith('http://') || uri.startsWith('https://')) { if (uri.startsWith('http:') || uri.startsWith('https:')) {
return { Uri: uri, Type: FileUriType.Remote }; return { Uri: uri, Type: FileUriType.Remote };
} }
//再判断是否是Base64 // 再判断是否是Base64
if (uri.startsWith('base64://')) { if (uri.startsWith('base64:')) {
return { Uri: uri, Type: FileUriType.Base64 }; return { Uri: uri, Type: FileUriType.Base64 };
} }
if (uri.startsWith('file://')) { // 默认file://
let filePath: string = uri.slice(7); if (uri.startsWith('file:')) {
// 兼容file:///
// file:///C:/1.jpg
if (uri.startsWith('file:///') && process.platform === 'win32') {
const filePath: string = uri.slice(8);
return { Uri: filePath, Type: FileUriType.Local };
}
// 处理默认规范
// file://C:\1.jpg
// file:///test/1.jpg
const filePath: string = uri.slice(7);
return { Uri: filePath, Type: FileUriType.Local }; return { Uri: filePath, Type: FileUriType.Local };
} }
if (uri.startsWith('data:')) { if (uri.startsWith('data:')) {

View File

@@ -1 +1 @@
export const napCatVersion = '4.1.21'; export const napCatVersion = '4.2.3';

View File

@@ -3,12 +3,12 @@ import { PicType } from '../types';
export async function getFileTypeForSendType(picPath: string): Promise<PicType> { export async function getFileTypeForSendType(picPath: string): Promise<PicType> {
const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg'; const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg';
const picTypeMap: { [key: string]: PicType } = { const picTypeMap: { [key: string]: PicType } = {
'webp': PicType.NEWPIC_WEBP, //'webp': PicType.NEWPIC_WEBP,
'gif': PicType.NEWPIC_GIF, 'gif': PicType.NEWPIC_GIF,
'png': PicType.NEWPIC_APNG, // 'png': PicType.NEWPIC_APNG,
'jpg': PicType.NEWPIC_JPEG, // 'jpg': PicType.NEWPIC_JPEG,
'jpeg': PicType.NEWPIC_JPEG, // 'jpeg': PicType.NEWPIC_JPEG,
'bmp': PicType.NEWPIC_BMP, // 'bmp': PicType.NEWPIC_BMP,
}; };
return picTypeMap[fileTypeResult] ?? PicType.NEWPIC_JPEG; return picTypeMap[fileTypeResult] ?? PicType.NEWPIC_JPEG;
} }

View File

@@ -62,7 +62,7 @@ export const GroupChange = {
operatorUid: ProtoField(5, ScalarType.STRING, true), operatorUid: ProtoField(5, ScalarType.STRING, true),
increaseType: ProtoField(6, ScalarType.UINT32), increaseType: ProtoField(6, ScalarType.UINT32),
field7: ProtoField(7, ScalarType.BYTES, true), field7: ProtoField(7, ScalarType.BYTES, true),
} };
export const PushMsgBody = { export const PushMsgBody = {
responseHead: ProtoField(1, () => ResponseHead), responseHead: ProtoField(1, () => ResponseHead),

View File

@@ -315,7 +315,7 @@ export class OneBotGroupApi {
} else if (grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) { } else if (grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) {
return await this.parseEssenceMsg(msg, grayTipElement.jsonGrayTipElement.jsonStr); return await this.parseEssenceMsg(msg, grayTipElement.jsonGrayTipElement.jsonStr);
} else { } else {
return await this.parseOtherJsonEvent(msg, grayTipElement.jsonGrayTipElement.jsonStr, this.core.context) return await this.parseOtherJsonEvent(msg, grayTipElement.jsonGrayTipElement.jsonStr, this.core.context);
} }
} }
return undefined; return undefined;

View File

@@ -673,7 +673,7 @@ export class OneBotMsgApi {
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
if (grayTipElement.jsonGrayTipElement.busiId == 1061) { if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement); const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement);
if (PokeEvent) { return PokeEvent }; if (PokeEvent) { return PokeEvent; };
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') { } else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid))); return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
} }
@@ -841,14 +841,14 @@ export class OneBotMsgApi {
} }
private async convertArrayToStringMessage(originMsg: OB11Message): Promise<OB11Message> { private async convertArrayToStringMessage(originMsg: OB11Message): Promise<OB11Message> {
let msg = structuredClone(originMsg); const msg = structuredClone(originMsg);
msg.message_format = 'string'; msg.message_format = 'string';
msg.message = msg.raw_message; msg.message = msg.raw_message;
return msg; return msg;
} }
async importArrayTostringMsg(originMsg: OB11Message) { async importArrayTostringMsg(originMsg: OB11Message) {
let msg = structuredClone(originMsg); const msg = structuredClone(originMsg);
msg.message_format = 'string'; msg.message_format = 'string';
msg.message = msg.raw_message; msg.message = msg.raw_message;
return msg; return msg;
@@ -924,7 +924,7 @@ export class OneBotMsgApi {
fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', e)); fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', e));
} }
} catch (error) { } catch (error) {
this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', (error as Error).message) this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', (error as Error).message);
} }
}); });
}, 60000); }, 60000);
@@ -972,16 +972,7 @@ export class OneBotMsgApi {
async parseSysMessage(msg: number[]) { async parseSysMessage(msg: number[]) {
// Todo Refactor // Todo Refactor
// const sysMsg = decodeSysMessage(Uint8Array.from(msg)); const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg));
// if (sysMsg.msgSpec.length === 0) {
// return;
// }
// const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
// if (msgType === 528 && subType === 39 && subSubType === 39) {
// if (!sysMsg.bodyWrapper) return;
// return await this.obContext.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
// }
let SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg));
if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) {
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
console.log(JSON.stringify(groupChange)); console.log(JSON.stringify(groupChange));
@@ -994,7 +985,6 @@ export class OneBotMsgApi {
); );
} else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) {
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
// console.log(JSON.stringify(groupChange),JSON.stringify(SysMessage));
return new OB11GroupDecreaseEvent( return new OB11GroupDecreaseEvent(
this.core, this.core,
groupChange.groupUin, groupChange.groupUin,
@@ -1002,7 +992,10 @@ export class OneBotMsgApi {
groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0, groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0,
this.groupChangDecreseType2String(groupChange.decreaseType), this.groupChangDecreseType2String(groupChange.decreaseType),
); );
} else if (SysMessage.contentHead.type == 528 && SysMessage.contentHead.subType == 39 && SysMessage.body?.msgContent) {
return await this.obContext.apis.UserApi.parseLikeEvent(SysMessage.body?.msgContent);
} }
/* /*
if (msgType === 732 && subType === 16 && subSubType === 16) { if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7))); const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));

View File

@@ -13,7 +13,7 @@ export class OneBotUserApi {
} }
async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> { async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> {
const likeTip = decodeProfileLikeTip(Uint8Array.from(wrappedBody)); const likeTip = decodeProfileLikeTip(wrappedBody);
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return; if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
this.core.context.logger.logDebug("收到点赞通知消息"); this.core.context.logger.logDebug("收到点赞通知消息");
const likeMsg = likeTip.content.msg; const likeMsg = likeTip.content.msg;

View File

@@ -213,7 +213,7 @@ export class NapCatOneBot11Adapter {
if (networkChange === OB11NetworkReloadType.NetWorkClose) { if (networkChange === OB11NetworkReloadType.NetWorkClose) {
await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]); await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]);
} }
} else if(adapterConfig.enable) { } else if (adapterConfig.enable) {
const newAdapter = new adapterClass(adapterConfig.name, adapterConfig, this.core, this.actions); const newAdapter = new adapterClass(adapterConfig.name, adapterConfig, this.core, this.actions);
await this.networkManager.registerAdapterAndOpen(newAdapter); await this.networkManager.registerAdapterAndOpen(newAdapter);
} }
@@ -266,23 +266,30 @@ export class NapCatOneBot11Adapter {
}; };
msgListener.onAddSendMsg = async (msg) => { msgListener.onAddSendMsg = async (msg) => {
try {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) {
await this.core.eventWrapper.registerListen('NodeIKernelMsgListener/onMsgInfoListUpdate', (msgList: RawMessage[]) => { const [updatemsgs] = await this.core.eventWrapper.registerListen('NodeIKernelMsgListener/onMsgInfoListUpdate', (msgList: RawMessage[]) => {
const report = msgList.find((e) => const report = msgList.find((e) =>
e.senderUin == this.core.selfInfo.uin && e.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && e.msgId === msg.msgId e.senderUin == this.core.selfInfo.uin && e.sendStatus !== SendStatusType.KSEND_STATUS_SENDING && e.msgId === msg.msgId
); );
return !!report; return !!report;
}, 1, 300); }, 1, 10 * 60 * 1000);
msg.id = MessageUnique.createUniqueMsgId( // 10分钟 超时
const updatemsg = updatemsgs.find((e) => e.msgId === msg.msgId);
if (updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS || updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS_NOSEQ) {
updatemsg.id = MessageUnique.createUniqueMsgId(
{ {
chatType: msg.chatType, chatType: updatemsg.chatType,
peerUid: msg.peerUid, peerUid: updatemsg.peerUid,
guildId: '', guildId: '',
}, },
msg.msgId updatemsg.msgId
); );
//此时上报的seq不是对的 不过对onebot业务无影响 this.emitMsg(updatemsg);
this.emitMsg(msg); }
}
} catch (error) {
this.context.logger.logError('处理发送消息失败', error);
} }
}; };
msgListener.onMsgRecall = async (chatType: ChatType, uid: string, msgSeq: string) => { msgListener.onMsgRecall = async (chatType: ChatType, uid: string, msgSeq: string) => {
@@ -291,10 +298,10 @@ export class NapCatOneBot11Adapter {
peerUid: uid, peerUid: uid,
guildId: '' guildId: ''
}; };
let msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS); const msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS);
let element = msg?.elements[0]; const element = msg?.elements[0];
if (msg && element) { if (msg && element) {
let recallEvent = await this.emitRecallMsg(msg, element); const recallEvent = await this.emitRecallMsg(msg, element);
try { try {
if (recallEvent) { if (recallEvent) {
await this.networkManager.emitEvent(recallEvent); await this.networkManager.emitEvent(recallEvent);
@@ -303,7 +310,7 @@ export class NapCatOneBot11Adapter {
this.context.logger.logError('处理消息撤回失败', e); this.context.logger.logError('处理消息撤回失败', e);
} }
} }
} };
msgListener.onKickedOffLine = async (kick) => { msgListener.onKickedOffLine = async (kick) => {
const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc); const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc);
this.networkManager this.networkManager
@@ -657,7 +664,7 @@ export class NapCatOneBot11Adapter {
private async emitRecallMsg(message: RawMessage, element: MessageElement) { private async emitRecallMsg(message: RawMessage, element: MessageElement) {
const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' }; const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' };
let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) ?? MessageUnique.createUniqueMsgId(peer, message.msgId); const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) ?? MessageUnique.createUniqueMsgId(peer, message.msgId);
if (message.chatType == ChatType.KCHATTYPEC2C) { if (message.chatType == ChatType.KCHATTYPEC2C) {
return await this.emitFriendRecallMsg(message, oriMessageId, element); return await this.emitFriendRecallMsg(message, oriMessageId, element);
} else if (message.chatType == ChatType.KCHATTYPEGROUP) { } else if (message.chatType == ChatType.KCHATTYPEGROUP) {
@@ -677,7 +684,7 @@ export class NapCatOneBot11Adapter {
private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid; const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if (!operatorUid) return undefined; if (!operatorUid) return undefined;
let operatorId = message.senderUin ?? await this.core.apis.UserApi.getUinByUidV2(operatorUid); const operatorId = message.senderUin ?? await this.core.apis.UserApi.getUinByUidV2(operatorUid);
return new OB11GroupRecallNoticeEvent( return new OB11GroupRecallNoticeEvent(
this.core, this.core,
+message.peerUin, +message.peerUin,

View File

@@ -102,7 +102,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
if (req.path === '' || req.path === '/') { if (req.path === '' || req.path === '/') {
const hello = OB11Response.ok({}); const hello = OB11Response.ok({});
hello.message = 'NapCat4 Ss Running'; hello.message = 'NapCat4 Ss Running';
return res.json(hello) return res.json(hello);
} }
const actionName = req.path.split('/')[1]; const actionName = req.path.split('/')[1];
const action = this.actions.get(actionName); const action = this.actions.get(actionName);