From 974b10b7b56a7a560ee427347c4d7d01890aeb78 Mon Sep 17 00:00:00 2001 From: H3CoF6 <1707889225@qq.com> Date: Sat, 24 Jan 2026 02:30:06 +0800 Subject: [PATCH 1/8] feat: support thumbnail for flash-transfer --- packages/napcat-core/apis/flash.ts | 46 +++++- packages/napcat-core/data/flash.ts | 26 ++-- packages/napcat-core/index.ts | 1 + packages/napcat-core/wrapper.ts | 15 +- .../action/file/flash/CreateFlashTask.ts | 135 +++++++++++++++++- 5 files changed, 206 insertions(+), 17 deletions(-) diff --git a/packages/napcat-core/apis/flash.ts b/packages/napcat-core/apis/flash.ts index 2d865161..3e5a182d 100644 --- a/packages/napcat-core/apis/flash.ts +++ b/packages/napcat-core/apis/flash.ts @@ -19,8 +19,10 @@ export class NTQQFlashApi { /** * 发起闪传上传任务 * @param fileListToUpload 上传文件绝对路径的列表,可以是文件夹!! + * @param thumbnailPath + * @param filesetName */ - async createFlashTransferUploadTask (fileListToUpload: string[]): Promise < GeneralCallResult & { + async createFlashTransferUploadTask (fileListToUpload: string[], thumbnailPath: string, filesetName: string): Promise < GeneralCallResult & { createFlashTransferResult: createFlashTransferResult; seq: number; } > { @@ -29,15 +31,34 @@ export class NTQQFlashApi { const timestamp : number = Date.now(); const selfInfo = this.core.selfInfo; + console.log(thumbnailPath); + const fileUploadArg = { screen: 1, // 1 + name: filesetName, uploaders: [{ uin: selfInfo.uin, uid: selfInfo.uid, sendEntrance: '', nickname: selfInfo.nick, }], + coverPath: thumbnailPath, paths: fileListToUpload, + excludePaths: [], + expireLeftTime: 0, + isNeedDelDeviceInfo: false, + isNeedDelLocation: false, + coverOriginalInfos: [ + { + path: fileListToUpload[0] || '', + thumbnailPath, + }, + ], + uploadSceneType: 10, + detectPrivacyInfoResult: { + exists: false, + allDetectResults: new Map(), + }, }; const uploadResult = await flashService.createFlashTransferUploadTask(timestamp, fileUploadArg); @@ -261,4 +282,27 @@ export class NTQQFlashApi { }; } } + + async createFileThumbnail (filePath: string): Promise { + const msgService = this.context.session.getMsgService(); + const savePath = msgService.getFileThumbSavePathForSend(750, true); + + const result = await this.core.util.createThumbnailImage( + 'flashtransfer', + filePath, + savePath, + { + width: 520, + height: 520, + }, + 'jpeg', + null + ); + if (result.result === 0) { + this.context.logger.log('获取缩略图成功!!'); + result.targetPath = savePath; + return result; + } + return result; + } } diff --git a/packages/napcat-core/data/flash.ts b/packages/napcat-core/data/flash.ts index 56abc817..525b4bb7 100644 --- a/packages/napcat-core/data/flash.ts +++ b/packages/napcat-core/data/flash.ts @@ -202,21 +202,25 @@ export interface createFlashTransferResult { } export interface StartFlashTaskRequests { - screen?: number; // 1 PC-QQ + screen: number; // 1 PC-QQ + name?: string; uploaders: UploaderInfo[]; permission?: {}; coverPath?: string; paths: string[]; // 文件的绝对路径,可以是文件夹 - // excludePaths: []; - // expireLeftTime: 0, - // isNeedDelDeviceInfo: boolean, - // isNeedDelLocation: boolean, - // coverOriginalInfos: [], - // uploadSceneType: 10, // 不知道怎么枚举 先硬编码吧 - // detectPrivacyInfoResult: { - // exists: boolean, - // allDetectResults: {} - // } + excludePaths?: any[]; + expireLeftTime?: number, // 0 + isNeedDelDeviceInfo: boolean, + isNeedDelLocation: boolean, + coverOriginalInfos?: { + path: string, + thumbnailPath: string, + }[], + uploadSceneType: number, // 不知道怎么枚举 先硬编码吧 (PC QQ 10) + detectPrivacyInfoResult: { + exists: boolean, + allDetectResults: {} + } } export interface FileListInfoRequests { diff --git a/packages/napcat-core/index.ts b/packages/napcat-core/index.ts index e444ea9b..cc7ff526 100644 --- a/packages/napcat-core/index.ts +++ b/packages/napcat-core/index.ts @@ -74,6 +74,7 @@ export function loadQQWrapper (execPath: string | undefined, QQVersion: string): } const nativemodule: { exports: WrapperNodeApi; } = { exports: {} as WrapperNodeApi }; process.dlopen(nativemodule, wrapperNodePath); + process.env['NAPCAT_WRAPPER_PATH'] = wrapperNodePath; return nativemodule.exports; } export function getMajorPath (execPath: string, QQVersion: string): string { diff --git a/packages/napcat-core/wrapper.ts b/packages/napcat-core/wrapper.ts index c94bf42c..a0822954 100644 --- a/packages/napcat-core/wrapper.ts +++ b/packages/napcat-core/wrapper.ts @@ -1,5 +1,6 @@ import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from './adapters'; import { + GeneralCallResult, NodeIKernelAvatarService, NodeIKernelBuddyService, NodeIKernelGroupService, @@ -27,7 +28,7 @@ import { NodeIKernelMSFService } from './services/NodeIKernelMSFService'; import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService'; import { NodeIKernelECDHService } from './services/NodeIKernelECDHService'; import { NodeIO3MiscService } from './services/NodeIO3MiscService'; -import { NodeIKernelFlashTransferService } from "./services/NodeIKernelFlashTransferService"; +import { NodeIKernelFlashTransferService } from './services/NodeIKernelFlashTransferService'; export interface NodeQQNTWrapperUtil { get(): NodeQQNTWrapperUtil; @@ -139,6 +140,18 @@ export interface NodeQQNTWrapperUtil { getNvidiaDriverVersion(): unknown; isNull(): unknown; + + createThumbnailImage( + serviceName: string, + filePath: string, + targetPath: string, + imgSize: { + width: number, + height: number + }, + fileFormat: string, + arg: unknown, // null + ): Promise < GeneralCallResult & { targetPath?: string } >; } export interface NodeIQQNTStartupSessionWrapper { create(): NodeIQQNTStartupSessionWrapper; diff --git a/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts b/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts index 7d2f811b..2246007d 100644 --- a/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts +++ b/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts @@ -1,6 +1,71 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction'; import { ActionName } from '@/napcat-onebot/action/router'; -import { Static, Type } from '@sinclair/typebox'; +import { Static, Type, Optional } from '@sinclair/typebox'; +import path from 'node:path'; +import fs from 'node:fs'; + +const richMediaList = [ + '.mp4', '.mov', '.avi', '.wmv', '.mpeg', '.mpg', '.flv', '.mkv', + '.png', '.gif', '.jpg', '.jpeg', '.webp', '.bmp', +]; + +const aiList = ['.ai', '.eps']; +const apkList = ['.apk']; +const audioList = ['.mp3', '.wav', '.wma', '.aac', '.flac', '.ogg', '.m4a', '.mid', '.amr', '.m4r']; +const bakList = ['.bak', '.tmp', '.old', '.swp']; +const codeList = ['.js', '.ts', '.jsx', '.tsx', '.json', '.c', '.cpp', '.h', '.hpp', '.java', '.py', '.go', '.rs', '.php', '.html', '.css', '.sh', '.bat', '.cmd', '.xml', '.yaml', '.yml', '.sql', '.lua', '.rb']; +const dmgList = ['.dmg']; +const docList = ['.doc', '.docx', '.wps', '.dot', '.dotx', '.odt', '.rtf']; +const exeList = ['.exe', '.msi', '.com', '.scr', '.bin']; +const fontList = ['.ttf', '.otf', '.woff', '.woff2', '.ttc', '.fon']; +const imageList = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.ico', '.tif', '.tiff']; +const ipaList = ['.ipa']; +const keynoteList = ['.key']; +const linkList = ['.lnk', '.url', '.webloc']; +const mindmapList = ['.xmind', '.mm', '.mindnode', '.emmx']; +const noteList = ['.enex', '.notes', '.one']; +const numbersList = ['.numbers']; +const pagesList = ['.pages']; +const pdfList = ['.pdf']; +const pkgList = ['.pkg']; +const pptList = ['.ppt', '.pptx', '.pps', '.ppsx', '.pot', '.odp']; +const psList = ['.psd']; +const rarList = ['.rar']; +const sketchList = ['.sketch']; +const txtList = ['.txt', '.md', '.log', '.ini', '.conf', '.cfg', '.info']; +const videoList = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv', '.mpeg', '.mpg', '.3gp', '.rmvb']; +const xlsList = ['.xls', '.xlsx', '.csv', '.et', '.xlt', '.ods']; +const zipList = ['.zip', '.7z', '.tar', '.gz', '.bz2', '.iso', '.cab', '.jar']; + +const extensionMap = { + ai: aiList, + apk: apkList, + audio: audioList, + bak: bakList, + code: codeList, + dmg: dmgList, + doc: docList, + exe: exeList, + font: fontList, + image: imageList, + ipa: ipaList, + keynote: keynoteList, + link: linkList, + mindmap: mindmapList, + note: noteList, + numbers: numbersList, + pages: pagesList, + pdf: pdfList, + pkg: pkgList, + ppt: pptList, + ps: psList, + rar: rarList, + sketch: sketchList, + txt: txtList, + video: videoList, + xls: xlsList, + zip: zipList, +}; // 不全部使用json因为:一个文件解析Form-data会变字符串!!! 但是api文档就写List const SchemaData = Type.Object({ @@ -8,6 +73,8 @@ const SchemaData = Type.Object({ Type.Array(Type.String()), Type.String(), ]), + name: Optional(Type.String()), + thumb_path: Optional(Type.String()), }); type Payload = Static; @@ -16,9 +83,69 @@ export class CreateFlashTask extends OneBotAction { override payloadSchema = SchemaData; async _handle (payload: Payload) { - // todo fileset的名字和缩略图还没实现!! - const fileList = Array.isArray(payload.files) ? payload.files : [payload.files]; + let iconName: string; + const qqPath = process.env['NAPCAT_WRAPPER_PATH'] || ''; - return await this.core.apis.FlashApi.createFlashTransferUploadTask(fileList); + const fileList = Array.isArray(payload.files) ? payload.files : [payload.files]; + let thumbPath: string = ''; + + if (fileList.length === 1) { + // 我是真没hook到那种合并的缩略图是哪个方法产生的,暂时不实现(怀疑是js直接canvas渲染的!!) + const filePath = fileList[0]; + if (filePath === undefined) { + return {}; + } + const ext = path.extname(filePath).toLowerCase(); + + if (richMediaList.includes(ext)) { + try { + const res = await this.core.apis.FlashApi.createFileThumbnail(filePath); + if (res && typeof res === 'object' && 'result' in res && res.result === 0) { + thumbPath = res.targetPath as string; + } + } catch (_e) { + } + } else { + let isDir = false; + + try { + const stat = await fs.promises.stat(filePath); + isDir = stat.isDirectory(); + } catch { + } + + if (isDir) { + iconName = 'folder'; + } else { + iconName = Object.keys(extensionMap).find(key => extensionMap[key].includes(ext)) || 'unknown'; + } + + // const __filename = fileURLToPath(import.meta.url); + // thumbPath = path.join(path.dirname(filePath), 'StaticThumbnail', `${iconName}.png`); // Gemini??? 害我找半天错??? + if (qqPath !== undefined) { + const basicPath = path.dirname(qqPath); + thumbPath = path.join(basicPath, 'resource', 'fileIcon', `${iconName}.png`); + } + } + } else { + iconName = 'multi_files'; + if (qqPath !== undefined) { + const basicPath = path.dirname(qqPath); + thumbPath = path.join(basicPath, 'resource', 'fileIcon', `${iconName}.png`); + } + } + + function toPlatformPath (inputPath: string) { + const unifiedPath = inputPath.replace(/[\\/]/g, path.sep); + return path.normalize(unifiedPath); + } + + let normalPath: string; + if (payload.thumb_path !== undefined) { + normalPath = path.normalize(payload.thumb_path); + } else { + normalPath = toPlatformPath(thumbPath); + } + return await this.core.apis.FlashApi.createFlashTransferUploadTask(fileList, normalPath, payload.name || ''); } } From 562665c8cad253398a6f350d33a182f1fa6ae1f7 Mon Sep 17 00:00:00 2001 From: H3CoF6 <1707889225@qq.com> Date: Sat, 24 Jan 2026 13:59:15 +0800 Subject: [PATCH 2/8] fix: fix get thumbnail path unknown type error --- packages/napcat-core/services/NodeIKernelMsgService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/napcat-core/services/NodeIKernelMsgService.ts b/packages/napcat-core/services/NodeIKernelMsgService.ts index 0fef61f7..8ffc5912 100644 --- a/packages/napcat-core/services/NodeIKernelMsgService.ts +++ b/packages/napcat-core/services/NodeIKernelMsgService.ts @@ -336,7 +336,7 @@ export interface NodeIKernelMsgService { assembleMobileQQRichMediaFilePath (...args: unknown[]): unknown; - getFileThumbSavePathForSend (...args: unknown[]): unknown; + getFileThumbSavePathForSend (arg1: number, arg2: boolean): string; getFileThumbSavePath (...args: unknown[]): unknown; From 04654b6cbf7fb70fb969c58af805613626355c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Sat, 24 Jan 2026 14:25:24 +0800 Subject: [PATCH 3/8] Refactor flash module types and enums Standardized TypeScript interface property formatting in flash.ts, flash data, and wrapper files. Introduced the UploadSceneType enum for upload scene types, replacing hardcoded numeric values. Improved type annotations and consistency across the flash API and related data structures. --- packages/napcat-core/apis/flash.ts | 39 ++--- packages/napcat-core/data/flash.ts | 189 ++++++++++++------------ packages/napcat-core/wrapper.ts | 222 ++++++++++++++--------------- 3 files changed, 232 insertions(+), 218 deletions(-) diff --git a/packages/napcat-core/apis/flash.ts b/packages/napcat-core/apis/flash.ts index 3e5a182d..971529b9 100644 --- a/packages/napcat-core/apis/flash.ts +++ b/packages/napcat-core/apis/flash.ts @@ -4,6 +4,7 @@ import { FileListResponse, FlashFileSetInfo, SendStatus, + UploadSceneType, } from '@/napcat-core/data/flash'; import { Peer } from '@/napcat-core/types'; @@ -22,13 +23,13 @@ export class NTQQFlashApi { * @param thumbnailPath * @param filesetName */ - async createFlashTransferUploadTask (fileListToUpload: string[], thumbnailPath: string, filesetName: string): Promise < GeneralCallResult & { + async createFlashTransferUploadTask (fileListToUpload: string[], thumbnailPath: string, filesetName: string): Promise { + }> { const flashService = this.context.session.getFlashTransferService(); - const timestamp : number = Date.now(); + const timestamp: number = Date.now(); const selfInfo = this.core.selfInfo; console.log(thumbnailPath); @@ -54,7 +55,7 @@ export class NTQQFlashApi { thumbnailPath, }, ], - uploadSceneType: 10, + uploadSceneType: UploadSceneType.KUPLOADSCENEAIOFILESELECTOR, // 不知道怎么枚举 先硬编码吧 (PC QQ 10) detectPrivacyInfoResult: { exists: false, allDetectResults: new Map(), @@ -75,9 +76,9 @@ export class NTQQFlashApi { * 下载闪传文件集 * @param fileSetId */ - async downloadFileSetBySetId (fileSetId: string): Promise < GeneralCallResult & { - extraInfo: unknown - } > { + async downloadFileSetBySetId (fileSetId: string): Promise { const flashService = this.context.session.getFlashTransferService(); const result = await flashService.startFileSetDownload(fileSetId, 1, { isIncludeCompressInnerFiles: false }); // 为了方便,暂时硬编码 @@ -93,7 +94,7 @@ export class NTQQFlashApi { * 获取闪传的外链分享 * @param fileSetId */ - async getShareLinkBySetId (fileSetId: string): Promise < GeneralCallResult & { + async getShareLinkBySetId (fileSetId: string): Promise { @@ -112,9 +113,9 @@ export class NTQQFlashApi { * 从分享外链获取文件集id * @param shareCode */ - async fromShareLinkFindSetId (shareCode: string): Promise < GeneralCallResult & { + async fromShareLinkFindSetId (shareCode: string): Promise { + }> { const flashService = this.context.session.getFlashTransferService(); const result = await flashService.getFileSetIdByCode(shareCode); @@ -131,7 +132,7 @@ export class NTQQFlashApi { * == 注意返回结构和其它的不同,没有GeneralCallResult!!! == * @param fileSetId */ - async getFileListBySetId (fileSetId: string): Promise < FileListResponse > { + async getFileListBySetId (fileSetId: string): Promise { const flashService = this.context.session.getFlashTransferService(); const requestArg = { @@ -174,11 +175,11 @@ export class NTQQFlashApi { * 获取闪传文件集合信息 * @param fileSetId */ - async getFileSetIndoBySetId (fileSetId: string): Promise < GeneralCallResult & { + async getFileSetIndoBySetId (fileSetId: string): Promise { + }> { const flashService = this.context.session.getFlashTransferService(); const requestArg = { @@ -199,13 +200,13 @@ export class NTQQFlashApi { * @param fileSetId * @param peer */ - async sendFlashMessage (fileSetId: string, peer:Peer): Promise < { + async sendFlashMessage (fileSetId: string, peer: Peer): Promise<{ errCode: number, errMsg: string, rsp: { - sendStatus: SendStatus[] - } - } > { + sendStatus: SendStatus[]; + }; + }> { const flashService = this.context.session.getFlashTransferService(); const target = { @@ -233,9 +234,9 @@ export class NTQQFlashApi { * @param fileSetId * @param options */ - async getFileTransUrl (fileSetId: string, options: { fileName?: string; fileIndex?: number }): Promise < GeneralCallResult & { + async getFileTransUrl (fileSetId: string, options: { fileName?: string; fileIndex?: number; }): Promise { + }> { const flashService = this.context.session.getFlashTransferService(); const result = await this.getFileListBySetId(fileSetId); diff --git a/packages/napcat-core/data/flash.ts b/packages/napcat-core/data/flash.ts index 525b4bb7..575fcbfd 100644 --- a/packages/napcat-core/data/flash.ts +++ b/packages/napcat-core/data/flash.ts @@ -1,5 +1,5 @@ export interface FlashBaseRequest { - fileSetId: string + fileSetId: string; } export interface UploaderInfo { @@ -19,14 +19,14 @@ export interface thumbnailInfo { } export interface SendTarget { - destType: number // 1私聊 + destType: number; // 1私聊 destUin?: string, destUid: string, } export interface SendTargetRequests { - fileSetId: string - targets: SendTarget[] + fileSetId: string; + targets: SendTarget[]; } export interface DownloadStatusInfo { @@ -59,7 +59,7 @@ export interface DownloadStatusInfo { downloadTaskId: string, downloadFilesetName: string, downloadFileTypeDistribution: string, - downloadFileSizeDistribution: string + downloadFileSizeDistribution: string; }, albumStorageFailImageNum: number, albumStorageFailVideoNum: number, @@ -67,8 +67,8 @@ export interface DownloadStatusInfo { albumStorageSucImageNum: number, albumStorageSucVideoNum: number, albumStorageSucFileIdList: [], - albumStorageFileNum: number - } + albumStorageFileNum: number; + }; } export interface physicalInfo { @@ -95,94 +95,94 @@ export interface uploadInfo { svrRrrCode: number, errMsg: string, isNeedDelDeviceInfo: boolean, - thumbnailUploadState: number + thumbnailUploadState: number; isSecondHit: boolean, hasModifiedErr: boolean, } export interface folderUploadInfo { - totalUploadedFileSize: string - successCount: number - failedCount: number + totalUploadedFileSize: string; + successCount: number; + failedCount: number; } export interface folderDownloadInfo { - totalDownloadedFileSize: string - totalFileSize: string - totalDownloadFileCount: number - successCount: number - failedCount: number - pausedCount: number - cancelCount: number - downloadingCount: number - partialDownloadCount: number - curLevelDownloadedFileCount: number - curLevelUnDownloadedFileCount: number + totalDownloadedFileSize: string; + totalFileSize: string; + totalDownloadFileCount: number; + successCount: number; + failedCount: number; + pausedCount: number; + cancelCount: number; + downloadingCount: number; + partialDownloadCount: number; + curLevelDownloadedFileCount: number; + curLevelUnDownloadedFileCount: number; } export interface compressFileFolderInfo { - downloadStatus: number - saveFileDirPath: string - totalFileCount: string - totalFileSize: string + downloadStatus: number; + saveFileDirPath: string; + totalFileCount: string; + totalFileSize: string; } export interface albumStorgeInfo { - status: number - localIdentifier: string - errorCode: number - timeCost: number + status: number; + localIdentifier: string; + errorCode: number; + timeCost: number; } export interface FlashOneFileInfo { - fileSetId: string - cliFileId: string // client?? 或许可以换取url - compressedFileFolderId: string - archiveIndex: 0 - indexPath: string - isDir: boolean // 文件或者文件夹!! - parentId: string - depth: number // 1 - cliFileIndex: number - fileType: number // 枚举!! 已完成枚举!! - name: string - namePinyin: string - isCover: boolean - isCoverOriginal: boolean - fileSize: string - fileCount: number - thumbnail: thumbnailInfo - physical: physicalInfo - srvFileId: string // service?? 服务器上面的id吗? - srvParentFileId: string - svrLastUpdateTimestamp: string - downloadInfo: downloadInfo - saveFilePath: string - search_relative_path: string - disk_relative_path: string - uploadInfo: uploadInfo - status: number - uploadStatus: number // 3已上传成功 - downloadStatus: number // 0未下载 - folderUploadInfo: folderUploadInfo - folderDownloadInfo: folderDownloadInfo - sha1: string - bookmark: string - compressFileFolderInfo: compressFileFolderInfo - uploadPauseReason: string - downloadPauseReason: string - filePhysicalSize: string - thumbnail_sha1: string | null - thumbnail_size: string | null - needAlbumStorage: boolean - albumStorageInfo: albumStorgeInfo + fileSetId: string; + cliFileId: string; // client?? 或许可以换取url + compressedFileFolderId: string; + archiveIndex: 0; + indexPath: string; + isDir: boolean; // 文件或者文件夹!! + parentId: string; + depth: number; // 1 + cliFileIndex: number; + fileType: number; // 枚举!! 已完成枚举!! + name: string; + namePinyin: string; + isCover: boolean; + isCoverOriginal: boolean; + fileSize: string; + fileCount: number; + thumbnail: thumbnailInfo; + physical: physicalInfo; + srvFileId: string; // service?? 服务器上面的id吗? + srvParentFileId: string; + svrLastUpdateTimestamp: string; + downloadInfo: downloadInfo; + saveFilePath: string; + search_relative_path: string; + disk_relative_path: string; + uploadInfo: uploadInfo; + status: number; + uploadStatus: number; // 3已上传成功 + downloadStatus: number; // 0未下载 + folderUploadInfo: folderUploadInfo; + folderDownloadInfo: folderDownloadInfo; + sha1: string; + bookmark: string; + compressFileFolderInfo: compressFileFolderInfo; + uploadPauseReason: string; + downloadPauseReason: string; + filePhysicalSize: string; + thumbnail_sha1: string | null; + thumbnail_size: string | null; + needAlbumStorage: boolean; + albumStorageInfo: albumStorgeInfo; } export interface fileListsInfo { parentId: string, depth: number, // 1 fileList: FlashOneFileInfo[], - paginationInfo: {} + paginationInfo: {}; isEnd: boolean, isCache: boolean, } @@ -200,7 +200,20 @@ export interface createFlashTransferResult { expireTime: string, expireLeftTime: string, } - +export enum UploadSceneType { + KUPLOADSCENEUNKNOWN, + KUPLOADSCENEFLOATWINDOWRIGHTCLICKMENU, + KUPLOADSCENEFLOATWINDOWDRAG, + KUPLOADSCENEFLOATWINDOWFILESELECTOR, + KUPLOADSCENEFLOATWINDOWSHORTCUTKEYCTRLCV, + KUPLOADSCENEH5LAUNCHCLIENTRIGHTCLICKMENU, + KUPLOADSCENEH5LAUNCHCLIENTDRAG, + KUPLOADSCENEH5LAUNCHCLIENTFILESELECTOR, + KUPLOADSCENEH5LAUNCHCLIENTSHORTCUTKEYCTRLCV, + KUPLOADSCENEAIODRAG, + KUPLOADSCENEAIOFILESELECTOR, + KUPLOADSCENEAIOSHORTCUTKEYCTRLCV +} export interface StartFlashTaskRequests { screen: number; // 1 PC-QQ name?: string; @@ -208,7 +221,7 @@ export interface StartFlashTaskRequests { permission?: {}; coverPath?: string; paths: string[]; // 文件的绝对路径,可以是文件夹 - excludePaths?: any[]; + excludePaths?: string[]; expireLeftTime?: number, // 0 isNeedDelDeviceInfo: boolean, isNeedDelLocation: boolean, @@ -216,11 +229,11 @@ export interface StartFlashTaskRequests { path: string, thumbnailPath: string, }[], - uploadSceneType: number, // 不知道怎么枚举 先硬编码吧 (PC QQ 10) + uploadSceneType: UploadSceneType, // 不知道怎么枚举 先硬编码吧 (PC QQ 10) detectPrivacyInfoResult: { exists: boolean, - allDetectResults: {} - } + allDetectResults: {}; + }; } export interface FileListInfoRequests { @@ -242,8 +255,8 @@ export interface FileListInfoRequests { sortField: number, sortOrder: number, }[], - isNeedPhysicalInfoReady: boolean - }[] + isNeedPhysicalInfoReady: boolean; + }[]; } export interface FlashFileSetInfo { @@ -262,23 +275,23 @@ export interface FlashFileSetInfo { urls: [ { spec: number, // 2 - url: string + url: string; } ], - localCachePath: string + localCachePath: string; }, uploaders: [ { uin: string, nickname: string, uid: string, - sendEntrance: string + sendEntrance: string; } ], expireLeftTime: number, aiClusteringStatus: { firstClusteringList: [], - shouldPull: boolean + shouldPull: boolean; }, createTime: number, expireTime: number, @@ -288,7 +301,7 @@ export interface FlashFileSetInfo { uploadInfo: { totalUploadedFileSize: number, successCount: number, - failedCount: number + failedCount: number; }, downloadInfo: { totalDownloadedFileSize: 0, @@ -300,7 +313,7 @@ export interface FlashFileSetInfo { cancelCount: 0, status: 0, curLevelDownloadedFileCount: number, - curLevelUnDownloadedFileCount: 0 + curLevelUnDownloadedFileCount: 0; }, transferType: number, isLocalCreate: true, @@ -310,12 +323,12 @@ export interface FlashFileSetInfo { downloadStatus: 0, downloadPauseReason: 0, saveFileSetDir: string, - uploadSceneType: 10, + uploadSceneType: UploadSceneType, downloadSceneType: 0, // 0 PC-QQ 103 web retryCount: number, isMergeShareUpload: 0, isRemoveDeviceInfo: boolean, - isRemoveLocation: boolean + isRemoveLocation: boolean; } export interface SendStatus { @@ -324,5 +337,5 @@ export interface SendStatus { target: { destType: number, destUid: string, - } + }; } diff --git a/packages/napcat-core/wrapper.ts b/packages/napcat-core/wrapper.ts index a0822954..d3dd2b5e 100644 --- a/packages/napcat-core/wrapper.ts +++ b/packages/napcat-core/wrapper.ts @@ -31,75 +31,75 @@ import { NodeIO3MiscService } from './services/NodeIO3MiscService'; import { NodeIKernelFlashTransferService } from './services/NodeIKernelFlashTransferService'; export interface NodeQQNTWrapperUtil { - get(): NodeQQNTWrapperUtil; + get (): NodeQQNTWrapperUtil; - getNTUserDataInfoConfig(): string; + getNTUserDataInfoConfig (): string; - emptyWorkingSet(n: number): void; + emptyWorkingSet (n: number): void; - getSsoCmdOfOidbReq(arg1: number, arg2: number): unknown; + getSsoCmdOfOidbReq (arg1: number, arg2: number): unknown; - getSsoBufferOfOidbReq(...args: unknown[]): unknown; // 有点看不懂参数定义 待补充 好像是三个参数 + getSsoBufferOfOidbReq (...args: unknown[]): unknown; // 有点看不懂参数定义 待补充 好像是三个参数 - getOidbRspInfo(arg: string): unknown; // 可能是错的 + getOidbRspInfo (arg: string): unknown; // 可能是错的 - getFileSize(path: string): Promise; // 直接的猜测 + getFileSize (path: string): Promise; // 直接的猜测 - genFileMd5Buf(arg: string): unknown; // 可能是错的 + genFileMd5Buf (arg: string): unknown; // 可能是错的 - genFileMd5Hex(path: string): unknown; // 直接的猜测 + genFileMd5Hex (path: string): unknown; // 直接的猜测 - genFileShaBuf(path: string): unknown; // 直接的猜测 + genFileShaBuf (path: string): unknown; // 直接的猜测 - genFileCumulateSha1(path: string): unknown; // 直接的猜测 + genFileCumulateSha1 (path: string): unknown; // 直接的猜测 - genFileShaHex(path: string): unknown; // 直接的猜测 + genFileShaHex (path: string): unknown; // 直接的猜测 - fileIsExist(path: string): unknown; + fileIsExist (path: string): unknown; - startTrace(path: string): unknown; // 可能是错的 + startTrace (path: string): unknown; // 可能是错的 - copyFile(src: string, dst: string): unknown; + copyFile (src: string, dst: string): unknown; - genFileShaAndMd5Hex(path: string, unknown: number): unknown; // 可能是错的 + genFileShaAndMd5Hex (path: string, unknown: number): unknown; // 可能是错的 - setTraceInfo(unknown: unknown): unknown; + setTraceInfo (unknown: unknown): unknown; - encodeOffLine(unknown: unknown): unknown; + encodeOffLine (unknown: unknown): unknown; - decodeOffLine(arg: string): unknown; // 可能是错的 传递hex + decodeOffLine (arg: string): unknown; // 可能是错的 传递hex - DecoderRecentInfo(arg: string): unknown; // 可能是错的 传递hex + DecoderRecentInfo (arg: string): unknown; // 可能是错的 传递hex - getPinyin(arg0: string, arg1: boolean): unknown; + getPinyin (arg0: string, arg1: boolean): unknown; - matchInPinyin(arg0: unknown[], arg1: string): unknown; // 参数特复杂 arg0是个复杂数据类型 + matchInPinyin (arg0: unknown[], arg1: string): unknown; // 参数特复杂 arg0是个复杂数据类型 - makeDirByPath(arg0: string): unknown; + makeDirByPath (arg0: string): unknown; - emptyWorkingSet(arg0: number): unknown; // 参数是UINT32 + emptyWorkingSet (arg0: number): unknown; // 参数是UINT32 - runProcess(arg0: string, arg1: boolean): unknown; + runProcess (arg0: string, arg1: boolean): unknown; - runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown; + runProcessArgs (arg0: string, arg1: { [key: string]: string; }, arg2: boolean): unknown; - calcThumbSize(arg0: number, arg1: number, arg2: unknown): unknown; + calcThumbSize (arg0: number, arg1: number, arg2: unknown): unknown; - fullWordToHalfWord(word: string): unknown; + fullWordToHalfWord (word: string): unknown; - getNTUserDataInfoConfig(): unknown; + getNTUserDataInfoConfig (): unknown; - pathIsReadableAndWriteable(path: string): unknown; // 直接的猜测 + pathIsReadableAndWriteable (path: string): unknown; // 直接的猜测 - resetUserDataSavePathToDocument(): unknown; + resetUserDataSavePathToDocument (): unknown; - getSoBuildInfo(): unknown; // 例如 0[0]_d491dc01e0a_0 + getSoBuildInfo (): unknown; // 例如 0[0]_d491dc01e0a_0 - registerCountInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown; + registerCountInstruments (arg0: string, arg1: string[], arg2: number, arg3: number): unknown; - registerValueInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown; + registerValueInstruments (arg0: string, arg1: string[], arg2: number, arg3: number): unknown; - registerValueInstrumentsWithBoundary( + registerValueInstrumentsWithBoundary ( arg0: string, arg1: unknown, arg2: unknown, @@ -107,7 +107,7 @@ export interface NodeQQNTWrapperUtil { arg4: number, ): unknown; - reportCountIndicators( + reportCountIndicators ( arg0: string, arg1: Map, arg2: string, @@ -115,7 +115,7 @@ export interface NodeQQNTWrapperUtil { arg4: boolean, ): unknown; - reportValueIndicators( + reportValueIndicators ( arg0: string, arg1: Map, arg2: string, @@ -123,154 +123,154 @@ export interface NodeQQNTWrapperUtil { arg4: number, ): unknown; - checkNewUserDataSaveDirAvailable(arg0: string): unknown; + checkNewUserDataSaveDirAvailable (arg0: string): unknown; - copyUserData(arg0: string, arg1: string): Promise; + copyUserData (arg0: string, arg1: string): Promise; - setUserDataSaveDirectory(arg0: string): Promise; + setUserDataSaveDirectory (arg0: string): Promise; - hasOtherRunningQQProcess(): boolean; + hasOtherRunningQQProcess (): boolean; - quitAllRunningQQProcess(arg: boolean): unknown; + quitAllRunningQQProcess (arg: boolean): unknown; - checkNvidiaConfig(): unknown; + checkNvidiaConfig (): unknown; - repairNvidiaConfig(): unknown; + repairNvidiaConfig (): unknown; - getNvidiaDriverVersion(): unknown; + getNvidiaDriverVersion (): unknown; - isNull(): unknown; + isNull (): unknown; - createThumbnailImage( + createThumbnailImage ( serviceName: string, filePath: string, targetPath: string, imgSize: { width: number, - height: number + height: number; }, fileFormat: string, - arg: unknown, // null - ): Promise < GeneralCallResult & { targetPath?: string } >; + arg?: number, // null undefined都行 + ): Promise; } export interface NodeIQQNTStartupSessionWrapper { - create(): NodeIQQNTStartupSessionWrapper; - stop(): void; - start(): void; - createWithModuleList(uk: unknown): unknown; - getSessionIdList(): unknown; + create (): NodeIQQNTStartupSessionWrapper; + stop (): void; + start (): void; + createWithModuleList (uk: unknown): unknown; + getSessionIdList (): unknown; } export interface NodeIQQNTWrapperSession { - getNTWrapperSession(str: string): NodeIQQNTWrapperSession; + getNTWrapperSession (str: string): NodeIQQNTWrapperSession; - get(): NodeIQQNTWrapperSession; + get (): NodeIQQNTWrapperSession; new(): NodeIQQNTWrapperSession; - create(): NodeIQQNTWrapperSession; + create (): NodeIQQNTWrapperSession; - init( + init ( wrapperSessionInitConfig: WrapperSessionInitConfig, nodeIDependsAdapter: NodeIDependsAdapter, nodeIDispatcherAdapter: NodeIDispatcherAdapter, nodeIKernelSessionListener: NodeIKernelSessionListener, ): void; - startNT(session: number): void; + startNT (session: number): void; - startNT(): void; + startNT (): void; - getBdhUploadService(): unknown; + getBdhUploadService (): unknown; - getECDHService(): NodeIKernelECDHService; + getECDHService (): NodeIKernelECDHService; - getMsgService(): NodeIKernelMsgService; + getMsgService (): NodeIKernelMsgService; - getProfileService(): NodeIKernelProfileService; + getProfileService (): NodeIKernelProfileService; - getProfileLikeService(): NodeIKernelProfileLikeService; + getProfileLikeService (): NodeIKernelProfileLikeService; - getGroupService(): NodeIKernelGroupService; + getGroupService (): NodeIKernelGroupService; - getStorageCleanService(): NodeIKernelStorageCleanService; + getStorageCleanService (): NodeIKernelStorageCleanService; - getBuddyService(): NodeIKernelBuddyService; + getBuddyService (): NodeIKernelBuddyService; - getRobotService(): NodeIKernelRobotService; + getRobotService (): NodeIKernelRobotService; - getTicketService(): NodeIKernelTicketService; + getTicketService (): NodeIKernelTicketService; - getTipOffService(): NodeIKernelTipOffService; + getTipOffService (): NodeIKernelTipOffService; - getNodeMiscService(): NodeIKernelNodeMiscService; + getNodeMiscService (): NodeIKernelNodeMiscService; - getRichMediaService(): NodeIKernelRichMediaService; + getRichMediaService (): NodeIKernelRichMediaService; - getMsgBackupService(): NodeIKernelMsgBackupService; + getMsgBackupService (): NodeIKernelMsgBackupService; - getAlbumService(): NodeIKernelAlbumService; + getAlbumService (): NodeIKernelAlbumService; - getTianShuService(): NodeIKernelTianShuService; + getTianShuService (): NodeIKernelTianShuService; - getUnitedConfigService(): NodeIKernelUnitedConfigService; + getUnitedConfigService (): NodeIKernelUnitedConfigService; - getSearchService(): NodeIKernelSearchService; + getSearchService (): NodeIKernelSearchService; - getFlashTransferService(): NodeIKernelFlashTransferService; + getFlashTransferService (): NodeIKernelFlashTransferService; - getDirectSessionService(): unknown; + getDirectSessionService (): unknown; - getRDeliveryService(): unknown; + getRDeliveryService (): unknown; - getAvatarService(): NodeIKernelAvatarService; + getAvatarService (): NodeIKernelAvatarService; - getFeedChannelService(): unknown; + getFeedChannelService (): unknown; - getYellowFaceService(): unknown; + getYellowFaceService (): unknown; - getCollectionService(): NodeIKernelCollectionService; + getCollectionService (): NodeIKernelCollectionService; - getSettingService(): unknown; + getSettingService (): unknown; - getQiDianService(): unknown; + getQiDianService (): unknown; - getFileAssistantService(): unknown; + getFileAssistantService (): unknown; - getGuildService(): unknown; + getGuildService (): unknown; - getSkinService(): unknown; + getSkinService (): unknown; - getTestPerformanceService(): NodeIkernelTestPerformanceService; + getTestPerformanceService (): NodeIkernelTestPerformanceService; - getQQPlayService(): unknown; + getQQPlayService (): unknown; - getDbToolsService(): unknown; + getDbToolsService (): unknown; - getUixConvertService(): NodeIKernelUixConvertService; + getUixConvertService (): NodeIKernelUixConvertService; - getOnlineStatusService(): unknown; + getOnlineStatusService (): unknown; - getRemotingService(): unknown; + getRemotingService (): unknown; - getGroupTabService(): unknown; + getGroupTabService (): unknown; - getGroupSchoolService(): unknown; + getGroupSchoolService (): unknown; - getLiteBusinessService(): unknown; + getLiteBusinessService (): unknown; - getGuildMsgService(): unknown; + getGuildMsgService (): unknown; - getLockService(): unknown; + getLockService (): unknown; - getMSFService(): NodeIKernelMSFService; + getMSFService (): NodeIKernelMSFService; - getGuildHotUpdateService(): unknown; + getGuildHotUpdateService (): unknown; - getAVSDKService(): unknown; + getAVSDKService (): unknown; - getRecentContactService(): NodeIKernelRecentContactService; + getRecentContactService (): NodeIKernelRecentContactService; - getConfigMgrService(): unknown; + getConfigMgrService (): unknown; } export interface EnginInitDesktopConfig { @@ -284,20 +284,20 @@ export interface EnginInitDesktopConfig { global_path_config: { desktopGlobalPath: string; }; - thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2 }; + thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2; }; } export interface NodeIQQNTWrapperEngine { - get(): NodeIQQNTWrapperEngine; + get (): NodeIQQNTWrapperEngine; - initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void; + initWithDeskTopConfig (config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void; } export interface WrapperNodeApi { NodeIO3MiscService: NodeIO3MiscService; NodeQQNTWrapperUtil: NodeQQNTWrapperUtil; NodeIQQNTWrapperSession: NodeIQQNTWrapperSession; - NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapper + NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapper; NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine; NodeIKernelLoginService: NodeIKernelLoginService; From 88260d5f4a8afa16d0aeec5718605f79ce279649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Sat, 24 Jan 2026 14:26:28 +0800 Subject: [PATCH 4/8] Update arg type in NodeQQNTWrapperUtil interface Changed the type of the 'arg' parameter in the NodeQQNTWrapperUtil interface from optional number to 'number | null | undefined' for improved type clarity. --- packages/napcat-core/wrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/napcat-core/wrapper.ts b/packages/napcat-core/wrapper.ts index d3dd2b5e..9f499f27 100644 --- a/packages/napcat-core/wrapper.ts +++ b/packages/napcat-core/wrapper.ts @@ -150,7 +150,7 @@ export interface NodeQQNTWrapperUtil { height: number; }, fileFormat: string, - arg?: number, // null undefined都行 + arg: number | null | undefined, // null undefined都行 ): Promise; } export interface NodeIQQNTStartupSessionWrapper { From 0dec0c471b67c98cdb3e4de1f897daed76e4ebe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Sat, 24 Jan 2026 14:32:45 +0800 Subject: [PATCH 5/8] Refactor flash scene type and update method params Introduced BusiScene enum for sceneType in FileListInfoRequests to improve type safety. Renamed parameters in getFileThumbSavePathForSend for better clarity. --- packages/napcat-core/data/flash.ts | 7 +++++-- packages/napcat-core/services/NodeIKernelMsgService.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/napcat-core/data/flash.ts b/packages/napcat-core/data/flash.ts index 575fcbfd..b829b74c 100644 --- a/packages/napcat-core/data/flash.ts +++ b/packages/napcat-core/data/flash.ts @@ -235,12 +235,15 @@ export interface StartFlashTaskRequests { allDetectResults: {}; }; } - +export enum BusiScene { + KBUSISCENEINVALID, + KBUSISCENEFLASHSCENE +} export interface FileListInfoRequests { seq: number, // 0 fileSetId: string, isUseCache: boolean, - sceneType: number, // 1 + sceneType: BusiScene, // 1 reqInfos: { count: number, // 18 ?? 硬编码吧 不懂 paginationInfo: {}, diff --git a/packages/napcat-core/services/NodeIKernelMsgService.ts b/packages/napcat-core/services/NodeIKernelMsgService.ts index 8ffc5912..3a33b799 100644 --- a/packages/napcat-core/services/NodeIKernelMsgService.ts +++ b/packages/napcat-core/services/NodeIKernelMsgService.ts @@ -336,7 +336,7 @@ export interface NodeIKernelMsgService { assembleMobileQQRichMediaFilePath (...args: unknown[]): unknown; - getFileThumbSavePathForSend (arg1: number, arg2: boolean): string; + getFileThumbSavePathForSend (thumbSize: number, createNeed: boolean): string; getFileThumbSavePath (...args: unknown[]): unknown; From 5939e613d479d4bbbe2a4dcee899cdb6f84f1887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Sat, 24 Jan 2026 14:40:08 +0800 Subject: [PATCH 6/8] Refactor downloadSceneType to use enum type Replaced numeric downloadSceneType fields with the DownloadSceneType enum in relevant interfaces. Updated NodeIKernelFlashTransferService method signatures to use DownloadSceneType for download operations, improving type safety and code clarity. --- packages/napcat-core/data/flash.ts | 20 +- .../NodeIKernelFlashTransferService.ts | 241 +++++++++--------- 2 files changed, 138 insertions(+), 123 deletions(-) diff --git a/packages/napcat-core/data/flash.ts b/packages/napcat-core/data/flash.ts index b829b74c..83e5e31d 100644 --- a/packages/napcat-core/data/flash.ts +++ b/packages/napcat-core/data/flash.ts @@ -53,7 +53,7 @@ export interface DownloadStatusInfo { isAllFileAlreadyDownloaded: boolean, saveFileSetDir: string, allWaitingStatusTask: boolean, - downloadSceneType: number, + downloadSceneType: DownloadSceneType, retryCount: number, statisticInfo: { downloadTaskId: string, @@ -261,7 +261,21 @@ export interface FileListInfoRequests { isNeedPhysicalInfoReady: boolean; }[]; } - +export enum DownloadSceneType { + KDOWNLOADSCENEUNKNOWN, + KDOWNLOADSCENEARKC2C, + KDOWNLOADSCENEARKC2CDETAILPAGE, + KDOWNLOADSCENEARKGROUP, + KDOWNLOADSCENEARKGROUPDETAILPAGE, + KDOWNLOADSCENELINKC2C, + KDOWNLOADSCENELINKGROUP, + KDOWNLOADSCENELINKCHANNEL, + KDOWNLOADSCENELINKTEMPCHAT, + KDOWNLOADSCENELINKOTHERINQQ, + KDOWNLOADSCENESCANQRCODE, + KDWONLOADSCENEFLASHTRANSFERCENTERCLIENT, + KDWONLOADSCENEFLASHTRANSFERCENTERSCHEMA +} export interface FlashFileSetInfo { fileSetId: string, name: string, @@ -327,7 +341,7 @@ export interface FlashFileSetInfo { downloadPauseReason: 0, saveFileSetDir: string, uploadSceneType: UploadSceneType, - downloadSceneType: 0, // 0 PC-QQ 103 web + downloadSceneType: DownloadSceneType, // 0 PC-QQ 103 web retryCount: number, isMergeShareUpload: 0, isRemoveDeviceInfo: boolean, diff --git a/packages/napcat-core/services/NodeIKernelFlashTransferService.ts b/packages/napcat-core/services/NodeIKernelFlashTransferService.ts index e7c687ad..c0c92bd7 100644 --- a/packages/napcat-core/services/NodeIKernelFlashTransferService.ts +++ b/packages/napcat-core/services/NodeIKernelFlashTransferService.ts @@ -10,6 +10,7 @@ import { DownloadStatusInfo, SendTargetRequests, FlashOneFileInfo, + DownloadSceneType, } from '../data/flash'; export interface NodeIKernelFlashTransferService { @@ -18,48 +19,48 @@ export interface NodeIKernelFlashTransferService { * @param timestamp * @param fileInfo */ - createFlashTransferUploadTask(timestamp: number, fileInfo: StartFlashTaskRequests): Promise < GeneralCallResult & { + createFlashTransferUploadTask (timestamp: number, fileInfo: StartFlashTaskRequests): Promise; // 2 arg 重点 // 自动上传 + }>; // 2 arg 重点 // 自动上传 - createMergeShareTask(...args: unknown[]): unknown; // 2 arg + createMergeShareTask (...args: unknown[]): unknown; // 2 arg - updateFlashTransfer(...args: unknown[]): unknown; // 2 arg + updateFlashTransfer (...args: unknown[]): unknown; // 2 arg - getFileSetList(...args: unknown[]): unknown; // 1 arg + getFileSetList (...args: unknown[]): unknown; // 1 arg - getFileSetListCount(...args: unknown[]): unknown; // 1 arg + getFileSetListCount (...args: unknown[]): unknown; // 1 arg /** * 获取file set 的信息 * @param fileSetIdDict */ - getFileSet(fileSetIdDict: FlashBaseRequest): Promise < GeneralCallResult & { + getFileSet (fileSetIdDict: FlashBaseRequest): Promise; // 1 arg + }>; // 1 arg /** * 获取file set 里面的文件信息(文件夹结构) * @param requestArgs */ - getFileList(requestArgs: FileListInfoRequests): Promise < { + getFileList (requestArgs: FileListInfoRequests): Promise<{ rsp: FileListResponse; - } > ; // 1 arg 这个方法QQ有bug??? 并没有,是我参数有问题 + }>; // 1 arg 这个方法QQ有bug??? 并没有,是我参数有问题 - getDownloadedFileCount(...args: unknown[]): unknown; // 1 arg + getDownloadedFileCount (...args: unknown[]): unknown; // 1 arg - getLocalFileList(...args: unknown[]): unknown; // 3 arg + getLocalFileList (...args: unknown[]): unknown; // 3 arg - batchRemoveUserFileSetHistory(...args: unknown[]): unknown; // 1 arg + batchRemoveUserFileSetHistory (...args: unknown[]): unknown; // 1 arg /** * 获取分享链接 * @param fileSetId */ - getShareLinkReq(fileSetId:string): Promise< GeneralCallResult & { + getShareLinkReq (fileSetId: string): Promise; @@ -68,235 +69,235 @@ export interface NodeIKernelFlashTransferService { * 由分享链接到fileSetId * @param shareCode */ - getFileSetIdByCode(shareCode: string): Promise < GeneralCallResult & { + getFileSetIdByCode (shareCode: string): Promise ; // 1 arg code == share code + }>; // 1 arg code == share code - batchRemoveFile(...args: unknown[]): unknown; // 1 arg + batchRemoveFile (...args: unknown[]): unknown; // 1 arg - checkUploadPathValid(...args: unknown[]): unknown; // 1 arg + checkUploadPathValid (...args: unknown[]): unknown; // 1 arg - cleanFailedFiles(...args: unknown[]): unknown; // 2 arg + cleanFailedFiles (...args: unknown[]): unknown; // 2 arg /** * 暂停所有的任务 */ - resumeAllUnfinishedTasks(): unknown; // 0 arg !! + resumeAllUnfinishedTasks (): unknown; // 0 arg !! - addFileSetUploadListener(...args: unknown[]): unknown; // 1 arg + addFileSetUploadListener (...args: unknown[]): unknown; // 1 arg - removeFileSetUploadListener(...args: unknown[]): unknown; // 1 arg + removeFileSetUploadListener (...args: unknown[]): unknown; // 1 arg /** * 开始上传任务 适用于已暂停的 * @param fileSetId */ - startFileSetUpload(fileSetId: string): void; // 1 arg 并不是新建任务,应该是暂停后的启动 + startFileSetUpload (fileSetId: string): void; // 1 arg 并不是新建任务,应该是暂停后的启动 /** * 结束,无法再次启动 * @param fileSetId */ - stopFileSetUpload(fileSetId: string): void; // 1 arg stop 后start无效 + stopFileSetUpload (fileSetId: string): void; // 1 arg stop 后start无效 /** * 暂停上传 * @param fileSetId */ - pauseFileSetUpload(fileSetId: string): void; // 1 arg 暂停上传 + pauseFileSetUpload (fileSetId: string): void; // 1 arg 暂停上传 /** * 继续上传 * @param args */ - resumeFileSetUpload(...args: unknown[]): unknown; // 1 arg 继续 + resumeFileSetUpload (...args: unknown[]): unknown; // 1 arg 继续 - pauseFileUpload(...args: unknown[]): unknown; // 1 arg + pauseFileUpload (...args: unknown[]): unknown; // 1 arg - resumeFileUpload(...args: unknown[]): unknown; // 1 arg + resumeFileUpload (...args: unknown[]): unknown; // 1 arg - stopFileUpload(...args: unknown[]): unknown; // 1 arg + stopFileUpload (...args: unknown[]): unknown; // 1 arg - asyncGetThumbnailPath(...args: unknown[]): unknown; // 2 arg + asyncGetThumbnailPath (...args: unknown[]): unknown; // 2 arg - setDownLoadDefaultFileDir(...args: unknown[]): unknown; // 1 arg + setDownLoadDefaultFileDir (...args: unknown[]): unknown; // 1 arg - setFileSetDownloadDir(...args: unknown[]): unknown; // 2 arg + setFileSetDownloadDir (...args: unknown[]): unknown; // 2 arg - getFileSetDownloadDir(...args: unknown[]): unknown; // 1 arg + getFileSetDownloadDir (...args: unknown[]): unknown; // 1 arg - setFlashTransferDir(...args: unknown[]): unknown; // 2 arg + setFlashTransferDir (...args: unknown[]): unknown; // 2 arg - addFileSetDownloadListener(...args: unknown[]): unknown; // 1 arg + addFileSetDownloadListener (...args: unknown[]): unknown; // 1 arg - removeFileSetDownloadListener(...args: unknown[]): unknown; // 1 arg + removeFileSetDownloadListener (...args: unknown[]): unknown; // 1 arg /** * 开始下载file set的函数 同开始上传 * @param fileSetId - * @param chatType 聊天类型 //因为没有peer,其实可以硬编码为1 (好友私聊) + * @param downloadSceneType 下载类型 //因为没有peer,其实可以硬编码为1 (好友私聊) * @param arg // 默认为false */ - startFileSetDownload(fileSetId:string, chatType: number, arg: { isIncludeCompressInnerFiles: boolean }): Promise < GeneralCallResult & { - extraInfo: 0 - } >; // 3 arg + startFileSetDownload (fileSetId: string, downloadSceneType: DownloadSceneType, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise; // 3 arg - stopFileSetDownload(fileSetId: string, arg1: { isIncludeCompressInnerFiles: boolean }): Promise < GeneralCallResult & { - extraInfo: 0 - } > ; // 2 arg 结束不可重启!! + stopFileSetDownload (fileSetId: string, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise; // 2 arg 结束不可重启!! - pauseFileSetDownload(fileSetId: string, arg1: { isIncludeCompressInnerFiles: boolean }): Promise < GeneralCallResult & { - extraInfo: 0 - } > ; // 2 arg + pauseFileSetDownload (fileSetId: string, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise; // 2 arg - resumeFileSetDownload(fileSetId: string, arg1: { isIncludeCompressInnerFiles: boolean }): Promise < GeneralCallResult & { - extraInfo: 0 - } > ; // 2 arg + resumeFileSetDownload (fileSetId: string, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise; // 2 arg - startFileListDownLoad(...args: unknown[]): unknown; // 4 arg // 大概率是选择set里面的部分文件进行下载,没必要,不想写 + startFileListDownLoad (...args: unknown[]): unknown; // 4 arg // 大概率是选择set里面的部分文件进行下载,没必要,不想写 - pauseFileListDownLoad(...args: unknown[]): unknown; // 2 arg + pauseFileListDownLoad (...args: unknown[]): unknown; // 2 arg - resumeFileListDownLoad(...args: unknown[]): unknown; // 2 arg + resumeFileListDownLoad (...args: unknown[]): unknown; // 2 arg - stopFileListDownLoad(...args: unknown[]): unknown; // 2 arg + stopFileListDownLoad (...args: unknown[]): unknown; // 2 arg - startThumbnailListDownload(fileSetId: string): Promise < GeneralCallResult >; // 1 arg // 缩略图下载 + startThumbnailListDownload (fileSetId: string): Promise; // 1 arg // 缩略图下载 - stopThumbnailListDownload(fileSetId: string): Promise < GeneralCallResult >; // 1 arg + stopThumbnailListDownload (fileSetId: string): Promise; // 1 arg - asyncRequestDownLoadStatus(fileSetId: string): Promise < DownloadStatusInfo >; // 1 arg + asyncRequestDownLoadStatus (fileSetId: string): Promise; // 1 arg - startFileTransferUrl(fileInfo: FlashOneFileInfo): Promise < { + startFileTransferUrl (fileInfo: FlashOneFileInfo): Promise<{ ret: number, url: string, - expireTimestampSeconds: string - } >; // 1 arg + expireTimestampSeconds: string; + }>; // 1 arg - startFileListDownLoadBySessionId(...args: unknown[]): unknown; // 2 arg + startFileListDownLoadBySessionId (...args: unknown[]): unknown; // 2 arg - addFileSetSimpleStatusListener(...args: unknown[]): unknown; // 2 arg + addFileSetSimpleStatusListener (...args: unknown[]): unknown; // 2 arg - addFileSetSimpleStatusMonitoring(...args: unknown[]): unknown; // 2 arg + addFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 2 arg - removeFileSetSimpleStatusMonitoring(...args: unknown[]): unknown; // 2 arg + removeFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 2 arg - removeFileSetSimpleStatusListener(...args: unknown[]): unknown; // 1 arg + removeFileSetSimpleStatusListener (...args: unknown[]): unknown; // 1 arg - addDesktopFileSetSimpleStatusListener(...args: unknown[]): unknown; // 1 arg + addDesktopFileSetSimpleStatusListener (...args: unknown[]): unknown; // 1 arg - addDesktopFileSetSimpleStatusMonitoring(...args: unknown[]): unknown; // 1 arg + addDesktopFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 1 arg - removeDesktopFileSetSimpleStatusMonitoring(...args: unknown[]): unknown; // 1 arg + removeDesktopFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 1 arg - removeDesktopFileSetSimpleStatusListener(...args: unknown[]): unknown; // 1 arg + removeDesktopFileSetSimpleStatusListener (...args: unknown[]): unknown; // 1 arg - addFileSetSimpleUploadInfoListener(...args: unknown[]): unknown; // 1 arg + addFileSetSimpleUploadInfoListener (...args: unknown[]): unknown; // 1 arg - addFileSetSimpleUploadInfoMonitoring(...args: unknown[]): unknown; // 1 arg + addFileSetSimpleUploadInfoMonitoring (...args: unknown[]): unknown; // 1 arg - removeFileSetSimpleUploadInfoMonitoring(...args: unknown[]): unknown; // 1 arg + removeFileSetSimpleUploadInfoMonitoring (...args: unknown[]): unknown; // 1 arg - removeFileSetSimpleUploadInfoListener(...args: unknown[]): unknown; // 1 arg + removeFileSetSimpleUploadInfoListener (...args: unknown[]): unknown; // 1 arg /** * 发送闪传消息 * @param sendArgs */ - sendFlashTransferMsg(sendArgs: SendTargetRequests): Promise < { + sendFlashTransferMsg (sendArgs: SendTargetRequests): Promise<{ errCode: number, errMsg: string, rsp: { - sendStatus: SendStatus[] - } - } >; // 1 arg 估计是file set id + sendStatus: SendStatus[]; + }; + }>; // 1 arg 估计是file set id - addFlashTransferTaskInfoListener(...args: unknown[]): unknown; // 1 arg + addFlashTransferTaskInfoListener (...args: unknown[]): unknown; // 1 arg - removeFlashTransferTaskInfoListener(...args: unknown[]): unknown; // 1 arg + removeFlashTransferTaskInfoListener (...args: unknown[]): unknown; // 1 arg - retrieveLocalLastFailedSetTasksInfo(): unknown; // 0 arg + retrieveLocalLastFailedSetTasksInfo (): unknown; // 0 arg - getFailedFileList(fileSetId: string): Promise < { + getFailedFileList (fileSetId: string): Promise<{ rsp: { seq: number; result: number; errMs: string; fileSetId: string; - fileList: [] - } - } >; // 1 arg + fileList: []; + }; + }>; // 1 arg - getLocalFileListByStatuses(...args: unknown[]): unknown; // 1 arg + getLocalFileListByStatuses (...args: unknown[]): unknown; // 1 arg - addTransferStateListener(...args: unknown[]): unknown; // 1 arg + addTransferStateListener (...args: unknown[]): unknown; // 1 arg - removeTransferStateListener(...args: unknown[]): unknown; // 1 arg + removeTransferStateListener (...args: unknown[]): unknown; // 1 arg - getFileSetFirstClusteringList(...args: unknown[]): unknown; // 3 arg + getFileSetFirstClusteringList (...args: unknown[]): unknown; // 3 arg - getFileSetClusteringList(...args: unknown[]): unknown; // 1 arg + getFileSetClusteringList (...args: unknown[]): unknown; // 1 arg - addFileSetClusteringListListener(...args: unknown[]): unknown; // 1 arg + addFileSetClusteringListListener (...args: unknown[]): unknown; // 1 arg - removeFileSetClusteringListListener(...args: unknown[]): unknown; // 1 arg + removeFileSetClusteringListListener (...args: unknown[]): unknown; // 1 arg - getFileSetClusteringDetail(...args: unknown[]): unknown; // 1 arg + getFileSetClusteringDetail (...args: unknown[]): unknown; // 1 arg - doAIOFlashTransferBubbleActionWithStatus(...args: unknown[]): unknown; // 4 arg + doAIOFlashTransferBubbleActionWithStatus (...args: unknown[]): unknown; // 4 arg - getFilesTransferProgress(...args: unknown[]): unknown; // 1 arg + getFilesTransferProgress (...args: unknown[]): unknown; // 1 arg - pollFilesTransferProgress(...args: unknown[]): unknown; // 1 arg + pollFilesTransferProgress (...args: unknown[]): unknown; // 1 arg - cancelPollFilesTransferProgress(...args: unknown[]): unknown; // 1 arg + cancelPollFilesTransferProgress (...args: unknown[]): unknown; // 1 arg - checkDownloadStatusBeforeLocalFileOper(...args: unknown[]): unknown; // 3 arg + checkDownloadStatusBeforeLocalFileOper (...args: unknown[]): unknown; // 3 arg - getCompressedFileFolder(...args: unknown[]): unknown; // 1 arg + getCompressedFileFolder (...args: unknown[]): unknown; // 1 arg - addFolderListener(...args: unknown[]): unknown; // 1 arg + addFolderListener (...args: unknown[]): unknown; // 1 arg - removeFolderListener(...args: unknown[]): unknown; + removeFolderListener (...args: unknown[]): unknown; - addCompressedFileListener(...args: unknown[]): unknown; + addCompressedFileListener (...args: unknown[]): unknown; - removeCompressedFileListener(...args: unknown[]): unknown; + removeCompressedFileListener (...args: unknown[]): unknown; - getFileCategoryList(...args: unknown[]): unknown; + getFileCategoryList (...args: unknown[]): unknown; - addDeviceStatusListener(...args: unknown[]): unknown; + addDeviceStatusListener (...args: unknown[]): unknown; - removeDeviceStatusListener(...args: unknown[]): unknown; + removeDeviceStatusListener (...args: unknown[]): unknown; - checkDeviceStatus(...args: unknown[]): unknown; + checkDeviceStatus (...args: unknown[]): unknown; - pauseAllTasks(...args: unknown[]): unknown; // 2 arg + pauseAllTasks (...args: unknown[]): unknown; // 2 arg - resumePausedTasksAfterDeviceStatus(...args: unknown[]): unknown; + resumePausedTasksAfterDeviceStatus (...args: unknown[]): unknown; - onSystemGoingToSleep(...args: unknown[]): unknown; + onSystemGoingToSleep (...args: unknown[]): unknown; - onSystemWokeUp(...args: unknown[]): unknown; + onSystemWokeUp (...args: unknown[]): unknown; - getFileMetas(...args: unknown[]): unknown; + getFileMetas (...args: unknown[]): unknown; - addDownloadCntStatisticsListener(...args: unknown[]): unknown; + addDownloadCntStatisticsListener (...args: unknown[]): unknown; - removeDownloadCntStatisticsListener(...args: unknown[]): unknown; + removeDownloadCntStatisticsListener (...args: unknown[]): unknown; - detectPrivacyInfoInPaths(...args: unknown[]): unknown; + detectPrivacyInfoInPaths (...args: unknown[]): unknown; - getFileThumbnailUrl(...args: unknown[]): unknown; + getFileThumbnailUrl (...args: unknown[]): unknown; - handleDownloadFinishAfterSaveToAlbum(...args: unknown[]): unknown; + handleDownloadFinishAfterSaveToAlbum (...args: unknown[]): unknown; - checkBatchFilesDownloadStatus(...args: unknown[]): unknown; + checkBatchFilesDownloadStatus (...args: unknown[]): unknown; - onCheckAlbumStorageStatusResult(...args: unknown[]): unknown; + onCheckAlbumStorageStatusResult (...args: unknown[]): unknown; - addFileAlbumStorageListener(...args: unknown[]): unknown; + addFileAlbumStorageListener (...args: unknown[]): unknown; - removeFileAlbumStorageListener(...args: unknown[]): unknown; + removeFileAlbumStorageListener (...args: unknown[]): unknown; - refreshFolderStatus(...args: unknown[]): unknown; + refreshFolderStatus (...args: unknown[]): unknown; } From b6b14637fa4408496803e8405bafa181931662d8 Mon Sep 17 00:00:00 2001 From: H3CoF6 <1707889225@qq.com> Date: Sat, 24 Jan 2026 20:15:05 +0800 Subject: [PATCH 7/8] refactor: remove thumbnail dependency for QQ resource icons --- .../action/file/flash/CreateFlashTask.ts | 91 +------------------ 1 file changed, 1 insertion(+), 90 deletions(-) diff --git a/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts b/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts index 2246007d..32ba1347 100644 --- a/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts +++ b/packages/napcat-onebot/action/file/flash/CreateFlashTask.ts @@ -2,71 +2,12 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction'; import { ActionName } from '@/napcat-onebot/action/router'; import { Static, Type, Optional } from '@sinclair/typebox'; import path from 'node:path'; -import fs from 'node:fs'; const richMediaList = [ '.mp4', '.mov', '.avi', '.wmv', '.mpeg', '.mpg', '.flv', '.mkv', '.png', '.gif', '.jpg', '.jpeg', '.webp', '.bmp', ]; -const aiList = ['.ai', '.eps']; -const apkList = ['.apk']; -const audioList = ['.mp3', '.wav', '.wma', '.aac', '.flac', '.ogg', '.m4a', '.mid', '.amr', '.m4r']; -const bakList = ['.bak', '.tmp', '.old', '.swp']; -const codeList = ['.js', '.ts', '.jsx', '.tsx', '.json', '.c', '.cpp', '.h', '.hpp', '.java', '.py', '.go', '.rs', '.php', '.html', '.css', '.sh', '.bat', '.cmd', '.xml', '.yaml', '.yml', '.sql', '.lua', '.rb']; -const dmgList = ['.dmg']; -const docList = ['.doc', '.docx', '.wps', '.dot', '.dotx', '.odt', '.rtf']; -const exeList = ['.exe', '.msi', '.com', '.scr', '.bin']; -const fontList = ['.ttf', '.otf', '.woff', '.woff2', '.ttc', '.fon']; -const imageList = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.ico', '.tif', '.tiff']; -const ipaList = ['.ipa']; -const keynoteList = ['.key']; -const linkList = ['.lnk', '.url', '.webloc']; -const mindmapList = ['.xmind', '.mm', '.mindnode', '.emmx']; -const noteList = ['.enex', '.notes', '.one']; -const numbersList = ['.numbers']; -const pagesList = ['.pages']; -const pdfList = ['.pdf']; -const pkgList = ['.pkg']; -const pptList = ['.ppt', '.pptx', '.pps', '.ppsx', '.pot', '.odp']; -const psList = ['.psd']; -const rarList = ['.rar']; -const sketchList = ['.sketch']; -const txtList = ['.txt', '.md', '.log', '.ini', '.conf', '.cfg', '.info']; -const videoList = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv', '.mpeg', '.mpg', '.3gp', '.rmvb']; -const xlsList = ['.xls', '.xlsx', '.csv', '.et', '.xlt', '.ods']; -const zipList = ['.zip', '.7z', '.tar', '.gz', '.bz2', '.iso', '.cab', '.jar']; - -const extensionMap = { - ai: aiList, - apk: apkList, - audio: audioList, - bak: bakList, - code: codeList, - dmg: dmgList, - doc: docList, - exe: exeList, - font: fontList, - image: imageList, - ipa: ipaList, - keynote: keynoteList, - link: linkList, - mindmap: mindmapList, - note: noteList, - numbers: numbersList, - pages: pagesList, - pdf: pdfList, - pkg: pkgList, - ppt: pptList, - ps: psList, - rar: rarList, - sketch: sketchList, - txt: txtList, - video: videoList, - xls: xlsList, - zip: zipList, -}; - // 不全部使用json因为:一个文件解析Form-data会变字符串!!! 但是api文档就写List const SchemaData = Type.Object({ files: Type.Union([ @@ -83,14 +24,11 @@ export class CreateFlashTask extends OneBotAction { override payloadSchema = SchemaData; async _handle (payload: Payload) { - let iconName: string; - const qqPath = process.env['NAPCAT_WRAPPER_PATH'] || ''; - const fileList = Array.isArray(payload.files) ? payload.files : [payload.files]; let thumbPath: string = ''; if (fileList.length === 1) { - // 我是真没hook到那种合并的缩略图是哪个方法产生的,暂时不实现(怀疑是js直接canvas渲染的!!) + // 我是真没hook到那种合并的缩略图是哪个方法产生的,暂时不实现(怀疑是js直接canvas渲染的!!) // 确认了猜想 const filePath = fileList[0]; if (filePath === undefined) { return {}; @@ -105,33 +43,6 @@ export class CreateFlashTask extends OneBotAction { } } catch (_e) { } - } else { - let isDir = false; - - try { - const stat = await fs.promises.stat(filePath); - isDir = stat.isDirectory(); - } catch { - } - - if (isDir) { - iconName = 'folder'; - } else { - iconName = Object.keys(extensionMap).find(key => extensionMap[key].includes(ext)) || 'unknown'; - } - - // const __filename = fileURLToPath(import.meta.url); - // thumbPath = path.join(path.dirname(filePath), 'StaticThumbnail', `${iconName}.png`); // Gemini??? 害我找半天错??? - if (qqPath !== undefined) { - const basicPath = path.dirname(qqPath); - thumbPath = path.join(basicPath, 'resource', 'fileIcon', `${iconName}.png`); - } - } - } else { - iconName = 'multi_files'; - if (qqPath !== undefined) { - const basicPath = path.dirname(qqPath); - thumbPath = path.join(basicPath, 'resource', 'fileIcon', `${iconName}.png`); } } From 07bd14a515fb054571ef665fbe93d56ae7d09f3f Mon Sep 17 00:00:00 2001 From: H3CoF6 <1707889225@qq.com> Date: Sat, 24 Jan 2026 20:18:53 +0800 Subject: [PATCH 8/8] fix: remove useless console.log --- packages/napcat-core/apis/flash.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/napcat-core/apis/flash.ts b/packages/napcat-core/apis/flash.ts index 971529b9..15395076 100644 --- a/packages/napcat-core/apis/flash.ts +++ b/packages/napcat-core/apis/flash.ts @@ -32,8 +32,6 @@ export class NTQQFlashApi { const timestamp: number = Date.now(); const selfInfo = this.core.selfInfo; - console.log(thumbnailPath); - const fileUploadArg = { screen: 1, // 1 name: filesetName,