mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
feat: api /get_file
This commit is contained in:
@@ -222,14 +222,14 @@ class DBUtil {
|
||||
return this.currentShortId;
|
||||
}
|
||||
|
||||
async addFileCache(fileName: string, data: FileCache) {
|
||||
const key = this.DB_KEY_PREFIX_FILE + fileName;
|
||||
async addFileCache(fileNameOrUuid: string, data: FileCache) {
|
||||
const key = this.DB_KEY_PREFIX_FILE + fileNameOrUuid;
|
||||
if (this.cache[key]) {
|
||||
return
|
||||
}
|
||||
let cacheDBData = {...data}
|
||||
delete cacheDBData['downloadFunc']
|
||||
this.cache[fileName] = data;
|
||||
this.cache[fileNameOrUuid] = data;
|
||||
try {
|
||||
await this.db.put(key, JSON.stringify(cacheDBData));
|
||||
} catch (e) {
|
||||
@@ -237,8 +237,8 @@ class DBUtil {
|
||||
}
|
||||
}
|
||||
|
||||
async getFileCache(fileName: string): Promise<FileCache | undefined> {
|
||||
const key = this.DB_KEY_PREFIX_FILE + fileName;
|
||||
async getFileCache(fileNameOrUuid: string): Promise<FileCache | undefined> {
|
||||
const key = this.DB_KEY_PREFIX_FILE + (fileNameOrUuid);
|
||||
if (this.cache[key]) {
|
||||
return this.cache[key] as FileCache
|
||||
}
|
||||
|
@@ -38,6 +38,8 @@ export interface FileCache {
|
||||
fileName: string
|
||||
filePath: string
|
||||
fileSize: string
|
||||
fileUuid?: string
|
||||
url?: string
|
||||
msgId?: string
|
||||
downloadFunc?: () => Promise<void>
|
||||
}
|
||||
|
@@ -4,7 +4,8 @@ import {
|
||||
CacheFileListItem,
|
||||
CacheFileType,
|
||||
CacheScanResult,
|
||||
ChatCacheList, ChatCacheListItemBasic,
|
||||
ChatCacheList,
|
||||
ChatCacheListItemBasic,
|
||||
ChatType,
|
||||
ElementType
|
||||
} from "../types";
|
||||
@@ -13,12 +14,13 @@ import fs from "fs";
|
||||
import {ReceiveCmdS} from "../hook";
|
||||
import {log} from "../../common/utils/log";
|
||||
|
||||
export class NTQQFileApi{
|
||||
export class NTQQFileApi {
|
||||
static async getFileType(filePath: string) {
|
||||
return await callNTQQApi<{ ext: string }>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_TYPE, args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
static async getFileMd5(filePath: string) {
|
||||
return await callNTQQApi<string>({
|
||||
className: NTQQApiClass.FS_API,
|
||||
@@ -26,6 +28,7 @@ export class NTQQFileApi{
|
||||
args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
static async copyFile(filePath: string, destPath: string) {
|
||||
return await callNTQQApi<string>({
|
||||
className: NTQQApiClass.FS_API,
|
||||
@@ -36,11 +39,13 @@ export class NTQQFileApi{
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
static async getFileSize(filePath: string) {
|
||||
return await callNTQQApi<number>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_SIZE, args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
// 上传文件到QQ的文件夹
|
||||
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC) {
|
||||
const md5 = await NTQQFileApi.getFileMd5(filePath);
|
||||
@@ -79,14 +84,18 @@ export class NTQQFileApi{
|
||||
fileSize
|
||||
}
|
||||
}
|
||||
static async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string) {
|
||||
|
||||
static async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, isFile: boolean = false) {
|
||||
// 用于下载收到的消息中的图片等
|
||||
if (fs.existsSync(sourcePath)) {
|
||||
if (sourcePath && fs.existsSync(sourcePath)) {
|
||||
return sourcePath
|
||||
}
|
||||
const apiParams = [
|
||||
{
|
||||
getReq: {
|
||||
fileModelId: "0",
|
||||
downloadSourceType: 0,
|
||||
triggerType: 1,
|
||||
msgId: msgId,
|
||||
chatType: chatType,
|
||||
peerUid: peerUid,
|
||||
@@ -96,20 +105,21 @@ export class NTQQFileApi{
|
||||
filePath: thumbPath,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
null,
|
||||
]
|
||||
// log("需要下载media", sourcePath);
|
||||
await callNTQQApi({
|
||||
methodName: NTQQApiMethod.DOWNLOAD_MEDIA,
|
||||
args: apiParams,
|
||||
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
|
||||
cmdCB: (payload: { notifyInfo: { filePath: string } }) => {
|
||||
// log("media 下载完成判断", payload.notifyInfo.filePath, sourcePath);
|
||||
return payload.notifyInfo.filePath == sourcePath;
|
||||
cmdCB: (payload: { notifyInfo: { filePath: string, msgId: string } }) => {
|
||||
log("media 下载完成判断", payload.notifyInfo.msgId, msgId);
|
||||
return payload.notifyInfo.msgId == msgId;
|
||||
}
|
||||
})
|
||||
return sourcePath
|
||||
}
|
||||
|
||||
static async getImageSize(filePath: string) {
|
||||
return await callNTQQApi<{ width: number, height: number }>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.IMAGE_SIZE, args: [filePath]
|
||||
@@ -118,7 +128,7 @@ export class NTQQFileApi{
|
||||
|
||||
}
|
||||
|
||||
export class NTQQFileCacheApi{
|
||||
export class NTQQFileCacheApi {
|
||||
static async setCacheSilentScan(isSilent: boolean = true) {
|
||||
return await callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.CACHE_SET_SILENCE,
|
||||
@@ -127,6 +137,7 @@ export class NTQQFileCacheApi{
|
||||
}, null]
|
||||
});
|
||||
}
|
||||
|
||||
static getCacheSessionPathList() {
|
||||
return callNTQQApi<{
|
||||
key: string,
|
||||
@@ -136,6 +147,7 @@ export class NTQQFileCacheApi{
|
||||
methodName: NTQQApiMethod.CACHE_PATH_SESSION,
|
||||
});
|
||||
}
|
||||
|
||||
static clearCache(cacheKeys: Array<string> = ['tmp', 'hotUpdate']) {
|
||||
return callNTQQApi<any>({ // TODO: 目前还不知道真正的返回值是什么
|
||||
methodName: NTQQApiMethod.CACHE_CLEAR,
|
||||
@@ -144,6 +156,7 @@ export class NTQQFileCacheApi{
|
||||
}, null]
|
||||
});
|
||||
}
|
||||
|
||||
static addCacheScannedPaths(pathMap: object = {}) {
|
||||
return callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.CACHE_ADD_SCANNED_PATH,
|
||||
@@ -152,6 +165,7 @@ export class NTQQFileCacheApi{
|
||||
}, null]
|
||||
});
|
||||
}
|
||||
|
||||
static scanCache() {
|
||||
callNTQQApi<GeneralCallResult>({
|
||||
methodName: ReceiveCmdS.CACHE_SCAN_FINISH,
|
||||
@@ -163,6 +177,7 @@ export class NTQQFileCacheApi{
|
||||
timeoutSecond: 300,
|
||||
});
|
||||
}
|
||||
|
||||
static getHotUpdateCachePath() {
|
||||
return callNTQQApi<string>({
|
||||
className: NTQQApiClass.HOTUPDATE_API,
|
||||
@@ -176,6 +191,7 @@ export class NTQQFileCacheApi{
|
||||
methodName: NTQQApiMethod.CACHE_PATH_DESKTOP_TEMP
|
||||
});
|
||||
}
|
||||
|
||||
static getChatCacheList(type: ChatType, pageSize: number = 1000, pageIndex: number = 0) {
|
||||
return new Promise<ChatCacheList>((res, rej) => {
|
||||
callNTQQApi<ChatCacheList>({
|
||||
@@ -190,6 +206,7 @@ export class NTQQFileCacheApi{
|
||||
.catch(e => rej(e));
|
||||
});
|
||||
}
|
||||
|
||||
static getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) {
|
||||
const _lastRecord = lastRecord ? lastRecord : {fileType: fileType};
|
||||
|
||||
@@ -204,6 +221,7 @@ export class NTQQFileCacheApi{
|
||||
}, null]
|
||||
})
|
||||
}
|
||||
|
||||
static async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {
|
||||
return await callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.CACHE_CHAT_CLEAR,
|
||||
|
@@ -2,10 +2,12 @@ import BaseAction from "./BaseAction";
|
||||
import fs from "fs/promises";
|
||||
import {dbUtil} from "../../common/db";
|
||||
import {getConfigUtil} from "../../common/config";
|
||||
import {log, uri2local} from "../../common/utils";
|
||||
import {log, sleep, uri2local} from "../../common/utils";
|
||||
import {NTQQFileApi} from "../../ntqqapi/api/file";
|
||||
import {ActionName} from "./types";
|
||||
|
||||
export interface GetFilePayload {
|
||||
file: string // 文件名
|
||||
file: string // 文件名或者fileUuid
|
||||
}
|
||||
|
||||
export interface GetFileResponse {
|
||||
@@ -31,13 +33,37 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
await fs.access(cache.filePath, fs.constants.F_OK)
|
||||
} catch (e) {
|
||||
log("file not found", e)
|
||||
const downloadResult = await uri2local(cache.url)
|
||||
if (downloadResult.success) {
|
||||
cache.filePath = downloadResult.path
|
||||
dbUtil.addFileCache(payload.file, cache).then()
|
||||
} else {
|
||||
throw new Error("file download failed. " + downloadResult.errMsg)
|
||||
if (cache.url){
|
||||
const downloadResult = await uri2local(cache.url)
|
||||
if (downloadResult.success) {
|
||||
cache.filePath = downloadResult.path
|
||||
dbUtil.addFileCache(payload.file, cache).then()
|
||||
} else {
|
||||
throw new Error("file download failed. " + downloadResult.errMsg)
|
||||
}
|
||||
}
|
||||
else{
|
||||
// 没有url的可能是私聊文件或者群文件,需要自己下载
|
||||
log("需要调用 NTQQ 下载文件api")
|
||||
if (cache.msgId) {
|
||||
let msg = await dbUtil.getMsgByLongId(cache.msgId)
|
||||
if (msg){
|
||||
log("找到了文件 msg", msg)
|
||||
const element = msg.elements.find(e=>e.fileElement)
|
||||
log("找到了文件 element", element);
|
||||
// 构建下载函数
|
||||
await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
||||
element.elementId, "", "", true)
|
||||
await sleep(1000);
|
||||
msg = await dbUtil.getMsgByLongId(cache.msgId)
|
||||
log("下载完成后的msg", msg)
|
||||
cache.filePath = msg?.elements.find(e=>e.fileElement)?.fileElement?.filePath
|
||||
dbUtil.addFileCache(payload.file, cache).then()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
let res: GetFileResponse = {
|
||||
file: cache.filePath,
|
||||
@@ -47,7 +73,11 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
}
|
||||
if (enableLocalFile2Url) {
|
||||
if (!cache.url) {
|
||||
res.base64 = await fs.readFile(cache.filePath, 'base64')
|
||||
try{
|
||||
res.base64 = await fs.readFile(cache.filePath, 'base64')
|
||||
}catch (e) {
|
||||
throw new Error("文件下载失败. " + e)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (autoDeleteFile) {
|
||||
@@ -57,4 +87,16 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
// }
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
export default class GetFile extends GetFileBase {
|
||||
actionName = ActionName.GetFile
|
||||
|
||||
protected async _handle(payload: {file_id: string, file: string}): Promise<GetFileResponse> {
|
||||
if (!payload.file_id) {
|
||||
throw new Error('file_id 不能为空')
|
||||
}
|
||||
payload.file = payload.file_id
|
||||
return super._handle(payload);
|
||||
}
|
||||
}
|
@@ -38,8 +38,10 @@ import GetGroupAddRequest from "./llonebot/GetGroupAddRequest";
|
||||
import SetQQAvatar from './llonebot/SetQQAvatar'
|
||||
import GoCQHTTPDownloadFile from "./go-cqhttp/DownloadFile";
|
||||
import GoCQHTTPGetGroupMsgHistory from "./go-cqhttp/GetGroupMsgHistory";
|
||||
import GetFile from "./GetFile";
|
||||
|
||||
export const actionHandlers = [
|
||||
new GetFile(),
|
||||
new Debug(),
|
||||
new GetConfigAction(),
|
||||
new SetConfigAction(),
|
||||
|
@@ -14,11 +14,14 @@ export interface InvalidCheckResult {
|
||||
}
|
||||
|
||||
export enum ActionName {
|
||||
// llonebot
|
||||
GetGroupIgnoreAddRequest = "get_group_ignore_add_request",
|
||||
SetQQAvatar = "set_qq_avatar",
|
||||
GetConfig = "get_config",
|
||||
SetConfig = "set_config",
|
||||
Debug = "llonebot_debug",
|
||||
GetFile = "get_file",
|
||||
// onebot 11
|
||||
SendLike = "send_like",
|
||||
GetLoginInfo = "get_login_info",
|
||||
GetFriendList = "get_friend_list",
|
||||
|
@@ -16,7 +16,8 @@ import {
|
||||
GroupMember,
|
||||
IMAGE_HTTP_HOST,
|
||||
RawMessage,
|
||||
SelfInfo, Sex,
|
||||
SelfInfo,
|
||||
Sex,
|
||||
TipGroupElementType,
|
||||
User
|
||||
} from '../ntqqapi/types';
|
||||
@@ -174,10 +175,12 @@ export class OB11Constructor {
|
||||
message_data["type"] = OB11MessageDataType.file;
|
||||
message_data["data"]["file"] = element.fileElement.fileName
|
||||
// message_data["data"]["path"] = element.fileElement.filePath
|
||||
// message_data["data"]["file_id"] = element.fileElement.fileUuid
|
||||
message_data["data"]["file_id"] = element.fileElement.fileUuid
|
||||
message_data["data"]["file_size"] = element.fileElement.fileSize
|
||||
dbUtil.addFileCache(element.fileElement.fileName, {
|
||||
dbUtil.addFileCache(element.fileElement.fileUuid, {
|
||||
msgId: msg.msgId,
|
||||
fileName: element.fileElement.fileName,
|
||||
fileUuid: element.fileElement.fileUuid,
|
||||
filePath: element.fileElement.filePath,
|
||||
fileSize: element.fileElement.fileSize,
|
||||
downloadFunc: async () => {
|
||||
@@ -251,18 +254,16 @@ export class OB11Constructor {
|
||||
// log("构造群增加事件", event)
|
||||
return event;
|
||||
}
|
||||
}
|
||||
else if (groupElement.type === TipGroupElementType.ban) {
|
||||
} else if (groupElement.type === TipGroupElementType.ban) {
|
||||
log("收到群群员禁言提示", groupElement)
|
||||
const memberUid = groupElement.shutUp.member.uid
|
||||
const adminUid = groupElement.shutUp.admin.uid
|
||||
let memberUin: string = ""
|
||||
let duration = parseInt(groupElement.shutUp.duration)
|
||||
let sub_type: "ban" | "lift_ban" = duration > 0 ? "ban" : "lift_ban"
|
||||
if (memberUid){
|
||||
if (memberUid) {
|
||||
memberUin = (await getGroupMember(msg.peerUid, memberUid))?.uin || (await NTQQUserApi.getUserDetailInfo(memberUid))?.uin
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
memberUin = "0"; // 0表示全员禁言
|
||||
if (duration > 0) {
|
||||
duration = -1
|
||||
@@ -273,16 +274,19 @@ export class OB11Constructor {
|
||||
return new OB11GroupBanEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(adminUin), duration, sub_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (element.fileElement){
|
||||
return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), {id: element.fileElement.fileUuid, name: element.fileElement.fileName, size: parseInt(element.fileElement.fileSize)})
|
||||
} else if (element.fileElement) {
|
||||
return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), {
|
||||
id: element.fileElement.fileUuid,
|
||||
name: element.fileElement.fileName,
|
||||
size: parseInt(element.fileElement.fileSize)
|
||||
})
|
||||
}
|
||||
|
||||
if (grayTipElement) {
|
||||
if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER){
|
||||
if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER) {
|
||||
log("收到新人被邀请进群消息", grayTipElement)
|
||||
const xmlElement = grayTipElement.xmlElement
|
||||
if (xmlElement?.content){
|
||||
if (xmlElement?.content) {
|
||||
const regex = /jp="(\d+)"/g;
|
||||
|
||||
let matches = [];
|
||||
@@ -291,7 +295,7 @@ export class OB11Constructor {
|
||||
while ((match = regex.exec(xmlElement.content)) !== null) {
|
||||
matches.push(match[1]);
|
||||
}
|
||||
if (matches.length === 2){
|
||||
if (matches.length === 2) {
|
||||
const [inviter, invitee] = matches;
|
||||
return new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(invitee), parseInt(inviter), "invite");
|
||||
}
|
||||
@@ -330,7 +334,7 @@ export class OB11Constructor {
|
||||
}[role]
|
||||
}
|
||||
|
||||
static sex(sex: Sex): OB11UserSex{
|
||||
static sex(sex: Sex): OB11UserSex {
|
||||
const sexMap = {
|
||||
[Sex.male]: OB11UserSex.male,
|
||||
[Sex.female]: OB11UserSex.female,
|
||||
@@ -338,6 +342,7 @@ export class OB11Constructor {
|
||||
}
|
||||
return sexMap[sex] || OB11UserSex.unknown
|
||||
}
|
||||
|
||||
static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
|
||||
return {
|
||||
group_id: parseInt(group_id),
|
||||
|
Reference in New Issue
Block a user