Compare commits

...

16 Commits

Author SHA1 Message Date
手瓜一十雪
63dd98d2df release: 2.0.30 2024-08-16 21:44:34 +08:00
手瓜一十雪
caaa6ed506 feat: support SetInputStatus 2024-08-16 20:35:05 +08:00
手瓜一十雪
caf23792cb Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-16 13:01:38 +08:00
手瓜一十雪
e430db20aa release: 2.0.29 2024-08-16 13:01:30 +08:00
手瓜一十雪
6fc5da9b67 Merge pull request #267 from Fripine/fix/OB11GroupRequestEvent
fix: wrong user_id in GroupRequestEvent
2024-08-16 13:00:32 +08:00
Fripine
f428e57724 fix: GroupRequestEvent 2024-08-16 12:57:42 +08:00
手瓜一十雪
14ab21fe9a Merge pull request #266 from Fripine/fix/OB11FriendRequestEvent
fix: wrong comment words in FriendRequestEvent
2024-08-16 12:44:33 +08:00
手瓜一十雪
85626e19da release: 2.0.28 2024-08-16 12:44:11 +08:00
Fripine
8712160fd7 fix: FriendRequestEvent 2024-08-16 12:21:33 +08:00
手瓜一十雪
75b33f5cb1 chore: fix 2024-08-16 12:16:21 +08:00
手瓜一十雪
f5e8ede847 release: 2.0.27 2024-08-16 10:49:22 +08:00
手瓜一十雪
3b3f684a8c chore: 清除废弃代码 2024-08-16 09:52:50 +08:00
手瓜一十雪
a78b60d40e chore: 进一步识别会话 2024-08-16 09:37:36 +08:00
手瓜一十雪
9ff06a3c44 release: 2.0.26 2024-08-15 23:08:31 +08:00
手瓜一十雪
8532dc486c fix: error 2024-08-15 23:07:59 +08:00
手瓜一十雪
861340f4bf release: 2.0.25 2024-08-15 22:17:58 +08:00
18 changed files with 178 additions and 100 deletions

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "2.0.24",
"version": "2.0.30",
"scripts": {
"build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell",

View File

@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
export const napcat_version = '2.0.24';
export const napcat_version = '2.0.30';
export class NapCatPathWrapper {
binaryPath: string;

View File

@@ -232,14 +232,21 @@ export async function uri2local(dir: string, uri: string, filename: string | und
//接下来都要有文件名
if (!filename) filename = randomUUID();
//解析Http和Https协议
if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) {
filename = pathInfo.name;
if (pathInfo.ext) {
filename += pathInfo.ext;
}
}
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
const fileExt = path.extname(HandledUri);
const fileName = filename + fileExt;
const filePath = path.join(dir, fileName);
const filePath = path.join(dir, filename);
const buffer = await httpDownload(HandledUri);
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: fileName, ext: fileExt, path: filePath, isLocal: true };
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
}
//解析Base64
if (UriType == FileUriType.Base64) {

File diff suppressed because one or more lines are too long

View File

@@ -16,6 +16,9 @@ export class NTQQMsgApi {
return this.context.session.getMsgService().fetchLongMsg(peer, msgId);
}
async sendShowInputStatusReq(peer: Peer, eventType: number) {
return this.context.session.getMsgService().sendShowInputStatusReq(peer.chatType, eventType, peer.peerUid);
}
async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
//console.log(peer, msgSeq, emojiId, emojiType, count);
//注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged M likiowa
@@ -93,7 +96,22 @@ export class NTQQMsgApi {
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
}
async getMsgExBySeq(peer: Peer, msgSeq: string) {
const DateNow = Math.floor(Date.now() / 1000);
const filterMsgFromTime = (DateNow - 300).toString();
const filterMsgToTime = DateNow.toString();
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
filterMsgType: [],
filterSendersUid: [],
filterMsgToTime: filterMsgToTime,
filterMsgFromTime: filterMsgFromTime,
isReverseOrder: false,
isIncludeCurrent: true,
pageLimit: 100,
});
return ret;
}
async setMsgRead(peer: Peer) {
return this.context.session.getMsgService().setMsgRead(peer);
}
@@ -102,18 +120,18 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent<
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
>(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1,
5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
>(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1,
5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true;
},
GroupCode,
params,
);
return true;
},
GroupCode,
params,
);
return data[1].item;
}
@@ -164,24 +182,24 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
timeout,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true;
}
>(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
timeout,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true;
}
return false;
},
'0',
peer,
msgElements,
new Map(),
);
}
return false;
},
'0',
peer,
msgElements,
new Map(),
);
const retMsg = data[1].find(msgRecord => {
if (msgRecord.guildId === msgId) {
return true;
@@ -209,25 +227,25 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent<
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
5000,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
}
>(
'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
5000,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
}
return false;
},
msgInfos,
srcPeer,
destPeer,
[],
new Map(),
);
}
return false;
},
msgInfos,
srcPeer,
destPeer,
[],
new Map(),
);
for (const msg of data[1]) {
const arkElement = msg.elements.find(ele => ele.arkElement);
if (!arkElement) {

View File

@@ -234,11 +234,6 @@ export class NTQQUserApi {
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
}
async getUserDetailInfoByUin(Uin: string) {
return this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUin>>
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
}
async forceFetchClientKey() {
return await this.context.session.getTicketService().forceFetchClientKey('');
}

View File

@@ -0,0 +1,44 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { ChatType, Peer } from '@/core';
const SchemaData = {
type: 'object',
properties: {
eventType: { type: 'string' },
group_id: { type: 'string' },
user_id: { type: 'string' }
},
required: ['eventType'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class SetInputStatus extends BaseAction<Payload, any> {
actionName = ActionName.SetInputStatus;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
let peer: Peer;
if (payload.group_id) {
peer = {
chatType: ChatType.group,
peerUid: payload.group_id
}
} else if (payload.user_id) {
let uid = await NTQQUserApi.getUidByUinV2(payload.user_id);
if (!uid) throw new Error('uid is empty');
peer = {
chatType: ChatType.friend,
peerUid: uid
}
} else {
throw new Error('请指定 group_id 或 user_id');
}
const ret = await NTQQMsgApi.sendShowInputStatusReq(peer, parseInt(payload.eventType));
return ret;
}
}

View File

@@ -21,20 +21,23 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
async _handle(payload: Payload): Promise<OB11User> {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const user_id = payload.user_id.toString();
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
const uid = (await NTQQUserApi.getUidByUinV2(user_id))!;
if (!uid || uid.indexOf('*') != -1) {
const ret = {
...extendData,
user_id: parseInt(extendData.info.uin) || 0,
nickname: extendData.info.nick,
...extendData.detail.simpleInfo.coreInfo,
...extendData.detail.commonExt,
...extendData.detail.simpleInfo.baseInfo,
...extendData.detail.simpleInfo.relationFlags,
user_id: parseInt(extendData.detail.uin) || 0,
nickname: extendData.detail.simpleInfo.coreInfo.nick,
sex: OB11UserSex.unknown,
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
qid: extendData.info.qid,
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
age: extendData.detail.simpleInfo.baseInfo.age || 0,
qid: extendData.detail.simpleInfo.baseInfo.qid,
level: calcQQLevel(extendData.detail.commonExt.qqLevel) || 0,
login_days: 0,
uid: '',
};
uid: ''
};
return ret;
}
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };

View File

@@ -78,6 +78,7 @@ import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot';
import GetGuildProfile from './guild/GetGuildProfile';
import SetModelShow from './go-cqhttp/SetModelShow';
import { SetInputStatus } from './extends/SetInputStatus';
export type ActionMap = Map<string, BaseAction<any, any>>;
@@ -165,6 +166,7 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
new GoCQHTTPUploadPrivateFile(onebotContext, coreContext),
new GetGuildProfile(onebotContext, coreContext),
new SetModelShow(onebotContext, coreContext),
new SetInputStatus(onebotContext, coreContext),
];
const actionMap = new Map();
for (const action of actionHandlers) {

View File

@@ -56,7 +56,7 @@ const _handlers: {
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
// then the qq is a group member
// Mlikiowa V2.0.24 Refactor Todo
// Mlikiowa V2.0.30 Refactor Todo
const uid = await coreContext.apis.UserApi.getUidByUinV2(`${atQQ}`);
if (!uid) throw new Error('Get Uid Error');
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, '');
@@ -161,7 +161,7 @@ const _handlers: {
} else {
postData = data;
}
// Mlikiowa V2.0.24 Refactor Todo
// Mlikiowa V2.0.30 Refactor Todo
const signUrl = obContext.configLoader.configData.musicSignUrl;
if (!signUrl) {
if (data.type === 'qq') {

View File

@@ -88,11 +88,9 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
// This function determines the type of message by the existence of user_id / group_id,
// not message_type.
// This redundant design of Ob11 here should be blamed.
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const NTQQUserApi = coreContext.apis.UserApi;
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
const group = (await NTQQGroupApi.getGroups()).find(e => e.groupCode == payload.group_id?.toString());
return {
chatType: ChatType.group,
peerUid: payload.group_id.toString(),
@@ -134,21 +132,17 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
};
}
// if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) {
// return { valid: false, message: `群${payload.group_id}不存在` };
// }
if (payload.user_id && payload.message_type !== 'group') {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(uid!);
// 此处有问题
if (!isBuddy) {
//return { valid: false, message: '异常消息' };
}
if (!isBuddy) {}
}
return { valid: true };
}
async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
if (payload.message_type === 'group') this.contextMode = ContextMode.Group;
if (payload.message_type === 'private') this.contextMode = ContextMode.Private;
const peer = await createContext(this.CoreContext, payload, this.contextMode);
const messages = normalize(

View File

@@ -106,5 +106,6 @@ export enum ActionName {
TestApi01 = 'test_api_01',
FetchEmojiLike = 'fetch_emoji_like',
GetGuildProfile = "get_guild_service_profile",
SetModelShow = "_set_model_show"
SetModelShow = "_set_model_show",
SetInputStatus = "set_input_status"
}

View File

@@ -417,7 +417,7 @@ export class OB11Constructor {
return;
}
//log("group msg", msg);
// Mlikiowa V2.0.24 Refactor Todo
// Mlikiowa V2.0.30 Refactor Todo
// if (msg.senderUin && msg.senderUin !== '0') {
// const member = await getGroupMember(msg.peerUid, msg.senderUin);
// if (member && member.cardName !== msg.sendMemberName) {
@@ -530,19 +530,18 @@ export class OB11Constructor {
const senderUin = emojiLikeData.gtip.qq.jp;
const msgSeq = emojiLikeData.gtip.url.msgseq;
const emojiId = emojiLikeData.gtip.face.id;
const replyMsgList = (await NTQQMsgApi.getMsgsBySeqAndCount({
const peer = {
chatType: ChatType.group,
guildId: '',
peerUid: msg.peerUid,
}, msgSeq, 1, true, true)).msgList;
console.log("表情回应消息长度检测", replyMsgList.length)
peerUid: msg.peerUid
}
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, msgSeq)).msgList;
if (replyMsgList.length < 1) {
return;
}
const replyMsg = replyMsgList.reverse()[0];//获取最顶层消息
//console.log('表情回应消息', msgSeq, ' 结算ID', replyMsg.msgId);
const replyMsg = replyMsgList.filter(e => e.msgSeq == msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
//console.log("表情回应消息长度检测", msgSeq, replyMsg.elements);
if (!replyMsg) throw new Error('找不到回应消息');
return new OB11GroupMsgEmojiLikeEvent(
core,
parseInt(msg.peerUid),

View File

@@ -18,10 +18,11 @@ import {
SendVideoElement,
viedo_type,
} from '@/core';
import * as fsnormal from 'node:fs';
import { promises as fs } from 'node:fs';
import ffmpeg from 'fluent-ffmpeg';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import { getVideoInfo } from '@/common/utils/video';
import { defaultVideoThumbB64, getVideoInfo } from '@/common/utils/video';
import { encodeSilk } from '@/common/utils/audio';
import faceConfig from '@/core/external/face_config.json';
import * as pathLib from 'node:path';
@@ -140,11 +141,7 @@ export class SendMsgElementConstructor {
}
static async video(coreContext: NapCatCore, filePath: string, fileName: string = '', diyThumbPath: string = '', videotype: viedo_type = viedo_type.VIDEO_FORMAT_MP4): Promise<SendVideoElement> {
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const NTQQFileApi = coreContext.apis.FileApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const logger = coreContext.context.logger;
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) {
@@ -179,7 +176,8 @@ export class SendMsgElementConstructor {
resolve(thumbPath);
}).catch(reject);
} else {
resolve(undefined);
fsnormal.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
resolve(thumbPath);
}
})
.screenshots({
@@ -224,6 +222,21 @@ export class SendMsgElementConstructor {
// sourceVideoCodecFormat: 2
},
};
// "fileElement": {
// "fileMd5": "",
// "fileName": "1.mp4",
// "filePath": "C:\\Users\\nanae\\OneDrive\\Desktop\\1.mp4",
// "fileSize": "1847007",
// "picHeight": 1280,
// "picWidth": 720,
// "picThumbPath": {},
// "file10MMd5": "",
// "fileSha": "",
// "fileSha3": "",
// "fileUuid": "",
// "fileSubId": "",
// "thumbFileSize": 750
// }
return element;
}

View File

@@ -291,8 +291,8 @@ export class NapCatOneBot11Adapter {
await this.networkManager.emitEvent(new OB11FriendRequestEvent(
this.core,
parseInt(requesterUin!),
req.friendUid + '|' + req.reqTime,
req.extWords,
req.friendUid + '|' + req.reqTime,
));
} catch (e) {
this.context.logger.logDebug('获取加好友者QQ号失败', e);
@@ -396,12 +396,12 @@ export class NapCatOneBot11Adapter {
} catch (e) {
this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e);
}
} else if (notify.type == GroupNotifyTypes.INVITE_ME) {
} else if (notify.type == GroupNotifyTypes.INVITE_ME && notify.status == 1) {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent(
this.core,
parseInt(notify.group.groupCode),
parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)),
parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)),
'invite',
notify.postscript,
flag,

View File

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

View File

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