mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-04 22:51:13 +00:00
feat: support thumbnail for flash-transfer
This commit is contained in:
parent
58220d3fbc
commit
974b10b7b5
@ -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<any> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<typeof SchemaData>;
|
||||
|
||||
@ -16,9 +83,69 @@ export class CreateFlashTask extends OneBotAction<Payload, unknown> {
|
||||
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 || '');
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user