feat: core code

This commit is contained in:
手瓜一十雪
2024-07-01 13:18:59 +08:00
parent cfd2c7fd0b
commit 8a42c9a7ab
76 changed files with 11178 additions and 0 deletions

67
src/core/.eslintrc.cjs Normal file
View File

@@ -0,0 +1,67 @@
module.exports = {
'root': true,
'env': {
'es2021': true,
'node': true
},
'extends': [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
],
'overrides': [
{
'env': {
'node': true
},
'files': [
'.eslintrc.{js,cjs}'
],
'parserOptions': {
'sourceType': 'script'
}
}
],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 'latest',
'sourceType': 'module'
},
'plugins': [
'@typescript-eslint',
'import'
],
'settings': {
'import/parsers': {
'@typescript-eslint/parser': ['.ts']
},
'import/resolver': {
'typescript': {
'alwaysTryTypes': true
}
}
},
'rules': {
'indent': [
'error',
2
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
'no-unused-vars': 'off',
'no-async-promise-executor': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-var-requires': 'off',
'object-curly-spacing': ['error', 'always'],
}
};

5
src/core/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.idea/
node_modules/
dist/
lib/
package-lock.json

3
src/core/README.md Normal file
View File

@@ -0,0 +1,3 @@
# @napneko/core
此仓库目前只用于隐藏源码,目前无法进行单独打包,只是作为 NapCatQQ 的 git submodule 引用。

51
src/core/build.cjs Normal file
View File

@@ -0,0 +1,51 @@
const swc = require("@swc/core");
const glob = require('glob');
const fs = require('fs-extra');
const files = glob.sync('src/**/*.ts');
function transfrom(file) {
return swc
.transformFile(file, {
// Some options cannot be specified in .swcrc
sourceMaps: false,
// Input files are treated as module by default.
// isModule: false,
module: {
type: 'commonjs'
},
// All options below can be configured via .swcrc
jsc: {
parser: {
syntax: "typescript",
decorators: true,
},
transform: {
"legacyDecorator": true,
"decoratorMetadata": true
},
target: 'es2017'
},
// "keepClassNames": true,
// "loose": true
})
.then((output) => {
// console.log(output.code); // transformed code
return {
file,
output
}
});
}
(async () => {
const result = await Promise.all(files.map((file) => {
return transfrom(file)
}));
await Promise.all(result.map((item) => {
return fs.outputFile(item.file.replace('src', 'dist').replace('.ts', '.js'), item.output.code)
}));
//console.timeEnd('swc build');
})()

45
src/core/package.json Normal file
View File

@@ -0,0 +1,45 @@
{
"name": "@napneko/core",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "./lib/index.js",
"files": [
"lib"
],
"scripts": {
"lint": "eslint --fix ./src/**/*.ts",
"build": "npx tsc --target es2022 --experimentalDecorators && node ./scripts/obfuscator.cjs"
},
"repository": {
"type": "git",
"url": "git+https://github.com/NapNeko/core.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/NapNeko/core/issues"
},
"homepage": "https://github.com/NapNeko/core#readme",
"devDependencies": {
"@babel/core": "^7.24.7",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@swc/core": "^1.6.1",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"eslint": "^8.57.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.1.1",
"typescript": "^5.4.5",
"vite": "^5.2.8",
"vite-plugin-babel": "^1.2.0",
"vite-plugin-dts": "^3.8.1"
},
"dependencies": {
"@log4js-node/log4js-api": "^1.0.2"
}
}

22
src/core/pub-package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "@napneko/core",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "./index.js",
"files": [
"lib"
],
"scripts": {
"lint": "eslint --fix ./src/**/*.ts",
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production",
"build": "npm run build:dev"
},
"author": "NapNeko",
"license": "MIT",
"bugs": {
"url": "https://github.com/NapNeko/NapCatQQ/issues"
},
"homepage": "https://github.com/NapNeko/NapCatQQ#readme"
}

View File

@@ -0,0 +1,45 @@
let fs = require('fs');
let path = require('path');
let JavaScriptObfuscator = require('javascript-obfuscator');
const dirPath = path.join(__dirname, '../dist/core');
const outputPath = dirPath;
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath, {recursive: true});
}
function obfuscateDir(currentPath, outputDir) {
fs.readdir(currentPath, {withFileTypes: true}, (err, entries) => {
if (err) throw err;
entries.forEach(entry => {
const localBasePath = path.join(currentPath, entry.name);
const outputLocalBasePath = path.join(outputDir, entry.name);
if (entry.isDirectory()) {
// 如果是目录,递归调用
if (!fs.existsSync(outputLocalBasePath)) {
fs.mkdirSync(outputLocalBasePath, {recursive: true});
}
obfuscateDir(localBasePath, outputLocalBasePath);
} else if (entry.isFile() && path.extname(entry.name) === '.js') {
// 如果是文件且为 .js进行混淆
fs.readFile(localBasePath, (err, content)=>{
// console.log('read file', localBasePath);
const obfuscated = JavaScriptObfuscator.obfuscate(content.toString(), {
compact: true,
controlFlowFlattening: true
});
// console.log('obfuscate file', localBasePath);
fs.writeFile(outputLocalBasePath, obfuscated.getObfuscatedCode(), ()=>{
// console.log(`[NapCat] [Obfuscator] ${localBasePath} => ${outputLocalBasePath}`);
});
});
}
});
});
}
// 开始混淆
obfuscateDir(dirPath, outputPath);

View File

@@ -0,0 +1,29 @@
import { log } from "@/common/utils/log";
interface IDependsAdapter {
onMSFStatusChange(arg1: number, arg2: number): void;
onMSFSsoError(args: unknown): void;
getGroupCode(args: unknown): void;
}
export interface NodeIDependsAdapter extends IDependsAdapter {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IDependsAdapter): NodeIDependsAdapter;
}
export class DependsAdapter implements IDependsAdapter {
onMSFStatusChange(arg1: number, arg2: number) {
// console.log(arg1, arg2);
// if (arg1 == 2 && arg2 == 2) {
// log("NapCat丢失网络连接,请检查网络")
// }
}
onMSFSsoError(args: unknown) {
}
getGroupCode(args: unknown) {
}
}

View File

@@ -0,0 +1,23 @@
interface IDispatcherAdapter {
dispatchRequest(arg: unknown): void;
dispatchCall(arg: unknown): void;
dispatchCallWithJson(arg: unknown): void;
}
export interface NodeIDispatcherAdapter extends IDispatcherAdapter {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IDispatcherAdapter): NodeIDispatcherAdapter;
}
export class DispatcherAdapter implements IDispatcherAdapter {
dispatchRequest(arg: unknown) {
}
dispatchCall(arg: unknown) {
}
dispatchCallWithJson(arg: unknown) {
}
}

View File

@@ -0,0 +1,48 @@
interface IGlobalAdapter {
onLog(...args: unknown[]): void;
onGetSrvCalTime(...args: unknown[]): void;
onShowErrUITips(...args: unknown[]): void;
fixPicImgType(...args: unknown[]): void;
getAppSetting(...args: unknown[]): void;
onInstallFinished(...args: unknown[]): void;
onUpdateGeneralFlag(...args: unknown[]): void;
onGetOfflineMsg(...args: unknown[]): void;
}
export interface NodeIGlobalAdapter extends IGlobalAdapter {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IGlobalAdapter): NodeIGlobalAdapter;
}
export class GlobalAdapter implements IGlobalAdapter {
onLog(...args: unknown[]) {
}
onGetSrvCalTime(...args: unknown[]) {
}
onShowErrUITips(...args: unknown[]) {
}
fixPicImgType(...args: unknown[]) {
}
getAppSetting(...args: unknown[]) {
}
onInstallFinished(...args: unknown[]) {
}
onUpdateGeneralFlag(...args: unknown[]) {
}
onGetOfflineMsg(...args: unknown[]) {
}
}

View File

@@ -0,0 +1,3 @@
export * from './NodeIDependsAdapter';
export * from './NodeIDispatcherAdapter';
export * from './NodeIGlobalAdapter';

View File

@@ -0,0 +1,53 @@
import { napCatCore } from "..";
export class NTQQCollectionApi {
static async createCollection(authorUin: string, authorUid: string, authorName: string, brief: string, rawData: string) {
let param = {
commInfo: {
bid: 1,
category: 2,
author: {
type: 1,
numId: authorUin,
strId: authorName,
groupId: '0',
groupName: '',
uid: authorUid
},
customGroupId: '0',
createTime: Date.now().toString(),
sequence: Date.now().toString()
},
richMediaSummary: {
originalUri: '',
publisher: '',
richMediaVersion: 0,
subTitle: '',
title: '',
brief: brief,
picList: [],
contentType: 1
},
richMediaContent: {
rawData: rawData,
bizDataList: [],
picList: [],
fileList: []
},
need_share_url: false
};
return napCatCore.session.getCollectionService().createNewCollectionItem(param);
}
static async getAllCollection(category: number = 0, count: number = 50) {
let param = {
category: category,
groupId: -1,
forceSync: true,
forceFromDb: false,
timeStamp: "0",
count: count,
searchDown: true
};
return napCatCore.session.getCollectionService().getCollectionItemList(param);
}
}

241
src/core/src/apis/file.ts Normal file
View File

@@ -0,0 +1,241 @@
import {
CacheFileList,
CacheFileListItem,
CacheFileType,
CacheScanResult,
ChatCacheList,
ChatCacheListItemBasic,
ChatType,
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, RawMessage
} from '@/core/entities';
import path from 'path';
import fs from 'fs';
import fsPromises from 'fs/promises';
import { log, logDebug, logError } from '@/common/utils/log';
import { GeneralCallResult, napCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
import { calculateFileMD5 } from '@/common/utils/file';
import * as fileType from 'file-type';
import { MsgListener } from '@/core/listeners';
import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
import { sessionConfig } from '@/core/sessionConfig';
import { randomUUID } from 'crypto';
import { rkeyManager } from '../utils/rkey';
import { AsyncQueue } from '@/common/utils/AsyncQueue';
// const rkeyExpireTime = 1000;
const getRKeyTaskQueue = new AsyncQueue();
const downloadMediaTasks: Map<string, (arg: OnRichMediaDownloadCompleteParams) => void> = new Map<string, (arg: OnRichMediaDownloadCompleteParams) => void>();
const downloadMediaListener = new MsgListener();
downloadMediaListener.onRichMediaDownloadComplete = arg => {
for (const [uuid, cb] of downloadMediaTasks) {
cb(arg);
downloadMediaTasks.delete(uuid);
}
};
setTimeout(() => {
napCatCore.onLoginSuccess(() => {
napCatCore.addListener(downloadMediaListener);
});
}, 100);
export class NTQQFileApi {
static async getFileType(filePath: string) {
return fileType.fileTypeFromFile(filePath);
}
static async copyFile(filePath: string, destPath: string) {
await napCatCore.util.copyFile(filePath, destPath);
}
static async getFileSize(filePath: string): Promise<number> {
return await napCatCore.util.getFileSize(filePath);
}
static async getVideoUrl(msg: RawMessage, element: any) {
return (await napCatCore.session.getRichMediaService().getVideoPlayUrlV2({
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '0'
}, msg.msgId, element.elementId, 0, { downSourceType: 1, triggerType: 1 })).urlResult.domainUrl[0].url;
}
// 上传文件到QQ的文件夹
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
// napCatCore.wrapper.util.
const fileMd5 = await calculateFileMD5(filePath);
let ext: string = (await NTQQFileApi.getFileType(filePath))?.ext as string || '';
if (ext) {
ext = '.' + ext;
}
let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf('.') === -1) {
fileName += ext;
}
const mediaPath = napCatCore.session.getMsgService().getRichMediaFilePathForGuild({
md5HexStr: fileMd5,
fileName: fileName,
elementType: elementType,
elementSubType,
thumbSize: 0,
needCreate: true,
downloadType: 1,
file_uuid: ''
});
await NTQQFileApi.copyFile(filePath, mediaPath!);
const fileSize = await NTQQFileApi.getFileSize(filePath);
return {
md5: fileMd5,
fileName,
path: mediaPath,
fileSize,
ext
};
}
static async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
// 用于下载收到的消息中的图片等
if (sourcePath && fs.existsSync(sourcePath)) {
if (force) {
try {
await fsPromises.unlink(sourcePath);
} catch (e) {
//
}
} else {
return sourcePath;
}
}
//logDebug('start downloadMedia', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
return new Promise<string>((resolve, reject) => {
let completed = false;
const cb = (arg: OnRichMediaDownloadCompleteParams) => {
//logDebug('downloadMedia complete', arg, msgId);
if (arg.msgId === msgId) {
completed = true;
let filePath = arg.filePath;
if (filePath.startsWith('\\')) {
// log('filePath start with \\');
const downloadPath = sessionConfig.defaultFileDownloadPath;
//logDebug('downloadPath', downloadPath);
filePath = path.join(downloadPath, filePath);
// 下载路径是下载文件夹的相对路径
}
resolve(filePath);
}
};
downloadMediaTasks.set(randomUUID(), cb);
setTimeout(() => {
if (!completed) {
reject('下载超时');
}
}, timeout);
napCatCore.session.getMsgService().downloadRichMedia({
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
});
});
}
static async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
return new Promise((resolve, reject) => {
imageSize(filePath, (err, dimensions) => {
if (err) {
reject(err);
} else {
resolve(dimensions);
}
});
});
}
static async getImageUrl(element: { originImageUrl: any; md5HexStr?: any; fileUuid: any; }, isPrivateImage: boolean) {
if (!element) {
return '';
}
const url = element.originImageUrl; // 没有域名
const md5HexStr = element.md5HexStr;
const fileMd5 = element.md5HexStr;
const fileUuid = element.fileUuid;
if (url) {
if (url.startsWith('/download')) {
if (url.includes('&rkey=')) {
return IMAGE_HTTP_HOST_NT + url;
}
const rkeyData = await rkeyManager.getRkey();
const existsRKey = isPrivateImage ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`;
} else {
// 老的图片url不需要rkey
return IMAGE_HTTP_HOST + url;
}
} else if (fileMd5 || md5HexStr) {
// 没有url需要自己拼接
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`;
}
logDebug('图片url获取失败', element);
return '';
}
}
export class NTQQFileCacheApi {
static async setCacheSilentScan(isSilent: boolean = true) {
return '';
}
static getCacheSessionPathList() {
return '';
}
static clearCache(cacheKeys: Array<string> = ['tmp', 'hotUpdate']) {
// 参数未验证
return napCatCore.session.getStorageCleanService().clearCacheDataByKeys(cacheKeys);
}
static addCacheScannedPaths(pathMap: object = {}) {
return napCatCore.session.getStorageCleanService().addCacheScanedPaths(pathMap);
}
static scanCache(): Promise<GeneralCallResult & {
size: string[]
}> {
// 需要注册Listener onFinishScan
return napCatCore.session.getStorageCleanService().scanCache();
}
static getHotUpdateCachePath() {
// 未实现
return '';
}
static getDesktopTmpPath() {
// 未实现
return '';
}
static getChatCacheList(type: ChatType, pageSize: number = 1000, pageIndex: number = 0) {
return napCatCore.session.getStorageCleanService().getChatCacheInfo(type, pageSize, 1, pageIndex);
}
static getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) {
const _lastRecord = lastRecord ? lastRecord : { fileType: fileType };
//需要五个参数
//return napCatCore.session.getStorageCleanService().getFileCacheInfo();
}
static async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {
return napCatCore.session.getStorageCleanService().clearChatCacheInfo(chats, fileKeys);
}
}

View File

@@ -0,0 +1,41 @@
import { FriendRequest, User } from '@/core/entities';
import { napCatCore, OnBuddyChangeParams } from '@/core';
import { NTEventDispatch } from '@/common/utils/EventTask';
export class NTQQFriendApi {
static async isBuddy(uid: string) {
return napCatCore.session.getBuddyService().isBuddy(uid);
}
static async getFriends(forced = false): Promise<User[]> {
let [_retData, _BuddyArg] = await NTEventDispatch.CallNormalEvent
<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
(
'NodeIKernelBuddyService/getBuddyList',
'NodeIKernelBuddyListener/onBuddyListChange',
1,
5000,
forced
);
const friends: User[] = [];
for (const categoryItem of _BuddyArg) {
for (const friend of categoryItem.buddyList) {
friends.push(friend);
}
}
return friends;
}
static async handleFriendRequest(flag: string, accept: boolean) {
let data = flag.split('|');
if (data.length < 2) {
return;
}
let friendUid = data[0];
let reqTime = data[1];
napCatCore.session.getBuddyService()?.approvalFriendRequest({
friendUid: friendUid,
reqTime: reqTime,
accept
});
}
}

189
src/core/src/apis/group.ts Normal file
View File

@@ -0,0 +1,189 @@
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType } from '../entities';
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
import { NTEventDispatch } from '@/common/utils/EventTask';
import { logDebug } from '@/common/utils/log';
// console.log(process.pid);
// setTimeout(async () => {
// console.log(JSON.stringify(await NTQQGroupApi.getMemberExtInfo(), null, 2));
// }, 20000);
export class NTQQGroupApi {
static async getGroups(forced = false) {
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
<(force: boolean) => Promise<any>, (updateType: number, groupList: Group[]) => void>
(
'NodeIKernelGroupService/getGroupList',
'NodeIKernelGroupListener/onGroupListUpdate',
1,
5000,
forced
);
return groupList;
}
static async getGroupRecommendContactArkJson(GroupCode: string) {
return napCatCore.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
}
static async CreatGroupFileFolder(groupCode: string, folderName: string) {
return napCatCore.session.getRichMediaService().createGroupFolder(groupCode, folderName);
}
static async DelGroupFile(groupCode: string, files: string[]) {
return napCatCore.session.getRichMediaService().deleteGroupFile(groupCode, [102], files);
}
static async DelGroupFileFolder(groupCode: string, folderId: string) {
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
}
static async getSingleScreenNotifies(num: number) {
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
(
'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
1,
5000,
false,
'',
num
);
return notifies;
}
static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const groupService = napCatCore.session.getGroupService();
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
const result = await groupService.getNextMemberList(sceneId!, undefined, num);
if (result.errCode !== 0) {
throw ('获取群成员列表出错,' + result.errMsg);
}
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
return result.result.infos;
/*
console.log(sceneId);
const result = await napCatCore.getGroupService().getNextMemberList(sceneId, num);
console.log(result);
return result;
*/
}
static async getGroupNotifies() {
// 获取管理员变更
// 加群通知,退出通知,需要管理员权限
}
static async GetGroupFileCount(Gids: Array<string>) {
return napCatCore.session.getRichMediaService().batchGetGroupFileCount(Gids);
}
static async getGroupIgnoreNotifies() {
}
static async getArkJsonGroupShare(GroupCode: string) {
let ret = await NTEventDispatch.CallNoListenerEvent
<(GroupId: string) => Promise<GeneralCallResult & { arkJson: string }>>(
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
5000,
GroupCode
);
return ret.arkJson;
}
//需要异常处理
static async uploadGroupBulletinPic(GroupCode: string, imageurl: string) {
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
}
static async handleGroupRequest(notify: GroupNotify, operateType: GroupRequestOperateTypes, reason?: string) {
return napCatCore.session.getGroupService().operateSysNotify(
false,
{
'operateType': operateType, // 2 拒绝
'targetMsg': {
'seq': notify.seq, // 通知序列号
'type': notify.type,
'groupCode': notify.group.groupCode,
'postscript': reason || ''
}
});
}
static async quitGroup(groupQQ: string) {
return napCatCore.session.getGroupService().quitGroup(groupQQ);
}
static async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
return napCatCore.session.getGroupService().kickMember(groupQQ, kickUids, refuseForever, kickReason);
}
static async banMember(groupQQ: string, memList: Array<{ uid: string, timeStamp: number }>) {
// timeStamp为秒数, 0为解除禁言
return napCatCore.session.getGroupService().setMemberShutUp(groupQQ, memList);
}
static async banGroup(groupQQ: string, shutUp: boolean) {
return napCatCore.session.getGroupService().setGroupShutUp(groupQQ, shutUp);
}
static async setMemberCard(groupQQ: string, memberUid: string, cardName: string) {
return napCatCore.session.getGroupService().modifyMemberCardName(groupQQ, memberUid, cardName);
}
static async setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole) {
return napCatCore.session.getGroupService().modifyMemberRole(groupQQ, memberUid, role);
}
static async setGroupName(groupQQ: string, groupName: string) {
return napCatCore.session.getGroupService().modifyGroupName(groupQQ, groupName, false);
}
// 头衔不可用
static async setGroupTitle(groupQQ: string, uid: string, title: string) {
}
static async publishGroupBulletin(groupQQ: string, content: string, picInfo: { id: string, width: number, height: number } | undefined = undefined, pinned: number = 0, confirmRequired: number = 0,) {
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com');
//text是content内容url编码
let data = {
text: encodeURI(content),
picInfo: picInfo,
oldFeedsId: '',
pinned: pinned,
confirmRequired: confirmRequired
};
return napCatCore.session.getGroupService().publishGroupBulletin(groupQQ, _Pskey!, data);
}
static async getGroupRemainAtTimes(GroupCode: string) {
napCatCore.session.getGroupService().getGroupRemainAtTimes(GroupCode);
}
static async getMemberExtInfo(groupCode: string, uin: string) {
// 仅NTQQ 9.9.11 24568测试 容易炸开谨慎使用
return napCatCore.session.getGroupService().getMemberExtInfo(
{
groupCode: groupCode,
sourceType: MemberExtSourceType.TITLETYPE,
beginUin: '0',
dataTime: '0',
uinList: [uin],
uinNum: '',
seq: '',
groupType: '',
richCardNameVer: '',
memberExtFilter: {
memberLevelInfoUin: 1,
memberLevelInfoPoint: 1,
memberLevelInfoActiveDay: 1,
memberLevelInfoLevel: 1,
memberLevelInfoName: 1,
levelName: 1,
dataTime: 1,
userShowFlag: 1,
sysShowFlag: 1,
timeToUpdate: 1,
nickName: 1,
specialTitle: 1,
levelNameNew: 1,
userShowFlagNew: 1,
msgNeedField: 1,
cmdUinFlagExt3Grocery: 1,
memberIcon: 1,
memberInfoSeq: 1
}
}
);
}
}

View File

@@ -0,0 +1,8 @@
export * from './file';
export * from './friend';
export * from './group';
export * from './msg';
export * from './user';
export * from './webapi';
export * from './sign';
export * from './system';

231
src/core/src/apis/msg.ts Normal file
View File

@@ -0,0 +1,231 @@
import { GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
import { selfInfo } from '@/core/data';
import { log, logError } from '@/common/utils/log';
import { sleep } from '@/common/utils/helper';
import { napCatCore } from '@/core';
import { MsgListener, onGroupFileInfoUpdateParamType } from '@/core/listeners';
import { GeneralCallResult } from '@/core/services/common';
import { randomUUID } from 'crypto';
const sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void | Promise<void>) | null> = {};// peerUid: callbackFunc
const sendSuccessCBMap: Record<string, ((sendSuccessMsg: RawMessage) => boolean | Promise<boolean>) | null> = {};// uuid: callbackFunc
const GroupFileInfoUpdateTasks: Map<string, ((groupFileListResult: onGroupFileInfoUpdateParamType) => void)> = new Map();
const sentMsgTasks: Map<string, (msg: RawMessage) => void> = new Map();
const msgListener = new MsgListener();
msgListener.onGroupFileInfoUpdate = (groupFileListResult: onGroupFileInfoUpdateParamType) => {
for (const [uuid, cb] of GroupFileInfoUpdateTasks) {
cb(groupFileListResult);
GroupFileInfoUpdateTasks.delete(uuid);
}
};
msgListener.onAddSendMsg = (msgRecord: RawMessage) => {
// console.log("sent msg", msgRecord, sendMessagePool);
for (const [uuid, cb] of sentMsgTasks) {
cb(msgRecord);
sentMsgTasks.delete(uuid);
}
if (sendMessagePool[msgRecord.peerUid]) {
const r = sendMessagePool[msgRecord.peerUid]?.(msgRecord);
if (r instanceof Promise) {
r.then().catch(logError);
}
}
};
msgListener.onMsgInfoListUpdate = (msgInfoList: RawMessage[]) => {
msgInfoList.forEach(msg => {
new Promise((resolve, reject) => {
for (const cbId in sendSuccessCBMap) {
const cb = sendSuccessCBMap[cbId]!;
const cbResult = cb(msg);
const checkResult = (result: boolean) => {
if (result) {
delete sendSuccessCBMap[cbId];
}
};
if (cbResult instanceof Promise) {
cbResult.then(checkResult);
} else {
checkResult(cbResult);
}
}
}).then().catch(log);
});
};
setTimeout(() => {
napCatCore.onLoginSuccess(() => {
napCatCore.addListener(msgListener);
});
}, 100);
export class NTQQMsgApi {
// static napCatCore: NapCatCore | null = null;
// enum BaseEmojiType {
// NORMAL_EMOJI,
// SUPER_EMOJI,
// RANDOM_SUPER_EMOJI,
// CHAIN_SUPER_EMOJI,
// EMOJI_EMOJI
// }
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
// 其实以官方文档为准是最好的https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
emojiId = emojiId.toString();
return napCatCore.session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set);
}
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
msgList: RawMessage[]
} | undefined> {
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
}
static async getMsgsByMsgId(peer: Peer, msgIds: string[]) {
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
}
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
return await napCatCore.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
}
static async activateChat(peer: Peer) {
// await this.fetchRecentContact();
// await sleep(500);
}
static async activateChatAndGetHistory(peer: Peer) {
}
static async setMsgRead(peer: Peer) {
return napCatCore.session.getMsgService().setMsgRead(peer);
}
static async getGroupFileList(GroupCode: string, params: GetFileListParam) {
return new Promise<Array<any>>(async (resolve, reject) => {
let complete = false;
setTimeout(() => {
if (!complete) {
reject('获取群文件列表超时');
}
}, 5000);
const GroupFileInfoUpdateCB = (groupFileListResult: onGroupFileInfoUpdateParamType) => {
complete = true;
resolve(groupFileListResult.item);
};
GroupFileInfoUpdateTasks.set(randomUUID(), GroupFileInfoUpdateCB);
await napCatCore.session.getRichMediaService().getGroupFileList(GroupCode, params);
});
}
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
// 消息时间从旧到新
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, true);
}
static async fetchRecentContact() {
}
static async recallMsg(peer: Peer, msgIds: string[]) {
await napCatCore.session.getMsgService().recallMsg({
chatType: peer.chatType,
peerUid: peer.peerUid
}, msgIds);
}
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000): Promise<RawMessage> {
const peerUid = peer.peerUid;
// 等待上一个相同的peer发送完
let checkLastSendUsingTime = 0;
const waitLastSend: () => Promise<void> = async () => {
if (checkLastSendUsingTime > timeout) {
throw ('发送超时');
}
const lastSending = sendMessagePool[peer.peerUid];
if (lastSending) {
// log("有正在发送的消息,等待中...")
await sleep(500);
checkLastSendUsingTime += 500;
return await waitLastSend();
} else {
return;
}
};
await waitLastSend();
return new Promise((resolve, reject) => {
let completed = false;
let sentMessage: RawMessage | null = null;
const sendSuccessCBId = randomUUID() as string;
sendSuccessCBMap[sendSuccessCBId] = (msgRecord: RawMessage) => {
if (msgRecord.msgId === sentMessage?.msgId) {
if (msgRecord.sendStatus === 2) {
delete sendSuccessCBMap[sendSuccessCBId];
completed = true;
resolve(msgRecord);
return true;
}
return false;
}
return false;
};
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
// console.log('收到sent 消息', rawMessage.msgId);
delete sendMessagePool[peerUid];
sentMessage = rawMessage;
};
setTimeout(() => {
if (completed) return;
delete sendMessagePool[peerUid];
delete sendSuccessCBMap[sendSuccessCBId];
reject('发送超时');
}, timeout);
const result = napCatCore.session.getMsgService().sendMsg('0', peer, msgElements, new Map());
});
}
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
return napCatCore.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
}
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
const msgInfos = msgIds.map(id => {
return { msgId: id, senderShowName: selfInfo.nick };
});
return new Promise((resolve, reject) => {
let complete = false;
const onSentCB = (msg: RawMessage) => {
const arkElement = msg.elements.find(ele => ele.arkElement);
if (!arkElement) {
// log("收到的不是转发消息")
return;
}
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
if (forwardData.app != 'com.tencent.multimsg') {
return;
}
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
complete = true;
resolve(msg);
}
};
sentMsgTasks.set(randomUUID(), onSentCB);
setTimeout(() => {
if (!complete) {
reject('转发消息超时');
}
}, 5000);
napCatCore.session.getMsgService().multiForwardMsgWithComment(msgInfos, srcPeer, destPeer, [], new Map());
}
);
}
}

271
src/core/src/apis/sign.ts Normal file
View File

@@ -0,0 +1,271 @@
import { logDebug } from '@/common/utils/log';
import { NTQQUserApi } from './user';
import { selfInfo } from '../data';
import { RequestUtil } from '@/common/utils/request';
import { WebApi } from './webapi';
import { checkFileReceived, checkFileReceived2, uri2local } from '@/common/utils/file';
import fs from 'node:fs'
import { sleep } from '@/common/utils/helper';
export interface IdMusicSignPostData {
type: 'qq' | '163',
id: string | number,
}
export interface CustomMusicSignPostData {
type: 'custom',
url: string,
audio: string,
title: string,
image?: string,
singer?: string
}
// export class MusicSign {
// private readonly url: string;
// constructor(url: string) {
// this.url = url;
// }
// sign(postData: CustomMusicSignPostData | IdMusicSignPostData): Promise<any> {
// return new Promise((resolve, reject) => {
// fetch(this.url, {
// method: 'POST', // 指定请求方法为 POST
// headers: {
// 'Content-Type': 'application/json' // 设置请求头,指明发送的数据类型为 JSON
// },
// body: JSON.stringify(postData) // 将 JavaScript 对象转换为 JSON 字符串作为请求体
// })
// .then(response => {
// if (!response.ok) {
// reject(response.statusText); // 请求失败,返回错误信息
// }
// return response.json(); // 解析 JSON 格式的响应体
// })
// .then(data => {
// logDebug('音乐消息生成成功', data);
// resolve(data);
// })
// .catch(error => {
// reject(error);
// });
// });
// }
// }
export interface MiniAppLuaJsonType {
prompt: string,
title: string,
preview: string,
jumpUrl: string,
tag: string,
tagIcon: string,
source: string,
sourcelogo: string,
}
export async function SignMiniApp(CardData: MiniAppLuaJsonType) {
// {
// "app": "com.tencent.miniapp.lua",
// "bizsrc": "tianxuan.imgJumpArk",
// "view": "miniapp",
// "prompt": "hi! 这里有我的日常故事,只想讲给你听",
// "config": {
// "type": "normal",
// "forward": 1,
// "autosize": 0
// },
// "meta": {
// "miniapp": {
// "title": "hi! 这里有我的日常故事,只想讲给你听",
// "preview": "https:\/\/tianquan.gtimg.cn\/qqAIAgent\/item\/7\/square.png",
// "jumpUrl": "https:\/\/club.vip.qq.com\/transfer?open_kuikly_info=%7B%22version%22%3A%20%221%22%2C%22src_type%22%3A%20%22web%22%2C%22kr_turbo_display%22%3A%20%221%22%2C%22page_name%22%3A%20%22vas_ai_persona_moments%22%2C%22bundle_name%22%3A%20%22vas_ai_persona_moments%22%7D&page_name=vas_ai_persona_moments&enteranceId=share&robot_uin=3889008584",
// "tag": "QQ智能体",
// "tagIcon": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png",
// "source": "QQ智能体",
// "sourcelogo": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png"
// }
// }
// }
// token : function(url,skey){
// var str = skey || cookie('skey') || cookie('rv2') || '',
// hash = 5381;
// if(url){
// var hostname = uri(url).hostname;
// if(hostname.indexOf('qun.qq.com') > -1 || (hostname.indexOf('qzone.qq.com') > -1 && hostname.indexOf('qun.qzone.qq.com') === -1)){
// str = cookie('p_skey') || str;
// }
// }
// for(var i = 0, len = str.length; i < len; ++i){
// hash += (hash << 5) + str.charAt(i).charCodeAt();
// }
// return hash & 0x7fffffff;
// },
//
// function signToken(skey: string) {
// let hash = 5381;
// for (let i = 0, len = skey.length; i < len; ++i) {
// hash += (hash << 5) + skey.charCodeAt(i);
// }
// return hash & 0x7fffffff;
// }
let signCard = {
"app": "com.tencent.miniapp.lua",
"bizsrc": "tianxuan.imgJumpArk",
"view": "miniapp",
"prompt": CardData.prompt,
"config": {
"type": "normal",
"forward": 1,
"autosize": 0
},
"meta": {
"miniapp": {
"title": CardData.title,
"preview": (CardData.preview as string).replace(/\\/g, "\\/\\/"),
"jumpUrl": (CardData.jumpUrl as string).replace(/\\/g, "\\/\\/"),
"tag": CardData.tag,
"tagIcon": (CardData.tagIcon as string).replace(/\\/g, "\\/\\/"),
"source": CardData.source,
"sourcelogo": (CardData.sourcelogo as string).replace(/\\/g, "\\/\\/")
}
}
};
// let signCard = {
// "app": "com.tencent.eventshare.lua",
// "prompt": "Bot Test",
// "bizsrc": "tianxuan.business",
// "meta": {
// "eventshare": {
// "button1URL": "https://www.bilibili.com",
// "button1disable": false,
// "button1title": "点我前往",
// "button2URL": "",
// "button2disable": false,
// "button2title": "",
// "buttonNum": 1,
// "jumpURL": "https://www.bilibili.com",
// "preview": "https://tianquan.gtimg.cn/shoal/card/9930bc4e-4a92-4da3-814f-8094a2421d9c.png",
// "tag": "QQ集卡",
// "tagIcon": "https://tianquan.gtimg.cn/shoal/card/c034854b-102d-40be-a545-5ca90a7c49c9.png",
// "title": "Bot Test"
// }
// },
// "config": {
// "autosize": 0,
// "collect": 0,
// "ctime": 1716568575,
// "forward": 1,
// "height": 336,
// "reply": 0,
// "round": 1,
// "type": "normal",
// "width": 263
// },
// "view": "eventshare",
// "ver": "0.0.0.1"
// };
let data = (await NTQQUserApi.getQzoneCookies());
const Bkn = WebApi.genBkn(data.p_skey);
const CookieValue = 'p_skey=' + data.p_skey + '; skey=' + data.skey + '; p_uin=o' + selfInfo.uin + '; uin=o' + selfInfo.uin;
let signurl = "https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenNewSignedArk?g_tk=" + Bkn + "&ark=" + encodeURIComponent(JSON.stringify(signCard));
let signed_ark = "";
try {
let retData = await RequestUtil.HttpGetJson<{ code: number, data: { signed_ark: string } }>(signurl, 'GET', undefined, { Cookie: CookieValue });
//logDebug('MiniApp JSON 消息生成成功', retData);
signed_ark = retData.data.signed_ark;
} catch (error) {
logDebug('MiniApp JSON 消息生成失败', error);
}
return signed_ark;
}
export async function SignMusicInternal(songname: string, singer: string, cover: string, songmid: string, songmusic: string) {
//curl -X POST 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003' -H 'Content-Type: application/json' -H 'Cookie: uin=o10086' -d '{"app":"com.tencent.qqreader.share","config":{"ctime":1718634110,"forward":1,"token":"9a63343c32d5a16bcde653eb97faa25d","type":"normal"},"extra":{"app_type":1,"appid":100497308,"msg_seq":14386738075403815000.0,"uin":1733139081},"meta":{"music":{"action":"","android_pkg_name":"","app_type":1,"appid":100497308,"ctime":1718634110,"desc":"周杰伦","jumpUrl":"https://i.y.qq.com/v8/playsong.html?songmid=0039MnYb0qxYhV&type=0","musicUrl":"http://ws.stream.qqmusic.qq.com/http://isure6.stream.qqmusic.qq.com/M800002202B43Cq4V4.mp3?fromtag=810033622&guid=br_xzg&trace=23fe7bcbe2336bbf&uin=553&vkey=CF0F5CE8B0FA16F3001F8A88D877A217EB5E4F00BDCEF1021EB6C48969CA33C6303987AEECE9CC840122DD2F917A59D6130D8A8CA4577C87","preview":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","cover":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","sourceMsgId":"0","source_icon":"https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0","source_url":"","tag":"QQ音乐","title":"晴天","uin":10086}},"prompt":"[分享]晴天","ver":"0.0.0.1","view":"music"}'
let signurl = 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003';
//let = "https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg";
let signCard = {
app: "com.tencent.qqreader.share",
config: {
ctime: 1718634110,
forward: 1,
token: "9a63343c32d5a16bcde653eb97faa25d",
type: "normal"
},
extra: {
app_type: 1,
appid: 100497308,
msg_seq: 14386738075403815000.0,
uin: 1733139081
},
meta: {
music:
{
action: "",
android_pkg_name: "",
app_type: 1,
appid: 100497308,
ctime: 1718634110,
desc: singer,
jumpUrl: "https://i.y.qq.com/v8/playsong.html?songmid=" + songmid + "&type=0",
musicUrl: songmusic,
preview: cover,
cover: cover,
sourceMsgId: "0",
source_icon: "https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0",
source_url: "",
tag: "QQ音乐",
title: songname,
uin: 10086
}
},
prompt: "[分享]" + songname,
ver: "0.0.0.1",
view: "music"
}
//console.log(JSON.stringify(signCard, null, 2));
let data = await RequestUtil.HttpGetJson<{ code: number, data: { arkResult: string } }>
(signurl, 'POST', signCard, { 'Cookie': 'uin=o10086', 'Content-Type': 'application/json' });
return data;
}
//注意处理错误
export async function CreateMusicThridWay0(id: string = '', mid: string = '') {
if (mid == '') {
let MusicInfo = await RequestUtil.HttpGetJson
<{ songinfo?: { data?: { track_info: { mid: string } } } }>
(
'https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"songinfo":{"method":"get_song_detail_yqq","param":{"song_type":0,"song_mid":"","song_id":' + id + '},"module":"music.pf_song_detail_svr"}}',
'GET',
undefined
);
mid = MusicInfo.songinfo?.data?.track_info.mid!;
}
//第三方接口 存在速率限制 现在勉强用
let MusicReal = await RequestUtil.HttpGetJson
<{ code: number, data?: { name: string, singer: string, url: string, cover: string } }>
('https://api.leafone.cn/api/qqmusic?id=' + mid + '&type=8', 'GET', undefined);
//console.log(MusicReal);
return { ...MusicReal.data, mid: mid };
}
export async function CreateMusicThridWay1(id: string = '', mid: string = '') {
}
//转换外域名为 https://qq.ugcimg.cn/v1/cpqcbu4b8870i61bde6k7cbmjgejq8mr3in82qir4qi7ielffv5slv8ck8g42novtmev26i233ujtuab6tvu2l2sjgtupfr389191v00s1j5oh5325j5eqi40774jv1i/khovifoh7jrqd6eahoiv7koh8o
//https://cgi.connect.qq.com/qqconnectopen/openapi/change_image_url?url=https://th.bing.com/th?id=OSK.b8ed36f1fb1889de6dc84fd81c187773&w=46&h=46&c=11&rs=1&qlt=80&o=6&dpr=2&pid=SANGAM
//外域名不行得走qgroup中转
//https://proxy.gtimg.cn/tx_tls_gate=y.qq.com/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg
//可外域名
//https://pic.ugcimg.cn/500955bdd6657ecc8e82e02d2df06800/jpg1
//QQ音乐gtimg接口
//https://y.gtimg.cn/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg?max_age=2592000
//还有一处公告上传可以上传高质量图片 持久为qq域名
export async function SignMusicWrapper(id: string = '') {
let MusicInfo = await CreateMusicThridWay0(id)!;
let MusicCard = await SignMusicInternal(MusicInfo.name!, MusicInfo.singer!, MusicInfo.cover!, MusicInfo.mid!, "https://ws.stream.qqmusic.qq.com/" + MusicInfo.url!);
return MusicCard;
}

View File

@@ -0,0 +1,39 @@
import { NTEventDispatch } from '@/common/utils/EventTask';
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
// setTimeout(async () => {
// let ret = await NTQQSystemApi.getArkJsonCollection('1-2-162b9b42-65b9-4405-a8ed-2e256ec8aa50');
// console.log(ret);
// }, 20000)
export class NTQQSystemApi {
static async hasOtherRunningQQProcess() {
return napCatCore.util.hasOtherRunningQQProcess();
}
static async ORCImage(filePath: string) {
return napCatCore.session.getNodeMiscService().wantWinScreenOCR(filePath);
}
static async translateEnWordToZn(words: string[]) {
return napCatCore.session.getRichMediaService().translateEnWordToZn(words);
}
//调用会超时 没灯用
static async getOnlineDev() {
return napCatCore.session.getMsgService().getOnLineDev();
}
//1-2-162b9b42-65b9-4405-a8ed-2e256ec8aa50
static async getArkJsonCollection(cid: string) {
let ret = await NTEventDispatch.CallNoListenerEvent
<(cid: string) => Promise<GeneralCallResult & { arkJson: string }>>(
'NodeIKernelCollectionService/collectionArkShare',
5000,
'1717662698058'
);
return ret;
}
static async BootMiniApp(appfile: string, params: string) {
await napCatCore.session.getNodeMiscService().setMiniAppVersion('2.16.4');
let c = await napCatCore.session.getNodeMiscService().getMiniAppPath();
console.log(c);
return napCatCore.session.getNodeMiscService().startNewMiniApp(appfile, params);
}
}

199
src/core/src/apis/user.ts Normal file
View File

@@ -0,0 +1,199 @@
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin } from '@/core/entities';
import { selfInfo } from '@/core/data';
import { CacheClassFuncAsync } from '@/common/utils/helper';
import { GeneralCallResult, napCatCore } from '@/core';
import { ProfileListener } from '@/core/listeners';
import { rejects } from 'assert';
import { randomUUID } from 'crypto';
import { RequestUtil } from '@/common/utils/request';
import { logDebug, logError } from '@/common/utils/log';
import { NTEventDispatch } from '@/common/utils/EventTask';
const userInfoCache: Record<string, User> = {}; // uid: User
const profileListener = new ProfileListener();
const userDetailHandlers: Map<string, ((profile: User) => void)> = new Map();
profileListener.onProfileDetailInfoChanged = (profile) => {
userInfoCache[profile.uid] = profile;
userDetailHandlers.forEach(handler => handler(profile));
};
setTimeout(() => {
napCatCore.onLoginSuccess(() => {
napCatCore.addListener(profileListener);
});
}, 100);
// 老版本逻辑现已移除
// console.log('onProfileDetailInfoChanged', profile);
// recevCount++;
// firstProfile = profile;
// if (recevCount === 2) {
// profileService.removeKernelProfileListener(listenerId);
// // if (!completed) {
// completed = true;
// resolve(profile);
// // }
// }
// };
export class NTQQUserApi {
static async setLongNick(longNick: string) {
return napCatCore.session.getProfileService().setLongNick(longNick);
}
static async setSelfOnlineStatus(status: number, extStatus: number, batteryStatus: number) {
return napCatCore.session.getMsgService().setStatus({ status: status, extStatus: extStatus, batteryStatus: batteryStatus });
}
static async getBuddyRecommendContactArkJson(uin: string, sencenID = '') {
return napCatCore.session.getBuddyService().getBuddyRecommendContactArkJson(uin, sencenID);
}
static async like(uid: string, count = 1): Promise<{ result: number, errMsg: string, succCounts: number }> {
return napCatCore.session.getProfileLikeService().setBuddyProfileLike({
friendUid: uid,
sourceId: 71,
doLikeCount: count,
doLikeTollCount: 0
});
}
static async setQQAvatar(filePath: string) {
type setQQAvatarRet = { result: number, errMsg: string };
const ret = await napCatCore.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
return { result: ret?.result, errMsg: ret?.errMsg };
}
static async getSelfInfo() {
}
static async getUserInfo(uid: string) {
}
// enum ProfileBizType {
// KALL,
// KBASEEXTEND,
// KVAS,
// KQZONE,
// KOTHER
// }
static async getUserDetailInfo(uid: string): Promise<User> {
// const existUser = userInfoCache[uid];
// if (existUser) {
// return existUser;
// }
const profileService = napCatCore.session.getProfileService();
// console.log('getUserDetailInfo', result);
return new Promise((resolve, reject) => {
const uuid = randomUUID();
let completed = false;
let retData: User | undefined = undefined;
let isFirst = true;
// 不管返回几次 超时有数据就该返回 兼容就好了
setTimeout(() => {
if (!completed) {
if (retData) {
resolve(retData);
} else {
reject('getUserDetailInfo timeout');
}
}
userDetailHandlers.delete(uuid);
}, 5000);
userDetailHandlers.set(uuid, (profile) => {
if (profile.uid === uid) {
if (isFirst) {
retData = profile;
isFirst = false;
// console.log('getUserDetailInfo', profile);
} else {
completed = true;
resolve(profile);
}
}
});
profileService.getUserDetailInfoWithBizInfo(uid, [0]).then(result => {
// console.log('getUserDetailInfo', result);
});
});
}
static async modifySelfProfile(param: ModifyProfileParams) {
return napCatCore.session.getProfileService().modifyDesktopMiniProfile(param);
}
//需要异常处理
@CacheClassFuncAsync(1800 * 1000)
static async getCookies(domain: string) {
const ClientKeyData = await NTQQUserApi.forceFetchClientKey();
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + selfInfo.uin + '%2Finfocenter&keyindex=19%27'
let cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
return cookies;
}
@CacheClassFuncAsync(1800 * 1000)
static async getPSkey(domainList: string[]) {
return await napCatCore.session.getTipOffService().getPskey(domainList, true);
}
static async getRobotUinRange(): Promise<Array<any>> {
const robotUinRanges = await napCatCore.session.getRobotService().getRobotUinRange({
justFetchMsgConfig: '1',
type: 1,
version: 0,
aioKeywordVersion: 0
});
// console.log(robotUinRanges?.response?.robotUinRanges);
return robotUinRanges?.response?.robotUinRanges;
}
//需要异常处理
@CacheClassFuncAsync(1800 * 1000)
static async getQzoneCookies() {
const ClientKeyData = await NTQQUserApi.forceFetchClientKey();
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2Fuser.qzone.qq.com%2F' + selfInfo.uin + '%2Finfocenter&keyindex=19%27'
let cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
return cookies;
}
//需要异常处理
@CacheClassFuncAsync(1800 * 1000)
static async getSkey(): Promise<string | undefined> {
const ClientKeyData = await NTQQUserApi.forceFetchClientKey();
if (ClientKeyData.result !== 0) {
throw new Error('getClientKey Error');
}
const clientKey = ClientKeyData.clientKey;
const keyIndex = ClientKeyData.keyIndex;
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + clientKey + '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=19%27';
let cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
const skey = cookies['skey'];
if (!skey) {
throw new Error('getSkey Skey is Empty');
}
return skey;
}
static async getUidByUin(Uin: string) {
let ret = await NTEventDispatch.CallNoListenerEvent
<(Uin: string[]) => Promise<{ uidInfo: Map<string, string> }>>(
'NodeIKernelUixConvertService/getUid',
5000,
[Uin]
);
return ret.uidInfo.get(Uin);
}
static async getUinByUid(Uid: string | undefined) {
if (!Uid) {
return '';
}
let ret = await NTEventDispatch.CallNoListenerEvent
<(Uin: string[]) => Promise<{ uinInfo: Map<string, string> }>>(
'NodeIKernelUixConvertService/getUin',
5000,
[Uid]
);
return ret.uinInfo.get(Uid);
}
static async getUserDetailInfoByUin(Uin: string) {
return NTEventDispatch.CallNoListenerEvent
<(Uin: string) => Promise<UserDetailInfoByUin>>(
'NodeIKernelProfileService/getUserDetailInfoByUin',
5000,
Uin
);
}
@CacheClassFuncAsync(3600 * 1000, 'ClientKey')
static async forceFetchClientKey() {
return await napCatCore.session.getTicketService().forceFetchClientKey('');
}
}

351
src/core/src/apis/webapi.ts Normal file
View File

@@ -0,0 +1,351 @@
import { selfInfo } from '@/core/data';
import { log, logDebug } from '@/common/utils/log';
import { NTQQUserApi } from './user';
import { RequestUtil } from '@/common/utils/request';
import { CacheClassFuncAsync } from '@/common/utils/helper';
export enum WebHonorType {
ALL = 'all',
TALKACTIVE = 'talkative',
PERFROMER = 'performer',
LEGEND = 'legend',
STORONGE_NEWBI = 'strong_newbie',
EMOTION = 'emotion'
}
export interface WebApiGroupMember {
uin: number
role: number
g: number
join_time: number
last_speak_time: number
lv: {
point: number
level: number
}
card: string
tags: string
flag: number
nick: string
qage: number
rm: number
}
interface WebApiGroupMemberRet {
ec: number
errcode: number
em: string
cache: number
adm_num: number
levelname: any
mems: WebApiGroupMember[]
count: number
svr_time: number
max_count: number
search_count: number
extmode: number
}
export interface WebApiGroupNoticeFeed {
u: number//发送者
fid: string//fid
pubt: number//时间
msg: {
text: string
text_face: string
title: string,
pics?: {
id: string,
w: string,
h: string
}[]
}
type: number
fn: number
cn: number
vn: number
settings: {
is_show_edit_card: number
remind_ts: number
tip_window_type: number
confirm_required: number
}
read_num: number
is_read: number
is_all_confirm: number
}
export interface WebApiGroupNoticeRet {
ec: number
em: string
ltsm: number
srv_code: number
read_only: number
role: number
feeds: WebApiGroupNoticeFeed[]
group: {
group_id: number
class_ext: number
}
sta: number,
gln: number
tst: number,
ui: any
server_time: number
svrt: number
ad: number
}
interface GroupEssenceMsg {
group_code: string
msg_seq: number
msg_random: number
sender_uin: string
sender_nick: string
sender_time: number
add_digest_uin: string
add_digest_nick: string
add_digest_time: number
msg_content: any[]
can_be_removed: true
}
export interface GroupEssenceMsgRet {
retcode: number
retmsg: string
data: {
msg_list: GroupEssenceMsg[]
is_end: boolean
group_role: number
config_page_url: string
}
}
export class WebApi {
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
static async getGroupEssenceMsg(GroupCode: string, page_start: string) {
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const Bkn = WebApi.genBkn(CookiesObject.skey);
const url = 'https://qun.qq.com/cgi-bin/group_digest/digest_list?bkn=' + Bkn + '&group_code=' + GroupCode + '&page_start=' + page_start + '&page_limit=20';
let ret;
try {
ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>(url, 'GET', '', { 'Cookie': CookieValue });
} catch {
return undefined;
}
//console.log(url, CookieValue);
if (ret.retcode !== 0) {
return undefined;
}
return ret;
}
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
static async getGroupMembers(GroupCode: string, cached: boolean = true): Promise<WebApiGroupMember[]> {
//logDebug('webapi 获取群成员', GroupCode);
let MemberData: Array<WebApiGroupMember> = new Array<WebApiGroupMember>();
try {
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const Bkn = WebApi.genBkn(CookiesObject.skey);
const retList: Promise<WebApiGroupMemberRet>[] = [];
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>('https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?st=0&end=40&sort=1&gc=' + GroupCode + '&bkn=' + Bkn, 'POST', '', { 'Cookie': CookieValue });
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
return [];
} else {
for (const key in fastRet.mems) {
MemberData.push(fastRet.mems[key]);
}
}
//初始化获取PageNum
const PageNum = Math.ceil(fastRet.count / 40);
//遍历批量请求
for (let i = 2; i <= PageNum; i++) {
const ret: Promise<WebApiGroupMemberRet> = RequestUtil.HttpGetJson<WebApiGroupMemberRet>('https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?st=' + (i - 1) * 40 + '&end=' + i * 40 + '&sort=1&gc=' + GroupCode + '&bkn=' + Bkn, 'POST', '', { 'Cookie': CookieValue });
retList.push(ret);
}
//批量等待
for (let i = 1; i <= PageNum; i++) {
const ret = await (retList[i]);
if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) {
continue;
}
for (const key in ret.mems) {
MemberData.push(ret.mems[key]);
}
}
} catch {
return MemberData;
}
return MemberData;
}
// public static async addGroupDigest(groupCode: string, msgSeq: string) {
// const url = `https://qun.qq.com/cgi-bin/group_digest/cancel_digest?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&msg_seq=${msgSeq}&msg_random=444021292`;
// const res = await this.request(url);
// return await res.json();
// }
// public async getGroupDigest(groupCode: string) {
// const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&page_start=0&page_limit=20`;
// const res = await this.request(url);
// return await res.json();
// }
static async setGroupNotice(GroupCode: string, Content: string = '') {
//https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=${bkn}
//qid=${群号}&bkn=${bkn}&text=${内容}&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const Bkn = WebApi.genBkn(CookiesObject.skey);
let ret: any = undefined;
const data = 'qid=' + GroupCode + '&bkn=' + Bkn + '&text=' + Content + '&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}';
const url = 'https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=' + Bkn;
try {
ret = await RequestUtil.HttpGetJson<any>(url, 'GET', '', { 'Cookie': CookieValue });
return ret;
} catch (e) {
return undefined;
}
return undefined;
}
static async getGrouptNotice(GroupCode: string): Promise<undefined | WebApiGroupNoticeRet> {
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const Bkn = WebApi.genBkn(CookiesObject.skey);
let ret: WebApiGroupNoticeRet | undefined = undefined;
//console.log(CookieValue);
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' + Bkn + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
try {
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': CookieValue });
if (ret?.ec !== 0) {
return undefined;
}
return ret;
} catch (e) {
return undefined;
}
return undefined;
}
static genBkn(sKey: string) {
sKey = sKey || '';
let hash = 5381;
for (let i = 0; i < sKey.length; i++) {
const code = sKey.charCodeAt(i);
hash = hash + (hash << 5) + code;
}
return (hash & 0x7FFFFFFF).toString();
}
@CacheClassFuncAsync(3600 * 1000, 'GroupHonorInfo')
static async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const Bkn = WebApi.genBkn(CookiesObject.skey);
async function getDataInternal(Internal_groupCode: string, Internal_type: number) {
let url = 'https://qun.qq.com/interactive/honorlist?gc=' + Internal_groupCode + '&type=' + Internal_type.toString();
let res = '';
let resJson;
try {
res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': CookieValue });
const match = res.match(/window\.__INITIAL_STATE__=(.*?);/);
if (match) {
resJson = JSON.parse(match[1].trim());
}
if (Internal_type === 1) {
return resJson?.talkativeList;
} else {
return resJson?.actorList;
}
} catch (e) {
logDebug('获取当前群荣耀失败', url, e);
}
return undefined;
}
let HonorInfo: any = { group_id: groupCode };
if (getType === WebHonorType.TALKACTIVE || getType === WebHonorType.ALL) {
try {
let RetInternal = await getDataInternal(groupCode, 1);
if (!RetInternal) {
throw new Error('获取龙王信息失败');
}
HonorInfo.current_talkative = {
user_id: RetInternal[0]?.uin,
avatar: RetInternal[0]?.avatar,
nickname: RetInternal[0]?.name,
day_count: 0,
description: RetInternal[0]?.desc
}
HonorInfo.talkative_list = [];
for (const talkative_ele of RetInternal) {
HonorInfo.talkative_list.push({
user_id: talkative_ele?.uin,
avatar: talkative_ele?.avatar,
description: talkative_ele?.desc,
day_count: 0,
nickname: talkative_ele?.name
});
}
} catch (e) {
logDebug(e);
}
}
if (getType === WebHonorType.PERFROMER || getType === WebHonorType.ALL) {
try {
let RetInternal = await getDataInternal(groupCode, 2);
if (!RetInternal) {
throw new Error('获取群聊之火失败');
}
HonorInfo.performer_list = [];
for (const performer_ele of RetInternal) {
HonorInfo.performer_list.push({
user_id: performer_ele?.uin,
nickname: performer_ele?.name,
avatar: performer_ele?.avatar,
description: performer_ele?.desc
});
}
} catch (e) {
logDebug(e);
}
}
if (getType === WebHonorType.PERFROMER || getType === WebHonorType.ALL) {
try {
let RetInternal = await getDataInternal(groupCode, 3);
if (!RetInternal) {
throw new Error('获取群聊炽焰失败');
}
HonorInfo.legend_list = [];
for (const legend_ele of RetInternal) {
HonorInfo.legend_list.push({
user_id: legend_ele?.uin,
nickname: legend_ele?.name,
avatar: legend_ele?.avatar,
desc: legend_ele?.description
});
}
} catch (e) {
logDebug('获取群聊炽焰失败', e);
}
}
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
try {
let RetInternal = await getDataInternal(groupCode, 6);
if (!RetInternal) {
throw new Error('获取快乐源泉失败');
}
HonorInfo.emotion_list = [];
for (const emotion_ele of RetInternal) {
HonorInfo.emotion_list.push({
user_id: emotion_ele?.uin,
nickname: emotion_ele?.name,
avatar: emotion_ele?.avatar,
desc: emotion_ele?.description
});
}
} catch (e) {
logDebug('获取快乐源泉失败', e);
}
}
//冒尖小春笋好像已经被tx扬了
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
HonorInfo.strong_newbie_list = [];
}
return HonorInfo;
}
}

478
src/core/src/core.ts Normal file
View File

@@ -0,0 +1,478 @@
import QQWrapper, { NodeIQQNTWrapperEngine, NodeIQQNTWrapperSession, NodeQQNTWrapperUtil } from '@/core/wrapper';
import {
NodeIKernelLoginService,
NodeIKernelBuddyService,
QuickLoginResult, passwordLoginArgType
} from '@/core/services';
import {
BuddyListener,
GroupListener,
LoginListener, MsgListener,
ProfileListener, SessionListener
} from '@/core/listeners';
import { DependsAdapter, DispatcherAdapter, GlobalAdapter, NodeIGlobalAdapter } from '@/core/adapters';
import path from 'node:path';
import os from 'node:os';
import fs from 'node:fs';
import { appid, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
import { hostname, systemVersion } from '@/common/utils/system';
import { genSessionConfig } from '@/core/sessionConfig';
import { dbUtil } from '@/common/utils/db';
import { sleep } from '@/common/utils/helper';
import crypto from 'node:crypto';
import { rawFriends, friends, groupMembers, groups, selfInfo, stat } from '@/core/data';
import { RawMessage } from '@/core/entities';
import { NTEventDispatch } from '@/common/utils/EventTask';
import {
enableConsoleLog,
enableFileLog,
log,
logDebug,
logError,
setLogLevel,
setLogSelfInfo
} from '@/common/utils/log';
import { napCatConfig } from '@/core/utils/config';
export interface OnLoginSuccess {
(uin: string, uid: string): void | Promise<void>;
}
export class NapCatCore {
public readonly session: NodeIQQNTWrapperSession;
public readonly util: NodeQQNTWrapperUtil;
public readonly engine: NodeIQQNTWrapperEngine;
private readonly loginListener: LoginListener;
private loginService: NodeIKernelLoginService;
private onLoginSuccessFuncList: OnLoginSuccess[] = [];
private proxyHandler = {
get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod
return (...args: unknown[]) => {
logDebug(`${target.constructor.name} has no method ${prop}`);
};
}
// 如果方法存在,正常返回
return Reflect.get(target, prop, receiver);
}
};
constructor() {
this.engine = new QQWrapper.NodeIQQNTWrapperEngine();
this.util = new QQWrapper.NodeQQNTWrapperUtil();
this.loginService = new QQWrapper.NodeIKernelLoginService();
this.session = new QQWrapper.NodeIQQNTWrapperSession();
this.loginListener = new LoginListener();
this.loginListener.onUserLoggedIn = (userid: string) => {
logError('当前账号(' + userid + ')已登录,无法重复登录');
};
this.initConfig();
this.loginListener.onQRCodeLoginSucceed = (arg) => {
this.initSession(arg.uin, arg.uid).then((r) => {
selfInfo.uin = arg.uin;
selfInfo.uid = arg.uid;
napCatConfig.read();
setLogLevel(napCatConfig.fileLogLevel, napCatConfig.consoleLogLevel);
enableFileLog(napCatConfig.fileLog);
enableConsoleLog(napCatConfig.consoleLog);
setLogSelfInfo(selfInfo);
const dataPath = path.resolve(this.dataPath, './NapCat/data');
fs.mkdirSync(dataPath, { recursive: true });
logDebug('本账号数据/缓存目录:', dataPath);
dbUtil.init(path.resolve(dataPath, `./${arg.uin}-v2.db`)).then(() => {
this.initDataListener();
this.onLoginSuccessFuncList.map(cb => {
new Promise((resolve, reject) => {
const result = cb(arg.uin, arg.uid);
if (result instanceof Promise) {
result.then(resolve).catch(reject);
}
}).then();
});
}).catch((e) => {
logError('数据库初始化失败', e);
});
// this.initDataListener();
}).catch((e) => {
logError('initSession failed', e);
throw new Error(`启动失败: ${JSON.stringify(e)}`);
});
};
// todo: 登录失败处理
this.loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
logError('登录失败(onQRCodeSessionFailed)', errMsg);
if (errType == 1 && errCode == 3) {
// 二维码过期刷新
this.loginService.getQRCodePicture();
}
};
this.loginListener.onLoginFailed = (args) => {
logError('登录失败(onLoginFailed)', args);
};
this.loginListener = new Proxy(this.loginListener, this.proxyHandler);
// 初始化流程initConfig, login, initSession, loginSuccess | initDataListener
this.loginService.addKernelLoginListener(new QQWrapper.NodeIKernelLoginListener(this.loginListener));
}
get dataPath(): string {
let result = this.util.getNTUserDataInfoConfig();
if (!result) {
result = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(result, { recursive: true });
}
return result;
}
get dataPathGlobal(): string {
return path.resolve(this.dataPath, './nt_qq/global');
}
private initConfig() {
this.engine.initWithDeskTopConfig({
base_path_prefix: '',
platform_type: 3,
app_type: 4,
app_version: qqVersionConfigInfo.curVersion,
os_version: 'Windows 10 Pro',
use_xlog: true,
qua: `V1_WIN_NQ_${qqVersionConfigInfo.curVersion.replace('-', '_')}_GW_B`,
global_path_config: {
desktopGlobalPath: this.dataPathGlobal,
},
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }
}, new QQWrapper.NodeIGlobalAdapter(new GlobalAdapter()));
this.loginService.initConfig({
machineId: '',
appid,
platVer: systemVersion,
commonPath: this.dataPathGlobal,
clientVer: qqVersionConfigInfo.curVersion,
hostName: hostname
});
}
private initSession(uin: string, uid: string): Promise<number> {
return new Promise(async (res, rej) => {
const sessionConfig = await genSessionConfig(uin, uid, this.dataPath);
const sessionListener = new SessionListener();
sessionListener.onSessionInitComplete = (r: unknown) => {
if ((r as number) === 0) {
return res(0);
}
rej(r);
};
// const oldOnSendOidbRepl = this.session.onSendOidbRepl;
// this.session.onSendOidbRepl = (...args: unknown[]) => {
// console.log('onSendOidbRepl', args);
// return oldOnSendOidbRepl(...args);
// };
this.session.init(sessionConfig,
new QQWrapper.NodeIDependsAdapter(new DependsAdapter()),
new QQWrapper.NodeIDispatcherAdapter(new DispatcherAdapter()),
new QQWrapper.NodeIKernelSessionListener(sessionListener)
);
try {
this.session.startNT(0);
} catch (__) { /* Empty */
try {
this.session.startNT();
} catch (e) {
rej('init failed ' + e);
}
}
});
}
private initDataListener() {
// 消息相关
interface LineDevice {
instanceId: number
clientType: number
devUid: string
}
interface KickedOffLineInfo {
appId: number
instanceId: number
sameDevice: boolean
tipsDesc: string
tipsTitle: string
kickedType: number
securityKickedType: number
}
const msgListener = new MsgListener();
msgListener.onLineDev = (Devices: LineDevice[]) => {
Devices.map((Device: LineDevice) => {
if (Device.clientType === 2) {
log('账号设备(' + Device.devUid + ') 在线状态变更');
}
});
};
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
// 下线通知
log('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
};
// msgListener.onMsgInfoListUpdate = (msgInfoList: RawMessage[]) => {
// stat.packet_received += 1;
// msgInfoList.map(msg => {
// console.log("onMsgInfoListUpdate", msg);
// if (msg.recallTime === '0') { // 不是撤回消息才入库/更新
// dbUtil.addMsg(msg).then().catch();
// }
// else {
// // 撤回的消息
// dbUtil.getMsgByLongId(msg.msgId).then(existMsg => {
// if (existMsg) {
// existMsg.recallTime = msg.recallTime;
// dbUtil.updateMsg(existMsg).then();
// }
// });
// }
// });
// };
msgListener.onAddSendMsg = (msg: RawMessage) => {
stat.packet_sent += 1;
stat.message_sent += 1;
stat.last_message_time = Math.floor(Date.now() / 1000);
};
msgListener.onRecvMsg = (msgList: RawMessage[]) => {
stat.packet_received += 1;
stat.message_received += msgList.length;
stat.last_message_time = Math.floor(Date.now() / 1000);
};
msgListener.onRecvSysMsg = (...args) => {
stat.packet_received += 1;
};
this.addListener(msgListener);
// 好友相关
const buddyListener = new BuddyListener();
buddyListener.onBuddyListChange = arg => {
rawFriends.length = 0;
rawFriends.push(...arg);
// console.log('onBuddyListChange', arg);
for (const categoryItem of arg) {
for (const friend of categoryItem.buddyList) {
// console.log("onBuddyListChange", friend)
const existFriend = friends.get(friend.uid);
if (existFriend) {
Object.assign(existFriend, friend);
}
else {
friends.set(friend.uid, friend);
}
}
// console.log("onBuddyListChange", friend)
}
};
this.addListener(buddyListener);
// 刷新一次好友列表
this.session.getBuddyService().getBuddyList(true).then(arg => {
// console.log('getBuddyList', arg);
});
interface SelfStatusInfo {
uid: string
status: number
extStatus: number
termType: number
netType: number
iconType: number
customStatus: any
setTime: string
}
const profileListener = new ProfileListener();
profileListener.onProfileDetailInfoChanged = (profile) => {
if (profile.uid === selfInfo.uid) {
Object.assign(selfInfo, profile);
}
};
profileListener.onSelfStatusChanged = (Info: SelfStatusInfo) => {
// if (Info.status == 20) {
// log("账号状态变更为离线")
// }
};
this.addListener(profileListener);
// 群相关
const groupListener = new GroupListener();
groupListener.onGroupListUpdate = (updateType, groupList) => {
// console.log("onGroupListUpdate", updateType, groupList)
groupList.map(g => {
const existGroup = groups.get(g.groupCode);
//群成员数量变化 应该刷新缓存
if (existGroup && g.memberCount === existGroup.memberCount) {
Object.assign(existGroup, g);
}
else {
groups.set(g.groupCode, g);
// 获取群成员
}
const sceneId = this.session.getGroupService().createMemberListScene(g.groupCode, 'groupMemberList_MainWindow');
this.session.getGroupService().getNextMemberList(sceneId!, undefined, 3000).then(r => {
// console.log(`get group ${g.groupCode} members`, r);
// r.result.infos.forEach(member => {
// });
// groupMembers.set(g.groupCode, r.result.infos);
});
});
};
groupListener.onMemberListChange = (arg) => {
// todo: 应该加一个内部自己维护的成员变动callback用于判断成员变化通知
const groupCode = arg.sceneId.split('_')[0];
if (groupMembers.has(groupCode)) {
const existMembers = groupMembers.get(groupCode)!;
arg.infos.forEach((member, uid) => {
const existMember = existMembers.get(uid);
if (existMember) {
Object.assign(existMember, member);
}
else {
existMembers!.set(uid, member);
}
//移除成员
if (member.isDelete) {
existMembers.delete(uid);
}
});
}
else {
groupMembers.set(groupCode, arg.infos);
}
// console.log('onMemberListChange', groupCode, arg);
};
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
// console.log('onMemberInfoChange', arg);
if (changeType === 0 && members.get(selfInfo.uid)?.isDelete) {
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
setTimeout(() => {
groups.delete(groupCode);
}, 5000);
}
const existMembers = groupMembers.get(groupCode);
if (existMembers) {
members.forEach((member, uid) => {
const existMember = existMembers.get(uid);
if (existMember) {
Object.assign(existMember, member);
}
else {
existMembers.set(uid, member);
}
//移除成员
if (member.isDelete) {
existMembers.delete(uid);
}
});
}
else {
groupMembers.set(groupCode, members);
}
};
this.addListener(groupListener);
}
addListener(
listener: BuddyListener | GroupListener | MsgListener | ProfileListener
): number {
// 根据listener的类型找到对应的service然后调用addListener方法
// logDebug('addListener', listener.constructor.name);
// proxy listener调用 listener 不存在的方法时不会报错
listener = new Proxy(listener, this.proxyHandler);
switch (listener.constructor.name) {
case 'BuddyListener': {
return this.session.getBuddyService().addKernelBuddyListener(new QQWrapper.NodeIKernelBuddyListener(listener as BuddyListener));
}
case 'GroupListener': {
return this.session.getGroupService().addKernelGroupListener(new QQWrapper.NodeIKernelGroupListener(listener as GroupListener));
}
case 'MsgListener': {
return this.session.getMsgService().addKernelMsgListener(new QQWrapper.NodeIKernelMsgListener(listener as MsgListener));
}
case 'ProfileListener': {
return this.session.getProfileService().addKernelProfileListener(new QQWrapper.NodeIKernelProfileListener(listener as ProfileListener));
}
default:
return -1;
}
}
onLoginSuccess(func: OnLoginSuccess) {
NTEventDispatch.init({
ListenerMap: QQWrapper,
WrapperSession: this.session,
});
this.onLoginSuccessFuncList.push(func);
}
async quickLogin(uin: string): Promise<QuickLoginResult> {
const loginList = await this.loginService.getLoginList();
if (loginList.result !== 0) throw new Error('没有可快速登录的QQ号');
const currentLogin = loginList.LocalLoginInfoList.find((item) => item.uin === uin);
if (!currentLogin || !currentLogin?.isQuickLogin) throw new Error(`${uin}快速登录不可用`);
await sleep(1000);
const loginRet = await this.loginService.quickLoginWithUin(uin);
if (!loginRet.result) {
throw new Error('快速登录失败 ' + loginRet.loginErrorInfo.errMsg);
}
return loginRet;
}
async qrLogin(cb: (url: string, base64: string, buffer: Buffer) => Promise<void>) {
return new Promise<{ url: string, base64: string, buffer: Buffer }>((resolve, reject) => {
this.loginListener.onQRCodeGetPicture = (arg) => {
const base64Data = arg.pngBase64QrcodeData.split('data:image/png;base64,')[1];
const buffer = Buffer.from(base64Data, 'base64');
cb(arg.qrcodeUrl, arg.pngBase64QrcodeData, buffer);
};
this.loginService.getQRCodePicture();
});
}
async passwordLogin(uin: string, password: string, proofSig?: string, proofRand?: string, proofSid?: string) {
const passwordMd5 = crypto.createHash('md5').update(password).digest('hex');
const loginArg: passwordLoginArgType = {
uin,
passwordMd5,
step: proofSig && proofRand && proofSid ? 1 : 0,
newDeviceLoginSig: '',
proofWaterSig: proofSig || '',
proofWaterRand: proofRand || '',
proofWaterSid: proofSid || ''
};
await this.loginService.getLoginList();
await sleep(1000);
const ret = await this.loginService.passwordLogin(loginArg);
switch (ret.result) {
case '0': { // Success
break;
}
case '140022008': { // CAPTCHA required
break;
}
case '4': // Mobile verify required
case '140022013': // Incorrect password
default:
}
}
async getQuickLoginList() {
const loginList = await this.loginService.getLoginList();
return loginList;
}
}
export const napCatCore = new NapCatCore();

97
src/core/src/data.ts Normal file
View File

@@ -0,0 +1,97 @@
import {
type Friend,
type Group,
type GroupMember, GroupNotify,
type SelfInfo,
BuddyCategoryType
} from './entities';
import { isNumeric } from '@/common/utils/helper';
import { NTQQGroupApi } from '@/core/apis';
export const selfInfo: SelfInfo = {
uid: '',
uin: '',
nick: '',
online: true
};
// 未来只在此处保留 selfInfo stat
// groupCode -> Group
export const groups: Map<string, Group> = new Map<string, Group>();
export function deleteGroup(groupQQ: string) {
groups.delete(groupQQ);
groupMembers.delete(groupQQ);
}
// 群号 -> 群成员map(uid=>GroupMember)
export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
// uid -> Friend 下面这俩个准备移除 QQ里面自带缓存
export const friends: Map<string, Friend> = new Map<string, Friend>();
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
export const groupNotifies: Record<string, GroupNotify> = {}; // flag->GroupNotify
export async function getGroup(qq: string | number): Promise<Group | undefined> {
let group = groups.get(qq.toString());
if (!group) {
try {
const _groups = await NTQQGroupApi.getGroups();
if (_groups.length) {
_groups.forEach(g => {
groups.set(g.groupCode, g);
});
}
} catch (e) {
return undefined;
}
}
group = groups.get(qq.toString());
return group;
}
export async function getGroupMember(groupQQ: string | number, memberUinOrUid: string | number) {
groupQQ = groupQQ.toString();
memberUinOrUid = memberUinOrUid.toString();
let members = groupMembers.get(groupQQ);
if (!members) {
try {
members = await NTQQGroupApi.getGroupMembers(groupQQ);
// 更新群成员列表
groupMembers.set(groupQQ, members);
}
catch (e) {
return null;
}
}
// log('getGroupMember', members);
const getMember = () => {
let member: GroupMember | undefined = undefined;
if (isNumeric(memberUinOrUid)) {
member = Array.from(members!.values()).find(member => member.uin === memberUinOrUid);
} else {
member = members!.get(memberUinOrUid);
}
return member;
};
let member = getMember();
if (!member) {
members = await NTQQGroupApi.getGroupMembers(groupQQ);
member = getMember();
}
return member;
}
// 考虑优化 移入QQ缓存或使用Api直接获取
export const tempGroupCodeMap: Record<string, string> = {}; // peerUid => 群号
// 保留 需要频繁读写
export const stat = {
packet_received: 0,
packet_sent: 0,
message_received: 0,
message_sent: 0,
last_message_time: 0,
// 以下字段无用, 全部为0
disconnect_times: 0,
lost_times: 0,
packet_lost: 0,
};

View File

@@ -0,0 +1,65 @@
import { ChatType } from './msg';
export interface CacheScanResult {
result: number;
size: [ // 单位为字节
string, // 系统总存储空间
string, // 系统可用存储空间
string, // 系统已用存储空间
string, // QQ总大小
string, // 「聊天与文件」大小
string, // 未知
string, // 「缓存数据」大小
string, // 「其他数据」大小
string, // 未知
]
}
export interface ChatCacheList {
pageCount: number;
infos: ChatCacheListItem[]
};
export interface ChatCacheListItem {
chatType: ChatType;
basicChatCacheInfo: ChatCacheListItemBasic;
guildChatCacheInfo: unknown[]; // TODO: 没用过频道所以不知道这里边的详细内容
}
export interface ChatCacheListItemBasic {
chatSize: string;
chatTime: string;
uid: string;
uin: string;
remarkName: string;
nickName: string;
chatType?: ChatType;
isChecked?: boolean;
}
export enum CacheFileType {
IMAGE = 0,
VIDEO = 1,
AUDIO = 2,
DOCUMENT = 3,
OTHER = 4,
}
export interface CacheFileList {
infos: CacheFileListItem[],
}
export interface CacheFileListItem {
fileSize: string;
fileTime: string;
fileKey: string;
elementId: string;
elementIdStr: string;
fileType: CacheFileType;
path: string;
fileName: string;
senderId: string;
previewPath: string;
senderName: string;
isChecked?: boolean;
}

View File

@@ -0,0 +1,370 @@
import {
AtType,
ElementType, FaceIndex, FaceType, PicElement,
PicType,
SendArkElement,
SendFaceElement,
SendFileElement, SendMarkdownElement, SendMarketFaceElement,
SendPicElement,
SendPttElement,
SendReplyElement,
SendTextElement,
SendVideoElement
} from './index';
import { promises as fs } from 'node:fs';
import ffmpeg from 'fluent-ffmpeg';
import { NTQQFileApi } from '@/core/apis/file';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import { log, logDebug, logError } from '@/common/utils/log';
import { defaultVideoThumb, getVideoInfo } from '@/common/utils/video';
import { encodeSilk } from '@/common/utils/audio';
import { isNull } from '@/common/utils/helper';
import faceConfig from './face_config.json';
import * as pathLib from 'node:path';
import { SignMiniApp } from '../apis';
export const mFaceCache = new Map<string, string>(); // emojiId -> faceName
export class SendMsgElementConstructor {
static text(content: string): SendTextElement {
return {
elementType: ElementType.TEXT,
elementId: '',
textElement: {
content,
atType: AtType.notAt,
atUid: '',
atTinyId: '',
atNtUid: '',
},
};
}
static at(atUid: string, atNtUid: string, atType: AtType, atName: string): SendTextElement {
return {
elementType: ElementType.TEXT,
elementId: '',
textElement: {
content: `@${atName}`,
atType,
atUid,
atTinyId: '',
atNtUid,
},
};
}
static reply(msgSeq: string, msgId: string, senderUin: string, senderUinStr: string): SendReplyElement {
return {
elementType: ElementType.REPLY,
elementId: '',
replyElement: {
replayMsgSeq: msgSeq, // raw.msgSeq
replayMsgId: msgId, // raw.msgId
senderUin: senderUin,
senderUinStr: senderUinStr,
}
};
}
static async pic(picPath: string, summary: string = '', subType: 0 | 1 = 0): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await NTQQFileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw '文件异常大小为0';
}
const imageSize = await NTQQFileApi.getImageSize(picPath);
const picElement: any = {
md5HexStr: md5,
fileSize: fileSize.toString(),
picWidth: imageSize?.width,
picHeight: imageSize?.height,
fileName: fileName,
sourcePath: path,
original: true,
picType: isGIF(picPath) ? PicType.gif : PicType.jpg,
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary
};
//logDebug('图片信息', picElement);
return {
elementType: ElementType.PIC,
elementId: '',
picElement,
};
}
static async file(filePath: string, fileName: string = '', folderId: string = ''): Promise<SendFileElement> {
const { md5, fileName: _fileName, path, fileSize } = await NTQQFileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw '文件异常大小为0';
}
const element: SendFileElement = {
elementType: ElementType.FILE,
elementId: '',
fileElement: {
fileName: fileName || _fileName,
folderId: folderId,
'filePath': path!,
'fileSize': (fileSize).toString(),
}
};
return element;
}
static async video(filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) {
throw '文件异常大小为0';
}
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
thumb = pathLib.dirname(thumb);
// log("thumb 目录", thumb)
let videoInfo = {
width: 1920, height: 1080,
time: 15,
format: 'mp4',
size: fileSize,
filePath
};
try {
videoInfo = await getVideoInfo(path);
//logDebug('视频信息', videoInfo);
} catch (e) {
logError('获取视频信息失败', e);
}
const createThumb = new Promise<string>((resolve, reject) => {
const thumbFileName = `${md5}_0.png`;
const thumbPath = pathLib.join(thumb, thumbFileName);
ffmpeg(filePath)
.on('end', () => {
})
.on('error', (err) => {
logDebug('获取视频封面失败,使用默认封面', err);
if (diyThumbPath) {
fs.copyFile(diyThumbPath, thumbPath).then(() => {
resolve(thumbPath);
}).catch(reject);
} else {
fs.writeFile(thumbPath, defaultVideoThumb).then(() => {
resolve(thumbPath);
}).catch(reject);
}
})
.screenshots({
timestamps: [0],
filename: thumbFileName,
folder: thumb,
size: videoInfo.width + 'x' + videoInfo.height
}).on('end', () => {
resolve(thumbPath);
});
});
const thumbPath = new Map();
const _thumbPath = await createThumb;
const thumbSize = (await fs.stat(_thumbPath)).size;
// log("生成缩略图", _thumbPath)
thumbPath.set(0, _thumbPath);
const thumbMd5 = await calculateFileMD5(_thumbPath);
const element: SendVideoElement = {
elementType: ElementType.VIDEO,
elementId: '',
videoElement: {
fileName: fileName || _fileName,
filePath: path,
videoMd5: md5,
thumbMd5,
fileTime: videoInfo.time,
thumbPath: thumbPath,
thumbSize,
thumbWidth: videoInfo.width,
thumbHeight: videoInfo.height,
fileSize: '' + fileSize,
// fileUuid: "",
// transferStatus: 0,
// progress: 0,
// invalidState: 0,
// fileSubId: "",
// fileBizId: null,
// originVideoMd5: "",
// fileFormat: 2,
// import_rich_media_context: null,
// sourceVideoCodecFormat: 2
}
};
return element;
}
static async ptt(pttPath: string): Promise<SendPttElement> {
const { converted, path: silkPath, duration } = await encodeSilk(pttPath);
// log("生成语音", silkPath, duration);
if (!silkPath) {
throw '语音转换失败, 请检查语音文件是否正常';
}
const { md5, fileName, path, fileSize } = await NTQQFileApi.uploadFile(silkPath!, ElementType.PTT);
if (fileSize === 0) {
throw '文件异常大小为0';
}
if (converted) {
fs.unlink(silkPath).then();
}
return {
elementType: ElementType.PTT,
elementId: '',
pttElement: {
fileName: fileName,
filePath: path,
md5HexStr: md5,
fileSize: fileSize,
// duration: Math.max(1, Math.round(fileSize / 1024 / 3)), // 一秒钟大概是3kb大小, 小于1秒的按1秒算
duration: duration || 1,
formatType: 1,
voiceType: 1,
voiceChangeType: 0,
canConvert2Text: true,
waveAmplitudes: [
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
],
fileSubId: '',
playState: 1,
autoConvertText: 0,
}
};
}
static face(faceId: number): SendFaceElement {
// 从face_config.json中获取表情名称
const sysFaces = faceConfig.sysface;
const emojiFaces = faceConfig.emoji;
const face: any = sysFaces.find((face) => face.QSid === faceId.toString());
faceId = parseInt(faceId.toString());
// let faceType = parseInt(faceId.toString().substring(0, 1));
let faceType = 1;
if (faceId >= 222) {
faceType = 2;
}
if (face.AniStickerType) {
faceType = 3;
}
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: faceId,
faceType,
faceText: face.QDes,
stickerId: face.AniStickerId,
stickerType: face.AniStickerType,
packId: face.AniStickerPackId,
sourceType: 1,
},
};
}
static mface(emojiPackageId: number, emojiId: string, key: string, faceName: string): SendMarketFaceElement {
return {
elementType: ElementType.MFACE,
marketFaceElement: {
emojiPackageId,
emojiId,
key,
faceName: faceName || mFaceCache.get(emojiId) || '[商城表情]',
},
};
}
static dice(resultId: number | null): SendFaceElement {
// 实际测试并不能控制结果
// 随机1到6
// if (isNull(resultId)) resultId = Math.floor(Math.random() * 6) + 1;
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: FaceIndex.dice,
faceType: FaceType.dice,
'faceText': '[骰子]',
'packId': '1',
'stickerId': '33',
'sourceType': 1,
'stickerType': 2,
// resultId: resultId.toString(),
'surpriseId': '',
// "randomType": 1,
}
};
}
// 猜拳(石头剪刀布)表情
static rps(resultId: number | null): SendFaceElement {
// 实际测试并不能控制结果
// if (isNull(resultId)) resultId = Math.floor(Math.random() * 3) + 1;
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
'faceIndex': FaceIndex.RPS,
'faceText': '[包剪锤]',
'faceType': 3,
'packId': '1',
'stickerId': '34',
'sourceType': 1,
'stickerType': 2,
// 'resultId': resultId.toString(),
'surpriseId': '',
// "randomType": 1,
}
};
}
static ark(data: any): SendArkElement {
if (typeof data !== 'string') {
data = JSON.stringify(data);
}
return {
elementType: ElementType.ARK,
elementId: '',
arkElement: {
bytesData: data,
linkInfo: null,
subElementType: null
}
};
}
static markdown(content: string): SendMarkdownElement {
return {
elementType: ElementType.MARKDOWN,
elementId: '',
markdownElement: {
content
}
};
}
static async miniapp(): Promise<SendArkElement> {
let ret = await SignMiniApp({
prompt: "Bot Test",
title: "Bot Test",
preview: "https://tianquan.gtimg.cn/qqAIAgent/item/7/square.png",
jumpUrl: "https://www.bilibili.com/",
tag: "Bot Test",
tagIcon: "https://tianquan.gtimg.cn/shoal/qqAIAgent/3e9d70c9-d98c-45b8-80b4-79d82971b514.png",
source: "Bot Test",
sourcelogo: "https://tianquan.gtimg.cn/shoal/qqAIAgent/3e9d70c9-d98c-45b8-80b4-79d82971b514.png"
});
return {
elementType: ElementType.ARK,
elementId: '',
arkElement: {
bytesData: ret,
linkInfo: null,
subElementType: null
}
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
import { QQLevel, Sex } from './user';
export interface Group {
groupCode: string,
maxMember: number,
memberCount: number,
groupName: string,
groupStatus: 0,
memberRole: 2,
isTop: boolean,
toppedTimestamp: string,
privilegeFlag: number, //65760
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
remarkName: string,
hasMemo: boolean,
groupShutupExpireTime: string, //"0",
personShutupExpireTime: string, //"0",
discussToGroupUin: string, //"0",
discussToGroupMaxMsgSeq: number,
discussToGroupTime: number,
groupFlagExt: number, //1073938496,
authGroupType: number, //0,
groupCreditLevel: number, //0,
groupFlagExt3: number, //0,
groupOwnerId: {
memberUin: string, //"0",
memberUid: string, //"u_fbf8N7aeuZEnUiJAbQ9R8Q"
}
}
export enum GroupMemberRole {
normal = 2,
admin = 3,
owner = 4
}
export interface GroupMember {
memberSpecialTitle?: string;
avatarPath: string;
cardName: string;
cardType: number;
isDelete: boolean;
nick: string;
qid: string;
remark: string;
role: GroupMemberRole; // 群主:4, 管理员:3群员:2
shutUpTime: number; // 禁言时间,单位是什么暂时不清楚
uid: string; // 加密的字符串
uin: string; // QQ号
isRobot: boolean;
sex?: Sex
qqLevel?: QQLevel
}

View File

@@ -0,0 +1,7 @@
export * from './user';
export * from './group';
export * from './msg';
export * from './notify';
export * from './cache';
export * from './constructor';

View File

@@ -0,0 +1,540 @@
import { GroupMemberRole } from './group';
export interface Peer {
chatType: ChatType;
peerUid: string; // 如果是群聊uid为群号私聊uid就是加密的字符串
guildId?: string;
}
export interface KickedOffLineInfo {
appId: number
instanceId: number
sameDevice: boolean
tipsDesc: string
tipsTitle: string
kickedType: number
securityKickedType: number
}
export interface GetFileListParam {
sortType: number
fileCount: number
startIndex: number
sortOrder: number
showOnlinedocFolder: number
}
export enum ElementType {
TEXT = 1,
PIC = 2,
FILE = 3,
PTT = 4,
VIDEO = 5,
FACE = 6,
REPLY = 7,
ARK = 10,
MFACE = 11,
MARKDOWN = 14
}
export interface SendTextElement {
elementType: ElementType.TEXT;
elementId: string;
textElement: {
content: string;
atType: number;
atUid: string;
atTinyId: string;
atNtUid: string;
};
}
export interface SendPttElement {
elementType: ElementType.PTT;
elementId: string;
pttElement: {
fileName: string;
filePath: string;
md5HexStr: string;
fileSize: number;
duration: number; // 单位是秒
formatType: number;
voiceType: number;
voiceChangeType: number;
canConvert2Text: boolean;
waveAmplitudes: number[];
fileSubId: string;
playState: number;
autoConvertText: number;
};
}
export enum PicType {
gif = 2000,
jpg = 1000
}
export enum PicSubType {
normal = 0, // 普通图片,大图
face = 1 // 表情包小图
}
export interface SendPicElement {
elementType: ElementType.PIC;
elementId: string;
picElement: {
md5HexStr: string;
fileSize: number | string;
picWidth: number;
picHeight: number;
fileName: string;
sourcePath: string;
original: boolean;
picType: PicType;
picSubType: PicSubType;
fileUuid: string;
fileSubId: string;
thumbFileSize: number;
summary: string;
};
}
export interface SendReplyElement {
elementType: ElementType.REPLY;
elementId: string;
replyElement: {
replayMsgSeq: string;
replayMsgId: string;
senderUin: string;
senderUinStr: string;
}
}
export interface SendFaceElement {
elementType: ElementType.FACE;
elementId: string;
faceElement: FaceElement;
}
export interface SendMarketFaceElement {
elementType: ElementType.MFACE;
marketFaceElement: MarketFaceElement;
}
export interface FileElement {
fileMd5?: string;
fileName: string;
filePath: string;
fileSize: string;
picHeight?: number;
picWidth?: number;
folderId?: string;
picThumbPath?: Map<number, string>;
file10MMd5?: string;
fileSha?: string;
fileSha3?: string;
fileUuid?: string;
fileSubId?: string;
thumbFileSize?: number;
fileBizId?: number
}
export interface SendFileElement {
elementType: ElementType.FILE;
elementId: string;
fileElement: FileElement;
}
export interface SendVideoElement {
elementType: ElementType.VIDEO;
elementId: string;
videoElement: VideoElement;
}
export interface SendArkElement {
elementType: ElementType.ARK;
elementId: string;
arkElement: ArkElement;
}
export interface SendMarkdownElement {
elementType: ElementType.MARKDOWN;
elementId: string;
markdownElement: MarkdownElement;
}
export type SendMessageElement = SendTextElement | SendPttElement |
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | SendVideoElement | SendArkElement | SendMarkdownElement
export enum AtType {
notAt = 0,
atAll = 1,
atUser = 2
}
export enum ChatType {
friend = 1,
group = 2,
chatDevice = 8, //移动设备?
temp = 100
}
// 来自Android分析
export enum ChatType2 {
KCHATTYPEADELIE = 42,
KCHATTYPEBUDDYNOTIFY = 5,
KCHATTYPEC2C = 1,
KCHATTYPECIRCLE = 113,
KCHATTYPEDATALINE = 8,
KCHATTYPEDATALINEMQQ = 134,
KCHATTYPEDISC = 3,
KCHATTYPEFAV = 41,
KCHATTYPEGAMEMESSAGE = 105,
KCHATTYPEGAMEMESSAGEFOLDER = 116,
KCHATTYPEGROUP = 2,
KCHATTYPEGROUPBLESS = 133,
KCHATTYPEGROUPGUILD = 9,
KCHATTYPEGROUPHELPER = 7,
KCHATTYPEGROUPNOTIFY = 6,
KCHATTYPEGUILD = 4,
KCHATTYPEGUILDMETA = 16,
KCHATTYPEMATCHFRIEND = 104,
KCHATTYPEMATCHFRIENDFOLDER = 109,
KCHATTYPENEARBY = 106,
KCHATTYPENEARBYASSISTANT = 107,
KCHATTYPENEARBYFOLDER = 110,
KCHATTYPENEARBYHELLOFOLDER = 112,
KCHATTYPENEARBYINTERACT = 108,
KCHATTYPEQQNOTIFY = 132,
KCHATTYPERELATEACCOUNT = 131,
KCHATTYPESERVICEASSISTANT = 118,
KCHATTYPESERVICEASSISTANTSUB = 201,
KCHATTYPESQUAREPUBLIC = 115,
KCHATTYPESUBSCRIBEFOLDER = 30,
KCHATTYPETEMPADDRESSBOOK = 111,
KCHATTYPETEMPBUSSINESSCRM = 102,
KCHATTYPETEMPC2CFROMGROUP = 100,
KCHATTYPETEMPC2CFROMUNKNOWN = 99,
KCHATTYPETEMPFRIENDVERIFY = 101,
KCHATTYPETEMPNEARBYPRO = 119,
KCHATTYPETEMPPUBLICACCOUNT = 103,
KCHATTYPETEMPWPA = 117,
KCHATTYPEUNKNOWN = 0,
KCHATTYPEWEIYUN = 40,
}
export interface PttElement {
canConvert2Text: boolean;
duration: number; // 秒数
fileBizId: null;
fileId: number; // 0
fileName: string; // "e4d09c784d5a2abcb2f9980bdc7acfe6.amr"
filePath: string; // "/Users//Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/nt_qq_a6b15c9820595d25a56c1633ce19ad40/nt_data/Ptt/2023-11/Ori/e4d09c784d5a2abcb2f9980bdc7acfe6.amr"
fileSize: string; // "4261"
fileSubId: string; // "0"
fileUuid: string; // "90j3z7rmRphDPrdVgP9udFBaYar#oK0TWZIV"
formatType: string; // 1
invalidState: number; // 0
md5HexStr: string; // "e4d09c784d5a2abcb2f9980bdc7acfe6"
playState: number; // 0
progress: number; // 0
text: string; // ""
transferStatus: number; // 0
translateStatus: number; // 0
voiceChangeType: number; // 0
voiceType: number; // 0
waveAmplitudes: number[];
}
export interface ArkElement {
bytesData: string;
linkInfo: null;
subElementType: null;
}
export const IMAGE_HTTP_HOST = 'https://gchat.qpic.cn';
export const IMAGE_HTTP_HOST_NT = 'https://multimedia.nt.qq.com.cn';
export interface PicElement {
picSubType?: number;
originImageUrl: string; // http url, 没有hosthost是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn
originImageMd5?: string;
sourcePath: string; // 图片本地路径
thumbPath: Map<number, string>;
picWidth: number;
picHeight: number;
fileSize: number;
fileName: string;
fileUuid: string;
md5HexStr?: string;
}
export enum GrayTipElementSubType {
INVITE_NEW_MEMBER = 12,
MEMBER_NEW_TITLE = 17
}
export interface GrayTipElement {
subElementType: GrayTipElementSubType;
revokeElement: {
operatorRole: string;
operatorUid: string;
operatorNick: string;
operatorRemark: string;
operatorMemRemark?: string;
wording: string; // 自定义的撤回提示语
}
aioOpGrayTipElement: TipAioOpGrayTipElement;
groupElement: TipGroupElement;
xmlElement: {
content: string;
templId: string;
};
jsonGrayTipElement: {
jsonStr: string;
};
}
export enum FaceType {
normal = 1, // 小黄脸
normal2 = 2, // 新小黄脸, 从faceIndex 222开始
dice = 3 // 骰子
}
export enum FaceIndex {
dice = 358,
RPS = 359 // 石头剪刀布
}
export interface FaceElement {
faceIndex: number;
faceType: FaceType;
faceText?: string;
packId?: string;
stickerId?: string;
sourceType?: number;
stickerType?: number;
resultId?: string;
surpriseId?: string;
randomType?: number;
}
export interface MarketFaceElement {
emojiPackageId: number;
faceName: string;
emojiId: string;
key: string;
}
export interface VideoElement {
filePath: string;
fileName: string;
videoMd5?: string;
thumbMd5?: string
fileTime?: number; // second
thumbSize?: number; // byte
fileFormat?: number; // 2表示mp4 参考下面条目
fileSize?: string; // byte
thumbWidth?: number;
thumbHeight?: number;
busiType?: 0; //
subBusiType?: 0; // 未知
thumbPath?: Map<number, any>;
transferStatus?: 0; // 未知
progress?: 0; // 下载进度?
invalidState?: 0; // 未知
fileUuid?: string; // 可以用于下载链接?
fileSubId?: string;
fileBizId?: null;
originVideoMd5?: string;
import_rich_media_context?: null;
sourceVideoCodecFormat?: number;
}
// export enum busiType{
// public static final int CREATOR_SHARE_ADV_XWORLD = 21;
// public static final int MINI_APP_MINI_GAME = 11;
// public static final int OFFICIAL_ACCOUNT_ADV = 4;
// public static final int OFFICIAL_ACCOUNT_ADV_GAME = 8;
// public static final int OFFICIAL_ACCOUNT_ADV_SHOP = 9;
// public static final int OFFICIAL_ACCOUNT_ADV_VIP = 7;
// public static final int OFFICIAL_ACCOUNT_LAYER_MASK_ADV = 14;
// public static final int OFFICIAL_ACCOUNT_SPORT = 13;
// public static final int OFFICIAL_ACCOUNT_TIAN_QI = 10;
// public static final int PC_QQTAB_ADV = 18;
// public static final int QIQIAOBAN_SDK = 15;
// public static final int QQ_CPS = 16;
// public static final int QQ_WALLET_CPS = 17;
// public static final int QZONE_FEEDS = 0;
// public static final int QZONE_PHOTO_TAIL = 2;
// public static final int QZONE_VIDEO_LAYER = 1;
// public static final int REWARD_GIFT_ADV = 6;
// public static final int REWARD_GROUPGIFT_ADV = 12;
// public static final int REWARD_PERSONAL_ADV = 5;
// public static final int WEISEE_OFFICIAL_ACCOUNT = 3;
// public static final int X_WORLD_CREATOR_ADV = 20;
// public static final int X_WORLD_QZONE_LAYER = 22;
// public static final int X_WORLD_VIDEO_ADV = 19;
// }
// export enum CategoryBusiType {
// _KCateBusiTypeDefault = 0,
// _kCateBusiTypeFaceCluster = 1,
// _kCateBusiTypeLabelCluster = 4,
// _kCateBusiTypeMonthCluster = 16,
// _kCateBusiTypePoiCluster = 2,
// _kCateBusiTypeYearCluster = 8,
// }
export enum viedo_type {
VIDEO_FORMAT_AFS = 7,
VIDEO_FORMAT_AVI = 1,
VIDEO_FORMAT_MKV = 4,
VIDEO_FORMAT_MOD = 9,
VIDEO_FORMAT_MOV = 8,
VIDEO_FORMAT_MP4 = 2,
VIDEO_FORMAT_MTS = 11,
VIDEO_FORMAT_RM = 6,
VIDEO_FORMAT_RMVB = 5,
VIDEO_FORMAT_TS = 10,
VIDEO_FORMAT_WMV = 3,
}
export interface MarkdownElement {
content: string;
}
export interface InlineKeyboardElementRowButton {
id: string;
label: string;
visitedLabel: string;
style: 1; // 未知
type: 2; // 未知
clickLimit: 0; // 未知
unsupportTips: string;
data: string;
atBotShowChannelList: boolean;
permissionType: number;
specifyRoleIds: [];
specifyTinyids: [];
isReply: false;
anchor: 0;
enter: false;
subscribeDataTemplateIds: []
}
export interface InlineKeyboardElement {
rows: [{
buttons: InlineKeyboardElementRowButton[]
}];
}
export interface TipAioOpGrayTipElement { // 这是什么提示来着?
operateType: number;
peerUid: string;
fromGrpCodeOfTmpChat: string;
}
export enum TipGroupElementType {
memberIncrease = 1,
kicked = 3, // 被移出群
ban = 8
}
// public final class MemberAddShowType {
// public static final int KOTHERADD = 0;
// public static final int KOTHERADDBYOTHERQRCODE = 2;
// public static final int KOTHERADDBYYOURQRCODE = 3;
// public static final int KOTHERINVITEOTHER = 5;
// public static final int KOTHERINVITEYOU = 6;
// public static final int KYOUADD = 1;
// public static final int KYOUADDBYOTHERQRCODE = 4;
// public static final int KYOUALREADYMEMBER = 8;
// public static final int KYOUINVITEOTHER = 7;
// }
export interface TipGroupElement {
type: TipGroupElementType; // 1是表示有人加入群; 自己加入群也会收到这个
role: 0; // 暂时不知
groupName: string; // 暂时获取不到
memberUid: string;
memberNick: string;
memberRemark: string;
adminUid: string;
adminNick: string;
adminRemark: string;
createGroup: null;
memberAdd?: {
showType: 1;
otherAdd: null;
otherAddByOtherQRCode: null;
otherAddByYourQRCode: null;
youAddByOtherQRCode: null;
otherInviteOther: null;
otherInviteYou: null;
youInviteOther: null
};
shutUp?: {
curTime: string;
duration: string; // 禁言时间,秒
admin: {
uid: string;
card: string;
name: string;
role: GroupMemberRole
};
member: {
uid: string
card: string;
name: string;
role: GroupMemberRole
}
}
}
export interface MultiForwardMsgElement {
xmlContent: string; // xml格式的消息内容
resId: string;
fileName: string;
}
export interface RawMessage {
// int32, 自己维护的消息id
id?: number;
msgId: string;
// 时间戳,秒
msgTime: string;
msgSeq: string;
msgType: number;
subMsgType: number;
senderUid: string;
senderUin: string; // 发送者QQ号
peerUid: string; // 群号 或者 QQ uid
peerUin: string; // 群号 或者 发送者QQ号
sendNickName: string;
sendMemberName?: string; // 发送者群名片
chatType: ChatType;
sendStatus?: number; // 消息状态别人发的2是已撤回自己发的2是已发送
recallTime: string; // 撤回时间, "0"是没有撤回
elements: {
elementId: string;
elementType: ElementType;
replyElement: {
senderUid: string; // 原消息发送者QQ号
sourceMsgIsIncPic: boolean; // 原消息是否有图片
sourceMsgText: string;
replayMsgSeq: string; // 源消息的msgSeq可以通过这个找到源消息的msgId
};
textElement: {
atType: AtType;
atUid: string; // QQ号
content: string;
atNtUid: string; // uid号
};
picElement: PicElement;
pttElement: PttElement;
arkElement: ArkElement;
grayTipElement: GrayTipElement;
faceElement: FaceElement;
videoElement: VideoElement;
fileElement: FileElement;
marketFaceElement: MarketFaceElement;
inlineKeyboardElement: InlineKeyboardElement;
markdownElement: MarkdownElement;
multiForwardMsgElement: MultiForwardMsgElement;
}[];
}

View File

@@ -0,0 +1,115 @@
export enum GroupNotifyTypes {
INVITE_ME = 1,
INVITED_JOIN = 4, // 有人接受了邀请入群
JOIN_REQUEST = 7,
ADMIN_SET = 8,
KICK_MEMBER = 9,
MEMBER_EXIT = 11, // 主动退出
ADMIN_UNSET = 12,
ADMIN_UNSET_OTHER = 13, // 其他人取消管理员
}
export interface GroupNotifies {
doubt: boolean;
nextStartSeq: string;
notifies: GroupNotify[];
}
export enum GroupNotifyStatus {
IGNORE = 0,
WAIT_HANDLE = 1,
APPROVE = 2,
REJECT = 3
}
export interface GroupNotify {
time: number; // 自己添加的字段,时间戳,毫秒, 用于判断收到短时间内收到重复的notify
seq: string; // 唯一标识符转成数字再除以1000应该就是时间戳
type: GroupNotifyTypes;
status: GroupNotifyStatus; // 0是已忽略1是未处理2是已同意
group: { groupCode: string; groupName: string };
user1: { uid: string; nickName: string }; // 被设置管理员的人
user2: { uid: string; nickName: string }; // 操作者
actionUser: { uid: string; nickName: string }; //未知
actionTime: string;
invitationExt: {
srcType: number; // 0?未知
groupCode: string; waitStatus: number
};
postscript: string; // 加群用户填写的验证信息
repeatSeqs: [];
warningTips: string
}
export enum GroupRequestOperateTypes {
approve = 1,
reject = 2
}
export enum BuddyReqType {
KMEINITIATOR,
KPEERINITIATOR,
KMEAGREED,
KMEAGREEDANDADDED,
KPEERAGREED,
KPEERAGREEDANDADDED,
KPEERREFUSED,
KMEREFUSED,
KMEIGNORED,
KMEAGREEANYONE,
KMESETQUESTION,
KMEAGREEANDADDFAILED,
KMSGINFO,
KMEINITIATORWAITPEERCONFIRM
}
export interface FriendRequest {
isDecide: boolean;
friendUid: string;
reqType: BuddyReqType,
reqTime: string; // 时间戳;秒
extWords: string; // 申请人填写的验证消息
isUnread: boolean;
friendNick: string;
sourceId: number;
groupCode: string
}
export interface FriendRequestNotify {
unreadNums: number;
buddyReqs: FriendRequest[];
}
export enum MemberExtSourceType {
DEFAULTTYPE = 0,
TITLETYPE = 1,
NEWGROUPTYPE = 2,
}
export interface GroupExtParam {
groupCode: string
seq: string
beginUin: string
dataTime: string
uinList: Array<string>
uinNum: string
groupType: string
richCardNameVer: string
sourceType: MemberExtSourceType
memberExtFilter: {
memberLevelInfoUin: number
memberLevelInfoPoint: number
memberLevelInfoActiveDay: number
memberLevelInfoLevel: number
memberLevelInfoName: number
levelName: number
dataTime: number
userShowFlag: number
sysShowFlag: number
timeToUpdate: number
nickName: number
specialTitle: number
levelNameNew: number
userShowFlagNew: number
msgNeedField: number
cmdUinFlagExt3Grocery: number
memberIcon: number
memberInfoSeq: number
}
}

View File

@@ -0,0 +1,162 @@
export enum Sex {
male = 1,
female = 2,
unknown = 255,
}
export interface BuddyCategoryType {
categoryId: number;
categroyName: string;
categroyMbCount: number;
buddyList: User[];
}
export interface ModifyProfileParams {
nick: string,
longNick: string,
sex: Sex,
birthday: { birthday_year: string, birthday_month: string, birthday_day: string },
location: any//undefined
}
export interface BuddyProfileLikeReq {
friendUids: string[];
basic: number;
vote: number;
favorite: number;
userProfile: number;
type: number;
start: number;
limit: number;
}
export interface QQLevel {
crownNum: number;
sunNum: number;
moonNum: number;
starNum: number
}
export interface User {
uid: string; // 加密的字符串
uin: string; // QQ号
nick: string;
avatarUrl?: string;
longNick?: string; // 签名
remark?: string;
sex?: Sex;
qqLevel?: QQLevel;
qid?: string
birthday_year?: number;
birthday_month?: number;
birthday_day?: number;
topTime?: string;
constellation?: number;
shengXiao?: number;
kBloodType?: number;
homeTown?: string; //"0-0-0";
makeFriendCareer?: number;
pos?: string;
eMail?: string
phoneNum?: string;
college?: string;
country?: string;
province?: string;
city?: string;
postCode?: string;
address?: string;
isBlock?: boolean;
isSpecialCareOpen?: boolean;
isSpecialCareZone?: boolean;
ringId?: string;
regTime?: number;
interest?: string;
labels?: string[];
isHideQQLevel?: number;
privilegeIcon?: {
jumpUrl: string;
openIconList: unknown[];
closeIconList: unknown[]
};
photoWall?: {
picList: unknown[]
};
vipFlag?: boolean;
yearVipFlag?: boolean;
svipFlag?: boolean;
vipLevel?: number;
status?: number;
qidianMasterFlag?: number;
qidianCrewFlag?: number;
qidianCrewFlag2?: number;
extStatus?: number;
recommendImgFlag?: number;
disableEmojiShortCuts?: number;
pendantId?: string;
}
export interface SelfInfo extends User {
online?: boolean;
}
export interface Friend extends User { }
export enum BizKey {
KPRIVILEGEICON,
KPHOTOWALL
}
export interface UserDetailInfoByUin {
result: number,
errMsg: string,
info: {
uid: string,//这个没办法用
qid: string,
uin: string,
nick: string,
remark: string,
longNick: string,
avatarUrl: string,
birthday_year: number,
birthday_month: number,
birthday_day: number,
sex: number,//0
topTime: string,
constellation: number,
shengXiao: number,
kBloodType: number,
homeTown: string,
makeFriendCareer: number,
pos: string,
eMail: string,
phoneNum: string,
college: string,
country: string,
province: string,
city: string,
postCode: string,
address: string,
isBlock: boolean,
isSpecialCareOpen: boolean,
isSpecialCareZone: boolean,
ringId: string,
regTime: number,
interest: string,
termType: number,
labels: any[],
qqLevel: { crownNum: number, sunNum: number, moonNum: number, starNum: number },
isHideQQLevel: number,
privilegeIcon: { jumpUrl: string, openIconList: any[], closeIconList: any[] },
isHidePrivilegeIcon: number,
photoWall: { picList: any[] },
vipFlag: boolean,
yearVipFlag: boolean,
svipFlag: boolean,
vipLevel: number,
status: number,
qidianMasterFlag: number,
qidianCrewFlag: number,
qidianCrewFlag2: number,
extStatus: number,
recommendImgFlag: number,
disableEmojiShortCuts: number,
pendantId: string,
vipNameColorId: string
}
}

18
src/core/src/index.ts Normal file
View File

@@ -0,0 +1,18 @@
import QQWrapper from './wrapper';
export * from './adapters';
export * from './apis';
export * from './entities';
export * from './listeners';
export * from './services';
export * as Adapters from './adapters';
export * as APIs from './apis';
export * as Entities from './entities';
export * as Listeners from './listeners';
export * as Services from './services';
export { QQWrapper as Wrapper };
export * as WrapperInterface from './wrapper';
export * as SessionConfig from './sessionConfig';
export { napCatCore } from './core';

View File

@@ -0,0 +1,97 @@
import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
export type OnBuddyChangeParams = BuddyCategoryType[]
interface IBuddyListener {
onBuddyListChange(arg: OnBuddyChangeParams): void,
onBuddyInfoChange(arg: unknown): void,
onBuddyDetailInfoChange(arg: unknown): void,
onNickUpdated(arg: unknown): void,
onBuddyRemarkUpdated(arg: unknown): void,
onAvatarUrlUpdated(arg: unknown): void,
onBuddyReqChange(arg: FriendRequestNotify): void,
onBuddyReqUnreadCntChange(arg: unknown): void,
onCheckBuddySettingResult(arg: unknown): void,
onAddBuddyNeedVerify(arg: unknown): void,
onSmartInfos(arg: unknown): void,
onSpacePermissionInfos(arg: unknown): void,
onDoubtBuddyReqChange(arg: unknown): void,
onDoubtBuddyReqUnreadNumChange(arg: unknown): void,
onBlockChanged(arg: unknown): void,
onAddMeSettingChanged(arg: unknown): void,
onDelBatchBuddyInfos(arg: unknown): void
}
export interface NodeIKernelBuddyListener extends IBuddyListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IBuddyListener): NodeIKernelBuddyListener;
}
export class BuddyListener implements IBuddyListener {
onAddBuddyNeedVerify(arg: unknown) {
}
onAddMeSettingChanged(arg: unknown) {
}
onAvatarUrlUpdated(arg: unknown) {
}
onBlockChanged(arg: unknown) {
}
onBuddyDetailInfoChange(arg: unknown) {
}
onBuddyInfoChange(arg: unknown) {
}
onBuddyListChange(arg: OnBuddyChangeParams): void {
}
onBuddyRemarkUpdated(arg: unknown): void {
}
onBuddyReqChange(arg: FriendRequestNotify): void {
}
onBuddyReqUnreadCntChange(arg: unknown): void {
}
onCheckBuddySettingResult(arg: unknown): void {
}
onDelBatchBuddyInfos(arg: unknown): void {
}
onDoubtBuddyReqChange(arg: unknown): void {
}
onDoubtBuddyReqUnreadNumChange(arg: unknown): void {
}
onNickUpdated(arg: unknown): void {
}
onSmartInfos(arg: unknown): void {
}
onSpacePermissionInfos(arg: unknown): void {
}
}

View File

@@ -0,0 +1,27 @@
export interface IKernelFileAssistantListener {
onFileStatusChanged(...args: unknown[]): unknown;
onSessionListChanged(...args: unknown[]): unknown;
onSessionChanged(...args: unknown[]): unknown;
onFileListChanged(...args: unknown[]): unknown;
onFileSearch(...args: unknown[]): unknown;
}
export interface NodeIKernelFileAssistantListener extends IKernelFileAssistantListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IKernelFileAssistantListener): NodeIKernelFileAssistantListener;
}
export class KernelFileAssistantListener implements IKernelFileAssistantListener {
onFileStatusChanged(...args: unknown[]) { }
onSessionListChanged(...args: unknown[]) { }
onSessionChanged(...args: unknown[]) { }
onFileListChanged(...args: unknown[]) { }
onFileSearch(...args: unknown[]) { }
}

View File

@@ -0,0 +1,240 @@
import { Group, GroupMember, GroupNotify } from '@/core/entities';
interface IGroupListener {
onGroupListUpdate(updateType: number, groupList: Group[]): void;
onGroupExtListUpdate(...args: unknown[]): void;
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]): void;
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): void;
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void;
onGroupDetailInfoChange(...args: unknown[]): void;
onGroupAllInfoChange(...args: unknown[]): void;
onGroupsMsgMaskResult(...args: unknown[]): void;
onGroupConfMemberChange(...args: unknown[]): void;
onGroupBulletinChange(...args: unknown[]): void;
onGetGroupBulletinListResult(...args: unknown[]): void;
onMemberListChange(arg: {
sceneId: string,
ids: string[],
infos: Map<string, GroupMember>,
finish: boolean,
hasRobot: boolean
}): void;
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void;
onSearchMemberChange(...args: unknown[]): void;
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void;
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void;
onGroupStatisticInfoChange(...args: unknown[]): void;
onJoinGroupNotify(...args: unknown[]): void;
onShutUpMemberListChanged(...args: unknown[]): void;
onGroupBulletinRemindNotify(...args: unknown[]): void;
onGroupFirstBulletinNotify(...args: unknown[]): void;
onJoinGroupNoVerifyFlag(...args: unknown[]): void;
onGroupArkInviteStateResult(...args: unknown[]): void;
// 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void;
}
export interface NodeIKernelGroupListener extends IGroupListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IGroupListener): NodeIKernelGroupListener;
}
export class GroupListener implements IGroupListener {
// 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void {
}
onGetGroupBulletinListResult(...args: unknown[]) {
}
onGroupAllInfoChange(...args: unknown[]) {
}
onGroupBulletinChange(...args: unknown[]) {
}
onGroupBulletinRemindNotify(...args: unknown[]) {
}
onGroupArkInviteStateResult(...args: unknown[]) {
}
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) {
}
onGroupConfMemberChange(...args: unknown[]) {
}
onGroupDetailInfoChange(...args: unknown[]) {
}
onGroupExtListUpdate(...args: unknown[]) {
}
onGroupFirstBulletinNotify(...args: unknown[]) {
}
onGroupListUpdate(updateType: number, groupList: Group[]) {
}
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]) {
}
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) {
}
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
}
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
}
onGroupsMsgMaskResult(...args: unknown[]) {
}
onGroupStatisticInfoChange(...args: unknown[]) {
}
onJoinGroupNotify(...args: unknown[]) {
}
onJoinGroupNoVerifyFlag(...args: unknown[]) {
}
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
}
onMemberListChange(arg: {
sceneId: string,
ids: string[],
infos: Map<string, GroupMember>, // uid -> GroupMember
finish: boolean,
hasRobot: boolean
}) {
}
onSearchMemberChange(...args: unknown[]) {
}
onShutUpMemberListChanged(...args: unknown[]) {
}
}
export class DebugGroupListener implements IGroupListener {
onGroupMemberLevelInfoChange(...args: unknown[]): void {
console.log('onGroupMemberLevelInfoChange:', ...args);
}
onGetGroupBulletinListResult(...args: unknown[]) {
console.log('onGetGroupBulletinListResult:', ...args);
}
onGroupAllInfoChange(...args: unknown[]) {
console.log('onGroupAllInfoChange:', ...args);
}
onGroupBulletinChange(...args: unknown[]) {
console.log('onGroupBulletinChange:', ...args);
}
onGroupBulletinRemindNotify(...args: unknown[]) {
console.log('onGroupBulletinRemindNotify:', ...args);
}
onGroupArkInviteStateResult(...args: unknown[]) {
console.log('onGroupArkInviteStateResult:', ...args);
}
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) {
console.log('onGroupBulletinRichMediaDownloadComplete:', ...args);
}
onGroupConfMemberChange(...args: unknown[]) {
console.log('onGroupConfMemberChange:', ...args);
}
onGroupDetailInfoChange(...args: unknown[]) {
console.log('onGroupDetailInfoChange:', ...args);
}
onGroupExtListUpdate(...args: unknown[]) {
console.log('onGroupExtListUpdate:', ...args);
}
onGroupFirstBulletinNotify(...args: unknown[]) {
console.log('onGroupFirstBulletinNotify:', ...args);
}
onGroupListUpdate(...args: unknown[]) {
console.log('onGroupListUpdate:', ...args);
}
onGroupNotifiesUpdated(...args: unknown[]) {
console.log('onGroupNotifiesUpdated:', ...args);
}
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) {
console.log('onGroupBulletinRichMediaProgressUpdate:', ...args);
}
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
}
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]){
console.log('onGroupSingleScreenNotifies:');
}
onGroupsMsgMaskResult(...args: unknown[]) {
console.log('onGroupsMsgMaskResult:', ...args);
}
onGroupStatisticInfoChange(...args: unknown[]) {
console.log('onGroupStatisticInfoChange:', ...args);
}
onJoinGroupNotify(...args: unknown[]) {
console.log('onJoinGroupNotify:', ...args);
}
onJoinGroupNoVerifyFlag(...args: unknown[]) {
console.log('onJoinGroupNoVerifyFlag:', ...args);
}
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
console.log('onMemberInfoChange:', groupCode, changeType, members);
}
onMemberListChange(...args: unknown[]) {
console.log('onMemberListChange:', ...args);
}
onSearchMemberChange(...args: unknown[]) {
console.log('onSearchMemberChange:', ...args);
}
onShutUpMemberListChanged(...args: unknown[]) {
console.log('onShutUpMemberListChanged:', ...args);
}
}

View File

@@ -0,0 +1,108 @@
export interface IKernelLoginListener {
onLoginConnected(...args: any[]): void;
onLoginDisConnected(...args: any[]): void;
onLoginConnecting(...args: any[]): void;
onQRCodeGetPicture(...args: any[]): void;
onQRCodeLoginPollingStarted(...args: any[]): void;
onQRCodeSessionUserScaned(...args: any[]): void;
onQRCodeLoginSucceed(...args: any[]): void;
onQRCodeSessionFailed(...args: any[]): void;
onLoginFailed(...args: any[]): void;
onLogoutSucceed(...args: any[]): void;
onLogoutFailed(...args: any[]): void;
onUserLoggedIn(...args: any[]): void;
onQRCodeSessionQuickLoginFailed(...args: any[]): void;
onPasswordLoginFailed(...args: any[]): void;
OnConfirmUnusualDeviceFailed(...args: any[]): void;
onQQLoginNumLimited(...args: any[]): void;
onLoginState(...args: any[]): void;
}
export interface NodeIKernelLoginListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IKernelLoginListener): NodeIKernelLoginListener;
}
export class LoginListener implements IKernelLoginListener {
onLoginConnected(...args: any[]): void {
}
onLoginDisConnected(...args: any[]): void {
}
onLoginConnecting(...args: any[]): void {
}
onQRCodeGetPicture(arg: { pngBase64QrcodeData: string, qrcodeUrl: string }): void {
// let base64Data: string = arg.pngBase64QrcodeData
// base64Data = base64Data.split("data:image/png;base64,")[1]
// let buffer = Buffer.from(base64Data, 'base64')
// console.log("onQRCodeGetPicture", arg);
}
onQRCodeLoginPollingStarted(...args: any[]): void {
}
onQRCodeSessionUserScaned(...args: any[]): void {
}
onQRCodeLoginSucceed(arg: QRCodeLoginSucceedResult): void {
}
onQRCodeSessionFailed(...args: any[]): void {
}
onLoginFailed(...args: any[]): void {
}
onLogoutSucceed(...args: any[]): void {
}
onLogoutFailed(...args: any[]): void {
}
onUserLoggedIn(...args: any[]): void {
}
onQRCodeSessionQuickLoginFailed(...args: any[]): void {
}
onPasswordLoginFailed(...args: any[]): void {
}
OnConfirmUnusualDeviceFailed(...args: any[]): void {
}
onQQLoginNumLimited(...args: any[]): void {
}
onLoginState(...args: any[]): void {
}
}
export interface QRCodeLoginSucceedResult {
account: string;
mainAccount: string;
uin: string; //拿UIN
uid: string; //拿UID
nickName: string; //一般是空的 拿不到
gender: number;
age: number;
faceUrl: string;//一般是空的 拿不到
}

View File

@@ -0,0 +1,478 @@
import { ChatType, RawMessage } from '@/core/entities';
export interface OnRichMediaDownloadCompleteParams {
fileModelId: string,
msgElementId: string,
msgId: string,
fileId: string,
fileProgress: string, // '0'
fileSpeed: string, // '0'
fileErrCode: string, // '0'
fileErrMsg: string,
fileDownType: number, // 暂时未知
thumbSize: number,
filePath: string,
totalSize: string,
trasferStatus: number,
step: number,
commonFileInfo: unknown | null,
fileSrvErrCode: string,
clientMsg: string,
businessId: number,
userTotalSpacePerDay: unknown | null,
userUsedSpacePerDay: unknown | null
}
export interface onGroupFileInfoUpdateParamType {
retCode: number
retMsg: string
clientWording: string
isEnd: boolean
item: Array<any>
allFileCount: string
nextIndex: string
reqId: string
}
// {
// sessionType: 1,
// chatType: 100,
// peerUid: 'u_PVQ3tl6K78xxxx',
// groupCode: '809079648',
// fromNick: '拾xxxx,
// sig: '0x'
// }
export interface TempOnRecvParams{
sessionType: number,//1
chatType: ChatType,//100
peerUid: string,//uid
groupCode: string,//gc
fromNick: string,//gc name
sig: string,
}
export interface IKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage): void;
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): void;
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): void;
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): void;
onContactUnreadCntUpdate(hashMap: unknown): void;
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): void;
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): void;
onEmojiDownloadComplete(emojiNotifyInfo: unknown): void;
onEmojiResourceUpdate(emojiResourceInfo: unknown): void;
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
onFileMsgCome(arrayList: unknown): void;
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
onFirstViewGroupGuildMapping(arrayList: unknown): void;
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): void;
onGroupFileInfoAdd(groupItem: unknown): void;
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType): void;
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): void;
onGroupTransferInfoAdd(groupItem: unknown): void;
onGroupTransferInfoUpdate(groupFileListResult: unknown): void;
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): void;
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): void;
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): void;
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): void;
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): void;
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): void;
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): void;
onInputStatusPush(inputStatusInfo: unknown): void;
onKickedOffLine(kickedInfo: unknown): void;
onLineDev(arrayList: unknown): void;
onLogLevelChanged(j2: unknown): void;
onMsgAbstractUpdate(arrayList: unknown): void;
onMsgBoxChanged(arrayList: unknown): void;
onMsgDelete(contact: unknown, arrayList: unknown): void;
onMsgEventListUpdate(hashMap: unknown): void;
onMsgInfoListAdd(arrayList: unknown): void;
onMsgInfoListUpdate(msgList: RawMessage[]): void;
onMsgQRCodeStatusChanged(i2: unknown): void;
onMsgRecall(i2: unknown, str: unknown, j2: unknown): void;
onMsgSecurityNotify(msgRecord: unknown): void;
onMsgSettingUpdate(msgSetting: unknown): void;
onNtFirstViewMsgSyncEnd(): void;
onNtMsgSyncEnd(): void;
onNtMsgSyncStart(): void;
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
onRecvGroupGuildFlag(i2: unknown): void;
onRecvMsg(...arrayList: unknown[]): void;
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): void;
onRecvOnlineFileMsg(arrayList: unknown): void;
onRecvS2CMsg(arrayList: unknown): void;
onRecvSysMsg(arrayList: unknown): void;
onRecvUDCFlag(i2: unknown): void;
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): void;
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): void;
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown): void;
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): void;
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): void;
onUnreadCntAfterFirstView(hashMap: unknown): void;
onUnreadCntUpdate(hashMap: unknown): void;
onUserChannelTabStatusChanged(z: unknown): void;
onUserOnlineStatusChanged(z: unknown): void;
onUserTabStatusChanged(arrayList: unknown): void;
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
// 第一次发现于Linux
onUserSecQualityChanged(...args: unknown[]): void;
onMsgWithRichLinkInfoUpdate(...args: unknown[]): void;
onRedTouchChanged(...args: unknown[]): void;
// 第一次发现于Win 9.9.9 23159
onBroadcastHelperProgerssUpdate(...args: unknown[]): void;
}
export interface NodeIKernelMsgListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IKernelMsgListener): NodeIKernelMsgListener;
}
export class MsgListener implements IKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage) {
}
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown) {
}
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown) {
}
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown) {
}
onContactUnreadCntUpdate(hashMap: unknown) {
}
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown) {
}
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown) {
}
onEmojiDownloadComplete(emojiNotifyInfo: unknown) {
}
onEmojiResourceUpdate(emojiResourceInfo: unknown) {
}
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown) {
}
onFileMsgCome(arrayList: unknown) {
}
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown) {
}
onFirstViewGroupGuildMapping(arrayList: unknown) {
}
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown) {
}
onGroupFileInfoAdd(groupItem: unknown) {
}
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType) {
}
onGroupGuildUpdate(groupGuildNotifyInfo: unknown) {
}
onGroupTransferInfoAdd(groupItem: unknown) {
}
onGroupTransferInfoUpdate(groupFileListResult: unknown) {
}
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown) {
}
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown) {
}
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown) {
}
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown) {
}
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown) {
}
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown) {
}
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown) {
}
onInputStatusPush(inputStatusInfo: unknown) {
}
onKickedOffLine(kickedInfo: unknown) {
}
onLineDev(arrayList: unknown) {
}
onLogLevelChanged(j2: unknown) {
}
onMsgAbstractUpdate(arrayList: unknown) {
}
onMsgBoxChanged(arrayList: unknown) {
}
onMsgDelete(contact: unknown, arrayList: unknown) {
}
onMsgEventListUpdate(hashMap: unknown) {
}
onMsgInfoListAdd(arrayList: unknown) {
}
onMsgInfoListUpdate(msgList: RawMessage[]) {
}
onMsgQRCodeStatusChanged(i2: unknown) {
}
onMsgRecall(i2: unknown, str: unknown, j2: unknown) {
}
onMsgSecurityNotify(msgRecord: unknown) {
}
onMsgSettingUpdate(msgSetting: unknown) {
}
onNtFirstViewMsgSyncEnd() {
}
onNtMsgSyncEnd() {
}
onNtMsgSyncStart() {
}
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown) {
}
onRecvGroupGuildFlag(i2: unknown) {
}
onRecvMsg(arrayList: RawMessage[]) {
}
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown) {
}
onRecvOnlineFileMsg(arrayList: unknown) {
}
onRecvS2CMsg(arrayList: unknown) {
}
onRecvSysMsg(arrayList: unknown) {
}
onRecvUDCFlag(i2: unknown) {
}
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) {
}
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown) {
}
onRichMediaUploadComplete(fileTransNotifyInfo: unknown) {
}
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown) {
}
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown) {
}
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown) {
}
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams) {
}
onUnreadCntAfterFirstView(hashMap: unknown) {
}
onUnreadCntUpdate(hashMap: unknown) {
}
onUserChannelTabStatusChanged(z: unknown) {
}
onUserOnlineStatusChanged(z: unknown) {
}
onUserTabStatusChanged(arrayList: unknown) {
}
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown) {
}
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown) {
}
// 第一次发现于Linux
onUserSecQualityChanged(...args: unknown[]) {
}
onMsgWithRichLinkInfoUpdate(...args: unknown[]) {
}
onRedTouchChanged(...args: unknown[]) {
}
// 第一次发现于Win 9.9.9-23159
onBroadcastHelperProgerssUpdate(...args: unknown[]) {
}
}

View File

@@ -0,0 +1,40 @@
import { User } from '@/core/entities';
interface IProfileListener {
onProfileSimpleChanged(...args: unknown[]): void;
onProfileDetailInfoChanged(profile: User): void;
onStatusUpdate(...args: unknown[]): void;
onSelfStatusChanged(...args: unknown[]): void;
onStrangerRemarkChanged(...args: unknown[]): void;
}
export interface NodeIKernelProfileListener extends IProfileListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IProfileListener): NodeIKernelProfileListener;
}
export class ProfileListener implements IProfileListener {
onProfileSimpleChanged(...args: unknown[]) {
}
onProfileDetailInfoChanged(profile: User) {
}
onStatusUpdate(...args: unknown[]) {
}
onSelfStatusChanged(...args: unknown[]) {
}
onStrangerRemarkChanged(...args: unknown[]) {
}
}

View File

@@ -0,0 +1,28 @@
export interface IKernelRobotListener {
onRobotFriendListChanged(...args: unknown[]): void;
onRobotListChanged(...args: unknown[]): void;
onRobotProfileChanged(...args: unknown[]): void;
}
export interface NodeIKernelRobotListener extends IKernelRobotListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IKernelRobotListener): NodeIKernelRobotListener;
}
export class KernelRobotListener implements IKernelRobotListener {
onRobotFriendListChanged(...args: unknown[]){
}
onRobotListChanged(...args: unknown[]){
}
onRobotProfileChanged(...args: unknown[]){
}
}

View File

@@ -0,0 +1,44 @@
export interface ISessionListener {
onNTSessionCreate(args: unknown): void;
onGProSessionCreate(args: unknown): void;
onSessionInitComplete(args: unknown): void;
onOpentelemetryInit(args: unknown): void;
onUserOnlineResult(args: unknown): void;
onGetSelfTinyId(args: unknown): void;
}
export interface NodeIKernelSessionListener extends ISessionListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: ISessionListener): NodeIKernelSessionListener;
}
export class SessionListener implements ISessionListener {
onNTSessionCreate(args: unknown) {
}
onGProSessionCreate(args: unknown) {
}
onSessionInitComplete(args: unknown) {
}
onOpentelemetryInit(args: unknown) {
}
onUserOnlineResult(args: unknown) {
}
onGetSelfTinyId(args: unknown) {
}
}

View File

@@ -0,0 +1,37 @@
export interface IStorageCleanListener {
onCleanCacheProgressChanged(args: unknown): void;
onScanCacheProgressChanged(args: unknown): void;
onCleanCacheStorageChanged(args: unknown): void;
onFinishScan(args: unknown): void;
onChatCleanDone(args: unknown): void;
}
export interface NodeIKernelStorageCleanListener extends IStorageCleanListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IStorageCleanListener): NodeIKernelStorageCleanListener;
}
export class StorageCleanListener implements IStorageCleanListener {
onCleanCacheProgressChanged(args: unknown) {
}
onScanCacheProgressChanged(args: unknown) {
}
onCleanCacheStorageChanged(args: unknown) {
}
onFinishScan(args: unknown) {
}
onChatCleanDone(args: unknown) {
}
}

View File

@@ -0,0 +1,9 @@
export interface IKernelTicketListener {
}
export interface NodeIKernelTicketListener extends IKernelTicketListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IKernelTicketListener): NodeIKernelTicketListener;
}
export class KernelTicketListener implements IKernelTicketListener {
}

View File

@@ -0,0 +1,12 @@
export * from './NodeIKernelSessionListener';
export * from './NodeIKernelLoginListener';
export * from './NodeIKernelMsgListener';
export * from './NodeIKernelGroupListener';
export * from './NodeIKernelBuddyListener';
export * from './NodeIKernelProfileListener';
export * from './NodeIKernelRobotListener';
export * from './NodeIKernelTicketListener';
export * from './NodeIKernelStorageCleanListener';
export * from './NodeIKernelFileAssistantListener';

View File

@@ -0,0 +1,46 @@
export interface NodeIKernelAlbumService {
setAlbumServiceInfo(...args: any[]): unknown;// needs 3 arguments
getMainPage(...args: any[]): unknown;// needs 2 arguments
getAlbumList(...args: any[]): unknown;// needs 1 arguments
getAlbumInfo(...args: any[]): unknown;// needs 1 arguments
deleteAlbum(...args: any[]): unknown;// needs 3 arguments
addAlbum(...args: any[]): unknown;// needs 2 arguments
deleteMedias(...args: any[]): unknown;// needs 4 arguments
modifyAlbum(...args: any[]): unknown;// needs 3 arguments
getMediaList(...args: any[]): unknown;// needs 1 arguments
quoteToQzone(...args: any[]): unknown;// needs 1 arguments
quoteToQunAlbum(...args: any[]): unknown;// needs 1 arguments
queryQuoteToQunAlbumStatus(...args: any[]): unknown;// needs 1 arguments
getQunFeeds(...args: any[]): unknown;//needs 1 arguments
getQunFeedDetail(...args: any[]): unknown;// needs 1 arguments
getQunNoticeList(...args: any[]): unknown;// needs 4 arguments
getQunComment(...args: any[]): unknown;// needs 1 arguments
getQunLikes(...args: any[]): unknown;// needs 4 arguments
deleteQunFeed(...args: any[]): unknown;// needs 1 arguments
doQunComment(...args: any[]): unknown;// needs 6 arguments
doQunReply(...args: any[]): unknown;// needs 7 arguments
doQunLike(...args: any[]): unknown;// needs 5 arguments
getRedPoints(...args: any[]): unknown;// needs 3 arguments
}

View File

@@ -0,0 +1,31 @@
export interface NodeIKernelAvatarService {
addAvatarListener(arg: unknown): unknown;
removeAvatarListener(arg: unknown): unknown;
getAvatarPath(arg1: unknown, arg2: unknown): unknown;
forceDownloadAvatar(uid: string, useCache: number): Promise<unknown>;
getGroupAvatarPath(arg1: unknown, arg2: unknown): unknown;
getConfGroupAvatarPath(arg: unknown): unknown;
forceDownloadGroupAvatar(arg1: unknown, arg2: unknown): unknown;
getGroupPortraitPath(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
forceDownloadGroupPortrait(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
getAvatarPaths(arg1: unknown, arg2: unknown): unknown;
getGroupAvatarPaths(arg1: unknown, arg2: unknown): unknown;
getConfGroupAvatarPaths(arg: unknown): unknown;
getAvatarPathByUin(arg1: unknown, arg2: unknown): unknown;
forceDownloadAvatarByUin(arg1: unknown, arg2: unknown): unknown;
isNull(): boolean;
}

View File

@@ -0,0 +1,103 @@
import { Friend } from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common';
import { NodeIKernelBuddyListener } from '@/core/listeners';
export interface NodeIKernelBuddyService {
// 以下为自行添加的wrapper.node中并没有这些方法,目的是简化调用
friends: Friend[];
getFriend(uidOrUin: string): Promise<Friend>;
// 以下为原生方法
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
removeKernelBuddyListener(listener: unknown): void;
getBuddyList(bool: boolean): Promise<GeneralCallResult>;
getBuddyNick(uid: number): string;
getBuddyRemark(uid: number): string;
setBuddyRemark(uid: number, remark: string): void;
getAvatarUrl(uid: number): string;
isBuddy(uid: string): boolean;
getCategoryNameWithUid(uid: number): string;
getTargetBuddySetting(uid: number): unknown;
getTargetBuddySettingByType(uid: number, type: number): unknown;
getBuddyReqUnreadCnt(): number;
getBuddyReq(): unknown;
delBuddyReq(uid: number): void;
clearBuddyReqUnreadCnt(): void;
reqToAddFriends(uid: number, msg: string): void;
setSpacePermission(uid: number, permission: number): void;
approvalFriendRequest(arg: {
friendUid: string;
reqTime: string;
accept: boolean;
}): Promise<void>;
delBuddy(uid: number): void;
delBatchBuddy(uids: number[]): void;
getSmartInfos(uid: number): unknown;
setBuddyCategory(uid: number, category: number): void;
setBatchBuddyCategory(uids: number[], category: number): void;
addCategory(category: string): void;
delCategory(category: string): void;
renameCategory(oldCategory: string, newCategory: string): void;
resortCategory(categorys: string[]): void;
pullCategory(uid: number, category: string): void;
setTop(uid: number, isTop: boolean): void;
SetSpecialCare(uid: number, isSpecialCare: boolean): void;
setMsgNotify(uid: number, isNotify: boolean): void;
hasBuddyList(): boolean;
setBlock(uid: number, isBlock: boolean): void;
isBlocked(uid: number): boolean;
modifyAddMeSetting(setting: unknown): void;
getAddMeSetting(): unknown;
getDoubtBuddyReq(): unknown;
getDoubtBuddyUnreadNum(): number;
approvalDoubtBuddyReq(uid: number, isAgree: boolean): void;
delDoubtBuddyReq(uid: number): void;
delAllDoubtBuddyReq(): void;
reportDoubtBuddyReqUnread(): void;
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<unknown>;
isNull(): boolean;
}

View File

@@ -0,0 +1,91 @@
import { GeneralCallResult } from "./common";
export interface NodeIKernelCollectionService {
addKernelCollectionListener(...args: any[]): unknown;//needs 1 arguments
removeKernelCollectionListener(...args: any[]): unknown;//needs 1 arguments
getCollectionItemList(param: {
category: number,
groupId: number,
forceSync: boolean,
forceFromDb: boolean,
timeStamp: string,
count: number,
searchDown: boolean
}): Promise<GeneralCallResult &
{
collectionSearchList: {
collectionItemList: Array<
{
cid: string,
type: number,
status: number,
author: {
type: number,
numId: string,
strId: string,
groupId: string,
groupName: string,
uid: string
},
bid: number,
category: number,
createTime: string,
collectTime: string,
modifyTime: string,
sequence: string,
shareUrl: string,
customGroupId: number,
securityBeat: boolean,
summary: {
textSummary: unknown,
linkSummary: unknown,
gallerySummary: unknown,
audioSummary: unknown,
videoSummary: unknown,
fileSummary: unknown,
locationSummary: unknown,
richMediaSummary: unknown,
}
}>,
hasMore: boolean,
bottomTimeStamp: string
}
}
>;//needs 1 arguments
getCollectionContent(...args: any[]): unknown;//needs 5 arguments
getCollectionCustomGroupList(...args: any[]): unknown;//needs 0 arguments
getCollectionUserInfo(...args: any[]): unknown;//needs 0 arguments
searchCollectionItemList(...args: any[]): unknown;//needs 2 arguments
addMsgToCollection(...args: any[]): unknown;//needs 2 arguments
collectionArkShare(...args: any[]): unknown;//needs 1 arguments
collectionFileForward(...args: any[]): unknown;//needs 3 arguments
downloadCollectionFile(...args: any[]): unknown;//needs 4 arguments
downloadCollectionFileThumbPic(...args: any[]): unknown;//needs 4 arguments
downloadCollectionPic(...args: any[]): unknown;//needs 3 arguments
cancelDownloadCollectionFile(...args: any[]): unknown;//needs 1 arguments
deleteCollectionItemList(...args: any[]): unknown;//needs 1 arguments
editCollectionItem(...args: any[]): unknown;//needs 2 arguments
getEditPicInfoByPath(...args: any[]): unknown;//needs 1 arguments
collectionFastUpload(...args: any[]): unknown;//needs 1 arguments
editCollectionItemAfterFastUpload(...args: any[]): unknown;//needs 2 arguments
createNewCollectionItem(...args: any[]): unknown;//needs 1 arguments
}

View File

@@ -0,0 +1,5 @@
export interface NodeIKernelDbToolsService {
depositDatabase(...args: unknown[]): unknown;
backupDatabase(...args: unknown[]): unknown;
retrieveDatabase(...args: unknown[]): unknown;
}

View File

@@ -0,0 +1,35 @@
export interface NodeIKernelFileAssistantService {
addKernelFileAssistantListener(arg1: unknown[]): unknown;
removeKernelFileAssistantListener(arg1: unknown[]): unknown;
getFileAssistantList(arg1: unknown[]): unknown;
getMoreFileAssistantList(arg1: unknown[]): unknown;
getFileSessionList(): unknown;
searchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
resetSearchFileSortType(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
searchMoreFile(arg1: unknown[]): unknown;
cancelSearchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
downloadFile(arg1: unknown[]): unknown;
forwardFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
cancelFileAction(arg1: unknown[]): unknown;
retryFileAction(arg1: unknown[]): unknown;
deleteFile(arg1: unknown[]): unknown;
saveAs(arg1: unknown, arg2: unknown): unknown;
saveAsWithRename(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
isNull(): boolean;
}

View File

@@ -0,0 +1,153 @@
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import {
GroupExtParam,
GroupMember,
GroupMemberRole,
GroupNotifyTypes,
GroupRequestOperateTypes,
} from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common';
export interface NodeIKernelGroupService {
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
removeKernelGroupListener(listenerId: unknown): void;
createMemberListScene(groupCode: string, scene: string): string;
destroyMemberListScene(): void;
//About Arg (a) name: lastId 根据手Q来看为object {index:?(number),uid:string}
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
errCode: number, errMsg: string,
result: { ids: string[], infos: Map<string, GroupMember>, finish: boolean, hasRobot: boolean }
}>;
getPrevMemberList(): unknown;
monitorMemberList(): unknown;
searchMember(uid: string): unknown;
getMemberInfo(uid: string): unknown;
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>;
modifyMemberRole(groupCode: string, uid: string, role: GroupMemberRole): void;
modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;
getTransferableMemberInfo(uid: string): unknown;
transferGroup(uid: string): void;
getGroupList(force: boolean): Promise<GeneralCallResult>;
getGroupExtList(force: boolean): Promise<GeneralCallResult>;
getGroupDetailInfo(groupCode: string): unknown;
getMemberExtInfo(param: GroupExtParam): Promise<unknown>;//req
getGroupAllInfo(): unknown;
getDiscussExistInfo(): unknown;
getGroupConfMember(): unknown;
getGroupMsgMask(): unknown;
getGroupPortrait(): void;
modifyGroupName(groupCode: string, groupName: string, arg: false): void;
modifyGroupRemark(groupCode: string, remark: string): void;
modifyGroupDetailInfo(groupCode: string, arg: unknown): void;
setGroupMsgMask(groupCode: string, arg: unknown): void;
changeGroupShieldSettingTemp(groupCode: string, arg: unknown): void;
inviteToGroup(arg: unknown): void;
inviteMembersToGroup(args: unknown[]): void;
inviteMembersToGroupWithMsg(args: unknown): void;
createGroup(arg: unknown): void;
createGroupWithMembers(arg: unknown): void;
quitGroup(groupCode: string): void;
destroyGroup(groupCode: string): void;
//获取单屏群通知列表
getSingleScreenNotifies(force: boolean, start_seq: string, num: number): Promise<GeneralCallResult>;
clearGroupNotifies(groupCode: string): void;
getGroupNotifiesUnreadCount(unknown: Boolean): Promise<GeneralCallResult>;
clearGroupNotifiesUnreadCount(groupCode: string): void;
operateSysNotify(
doubt: boolean,
operateMsg: {
operateType: GroupRequestOperateTypes, // 2 拒绝
targetMsg: {
seq: string, // 通知序列号
type: GroupNotifyTypes,
groupCode: string,
postscript: string
}
}): Promise<void>;
setTop(groupCode: string, isTop: boolean): void;
getGroupBulletin(groupCode: string): unknown;
deleteGroupBulletin(groupCode: string, seq: string): void;
publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>;
publishInstructionForNewcomers(groupCode: string, arg: unknown): void;
uploadGroupBulletinPic(groupCode: string, pskey: string, imagePath: string): Promise<GeneralCallResult & {
errCode: number;
picInfo?: {
id: string,
width: number,
height: number
}
}>;
downloadGroupBulletinRichMedia(groupCode: string): unknown;
getGroupBulletinList(groupCode: string): unknown;
getGroupStatisticInfo(groupCode: string): unknown;
getGroupRemainAtTimes(groupCode: string): number;
getJoinGroupNoVerifyFlag(groupCode: string): unknown;
getGroupArkInviteState(groupCode: string): unknown;
reqToJoinGroup(groupCode: string, arg: unknown): void;
setGroupShutUp(groupCode: string, shutUp: boolean): void;
getGroupShutUpMemberList(groupCode: string): unknown[];
setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<void>;
getGroupRecommendContactArkJson(groupCode: string): unknown;
getJoinGroupLink(groupCode: string): unknown;
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
isNull(): boolean;
}

View File

@@ -0,0 +1,82 @@
import { NodeIKernelLoginListener } from '@/core/listeners/NodeIKernelLoginListener';
export interface LoginInitConfig {
machineId: '';
appid: string;
platVer: string;
commonPath: string;
clientVer: string;
hostName: string;
}
export interface passwordLoginRetType {
result: string,
loginErrorInfo: {
step: number;
errMsg: string;
proofWaterUrl: string;
newDevicePullQrCodeSig: string;
jumpUrl: string,
jumpWord: string;
tipsTitle: string;
tipsContent: string;
}
}
export interface passwordLoginArgType {
uin: string;
passwordMd5: string;//passwMD5
step: number;//猜测是需要二次认证 参数 一次为0
newDeviceLoginSig: string;
proofWaterSig: string;
proofWaterRand: string;
proofWaterSid: string;
}
export interface LoginListItem {
uin: string;
uid: string;
nickName: string;
faceUrl: string;
facePath: string;
loginType: 1; // 1是二维码登录
isQuickLogin: boolean; // 是否可以快速登录
isAutoLogin: boolean; // 是否可以自动登录
}
export interface QuickLoginResult{
result: string
loginErrorInfo: {
step: number,
errMsg: string,
proofWaterUrl: string,
newDevicePullQrCodeSig: string,
jumpUrl: string,
jumpWord: string,
tipsTitle: string,
tipsContent: string
}
}
export interface NodeIKernelLoginService {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeIKernelLoginService;
addKernelLoginListener(listener: NodeIKernelLoginListener): number;
removeKernelLoginListener(listener: number): void;
initConfig(config: LoginInitConfig): void;
getLoginMiscData(cb: (r: unknown) => void): void;
getLoginList(): Promise<{
result: number, // 0是ok
LocalLoginInfoList: LoginListItem[]
}>;
quickLoginWithUin(uin: string): Promise<QuickLoginResult>;
passwordLogin(param: passwordLoginArgType): Promise<any>;
getQRCodePicture(): boolean;
}

View File

@@ -0,0 +1,27 @@
export interface NodeIKernelMsgBackupService {
addKernelMsgBackupListener(...args: any[]): unknown;// needs 1 arguments
removeKernelMsgBackupListener(...args: any[]): unknown;// needs 1 arguments
getMsgBackupLocation(...args: any[]): unknown;// needs 0 arguments
setMsgBackupLocation(...args: any[]): unknown;// needs 1 arguments
requestMsgBackup(...args: any[]): unknown;// needs 0 arguments
requestMsgRestore(...args: any[]): unknown;// needs 1 arguments
requestMsgMigrate(...args: any[]): unknown;// needs 1 arguments
getLocalStorageBackup(...args: any[]): unknown;// needs 0 arguments
deleteLocalBackup(...args: any[]): unknown;// needs 1 arguments
clearCache(...args: any[]): unknown;// needs 0 arguments
start(...args: any[]): unknown;// needs 1 arguments
stop(...args: any[]): unknown;// needs 1 arguments
pause(...args: any[]): unknown;// needs 2 arguments
}

View File

@@ -0,0 +1,666 @@
import { ChatType, ElementType, Peer, RawMessage, SendMessageElement } from '@/core/entities';
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
import { GeneralCallResult } from '@/core/services/common';
export interface NodeIKernelMsgService {
addKernelMsgListener(nodeIKernelMsgListener: NodeIKernelMsgListener): number;
sendMsg(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>): Promise<unknown>;
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
addKernelMsgImportToolListener(arg: Object): unknown;
removeKernelMsgListener(args: unknown): unknown;
addKernelTempChatSigListener(...args: unknown[]): unknown;
removeKernelTempChatSigListener(...args: unknown[]): unknown;
setAutoReplyTextList(AutoReplyText: Array<unknown>, i2: number): unknown;
getAutoReplyTextList(...args: unknown[]): unknown;
getOnLineDev(): Promise<any>;
kickOffLine(DevInfo: Object): unknown;
setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>;
fetchStatusMgrInfo(): unknown;
fetchStatusUnitedConfigInfo(): unknown;
getOnlineStatusSmallIconBasePath(): unknown;
getOnlineStatusSmallIconFileNameByUrl(Url: string): unknown;
downloadOnlineStatusSmallIconByUrl(arg0: number, arg1: string): unknown;
getOnlineStatusBigIconBasePath(): unknown;
downloadOnlineStatusBigIconByUrl(arg0: number, arg1: string): unknown;
getOnlineStatusCommonPath(arg: string): unknown;
getOnlineStatusCommonFileNameByUrl(Url: string): unknown;
downloadOnlineStatusCommonByUrl(arg0: string, arg1: string): unknown;
// this.tokenType = i2;
// this.apnsToken = bArr;
// this.voipToken = bArr2;
// this.profileId = str;
setToken(arg: Object): unknown;
switchForeGround(): unknown;
switchBackGround(arg: Object): unknown;
//hex
setTokenForMqq(token: string): unknown;
switchForeGroundForMqq(...args: unknown[]): unknown;
switchBackGroundForMqq(...args: unknown[]): unknown;
getMsgSetting(...args: unknown[]): unknown;
setMsgSetting(...args: unknown[]): unknown;
addSendMsg(...args: unknown[]): unknown;
cancelSendMsg(...args: unknown[]): unknown;
switchToOfflineSendMsg(...args: unknown[]): unknown;
reqToOfflineSendMsg(...args: unknown[]): unknown;
refuseReceiveOnlineFileMsg(peer: Peer, MsgId: string): unknown;
resendMsg(...args: unknown[]): unknown;
recallMsg(...args: unknown[]): unknown;
reeditRecallMsg(...args: unknown[]): unknown;
forwardMsg(...args: unknown[]): Promise<GeneralCallResult>;
forwardMsgWithComment(...args: unknown[]): unknown;
forwardSubMsgWithComment(...args: unknown[]): unknown;
forwardRichMsgInVist(...args: unknown[]): unknown;
forwardFile(...args: unknown[]): unknown;
//Array<Msg>, Peer from, Peer to
multiForwardMsg(...args: unknown[]): unknown;
multiForwardMsgWithComment(...args: unknown[]): unknown;
deleteRecallMsg(...args: unknown[]): unknown;
deleteRecallMsgForLocal(...args: unknown[]): unknown;
addLocalGrayTipMsg(...args: unknown[]): unknown;
addLocalJsonGrayTipMsg(...args: unknown[]): unknown;
addLocalJsonGrayTipMsgExt(...args: unknown[]): unknown;
IsLocalJsonTipValid(...args: unknown[]): unknown;
addLocalAVRecordMsg(...args: unknown[]): unknown;
addLocalTofuRecordMsg(...args: unknown[]): unknown;
addLocalRecordMsg(...args: unknown[]): unknown;
deleteMsg(...args: unknown[]): unknown;
updateElementExtBufForUI(...args: unknown[]): unknown;
updateMsgRecordExtPbBufForUI(...args: unknown[]): unknown;
startMsgSync(...args: unknown[]): unknown;
startGuildMsgSync(...args: unknown[]): unknown;
isGuildChannelSync(...args: unknown[]): unknown;
getMsgUniqueId(UniqueId: string): string;
isMsgMatched(...args: unknown[]): unknown;
getOnlineFileMsgs(...args: unknown[]): unknown;
getAllOnlineFileMsgs(...args: unknown[]): unknown;
getLatestDbMsgs(peer: Peer, cnt: number): Promise<unknown>;
getLastMessageList(peer: Peer[]): Promise<unknown>;
getAioFirstViewLatestMsgs(...args: unknown[]): unknown;
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>;
// this.$peer = contact;
// this.$msgTime = j2;
// this.$clientSeq = j3;
// this.$cnt = i2;
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<unknown>;
getMsgsWithStatus(params: {
peer: Peer
msgId: string
msgTime: unknown
cnt: unknown
queryOrder: boolean
isIncludeSelf: boolean
appid: unknown
}): Promise<unknown>;
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<unknown>;
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getMsgsByMsgId(peer: Peer, ids: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getRecallMsgsByMsgId(peer: Peer, MsgId: string[]): Promise<unknown>;
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<unknown>;
getSingleMsg(Peer: Peer, msgSeq: string): unknown;
getSourceOfReplyMsg(...args: unknown[]): unknown;
getSourceOfReplyMsgV2(...args: unknown[]): unknown;
getMsgByClientSeqAndTime(...args: unknown[]): unknown;
getSourceOfReplyMsgByClientSeqAndTime(...args: unknown[]): unknown;
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: unknown): unknown;
getMsgsByTypeFilters(...args: unknown[]): unknown;
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown;
queryMsgsWithFilter(...args: unknown[]): unknown;
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: {
chatInfo: {
chatType: number,
peerUid: string
},
filterMsgType: [],
filterSendersUid: [],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}): Promise<unknown>;
// this.chatType = i2;
// this.peerUid = str;
// this.chatInfo = new ChatInfo();
// this.filterMsgType = new ArrayList<>();
// this.filterSendersUid = new ArrayList<>();
// this.chatInfo = chatInfo;
// this.filterMsgType = arrayList;
// this.filterSendersUid = arrayList2;
// this.filterMsgFromTime = j2;
// this.filterMsgToTime = j3;
// this.pageLimit = i2;
// 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: [],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
isIncludeCurrent: boolean
}): Promise<unknown>;
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
queryFileMsgsDesktop(...args: unknown[]): unknown;
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>;
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
queryEmoticonMsgs(...args: unknown[]): unknown;
queryTroopEmoticonMsgs(...args: unknown[]): unknown;
queryMsgsAndAbstractsWithFilter(...args: unknown[]): unknown;
setFocusOnGuild(...args: unknown[]): unknown;
setFocusSession(...args: unknown[]): unknown;
enableFilterUnreadInfoNotify(...args: unknown[]): unknown;
enableFilterMsgAbstractNotify(...args: unknown[]): unknown;
onScenesChangeForSilenceMode(...args: unknown[]): unknown;
getContactUnreadCnt(...args: unknown[]): unknown;
getUnreadCntInfo(...args: unknown[]): unknown;
getGuildUnreadCntInfo(...args: unknown[]): unknown;
getGuildUnreadCntTabInfo(...args: unknown[]): unknown;
getAllGuildUnreadCntInfo(...args: unknown[]): unknown;
getAllJoinGuildCnt(...args: unknown[]): unknown;
getAllDirectSessionUnreadCntInfo(...args: unknown[]): unknown;
getCategoryUnreadCntInfo(...args: unknown[]): unknown;
getGuildFeedsUnreadCntInfo(...args: unknown[]): unknown;
setUnVisibleChannelCntInfo(...args: unknown[]): unknown;
setUnVisibleChannelTypeCntInfo(...args: unknown[]): unknown;
setVisibleGuildCntInfo(...args: unknown[]): unknown;
setMsgRead(peer: Peer): Promise<GeneralCallResult>;
setAllC2CAndGroupMsgRead(...args: unknown[]): unknown;
setGuildMsgRead(...args: unknown[]): unknown;
setAllGuildMsgRead(...args: unknown[]): unknown;
setMsgReadAndReport(...args: unknown[]): unknown;
setSpecificMsgReadAndReport(...args: unknown[]): unknown;
setLocalMsgRead(...args: unknown[]): unknown;
setGroupGuildMsgRead(...args: unknown[]): unknown;
getGuildGroupTransData(...args: unknown[]): unknown;
setGroupGuildBubbleRead(...args: unknown[]): unknown;
getGuildGroupBubble(...args: unknown[]): unknown;
fetchGroupGuildUnread(...args: unknown[]): unknown;
setGroupGuildFlag(...args: unknown[]): unknown;
setGuildUDCFlag(...args: unknown[]): unknown;
setGuildTabUserFlag(...args: unknown[]): unknown;
setBuildMode(...args: unknown[]): unknown;
setConfigurationServiceData(...args: unknown[]): unknown;
setMarkUnreadFlag(...args: unknown[]): unknown;
getChannelEventFlow(...args: unknown[]): unknown;
getMsgEventFlow(...args: unknown[]): unknown;
getRichMediaFilePathForMobileQQSend(...args: unknown[]): unknown;
getRichMediaFilePathForGuild(arg: {
md5HexStr: string,
fileName: string,
elementType: ElementType,
elementSubType: number,
thumbSize: 0,
needCreate: true,
downloadType: 1,
file_uuid: ''
}): string;
assembleMobileQQRichMediaFilePath(...args: unknown[]): unknown;
getFileThumbSavePathForSend(...args: unknown[]): unknown;
getFileThumbSavePath(...args: unknown[]): unknown;
translatePtt2Text(...args: unknown[]): unknown;
setPttPlayedState(...args: unknown[]): unknown;
fetchFavEmojiList(...args: unknown[]): unknown;
addFavEmoji(...args: unknown[]): unknown;
fetchMarketEmoticonList(...args: unknown[]): unknown;
fetchMarketEmoticonShowImage(...args: unknown[]): unknown;
fetchMarketEmoticonAioImage(...args: unknown[]): unknown;
fetchMarketEmotionJsonFile(...args: unknown[]): unknown;
getMarketEmoticonPath(...args: unknown[]): unknown;
getMarketEmoticonPathBySync(...args: unknown[]): unknown;
fetchMarketEmoticonFaceImages(...args: unknown[]): unknown;
fetchMarketEmoticonAuthDetail(...args: unknown[]): unknown;
getFavMarketEmoticonInfo(...args: unknown[]): unknown;
addRecentUsedFace(...args: unknown[]): unknown;
getRecentUsedFaceList(...args: unknown[]): unknown;
getMarketEmoticonEncryptKeys(...args: unknown[]): unknown;
downloadEmojiPic(...args: unknown[]): unknown;
deleteFavEmoji(...args: unknown[]): unknown;
modifyFavEmojiDesc(...args: unknown[]): unknown;
queryFavEmojiByDesc(...args: unknown[]): unknown;
getHotPicInfoListSearchString(...args: unknown[]): unknown;
getHotPicSearchResult(...args: unknown[]): unknown;
getHotPicHotWords(...args: unknown[]): unknown;
getHotPicJumpInfo(...args: unknown[]): unknown;
getEmojiResourcePath(...args: unknown[]): unknown;
JoinDragonGroupEmoji(...args: unknown[]): unknown;
getMsgAbstracts(...args: unknown[]): unknown;
getMsgAbstract(...args: unknown[]): unknown;
getMsgAbstractList(...args: unknown[]): unknown;
getMsgAbstractListBySeqRange(...args: unknown[]): unknown;
refreshMsgAbstracts(...args: unknown[]): unknown;
refreshMsgAbstractsByGuildIds(...args: unknown[]): unknown;
getRichMediaElement(...args: unknown[]): unknown;
cancelGetRichMediaElement(...args: unknown[]): unknown;
refuseGetRichMediaElement(...args: unknown[]): unknown;
switchToOfflineGetRichMediaElement(...args: unknown[]): unknown;
downloadRichMedia(...args: unknown[]): unknown;
getFirstUnreadMsgSeq(...args: unknown[]): unknown;
getFirstUnreadCommonMsg(...args: unknown[]): unknown;
getFirstUnreadAtmeMsg(...args: unknown[]): unknown;
getFirstUnreadAtallMsg(...args: unknown[]): unknown;
getNavigateInfo(...args: unknown[]): unknown;
getChannelFreqLimitInfo(...args: unknown[]): unknown;
getRecentUseEmojiList(...args: unknown[]): unknown;
getRecentEmojiList(...args: unknown[]): unknown;
setMsgEmojiLikes(...args: unknown[]): unknown;
getMsgEmojiLikesList(...args: unknown[]): unknown;
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
clickInlineKeyboardButton(...args: unknown[]): unknown;
setCurOnScreenMsg(...args: unknown[]): unknown;
setCurOnScreenMsgForMsgEvent(...args: unknown[]): unknown;
getMiscData(key: string): unknown;
setMiscData(key: string, value: string): unknown;
getBookmarkData(...args: unknown[]): unknown;
setBookmarkData(...args: unknown[]): unknown;
sendShowInputStatusReq(ChatType: number, EventType: number, toUid: string): Promise<unknown>;
queryCalendar(...args: unknown[]): unknown;
queryFirstMsgSeq(...args: unknown[]): unknown;
queryRoamCalendar(...args: unknown[]): unknown;
queryFirstRoamMsg(...args: unknown[]): unknown;
fetchLongMsg(...args: unknown[]): unknown;
fetchLongMsgWithCb(...args: unknown[]): unknown;
setIsStopKernelFetchLongMsg(...args: unknown[]): unknown;
insertGameResultAsMsgToDb(...args: unknown[]): unknown;
getMultiMsg(...args: unknown[]): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>;
setDraft(...args: unknown[]): unknown;
getDraft(...args: unknown[]): unknown;
deleteDraft(...args: unknown[]): unknown;
getRecentHiddenSesionList(...args: unknown[]): unknown;
setRecentHiddenSession(...args: unknown[]): unknown;
delRecentHiddenSession(...args: unknown[]): unknown;
getCurHiddenSession(...args: unknown[]): unknown;
setCurHiddenSession(...args: unknown[]): unknown;
setReplyDraft(...args: unknown[]): unknown;
getReplyDraft(...args: unknown[]): unknown;
deleteReplyDraft(...args: unknown[]): unknown;
getFirstUnreadAtMsg(...args: unknown[]): unknown;
clearMsgRecords(...args: unknown[]): unknown;//设置已读后调用我觉得比较好 清理记录
IsExistOldDb(...args: unknown[]): unknown;
canImportOldDbMsg(...args: unknown[]): unknown;
setPowerStatus(z: boolean): unknown;
canProcessDataMigration(...args: unknown[]): unknown;
importOldDbMsg(...args: unknown[]): unknown;
stopImportOldDbMsgAndroid(...args: unknown[]): unknown;
isMqqDataImportFinished(...args: unknown[]): unknown;
getMqqDataImportTableNames(...args: unknown[]): unknown;
getCurChatImportStatusByUin(...args: unknown[]): unknown;
getDataImportUserLevel(...args: unknown[]): unknown;
getMsgQRCode(...args: unknown[]): unknown;
getGuestMsgAbstracts(...args: unknown[]): unknown;
getGuestMsgByRange(...args: unknown[]): unknown;
getGuestMsgAbstractByRange(...args: unknown[]): unknown;
registerSysMsgNotification(...args: unknown[]): unknown;
unregisterSysMsgNotification(...args: unknown[]): unknown;
enterOrExitAio(...args: unknown[]): unknown;
// this.peerUid = "";
// this.peerNickname = "";
// this.fromGroupCode = "";
// this.sig = new byte[0];
// this.selfUid = "";
// this.selfPhone = "";
// this.chatType = i2;
// this.peerUid = str;
// this.peerNickname = str2;
// this.fromGroupCode = str3;
// this.sig = bArr;
// this.selfUid = str4;
// this.selfPhone = str5;
// this.gameSession = tempChatGameSession;
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
//chattype,uid->Promise<any>
getTempChatInfo(ChatType: number, Uid: string): unknown;
setContactLocalTop(...args: unknown[]): unknown;
switchAnonymousChat(...args: unknown[]): unknown;
renameAnonyChatNick(...args: unknown[]): unknown;
getAnonymousInfo(...args: unknown[]): unknown;
updateAnonymousInfo(...args: unknown[]): unknown;
sendSummonMsg(peer: Peer, MsgElement: unknown, MsgAttributeInfo: unknown): Promise<unknown>;//频道的东西
outputGuildUnreadInfo(...args: unknown[]): unknown;
checkMsgWithUrl(...args: unknown[]): unknown;
checkTabListStatus(...args: unknown[]): unknown;
getABatchOfContactMsgBoxInfo(...args: unknown[]): unknown;
insertMsgToMsgBox(...args: unknown[]): unknown;
isHitEmojiKeyword(...args: unknown[]): unknown;
getKeyWordRelatedEmoji(...args: unknown[]): unknown;
recordEmoji(...args: unknown[]): unknown;
fetchGetHitEmotionsByWord(args: Object): Promise<unknown>;//表情推荐?
deleteAllRoamMsgs(...args: unknown[]): unknown;//漫游消息?
packRedBag(...args: unknown[]): unknown;
grabRedBag(...args: unknown[]): unknown;
pullDetail(...args: unknown[]): unknown;
selectPasswordRedBag(...args: unknown[]): unknown;
pullRedBagPasswordList(...args: unknown[]): unknown;
requestTianshuAdv(...args: unknown[]): unknown;
tianshuReport(...args: unknown[]): unknown;
tianshuMultiReport(...args: unknown[]): unknown;
GetMsgSubType(a0: number, a1: number): unknown;
setIKernelPublicAccountAdapter(...args: unknown[]): unknown;
//tempChatGameSession有关
createUidFromTinyId(fromTinyId: string, toTinyId: string): unknown;
dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown;
dataMigrationGetMsgList(...args: unknown[]): unknown;
dataMigrationStopOperation(...args: unknown[]): unknown;
dataMigrationImportMsgPbRecord(...args: unknown[]): unknown;
dataMigrationGetResourceLocalDestinyPath(...args: unknown[]): unknown;
dataMigrationSetIOSPathPrefix(...args: unknown[]): unknown;
getServiceAssistantSwitch(...args: unknown[]): unknown;
setServiceAssistantSwitch(...args: unknown[]): unknown;
setSubscribeFolderUsingSmallRedPoint(...args: unknown[]): unknown;
clearGuildNoticeRedPoint(...args: unknown[]): unknown;
clearFeedNoticeRedPoint(...args: unknown[]): unknown;
clearFeedSquareRead(...args: unknown[]): unknown;
IsC2CStyleChatType(...args: unknown[]): unknown;
IsTempChatType(uin: number): unknown;//猜的
getGuildInteractiveNotification(...args: unknown[]): unknown;
getGuildNotificationAbstract(...args: unknown[]): unknown;
setFocusOnBase(...args: unknown[]): unknown;
queryArkInfo(...args: unknown[]): unknown;
queryUserSecQuality(...args: unknown[]): unknown;
getGuildMsgAbFlag(...args: unknown[]): unknown;
getGroupMsgStorageTime(): unknown;//这是嘛啊
}

View File

@@ -0,0 +1,12 @@
import { GeneralCallResult } from "./common";
//没扒干净 因为用不着
export interface NodeIKernelNodeMiscService {
getMiniAppPath(): unknown;
setMiniAppVersion(version:string): unknown;
wantWinScreenOCR(imagepath: string): Promise<GeneralCallResult>;
SendMiniAppMsg(arg1: string, arg2: string, arg3: string): unknown;
startNewMiniApp(appfile: string, params: string): unknown;
// 我的计划是转发给一个新程序避免吃掉Electron_AS_Node的环境 然后重写启动MiniApp 挂载相应JS脚本 这样有个问题
// 需要自己转发ipc参数 然后必须处在gui环境 且完成校验破解 才能实现发包 有点抽象了
}

View File

@@ -0,0 +1,36 @@
export interface NodeIKernelOnlineStatusService {
addKernelOnlineStatusListener(listener: unknown): void;
removeKernelOnlineStatusListener(listenerId: unknown): void;
getShouldShowAIOStatusAnimation(arg: unknown): unknown;
setReadLikeList(arg: unknown): unknown;
getLikeList(arg: unknown): unknown;
setLikeStatus(arg: unknown): unknown;
getAggregationPageEntrance(): unknown;
didClickAggregationPageEntrance(): unknown;
getAggregationGroupModels(): unknown;
// {
// "businessType": 1,
// "uins": [
// "1627126029",
// "66600000",
// "71702575"
// ]
// }
checkLikeStatus(param: {
businessType: number,
uins: string[]
}): Promise<any>;
isNull(): boolean;
}

View File

@@ -0,0 +1,15 @@
import { BuddyProfileLikeReq } from "../entities/user";
export interface NodeIKernelProfileLikeService {
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void;
removeKernelProfileLikeListener(listener: unknown): void;
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number };
getBuddyProfileLike(req: BuddyProfileLikeReq): void;
getProfileLikeScidResourceInfo(...args: unknown[]): void;
isNull(): boolean;
}

View File

@@ -0,0 +1,77 @@
import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener } from '../listeners';
import { GeneralCallResult } from '@/core/services/common';
export interface NodeIKernelProfileService {
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
removeKernelProfileListener(listenerId: number): void;
prepareRegionConfig(...args: unknown[]): unknown;
getLocalStrangerRemark(): Promise<AnyCnameRecord>;
enumCountryOptions(): Array<string>;
enumProvinceOptions(Country: string): Array<string>;
enumCityOptions(Country: string, Province: string): unknown;
enumAreaOptions(...args: unknown[]): unknown;
//SimpleInfo
// this.uid = "";
// this.uid = str;
// this.uin = j2;
// this.isBuddy = z;
// this.coreInfo = coreInfo;
// this.baseInfo = baseInfo;
// this.status = statusInfo;
// this.vasInfo = vasInfo;
// this.relationFlags = relationFlag;
// this.otherFlags = otherFlag;
// this.intimate = intimate;
modifySelfProfile(...args: unknown[]): Promise<unknown>;
modifyDesktopMiniProfile(param: ModifyProfileParams): Promise<GeneralCallResult>;
setNickName(NickName: string): Promise<unknown>;
setLongNick(longNick: string): Promise<unknown>;
setBirthday(...args: unknown[]): Promise<unknown>;
setGander(...args: unknown[]): Promise<unknown>;
setHeader(arg: string): Promise<unknown>;
setRecommendImgFlag(...args: unknown[]): Promise<unknown>;
getUserSimpleInfo(force: boolean, uids: string[],): Promise<unknown>;
getUserDetailInfo(uid: string): Promise<unknown>;
getUserDetailInfoWithBizInfo(uid: string, Biz: BizKey[]): Promise<GeneralCallResult>;
getUserDetailInfoByUin(uin: string): Promise<UserDetailInfoByUin>;
getZplanAvatarInfos(args: string[]): Promise<unknown>;
getStatus(uid: string): Promise<unknown>;
startStatusPolling(isForceReset: boolean): Promise<unknown>;
getSelfStatus(): Promise<unknown>;
//
setdisableEmojiShortCuts(...args: unknown[]): unknown;
getProfileQzonePicInfo(uid: string, type: number, force: boolean): Promise<unknown>;
//profileService.getCoreInfo("UserRemarkServiceImpl::getStrangerRemarkByUid", arrayList);
getCoreInfo(name: string, arg: any[]): unknown;
//m429253e12.getOtherFlag("FriendListInfoCache_getKernelDataAndPutCache", new ArrayList<>());
isNull(): boolean;
}

View File

@@ -0,0 +1,63 @@
import { Peer } from "../entities";
export interface NodeIKernelRecentContactService {
setGuildDisplayStatus(...args: unknown[]): unknown; // 2 arguments
setContactListTop(...args: unknown[]): unknown; // 2 arguments
updateRecentContactExtBufForUI(...args: unknown[]): unknown; // 2 arguments
upsertRecentContactManually(...args: unknown[]): unknown; // 1 arguments
enterOrExitMsgList(...args: unknown[]): unknown; // 1 arguments
getRecentContactListSnapShot(...args: unknown[]): unknown; // 1 arguments
clearMsgUnreadCount(...args: unknown[]): unknown; // 1 arguments
getRecentContactListSyncLimit(count: number): unknown;
jumpToSpecifyRecentContact(...args: unknown[]): unknown; // 1 arguments
fetchAndSubscribeABatchOfRecentContact(...args: unknown[]): unknown; // 1 arguments
addRecentContact(peer: Peer): unknown;
deleteRecentContacts(peer: Peer): unknown; // 猜测
getContacts(peers: Peer[]): Promise<unknown>;
setThirdPartyBusinessInfos(...args: unknown[]): unknown; // 1 arguments
updateGameMsgConfigs(...args: unknown[]): unknown; // 1 arguments
removeKernelRecentContactListener(...args: unknown[]): unknown; // 1 arguments
addKernelRecentContactListener(...args: unknown[]): unknown; // 1 arguments
clearRecentContactsByChatType(...args: unknown[]): unknown; // 1 arguments
upInsertModule(...args: unknown[]): unknown; // 1 arguments
jumpToSpecifyRecentContactVer2(...args: unknown[]): unknown; // 1 arguments
deleteRecentContactsVer2(...args: unknown[]): unknown; // 1 arguments
getRecentContactList(): unknown;
getMsgUnreadCount(): unknown;
clearRecentContacts(): unknown;
getServiceAssistantRecentContactInfos(): unknown;
getRecentContactInfos(): unknown;
getUnreadDetailsInfos(): unknown;
cleanAllModule(): unknown;
setAllGameMsgRead(): unknown;
getRecentContactListSync(): unknown;
}

View File

@@ -0,0 +1,170 @@
import { GetFileListParam, Peer } from "../entities";
import { GeneralCallResult } from "./common";
export interface NodeIKernelRichMediaService {
//getVideoPlayUrl(peer, msgId, elemId, videoCodecFormat, VideoRequestWay.KHAND, cb);
// public enum VideoCodecFormatType {
// KCODECFORMATH264,
// KCODECFORMATH265,
// KCODECFORMATH266,
// KCODECFORMATAV1
// }
// public enum VideoRequestWay {
// KUNKNOW,
// KHAND,
// KAUTO
// }
getVideoPlayUrl(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, VideoRequestWay: number): Promise<unknown>;
//exParams (RMReqExParams)
// this.downSourceType = i2;
// this.triggerType = i3;
//peer, msgId, elemId, videoCodecFormat, exParams
// 1 0 频道在用
// 1 1
// 0 2
// public static final int KCOMMONREDENVELOPEMSGTYPEINMSGBOX = 1007;
// public static final int KDOWNSOURCETYPEAIOINNER = 1;
// public static final int KDOWNSOURCETYPEBIGSCREEN = 2;
// public static final int KDOWNSOURCETYPEHISTORY = 3;
// public static final int KDOWNSOURCETYPEUNKNOWN = 0;
// public static final int KTRIGGERTYPEAUTO = 1;
// public static final int KTRIGGERTYPEMANUAL = 0;
getVideoPlayUrlV2(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, exParams: { downSourceType: number, triggerType: number }): Promise<GeneralCallResult & {
urlResult: {
v4IpUrl: [],
v6IpUrl: [],
domainUrl: Array<{
url: string,
isHttps: boolean,
httpsDomain: string
}>,
videoCodecFormat: number
}
}>;
getRichMediaFileDir(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
// this.senderUid = "";
// this.peerUid = "";
// this.guildId = "";
// this.elem = new MsgElement();
// this.downloadType = i2;
// this.thumbSize = i3;
// this.msgId = j2;
// this.msgRandom = j3;
// this.msgSeq = j4;
// this.msgTime = j5;
// this.chatType = i4;
// this.senderUid = str;
// this.peerUid = str2;
// this.guildId = str3;
// this.elem = msgElement;
// this.useHttps = num;
getVideoPlayUrlInVisit(arg: unknown): unknown;
isFileExpired(arg: unknown): unknown;
deleteGroupFolder(GroupCode: string, FolderId: string): Promise<GeneralCallResult & { groupFileCommonResult: { retCode: number, retMsg: string, clientWording: string } }>;
//参数与getVideoPlayUrlInVisit一样
downloadRichMediaInVisit(arg: unknown): unknown;
downloadFileForModelId(peer: Peer, arg: unknown[], arg3: string): unknown;
//第三个参数 Array<Type>
// this.fileId = "";
// this.fileName = "";
// this.fileId = str;
// this.fileName = str2;
// this.fileSize = j2;
// this.fileModelId = j3;
downloadFileForFileUuid(peer: Peer, arg1: string, arg3: unknown[]): unknown;
downloadFileByUrlListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
downloadFileForFileInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: any, groupItem: Array<any> } }>
downloadFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown): unknown;
createGroupFoldertransgroupfile(arg1: unknown, arg2: unknown): unknown;
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
deleteGroupFoldertransgroupfile(arg1: unknown, arg2: unknown): unknown;
deleteTransferInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
cancelUrlDownload(arg: unknown): unknown;
updateOnlineVideoElemStatus(arg: unknown): unknown;
getGroupSpace(arg: unknown): unknown;
getGroupFileList(groupCode: string, params: GetFileListParam): Promise<GeneralCallResult & {
groupSpaceResult: {
retCode: number
retMsg: string
clientWording: string
totalSpace: number
usedSpace: number
allUpload: boolean
}
}>;
getGroupFileInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
getGroupFileListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
getGroupTransferListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
moveGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
transGroupFile(arg1: unknown, arg2: unknown): unknown;
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
transGroupFileResult: {
result: any
successFileIdList: Array<any>
failFileIdList: Array<any>
}
}>;
translateEnWordToZn(words: string[]): Promise<GeneralCallResult & { words: string[] }>;
getScreenOCR(path: string): Promise<unknown>;
batchGetGroupFileCount(Gids: Array<string>): Promise<GeneralCallResult & { groupCodes: Array<string>, groupFileCounts: Array<number> }>;
queryPicDownloadSize(arg: unknown): unknown;
searchGroupFiletransgroupfile(arg1: unknown, arg2: unknown): unknown;
searchMoreGroupFile(arg: unknown): unknown;
cancelSearcheGroupFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
onlyDownloadFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
onlyUploadFiletransgroupfile(arg1: unknown, arg2: unknown): unknown;
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
uploadRMFileWithoutMsg(arg: unknown): unknown;
isNull(): boolean;
}

View File

@@ -0,0 +1,34 @@
import { NodeIKernelRobotListener } from "@/core/listeners";
export interface NodeIKernelRobotService {
fetchGroupRobotStoreDiscovery(arg: unknown): unknown;
sendGroupRobotStoreSearch(arg: unknown): unknown;
fetchGroupRobotStoreCategoryList(arg: unknown): unknown;
FetchSubscribeMsgTemplate(arg: unknown): unknown;
FetchSubcribeMsgTemplateStatus(arg: unknown): unknown;
SubscribeMsgTemplateSet(arg1: unknown, arg2: unknown): unknown;
fetchRecentUsedRobots(arg: unknown): unknown;
fetchShareArkInfo(arg: unknown): unknown;
addKernelRobotListener(Listener: NodeIKernelRobotListener): number;
removeKernelRobotListener(ListenerId: number): unknown;
getAllRobotFriendsFromCache(): unknown;
fetchAllRobots(arg1: unknown, arg2: unknown): unknown;
removeAllRecommendCache(): unknown;
setRobotPickTts(arg1: unknown, arg2: unknown): unknown;
getRobotUinRange(data: any): Promise<{ response: { robotUinRanges: any } }>
isNull(): boolean;
}

View File

@@ -0,0 +1,79 @@
export interface NodeIKernelSearchService{
addKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
removeKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
searchStrangerr(...args: any[]): unknown;// needs 3 arguments
searchGroupr(...args: any[]): unknown;// needs 1 arguments
searchLocalInfor(...args: any[]): unknown;// needs 2 arguments
cancelSearchLocalInfor(...args: any[]): unknown;// needs 3 arguments
searchBuddyChatInfor(...args: any[]): unknown;// needs 2 arguments
searchMoreBuddyChatInfor(...args: any[]): unknown;// needs 1 arguments
cancelSearchBuddyChatInfor(...args: any[]): unknown;// needs 3 arguments
searchContactr(...args: any[]): unknown;// needs 2 arguments
searchMoreContactr(...args: any[]): unknown;// needs 1 arguments
cancelSearchContactr(...args: any[]): unknown;// needs 3 arguments
searchGroupChatInfor(...args: any[]): unknown;// needs 3 arguments
resetSearchGroupChatInfoSortTyper(...args: any[]): unknown;// needs 3 arguments
resetSearchGroupChatInfoFilterMembersr(...args: any[]): unknown;// needs 3 arguments
searchMoreGroupChatInfor(...args: any[]): unknown;// needs 1 arguments
cancelSearchGroupChatInfor(...args: any[]): unknown;// needs 3 arguments
searchChatsWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
searchMoreChatsWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
cancelSearchChatsWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
searchChatMsgsr(...args: any[]): unknown;// needs 2 arguments
searchMoreChatMsgsr(...args: any[]): unknown;// needs 1 arguments
cancelSearchChatMsgsr(...args: any[]): unknown;// needs 3 arguments
searchMsgWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
searchMoreMsgWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
cancelSearchMsgWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
searchFileWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
searchMoreFileWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
cancelSearchFileWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
searchAtMeChatsr(...args: any[]): unknown;// needs 3 arguments
searchMoreAtMeChatsr(...args: any[]): unknown;// needs 1 arguments
cancelSearchAtMeChatsr(...args: any[]): unknown;// needs 3 arguments
searchChatAtMeMsgsr(...args: any[]): unknown;// needs 1 arguments
searchMoreChatAtMeMsgsr(...args: any[]): unknown;// needs 1 arguments
cancelSearchChatAtMeMsgsr(...args: any[]): unknown;// needs 3 arguments
addSearchHistoryr(...args: any[]): unknown;// needs 1 arguments
removeSearchHistoryr(...args: any[]): unknown;// needs 1 arguments
searchCacher(...args: any[]): unknown;// needs 3 arguments
clearSearchCacher(...args: any[]): unknown;// needs 1 arguments
}

View File

@@ -0,0 +1,41 @@
import { NodeIKernelStorageCleanListener } from "@/core/listeners";
import { GeneralCallResult } from "./common";
export interface NodeIKernelStorageCleanService {
addKernelStorageCleanListener(Listener: NodeIKernelStorageCleanListener): number;
removeKernelStorageCleanListener(ListenerId: number): void;
addCacheScanedPaths(arg: unknown): unknown;
addFilesScanedPaths(arg: unknown): unknown;
scanCache(): Promise<GeneralCallResult & {
size: string[]
}>;
addReportData(arg: unknown): unknown;
reportData(): unknown;
getChatCacheInfo(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown): unknown;
getFileCacheInfo(arg1: unknown, arg2: unknown, arg3: unknown, arg44: unknown, args5: unknown): unknown;
clearChatCacheInfo(arg1: unknown, arg2: unknown): unknown;
clearCacheDataByKeys(arg: unknown): unknown;
setSilentScan(arg: unknown): unknown;
closeCleanWindow(): unknown;
clearAllChatCacheInfo(): unknown;
endScan(arg: unknown): unknown;
addNewDownloadOrUploadFile(arg: unknown): unknown;
isNull(): boolean;
}

View File

@@ -0,0 +1,10 @@
export interface NodeIKernelTianShuService {
addKernelTianShuListener(...args: any[]): unknown;// needs 1 arguments
removeKernelTianShuListener(...args: any[]): unknown;// needs 1 arguments
requesTianShuNumeralRe(...args: any[]): unknown;//d needs 1 arguments
reportTianShuNumeralRed(...args: any[]): unknown;// needs 1 arguments
}

View File

@@ -0,0 +1,12 @@
import { forceFetchClientKeyRetType } from "./common";
export interface NodeIKernelTicketService {
addKernelTicketListener(listener: unknown): void;
removeKernelTicketListener(listenerId: unknown): void;
forceFetchClientKey(arg: string): Promise<forceFetchClientKeyRetType>;
isNull(): boolean;
}

View File

@@ -0,0 +1,20 @@
import { GeneralCallResult } from "./common";
export interface NodeIKernelTipOffService {
addKernelTipOffListener(listener: unknown): void;
removeKernelTipOffListener(listenerId: unknown): void;
tipOffSendJsData(args: unknown[]): Promise<unknown>;//2
getPskey(domainList: string[], nocache: boolean): Promise<GeneralCallResult & { domainPskeyMap: Map<string, string> }>;//2
tipOffSendJsData(args: unknown[]): Promise<unknown>;//2
tipOffMsgs(args: unknown[]): Promise<unknown>;//1
encodeUinAesInfo(args: unknown[]): Promise<unknown>;//2
isNull(): boolean;
}

View File

@@ -0,0 +1,5 @@
export interface NodeIKernelUixConvertService {
getUin(uid: string[]): Promise<{ uidInfo: Map<string, string> }>;
getUid(uin: string[]): Promise<{ uinInfo: Map<string, string> }>;
}

View File

@@ -0,0 +1,15 @@
export interface NodeIKernelUnitedConfigService{
addKernelUnitedConfigListener(...args: any[]): unknown;// needs 1 arguments
removeKernelUnitedConfigListener(...args: any[]): unknown;// needs 1 arguments
fetchUnitedCommendConfig(...args: any[]): unknown;// needs 1 arguments
fetchUnitedSwitchConfig(...args: any[]): unknown;// needs 1 arguments
loadUnitedConfig(...args: any[]): unknown;// needs 1 arguments
isUnitedConfigSwitchOn(...args: any[]): unknown;// needs 1 arguments
registerUnitedConfigPushGroupList(...args: any[]): unknown;// needs 1 arguments
}

View File

@@ -0,0 +1,5 @@
// public interface IYellowFaceForManagerService extends QRouteApi {
// void download(@NotNull String resourceConfigJson, @NotNull String resourceDir, @NotNull String cacheDir, boolean force, @NotNull IKernelYellowFaceDownloadCallback callback);
// void setHistory(@NotNull String fullMd5, @NotNull IOperateCallback callback);
// }

View File

@@ -0,0 +1,15 @@
export enum GeneralCallResultStatus {
OK = 0,
// ERROR = 1,
}
export interface GeneralCallResult{
result: GeneralCallResultStatus,
errMsg: string
}
export interface forceFetchClientKeyRetType extends GeneralCallResult {
url: string;
keyIndex: string;
clientKey: string;
expireTime: string;
}

View File

@@ -0,0 +1,16 @@
export * from './common';
export * from './NodeIKernelAvatarService';
export * from './NodeIKernelBuddyService';
export * from './NodeIKernelFileAssistantService';
export * from './NodeIKernelGroupService';
export * from './NodeIKernelLoginService';
export * from './NodeIKernelMsgService';
export * from './NodeIKernelOnlineStatusService';
export * from './NodeIKernelProfileLikeService';
export * from './NodeIKernelProfileService';
export * from './NodeIKernelTicketService';
export * from './NodeIKernelStorageCleanService';
export * from './NodeIKernelRobotService';
export * from './NodeIKernelRichMediaService';
export * from './NodeIKernelDbToolsService';
export * from './NodeIKernelTipOffService'

View File

@@ -0,0 +1,131 @@
import { appid, qqPkgInfo, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
import { hostname, systemName, systemVersion } from '@/common/utils/system';
import path from 'node:path';
import fs from 'node:fs';
import { getMachineId } from '@/common/utils/system';
// 补充
export enum PlatformType {
KUNKNOWN,
KANDROID,
KIOS,
KWINDOWS,
KMAC
}
export enum DeviceType {
KUNKNOWN,
KPHONE,
KPAD,
KCOMPUTER
}
//推送类型
export enum VendorType {
KNOSETONIOS = 0,
KSUPPORTGOOGLEPUSH = 99,
KSUPPORTHMS = 3,
KSUPPORTOPPOPUSH = 4,
KSUPPORTTPNS = 2,
KSUPPORTVIVOPUSH = 5,
KUNSUPPORTANDROIDPUSH = 1
}
export interface WrapperSessionInitConfig {
selfUin: string
selfUid: string
desktopPathConfig: {
account_path: string // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
}
clientVer: string // 9.9.8-22355
a2: '',
d2: '',
d2Key: '',
machineId: '',
platform: 3, // 3是Windows?
platVer: string, // 系统版本号, 应该可以固定
appid: string,
rdeliveryConfig: {
appKey: '',
systemId: 0,
appId: '',
logicEnvironment: '',
platform: 3,
language: '',
sdkVersion: '',
userId: '',
appVersion: '',
osVersion: '',
bundleId: '',
serverUrl: '',
fixedAfterHitKeys: ['']
}
'defaultFileDownloadPath': string, // 这个可以通过环境变量获取?
'deviceInfo': {
'guid': string,
'buildVer': string,
'localId': 2052,
'devName': string,
'devType': string,
'vendorName': '',
'osVer': string,
'vendorOsName': string,
'setMute': false,
'vendorType': 0
},
'deviceConfig': '{"appearance":{"isSplitViewMode":true},"msg":{}}'
}
export const sessionConfig: WrapperSessionInitConfig | any = {};
export async function genSessionConfig(selfUin: string, selfUid: string, account_path: string): Promise<WrapperSessionInitConfig> {
const downloadPath = path.join(account_path, 'NapCat', 'temp');
fs.mkdirSync(downloadPath, { recursive: true });
let guid: string = await getMachineId();
//console.log(guid);
// guid = '52afb776-82f6-4e59-9d38-44705b112d0a';
//let guid: string = await getMachineId();
const config: WrapperSessionInitConfig = {
selfUin,
selfUid,
desktopPathConfig: {
account_path // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
},
clientVer: qqVersionConfigInfo.curVersion, // 9.9.8-22355
a2: '',
d2: '',
d2Key: '',
machineId: '',
platform: 3, // 3是Windows?
platVer: systemVersion, // 系统版本号, 应该可以固定
appid: appid,
rdeliveryConfig: {
appKey: '',
systemId: 0,
appId: '',
logicEnvironment: '',
platform: 3,
language: '',
sdkVersion: '',
userId: '',
appVersion: '',
osVersion: '',
bundleId: '',
serverUrl: '',
fixedAfterHitKeys: ['']
},
'defaultFileDownloadPath': downloadPath,
'deviceInfo': {
guid,
'buildVer': qqPkgInfo.version,
'localId': 2052,
'devName': hostname,
'devType': systemName,
'vendorName': '',
'osVer': systemVersion,
'vendorOsName': systemName,
'setMute': false,
'vendorType': 0
},
'deviceConfig': '{"appearance":{"isSplitViewMode":true},"msg":{}}'
};
Object.assign(sessionConfig, config);
// log(sessionConfig);
return config;
}

View File

@@ -0,0 +1,30 @@
import path from 'node:path';
import fs from 'node:fs';
import { enableConsoleLog, enableFileLog, logDebug, logError, LogLevel, setLogLevel } from '@/common/utils/log';
import { ConfigBase } from '@/common/utils/ConfigBase';
import { selfInfo } from '@/core/data';
export interface NapCatConfig {
fileLog: boolean,
consoleLog: boolean,
fileLogLevel: LogLevel,
consoleLogLevel: LogLevel,
}
class Config extends ConfigBase<NapCatConfig> implements NapCatConfig{
fileLog = true;
consoleLog = true;
fileLogLevel = LogLevel.DEBUG;
consoleLogLevel = LogLevel.INFO;
constructor() {
super();
}
getConfigPath() {
return path.join(this.getConfigDir(), `napcat_${selfInfo.uin}.json`);
}
}
export const napCatConfig = new Config();

View File

@@ -0,0 +1,42 @@
//远端rkey获取
import { logError } from '@/common/utils/log';
import { RequestUtil } from '@/common/utils/request';
interface ServerRkeyData {
group_rkey: string;
private_rkey: string;
expired_time: number;
}
class RkeyManager {
serverUrl: string = '';
private rkeyData: ServerRkeyData = {
group_rkey: '',
private_rkey: '',
expired_time: 0
};
constructor(serverUrl: string) {
this.serverUrl = serverUrl;
}
async getRkey() {
if (this.isExpired()) {
try {
await this.refreshRkey();
} catch (e) {
logError('获取rkey失败', e);
}
}
return this.rkeyData;
}
isExpired(): boolean {
const now = new Date().getTime() / 1000;
// console.log(`now: ${now}, expired_time: ${this.rkeyData.expired_time}`);
return now > this.rkeyData.expired_time;
}
async refreshRkey(): Promise<any> {
//刷新rkey
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(this.serverUrl, 'GET');
}
}
export const rkeyManager = new RkeyManager('http://napcat-sign.wumiao.wang:2082/rkey');

296
src/core/src/wrapper.ts Normal file
View File

@@ -0,0 +1,296 @@
import path from 'node:path';
import fs from 'node:fs';
import { WrapperSessionInitConfig } from './sessionConfig';
import {
NodeIDependsAdapter,
NodeIDispatcherAdapter,
NodeIGlobalAdapter,
} from './adapters';
import {
NodeIKernelSessionListener,
NodeIKernelMsgListener,
NodeIKernelLoginListener,
NodeIKernelBuddyListener,
NodeIKernelGroupListener,
NodeIKernelProfileListener,
} from './listeners';
import {
NodeIKernelLoginService,
NodeIKernelMsgService,
NodeIKernelBuddyService,
NodeIKernelGroupService,
NodeIKernelProfileService,
NodeIKernelProfileLikeService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
NodeIKernelRichMediaService,
NodeIKernelAvatarService,
} from './services';
import { qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
import { NodeIKernelStorageCleanService } from './services/NodeIKernelStorageCleanService';
import { NodeIKernelRobotService } from './services/NodeIKernelRobotService';
import { dirname } from "node:path"
import { fileURLToPath } from "node:url"
import { NodeIKernelNodeMiscService } from './services/NodeIKernelNodeMiscService';
import { NodeIKernelUixConvertService } from './services/NodeIKernelUixConvertService';
import { NodeIKernelMsgBackupService } from './services/NodeIKernelMsgBackupService';
import { NodeIKernelAlbumService } from './services/NodeIKernelAlbumService';
import { NodeIKernelTianShuService } from './services/NodeIKernelTianShuService';
import { NodeIKernelUnitedConfigService } from './services/NodeIKernelUnitedConfigService';
import { NodeIKernelSearchService } from './services/NodeIKernelSearchService';
import { NodeIKernelCollectionService } from './services/NodeIKernelCollectionService';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export interface NodeQQNTWrapperUtil {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil
getNTUserDataInfoConfig(): string
emptyWorkingSet(n: number): void
getSsoCmdOfOidbReq(arg1: number, arg2: number): unknown,
getSsoBufferOfOidbReq(...args: unknown[]): unknown,//有点看不懂参数定义 待补充 好像是三个参数
getOidbRspInfo(arg: string): unknown,//可能是错的
getFileSize(path: string): Promise<number>,//直接的猜测
genFileMd5Buf(arg: string): unknown,//可能是错的
genFileMd5Hex(path: string): unknown,//直接的猜测
genFileShaBuf(path: string): unknown,//直接的猜测
genFileCumulateSha1(path: string): unknown,//直接的猜测
genFileShaHex(path: string): unknown,//直接的猜测
fileIsExist(path: string): unknown,
startTrace(path: string): unknown,//可能是错的
copyFile(src: string, dst: string): unknown,
genFileShaAndMd5Hex(path: string, unknown: number): unknown,//可能是错的
setTraceInfo(unknown: Object): unknown,
encodeOffLine(unknown: Object): unknown,
decodeOffLine(arg: string): unknown,//可能是错的 传递hex
DecoderRecentInfo(arg: string): unknown,//可能是错的 传递hex
getPinyin(arg0: string, arg1: boolean): unknown,
matchInPinyin(arg0: any[], arg1: string): unknown,//参数特复杂 arg0是个复杂数据类型
makeDirByPath(arg0: string): unknown,
emptyWorkingSet(arg0: number): unknown,//参数是UINT32
runProcess(arg0: string, arg1: boolean): unknown,
runProcessArgs(arg0: string, arg1: { [key: string]: string; }, arg2: boolean): unknown,
calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown,
fullWordToHalfWord(arg0: string): unknown,
getNTUserDataInfoConfig(): unknown,
pathIsReadableAndWriteable(path: string): unknown,//直接的猜测
resetUserDataSavePathToDocument(): unknown,
getSoBuildInfo(): any,//例如 0[0]_d491dc01e0a_0
registerCountInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown,
registerValueInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown,
registerValueInstrumentsWithBoundary(arg0: string, arg1: unknown, arg2: unknown, arg3: number, arg4: number): unknown,
reportCountIndicators(arg0: string, arg1: Map<unknown, unknown>, arg2: string, arg3: number, arg4: boolean): unknown,
reportValueIndicators(arg0: string, arg1: Map<unknown, unknown>, arg2: string, arg3: boolean, arg4: number): unknown,
checkNewUserDataSaveDirAvailable(arg0: string): unknown,
copyUserData(arg0: string, arg1: string): Promise<any>,
setUserDataSaveDirectory(arg0: string): Promise<any>,
hasOtherRunningQQProcess(): boolean,
quitAllRunningQQProcess(arg: boolean): unknown,
checkNvidiaConfig(): unknown,
repairNvidiaConfig(): unknown,
getNvidiaDriverVersion(): unknown,
isNull(): unknown
}
export interface NodeIQQNTWrapperSession {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeIQQNTWrapperSession;
init(
wrapperSessionInitConfig: WrapperSessionInitConfig,
nodeIDependsAdapter: NodeIDependsAdapter,
nodeIDispatcherAdapter: NodeIDispatcherAdapter,
nodeIKernelSessionListener: NodeIKernelSessionListener
): void;
startNT(n: 0): void;
startNT(): void;
getMsgService(): NodeIKernelMsgService;
getProfileService(): NodeIKernelProfileService;
getProfileLikeService(): NodeIKernelProfileLikeService;
getGroupService(): NodeIKernelGroupService;
getStorageCleanService(): NodeIKernelStorageCleanService;
getBuddyService(): NodeIKernelBuddyService;
getRobotService(): NodeIKernelRobotService;
getTicketService(): NodeIKernelTicketService;
getTipOffService(): NodeIKernelTipOffService;
getNodeMiscService(): NodeIKernelNodeMiscService;
getRichMediaService(): NodeIKernelRichMediaService;
getMsgBackupService(): NodeIKernelMsgBackupService;
getAlbumService(): NodeIKernelAlbumService;
getTianShuService(): NodeIKernelTianShuService;
getUnitedConfigService(): NodeIKernelUnitedConfigService;
getSearchService(): NodeIKernelSearchService;
getDirectSessionService(): unknown;
getRDeliveryService(): unknown;
getAvatarService(): NodeIKernelAvatarService;
getFeedChannelService(): unknown;
getYellowFaceService(): unknown;
getCollectionService(): NodeIKernelCollectionService;
getSettingService(): unknown;
getQiDianService(): unknown;
getFileAssistantService(): unknown;
getGuildService(): unknown;
getSkinService(): unknown;
getTestPerformanceService(): unknown;
getQQPlayService(): unknown;
getDbToolsService(): unknown;
getUixConvertService(): NodeIKernelUixConvertService;
getOnlineStatusService(): unknown;
getRemotingService(): unknown;
getGroupTabService(): unknown;
getGroupSchoolService(): unknown;
getLiteBusinessService(): unknown;
getGuildMsgService(): unknown;
getLockService(): unknown;
getMSFService(): unknown
getGuildHotUpdateService(): unknown;
getAVSDKService(): unknown;
getRecentContactService(): unknown;
getConfigMgrService(): unknown;
}
export interface EnginInitDesktopConfig {
base_path_prefix: string,
platform_type: 3,
app_type: 4,
app_version: string,
os_version: string,
use_xlog: true,
qua: string,
global_path_config: {
desktopGlobalPath: string,
},
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }
}
export interface NodeIQQNTWrapperEngine {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeIQQNTWrapperEngine;
initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void;
}
export interface WrapperNodeApi {
[key: string]: any;
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
NodeIKernelGroupListener: NodeIKernelGroupListener;
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
NodeIKernelMsgListener: NodeIKernelMsgListener;
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
NodeIGlobalAdapter: NodeIGlobalAdapter;
NodeIDependsAdapter: NodeIDependsAdapter;
NodeIDispatcherAdapter: NodeIDispatcherAdapter;
NodeIKernelSessionListener: NodeIKernelSessionListener;
NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelLoginListener: NodeIKernelLoginListener;
NodeIKernelProfileService: NodeIKernelProfileService;
NodeIKernelProfileListener: NodeIKernelProfileListener;
}
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
if (!fs.existsSync(wrapperNodePath)) {
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${qqVersionConfigInfo.curVersion}/wrapper.node`);
}
let WrapperLoader = path.join(__dirname, "WrapperLoader.cjs");
//此处待优化
fs.writeFileSync(WrapperLoader, `
module.exports = require("${wrapperNodePath.replace(/\\/g, "\\\\")}");
exports = module.exports;
`)
const QQWrapper: WrapperNodeApi = (await import("file://" + WrapperLoader)).default;
export default QQWrapper;

43
src/core/tsconfig.json Normal file
View File

@@ -0,0 +1,43 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist",
"declaration": true,
"target": "ESNext",
"useDefineForClassFields": true,
"esModuleInterop": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "Node",
"experimentalDecorators": true,
"allowImportingTsExtensions": false,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/common/*": [
"../common/*"
],
"@/core": [
"./src/index"
],
"@/core/*": [
"./src/*"
]
}
},
"include": [
"./src/**/*.ts"
]
}

116
src/core/vite.config.ts Normal file
View File

@@ -0,0 +1,116 @@
import { UserConfig, defineConfig } from 'vite';
import { builtinModules } from 'module';
import obfuscator from 'rollup-plugin-obfuscator';
import { Plugin } from 'vite';
import path from 'node:path';
import dts from 'vite-plugin-dts';
import cp from 'vite-plugin-cp';
import babel from 'vite-plugin-babel';
const external: string[] = [ /* Empty */];
const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat();
const baseConfig: UserConfig = {
build: {
target: 'modules',
outDir: './',
lib: {
name: '@napneko/core',
entry: 'src/index.ts',
formats: ['es'],
fileName: () => 'index.js',
},
rollupOptions: {
input: {
index: path.resolve(__dirname, 'src/index.ts'),
qqnt: path.resolve(__dirname, 'src/qqnt/index.ts'),
'qqnt/apis': path.resolve(__dirname, 'src/qqnt/apis/index.ts'),
'qqnt/listeners': path.resolve(__dirname, 'src/qqnt/listeners/index.ts'),
'qqnt/entities': path.resolve(__dirname, 'src/qqnt/entities/index.ts'),
'qqnt/adapters': path.resolve(__dirname, 'src/qqnt/adapters/index.ts'),
'qqnt/services': path.resolve(__dirname, 'src/qqnt/services/index.ts'),
service: path.resolve(__dirname, 'src/service/index.ts')
},
output: {
// 输出设置为系统模块格式,确保目录结构被保持
format: 'esm',
dir: path.resolve(__dirname, './dist/core/src'),
entryFileNames: '[name]/index.js',
chunkFileNames: '[name]/[hash]/index.js',
// preserveModules: true, // 保持模块结构
// preserveModulesRoot: 'src'
},
external: [...nodeModules, ...external],
},
},
resolve: {
alias: {
'@/common': path.resolve(__dirname, '../common'),
'@/core': path.resolve(__dirname, './src'),
'./lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg',
}
},
};
const commonPlugins: Plugin[] = [
babel({
filter: /.*\.(ts)$/,
babelConfig: {
babelrc: false,
configFile: false,
presets: ["@babel/preset-typescript"],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-class-properties',
],
},
}),
dts({
outDir: './dist',
staticImport: true,
rollupTypes: false,
include: 'src/**/*.ts',
}),
cp({
targets: [
// ...external.map(genCpModule),
{ src: './pub-package.json', dest: '../core.lib', rename: 'package.json' },
]
})
];
export default defineConfig(({ mode }) => {
const result: UserConfig = { ...baseConfig };
if (mode === 'production') {
result.build!.minify = 'esbuild';
result.plugins = [
obfuscator({
options: {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: false,
disableConsoleOutput: false,
identifierNamesGenerator: 'hexadecimal',
log: false,
renameGlobals: false,
rotateStringArray: true,
selfDefending: true,
stringArray: true,
stringArrayEncoding: ['base64'],
stringArrayThreshold: 0.75,
transformObjectKeys: true,
unicodeEscapeSequence: false
},
include: ['src/**/*.js', 'src/**/*.ts'],
}),
...commonPlugins
];
} else {
result.build!.minify = false;
result.plugins = [...commonPlugins];
}
return result;
});