Compare commits

...

18 Commits

Author SHA1 Message Date
手瓜一十雪
48d5cb53bd release: 1.7.6 2024-08-03 22:45:36 +08:00
手瓜一十雪
fd7d2dbf53 release: 1.7.5 2024-08-03 20:48:13 +08:00
手瓜一十雪
6609697752 release: 1.7.5 2024-08-03 20:47:05 +08:00
手瓜一十雪
dcd6e1973e build:1.7.5For9.9.15 2024-08-03 16:23:55 +08:00
手瓜一十雪
3614a6e932 chore: 9.9.15 support 2024-08-03 16:08:23 +08:00
手瓜一十雪
931a0210e5 chore: 兼容9.915 信息获取 2024-08-03 15:36:56 +08:00
手瓜一十雪
f9e7de4b42 build: 1.7.5For9.9.15 2024-08-03 15:07:51 +08:00
手瓜一十雪
8e0b79594e style: lint 2024-08-03 15:06:02 +08:00
手瓜一十雪
17122c4360 feat: 精简历史获取 2024-08-03 15:03:41 +08:00
手瓜一十雪
154f7b6a30 chore: 清除老旧代码 2024-08-03 14:57:24 +08:00
手瓜一十雪
52e5543d0b chore: queryEmoticonMsgs 2024-08-03 14:44:48 +08:00
手瓜一十雪
3c304bd2ae feat: 补全类型 开始对9.9.15针对优化 2024-08-03 14:25:26 +08:00
手瓜一十雪
26609bb8fd chore: 9.9.15兼容sendmsg 2024-08-03 13:11:25 +08:00
手瓜一十雪
de3fa9aaa4 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-03 12:28:41 +08:00
手瓜一十雪
788665f84c chore: support win 9.9.15 2024-08-03 12:28:25 +08:00
手瓜一十雪
3943782971 Merge pull request #201 from idranme/main
feat: at segment add name
2024-08-03 07:08:25 +08:00
idranme
8f899c40f2 chore 2024-08-02 15:49:01 +00:00
idranme
a1f582399e feat: at segment add name 2024-08-02 15:45:37 +00:00
26 changed files with 440 additions and 221 deletions

View File

@@ -1,32 +0,0 @@
# v1.6.8
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
## 使用前警告
1. 在最近版本由于QQ本体大幅变动为了保证NapCat可用性NapCat近期启动与安装方式将将大幅变动请关注文档和社群获取。
2. 在Core上完全执行开源请不要用于违法用途如此可能造成NapCat完全停止更新。
3. 针对原启动方式的围堵NapCat研发了多种方式除此其余理论与扩展的分析和思路将部分展示于Docs以便各位参与开发与维护NapCat。
## 其余·备注
启动方式: WayBoot.03 Electron Main进程为Node 直接注入代码 同理项目: LiteLoader
## 修复与优化
1. 移除数据库文件读写 ~ 优化性能
2. 重构消息发送 极限速度优化 ~ 优化性能
3. WebUi配置热重载优化 ~ 修复问题
4. 修复偶现崩溃问题 ~ 修复问题
5. 修复群邀请通知事件多次推送问题 ~ 修复问题
6. 尝试修复因缓存引起的字段不全问题 ~ 修复问题
7. 修复在非常非常高并发的情况 上报自身消息 回复回错问题 ~ 修复问题
8. 修复图片SubType字段位置错误问题 ~ 修复问题
9. 修复Uid/Uin转换问题 ~ 修复问题
## 新增与调整
1. 最后发言时间重构 入群时间失效 ~ 替换功能
2. 重构文件发送/获取 ~ 优化性能
3. 支持GOCQ私聊上传接口 ~ 新增功能
4. 悄悄告诉你ws http可以同一个端口 ~ 新增功能
5. 根据config目录的默认配置初始化新的配置文件 ~ 新增功能
6. WebUi可以部署在nginx代理二级目录 配置端口设置为0可关闭WebUi ~ 新增功能
7. 新增收藏表情拉取接口 ~ 新增功能
8. 新增群头像设置接口 ~ 新增功能
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,11 @@
# v1.7.6
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
## 启动的方式
Way03/Way05
## 新增与调整
1. 支持 9.9.15/3.2.12 版本QQ
新增的 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.7.4",
"version": "1.7.6",
"scripts": {
"watch:dev": "vite --mode development",
"watch:prod": "vite --mode production",

View File

@@ -150,7 +150,7 @@ export class NTEventWrapper {
});
}
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>
(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, checker: (...args: Parameters<ListenerType>) => boolean, ...args: Parameters<EventType>) {
(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, checker: (...args: Parameters<ListenerType>) => boolean, ...args: Parameters<EventType>) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
const id = randomUUID();
let complete = 0;

View File

@@ -3,7 +3,6 @@ import fs from 'node:fs';
import os from 'node:os';
import { systemPlatform } from '@/common/utils/system';
import { logError } from '@/common/utils/log';
export const exePath = process.execPath;
export const pkgInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'package.json');
@@ -38,16 +37,16 @@ type QQVersionConfigInfo = {
}
let _qqVersionConfigInfo: QQVersionConfigInfo = {
'baseVersion': '9.9.12-25765',
'curVersion': '9.9.12-25765',
'baseVersion': '9.9.15-26702',
'curVersion': '9.9.15-26702',
'prevVersion': '',
'onErrorVersions': [],
'buildId': '25765'
'buildId': '26702'
};
if (fs.existsSync(configVersionInfoPath)) {
try {
const _ =JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
const _ = JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
_qqVersionConfigInfo = Object.assign(_qqVersionConfigInfo, _);
} catch (e) {
logError('Load QQ version config info failed, Use default version', e);
@@ -68,10 +67,12 @@ export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toSt
// Linux
// app_version: '3.2.9-25765',
// qua: 'V1_LNX_NQ_3.2.10_25765_GW_B',
let _appid: string = '537234702'; // 默认为 Windows 平台的 appid
export function requireMinNTQBuild(buildStr: string) {
return parseInt(qqVersionConfigInfo.buildId) >= parseInt(buildStr);
}
let _appid: string = '537237765'; // 默认为 Windows 平台的 appid
if (systemPlatform === 'linux') {
_appid = '537234773';
_appid = '537237950';
}
// todo: mac 平台的 appid
export const appid = _appid;

View File

@@ -1,12 +1,13 @@
import { ElementType, GetFileListParam, MessageElement, Peer, RawMessage, SendMessageElement, SendMsgElementConstructor } from '@/core/entities';
import { GetFileListParam, Peer, RawMessage, SendMessageElement, SendMsgElementConstructor } from '@/core/entities';
import { friends, groups, selfInfo } from '@/core/data';
import { log, logError, logWarn } from '@/common/utils/log';
import { log, logWarn } from '@/common/utils/log';
import { sleep } from '@/common/utils/helper';
import { napCatCore, NodeIKernelMsgService, NTQQUserApi } from '@/core';
import { NodeIKernelMsgListener, onGroupFileInfoUpdateParamType } from '@/core/listeners';
import { napCatCore, NTQQUserApi } from '@/core';
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
import { GeneralCallResult } from '@/core/services/common';
import { MessageUnique } from '../../../common/utils/MessageUnique';
import { NTEventDispatch } from '@/common/utils/EventTask';
import { requireMinNTQBuild } from '@/common/utils/QQBasicInfo';
async function LoadMessageIdList(Peer: Peer, msgId: string) {
let msgList = await NTQQMsgApi.getMsgHistory(Peer, msgId, 50);
@@ -65,28 +66,7 @@ setTimeout(() => {
// console.log(await NTQQMsgApi.multiForwardMsg(peer, peer, [MsgId]));
// }, 25000)
let SendMsgSeq = new Map<Peer, Array<number>>();
export class NTQQMsgApi {
// static async CheckSendMode() {
// try {
// NTQQMsgApi.sendMsgV2({ chatType: 1, peerUid: selfInfo.uid }, [SendMsgElementConstructor.text('消息队列模式测试')], true, 10000).then().catch();
// MsgSendMode = 2;
// logNotice('[消息队列] 消息模式确认: MsgId异步队列');
// return true;
// } catch (error) {
// logNotice('[消息队列] 消息模式失败: MsgId异步队列');
// }
// try {
// NTQQMsgApi.sendMsgV1({ chatType: 1, peerUid: selfInfo.uid }, [SendMsgElementConstructor.text('消息队列模式测试')], true, 10000).then().catch();
// MsgSendMode = 1;
// logNotice('[消息队列] 消息模式确认: MsgSeq异步队列');
// return true;
// } catch (error) {
// logNotice('[消息队列] 消息模式失败: MsgSeq异步队列');
// }
// return false;
// }
// static napCatCore: NapCatCore | null = null;
// enum BaseEmojiType {
// NORMAL_EMOJI,
@@ -107,7 +87,7 @@ export class NTQQMsgApi {
} | undefined> {
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
}
static async getLastestMsgByUids(peer: Peer) {
static async getLastestMsgByUids(peer: Peer, count: number = 20) {
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: peer,
filterMsgType: [],
@@ -175,12 +155,6 @@ export class NTQQMsgApi {
peerUid: peer.peerUid
}, msgIds);
}
//并发Seq
static async sendMsgV3(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
let msgList = await NTQQMsgApi.getLastestMsgByUids(peer);
let data = await napCatCore.session.getMsgService().sendMsg("0", peer, msgElements, new Map());
SendMsgSeq.get(peer)?.push()
}
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
// function generateMsgId() {
// const timestamp = Math.floor(Date.now() / 1000);
@@ -192,7 +166,7 @@ export class NTQQMsgApi {
// return msgId;
// }
let msgId = await NTQQMsgApi.getMsgUnique(await NTQQMsgApi.getServerTime());
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
let data = await NTEventDispatch.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
@@ -226,7 +200,7 @@ export class NTQQMsgApi {
}
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
//唉? !我有个想法
let msgId = await NTQQMsgApi.getMsgUnique(await NTQQMsgApi.getServerTime());
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
peer.guildId = msgId;
let data = await NTEventDispatch.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
@@ -256,16 +230,12 @@ export class NTQQMsgApi {
});
return retMsg;
}
static async getMsgUniqueEx() {
let msgId = await NTQQMsgApi.getMsgUnique(await NTQQMsgApi.getServerTime());
return msgId;
}
static async getMsgUnique(time: string) {
static async getMsgUnique(chatType: number, time: string) {
if (requireMinNTQBuild('26702')) {
return napCatCore.session.getMsgService().generateMsgUniqueId(chatType, time);
}
return napCatCore.session.getMsgService().getMsgUniqueId(time);
}
static async getMsgUniqueByTimeV2() {
return NTEventDispatch.CallNoListenerEvent<NodeIKernelMsgService['getMsgUniqueId']>('NodeIKernelMsgService/getMsgUniqueId', 5000, await NTQQMsgApi.getServerTimeV2())
}
static async getServerTime() {
return napCatCore.session.getMSFService().getServerTime();
}

View File

@@ -6,7 +6,8 @@ import { NodeIKernelProfileListener, ProfileListener } from '@/core/listeners';
import { RequestUtil } from '@/common/utils/request';
import { logWarn } from '@/common/utils/log';
import { NTEventDispatch } from '@/common/utils/EventTask';
import { NodeIKernelProfileService } from '@/core/services';
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
import { requireMinNTQBuild } from '@/common/utils/QQBasicInfo';
export class NTQQUserApi {
static async getProfileLike(uid: string) {
@@ -64,7 +65,49 @@ export class NTQQUserApi {
// KQZONE,
// KOTHER
// }
static async fetchUserDetailInfo(uid: string) {
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
let [_retData, profile] = await NTEventDispatch.CallNormalEvent
<EventService, EventListener>
(
'NodeIKernelProfileService/fetchUserDetailInfo',
'NodeIKernelProfileListener/onUserDetailInfoChanged',
1,
5000,
(profile) => {
if (profile.uid === uid) {
return true;
}
return false;
},
"BuddyProfileStore",
[
uid
],
UserDetailSource.KSERVER,
[
ProfileBizType.KALL
]
);
let RetUser: User = {
...profile.simpleInfo.coreInfo,
...profile.simpleInfo.status,
...profile.simpleInfo.vasInfo,
...profile.commonExt,
...profile.simpleInfo.baseInfo,
qqLevel: profile.commonExt.qqLevel,
pendantId: ""
};
return RetUser;
}
static async getUserDetailInfo(uid: string) {
if (requireMinNTQBuild('26702')) {
return this.fetchUserDetailInfo(uid);
}
return this.getUserDetailInfoOld(uid);
}
static async getUserDetailInfoOld(uid: string) {
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
let [_retData, profile] = await NTEventDispatch.CallNormalEvent

View File

@@ -9,7 +9,178 @@ export interface BuddyCategoryType {
categroyMbCount: number;
buddyList: User[];
}
interface CoreInfo {
uid: string;
uin: string;
nick: string;
remark: string;
}
interface BaseInfo {
qid: string;
longNick: string;
birthday_year: number;
birthday_month: number;
birthday_day: number;
age: number;
sex: number;
eMail: string;
phoneNum: string;
categoryId: number;
richTime: number;
richBuffer: string;
}
interface MusicInfo {
buf: string;
}
interface VideoBizInfo {
cid: string;
tvUrl: string;
synchType: string;
}
interface VideoInfo {
name: string;
}
interface ExtOnlineBusinessInfo {
buf: string;
customStatus: any;
videoBizInfo: VideoBizInfo;
videoInfo: VideoInfo;
}
interface ExtBuffer {
buf: string;
}
interface UserStatus {
uid: string;
uin: string;
status: number;
extStatus: number;
batteryStatus: number;
termType: number;
netType: number;
iconType: number;
customStatus: any;
setTime: string;
specialFlag: number;
abiFlag: number;
eNetworkType: number;
showName: string;
termDesc: string;
musicInfo: MusicInfo;
extOnlineBusinessInfo: ExtOnlineBusinessInfo;
extBuffer: ExtBuffer;
}
interface PrivilegeIcon {
jumpUrl: string;
openIconList: any[];
closeIconList: any[];
}
interface VasInfo {
vipFlag: boolean;
yearVipFlag: boolean;
svipFlag: boolean;
vipLevel: number;
bigClub: boolean;
bigClubLevel: number;
nameplateVipType: number;
grayNameplateFlag: number;
superVipTemplateId: number;
diyFontId: number;
pendantId: number;
pendantDiyId: number;
faceId: number;
vipFont: number;
vipFontType: number;
magicFont: number;
fontEffect: number;
newLoverDiamondFlag: number;
extendNameplateId: number;
diyNameplateIDs: any[];
vipStartFlag: number;
vipDataFlag: number;
gameNameplateId: string;
gameLastLoginTime: string;
gameRank: number;
gameIconShowFlag: boolean;
gameCardId: string;
vipNameColorId: string;
privilegeIcon: PrivilegeIcon;
}
interface RelationFlags {
topTime: string;
isBlock: boolean;
isMsgDisturb: boolean;
isSpecialCareOpen: boolean;
isSpecialCareZone: boolean;
ringId: string;
isBlocked: boolean;
recommendImgFlag: number;
disableEmojiShortCuts: number;
qidianMasterFlag: number;
qidianCrewFlag: number;
qidianCrewFlag2: number;
isHideQQLevel: number;
isHidePrivilegeIcon: number;
}
interface CommonExt {
constellation: number;
shengXiao: number;
kBloodType: number;
homeTown: string;
makeFriendCareer: number;
pos: string;
college: string;
country: string;
province: string;
city: string;
postCode: string;
address: string;
regTime: number;
interest: string;
labels: any[];
qqLevel: QQLevel;
}
interface Pic {
picId: string;
picTime: number;
picUrlMap: Record<string, string>;
}
interface PhotoWall {
picList: Pic[];
}
interface SimpleInfo {
uid: string;
uin: string;
coreInfo: CoreInfo;
baseInfo: BaseInfo;
status: UserStatus;
vasInfo: VasInfo;
relationFlags: RelationFlags;
otherFlags: any;
intimate: any;
}
export interface UserDetailInfoListenerArg {
uid: string;
uin: string;
simpleInfo: SimpleInfo;
commonExt: CommonExt;
photoWall: PhotoWall;
}
export interface ModifyProfileParams {
nick: string,
longNick: string,

View File

@@ -1,8 +1,8 @@
import { User } from '@/core/entities';
import { User, UserDetailInfoListenerArg } from '@/core/entities';
interface IProfileListener {
onProfileSimpleChanged(...args: unknown[]): void;
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void;
onProfileDetailInfoChanged(profile: User): void;
onStatusUpdate(...args: unknown[]): void;
@@ -18,6 +18,9 @@ export interface NodeIKernelProfileListener extends IProfileListener {
}
export class ProfileListener implements IProfileListener {
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void {
}
onProfileSimpleChanged(...args: unknown[]) {
}

View File

@@ -1,19 +1,35 @@
import { Friend } from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common';
import { NodeIKernelBuddyListener } from '@/core/listeners';
export enum BuddyListReqType {
KNOMAL,
KLETTER
}
export interface NodeIKernelBuddyService {
// 以下为自行添加的wrapper.node中并没有这些方法,目的是简化调用
friends: Friend[];
getFriend(uidOrUin: string): Promise<Friend>;
// 26702 以上
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult>;
//26702 以上
getBuddyListFromCache(callFrom: string): Promise<Array<
{
categoryId: number,//9999应该跳过 那是兜底数据吧
categorySortId: number,//排序方式
categroyName: string,//分类名
categroyMbCount: number,//不懂
onlineCount: number,//在线数目
buddyUids: Array<string>//Uids
}>>;
// 以下为原生方法
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
getAllBuddyCount(): number;
removeKernelBuddyListener(listener: unknown): void;
getBuddyList(bool: boolean): Promise<GeneralCallResult>;
/**
* @deprecated
* @param nocache 使用缓存
*/
getBuddyList(nocache: boolean): Promise<GeneralCallResult>;
getBuddyNick(uid: number): string;

View File

@@ -0,0 +1,3 @@
export interface NodeIKernelECDHService{
}

View File

@@ -9,7 +9,44 @@ import {
import { GeneralCallResult } from '@/core/services/common';
export interface NodeIKernelGroupService {
setHeader(uid:string,path:string): unknown;
getGroupMemberLevelInfo(groupCode: string): Promise<unknown>;
//26702
getGroupHonorList(groupCodes: Array<string>): unknown;
getUinByUids(uins: string[]): Promise<unknown>;
getUidByUins(uins: string[]): Promise<unknown>;
//26702(其实更早 但是我不知道)
checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>;
//26702(其实更早 但是我不知道)
getGroupLatestEssenceList(groupCode: string): Promise<unknown>;
//26702(其实更早 但是我不知道)
shareDigest(Req: {
appId: string,
appType: number,
msgStyle: number,
recvUin: string,
sendType: number,
clientInfo: {
platform: number
},
richMsg: {
usingArk: boolean,
title: string,
summary: string,
url: string,
pictureUrl: string,
brief: string
}
}): Promise<unknown>;
//26702(其实更早 但是我不知道)
isEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
//26702(其实更早 但是我不知道)
queryCachedEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
//26702(其实更早 但是我不知道)
fetchGroupEssenceList(Req: { groupCode: string, pageStart: number, pageLimit: number }, Arg: unknown): Promise<unknown>;
//26702
getAllMemberList(groupCode: string, refresh: boolean): Promise<unknown>;
setHeader(uid: string, path: string): unknown;
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
@@ -39,7 +76,7 @@ export interface NodeIKernelGroupService {
modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;
getTransferableMemberInfo(uid: string): unknown;
getTransferableMemberInfo(groupCode: string): unknown;//获取整个群的
transferGroup(uid: string): void;

View File

@@ -2,7 +2,20 @@ import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } fro
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
import { GeneralCallResult } from '@/core/services/common';
export interface QueryMsgsParams {
chatInfo: Peer,
filterMsgType: [],
filterSendersUid: [],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}
export interface NodeIKernelMsgService {
generateMsgUniqueId(chatType: number, time: string): string;
addKernelMsgListener(nodeIKernelMsgListener: NodeIKernelMsgListener): number;
sendMsg(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>): Promise<GeneralCallResult>;
@@ -214,19 +227,7 @@ export interface NodeIKernelMsgService {
* @param param.isIncludeCurrent 是否包含当前页码。
* @returns 返回一个Promise解析为查询结果的未知类型对象。
*/
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: {
chatInfo: {
chatType: number,
peerUid: string
},
filterMsgType: [],
filterSendersUid: Array<string>,
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}): Promise<unknown>;
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: QueryMsgsParams): Promise<unknown>;
// this.chatType = i2;
// this.peerUid = str;
@@ -243,19 +244,7 @@ export interface NodeIKernelMsgService {
// this.isReverseOrder = z;
// this.isIncludeCurrent = z2;
//queryMsgsWithFilterEx(0L, 0L, 0L, new QueryMsgsParams(new ChatInfo(2, str), new ArrayList(), new ArrayList(), 0L, 0L, 250, false, true))
queryMsgsWithFilterEx(msgId: string, msgTime: string, megSeq: string, param: {
chatInfo: {
chatType: number,
peerUid: string
},
filterMsgType: [],
filterSendersUid: string[],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}): Promise<GeneralCallResult & {
queryMsgsWithFilterEx(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>;
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
@@ -263,39 +252,15 @@ export interface NodeIKernelMsgService {
setMsgRichInfoFlag(...args: unknown[]): unknown;
queryPicOrVideoMsgs(msgId: string, msgTime: string, megSeq: string, param: {
chatInfo: {
chatType: number,
peerUid: string
},
filterMsgType: [],
filterSendersUid: [],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}): Promise<unknown>;
queryPicOrVideoMsgs(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<unknown>;
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
queryEmoticonMsgs(...args: unknown[]): unknown;
queryEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>;
queryTroopEmoticonMsgs(...args: unknown[]): unknown;
queryTroopEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>;
queryMsgsAndAbstractsWithFilter(msgId: string, msgTime: string, megSeq: string, param: {
chatInfo: {
chatType: number,
peerUid: string
},
filterMsgType: [],
filterSendersUid: [],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}): unknown;
queryMsgsAndAbstractsWithFilter(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): unknown;
setFocusOnGuild(...args: unknown[]): unknown;
@@ -627,9 +592,9 @@ export interface NodeIKernelMsgService {
// this.selfPhone = str5;
// this.gameSession = tempChatGameSession;
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
//chattype,uid->Promise<any>
getTempChatInfo(ChatType: number, Uid: string): unknown;

View File

@@ -2,8 +2,19 @@ import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener } from '../listeners';
import { GeneralCallResult } from '@/core/services/common';
export enum UserDetailSource {
KDB,
KSERVER
}
export enum ProfileBizType {
KALL,
KBASEEXTEND,
KVAS,
KQZONE,
KOTHER
}
export interface NodeIKernelProfileService {
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
addKernelProfileListener(listener: NodeIKernelProfileListener): number;

View File

@@ -42,6 +42,7 @@ import { NodeIKernelCollectionService } from './services/NodeIKernelCollectionSe
import { NodeIKernelRecentContactService } from './services/NodeIKernelRecentContactService';
import { NodeIKernelMSFService } from './services/NodeIKernelMSFService';
import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService';
import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
const __filename = fileURLToPath(import.meta.url);
@@ -157,6 +158,10 @@ export interface NodeIQQNTWrapperSession {
startNT(): void;
getBdhUploadService(): unknown;
getECDHService(): NodeIKernelECDHService;
getMsgService(): NodeIKernelMsgService;
getProfileService(): NodeIKernelProfileService;

View File

@@ -50,17 +50,17 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
try {
UuidData = UUIDConverter.decode(payload.file);
if (UuidData) {
let peerUin = UuidData.high;
let msgId = UuidData.low;
let isGroup = await getGroup(peerUin);
const peerUin = UuidData.high;
const msgId = UuidData.low;
const isGroup = await getGroup(peerUin);
let peer: Peer | undefined;
//识别Peer
if (isGroup) {
peer = { chatType: ChatType.group, peerUid: peerUin };
}
let PeerUid = await NTQQUserApi.getUidByUin(peerUin);
const PeerUid = await NTQQUserApi.getUidByUin(peerUin);
if (PeerUid) {
let isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
if (isBuddy) {
peer = { chatType: ChatType.friend, peerUid: PeerUid };
} else {
@@ -70,18 +70,18 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
if (!peer) {
throw new Error('chattype not support');
}
let msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
if (msgList.msgList.length == 0) {
throw new Error('msg not found');
}
let msg = msgList.msgList[0];
let findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
const msg = msgList.msgList[0];
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
if (!findEle) {
throw new Error('element not found');
}
let downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
let fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
let fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
const downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
const fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
const fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
const res: GetFileResponse = {
file: downloadPath,
url: downloadPath,

View File

@@ -2,7 +2,7 @@ import BaseAction from '../BaseAction';
import { OB11Message, OB11User } from '../../types';
import { getGroup, groups } from '@/core/data';
import { ActionName } from '../types';
import { ChatType } from '@/core/entities';
import { ChatType, RawMessage } from '@/core/entities';
import { NTQQMsgApi } from '@/core/apis/msg';
import { OB11Constructor } from '../../constructor';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -36,13 +36,13 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
chatType: ChatType.group,
peerUid: group.groupCode
};
let msgList: RawMessage[];
if (!payload.message_seq) {
const latestMsgId = (await NTQQMsgApi.getLastestMsgByUids(peer)).msgList[0].msgId;
targetMsgShortId = await MessageUnique.createMsg(peer, latestMsgId || '0');
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, count)).msgList;
} else {
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0';
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, count)).msgList;
}
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0';
const historyResult = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, count));
const msgList = historyResult.msgList;
await Promise.all(msgList.map(async msg => {
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
}));

View File

@@ -36,7 +36,7 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
const msg = await NTQQMsgApi.getMsgsByMsgId(
peer,
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
let retMsg = await OB11Constructor.message(msg.msgList[0]);
const retMsg = await OB11Constructor.message(msg.msgList[0]);
try {
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
retMsg.message_seq = retMsg.message_id;

View File

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

View File

@@ -102,5 +102,5 @@ export enum ActionName {
SetGroupHeader = 'set_group_head',
FetchCustomFace = 'fetch_custom_face',
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
TestApi01 = "test_api_01"
TestApi01 = 'test_api_01'
}

View File

@@ -101,33 +101,40 @@ export class OB11Constructor {
}
}
for (const element of msg.elements) {
const message_data: OB11MessageData | any = {
data: {},
type: 'unknown'
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any
};
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
message_data['type'] = OB11MessageDataType.at;
let qq: `${number}` | 'all';
let name: string | undefined;
if (element.textElement.atType == AtType.atAll) {
// message_data["data"]["mention"] = "all"
message_data['data']['qq'] = 'all';
qq = 'all';
}
else {
const atUid = element.textElement.atNtUid;
const { atNtUid, content } = element.textElement;
let atQQ = element.textElement.atUid;
if (!atQQ || atQQ === '0') {
const atMember = await getGroupMember(msg.peerUin, atUid);
const atMember = await getGroupMember(msg.peerUin, atNtUid);
if (atMember) {
atQQ = atMember.uin;
}
}
if (atQQ) {
// message_data["data"]["mention"] = atQQ
message_data['data']['qq'] = atQQ;
qq = atQQ as `${number}`;
name = content.replace('@', '');
}
}
message_data = {
type: OB11MessageDataType.at,
data: {
qq: qq!,
name
}
};
}
else if (element.textElement) {
message_data['type'] = 'text';
message_data['type'] = OB11MessageDataType.text;
let text = element.textElement.content;
if (!text.trim()) {
@@ -140,7 +147,7 @@ export class OB11Constructor {
message_data['data']['text'] = text;
}
else if (element.replyElement) {
message_data['type'] = 'reply';
message_data['type'] = OB11MessageDataType.reply;
//log("收到回复消息", element.replyElement);
try {
//做这么多都是因为NC速度太快 可能nt还没有写入数据库
@@ -163,14 +170,14 @@ export class OB11Constructor {
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString();
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
} catch (e: any) {
message_data['type'] = "unknown";
message_data['type'] = 'unknown' as any;
message_data['data'] = undefined;
logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq);
}
}
else if (element.picElement) {
message_data['type'] = 'image';
message_data['type'] = OB11MessageDataType.image;
// message_data["data"]["file"] = element.picElement.sourcePath
message_data['data']['file'] = element.picElement.fileName;
message_data['data']['subType'] = element.picElement.picSubType;
@@ -199,13 +206,13 @@ export class OB11Constructor {
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
FileElement.fileSize,
FileElement.fileName
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
FileElement.fileSize,
FileElement.fileName
);
}
else if (element.videoElement) {
@@ -246,13 +253,13 @@ export class OB11Constructor {
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
videoElement.fileSize || '0',
videoElement.fileName
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
videoElement.fileSize || '0',
videoElement.fileName
);
}
else if (element.pttElement) {
@@ -267,13 +274,13 @@ export class OB11Constructor {
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
element.pttElement.fileSize || '0',
element.pttElement.fileUuid || ''
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
element.pttElement.fileSize || '0',
element.pttElement.fileUuid || ''
);
//以uuid作为文件名
}
@@ -319,7 +326,7 @@ export class OB11Constructor {
message_data['type'] = OB11MessageDataType.forward;
message_data['data']['id'] = msg.msgId;
}
if (message_data.type !== 'unknown' && message_data.data) {
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);
if (messagePostFormat === 'string') {

View File

@@ -66,16 +66,16 @@ export function encodeCQCode(data: OB11MessageData) {
let result = '[CQ:' + data.type;
for (const name in data.data) {
let value = data.data[name];
try {
// Check if the value can be converted to a string
value = value.toString();
} catch (error) {
// If it can't be converted, skip this name-value pair
// console.warn(`Skipping problematic name-value pair. Name: ${name}, Value: ${value}`);
const value = data.data[name];
if (value === undefined) {
continue;
}
result += `,${name}=${CQCodeEscape(value)}`;
try {
const text = value.toString();
result += `,${name}=${CQCodeEscape(text)}`;
} catch (error) {
// If it can't be converted, skip this name-value pair
}
}
result += ']';
return result;

View File

@@ -114,6 +114,7 @@ export interface OB11MessageAt {
type: OB11MessageDataType.at
data: {
qq: `${number}` | 'all'
name?: string
}
}
@@ -177,13 +178,20 @@ export interface OB11MessageMarkdown {
}
}
export interface OB11MessageForward {
type: OB11MessageDataType.forward
data: {
id: string
}
}
export type OB11MessageData =
OB11MessageText |
OB11MessageFace | OB11MessageMFace |
OB11MessageAt | OB11MessageReply |
OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo |
OB11MessageNode | OB11MessageIdMusic | OB11MessageCustomMusic | OB11MessageJson |
OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown
OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward
export interface OB11PostSendMsg {
message_type?: 'private' | 'group'

View File

@@ -1 +1 @@
export const version = '1.7.4';
export const version = '1.7.6';

View File

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