Compare commits

...

13 Commits

Author SHA1 Message Date
手瓜一十雪
8fb8c888f5 refactor: 移除未使用的uidCache和uinCache逻辑 2024-12-04 19:48:59 +08:00
Mlikiowa
4a2884509e release: v4.2.20 2024-12-04 11:46:12 +00:00
手瓜一十雪
e295235a89 fix: #596 2024-12-04 19:45:46 +08:00
手瓜一十雪
ef515a38d0 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-04 18:50:55 +08:00
手瓜一十雪
02cff040e3 refactor: 移除未使用的createUidFromTinyId和getStatusByUid方法 2024-12-04 18:50:51 +08:00
Mlikiowa
bb0f65a52d release: v4.2.19 2024-12-04 10:42:32 +00:00
手瓜一十雪
d51d6a5cc1 refactor: getUidByUinV2/getUinByUidV2 2024-12-04 18:41:28 +08:00
手瓜一十雪
eb99379a79 fix: 性能优化 2024-12-04 18:29:33 +08:00
Mlikiowa
388eb57d0d release: v4.2.18 2024-12-04 03:40:59 +00:00
手瓜一十雪
0b8131392a chore: 移出调试 2024-12-04 11:40:36 +08:00
手瓜一十雪
229efbd006 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-04 11:39:09 +08:00
手瓜一十雪
a482fa3a8d refactor: 优化调整文件处理 2024-12-04 11:38:59 +08:00
Mlikiowa
6cf047af39 release: v4.2.17 2024-12-04 02:57:21 +00:00
17 changed files with 96 additions and 160 deletions

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "4.2.16",
"version": "4.2.20",
"icon": "./logo.png",
"authors": [
{

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.2.16",
"version": "4.2.20",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",

View File

@@ -1,9 +1,7 @@
import fs from 'fs';
import { stat } from 'fs/promises';
import crypto, { randomUUID } from 'crypto';
import util from 'util';
import path from 'node:path';
import * as fileType from 'file-type';
import { solveProblem } from '@/common/helper';
export interface HttpDownloadOptions {
@@ -15,7 +13,6 @@ type Uri2LocalRes = {
success: boolean,
errMsg: string,
fileName: string,
ext: string,
path: string
}
@@ -73,27 +70,6 @@ async function checkFile(path: string): Promise<void> {
// 如果文件存在则无需做任何事情Promise 解决resolve自身
}
export async function file2base64(path: string) {
const readFile = util.promisify(fs.readFile);
const result = {
err: '',
data: '',
};
try {
try {
await checkFileExist(path, 5000);
} catch (e: any) {
result.err = e.toString();
return result;
}
const data = await readFile(path);
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) => {
// 创建一个流式读取器
@@ -160,20 +136,6 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
return Buffer.from(buffer);
}
export async function checkFileV2(filePath: string) {
try {
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
if (ext) {
fs.renameSync(filePath, filePath + `.${ext}`);
filePath += `.${ext}`;
return { success: true, ext: ext, path: filePath };
}
} catch (e) {
// log("获取文件类型失败", filePath,e.stack)
}
return { success: false, ext: '', path: filePath };
}
export enum FileUriType {
Unknown = 0,
Local = 1,
@@ -213,63 +175,32 @@ export async function checkUriType(Uri: string) {
return { Uri: Uri, Type: FileUriType.Unknown };
}
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> {
export async function uriToLocalFile(dir: string, uri: string): Promise<Uri2LocalRes> {
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
//解析失败
const tempName = randomUUID();
if (!filename) filename = randomUUID();
const filename = randomUUID();
const filePath = path.join(dir, filename);
//解析Http和Https协议
if (UriType == FileUriType.Unknown) {
return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' };
}
switch (UriType) {
case FileUriType.Local:
const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
//解析File协议和本地文件
if (UriType == FileUriType.Local) {
const fileExt = path.extname(HandledUri);
let filename = path.basename(HandledUri, fileExt);
filename += fileExt;
//复制文件到临时文件并保持后缀
const filenameTemp = tempName + fileExt;
const filePath = path.join(dir, filenameTemp);
fs.copyFileSync(HandledUri, filePath);
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
}
case FileUriType.Remote:
const buffer = await httpDownload(HandledUri);
fs.writeFileSync(filePath, buffer, { flag: 'wx' });
return { success: true, errMsg: '', fileName: filename, path: filePath };
//接下来都要有文件名
if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) {
const pathlen = 200 - dir.length - pathInfo.name.length;
filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断
if (pathInfo.ext) {
filename += pathInfo.ext;
}
}
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10);
const filePath = path.join(dir, tempName + fileExt);
const buffer = await httpDownload(HandledUri);
//没有文件就创建
fs.writeFileSync(filePath, buffer, { flag: 'wx' });
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
}
case FileUriType.Base64:
const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer, { flag: 'wx' });
return { success: true, errMsg: '', fileName: filename, path: filePath };
//解析Base64
if (UriType == FileUriType.Base64) {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const buffer = Buffer.from(base64, 'base64');
let filePath = path.join(dir, filename);
let fileExt = '';
fs.writeFileSync(filePath, buffer);
const { success, ext, path: fileTypePath } = await checkFileV2(filePath);
if (success) {
filePath = fileTypePath;
fileExt = ext;
filename = filename + '.' + ext;
}
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
}
return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' };
}
}

View File

@@ -1 +1 @@
export const napCatVersion = '4.2.16';
export const napCatVersion = '4.2.20';

View File

@@ -6,7 +6,6 @@ import {
Peer,
PicElement,
PicSubType,
PicType,
RawMessage,
SendFileElement,
SendPicElement,
@@ -17,7 +16,7 @@ import path from 'path';
import fs from 'fs';
import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore, SearchResultItem } from '@/core';
import * as fileType from 'file-type';
import { fileTypeFromFile } from 'file-type';
import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
import { RkeyManager } from '@/core/helper/rkey';
@@ -41,7 +40,7 @@ export class NTQQFileApi {
this.rkeyManager = new RkeyManager([
'https://rkey.napneko.icu/rkeys'
],
this.context.logger
this.context.logger
);
}
@@ -62,7 +61,7 @@ export class NTQQFileApi {
async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
const fileMd5 = await calculateFileMD5(filePath);
const extOrEmpty = (await fileType.fileTypeFromFile(filePath))?.ext;
let extOrEmpty = await fileTypeFromFile(filePath).then(e => e?.ext ?? '').catch(e => '');
const ext = extOrEmpty ? `.${extOrEmpty}` : '';
let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf('.') === -1) {
@@ -158,7 +157,7 @@ export class NTQQFileApi {
let fileExt = 'mp4';
try {
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
const tempExt = (await fileTypeFromFile(filePath))?.ext;
if (tempExt) fileExt = tempExt;
} catch (e) {
this.context.logger.logError('获取文件类型失败', e);
@@ -306,18 +305,18 @@ export class NTQQFileApi {
element.elementType === ElementType.FILE
) {
switch (element.elementType) {
case ElementType.PIC:
case ElementType.PIC:
element.picElement!.sourcePath = elementResults[elementIndex];
break;
case ElementType.VIDEO:
break;
case ElementType.VIDEO:
element.videoElement!.filePath = elementResults[elementIndex];
break;
case ElementType.PTT:
break;
case ElementType.PTT:
element.pttElement!.filePath = elementResults[elementIndex];
break;
case ElementType.FILE:
break;
case ElementType.FILE:
element.fileElement!.filePath = elementResults[elementIndex];
break;
break;
}
elementIndex++;
}

View File

@@ -2,6 +2,8 @@ import { ModifyProfileParams, User, UserDetailSource } from '@/core/types';
import { RequestUtil } from '@/common/request';
import { InstanceContext, NapCatCore, ProfileBizType } from '..';
import { solveAsyncProblem } from '@/common/helper';
import { promisify } from 'node:util';
import { LRUCache } from '@/common/lru-cache';
export class NTQQUserApi {
context: InstanceContext;
@@ -11,13 +13,6 @@ export class NTQQUserApi {
this.context = context;
this.core = core;
}
//self_tind格式
async createUidFromTinyId(tinyId: string) {
return this.context.session.getMsgService().createUidFromTinyId(this.core.selfInfo.uin, tinyId);
}
async getStatusByUid(uid: string) {
return this.context.session.getProfileService().getStatus(uid);
}
async getCoreAndBaseInfo(uids: string[]) {
return await this.core.eventWrapper.callNoListenerEvent(
@@ -26,7 +21,7 @@ export class NTQQUserApi {
uids,
);
}
// 默认获取自己的 type = 2 获取别人 type = 1
async getProfileLike(uid: string, start: number, count: number, type: number = 2) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({
@@ -99,7 +94,7 @@ export class NTQQUserApi {
};
return RetUser;
}
async getUserDetailInfo(uid: string): Promise<User> {
let retUser = await solveAsyncProblem(async (uid) => this.fetchUserDetailInfo(uid, UserDetailSource.KDB), uid);
if (retUser && retUser.uin !== '0') {
@@ -170,34 +165,44 @@ export class NTQQUserApi {
if (!skey) {
throw new Error('SKey is Empty');
}
return skey;
}
//后期改成流水线处理
async getUidByUinV2(Uin: string) {
let uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
if (uid) return uid;
uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
if (uid) return uid;
uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
if (uid) return uid;
const unverifiedUid = (await this.getUserDetailInfoByUin(Uin)).detail.uid;//从QQ Native 特殊转换
if (unverifiedUid.indexOf('*') == -1) uid = unverifiedUid;
//if (uid) return uid;
const services = [
() => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined),
() => promisify<string, string[], Map<string, string>>
(this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined),
() => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined),
() => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined),
];
let uid: string | undefined = undefined;
for (const service of services) {
uid = await service();
if (uid && uid.indexOf('*') == -1 && uid !== '') {
break;
}
}
return uid;
}
//后期改成流水线处理
async getUinByUidV2(Uid: string) {
let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
if (uin && uin !== '0') return uin;
uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
if (uin && uin !== '0') return uin;
uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
if (uin && uin !== '0') return uin;
uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid);
if (uin && uin !== '0') return uin;
uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
const services = [
() => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined),
() => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined),
() => promisify<string, string[], Map<string, string>>
(this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined),
() => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined),
() => this.getUserDetailInfo(Uid).then((data) => data.uin).catch(() => undefined),
];
let uin: string | undefined = undefined;
for (const service of services) {
uin = await service();
if (uin && uin !== '0' && uin !== '') {
break;
}
}
return uin;
}

View File

@@ -1,7 +1,7 @@
import * as fileType from 'file-type';
import { fileTypeFromFile } from 'file-type';
import { PicType } from '../types';
export async function getFileTypeForSendType(picPath: string): Promise<PicType> {
const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg';
const fileTypeResult = (await fileTypeFromFile(picPath))?.ext ?? 'jpg';
const picTypeMap: { [key: string]: PicType } = {
//'webp': PicType.NEWPIC_WEBP,
'gif': PicType.NEWPIC_GIF,

View File

@@ -4,14 +4,14 @@ import { GeneralCallResult } from '@/core/services/common';
export interface NodeIKernelProfileService {
getOtherFlag(callfrom: string, uids: string[]): Promise<Map<string, any>>;
getVasInfo(callfrom: string, uids: string[]): Promise<Map<string, any>>;
getRelationFlag(callfrom: string, uids: string[]): Promise<Map<string, any>>;
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string, string>>;
getUidByUin(callfrom: string, uin: Array<string>): Map<string, string>;
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string, string>>;
getUinByUid(callfrom: string, uid: Array<string>): Map<string, string>;
getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>;

View File

@@ -1,6 +1,6 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { checkFileExist, uri2local } from '@/common/file';
import { checkFileExist, uriToLocalFile } from '@/common/file';
import fs from 'fs';
import { Static, Type } from '@sinclair/typebox';
@@ -15,7 +15,7 @@ export class OCRImage extends OneBotAction<Payload, any> {
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.image));
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image));
if (!success) {
throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`);
}

View File

@@ -1,7 +1,7 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import fs from 'node:fs/promises';
import { checkFileExist, uri2local } from '@/common/file';
import { checkFileExist, uriToLocalFile } from '@/common/file';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
@@ -14,7 +14,7 @@ export default class SetAvatar extends OneBotAction<Payload, null> {
actionName = ActionName.SetQQAvatar;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
if (!success) {
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
}

View File

@@ -1,4 +1,4 @@
import { checkFileExist, uri2local } from '@/common/file';
import { checkFileExist, uriToLocalFile } from '@/common/file';
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { unlink } from 'node:fs/promises';
@@ -28,7 +28,7 @@ export class SendGroupNotice extends OneBotAction<Payload, null> {
const {
path,
success,
} = (await uri2local(this.core.NapCatTempPath, payload.image));
} = (await uriToLocalFile(this.core.NapCatTempPath, payload.image));
if (!success) {
throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`);
}

View File

@@ -1,6 +1,6 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { checkFileExistV2, uri2local } from '@/common/file';
import { checkFileExistV2, uriToLocalFile } from '@/common/file';
import { Static, Type } from '@sinclair/typebox';
import fs from 'node:fs/promises';
const SchemaData = Type.Object({
@@ -15,7 +15,7 @@ export default class SetGroupPortrait extends OneBotAction<Payload, any> {
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<any> {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
if (!success) {
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
}

View File

@@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { ChatType, Peer } from '@/core/types';
import fs from 'fs';
import { uri2local } from '@/common/file';
import { uriToLocalFile } from '@/common/file';
import { SendMessageContext } from '@/onebot/api';
import { Static, Type } from '@sinclair/typebox';
@@ -25,7 +25,7 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction<Payload, null>
if (fs.existsSync(file)) {
file = `file://${file}`;
}
const downloadResult = await uri2local(this.core.NapCatTempPath, file);
const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, file);
const peer: Peer = {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),

View File

@@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { ChatType, Peer, SendFileElement } from '@/core/types';
import fs from 'fs';
import { uri2local } from '@/common/file';
import { uriToLocalFile } from '@/common/file';
import { SendMessageContext } from '@/onebot/api';
import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg';
import { Static, Type } from '@sinclair/typebox';
@@ -36,7 +36,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, nul
if (fs.existsSync(file)) {
file = `file://${file}`;
}
const downloadResult = await uri2local(this.core.NapCatTempPath, file);
const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, file);
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}

View File

@@ -21,7 +21,8 @@ export class GetGroupMemberList extends OneBotAction<Payload, OB11GroupMember[]>
const memberCache = this.core.apis.GroupApi.groupMemberCache;
let groupMembers = memberCache.get(groupIdStr);
if (noCache || !groupMembers) {
await this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr);
this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr).then().catch();
//下次刷新
groupMembers = memberCache.get(groupIdStr);
if (!groupMembers) {
throw new Error(`Failed to get group member list for group ${groupIdStr}`);

View File

@@ -1,6 +1,6 @@
import { ActionName } from '@/onebot/action/router';
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
import { uri2local } from "@/common/file";
import { uriToLocalFile } from "@/common/file";
import { ChatType, Peer } from "@/core";
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
import { Static, Type } from '@sinclair/typebox';
@@ -23,7 +23,7 @@ export class SendGroupAiRecord extends GetPacketStatusDepends<Payload, {
async _handle(payload: Payload) {
const rawRsp = await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
const url = await this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+payload.group_id, rawRsp.msgInfoBody[0].index);
const { path, errMsg, success } = (await uri2local(this.core.NapCatTempPath, url));
const { path, errMsg, success } = (await uriToLocalFile(this.core.NapCatTempPath, url));
if (!success) {
throw new Error(errMsg);
}

View File

@@ -23,7 +23,7 @@ import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataTyp
import { OB11Construct } from '@/onebot/helper/data';
import { EventType } from '@/onebot/event/OneBotEvent';
import { encodeCQCode } from '@/onebot/helper/cqcode';
import { uri2local } from '@/common/file';
import { uriToLocalFile } from '@/common/file';
import { RequestUtil } from '@/common/request';
import fsPromise, { constants } from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
@@ -514,7 +514,7 @@ export class OneBotMsgApi {
let thumb = sendMsg.data.thumb;
if (thumb) {
const uri2LocalRes = await uri2local(this.core.NapCatTempPath, thumb);
const uri2LocalRes = await uriToLocalFile(this.core.NapCatTempPath, thumb);
if (uri2LocalRes.success) thumb = uri2LocalRes.path;
}
return await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
@@ -932,7 +932,7 @@ export class OneBotMsgApi {
{ data: inputdata }: OB11MessageFileBase,
{ deleteAfterSentFiles }: SendMessageContext,
) {
const realUri = inputdata.url || inputdata.file || inputdata.path || '';
const realUri = inputdata.url ?? inputdata.file ?? inputdata.path ?? '';
if (realUri.length === 0) {
this.core.context.logger.logError('文件消息缺少参数', inputdata);
throw Error('文件消息缺少参数');
@@ -942,7 +942,7 @@ export class OneBotMsgApi {
fileName,
errMsg,
success,
} = (await uri2local(this.core.NapCatTempPath, realUri));
} = (await uriToLocalFile(this.core.NapCatTempPath, realUri));
if (!success) {
this.core.context.logger.logError('文件下载失败', errMsg);