diff --git a/src/core/.eslintrc.cjs b/src/core/.eslintrc.cjs new file mode 100644 index 000000000..3212fad6f --- /dev/null +++ b/src/core/.eslintrc.cjs @@ -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'], + } +}; diff --git a/src/core/.gitignore b/src/core/.gitignore new file mode 100644 index 000000000..bc178a54a --- /dev/null +++ b/src/core/.gitignore @@ -0,0 +1,5 @@ +.idea/ +node_modules/ +dist/ +lib/ +package-lock.json diff --git a/src/core/README.md b/src/core/README.md new file mode 100644 index 000000000..b6b779bbb --- /dev/null +++ b/src/core/README.md @@ -0,0 +1,3 @@ +# @napneko/core + +此仓库目前只用于隐藏源码,目前无法进行单独打包,只是作为 NapCatQQ 的 git submodule 引用。 \ No newline at end of file diff --git a/src/core/build.cjs b/src/core/build.cjs new file mode 100644 index 000000000..0090b28a4 --- /dev/null +++ b/src/core/build.cjs @@ -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'); +})() diff --git a/src/core/package.json b/src/core/package.json new file mode 100644 index 000000000..f4220b925 --- /dev/null +++ b/src/core/package.json @@ -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" + } +} diff --git a/src/core/pub-package.json b/src/core/pub-package.json new file mode 100644 index 000000000..d7b4b825f --- /dev/null +++ b/src/core/pub-package.json @@ -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" +} diff --git a/src/core/scripts/obfuscator.cjs b/src/core/scripts/obfuscator.cjs new file mode 100644 index 000000000..a4c125822 --- /dev/null +++ b/src/core/scripts/obfuscator.cjs @@ -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); diff --git a/src/core/src/adapters/NodeIDependsAdapter.ts b/src/core/src/adapters/NodeIDependsAdapter.ts new file mode 100644 index 000000000..7f77a83c1 --- /dev/null +++ b/src/core/src/adapters/NodeIDependsAdapter.ts @@ -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) { + } +} diff --git a/src/core/src/adapters/NodeIDispatcherAdapter.ts b/src/core/src/adapters/NodeIDispatcherAdapter.ts new file mode 100644 index 000000000..20557687c --- /dev/null +++ b/src/core/src/adapters/NodeIDispatcherAdapter.ts @@ -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) { + } +} diff --git a/src/core/src/adapters/NodeIGlobalAdapter.ts b/src/core/src/adapters/NodeIGlobalAdapter.ts new file mode 100644 index 000000000..29cb906ae --- /dev/null +++ b/src/core/src/adapters/NodeIGlobalAdapter.ts @@ -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[]) { + } +} diff --git a/src/core/src/adapters/index.ts b/src/core/src/adapters/index.ts new file mode 100644 index 000000000..2db6d2886 --- /dev/null +++ b/src/core/src/adapters/index.ts @@ -0,0 +1,3 @@ +export * from './NodeIDependsAdapter'; +export * from './NodeIDispatcherAdapter'; +export * from './NodeIGlobalAdapter'; diff --git a/src/core/src/apis/collection.ts b/src/core/src/apis/collection.ts new file mode 100644 index 000000000..bcaf8795f --- /dev/null +++ b/src/core/src/apis/collection.ts @@ -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); + } +} diff --git a/src/core/src/apis/file.ts b/src/core/src/apis/file.ts new file mode 100644 index 000000000..582e383ab --- /dev/null +++ b/src/core/src/apis/file.ts @@ -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 void> = new Map 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 { + 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((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 { + 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 = ['tmp', 'hotUpdate']) { + // 参数未验证 + return napCatCore.session.getStorageCleanService().clearCacheDataByKeys(cacheKeys); + } + + static addCacheScannedPaths(pathMap: object = {}) { + return napCatCore.session.getStorageCleanService().addCacheScanedPaths(pathMap); + } + + static scanCache(): Promise { + // 需要注册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); + } +} diff --git a/src/core/src/apis/friend.ts b/src/core/src/apis/friend.ts new file mode 100644 index 000000000..e6cb961ee --- /dev/null +++ b/src/core/src/apis/friend.ts @@ -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 { + let [_retData, _BuddyArg] = await NTEventDispatch.CallNormalEvent + <(force: boolean) => Promise, (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 + }); + } +} diff --git a/src/core/src/apis/group.ts b/src/core/src/apis/group.ts new file mode 100644 index 000000000..5042574c5 --- /dev/null +++ b/src/core/src/apis/group.ts @@ -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, (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, (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> { + 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) { + return napCatCore.session.getRichMediaService().batchGetGroupFileCount(Gids); + } + static async getGroupIgnoreNotifies() { + } + static async getArkJsonGroupShare(GroupCode: string) { + let ret = await NTEventDispatch.CallNoListenerEvent + <(GroupId: string) => Promise>( + '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 + } + } + ); + } +} \ No newline at end of file diff --git a/src/core/src/apis/index.ts b/src/core/src/apis/index.ts new file mode 100644 index 000000000..4c0619e3a --- /dev/null +++ b/src/core/src/apis/index.ts @@ -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'; \ No newline at end of file diff --git a/src/core/src/apis/msg.ts b/src/core/src/apis/msg.ts new file mode 100644 index 000000000..f63331293 --- /dev/null +++ b/src/core/src/apis/msg.ts @@ -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 void | Promise) | null> = {};// peerUid: callbackFunc + +const sendSuccessCBMap: Record boolean | Promise) | null> = {};// uuid: callbackFunc + +const GroupFileInfoUpdateTasks: Map void)> = new Map(); + +const sentMsgTasks: Map 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 { + 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>(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 { + const peerUid = peer.peerUid; + // 等待上一个相同的peer发送完 + let checkLastSendUsingTime = 0; + const waitLastSend: () => Promise = 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 { + 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()); + } + ); + } +} diff --git a/src/core/src/apis/sign.ts b/src/core/src/apis/sign.ts new file mode 100644 index 000000000..7c554a7c7 --- /dev/null +++ b/src/core/src/apis/sign.ts @@ -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 { +// 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¬ice=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; +} \ No newline at end of file diff --git a/src/core/src/apis/system.ts b/src/core/src/apis/system.ts new file mode 100644 index 000000000..e2e162a02 --- /dev/null +++ b/src/core/src/apis/system.ts @@ -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>( + '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); + } +} \ No newline at end of file diff --git a/src/core/src/apis/user.ts b/src/core/src/apis/user.ts new file mode 100644 index 000000000..357ed6610 --- /dev/null +++ b/src/core/src/apis/user.ts @@ -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 = {}; // uid: User + +const profileListener = new ProfileListener(); + +const userDetailHandlers: Map 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 { + // 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> { + 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 { + 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 }>>( + '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 }>>( + 'NodeIKernelUixConvertService/getUin', + 5000, + [Uid] + ); + return ret.uinInfo.get(Uid); + } + static async getUserDetailInfoByUin(Uin: string) { + return NTEventDispatch.CallNoListenerEvent + <(Uin: string) => Promise>( + 'NodeIKernelProfileService/getUserDetailInfoByUin', + 5000, + Uin + ); + } + @CacheClassFuncAsync(3600 * 1000, 'ClientKey') + static async forceFetchClientKey() { + return await napCatCore.session.getTicketService().forceFetchClientKey(''); + } +} diff --git a/src/core/src/apis/webapi.ts b/src/core/src/apis/webapi.ts new file mode 100644 index 000000000..9af398030 --- /dev/null +++ b/src/core/src/apis/webapi.ts @@ -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(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 { + //logDebug('webapi 获取群成员', GroupCode); + let MemberData: Array = new Array(); + 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[] = []; + const fastRet = await RequestUtil.HttpGetJson('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 = RequestUtil.HttpGetJson('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(url, 'GET', '', { 'Cookie': CookieValue }); + return ret; + } catch (e) { + return undefined; + } + return undefined; + } + static async getGrouptNotice(GroupCode: string): Promise { + 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(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; + } +} diff --git a/src/core/src/core.ts b/src/core/src/core.ts new file mode 100644 index 000000000..2ac4bcfbf --- /dev/null +++ b/src/core/src/core.ts @@ -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; +} + + +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 { + 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 { + 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) { + 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(); + + diff --git a/src/core/src/data.ts b/src/core/src/data.ts new file mode 100644 index 000000000..9a4072b9c --- /dev/null +++ b/src/core/src/data.ts @@ -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 = new Map(); + +export function deleteGroup(groupQQ: string) { + groups.delete(groupQQ); + groupMembers.delete(groupQQ); +} + +// 群号 -> 群成员map(uid=>GroupMember) +export const groupMembers: Map> = new Map>(); + +// uid -> Friend 下面这俩个准备移除 QQ里面自带缓存 +export const friends: Map = new Map(); +export const rawFriends: Array = []; // 带分组的好友列表 + +export const groupNotifies: Record = {}; // flag->GroupNotify +export async function getGroup(qq: string | number): Promise { + 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 = {}; // 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, +}; diff --git a/src/core/src/entities/cache.ts b/src/core/src/entities/cache.ts new file mode 100644 index 000000000..8c53d1b0e --- /dev/null +++ b/src/core/src/entities/cache.ts @@ -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; +} diff --git a/src/core/src/entities/constructor.ts b/src/core/src/entities/constructor.ts new file mode 100644 index 000000000..d1c22e501 --- /dev/null +++ b/src/core/src/entities/constructor.ts @@ -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(); // 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 { + 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 { + 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 { + 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((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 { + 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 { + 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 + } + }; + } +} diff --git a/src/core/src/entities/face_config.json b/src/core/src/entities/face_config.json new file mode 100644 index 000000000..84bb8fecd --- /dev/null +++ b/src/core/src/entities/face_config.json @@ -0,0 +1,3665 @@ +{ + "sysface": [ + { + "QSid": "392", + "QDes": "/龙年快乐", + "IQLid": "392", + "AQLid": "392", + "EMCode": "10392", + "AniStickerType": 3, + "AniStickerPackId": "1", + "AniStickerId": "38" + }, + { + "QSid": "393", + "QDes": "/新年中龙", + "IQLid": "393", + "AQLid": "393", + "EMCode": "10393", + "QHide": "1", + "AniStickerType": 3, + "AniStickerPackId": "1", + "AniStickerId": "39" + }, + { + "QSid": "364", + "QDes": "/超级赞", + "IQLid": "364", + "AQLid": "364", + "EMCode": "10364", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "1", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "366", + "QDes": "/芒狗", + "IQLid": "366", + "AQLid": "366", + "EMCode": "10366", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "2", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "362", + "QDes": "/好兄弟", + "IQLid": "362", + "AQLid": "362", + "EMCode": "10362", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "3", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "397", + "QDes": "/抛媚眼", + "IQLid": "397", + "AQLid": "397", + "EMCode": "10397", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "4", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "396", + "QDes": "/狼狗", + "IQLid": "396", + "AQLid": "396", + "EMCode": "10396", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "5", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "360", + "QDes": "/亲亲", + "IQLid": "360", + "AQLid": "360", + "EMCode": "10360", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "6", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "361", + "QDes": "/狗狗笑哭", + "IQLid": "361", + "AQLid": "361", + "EMCode": "10361", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "7", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "363", + "QDes": "/狗狗可怜", + "IQLid": "363", + "AQLid": "363", + "EMCode": "10363", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "8", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "365", + "QDes": "/狗狗生气", + "IQLid": "365", + "AQLid": "365", + "EMCode": "10365", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "9", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "367", + "QDes": "/狗狗疑问", + "IQLid": "367", + "AQLid": "367", + "EMCode": "10367", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "10", + "AniStickerPackId": "2", + "AniStickerPackName": "汪汪" + }, + { + "QSid": "399", + "QDes": "/tui", + "IQLid": "399", + "AQLid": "399", + "EMCode": "10399", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "1", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "398", + "QDes": "/超级ok", + "IQLid": "398", + "AQLid": "398", + "EMCode": "10398", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "2", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "373", + "QDes": "/忙", + "IQLid": "373", + "AQLid": "373", + "EMCode": "10373", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "3", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "370", + "QDes": "/祝贺", + "IQLid": "370", + "AQLid": "370", + "EMCode": "10370", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "4", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "375", + "QDes": "/超级鼓掌", + "IQLid": "375", + "AQLid": "375", + "EMCode": "10375", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "5", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "368", + "QDes": "/奥特笑哭", + "IQLid": "368", + "AQLid": "368", + "EMCode": "10368", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "6", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "369", + "QDes": "/彩虹", + "IQLid": "369", + "AQLid": "369", + "EMCode": "10369", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "7", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "371", + "QDes": "/冒泡", + "IQLid": "371", + "AQLid": "371", + "EMCode": "10371", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "8", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "372", + "QDes": "/气呼呼", + "IQLid": "372", + "AQLid": "372", + "EMCode": "10372", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "9", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "374", + "QDes": "/波波流泪", + "IQLid": "374", + "AQLid": "374", + "EMCode": "10374", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "10", + "AniStickerPackId": "6", + "AniStickerPackName": "噗噗星人" + }, + { + "QSid": "382", + "QDes": "/emo", + "IQLid": "382", + "AQLid": "382", + "EMCode": "10382", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "1", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "383", + "QDes": "/企鹅爱心", + "IQLid": "383", + "AQLid": "383", + "EMCode": "10383", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "2", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "401", + "QDes": "/超级转圈", + "IQLid": "401", + "AQLid": "401", + "EMCode": "10401", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "3", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "400", + "QDes": "/快乐", + "IQLid": "400", + "AQLid": "400", + "EMCode": "10400", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "4", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "380", + "QDes": "/真棒", + "IQLid": "380", + "AQLid": "380", + "EMCode": "10380", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "5", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "381", + "QDes": "/路过", + "IQLid": "381", + "AQLid": "381", + "EMCode": "10381", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "6", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "379", + "QDes": "/企鹅流泪", + "IQLid": "379", + "AQLid": "379", + "EMCode": "10379", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "7", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "376", + "QDes": "/跺脚", + "IQLid": "376", + "AQLid": "376", + "EMCode": "10376", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "8", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "378", + "QDes": "/企鹅笑哭", + "IQLid": "378", + "AQLid": "378", + "EMCode": "10378", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "9", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "377", + "QDes": "/嗨", + "IQLid": "377", + "AQLid": "377", + "EMCode": "10377", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "10", + "AniStickerPackId": "5", + "AniStickerPackName": "企鹅" + }, + { + "QSid": "403", + "QDes": "/出去玩", + "IQLid": "403", + "AQLid": "403", + "EMCode": "10403", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "1", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "402", + "QDes": "/别说话", + "IQLid": "402", + "AQLid": "402", + "EMCode": "10402", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "2", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "390", + "QDes": "/太头秃", + "IQLid": "390", + "AQLid": "390", + "EMCode": "10390", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "3", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "391", + "QDes": "/太沧桑", + "IQLid": "391", + "AQLid": "391", + "EMCode": "10391", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "4", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "388", + "QDes": "/太头疼", + "IQLid": "388", + "AQLid": "388", + "EMCode": "10388", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "5", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "389", + "QDes": "/太赞了", + "IQLid": "389", + "AQLid": "389", + "EMCode": "10389", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "6", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "386", + "QDes": "/呜呜呜", + "IQLid": "386", + "AQLid": "386", + "EMCode": "10386", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "7", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "385", + "QDes": "/太气了", + "IQLid": "385", + "AQLid": "385", + "EMCode": "10385", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "8", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "384", + "QDes": "/晚安", + "IQLid": "384", + "AQLid": "384", + "EMCode": "10384", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "9", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "387", + "QDes": "/太好笑", + "IQLid": "387", + "AQLid": "387", + "EMCode": "10387", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "10", + "AniStickerPackId": "4", + "AniStickerPackName": "QQ黄脸" + }, + { + "QSid": "413", + "QDes": "/摇起来", + "IQLid": "413", + "AQLid": "413", + "EMCode": "10413", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "1", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "405", + "QDes": "/好运来", + "IQLid": "405", + "AQLid": "405", + "EMCode": "10405", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "2", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "404", + "QDes": "/闪亮登场", + "IQLid": "404", + "AQLid": "404", + "EMCode": "10404", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "3", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "406", + "QDes": "/姐是女王", + "IQLid": "406", + "AQLid": "406", + "EMCode": "10406", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "4", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "410", + "QDes": "/么么哒", + "IQLid": "410", + "AQLid": "410", + "EMCode": "10410", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "5", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "411", + "QDes": "/一起嗨", + "IQLid": "411", + "AQLid": "411", + "EMCode": "10411", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "6", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "407", + "QDes": "/我听听", + "IQLid": "407", + "AQLid": "407", + "EMCode": "10407", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "7", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "408", + "QDes": "/臭美", + "IQLid": "408", + "AQLid": "408", + "EMCode": "10408", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "8", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "412", + "QDes": "/开心", + "IQLid": "412", + "AQLid": "412", + "EMCode": "10412", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "9", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "409", + "QDes": "/送你花花", + "IQLid": "409", + "AQLid": "409", + "EMCode": "10409", + "QHide": "1", + "AniStickerType": 1, + "AniStickerId": "10", + "AniStickerPackId": "3", + "AniStickerPackName": "喜花妮" + }, + { + "QSid": "394", + "QDes": "/新年大龙", + "IQLid": "394", + "AQLid": "394", + "EMCode": "10394", + "QHide": "1", + "AniStickerType": 3, + "AniStickerPackId": "1", + "AniStickerId": "40" + }, + { + "QSid": "14", + "QDes": "/微笑", + "IQLid": "23", + "AQLid": "23", + "EMCode": "100" + }, + { + "QSid": "1", + "QDes": "/撇嘴", + "IQLid": "40", + "AQLid": "40", + "EMCode": "101" + }, + { + "QSid": "2", + "QDes": "/色", + "IQLid": "19", + "AQLid": "19", + "EMCode": "102" + }, + { + "QSid": "3", + "QDes": "/发呆", + "IQLid": "43", + "AQLid": "43", + "EMCode": "103" + }, + { + "QSid": "4", + "QDes": "/得意", + "IQLid": "21", + "AQLid": "21", + "EMCode": "104" + }, + { + "QSid": "6", + "QDes": "/害羞", + "IQLid": "20", + "AQLid": "20", + "EMCode": "106" + }, + { + "QSid": "7", + "QDes": "/闭嘴", + "IQLid": "104", + "AQLid": "106", + "EMCode": "107" + }, + { + "QSid": "8", + "QDes": "/睡", + "IQLid": "35", + "AQLid": "35", + "EMCode": "108" + }, + { + "QSid": "9", + "QDes": "/大哭", + "IQLid": "10", + "AQLid": "10", + "EMCode": "109" + }, + { + "QSid": "5", + "QDes": "/流泪", + "IQLid": "9", + "AQLid": "9", + "EMCode": "105", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "16" + }, + { + "QSid": "10", + "QDes": "/尴尬", + "IQLid": "25", + "AQLid": "25", + "EMCode": "110" + }, + { + "QSid": "11", + "QDes": "/发怒", + "IQLid": "24", + "AQLid": "24", + "EMCode": "111" + }, + { + "QSid": "12", + "QDes": "/调皮", + "IQLid": "1", + "AQLid": "1", + "EMCode": "112" + }, + { + "QSid": "13", + "QDes": "/呲牙", + "IQLid": "0", + "AQLid": "0", + "EMCode": "113" + }, + { + "QSid": "0", + "QDes": "/惊讶", + "IQLid": "33", + "AQLid": "33", + "EMCode": "114" + }, + { + "QSid": "15", + "QDes": "/难过", + "IQLid": "32", + "AQLid": "32", + "EMCode": "115" + }, + { + "QSid": "16", + "QDes": "/酷", + "IQLid": "12", + "AQLid": "12", + "EMCode": "116" + }, + { + "QSid": "96", + "QDes": "/冷汗", + "IQLid": "27", + "AQLid": "27", + "EMCode": "117" + }, + { + "QSid": "18", + "QDes": "/抓狂", + "IQLid": "13", + "AQLid": "13", + "EMCode": "118" + }, + { + "QSid": "19", + "QDes": "/吐", + "IQLid": "22", + "AQLid": "22", + "EMCode": "119" + }, + { + "QSid": "20", + "QDes": "/偷笑", + "IQLid": "3", + "AQLid": "3", + "EMCode": "120" + }, + { + "QSid": "21", + "QDes": "/可爱", + "IQLid": "18", + "AQLid": "18", + "EMCode": "121" + }, + { + "QSid": "22", + "QDes": "/白眼", + "IQLid": "30", + "AQLid": "30", + "EMCode": "122" + }, + { + "QSid": "23", + "QDes": "/傲慢", + "IQLid": "31", + "AQLid": "31", + "EMCode": "123" + }, + { + "QSid": "24", + "QDes": "/饥饿", + "IQLid": "79", + "AQLid": "81", + "EMCode": "124" + }, + { + "QSid": "25", + "QDes": "/困", + "IQLid": "80", + "AQLid": "82", + "EMCode": "125" + }, + { + "QSid": "26", + "QDes": "/惊恐", + "IQLid": "26", + "AQLid": "26", + "EMCode": "126" + }, + { + "QSid": "27", + "QDes": "/流汗", + "IQLid": "2", + "AQLid": "2", + "EMCode": "127" + }, + { + "QSid": "28", + "QDes": "/憨笑", + "IQLid": "37", + "AQLid": "37", + "EMCode": "128" + }, + { + "QSid": "29", + "QDes": "/悠闲", + "IQLid": "50", + "AQLid": "50", + "EMCode": "129" + }, + { + "QSid": "30", + "QDes": "/奋斗", + "IQLid": "42", + "AQLid": "42", + "EMCode": "130" + }, + { + "QSid": "31", + "QDes": "/咒骂", + "IQLid": "81", + "AQLid": "83", + "EMCode": "131" + }, + { + "QSid": "32", + "QDes": "/疑问", + "IQLid": "34", + "AQLid": "34", + "EMCode": "132" + }, + { + "QSid": "33", + "QDes": "/嘘", + "IQLid": "11", + "AQLid": "11", + "EMCode": "133" + }, + { + "QSid": "34", + "QDes": "/晕", + "IQLid": "49", + "AQLid": "49", + "EMCode": "134" + }, + { + "QSid": "35", + "QDes": "/折磨", + "IQLid": "82", + "AQLid": "84", + "EMCode": "135" + }, + { + "QSid": "36", + "QDes": "/衰", + "IQLid": "39", + "AQLid": "39", + "EMCode": "136" + }, + { + "QSid": "37", + "QDes": "/骷髅", + "isStatic": "1", + "IQLid": "76", + "AQLid": "78", + "EMCode": "137" + }, + { + "QSid": "38", + "QDes": "/敲打", + "IQLid": "5", + "AQLid": "5", + "EMCode": "138" + }, + { + "QSid": "39", + "QDes": "/再见", + "IQLid": "4", + "AQLid": "4", + "EMCode": "139" + }, + { + "QSid": "97", + "QDes": "/擦汗", + "IQLid": "6", + "AQLid": "6", + "EMCode": "140" + }, + { + "QSid": "98", + "QDes": "/抠鼻", + "IQLid": "83", + "AQLid": "85", + "EMCode": "141" + }, + { + "QSid": "99", + "QDes": "/鼓掌", + "IQLid": "84", + "AQLid": "86", + "EMCode": "142" + }, + { + "QSid": "100", + "QDes": "/糗大了", + "IQLid": "85", + "AQLid": "87", + "EMCode": "143" + }, + { + "QSid": "101", + "QDes": "/坏笑", + "IQLid": "46", + "AQLid": "46", + "EMCode": "144" + }, + { + "QSid": "102", + "QDes": "/左哼哼", + "IQLid": "86", + "AQLid": "88", + "EMCode": "145" + }, + { + "QSid": "103", + "QDes": "/右哼哼", + "IQLid": "44", + "AQLid": "44", + "EMCode": "146" + }, + { + "QSid": "104", + "QDes": "/哈欠", + "IQLid": "87", + "AQLid": "89", + "EMCode": "147" + }, + { + "QSid": "105", + "QDes": "/鄙视", + "IQLid": "48", + "AQLid": "48", + "EMCode": "148" + }, + { + "QSid": "106", + "QDes": "/委屈", + "IQLid": "14", + "AQLid": "14", + "EMCode": "149" + }, + { + "QSid": "107", + "QDes": "/快哭了", + "IQLid": "88", + "AQLid": "90", + "EMCode": "150" + }, + { + "QSid": "108", + "QDes": "/阴险", + "IQLid": "41", + "AQLid": "41", + "EMCode": "151" + }, + { + "QSid": "305", + "QDes": "/右亲亲", + "IQLid": "305", + "AQLid": "305", + "EMCode": "10305" + }, + { + "QSid": "109", + "QDes": "/左亲亲", + "IQLid": "36", + "AQLid": "36", + "EMCode": "152" + }, + { + "QSid": "110", + "QDes": "/吓", + "IQLid": "89", + "AQLid": "91", + "EMCode": "153" + }, + { + "QSid": "111", + "QDes": "/可怜", + "IQLid": "51", + "AQLid": "51", + "EMCode": "154" + }, + { + "QSid": "172", + "QDes": "/眨眼睛", + "IQLid": "142", + "AQLid": "164", + "EMCode": "242" + }, + { + "QSid": "182", + "QDes": "/笑哭", + "IQLid": "152", + "AQLid": "174", + "EMCode": "252" + }, + { + "QSid": "179", + "QDes": "/doge", + "IQLid": "149", + "AQLid": "171", + "EMCode": "249" + }, + { + "QSid": "173", + "QDes": "/泪奔", + "IQLid": "143", + "AQLid": "165", + "EMCode": "243" + }, + { + "QSid": "174", + "QDes": "/无奈", + "IQLid": "144", + "AQLid": "166", + "EMCode": "244" + }, + { + "QSid": "212", + "QDes": "/托腮", + "IQLid": "182", + "AQLid": "161", + "EMCode": "282" + }, + { + "QSid": "175", + "QDes": "/卖萌", + "IQLid": "145", + "AQLid": "167", + "EMCode": "245" + }, + { + "QSid": "178", + "QDes": "/斜眼笑", + "IQLid": "148", + "AQLid": "170", + "EMCode": "248" + }, + { + "QSid": "177", + "QDes": "/喷血", + "IQLid": "147", + "AQLid": "169", + "EMCode": "247" + }, + { + "QSid": "176", + "QDes": "/小纠结", + "IQLid": "146", + "AQLid": "168", + "EMCode": "246" + }, + { + "QSid": "183", + "QDes": "/我最美", + "IQLid": "153", + "AQLid": "175", + "EMCode": "253" + }, + { + "QSid": "262", + "QDes": "/脑阔疼", + "IQLid": "262", + "AQLid": "262", + "EMCode": "10262" + }, + { + "QSid": "263", + "QDes": "/沧桑", + "IQLid": "263", + "AQLid": "263", + "EMCode": "10263" + }, + { + "QSid": "264", + "QDes": "/捂脸", + "IQLid": "264", + "AQLid": "264", + "EMCode": "10264" + }, + { + "QSid": "265", + "QDes": "/辣眼睛", + "IQLid": "265", + "AQLid": "265", + "EMCode": "10265" + }, + { + "QSid": "266", + "QDes": "/哦哟", + "IQLid": "266", + "AQLid": "266", + "EMCode": "10266" + }, + { + "QSid": "267", + "QDes": "/头秃", + "IQLid": "267", + "AQLid": "267", + "EMCode": "10267" + }, + { + "QSid": "268", + "QDes": "/问号脸", + "IQLid": "268", + "AQLid": "268", + "EMCode": "10268" + }, + { + "QSid": "269", + "QDes": "/暗中观察", + "IQLid": "269", + "AQLid": "269", + "EMCode": "10269" + }, + { + "QSid": "270", + "QDes": "/emm", + "IQLid": "270", + "AQLid": "270", + "EMCode": "10270" + }, + { + "QSid": "271", + "QDes": "/吃瓜", + "IQLid": "271", + "AQLid": "271", + "EMCode": "10271" + }, + { + "QSid": "272", + "QDes": "/呵呵哒", + "IQLid": "272", + "AQLid": "272", + "EMCode": "10272" + }, + { + "QSid": "277", + "QDes": "/汪汪", + "IQLid": "277", + "AQLid": "277", + "EMCode": "10277" + }, + { + "QSid": "307", + "QDes": "/喵喵", + "IQLid": "307", + "AQLid": "307", + "EMCode": "10307" + }, + { + "QSid": "306", + "QDes": "/牛气冲天", + "isStatic": "1", + "IQLid": "306", + "AQLid": "306", + "EMCode": "10306" + }, + { + "QSid": "281", + "QDes": "/无眼笑", + "IQLid": "281", + "AQLid": "281", + "EMCode": "10281" + }, + { + "QSid": "282", + "QDes": "/敬礼", + "IQLid": "282", + "AQLid": "282", + "EMCode": "10282" + }, + { + "QSid": "283", + "QDes": "/狂笑", + "IQLid": "283", + "AQLid": "283", + "EMCode": "10283" + }, + { + "QSid": "284", + "QDes": "/面无表情", + "IQLid": "284", + "AQLid": "284", + "EMCode": "10284" + }, + { + "QSid": "285", + "QDes": "/摸鱼", + "IQLid": "285", + "AQLid": "285", + "EMCode": "10285" + }, + { + "QSid": "293", + "QDes": "/摸锦鲤", + "IQLid": "293", + "AQLid": "293", + "EMCode": "10293" + }, + { + "QSid": "286", + "QDes": "/魔鬼笑", + "IQLid": "286", + "AQLid": "286", + "EMCode": "10286" + }, + { + "QSid": "287", + "QDes": "/哦", + "IQLid": "287", + "AQLid": "287", + "EMCode": "10287" + }, + { + "QSid": "289", + "QDes": "/睁眼", + "IQLid": "289", + "AQLid": "289", + "EMCode": "10289" + }, + { + "QSid": "294", + "QDes": "/期待", + "IQLid": "294", + "AQLid": "294", + "EMCode": "10294" + }, + { + "QSid": "297", + "QDes": "/拜谢", + "IQLid": "297", + "AQLid": "297", + "EMCode": "10297" + }, + { + "QSid": "298", + "QDes": "/元宝", + "IQLid": "298", + "AQLid": "298", + "EMCode": "10298" + }, + { + "QSid": "299", + "QDes": "/牛啊", + "IQLid": "299", + "AQLid": "299", + "EMCode": "10299" + }, + { + "QSid": "300", + "QDes": "/胖三斤", + "IQLid": "300", + "AQLid": "300", + "EMCode": "10300" + }, + { + "QSid": "323", + "QDes": "/嫌弃", + "IQLid": "323", + "AQLid": "323", + "EMCode": "10323" + }, + { + "QSid": "332", + "QDes": "/举牌牌", + "IQLid": "332", + "AQLid": "332", + "EMCode": "10332" + }, + { + "QSid": "336", + "QDes": "/豹富", + "IQLid": "336", + "AQLid": "336", + "EMCode": "10336" + }, + { + "QSid": "353", + "QDes": "/拜托", + "IQLid": "353", + "AQLid": "353", + "EMCode": "10353" + }, + { + "QSid": "355", + "QDes": "/耶", + "IQLid": "355", + "AQLid": "355", + "EMCode": "10355" + }, + { + "QSid": "356", + "QDes": "/666", + "IQLid": "356", + "AQLid": "356", + "EMCode": "10356" + }, + { + "QSid": "354", + "QDes": "/尊嘟假嘟", + "IQLid": "354", + "AQLid": "354", + "EMCode": "10354" + }, + { + "QSid": "352", + "QDes": "/咦", + "IQLid": "352", + "AQLid": "352", + "EMCode": "10352" + }, + { + "QSid": "357", + "QDes": "/裂开", + "IQLid": "357", + "AQLid": "357", + "EMCode": "10357" + }, + { + "QSid": "334", + "QDes": "/虎虎生威", + "IQLid": "334", + "AQLid": "334", + "EMCode": "10334" + }, + { + "QSid": "347", + "QDes": "/大展宏兔", + "IQLid": "347", + "AQLid": "347", + "EMCode": "10347" + }, + { + "QSid": "303", + "QDes": "/右拜年", + "IQLid": "303", + "AQLid": "303", + "EMCode": "10303" + }, + { + "QSid": "302", + "QDes": "/左拜年", + "IQLid": "302", + "AQLid": "302", + "EMCode": "10302" + }, + { + "QSid": "295", + "QDes": "/拿到红包", + "IQLid": "295", + "AQLid": "295", + "EMCode": "10295" + }, + { + "QSid": "311", + "QDes": "/打call", + "IQLid": "311", + "AQLid": "311", + "EMCode": "10311", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "1" + }, + { + "QSid": "312", + "QDes": "/变形", + "IQLid": "312", + "AQLid": "312", + "EMCode": "10312", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "2" + }, + { + "QSid": "314", + "QDes": "/仔细分析", + "IQLid": "314", + "AQLid": "314", + "EMCode": "10314", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "4" + }, + { + "QSid": "317", + "QDes": "/菜汪", + "IQLid": "317", + "AQLid": "317", + "EMCode": "10317", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "7" + }, + { + "QSid": "318", + "QDes": "/崇拜", + "IQLid": "318", + "AQLid": "318", + "EMCode": "10318", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "8" + }, + { + "QSid": "319", + "QDes": "/比心", + "IQLid": "319", + "AQLid": "319", + "EMCode": "10319", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "9" + }, + { + "QSid": "320", + "QDes": "/庆祝", + "IQLid": "320", + "AQLid": "320", + "EMCode": "10320", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "10" + }, + { + "QSid": "324", + "QDes": "/吃糖", + "IQLid": "324", + "AQLid": "324", + "EMCode": "10324", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "12" + }, + { + "QSid": "325", + "QDes": "/惊吓", + "IQLid": "325", + "AQLid": "325", + "EMCode": "10325", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "14" + }, + { + "QSid": "337", + "QDes": "/花朵脸", + "IQLid": "337", + "AQLid": "337", + "EMCode": "10337", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "22" + }, + { + "QSid": "338", + "QDes": "/我想开了", + "IQLid": "338", + "AQLid": "338", + "EMCode": "10338", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "20" + }, + { + "QSid": "339", + "QDes": "/舔屏", + "IQLid": "339", + "AQLid": "339", + "EMCode": "10339", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "21" + }, + { + "QSid": "341", + "QDes": "/打招呼", + "IQLid": "341", + "AQLid": "341", + "EMCode": "10341", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "24" + }, + { + "QSid": "342", + "QDes": "/酸Q", + "IQLid": "342", + "AQLid": "342", + "EMCode": "10342", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "26" + }, + { + "QSid": "343", + "QDes": "/我方了", + "IQLid": "343", + "AQLid": "343", + "EMCode": "10343", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "27" + }, + { + "QSid": "344", + "QDes": "/大怨种", + "IQLid": "344", + "AQLid": "344", + "EMCode": "10344", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "28" + }, + { + "QSid": "345", + "QDes": "/红包多多", + "IQLid": "345", + "AQLid": "345", + "EMCode": "10345", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "29" + }, + { + "QSid": "346", + "QDes": "/你真棒棒", + "IQLid": "346", + "AQLid": "346", + "EMCode": "10346", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "25" + }, + { + "QSid": "181", + "QDes": "/戳一戳", + "IQLid": "151", + "AQLid": "173", + "EMCode": "251", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "37" + }, + { + "QSid": "74", + "QDes": "/太阳", + "isStatic": "1", + "IQLid": "73", + "AQLid": "75", + "EMCode": "176", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "35" + }, + { + "QSid": "75", + "QDes": "/月亮", + "isStatic": "1", + "IQLid": "67", + "AQLid": "68", + "EMCode": "175", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "36" + }, + { + "QSid": "351", + "QDes": "/敲敲", + "IQLid": "351", + "AQLid": "351", + "EMCode": "10351", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "30" + }, + { + "QSid": "349", + "QDes": "/坚强", + "IQLid": "349", + "AQLid": "349", + "EMCode": "10349", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "32" + }, + { + "QSid": "350", + "QDes": "/贴贴", + "IQLid": "350", + "AQLid": "350", + "EMCode": "10350", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "31" + }, + { + "QSid": "395", + "QDes": "/略略略", + "IQLid": "395", + "AQLid": "395", + "EMCode": "10395", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "41" + }, + { + "QSid": "114", + "QDes": "/篮球", + "IQLid": "90", + "AQLid": "92", + "EMCode": "158", + "AniStickerType": 2, + "AniStickerPackId": "1", + "AniStickerId": "13" + }, + { + "QSid": "358", + "QDes": "/骰子", + "IQLid": "358", + "AQLid": "358", + "QHide": "1", + "EMCode": "10358", + "AniStickerType": 2, + "AniStickerPackId": "1", + "AniStickerId": "33" + }, + { + "QSid": "359", + "QDes": "/包剪锤", + "IQLid": "359", + "AQLid": "359", + "QHide": "1", + "EMCode": "10359", + "AniStickerType": 2, + "AniStickerPackId": "1", + "AniStickerId": "34" + }, + { + "QSid": "326", + "QDes": "/生气", + "IQLid": "326", + "AQLid": "326", + "EMCode": "10326", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "15" + }, + { + "QSid": "53", + "QDes": "/蛋糕", + "IQLid": "59", + "AQLid": "59", + "EMCode": "168", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "17" + }, + { + "QSid": "49", + "QDes": "/拥抱", + "IQLid": "45", + "AQLid": "45", + "EMCode": "178" + }, + { + "QSid": "66", + "QDes": "/爱心", + "IQLid": "28", + "AQLid": "28", + "EMCode": "166" + }, + { + "QSid": "63", + "QDes": "/玫瑰", + "IQLid": "8", + "AQLid": "8", + "EMCode": "163" + }, + { + "QSid": "64", + "QDes": "/凋谢", + "IQLid": "57", + "AQLid": "57", + "EMCode": "164" + }, + { + "QSid": "187", + "QDes": "/幽灵", + "IQLid": "157", + "AQLid": "179", + "EMCode": "257" + }, + { + "QSid": "146", + "QDes": "/爆筋", + "IQLid": "116", + "AQLid": "118", + "EMCode": "121011" + }, + { + "QSid": "116", + "QDes": "/示爱", + "IQLid": "29", + "AQLid": "29", + "EMCode": "165" + }, + { + "QSid": "67", + "QDes": "/心碎", + "IQLid": "72", + "AQLid": "74", + "EMCode": "167" + }, + { + "QSid": "60", + "QDes": "/咖啡", + "IQLid": "66", + "AQLid": "66", + "EMCode": "160" + }, + { + "QSid": "185", + "QDes": "/羊驼", + "IQLid": "155", + "AQLid": "177", + "EMCode": "255" + }, + { + "QSid": "137", + "QDes": "/鞭炮", + "isStatic": "1", + "IQLid": "107", + "AQLid": "109", + "EMCode": "121002", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "18" + }, + { + "QSid": "333", + "QDes": "/烟花", + "isStatic": "1", + "IQLid": "333", + "AQLid": "333", + "EMCode": "10333", + "AniStickerType": 1, + "AniStickerPackId": "1", + "AniStickerId": "19" + }, + { + "QSid": "76", + "QDes": "/赞", + "IQLid": "52", + "AQLid": "52", + "EMCode": "179" + }, + { + "QSid": "124", + "QDes": "/OK", + "IQLid": "64", + "AQLid": "64", + "EMCode": "189" + }, + { + "QSid": "118", + "QDes": "/抱拳", + "IQLid": "56", + "AQLid": "56", + "EMCode": "183" + }, + { + "QSid": "78", + "QDes": "/握手", + "IQLid": "54", + "AQLid": "54", + "EMCode": "181" + }, + { + "QSid": "119", + "QDes": "/勾引", + "IQLid": "63", + "AQLid": "63", + "EMCode": "184" + }, + { + "QSid": "79", + "QDes": "/胜利", + "IQLid": "55", + "AQLid": "55", + "EMCode": "182" + }, + { + "QSid": "120", + "QDes": "/拳头", + "IQLid": "71", + "AQLid": "73", + "EMCode": "185" + }, + { + "QSid": "121", + "QDes": "/差劲", + "IQLid": "70", + "AQLid": "72", + "EMCode": "186" + }, + { + "QSid": "77", + "QDes": "/踩", + "IQLid": "53", + "AQLid": "53", + "EMCode": "180" + }, + { + "QSid": "123", + "QDes": "/NO", + "IQLid": "92", + "AQLid": "94", + "EMCode": "188" + }, + { + "QSid": "201", + "QDes": "/点赞", + "IQLid": "171", + "AQLid": "150", + "EMCode": "271" + }, + { + "QSid": "273", + "QDes": "/我酸了", + "isStatic": "1", + "IQLid": "273", + "AQLid": "273", + "EMCode": "10273" + }, + { + "QSid": "46", + "QDes": "/猪头", + "isStatic": "1", + "IQLid": "7", + "AQLid": "7", + "EMCode": "162" + }, + { + "QSid": "112", + "QDes": "/菜刀", + "IQLid": "17", + "AQLid": "17", + "EMCode": "155" + }, + { + "QSid": "56", + "QDes": "/刀", + "IQLid": "68", + "AQLid": "70", + "EMCode": "171" + }, + { + "QSid": "169", + "QDes": "/手枪", + "isStatic": "1", + "IQLid": "139", + "AQLid": "141", + "EMCode": "121034" + }, + { + "QSid": "171", + "QDes": "/茶", + "IQLid": "141", + "AQLid": "163", + "EMCode": "241" + }, + { + "QSid": "59", + "QDes": "/便便", + "IQLid": "15", + "AQLid": "15", + "EMCode": "174" + }, + { + "QSid": "144", + "QDes": "/喝彩", + "isStatic": "1", + "IQLid": "114", + "AQLid": "116", + "EMCode": "121009" + }, + { + "QSid": "147", + "QDes": "/棒棒糖", + "isStatic": "1", + "IQLid": "117", + "AQLid": "119", + "EMCode": "121012" + }, + { + "QSid": "89", + "QDes": "/西瓜", + "isStatic": "1", + "IQLid": "60", + "AQLid": "60", + "EMCode": "156" + }, + { + "QSid": "148", + "QDes": "/喝奶", + "isStatic": "1", + "IQLid": "118", + "AQLid": "120", + "QHide": "1", + "EMCode": "121013" + }, + { + "QSid": "55", + "QDes": "/炸弹", + "isStatic": "1", + "IQLid": "16", + "AQLid": "16", + "QHide": "1", + "EMCode": "170" + }, + { + "QSid": "41", + "QDes": "/发抖", + "isStatic": "1", + "IQLid": "69", + "AQLid": "71", + "EMCode": "193" + }, + { + "QSid": "125", + "QDes": "/转圈", + "IQLid": "95", + "AQLid": "97", + "EMCode": "195" + }, + { + "QSid": "42", + "QDes": "/爱情", + "IQLid": "38", + "AQLid": "38", + "EMCode": "190" + }, + { + "QSid": "43", + "QDes": "/跳跳", + "IQLid": "93", + "AQLid": "95", + "EMCode": "192" + }, + { + "QSid": "86", + "QDes": "/怄火", + "IQLid": "94", + "AQLid": "96", + "EMCode": "194" + }, + { + "QSid": "129", + "QDes": "/挥手", + "IQLid": "77", + "AQLid": "79", + "EMCode": "199" + }, + { + "QSid": "226", + "QDes": "/拍桌", + "IQLid": "196", + "isCMEmoji": "1", + "AQLid": "198", + "QHide": "1", + "EMCode": "297" + }, + { + "QSid": "85", + "QDes": "/飞吻", + "isStatic": "1", + "IQLid": "47", + "AQLid": "47", + "EMCode": "191" + }, + { + "QSid": "215", + "QDes": "/糊脸", + "IQLid": "185", + "isCMEmoji": "1", + "AQLid": "187", + "QHide": "1", + "EMCode": "285" + }, + { + "QSid": "214", + "QDes": "/啵啵", + "IQLid": "184", + "isCMEmoji": "1", + "AQLid": "186", + "QHide": "1", + "EMCode": "284" + }, + { + "QSid": "222", + "QDes": "/抱抱", + "IQLid": "192", + "isCMEmoji": "1", + "AQLid": "194", + "QHide": "1", + "EMCode": "292" + }, + { + "QSid": "203", + "QDes": "/托脸", + "IQLid": "173", + "AQLid": "152", + "QHide": "1", + "EMCode": "273" + }, + { + "QSid": "235", + "QDes": "/颤抖", + "IQLid": "205", + "isCMEmoji": "1", + "AQLid": "207", + "QHide": "1", + "EMCode": "305" + }, + { + "QSid": "241", + "QDes": "/生日快乐", + "IQLid": "211", + "isCMEmoji": "1", + "AQLid": "213", + "QHide": "1", + "EMCode": "311" + }, + { + "QSid": "237", + "QDes": "/偷看", + "IQLid": "207", + "isCMEmoji": "1", + "AQLid": "209", + "QHide": "1", + "EMCode": "307" + }, + { + "QSid": "218", + "QDes": "/舔一舔", + "IQLid": "188", + "isCMEmoji": "1", + "AQLid": "190", + "QHide": "1", + "EMCode": "288" + }, + { + "QSid": "233", + "QDes": "/掐一掐", + "IQLid": "203", + "isCMEmoji": "1", + "AQLid": "205", + "QHide": "1", + "EMCode": "303" + }, + { + "QSid": "232", + "QDes": "/佛系", + "IQLid": "202", + "isCMEmoji": "1", + "AQLid": "204", + "QHide": "1", + "EMCode": "302" + }, + { + "QSid": "238", + "QDes": "/扇脸", + "IQLid": "208", + "isCMEmoji": "1", + "AQLid": "210", + "QHide": "1", + "EMCode": "308" + }, + { + "QSid": "217", + "QDes": "/扯一扯", + "IQLid": "187", + "isCMEmoji": "1", + "AQLid": "189", + "QHide": "1", + "EMCode": "287" + }, + { + "QSid": "225", + "QDes": "/撩一撩", + "IQLid": "195", + "isCMEmoji": "1", + "AQLid": "197", + "QHide": "1", + "EMCode": "296" + }, + { + "QSid": "230", + "QDes": "/嘲讽", + "IQLid": "200", + "isCMEmoji": "1", + "AQLid": "202", + "QHide": "1", + "EMCode": "300" + }, + { + "QSid": "194", + "QDes": "/不开心", + "IQLid": "164", + "AQLid": "143", + "QHide": "1", + "EMCode": "264" + }, + { + "QSid": "210", + "QDes": "/飙泪", + "IQLid": "180", + "AQLid": "159", + "QHide": "1", + "EMCode": "280" + }, + { + "QSid": "193", + "QDes": "/大笑", + "IQLid": "163", + "AQLid": "185", + "QHide": "1", + "EMCode": "263" + }, + { + "QSid": "204", + "QDes": "/吃", + "IQLid": "174", + "AQLid": "153", + "QHide": "1", + "EMCode": "274" + }, + { + "QSid": "200", + "QDes": "/求求", + "IQLid": "170", + "AQLid": "149", + "QHide": "1", + "EMCode": "270" + }, + { + "QSid": "290", + "QDes": "/敲开心", + "IQLid": "290", + "isCMEmoji": "1", + "AQLid": "290", + "QHide": "1", + "EMCode": "20240" + }, + { + "QSid": "224", + "QDes": "/开枪", + "IQLid": "194", + "isCMEmoji": "1", + "AQLid": "196", + "QHide": "1", + "EMCode": "295" + }, + { + "QSid": "229", + "QDes": "/干杯", + "IQLid": "199", + "isCMEmoji": "1", + "AQLid": "201", + "QHide": "1", + "EMCode": "299" + }, + { + "QSid": "221", + "QDes": "/顶呱呱", + "IQLid": "191", + "isCMEmoji": "1", + "AQLid": "193", + "QHide": "1", + "EMCode": "291" + }, + { + "QSid": "219", + "QDes": "/蹭一蹭", + "IQLid": "189", + "isCMEmoji": "1", + "AQLid": "191", + "QHide": "1", + "EMCode": "289" + }, + { + "QSid": "227", + "QDes": "/拍手", + "IQLid": "197", + "isCMEmoji": "1", + "AQLid": "199", + "QHide": "1", + "EMCode": "294" + }, + { + "QSid": "216", + "QDes": "/拍头", + "IQLid": "186", + "isCMEmoji": "1", + "AQLid": "188", + "QHide": "1", + "EMCode": "286" + }, + { + "QSid": "231", + "QDes": "/哼", + "IQLid": "201", + "isCMEmoji": "1", + "AQLid": "203", + "QHide": "1", + "EMCode": "301" + }, + { + "QSid": "244", + "QDes": "/扔狗", + "IQLid": "214", + "isCMEmoji": "1", + "AQLid": "216", + "QHide": "1", + "EMCode": "312" + }, + { + "QSid": "223", + "QDes": "/暴击", + "IQLid": "193", + "isCMEmoji": "1", + "AQLid": "195", + "QHide": "1", + "EMCode": "293" + }, + { + "QSid": "243", + "QDes": "/甩头", + "IQLid": "213", + "isCMEmoji": "1", + "AQLid": "215", + "QHide": "1", + "EMCode": "313" + }, + { + "QSid": "211", + "QDes": "/我不看", + "IQLid": "181", + "AQLid": "160", + "QHide": "1", + "EMCode": "281" + }, + { + "QSid": "292", + "QDes": "/让我康康", + "IQLid": "292", + "isCMEmoji": "1", + "AQLid": "292", + "QHide": "1", + "EMCode": "20242" + }, + { + "QSid": "240", + "QDes": "/喷脸", + "IQLid": "210", + "isCMEmoji": "1", + "AQLid": "212", + "QHide": "1", + "EMCode": "310" + }, + { + "QSid": "180", + "QDes": "/惊喜", + "IQLid": "150", + "AQLid": "172", + "QHide": "1", + "EMCode": "250" + }, + { + "QSid": "122", + "QDes": "/爱你", + "IQLid": "65", + "AQLid": "65", + "QHide": "1", + "EMCode": "187" + }, + { + "QSid": "202", + "QDes": "/无聊", + "IQLid": "172", + "AQLid": "151", + "QHide": "1", + "EMCode": "272" + }, + { + "QSid": "278", + "QDes": "/汗", + "IQLid": "278", + "isCMEmoji": "1", + "AQLid": "278", + "QHide": "1", + "EMCode": "20237" + }, + { + "QSid": "301", + "QDes": "/好闪", + "IQLid": "301", + "AQLid": "301", + "QHide": "1", + "EMCode": "10301" + }, + { + "QSid": "288", + "QDes": "/请", + "IQLid": "288", + "AQLid": "288", + "QHide": "1", + "EMCode": "10288" + }, + { + "QSid": "322", + "QDes": "/拒绝", + "IQLid": "322", + "AQLid": "322", + "QHide": "1", + "EMCode": "10322" + }, + { + "QSid": "198", + "QDes": "/呃", + "IQLid": "168", + "AQLid": "147", + "QHide": "1", + "EMCode": "268" + }, + { + "QSid": "348", + "QDes": "/福萝卜", + "IQLid": "348", + "AQLid": "348", + "QHide": "1", + "EMCode": "10348" + }, + { + "QSid": "206", + "QDes": "/害怕", + "IQLid": "176", + "AQLid": "155", + "QHide": "1", + "EMCode": "276" + }, + { + "QSid": "239", + "QDes": "/原谅", + "IQLid": "209", + "isCMEmoji": "1", + "AQLid": "211", + "QHide": "1", + "EMCode": "309" + } + ], + "emoji": [ + { + "QSid": "😊", + "QCid": "128522", + "AQLid": "0", + "QDes": "/嘿嘿", + "EMCode": "400832" + }, + { + "QSid": "😌", + "QCid": "128524", + "AQLid": "1", + "QDes": "/羞涩", + "EMCode": "400834" + }, + { + "QSid": "😚", + "QCid": "128538", + "AQLid": "2", + "QDes": "/亲亲", + "EMCode": "400848" + }, + { + "QSid": "😓", + "QCid": "128531", + "AQLid": "3", + "QDes": "/汗", + "EMCode": "400841" + }, + { + "QSid": "😰", + "QCid": "128560", + "AQLid": "4", + "QDes": "/紧张", + "EMCode": "400870" + }, + { + "QSid": "😝", + "QCid": "128541", + "AQLid": "5", + "QDes": "/吐舌", + "EMCode": "400851" + }, + { + "QSid": "😁", + "QCid": "128513", + "AQLid": "6", + "QDes": "/呲牙", + "EMCode": "400823" + }, + { + "QSid": "😜", + "QCid": "128540", + "AQLid": "7", + "QDes": "/淘气", + "EMCode": "400850" + }, + { + "QSid": "☺", + "QCid": "9786", + "AQLid": "8", + "QDes": "/可爱", + "EMCode": "401181" + }, + { + "QSid": "😉", + "QCid": "128521", + "AQLid": "9", + "QDes": "/媚眼", + "QHide": "1", + "EMCode": "400831" + }, + { + "QSid": "😍", + "QCid": "128525", + "AQLid": "10", + "QDes": "/花痴", + "EMCode": "400835" + }, + { + "QSid": "😔", + "QCid": "128532", + "AQLid": "11", + "QDes": "/失落", + "EMCode": "400842" + }, + { + "QSid": "😄", + "QCid": "128516", + "AQLid": "12", + "QDes": "/高兴", + "EMCode": "400826" + }, + { + "QSid": "😏", + "QCid": "128527", + "AQLid": "13", + "QDes": "/哼哼", + "EMCode": "400837" + }, + { + "QSid": "😒", + "QCid": "128530", + "AQLid": "14", + "QDes": "/不屑", + "EMCode": "400840" + }, + { + "QSid": "😳", + "QCid": "128563", + "AQLid": "15", + "QDes": "/瞪眼", + "EMCode": "400873" + }, + { + "QSid": "😘", + "QCid": "128536", + "AQLid": "16", + "QDes": "/飞吻", + "EMCode": "400846" + }, + { + "QSid": "😭", + "QCid": "128557", + "AQLid": "17", + "QDes": "/大哭", + "EMCode": "400867" + }, + { + "QSid": "😱", + "QCid": "128561", + "AQLid": "18", + "QDes": "/害怕", + "EMCode": "400871" + }, + { + "QSid": "😂", + "QCid": "128514", + "AQLid": "19", + "QDes": "/激动", + "EMCode": "400824" + }, + { + "QSid": "💪", + "QCid": "128170", + "AQLid": "20", + "QDes": "/肌肉", + "EMCode": "400644" + }, + { + "QSid": "👊", + "QCid": "128074", + "AQLid": "21", + "QDes": "/拳头", + "EMCode": "400390" + }, + { + "QSid": "👍", + "QCid": "128077", + "AQLid": "22", + "QDes": "/厉害", + "EMCode": "400408" + }, + { + "QSid": "☝", + "QCid": "9757", + "AQLid": "23", + "QDes": "/向上", + "QHide": "1", + "EMCode": "401203" + }, + { + "QSid": "👏", + "QCid": "128079", + "AQLid": "24", + "QDes": "/鼓掌", + "EMCode": "400420" + }, + { + "QSid": "✌", + "QCid": "9996", + "AQLid": "25", + "QDes": "/胜利", + "QHide": "1", + "EMCode": "401210" + }, + { + "QSid": "👎", + "QCid": "128078", + "AQLid": "26", + "QDes": "/鄙视", + "EMCode": "400414" + }, + { + "QSid": "🙏", + "QCid": "128591", + "AQLid": "27", + "QDes": "/合十", + "EMCode": "400396" + }, + { + "QSid": "👌", + "QCid": "128076", + "AQLid": "28", + "QDes": "/好的", + "EMCode": "400402" + }, + { + "QSid": "👈", + "QCid": "128072", + "AQLid": "29", + "QDes": "/向左", + "QHide": "1", + "EMCode": "400378" + }, + { + "QSid": "👉", + "QCid": "128073", + "AQLid": "30", + "QDes": "/向右", + "QHide": "1", + "EMCode": "400384" + }, + { + "QSid": "👆", + "QCid": "128070", + "AQLid": "31", + "QDes": "/向上", + "EMCode": "400366" + }, + { + "QSid": "👇", + "QCid": "128071", + "AQLid": "32", + "QDes": "/向下", + "QHide": "1", + "EMCode": "400372" + }, + { + "QSid": "👀", + "QCid": "128064", + "AQLid": "33", + "QDes": "/眼睛", + "EMCode": "400351" + }, + { + "QSid": "👃", + "QCid": "128067", + "AQLid": "34", + "QDes": "/鼻子", + "QHide": "1", + "EMCode": "400358" + }, + { + "QSid": "👄", + "QCid": "128068", + "AQLid": "35", + "QDes": "/嘴唇", + "QHide": "1", + "EMCode": "400364" + }, + { + "QSid": "👂", + "QCid": "128066", + "AQLid": "36", + "QDes": "/耳朵", + "QHide": "1", + "EMCode": "400352" + }, + { + "QSid": "🍚", + "QCid": "127834", + "AQLid": "37", + "QDes": "/米饭", + "QHide": "1", + "EMCode": "400149" + }, + { + "QSid": "🍝", + "QCid": "127837", + "AQLid": "38", + "QDes": "/意面", + "QHide": "1", + "EMCode": "400152" + }, + { + "QSid": "🍜", + "QCid": "127836", + "AQLid": "39", + "QDes": "/拉面", + "EMCode": "400151" + }, + { + "QSid": "🍙", + "QCid": "127833", + "AQLid": "40", + "QDes": "/饭团", + "QHide": "1", + "EMCode": "400148" + }, + { + "QSid": "🍧", + "QCid": "127847", + "AQLid": "41", + "QDes": "/刨冰", + "EMCode": "400162" + }, + { + "QSid": "🍣", + "QCid": "127843", + "AQLid": "42", + "QDes": "/寿司", + "QHide": "1", + "EMCode": "400158" + }, + { + "QSid": "🎂", + "QCid": "127874", + "AQLid": "43", + "QDes": "/蛋糕", + "QHide": "1", + "EMCode": "400186" + }, + { + "QSid": "🍞", + "QCid": "127838", + "AQLid": "44", + "QDes": "/面包", + "EMCode": "400153" + }, + { + "QSid": "🍔", + "QCid": "127828", + "AQLid": "45", + "QDes": "/汉堡", + "QHide": "1", + "EMCode": "400143" + }, + { + "QSid": "🍳", + "QCid": "127859", + "AQLid": "46", + "QDes": "/煎蛋", + "QHide": "1", + "EMCode": "400174" + }, + { + "QSid": "🍟", + "QCid": "127839", + "AQLid": "47", + "QDes": "/薯条", + "QHide": "1", + "EMCode": "400154" + }, + { + "QSid": "🍺", + "QCid": "127866", + "AQLid": "48", + "QDes": "/啤酒", + "EMCode": "400181" + }, + { + "QSid": "🍻", + "QCid": "127867", + "AQLid": "49", + "QDes": "/干杯", + "EMCode": "400182" + }, + { + "QSid": "🍸", + "QCid": "127864", + "AQLid": "50", + "QDes": "/高脚杯", + "QHide": "1", + "EMCode": "400179" + }, + { + "QSid": "☕", + "QCid": "9749", + "AQLid": "51", + "QDes": "/咖啡", + "EMCode": "401262" + }, + { + "QSid": "🍎", + "QCid": "127822", + "AQLid": "52", + "QDes": "/苹果", + "EMCode": "400137" + }, + { + "QSid": "🍊", + "QCid": "127818", + "AQLid": "53", + "QDes": "/橙子", + "QHide": "1", + "EMCode": "400133" + }, + { + "QSid": "🍓", + "QCid": "127827", + "AQLid": "54", + "QDes": "/草莓", + "EMCode": "400142" + }, + { + "QSid": "🍉", + "QCid": "127817", + "AQLid": "55", + "QDes": "/西瓜", + "EMCode": "400132" + }, + { + "QSid": "💊", + "QCid": "128138", + "AQLid": "56", + "QDes": "/药丸", + "QHide": "1", + "EMCode": "400612" + }, + { + "QSid": "🚬", + "QCid": "128684", + "AQLid": "57", + "QDes": "/吸烟", + "EMCode": "400987" + }, + { + "QSid": "🎄", + "QCid": "127876", + "AQLid": "58", + "QDes": "/圣诞树", + "QHide": "1", + "EMCode": "400188" + }, + { + "QSid": "🌹", + "QCid": "127801", + "AQLid": "59", + "QDes": "/玫瑰", + "EMCode": "400116" + }, + { + "QSid": "🎉", + "QCid": "127881", + "AQLid": "60", + "QDes": "/庆祝", + "EMCode": "400198" + }, + { + "QSid": "🌴", + "QCid": "127796", + "AQLid": "61", + "QDes": "/椰子树", + "QHide": "1", + "EMCode": "400112" + }, + { + "QSid": "💝", + "QCid": "128157", + "AQLid": "62", + "QDes": "/礼物", + "EMCode": "400631" + }, + { + "QSid": "🎀", + "QCid": "127872", + "AQLid": "63", + "QDes": "/蝴蝶结", + "QHide": "1", + "EMCode": "400184" + }, + { + "QSid": "🎈", + "QCid": "127880", + "AQLid": "64", + "QDes": "/气球", + "QHide": "1", + "EMCode": "400197" + }, + { + "QSid": "🐚", + "QCid": "128026", + "AQLid": "65", + "QDes": "/海螺", + "QHide": "1", + "EMCode": "400314" + }, + { + "QSid": "💍", + "QCid": "128141", + "AQLid": "66", + "QDes": "/戒指", + "QHide": "1", + "EMCode": "400615" + }, + { + "QSid": "💣", + "QCid": "128163", + "AQLid": "67", + "QDes": "/炸弹", + "EMCode": "400637" + }, + { + "QSid": "👑", + "QCid": "128081", + "AQLid": "68", + "QDes": "/皇冠", + "QHide": "1", + "EMCode": "400432" + }, + { + "QSid": "🔔", + "QCid": "128276", + "AQLid": "69", + "QDes": "/铃铛", + "QHide": "1", + "EMCode": "400751" + }, + { + "QSid": "⭐", + "QCid": "11088", + "AQLid": "70", + "QDes": "/星星", + "QHide": "1", + "EMCode": "401686" + }, + { + "QSid": "✨", + "QCid": "10024", + "AQLid": "71", + "QDes": "/闪光", + "EMCode": "401137" + }, + { + "QSid": "💨", + "QCid": "128168", + "AQLid": "72", + "QDes": "/吹气", + "EMCode": "400642" + }, + { + "QSid": "💦", + "QCid": "128166", + "AQLid": "73", + "QDes": "/水", + "EMCode": "400640" + }, + { + "QSid": "🔥", + "QCid": "128293", + "AQLid": "74", + "QDes": "/火", + "EMCode": "400768" + }, + { + "QSid": "🏆", + "QCid": "127942", + "AQLid": "75", + "QDes": "/奖杯", + "QHide": "1", + "EMCode": "400256" + }, + { + "QSid": "💰", + "QCid": "128176", + "AQLid": "76", + "QDes": "/钱", + "QHide": "1", + "EMCode": "400655" + }, + { + "QSid": "💤", + "QCid": "128164", + "AQLid": "77", + "QDes": "/睡觉", + "EMCode": "400638" + }, + { + "QSid": "⚡", + "QCid": "9889", + "AQLid": "78", + "QDes": "/闪电", + "QHide": "1", + "EMCode": "401685" + }, + { + "QSid": "👣", + "QCid": "128099", + "AQLid": "79", + "QDes": "/脚印", + "QHide": "1", + "EMCode": "400450" + }, + { + "QSid": "💩", + "QCid": "128169", + "AQLid": "80", + "QDes": "/便便", + "EMCode": "400643" + }, + { + "QSid": "💉", + "QCid": "128137", + "AQLid": "81", + "QDes": "/打针", + "EMCode": "400611" + }, + { + "QSid": "♨", + "QCid": "9832", + "AQLid": "82", + "QDes": "/热", + "QHide": "1", + "EMCode": "401287" + }, + { + "QSid": "📫", + "QCid": "128235", + "AQLid": "83", + "QDes": "/邮箱", + "EMCode": "400714" + }, + { + "QSid": "🔑", + "QCid": "128273", + "AQLid": "84", + "QDes": "/钥匙", + "QHide": "1", + "EMCode": "400748" + }, + { + "QSid": "🔒", + "QCid": "128274", + "AQLid": "85", + "QDes": "/锁", + "QHide": "1", + "EMCode": "400749" + }, + { + "QSid": "✈", + "QCid": "9992", + "AQLid": "86", + "QDes": "/飞机", + "QHide": "1", + "EMCode": "401298" + }, + { + "QSid": "🚄", + "QCid": "128644", + "AQLid": "87", + "QDes": "/列车", + "QHide": "1", + "EMCode": "400942" + }, + { + "QSid": "🚗", + "QCid": "128663", + "AQLid": "88", + "QDes": "/汽车", + "QHide": "1", + "EMCode": "400961" + }, + { + "QSid": "🚤", + "QCid": "128676", + "AQLid": "89", + "QDes": "/快艇", + "QHide": "1", + "EMCode": "400979" + }, + { + "QSid": "🚲", + "QCid": "128690", + "AQLid": "90", + "QDes": "/自行车", + "QHide": "1", + "EMCode": "400993" + }, + { + "QSid": "🐎", + "QCid": "128014", + "AQLid": "91", + "QDes": "/骑马", + "EMCode": "400302" + }, + { + "QSid": "🚀", + "QCid": "128640", + "AQLid": "92", + "QDes": "/火箭", + "QHide": "1", + "EMCode": "400938" + }, + { + "QSid": "🚌", + "QCid": "128652", + "AQLid": "93", + "QDes": "/公交", + "QHide": "1", + "EMCode": "400950" + }, + { + "QSid": "⛵", + "QCid": "9973", + "AQLid": "94", + "QDes": "/船", + "QHide": "1", + "EMCode": "401294" + }, + { + "QSid": "👩", + "QCid": "128105", + "AQLid": "95", + "QDes": "/妈妈", + "QHide": "1", + "EMCode": "400482" + }, + { + "QSid": "👨", + "QCid": "128104", + "AQLid": "96", + "QDes": "/爸爸", + "QHide": "1", + "EMCode": "400465" + }, + { + "QSid": "👧", + "QCid": "128103", + "AQLid": "97", + "QDes": "/女孩", + "EMCode": "400459" + }, + { + "QSid": "👦", + "QCid": "128102", + "AQLid": "98", + "QDes": "/男孩", + "EMCode": "400453" + }, + { + "QSid": "🐵", + "QCid": "128053", + "AQLid": "99", + "QDes": "/猴", + "EMCode": "400341" + }, + { + "QSid": "🐙", + "QCid": "128025", + "AQLid": "100", + "QDes": "/章鱼", + "QHide": "1", + "EMCode": "400313" + }, + { + "QSid": "🐷", + "QCid": "128055", + "AQLid": "101", + "QDes": "/猪", + "EMCode": "400343" + }, + { + "QSid": "💀", + "QCid": "128128", + "AQLid": "102", + "QDes": "/骷髅", + "QHide": "1", + "EMCode": "400572" + }, + { + "QSid": "🐤", + "QCid": "128036", + "AQLid": "103", + "QDes": "/小鸡", + "QHide": "1", + "EMCode": "400324" + }, + { + "QSid": "🐨", + "QCid": "128040", + "AQLid": "104", + "QDes": "/树懒", + "QHide": "1", + "EMCode": "400328" + }, + { + "QSid": "🐮", + "QCid": "128046", + "AQLid": "105", + "QDes": "/牛", + "EMCode": "400334" + }, + { + "QSid": "🐔", + "QCid": "128020", + "AQLid": "106", + "QDes": "/公鸡", + "EMCode": "400308" + }, + { + "QSid": "🐸", + "QCid": "128056", + "AQLid": "107", + "QDes": "/青蛙", + "EMCode": "400344" + }, + { + "QSid": "👻", + "QCid": "128123", + "AQLid": "108", + "QDes": "/幽灵", + "EMCode": "400562" + }, + { + "QSid": "🐛", + "QCid": "128027", + "AQLid": "109", + "QDes": "/虫", + "EMCode": "400315" + }, + { + "QSid": "🐠", + "QCid": "128032", + "AQLid": "110", + "QDes": "/鱼", + "QHide": "1", + "EMCode": "400320" + }, + { + "QSid": "🐶", + "QCid": "128054", + "AQLid": "111", + "QDes": "/狗", + "EMCode": "400342" + }, + { + "QSid": "🐯", + "QCid": "128047", + "AQLid": "112", + "QDes": "/老虎", + "QHide": "1", + "EMCode": "400335" + }, + { + "QSid": "👼", + "QCid": "128124", + "AQLid": "113", + "QDes": "/天使", + "QHide": "1", + "EMCode": "400563" + }, + { + "QSid": "🐧", + "QCid": "128039", + "AQLid": "114", + "QDes": "/企鹅", + "QHide": "1", + "EMCode": "400327" + }, + { + "QSid": "🐳", + "QCid": "128051", + "AQLid": "115", + "QDes": "/鲸鱼", + "EMCode": "400339" + }, + { + "QSid": "🐭", + "QCid": "128045", + "AQLid": "116", + "QDes": "/老鼠", + "QHide": "1", + "EMCode": "400333" + }, + { + "QSid": "👒", + "QCid": "128082", + "AQLid": "117", + "QDes": "/帽子", + "QHide": "1", + "EMCode": "400433" + }, + { + "QSid": "👗", + "QCid": "128087", + "AQLid": "118", + "QDes": "/连衣裙", + "QHide": "1", + "EMCode": "400438" + }, + { + "QSid": "💄", + "QCid": "128132", + "AQLid": "119", + "QDes": "/口红", + "QHide": "1", + "EMCode": "400591" + }, + { + "QSid": "👠", + "QCid": "128096", + "AQLid": "120", + "QDes": "/高跟鞋", + "QHide": "1", + "EMCode": "400447" + }, + { + "QSid": "👢", + "QCid": "128098", + "AQLid": "121", + "QDes": "/靴子", + "EMCode": "400449" + }, + { + "QSid": "🌂", + "QCid": "127746", + "AQLid": "122", + "QDes": "/雨伞", + "QHide": "1", + "EMCode": "400077" + }, + { + "QSid": "👜", + "QCid": "128092", + "AQLid": "123", + "QDes": "/包", + "QHide": "1", + "EMCode": "400443" + }, + { + "QSid": "👙", + "QCid": "128089", + "AQLid": "124", + "QDes": "/内衣", + "QHide": "1", + "EMCode": "400440" + }, + { + "QSid": "👕", + "QCid": "128085", + "AQLid": "125", + "QDes": "/衣服", + "QHide": "1", + "EMCode": "400436" + }, + { + "QSid": "👟", + "QCid": "128095", + "AQLid": "126", + "QDes": "/鞋子", + "QHide": "1", + "EMCode": "400446" + }, + { + "QSid": "☁", + "QCid": "9729", + "AQLid": "127", + "QDes": "/云朵", + "QHide": "1", + "EMCode": "401329" + }, + { + "QSid": "☀", + "QCid": "9728", + "AQLid": "128", + "QDes": "/晴天", + "EMCode": "401328" + }, + { + "QSid": "☔", + "QCid": "9748", + "AQLid": "129", + "QDes": "/雨天", + "QHide": "1", + "EMCode": "401342" + }, + { + "QSid": "🌙", + "QCid": "127769", + "AQLid": "130", + "QDes": "/月亮", + "QHide": "1", + "EMCode": "400100" + }, + { + "QSid": "⛄", + "QCid": "9924", + "AQLid": "131", + "QDes": "/雪人", + "QHide": "1", + "EMCode": "401346" + }, + { + "QSid": "⭕", + "QCid": "11093", + "AQLid": "132", + "QDes": "/正确", + "QHide": "1", + "EMCode": "401687" + }, + { + "QSid": "❌", + "QCid": "10060", + "AQLid": "133", + "QDes": "/错误", + "QHide": "1", + "EMCode": "401142" + }, + { + "QSid": "❔", + "QCid": "10068", + "AQLid": "134", + "QDes": "/问号", + "EMCode": "401145" + }, + { + "QSid": "❕", + "QCid": "10069", + "AQLid": "135", + "QDes": "/叹号", + "QHide": "1", + "EMCode": "401146" + }, + { + "QSid": "☎", + "QCid": "9742", + "AQLid": "136", + "QDes": "/电话", + "QHide": "1", + "EMCode": "401398" + }, + { + "QSid": "📷", + "QCid": "128247", + "AQLid": "137", + "QDes": "/相机", + "QHide": "1", + "EMCode": "400726" + }, + { + "QSid": "📱", + "QCid": "128241", + "AQLid": "138", + "QDes": "/手机", + "QHide": "1", + "EMCode": "400720" + }, + { + "QSid": "📠", + "QCid": "128224", + "AQLid": "139", + "QDes": "/传真", + "QHide": "1", + "EMCode": "400703" + }, + { + "QSid": "💻", + "QCid": "128187", + "AQLid": "140", + "QDes": "/电脑", + "QHide": "1", + "EMCode": "400666" + }, + { + "QSid": "🎥", + "QCid": "127909", + "AQLid": "141", + "QDes": "/摄影机", + "QHide": "1", + "EMCode": "400214" + }, + { + "QSid": "🎤", + "QCid": "127908", + "AQLid": "142", + "QDes": "/话筒", + "QHide": "1", + "EMCode": "400213" + }, + { + "QSid": "🔫", + "QCid": "128299", + "AQLid": "143", + "QDes": "/手枪", + "EMCode": "400774" + }, + { + "QSid": "💿", + "QCid": "128191", + "AQLid": "144", + "QDes": "/光碟", + "QHide": "1", + "EMCode": "400670" + }, + { + "QSid": "💓", + "QCid": "128147", + "AQLid": "145", + "QDes": "/爱心", + "EMCode": "400621" + }, + { + "QSid": "♣", + "QCid": "9827", + "AQLid": "146", + "QDes": "/扑克", + "QHide": "1", + "EMCode": "401385" + }, + { + "QSid": "🀄", + "QCid": "126980", + "AQLid": "147", + "QDes": "/麻将", + "QHide": "1", + "EMCode": "401386" + }, + { + "QSid": "〽", + "QCid": "12349", + "AQLid": "148", + "QDes": "/股票", + "QHide": "1", + "EMCode": "401691" + }, + { + "QSid": "🎰", + "QCid": "127920", + "AQLid": "149", + "QDes": "/老虎机", + "QHide": "1", + "EMCode": "400225" + }, + { + "QSid": "🚥", + "QCid": "128677", + "AQLid": "150", + "QDes": "/信号灯", + "QHide": "1", + "EMCode": "400980" + }, + { + "QSid": "🚧", + "QCid": "128679", + "AQLid": "151", + "QDes": "/路障", + "QHide": "1", + "EMCode": "400982" + }, + { + "QSid": "🎸", + "QCid": "127928", + "AQLid": "152", + "QDes": "/吉他", + "QHide": "1", + "EMCode": "400233" + }, + { + "QSid": "💈", + "QCid": "128136", + "AQLid": "153", + "QDes": "/理发厅", + "QHide": "1", + "EMCode": "400610" + }, + { + "QSid": "🛀", + "QCid": "128704", + "AQLid": "154", + "QDes": "/浴缸", + "QHide": "1", + "EMCode": "401022" + }, + { + "QSid": "🚽", + "QCid": "128701", + "AQLid": "155", + "QDes": "/马桶", + "QHide": "1", + "EMCode": "401019" + }, + { + "QSid": "🏠", + "QCid": "127968", + "AQLid": "156", + "QDes": "/家", + "QHide": "1", + "EMCode": "400271" + }, + { + "QSid": "⛪", + "QCid": "9962", + "AQLid": "157", + "QDes": "/教堂", + "QHide": "1", + "EMCode": "401281" + }, + { + "QSid": "🏦", + "QCid": "127974", + "AQLid": "158", + "QDes": "/银行", + "QHide": "1", + "EMCode": "400277" + }, + { + "QSid": "🏥", + "QCid": "127973", + "AQLid": "159", + "QDes": "/医院", + "QHide": "1", + "EMCode": "400276" + }, + { + "QSid": "🏨", + "QCid": "127976", + "AQLid": "160", + "QDes": "/酒店", + "QHide": "1", + "EMCode": "400279" + }, + { + "QSid": "🏧", + "QCid": "127975", + "AQLid": "161", + "QDes": "/取款机", + "QHide": "1", + "EMCode": "400278" + }, + { + "QSid": "🏪", + "QCid": "127978", + "AQLid": "162", + "QDes": "/便利店", + "EMCode": "400281" + }, + { + "QSid": "🚹", + "QCid": "128697", + "AQLid": "163", + "QDes": "/男性", + "QHide": "1", + "EMCode": "401015" + }, + { + "QSid": "🚺", + "QCid": "128698", + "AQLid": "164", + "QDes": "/女性", + "QHide": "1", + "EMCode": "401016" + } + ] +} \ No newline at end of file diff --git a/src/core/src/entities/group.ts b/src/core/src/entities/group.ts new file mode 100644 index 000000000..8e8dde297 --- /dev/null +++ b/src/core/src/entities/group.ts @@ -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 +} \ No newline at end of file diff --git a/src/core/src/entities/index.ts b/src/core/src/entities/index.ts new file mode 100644 index 000000000..97eb9b0e4 --- /dev/null +++ b/src/core/src/entities/index.ts @@ -0,0 +1,7 @@ +export * from './user'; +export * from './group'; +export * from './msg'; +export * from './notify'; +export * from './cache'; +export * from './constructor'; + diff --git a/src/core/src/entities/msg.ts b/src/core/src/entities/msg.ts new file mode 100644 index 000000000..73572fa61 --- /dev/null +++ b/src/core/src/entities/msg.ts @@ -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; + 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, 没有host,host是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn + originImageMd5?: string; + sourcePath: string; // 图片本地路径 + thumbPath: Map; + 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; + 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; + }[]; +} diff --git a/src/core/src/entities/notify.ts b/src/core/src/entities/notify.ts new file mode 100644 index 000000000..c893e8af3 --- /dev/null +++ b/src/core/src/entities/notify.ts @@ -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 + 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 + } +} \ No newline at end of file diff --git a/src/core/src/entities/user.ts b/src/core/src/entities/user.ts new file mode 100644 index 000000000..e0768a08e --- /dev/null +++ b/src/core/src/entities/user.ts @@ -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 + } +} \ No newline at end of file diff --git a/src/core/src/index.ts b/src/core/src/index.ts new file mode 100644 index 000000000..4dcf5fe8e --- /dev/null +++ b/src/core/src/index.ts @@ -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'; diff --git a/src/core/src/listeners/NodeIKernelBuddyListener.ts b/src/core/src/listeners/NodeIKernelBuddyListener.ts new file mode 100644 index 000000000..361d34f61 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelBuddyListener.ts @@ -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 { + } +} diff --git a/src/core/src/listeners/NodeIKernelFileAssistantListener.ts b/src/core/src/listeners/NodeIKernelFileAssistantListener.ts new file mode 100644 index 000000000..8d7a604f5 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelFileAssistantListener.ts @@ -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[]) { } +} \ No newline at end of file diff --git a/src/core/src/listeners/NodeIKernelGroupListener.ts b/src/core/src/listeners/NodeIKernelGroupListener.ts new file mode 100644 index 000000000..d9e91b6c4 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelGroupListener.ts @@ -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, + finish: boolean, + hasRobot: boolean + }): void; + + onMemberInfoChange(groupCode: string, changeType: number, members: Map): 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) { + } + + onMemberListChange(arg: { + sceneId: string, + ids: string[], + infos: Map, // 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) { + 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); + } +} diff --git a/src/core/src/listeners/NodeIKernelLoginListener.ts b/src/core/src/listeners/NodeIKernelLoginListener.ts new file mode 100644 index 000000000..4c799ebf9 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelLoginListener.ts @@ -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;//一般是空的 拿不到 +} diff --git a/src/core/src/listeners/NodeIKernelMsgListener.ts b/src/core/src/listeners/NodeIKernelMsgListener.ts new file mode 100644 index 000000000..0844b9772 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelMsgListener.ts @@ -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 + 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[]) { + + } +} diff --git a/src/core/src/listeners/NodeIKernelProfileListener.ts b/src/core/src/listeners/NodeIKernelProfileListener.ts new file mode 100644 index 000000000..ca621d3b8 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelProfileListener.ts @@ -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[]) { + + } +} diff --git a/src/core/src/listeners/NodeIKernelRobotListener.ts b/src/core/src/listeners/NodeIKernelRobotListener.ts new file mode 100644 index 000000000..9c319feea --- /dev/null +++ b/src/core/src/listeners/NodeIKernelRobotListener.ts @@ -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[]){ + + } +} diff --git a/src/core/src/listeners/NodeIKernelSessionListener.ts b/src/core/src/listeners/NodeIKernelSessionListener.ts new file mode 100644 index 000000000..4653a4845 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelSessionListener.ts @@ -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) { + + } +} diff --git a/src/core/src/listeners/NodeIKernelStorageCleanListener.ts b/src/core/src/listeners/NodeIKernelStorageCleanListener.ts new file mode 100644 index 000000000..ed872d885 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelStorageCleanListener.ts @@ -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) { + + } +} \ No newline at end of file diff --git a/src/core/src/listeners/NodeIKernelTicketListener.ts b/src/core/src/listeners/NodeIKernelTicketListener.ts new file mode 100644 index 000000000..1fe32a155 --- /dev/null +++ b/src/core/src/listeners/NodeIKernelTicketListener.ts @@ -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 { +} \ No newline at end of file diff --git a/src/core/src/listeners/index.ts b/src/core/src/listeners/index.ts new file mode 100644 index 000000000..cc8ad4a22 --- /dev/null +++ b/src/core/src/listeners/index.ts @@ -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'; + diff --git a/src/core/src/services/NodeIKernelAlbumService.ts b/src/core/src/services/NodeIKernelAlbumService.ts new file mode 100644 index 000000000..651f764b2 --- /dev/null +++ b/src/core/src/services/NodeIKernelAlbumService.ts @@ -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 + +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelAvatarService.ts b/src/core/src/services/NodeIKernelAvatarService.ts new file mode 100644 index 000000000..e8541846f --- /dev/null +++ b/src/core/src/services/NodeIKernelAvatarService.ts @@ -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; + + 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; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelBuddyService.ts b/src/core/src/services/NodeIKernelBuddyService.ts new file mode 100644 index 000000000..de3f029cc --- /dev/null +++ b/src/core/src/services/NodeIKernelBuddyService.ts @@ -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; + + // 以下为原生方法 + addKernelBuddyListener(listener: NodeIKernelBuddyListener): number; + + removeKernelBuddyListener(listener: unknown): void; + + getBuddyList(bool: boolean): Promise; + + 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; + + 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; + + isNull(): boolean; +} diff --git a/src/core/src/services/NodeIKernelCollectionService.ts b/src/core/src/services/NodeIKernelCollectionService.ts new file mode 100644 index 000000000..74ed399f8 --- /dev/null +++ b/src/core/src/services/NodeIKernelCollectionService.ts @@ -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, + 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 +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelDbToolsService.ts b/src/core/src/services/NodeIKernelDbToolsService.ts new file mode 100644 index 000000000..7c2b3cebd --- /dev/null +++ b/src/core/src/services/NodeIKernelDbToolsService.ts @@ -0,0 +1,5 @@ +export interface NodeIKernelDbToolsService { + depositDatabase(...args: unknown[]): unknown; + backupDatabase(...args: unknown[]): unknown; + retrieveDatabase(...args: unknown[]): unknown; +} diff --git a/src/core/src/services/NodeIKernelFileAssistantService.ts b/src/core/src/services/NodeIKernelFileAssistantService.ts new file mode 100644 index 000000000..660372239 --- /dev/null +++ b/src/core/src/services/NodeIKernelFileAssistantService.ts @@ -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; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelGroupService.ts b/src/core/src/services/NodeIKernelGroupService.ts new file mode 100644 index 000000000..339c1f236 --- /dev/null +++ b/src/core/src/services/NodeIKernelGroupService.ts @@ -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, 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; + + 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; + + getGroupExtList(force: boolean): Promise; + + getGroupDetailInfo(groupCode: string): unknown; + + getMemberExtInfo(param: GroupExtParam): Promise;//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; + + clearGroupNotifies(groupCode: string): void; + + getGroupNotifiesUnreadCount(unknown: Boolean): Promise; + + clearGroupNotifiesUnreadCount(groupCode: string): void; + + operateSysNotify( + doubt: boolean, + operateMsg: { + operateType: GroupRequestOperateTypes, // 2 拒绝 + targetMsg: { + seq: string, // 通知序列号 + type: GroupNotifyTypes, + groupCode: string, + postscript: string + } + }): Promise; + + setTop(groupCode: string, isTop: boolean): void; + + getGroupBulletin(groupCode: string): unknown; + + deleteGroupBulletin(groupCode: string, seq: string): void; + + publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise; + + publishInstructionForNewcomers(groupCode: string, arg: unknown): void; + + uploadGroupBulletinPic(groupCode: string, pskey: string, imagePath: string): Promise; + + 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; + + getGroupRecommendContactArkJson(groupCode: string): unknown; + + getJoinGroupLink(groupCode: string): unknown; + + modifyGroupExtInfo(groupCode: string, arg: unknown): void; + + isNull(): boolean; +} diff --git a/src/core/src/services/NodeIKernelLoginService.ts b/src/core/src/services/NodeIKernelLoginService.ts new file mode 100644 index 000000000..d3941f6b4 --- /dev/null +++ b/src/core/src/services/NodeIKernelLoginService.ts @@ -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; + + passwordLogin(param: passwordLoginArgType): Promise; + + getQRCodePicture(): boolean; +} diff --git a/src/core/src/services/NodeIKernelMsgBackupService.ts b/src/core/src/services/NodeIKernelMsgBackupService.ts new file mode 100644 index 000000000..aef276fc2 --- /dev/null +++ b/src/core/src/services/NodeIKernelMsgBackupService.ts @@ -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 +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelMsgService.ts b/src/core/src/services/NodeIKernelMsgService.ts new file mode 100644 index 000000000..74658d193 --- /dev/null +++ b/src/core/src/services/NodeIKernelMsgService.ts @@ -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): Promise; + + recallMsg(peer: Peer, msgIds: string[]): Promise; + + addKernelMsgImportToolListener(arg: Object): unknown; + + removeKernelMsgListener(args: unknown): unknown; + + addKernelTempChatSigListener(...args: unknown[]): unknown; + + removeKernelTempChatSigListener(...args: unknown[]): unknown; + + setAutoReplyTextList(AutoReplyText: Array, i2: number): unknown; + + getAutoReplyTextList(...args: unknown[]): unknown; + + getOnLineDev(): Promise; + + kickOffLine(DevInfo: Object): unknown; + + setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise; + + 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; + + forwardMsgWithComment(...args: unknown[]): unknown; + + forwardSubMsgWithComment(...args: unknown[]): unknown; + + forwardRichMsgInVist(...args: unknown[]): unknown; + + forwardFile(...args: unknown[]): unknown; + //Array, 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; + + getLastMessageList(peer: Peer[]): Promise; + + getAioFirstViewLatestMsgs(...args: unknown[]): unknown; + + getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise; + + getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise; + + // this.$peer = contact; + // this.$msgTime = j2; + // this.$clientSeq = j3; + // this.$cnt = i2; + + getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise; + + getMsgsWithStatus(params: { + peer: Peer + msgId: string + msgTime: unknown + cnt: unknown + queryOrder: boolean + isIncludeSelf: boolean + appid: unknown + }): Promise; + + getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise; + + getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise; + + getMsgsByMsgId(peer: Peer, ids: string[]): Promise; + + getRecallMsgsByMsgId(peer: Peer, MsgId: string[]): Promise; + + getMsgsBySeqList(peer: Peer, seqList: string[]): Promise; + + 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; + // 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; + //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; + + 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; + + 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; + + 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; + + 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 + 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;//频道的东西 + + 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;//表情推荐? + + 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;//这是嘛啊 + +} diff --git a/src/core/src/services/NodeIKernelNodeMiscService.ts b/src/core/src/services/NodeIKernelNodeMiscService.ts new file mode 100644 index 000000000..150c2a699 --- /dev/null +++ b/src/core/src/services/NodeIKernelNodeMiscService.ts @@ -0,0 +1,12 @@ +import { GeneralCallResult } from "./common"; + +//没扒干净 因为用不着 +export interface NodeIKernelNodeMiscService { + getMiniAppPath(): unknown; + setMiniAppVersion(version:string): unknown; + wantWinScreenOCR(imagepath: string): Promise; + SendMiniAppMsg(arg1: string, arg2: string, arg3: string): unknown; + startNewMiniApp(appfile: string, params: string): unknown; + // 我的计划是转发给一个新程序避免吃掉Electron_AS_Node的环境 然后重写启动MiniApp 挂载相应JS脚本 这样有个问题 + // 需要自己转发ipc参数 然后必须处在gui环境 且完成校验破解 才能实现发包 有点抽象了 +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelOnlineStatusService.ts b/src/core/src/services/NodeIKernelOnlineStatusService.ts new file mode 100644 index 000000000..b89f5e012 --- /dev/null +++ b/src/core/src/services/NodeIKernelOnlineStatusService.ts @@ -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; + + isNull(): boolean; +} diff --git a/src/core/src/services/NodeIKernelProfileLikeService.ts b/src/core/src/services/NodeIKernelProfileLikeService.ts new file mode 100644 index 000000000..0e30cefea --- /dev/null +++ b/src/core/src/services/NodeIKernelProfileLikeService.ts @@ -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; +} diff --git a/src/core/src/services/NodeIKernelProfileService.ts b/src/core/src/services/NodeIKernelProfileService.ts new file mode 100644 index 000000000..9cd5950e0 --- /dev/null +++ b/src/core/src/services/NodeIKernelProfileService.ts @@ -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; + + enumCountryOptions(): Array; + + enumProvinceOptions(Country: string): Array; + + 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; + + modifyDesktopMiniProfile(param: ModifyProfileParams): Promise; + + setNickName(NickName: string): Promise; + + setLongNick(longNick: string): Promise; + + setBirthday(...args: unknown[]): Promise; + + setGander(...args: unknown[]): Promise; + + setHeader(arg: string): Promise; + + setRecommendImgFlag(...args: unknown[]): Promise; + + getUserSimpleInfo(force: boolean, uids: string[],): Promise; + + getUserDetailInfo(uid: string): Promise; + + getUserDetailInfoWithBizInfo(uid: string, Biz: BizKey[]): Promise; + + getUserDetailInfoByUin(uin: string): Promise; + + getZplanAvatarInfos(args: string[]): Promise; + + getStatus(uid: string): Promise; + + startStatusPolling(isForceReset: boolean): Promise; + + getSelfStatus(): Promise; + // + setdisableEmojiShortCuts(...args: unknown[]): unknown; + + getProfileQzonePicInfo(uid: string, type: number, force: boolean): Promise; + + //profileService.getCoreInfo("UserRemarkServiceImpl::getStrangerRemarkByUid", arrayList); + getCoreInfo(name: string, arg: any[]): unknown; + //m429253e12.getOtherFlag("FriendListInfoCache_getKernelDataAndPutCache", new ArrayList<>()); + isNull(): boolean; +} diff --git a/src/core/src/services/NodeIKernelRecentContactService.ts b/src/core/src/services/NodeIKernelRecentContactService.ts new file mode 100644 index 000000000..43c29960d --- /dev/null +++ b/src/core/src/services/NodeIKernelRecentContactService.ts @@ -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; + + 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; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelRichMediaService.ts b/src/core/src/services/NodeIKernelRichMediaService.ts new file mode 100644 index 000000000..d21bdb786 --- /dev/null +++ b/src/core/src/services/NodeIKernelRichMediaService.ts @@ -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; + + //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, + 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; + + //参数与getVideoPlayUrlInVisit一样 + downloadRichMediaInVisit(arg: unknown): unknown; + + downloadFileForModelId(peer: Peer, arg: unknown[], arg3: string): unknown; + //第三个参数 Array + // 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 } }> + + 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; + + 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, Files: Array): Promise + failFileIdList: Array + } + }>; + + translateEnWordToZn(words: string[]): Promise; + + getScreenOCR(path: string): Promise; + + batchGetGroupFileCount(Gids: Array): Promise, groupFileCounts: Array }>; + + 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; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelRobotService.ts b/src/core/src/services/NodeIKernelRobotService.ts new file mode 100644 index 000000000..b406c0019 --- /dev/null +++ b/src/core/src/services/NodeIKernelRobotService.ts @@ -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; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelSearchService.ts b/src/core/src/services/NodeIKernelSearchService.ts new file mode 100644 index 000000000..d7e11d82c --- /dev/null +++ b/src/core/src/services/NodeIKernelSearchService.ts @@ -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 +} diff --git a/src/core/src/services/NodeIKernelStorageCleanService.ts b/src/core/src/services/NodeIKernelStorageCleanService.ts new file mode 100644 index 000000000..34c267146 --- /dev/null +++ b/src/core/src/services/NodeIKernelStorageCleanService.ts @@ -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; + + 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; +} diff --git a/src/core/src/services/NodeIKernelTianShuService.ts b/src/core/src/services/NodeIKernelTianShuService.ts new file mode 100644 index 000000000..48622c7e8 --- /dev/null +++ b/src/core/src/services/NodeIKernelTianShuService.ts @@ -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 + +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelTicketService.ts b/src/core/src/services/NodeIKernelTicketService.ts new file mode 100644 index 000000000..0178f755c --- /dev/null +++ b/src/core/src/services/NodeIKernelTicketService.ts @@ -0,0 +1,12 @@ +import { forceFetchClientKeyRetType } from "./common"; + +export interface NodeIKernelTicketService { + + addKernelTicketListener(listener: unknown): void; + + removeKernelTicketListener(listenerId: unknown): void; + + forceFetchClientKey(arg: string): Promise; + + isNull(): boolean; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelTipOffService.ts b/src/core/src/services/NodeIKernelTipOffService.ts new file mode 100644 index 000000000..7071f8938 --- /dev/null +++ b/src/core/src/services/NodeIKernelTipOffService.ts @@ -0,0 +1,20 @@ +import { GeneralCallResult } from "./common"; + +export interface NodeIKernelTipOffService { + + addKernelTipOffListener(listener: unknown): void; + + removeKernelTipOffListener(listenerId: unknown): void; + + tipOffSendJsData(args: unknown[]): Promise;//2 + + getPskey(domainList: string[], nocache: boolean): Promise }>;//2 + + tipOffSendJsData(args: unknown[]): Promise;//2 + + tipOffMsgs(args: unknown[]): Promise;//1 + + encodeUinAesInfo(args: unknown[]): Promise;//2 + + isNull(): boolean; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelUixConvertService.ts b/src/core/src/services/NodeIKernelUixConvertService.ts new file mode 100644 index 000000000..c4d284716 --- /dev/null +++ b/src/core/src/services/NodeIKernelUixConvertService.ts @@ -0,0 +1,5 @@ +export interface NodeIKernelUixConvertService { + getUin(uid: string[]): Promise<{ uidInfo: Map }>; + + getUid(uin: string[]): Promise<{ uinInfo: Map }>; +} \ No newline at end of file diff --git a/src/core/src/services/NodeIKernelUnitedConfigService.ts b/src/core/src/services/NodeIKernelUnitedConfigService.ts new file mode 100644 index 000000000..f96a4f0ea --- /dev/null +++ b/src/core/src/services/NodeIKernelUnitedConfigService.ts @@ -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 +} diff --git a/src/core/src/services/NodeIYellowFaceService.ts b/src/core/src/services/NodeIYellowFaceService.ts new file mode 100644 index 000000000..31ef0ff63 --- /dev/null +++ b/src/core/src/services/NodeIYellowFaceService.ts @@ -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); +// } \ No newline at end of file diff --git a/src/core/src/services/common.ts b/src/core/src/services/common.ts new file mode 100644 index 000000000..acd1e6f13 --- /dev/null +++ b/src/core/src/services/common.ts @@ -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; + } \ No newline at end of file diff --git a/src/core/src/services/index.ts b/src/core/src/services/index.ts new file mode 100644 index 000000000..efab2edd1 --- /dev/null +++ b/src/core/src/services/index.ts @@ -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' \ No newline at end of file diff --git a/src/core/src/sessionConfig.ts b/src/core/src/sessionConfig.ts new file mode 100644 index 000000000..c31f90db6 --- /dev/null +++ b/src/core/src/sessionConfig.ts @@ -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 { + 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; +} diff --git a/src/core/src/utils/config.ts b/src/core/src/utils/config.ts new file mode 100644 index 000000000..d7e90ca7e --- /dev/null +++ b/src/core/src/utils/config.ts @@ -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 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(); + diff --git a/src/core/src/utils/rkey.ts b/src/core/src/utils/rkey.ts new file mode 100644 index 000000000..d22daeb43 --- /dev/null +++ b/src/core/src/utils/rkey.ts @@ -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 { + //刷新rkey + this.rkeyData = await RequestUtil.HttpGetJson(this.serverUrl, 'GET'); + } +} +export const rkeyManager = new RkeyManager('http://napcat-sign.wumiao.wang:2082/rkey'); diff --git a/src/core/src/wrapper.ts b/src/core/src/wrapper.ts new file mode 100644 index 000000000..9f21cd809 --- /dev/null +++ b/src/core/src/wrapper.ts @@ -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,//直接的猜测 + + 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, arg2: string, arg3: number, arg4: boolean): unknown, + + reportValueIndicators(arg0: string, arg1: Map, arg2: string, arg3: boolean, arg4: number): unknown, + + checkNewUserDataSaveDirAvailable(arg0: string): unknown, + + copyUserData(arg0: string, arg1: string): Promise, + + setUserDataSaveDirectory(arg0: string): Promise, + + 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; diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json new file mode 100644 index 000000000..a4f821066 --- /dev/null +++ b/src/core/tsconfig.json @@ -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" + ] +} diff --git a/src/core/vite.config.ts b/src/core/vite.config.ts new file mode 100644 index 000000000..2c7b9ed9c --- /dev/null +++ b/src/core/vite.config.ts @@ -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; +});