mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-24 09:00:06 +08:00
* feat: pnpm new * Refactor build and release workflows, update dependencies Switch build scripts and workflows from npm to pnpm, update build and artifact paths, and simplify release workflow by removing version detection and changelog steps. Add new dependencies (silk-wasm, express, ws, node-pty-prebuilt-multiarch), update exports in package.json files, and add vite config for napcat-framework. Also, rename manifest.json for framework package and fix static asset copying in shell build config.
79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|
import { ActionName } from '@/napcat-onebot/action/router';
|
|
import fs from 'fs';
|
|
import { join as joinPath } from 'node:path';
|
|
import { calculateFileMD5, uriToLocalFile } from 'napcat-common/src/file';
|
|
import { randomUUID } from 'crypto';
|
|
import { Static, Type } from '@sinclair/typebox';
|
|
|
|
interface FileResponse {
|
|
file: string;
|
|
}
|
|
|
|
const SchemaData = Type.Object({
|
|
url: Type.Optional(Type.String()),
|
|
base64: Type.Optional(Type.String()),
|
|
name: Type.Optional(Type.String()),
|
|
headers: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())])),
|
|
});
|
|
|
|
type Payload = Static<typeof SchemaData>;
|
|
|
|
export default class GoCQHTTPDownloadFile extends OneBotAction<Payload, FileResponse> {
|
|
override actionName = ActionName.GoCQHTTP_DownloadFile;
|
|
override payloadSchema = SchemaData;
|
|
|
|
async _handle (payload: Payload): Promise<FileResponse> {
|
|
const isRandomName = !payload.name;
|
|
const name = payload.name || randomUUID();
|
|
let result: Awaited<ReturnType<typeof uriToLocalFile>>;
|
|
|
|
if (payload.base64) {
|
|
result = await uriToLocalFile(this.core.NapCatTempPath, `base64://${payload.base64}`, name);
|
|
} else if (payload.url) {
|
|
const headers = this.getHeaders(payload.headers);
|
|
result = await uriToLocalFile(this.core.NapCatTempPath, payload.url, name, headers);
|
|
} else {
|
|
throw new Error('不存在任何文件, 无法下载');
|
|
}
|
|
if (!result.success) {
|
|
throw new Error(result.errMsg);
|
|
}
|
|
const filePath = result.path;
|
|
if (fs.existsSync(filePath)) {
|
|
if (isRandomName) {
|
|
// 默认实现要名称未填写时文件名为文件 md5
|
|
const md5 = await calculateFileMD5(filePath);
|
|
const newPath = joinPath(this.core.NapCatTempPath, md5);
|
|
fs.renameSync(filePath, newPath);
|
|
return { file: newPath };
|
|
}
|
|
return { file: filePath };
|
|
} else {
|
|
throw new Error('文件写入失败, 检查权限');
|
|
}
|
|
}
|
|
|
|
getHeaders (headersIn?: string | string[]): Record<string, string> {
|
|
const headers: Record<string, string> = {};
|
|
if (typeof headersIn === 'string') {
|
|
headersIn = headersIn.split('[\\r\\n]');
|
|
}
|
|
if (Array.isArray(headersIn)) {
|
|
for (const headerItem of headersIn) {
|
|
const spilt = headerItem.indexOf('=');
|
|
if (spilt < 0) {
|
|
headers[headerItem] = '';
|
|
} else {
|
|
const key = headerItem.substring(0, spilt);
|
|
headers[key] = headerItem.substring(spilt + 1);
|
|
}
|
|
}
|
|
}
|
|
if (!headers['Content-Type']) {
|
|
headers['Content-Type'] = 'application/octet-stream';
|
|
}
|
|
return headers;
|
|
}
|
|
}
|