diff --git a/src/onebot11/action/go-cqhttp/DownloadFile.ts b/src/onebot11/action/go-cqhttp/DownloadFile.ts new file mode 100644 index 0000000..6c457e1 --- /dev/null +++ b/src/onebot11/action/go-cqhttp/DownloadFile.ts @@ -0,0 +1,103 @@ +import BaseAction from "../BaseAction"; +import {ActionName} from "../types"; +import fs from "fs"; +import {join as joinPath} from "node:path"; +import {calculateFileMD5, DATA_DIR} from "../../../common/utils"; + +interface Payload { + thread_count?: number + url?: string + base64?: string + name?: string + headers?: string | string[] +} + +interface FileResponse { + file: string +} + +const localPath = joinPath(DATA_DIR, "file_cache") +export default class GoCQHTTPDownloadFile extends BaseAction { + actionName = ActionName.GoCQHTTP_DownloadFile + + constructor() { + super(); + if (!fs.existsSync(localPath)) { + fs.mkdirSync(localPath) + } + } + + protected async _handle(payload: Payload): Promise { + let name = payload.name || ""; + const isRandomName = !payload.name + + if (isRandomName) { + do { + name = this.generateRandomString(10); + // 使用循环防止极低概率的情况下随机出已有的文件, 导致覆盖 + } while (fs.existsSync(joinPath(localPath, name))); + } + + const filePath = joinPath(localPath, name); + + if (payload.base64) { + fs.writeFileSync(filePath, payload.base64, 'base64') + } else if (payload.url) { + const headers = this.getHeaders(payload.headers); + + const result = await fetch(payload.url, {headers}) + if (! result.ok) throw new Error(`下载文件失败: ${result.statusText}`) + + const blob = await result.blob(); + let buffer = await blob.arrayBuffer(); + fs.writeFileSync(filePath, Buffer.from(buffer), 'binary'); + } else { + throw new Error("不存在任何文件, 无法下载") + } + if (fs.existsSync(filePath)) { + + if (isRandomName) { + // 默认实现要名称未填写时文件名为文件 md5 + const md5 = await calculateFileMD5(filePath); + const newPath = joinPath(localPath, md5); + fs.renameSync(filePath, newPath); + return { file: newPath } + } + return { file: filePath } + } else { + throw new Error("文件写入失败, 检查权限") + } + } + + generateRandomString(length: number): string { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let randomString = ''; + for (let i = 0; i < length; i++) { + const randomIndex = Math.floor(Math.random() * characters.length); + randomString += characters.charAt(randomIndex); + } + return randomString; + } + + getHeaders(headersIn?: string | string[]): any { + const headers = {}; + 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(0, spilt + 1); + } + } + } + if (!headers['Content-Type']) { + headers['Content-Type'] = 'application/octet-stream'; + } + return headers; + } +} \ No newline at end of file diff --git a/src/onebot11/action/index.ts b/src/onebot11/action/index.ts index af557be..def74d6 100644 --- a/src/onebot11/action/index.ts +++ b/src/onebot11/action/index.ts @@ -36,6 +36,7 @@ import GoCQHTTPUploadGroupFile from "./go-cqhttp/UploadGroupFile"; import {GetConfigAction, SetConfigAction} from "./llonebot/Config"; import GetGroupAddRequest from "./llonebot/GetGroupAddRequest"; import SetQQAvatar from './llonebot/SetQQAvatar' +import GoCQHTTPDownloadFile from "./go-cqhttp/DownloadFile"; export const actionHandlers = [ new Debug(), @@ -72,6 +73,7 @@ export const actionHandlers = [ new GoCQHTTPSendGroupForwardMsg(), new GoCQHTTPSendPrivateForwardMsg(), new GoCQHTTPGetStrangerInfo(), + new GoCQHTTPDownloadFile(), new GetGuildList(), new GoCQHTTPMarkMsgAsRead(), new GoCQHTTPUploadGroupFile(), diff --git a/src/onebot11/action/types.ts b/src/onebot11/action/types.ts index 48db8fd..a00bf13 100644 --- a/src/onebot11/action/types.ts +++ b/src/onebot11/action/types.ts @@ -54,4 +54,5 @@ export enum ActionName { GetGuildList = "get_guild_list", GoCQHTTP_MarkMsgAsRead = "mark_msg_as_read", GoCQHTTP_UploadGroupFile = "upload_group_file", + GoCQHTTP_DownloadFile = "download_file", } \ No newline at end of file