Merge remote-tracking branch 'origin/v2' into v2

This commit is contained in:
Wesley F. Young
2024-08-09 15:14:31 +08:00
3 changed files with 558 additions and 239 deletions

301
src/common/utils/file.ts Normal file
View File

@@ -0,0 +1,301 @@
import fs from 'fs';
import fsPromise, { stat } from 'fs/promises';
import crypto from 'crypto';
import util from 'util';
import path from 'node:path';
import * as fileType from 'file-type';
import { randomUUID } from 'crypto';
import { LogWrapper } from './log';
export function isGIF(path: string) {
const buffer = Buffer.alloc(4);
const fd = fs.openSync(path, 'r');
fs.readSync(fd, buffer, 0, 4, 0);
fs.closeSync(fd);
return buffer.toString() === 'GIF8';
}
// 定义一个异步函数来检查文件是否存在
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
return new Promise((resolve, reject) => {
const startTime = Date.now();
function check() {
if (fs.existsSync(path)) {
resolve();
} else if (Date.now() - startTime > timeout) {
reject(new Error(`文件不存在: ${path}`));
} else {
setTimeout(check, 100);
}
}
check();
});
}
// 定义一个异步函数来检查文件是否存在
export async function checkFileReceived2(path: string, timeout: number = 3000): Promise<void> {
// 使用 Promise.race 来同时进行文件状态检查和超时计时
// Promise.race 会返回第一个解决resolve或拒绝reject的 Promise
await Promise.race([
checkFile(path),
timeoutPromise(timeout, `文件不存在: ${path}`),
]);
}
// 转换超时时间至 Promise
function timeoutPromise(timeout: number, errorMsg: string): Promise<void> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(errorMsg));
}, timeout);
});
}
// 异步检查文件是否存在
async function checkFile(path: string): Promise<void> {
try {
await stat(path);
} catch (error: any) {
if (error.code === 'ENOENT') {
// 如果文件不存在,则抛出一个错误
throw new Error(`文件不存在: ${path}`);
} else {
// 对于 stat 调用的其他错误,重新抛出
throw error;
}
}
// 如果文件存在则无需做任何事情Promise 解决resolve自身
}
export async function file2base64(path: string) {
const readFile = util.promisify(fs.readFile);
const result = {
err: '',
data: ''
};
try {
// 读取文件内容
// if (!fs.existsSync(path)){
// path = path.replace("\\Ori\\", "\\Thumb\\");
// }
try {
await checkFileReceived(path, 5000);
} catch (e: any) {
result.err = e.toString();
return result;
}
const data = await readFile(path);
// 转换为Base64编码
result.data = data.toString('base64');
} catch (err: any) {
result.err = err.toString();
}
return result;
}
export function calculateFileMD5(filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
// 创建一个流式读取器
const stream = fs.createReadStream(filePath);
const hash = crypto.createHash('md5');
stream.on('data', (data: Buffer) => {
// 当读取到数据时,更新哈希对象的状态
hash.update(data);
});
stream.on('end', () => {
// 文件读取完成,计算哈希
const md5 = hash.digest('hex');
resolve(md5);
});
stream.on('error', (err: Error) => {
// 处理可能的读取错误
reject(err);
});
});
}
export interface HttpDownloadOptions {
url: string;
headers?: Record<string, string> | string;
}
export async function httpDownload(options: string | HttpDownloadOptions): Promise<Buffer> {
const chunks: Buffer[] = [];
let url: string;
let headers: Record<string, string> = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36'
};
if (typeof options === 'string') {
url = options;
const host = new URL(url).hostname;
headers['Host'] = host;
} else {
url = options.url;
if (options.headers) {
if (typeof options.headers === 'string') {
headers = JSON.parse(options.headers);
} else {
headers = options.headers;
}
}
}
const fetchRes = await fetch(url, { headers }).catch((err) => {
if (err.cause) {
throw err.cause;
}
throw err;
});
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`);
const blob = await fetchRes.blob();
const buffer = await blob.arrayBuffer();
return Buffer.from(buffer);
}
type Uri2LocalRes = {
success: boolean,
errMsg: string,
fileName: string,
ext: string,
path: string,
isLocal: boolean
}
export async function uri2local(TempDir: string, UriOrPath: string, fileName: string | null = null): Promise<Uri2LocalRes> {
const res = {
success: false,
errMsg: '',
fileName: '',
ext: '',
path: '',
isLocal: false
};
if (!fileName) fileName = randomUUID();
let filePath = path.join(TempDir, fileName);//临时目录
let url = null;
//区分path和uri
try {
if (fs.existsSync(UriOrPath)) url = new URL('file://' + UriOrPath);
} catch (error: any) { }
try {
url = new URL(UriOrPath);
} catch (error: any) { }
//验证url
if (!url) {
res.errMsg = `UriOrPath ${UriOrPath} 解析失败,可能${UriOrPath}不存在`;
return res;
}
if (url.protocol == 'base64:') {
// base64转成文件
const base64Data = UriOrPath.split('base64://')[1];
try {
const buffer = Buffer.from(base64Data, 'base64');
fs.writeFileSync(filePath, buffer);
} catch (e: any) {
res.errMsg = 'base64文件下载失败,' + e.toString();
return res;
}
} else if (url.protocol == 'http:' || url.protocol == 'https:') {
// 下载文件
let buffer: Buffer | null = null;
try {
buffer = await httpDownload(UriOrPath);
} catch (e: any) {
res.errMsg = `${url}下载失败,` + e.toString();
return res;
}
try {
const pathInfo = path.parse(decodeURIComponent(url.pathname));
if (pathInfo.name) {
fileName = pathInfo.name;
if (pathInfo.ext) {
fileName += pathInfo.ext;
// res.ext = pathInfo.ext
}
}
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
res.fileName = fileName;
filePath = path.join(TempDir, randomUUID() + fileName);
fs.writeFileSync(filePath, buffer);
} catch (e: any) {
res.errMsg = `${url}下载失败,` + e.toString();
return res;
}
} else {
let pathname: string;
if (url.protocol === 'file:') {
// await fs.copyFile(url.pathname, filePath);
pathname = decodeURIComponent(url.pathname);
if (process.platform === 'win32') {
filePath = pathname.slice(1);
} else {
filePath = pathname;
}
}
else {
// 26702执行forword file文件操作 不应该在这里乱来
// const cache = await dbUtil.getFileCacheByName(uri);
// if (cache) {
// filePath = cache.path;
// } else {
// filePath = uri;
// }
}
res.isLocal = true;
}
// else{
// res.errMsg = `不支持的file协议,` + url.protocol
// return res
// }
// if (isGIF(filePath) && !res.isLocal) {
// await fs.rename(filePath, filePath + ".gif");
// filePath += ".gif";
// }
if (!res.isLocal && !res.ext) {
try {
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
if (ext) {
fs.renameSync(filePath, filePath + `.${ext}`);
filePath += `.${ext}`;
res.fileName += `.${ext}`;
res.ext = ext;
}
} catch (e) {
// log("获取文件类型失败", filePath,e.stack)
}
}
res.success = true;
res.path = filePath;
return res;
}
export async function copyFolder(sourcePath: string, destPath: string, logger: LogWrapper) {
try {
const entries = await fsPromise.readdir(sourcePath, { withFileTypes: true });
await fsPromise.mkdir(destPath, { recursive: true });
for (const entry of entries) {
const srcPath = path.join(sourcePath, entry.name);
const dstPath = path.join(destPath, entry.name);
if (entry.isDirectory()) {
await copyFolder(srcPath, dstPath, logger);
} else {
try {
await fsPromise.copyFile(srcPath, dstPath);
} catch (error) {
logger.logError(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
// 这里可以决定是否要继续复制其他文件
}
}
}
} catch (error) {
logger.logError('复制文件夹时出错:', error);
}
}

View File

@@ -1,9 +1,9 @@
import { import {
CacheFileListItem, CacheFileListItem,
CacheFileType, CacheFileType,
ChatCacheListItemBasic, ChatCacheListItemBasic,
ChatType, ChatType,
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, Peer, PicElement ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, Peer, PicElement
} from '@/core/entities'; } from '@/core/entities';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
@@ -14,83 +14,84 @@ import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface'; import { ISizeCalculationResult } from 'image-size/dist/types/interface';
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService'; import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
import { RkeyManager } from '../helper/rkey'; import { RkeyManager } from '../helper/rkey';
import { calculateFileMD5 } from '@/common/utils/file';
export class NTQQFileApi { export class NTQQFileApi {
context: InstanceContext; context: InstanceContext;
core: NapCatCore; core: NapCatCore;
rkeyManager: RkeyManager; rkeyManager: RkeyManager;
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
this.core = core; this.core = core;
this.rkeyManager = new RkeyManager('http://napcat-sign.wumiao.wang:2082/rkey', this.context.logger); this.rkeyManager = new RkeyManager('http://napcat-sign.wumiao.wang:2082/rkey', this.context.logger);
} }
async getFileType(filePath: string) { async getFileType(filePath: string) {
return fileType.fileTypeFromFile(filePath); return fileType.fileTypeFromFile(filePath);
} }
async copyFile(filePath: string, destPath: string) { async copyFile(filePath: string, destPath: string) {
await this.context.wrapper.util.copyFile(filePath, destPath); await this.context.wrapper.util.copyFile(filePath, destPath);
} }
async getFileSize(filePath: string): Promise<number> { async getFileSize(filePath: string): Promise<number> {
return await this.context.wrapper.util.getFileSize(filePath); return await this.context.wrapper.util.getFileSize(filePath);
} }
async getVideoUrl(peer: Peer, msgId: string, elementId: string) { async getVideoUrl(peer: Peer, msgId: string, elementId: string) {
return (await this.context.session.getRichMediaService().getVideoPlayUrlV2(peer, msgId, elementId, 0, { downSourceType: 1, triggerType: 1 })).urlResult.domainUrl; return (await this.context.session.getRichMediaService().getVideoPlayUrlV2(peer, msgId, elementId, 0, { downSourceType: 1, triggerType: 1 })).urlResult.domainUrl;
} }
// 上传文件到QQ的文件夹 // 上传文件到QQ的文件夹
async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) { async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
// napCatCore.wrapper.util. // napCatCore.wrapper.util.
const fileMd5 = await calculateFileMD5(filePath); const fileMd5 = await calculateFileMD5(filePath);
let ext: string = (await this.getFileType(filePath))?.ext as string || ''; let ext: string = (await this.getFileType(filePath))?.ext as string || '';
if (ext) { if (ext) {
ext = '.' + ext; ext = '.' + ext;
}
let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf('.') === -1) {
fileName += ext;
}
const mediaPath = this.context.session.getMsgService().getRichMediaFilePathForGuild({
md5HexStr: fileMd5,
fileName: fileName,
elementType: elementType,
elementSubType,
thumbSize: 0,
needCreate: true,
downloadType: 1,
file_uuid: ''
});
await this.copyFile(filePath, mediaPath!);
const fileSize = await this.getFileSize(filePath);
return {
md5: fileMd5,
fileName,
path: mediaPath,
fileSize,
ext
};
} }
async downloadMediaByUuid() { let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf('.') === -1) {
fileName += ext;
}
const mediaPath = this.context.session.getMsgService().getRichMediaFilePathForGuild({
md5HexStr: fileMd5,
fileName: fileName,
elementType: elementType,
elementSubType,
thumbSize: 0,
needCreate: true,
downloadType: 1,
file_uuid: ''
});
await this.copyFile(filePath, mediaPath!);
const fileSize = await this.getFileSize(filePath);
return {
md5: fileMd5,
fileName,
path: mediaPath,
fileSize,
ext
};
}
async downloadMediaByUuid() {
//napCatCore.session.getRichMediaService().downloadFileForFileUuid(); //napCatCore.session.getRichMediaService().downloadFileForFileUuid();
} }
async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) { 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); //logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
// 用于下载收到的消息中的图片等 // 用于下载收到的消息中的图片等
if (sourcePath && fs.existsSync(sourcePath)) { if (sourcePath && fs.existsSync(sourcePath)) {
if (force) { if (force) {
try { try {
await fsPromises.unlink(sourcePath); await fsPromises.unlink(sourcePath);
} catch (e) { } catch (e) {
// //
}
} else {
return sourcePath;
}
} }
const data = await this.core.eventWrapper.CallNormalEvent< } else {
return sourcePath;
}
}
const data = await this.core.eventWrapper.CallNormalEvent<
( (
params: { params: {
fileModelId: string, fileModelId: string,
downloadSourceType: number, downloadSourceType: number,
triggerType: number, triggerType: number,
@@ -103,111 +104,111 @@ export class NTQQFileApi {
filePath: string filePath: string
}) => Promise<unknown>, }) => Promise<unknown>,
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void (fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
>( >(
'NodeIKernelMsgService/downloadRichMedia', 'NodeIKernelMsgService/downloadRichMedia',
'NodeIKernelMsgListener/onRichMediaDownloadComplete', 'NodeIKernelMsgListener/onRichMediaDownloadComplete',
1, 1,
timeout, timeout,
(arg: OnRichMediaDownloadCompleteParams) => { (arg: OnRichMediaDownloadCompleteParams) => {
if (arg.msgId === msgId) { if (arg.msgId === msgId) {
return true; return true;
}
return false;
},
{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath
}
);
let filePath = data[1].filePath;
if (filePath.startsWith('\\')) {
// log('filePath start with \\');
const downloadPath = sessionConfig.defaultFileDownloadPath;
//logDebug('downloadPath', downloadPath);
filePath = path.join(downloadPath, filePath);
// 下载路径是下载文件夹的相对路径
} }
return filePath; return false;
},
{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath
}
);
let filePath = data[1].filePath;
if (filePath.startsWith('\\')) {
// log('filePath start with \\');
const downloadPath = sessionConfig.defaultFileDownloadPath;
//logDebug('downloadPath', downloadPath);
filePath = path.join(downloadPath, filePath);
// 下载路径是下载文件夹的相对路径
} }
return filePath;
}
async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> { async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
imageSize(filePath, (err, dimensions) => { imageSize(filePath, (err, dimensions) => {
if (err) { if (err) {
reject(err); reject(err);
} else {
resolve(dimensions);
}
});
});
}
async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
let GroupData;
let BuddyData;
if (peer.chatType === ChatType.group) {
GroupData =
[{
groupCode: peer.peerUid,
isConf: false,
hasModifyConfGroupFace: true,
hasModifyConfGroupName: true,
groupName: "NapCat.Cached",
remark: "NapCat.Cached"
}];
} else if (peer.chatType === ChatType.friend) {
BuddyData = [{
category_name: 'NapCat.Cached',
peerUid: peer.peerUid,
peerUin: peer.peerUid,
remark: 'NapCat.Cached'
}];
} else { } else {
return undefined; resolve(dimensions);
} }
});
return this.context.session.getSearchService().addSearchHistory({ });
type: 4, }
contactList: [], async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
id: -1, let GroupData;
groupInfos: [], let BuddyData;
msgs: [], if (peer.chatType === ChatType.group) {
fileInfos: [ GroupData =
{ [{
chatType: peer.chatType, groupCode: peer.peerUid,
buddyChatInfo: BuddyData || [], isConf: false,
discussChatInfo: [], hasModifyConfGroupFace: true,
groupChatInfo: GroupData || [], hasModifyConfGroupName: true,
dataLineChatInfo: [], groupName: "NapCat.Cached",
tmpChatInfo: [], remark: "NapCat.Cached"
msgId: msgId, }];
msgSeq: msgSeq, } else if (peer.chatType === ChatType.friend) {
msgTime: Math.floor(Date.now() / 1000).toString(), BuddyData = [{
senderUid: senderUid, category_name: 'NapCat.Cached',
senderNick: 'NapCat.Cached', peerUid: peer.peerUid,
senderRemark: 'NapCat.Cached', peerUin: peer.peerUid,
senderCard: 'NapCat.Cached', remark: 'NapCat.Cached'
elemId: elemId, }];
elemType: elemType, } else {
fileSize: fileSize, return undefined;
filePath: '',
fileName: fileName,
hits: [{
start: 12,
end: 14
}]
}
]
});
} }
async searchfile(keys: string[]) {
return this.context.session.getSearchService().addSearchHistory({
type: 4,
contactList: [],
id: -1,
groupInfos: [],
msgs: [],
fileInfos: [
{
chatType: peer.chatType,
buddyChatInfo: BuddyData || [],
discussChatInfo: [],
groupChatInfo: GroupData || [],
dataLineChatInfo: [],
tmpChatInfo: [],
msgId: msgId,
msgSeq: msgSeq,
msgTime: Math.floor(Date.now() / 1000).toString(),
senderUid: senderUid,
senderNick: 'NapCat.Cached',
senderRemark: 'NapCat.Cached',
senderCard: 'NapCat.Cached',
elemId: elemId,
elemType: elemType,
fileSize: fileSize,
filePath: '',
fileName: fileName,
hits: [{
start: 12,
end: 14
}]
}
]
});
}
async searchfile(keys: string[]) {
type EventType = NodeIKernelSearchService['searchFileWithKeywords']; type EventType = NodeIKernelSearchService['searchFileWithKeywords'];
interface OnListener { interface OnListener {
searchId: string, searchId: string,
@@ -250,98 +251,98 @@ export class NTQQFileApi {
const Event = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelSearchService/searchFileWithKeywords'); const Event = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelSearchService/searchFileWithKeywords');
let id = ''; let id = '';
const Listener = this.core.eventWrapper.RegisterListen<(params: OnListener) => void>('NodeIKernelSearchListener/onSearchFileKeywordsResult', 1, 20000, (params) => { const Listener = this.core.eventWrapper.RegisterListen<(params: OnListener) => void>('NodeIKernelSearchListener/onSearchFileKeywordsResult', 1, 20000, (params) => {
if (id !== '' && params.searchId == id) { if (id !== '' && params.searchId == id) {
return true; return true;
} }
return false; return false;
}); });
id = await Event!(keys, 12); id = await Event!(keys, 12);
const [ret] = (await Listener); const [ret] = (await Listener);
return ret; return ret;
}
async getImageUrl(element: PicElement) {
if (!element) {
return '';
} }
async getImageUrl(element: PicElement) { const url: string = element.originImageUrl!; // 没有域名
if (!element) { const md5HexStr = element.md5HexStr;
return ''; const fileMd5 = element.md5HexStr;
} const fileUuid = element.fileUuid;
const url: string = element.originImageUrl!; // 没有域名
const md5HexStr = element.md5HexStr;
const fileMd5 = element.md5HexStr;
const fileUuid = element.fileUuid;
if (url) { if (url) {
const UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接 const UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接
const imageAppid = UrlParse.searchParams.get('appid'); const imageAppid = UrlParse.searchParams.get('appid');
const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid); const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid);
if (isNewPic) { if (isNewPic) {
let UrlRkey = UrlParse.searchParams.get('rkey'); let UrlRkey = UrlParse.searchParams.get('rkey');
if (UrlRkey) { if (UrlRkey) {
return IMAGE_HTTP_HOST_NT + url; return IMAGE_HTTP_HOST_NT + url;
}
const rkeyData = await this.rkeyManager.getRkey();
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`;
} 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`;
} }
this.context.logger.logDebug('图片url获取失败', element); const rkeyData = await this.rkeyManager.getRkey();
return ''; UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`;
} 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`;
} }
this.context.logger.logDebug('图片url获取失败', element);
return '';
}
} }
export class NTQQFileCacheApi { export class NTQQFileCacheApi {
context: InstanceContext; context: InstanceContext;
core: NapCatCore; core: NapCatCore;
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
this.core = core; this.core = core;
} }
async setCacheSilentScan(isSilent: boolean = true) { async setCacheSilentScan(isSilent: boolean = true) {
return ''; return '';
} }
getCacheSessionPathList() { getCacheSessionPathList() {
return ''; return '';
} }
clearCache(cacheKeys: Array<string> = ['tmp', 'hotUpdate']) { clearCache(cacheKeys: Array<string> = ['tmp', 'hotUpdate']) {
// 参数未验证 // 参数未验证
return this.context.session.getStorageCleanService().clearCacheDataByKeys(cacheKeys); return this.context.session.getStorageCleanService().clearCacheDataByKeys(cacheKeys);
} }
addCacheScannedPaths(pathMap: object = {}) { addCacheScannedPaths(pathMap: object = {}) {
return this.context.session.getStorageCleanService().addCacheScanedPaths(pathMap); return this.context.session.getStorageCleanService().addCacheScanedPaths(pathMap);
} }
scanCache() { scanCache() {
//return (await this.context.session.getStorageCleanService().scanCache()).size; //return (await this.context.session.getStorageCleanService().scanCache()).size;
} }
getHotUpdateCachePath() { getHotUpdateCachePath() {
// 未实现 // 未实现
return ''; return '';
} }
getDesktopTmpPath() { getDesktopTmpPath() {
// 未实现 // 未实现
return ''; return '';
} }
getChatCacheList(type: ChatType, pageSize: number = 1000, pageIndex: number = 0) { getChatCacheList(type: ChatType, pageSize: number = 1000, pageIndex: number = 0) {
return this.context.session.getStorageCleanService().getChatCacheInfo(type, pageSize, 1, pageIndex); return this.context.session.getStorageCleanService().getChatCacheInfo(type, pageSize, 1, pageIndex);
} }
getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) { getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) {
const _lastRecord = lastRecord ? lastRecord : { fileType: fileType }; const _lastRecord = lastRecord ? lastRecord : { fileType: fileType };
//需要五个参数 //需要五个参数
//return napCatCore.session.getStorageCleanService().getFileCacheInfo(); //return napCatCore.session.getStorageCleanService().getFileCacheInfo();
} }
async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) { async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {
return this.context.session.getStorageCleanService().clearChatCacheInfo(chats, fileKeys); return this.context.session.getStorageCleanService().clearChatCacheInfo(chats, fileKeys);
} }
} }

View File

@@ -9,7 +9,7 @@ import { sleep } from "@/common/utils/helper";
import { SelfInfo, LineDevice, SelfStatusInfo } from "./entities"; import { SelfInfo, LineDevice, SelfStatusInfo } from "./entities";
import { LegacyNTEventWrapper } from "@/common/framework/event-legacy"; import { LegacyNTEventWrapper } from "@/common/framework/event-legacy";
import { NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi, NTQQWebApi } from "./apis"; import { NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi, NTQQWebApi } from "./apis";
import os from "node:os";
export enum NapCatCoreWorkingEnv { export enum NapCatCoreWorkingEnv {
Unknown = 0, Unknown = 0,
Shell = 1, Shell = 1,
@@ -31,7 +31,8 @@ export class NapCatCore {
readonly ApiContext: NTApiContext; readonly ApiContext: NTApiContext;
readonly eventWrapper: LegacyNTEventWrapper; readonly eventWrapper: LegacyNTEventWrapper;
// readonly eventChannel: NTEventChannel; // readonly eventChannel: NTEventChannel;
NapCatDataPath: string;
NapCatTempPath: string;
// runtime info, not readonly // runtime info, not readonly
selfInfo: SelfInfo; selfInfo: SelfInfo;
// 通过构造器递过去的 runtime info 应该尽量少 // 通过构造器递过去的 runtime info 应该尽量少
@@ -47,10 +48,26 @@ export class NapCatCore {
UserApi: new NTQQUserApi(this.context, this), UserApi: new NTQQUserApi(this.context, this),
GroupApi: new NTQQGroupApi(this.context, this) GroupApi: new NTQQGroupApi(this.context, this)
}; };
this.NapCatDataPath = path.join(this.dataPath, 'NapCat');
fs.mkdirSync(this.NapCatDataPath, { recursive: true });
this.NapCatTempPath = path.join(this.NapCatDataPath, 'temp');
// 创建临时目录
if (!fs.existsSync(this.NapCatTempPath)) {
fs.mkdirSync(this.NapCatTempPath, { recursive: true });
}
} }
getApiContext() { getApiContext() {
return this.ApiContext; return this.ApiContext;
} }
get dataPath(): string {
let result = this.context.wrapper.util.getNTUserDataInfoConfig();
if (!result) {
result = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(result, { recursive: true });
}
return result;
}
// Renamed from 'InitDataListener' // Renamed from 'InitDataListener'
async initNapCatCoreListeners() { async initNapCatCoreListeners() {
const msgListener = new MsgListener(); const msgListener = new MsgListener();