feat: file cache db

This commit is contained in:
linyuchen 2024-03-08 23:27:20 +08:00
parent 62eefbdb69
commit 0480208738
6 changed files with 61 additions and 25 deletions

View File

@ -28,8 +28,6 @@ export const llonebotError: LLOneBotError = {
otherError: '' otherError: ''
} }
export const fileCache = new Map<string, FileCache>()
export async function getFriend(qq: string, uid: string = ""): Promise<Friend | undefined> { export async function getFriend(qq: string, uid: string = ""): Promise<Friend | undefined> {
let filterKey = uid ? "uid" : "uin" let filterKey = uid ? "uid" : "uin"

View File

@ -1,17 +1,17 @@
// import {DATA_DIR} from "./utils";
import {Level} from "level"; import {Level} from "level";
import {RawMessage} from "../ntqqapi/types"; import {RawMessage} from "../ntqqapi/types";
import {DATA_DIR, log} from "./utils"; import {DATA_DIR, log} from "./utils";
import {selfInfo} from "./data"; import {selfInfo} from "./data";
import * as wasi from "wasi"; import {FileCache} from "./types";
class DBUtil { class DBUtil {
private readonly DB_KEY_PREFIX_MSG_ID = "msg_id_"; private readonly DB_KEY_PREFIX_MSG_ID = "msg_id_";
private readonly DB_KEY_PREFIX_MSG_SHORT_ID = "msg_short_id_"; private readonly DB_KEY_PREFIX_MSG_SHORT_ID = "msg_short_id_";
private readonly DB_KEY_PREFIX_MSG_SEQ_ID = "msg_seq_id_"; private readonly DB_KEY_PREFIX_MSG_SEQ_ID = "msg_seq_id_";
private readonly DB_KEY_PREFIX_FILE = "file_";
private db: Level; private db: Level;
private cache: Record<string, RawMessage> = {} // <msg_id_ | msg_short_id_ | msg_seq_id_><id>: RawMessage private cache: Record<string, RawMessage | string | FileCache> = {} // <msg_id_ | msg_short_id_ | msg_seq_id_><id>: RawMessage
private currentShortId: number; private currentShortId: number;
/* /*
@ -19,6 +19,7 @@ class DBUtil {
* msg_id_101231230999: {} // 长id: RawMessage * msg_id_101231230999: {} // 长id: RawMessage
* msg_short_id_1: 101231230999 // 短id: 长id * msg_short_id_1: 101231230999 // 短id: 长id
* msg_seq_id_1: 101231230999 // 序列id: 长id * msg_seq_id_1: 101231230999 // 序列id: 长id
* file_7827DBAFJFW2323.png: {} // 文件名: FileCache
* */ * */
constructor() { constructor() {
@ -61,7 +62,7 @@ class DBUtil {
async getMsgByShortId(shortMsgId: number): Promise<RawMessage> { async getMsgByShortId(shortMsgId: number): Promise<RawMessage> {
const shortMsgIdKey = this.DB_KEY_PREFIX_MSG_SHORT_ID + shortMsgId; const shortMsgIdKey = this.DB_KEY_PREFIX_MSG_SHORT_ID + shortMsgId;
if (this.cache[shortMsgIdKey]) { if (this.cache[shortMsgIdKey]) {
return this.cache[shortMsgIdKey] return this.cache[shortMsgIdKey] as RawMessage
} }
const longId = await this.db.get(shortMsgIdKey); const longId = await this.db.get(shortMsgIdKey);
const msg = await this.getMsgByLongId(longId) const msg = await this.getMsgByLongId(longId)
@ -72,7 +73,7 @@ class DBUtil {
async getMsgByLongId(longId: string): Promise<RawMessage> { async getMsgByLongId(longId: string): Promise<RawMessage> {
const longIdKey = this.DB_KEY_PREFIX_MSG_ID + longId; const longIdKey = this.DB_KEY_PREFIX_MSG_ID + longId;
if (this.cache[longIdKey]) { if (this.cache[longIdKey]) {
return this.cache[longIdKey] return this.cache[longIdKey] as RawMessage
} }
const data = await this.db.get(longIdKey) const data = await this.db.get(longIdKey)
const msg = JSON.parse(data) const msg = JSON.parse(data)
@ -83,7 +84,7 @@ class DBUtil {
async getMsgBySeqId(seqId: string): Promise<RawMessage> { async getMsgBySeqId(seqId: string): Promise<RawMessage> {
const seqIdKey = this.DB_KEY_PREFIX_MSG_SEQ_ID + seqId; const seqIdKey = this.DB_KEY_PREFIX_MSG_SEQ_ID + seqId;
if (this.cache[seqIdKey]) { if (this.cache[seqIdKey]) {
return this.cache[seqIdKey] return this.cache[seqIdKey] as RawMessage
} }
const longId = await this.db.get(seqIdKey); const longId = await this.db.get(seqIdKey);
const msg = await this.getMsgByLongId(longId) const msg = await this.getMsgByLongId(longId)
@ -95,7 +96,7 @@ class DBUtil {
// 有则更新,无则添加 // 有则更新,无则添加
// log("addMsg", msg.msgId, msg.msgSeq, msg.msgShortId); // log("addMsg", msg.msgId, msg.msgSeq, msg.msgShortId);
const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId
let existMsg = this.cache[longIdKey] let existMsg = this.cache[longIdKey] as RawMessage
if (!existMsg) { if (!existMsg) {
try { try {
existMsg = await this.getMsgByLongId(msg.msgId) existMsg = await this.getMsgByLongId(msg.msgId)
@ -136,7 +137,7 @@ class DBUtil {
async updateMsg(msg: RawMessage) { async updateMsg(msg: RawMessage) {
const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId
let existMsg = this.cache[longIdKey] let existMsg = this.cache[longIdKey] as RawMessage
if (!existMsg) { if (!existMsg) {
try { try {
existMsg = await this.getMsgByLongId(msg.msgId) existMsg = await this.getMsgByLongId(msg.msgId)
@ -179,6 +180,31 @@ class DBUtil {
await this.db.put(key, this.currentShortId.toString()); await this.db.put(key, this.currentShortId.toString());
return this.currentShortId; return this.currentShortId;
} }
async addFileCache(fileName: string, data: FileCache) {
const key = this.DB_KEY_PREFIX_FILE + fileName;
if (this.cache[key]) {
return
}
let cacheDBData = {...data}
delete cacheDBData['downloadFunc']
this.cache[fileName] = data;
await this.db.put(key, JSON.stringify(cacheDBData));
}
async getFileCache(fileName: string): Promise<FileCache | undefined> {
const key = this.DB_KEY_PREFIX_FILE + fileName;
if (this.cache[key]) {
return this.cache[key] as FileCache
}
try{
let data = await this.db.get(key);
return JSON.parse(data);
}catch (e) {
}
}
} }
export const dbUtil = new DBUtil(); export const dbUtil = new DBUtil();

View File

@ -1,7 +1,7 @@
import BaseAction from "./BaseAction"; import BaseAction from "./BaseAction";
import {fileCache} from "../../common/data";
import {getConfigUtil} from "../../common/utils"; import {getConfigUtil} from "../../common/utils";
import fs from "fs/promises"; import fs from "fs/promises";
import {dbUtil} from "../../common/db";
export interface GetFilePayload { export interface GetFilePayload {
file: string // 文件名 file: string // 文件名
@ -18,7 +18,7 @@ export interface GetFileResponse {
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> { export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> { protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const cache = fileCache.get(payload.file) const cache = await dbUtil.getFileCache(payload.file)
const {autoDeleteFile, enableLocalFile2Url, autoDeleteFileSecond} = getConfigUtil().getConfig() const {autoDeleteFile, enableLocalFile2Url, autoDeleteFileSecond} = getConfigUtil().getConfig()
if (!cache) { if (!cache) {
throw new Error('file not found') throw new Error('file not found')

View File

@ -18,6 +18,7 @@ import {log} from "../../common/utils";
import {decodeCQCode} from "../cqcode"; import {decodeCQCode} from "../cqcode";
import {dbUtil} from "../../common/db"; import {dbUtil} from "../../common/db";
import {ALLOW_SEND_TEMP_MSG} from "../../common/config"; import {ALLOW_SEND_TEMP_MSG} from "../../common/config";
import {FileCache} from "../../common/types";
function checkSendMessage(sendMsgList: OB11MessageData[]) { function checkSendMessage(sendMsgList: OB11MessageData[]) {
function checkUri(uri: string): boolean { function checkUri(uri: string): boolean {
@ -328,9 +329,20 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
case OB11MessageDataType.file: case OB11MessageDataType.file:
case OB11MessageDataType.video: case OB11MessageDataType.video:
case OB11MessageDataType.voice: { case OB11MessageDataType.voice: {
const file = sendMsg.data?.file let file = sendMsg.data?.file
const payloadFileName = sendMsg.data?.name const payloadFileName = sendMsg.data?.name
if (file) { if (file) {
const cache = await dbUtil.getFileCache(file)
if (cache){
if (fs.existsSync(cache.filePath)){
file = "file://" + cache.filePath
}
else if (cache.downloadFunc){
await cache.downloadFunc()
file = cache.filePath;
log("找到文件缓存", file);
}
}
const {path, isLocal, fileName, errMsg} = (await uri2local(file)) const {path, isLocal, fileName, errMsg} = (await uri2local(file))
if (errMsg){ if (errMsg){
throw errMsg throw errMsg

View File

@ -9,7 +9,7 @@ import {
OB11UserSex OB11UserSex
} from "./types"; } from "./types";
import {AtType, ChatType, Group, GroupMember, IMAGE_HTTP_HOST, RawMessage, SelfInfo, User} from '../ntqqapi/types'; import {AtType, ChatType, Group, GroupMember, IMAGE_HTTP_HOST, RawMessage, SelfInfo, User} from '../ntqqapi/types';
import {fileCache, getFriend, getGroupMember, selfInfo, tempGroupCodeMap} from '../common/data'; import {getFriend, getGroupMember, selfInfo, tempGroupCodeMap} from '../common/data';
import {getConfigUtil, log} from "../common/utils"; import {getConfigUtil, log} from "../common/utils";
import {NTQQApi} from "../ntqqapi/ntcall"; import {NTQQApi} from "../ntqqapi/ntcall";
import {EventType} from "./event/OB11BaseEvent"; import {EventType} from "./event/OB11BaseEvent";
@ -120,11 +120,11 @@ export class OB11Constructor {
message_data["data"]["url"] = IMAGE_HTTP_HOST + url message_data["data"]["url"] = IMAGE_HTTP_HOST + url
} }
else if (fileMd5){ else if (fileMd5){
message_data["data"]["file_id"] = `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${fileMd5}/0` message_data["data"]["file_id"] = `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${fileMd5.toUpperCase()}/0`
} }
// message_data["data"]["file_id"] = element.picElement.fileUuid // message_data["data"]["file_id"] = element.picElement.fileUuid
message_data["data"]["file_size"] = element.picElement.fileSize message_data["data"]["file_size"] = element.picElement.fileSize
fileCache.set(element.picElement.fileName, { dbUtil.addFileCache(element.picElement.fileName, {
fileName: element.picElement.fileName, fileName: element.picElement.fileName,
filePath: element.picElement.sourcePath, filePath: element.picElement.sourcePath,
fileSize: element.picElement.fileSize.toString(), fileSize: element.picElement.fileSize.toString(),
@ -133,7 +133,7 @@ export class OB11Constructor {
await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
element.elementId, element.picElement.thumbPath?.get(0) || "", element.picElement.sourcePath) element.elementId, element.picElement.thumbPath?.get(0) || "", element.picElement.sourcePath)
} }
}) }).then()
// 不在自动下载图片 // 不在自动下载图片
} else if (element.videoElement) { } else if (element.videoElement) {
@ -142,7 +142,7 @@ export class OB11Constructor {
message_data["data"]["path"] = element.videoElement.filePath message_data["data"]["path"] = element.videoElement.filePath
// message_data["data"]["file_id"] = element.videoElement.fileUuid // message_data["data"]["file_id"] = element.videoElement.fileUuid
message_data["data"]["file_size"] = element.videoElement.fileSize message_data["data"]["file_size"] = element.videoElement.fileSize
fileCache.set(element.videoElement.fileName, { dbUtil.addFileCache(element.videoElement.fileName, {
fileName: element.videoElement.fileName, fileName: element.videoElement.fileName,
filePath: element.videoElement.filePath, filePath: element.videoElement.filePath,
fileSize: element.videoElement.fileSize, fileSize: element.videoElement.fileSize,
@ -150,7 +150,7 @@ export class OB11Constructor {
await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
element.elementId, element.videoElement.thumbPath.get(0), element.videoElement.filePath) element.elementId, element.videoElement.thumbPath.get(0), element.videoElement.filePath)
} }
}) }).then()
// 怎么拿到url呢 // 怎么拿到url呢
} else if (element.fileElement) { } else if (element.fileElement) {
message_data["type"] = OB11MessageDataType.file; message_data["type"] = OB11MessageDataType.file;
@ -158,7 +158,7 @@ export class OB11Constructor {
// message_data["data"]["path"] = element.fileElement.filePath // 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 message_data["data"]["file_size"] = element.fileElement.fileSize
fileCache.set(element.fileElement.fileName, { dbUtil.addFileCache(element.fileElement.fileName, {
fileName: element.fileElement.fileName, fileName: element.fileElement.fileName,
filePath: element.fileElement.filePath, filePath: element.fileElement.filePath,
fileSize: element.fileElement.fileSize, fileSize: element.fileElement.fileSize,
@ -166,7 +166,7 @@ export class OB11Constructor {
await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
element.elementId, null, element.fileElement.filePath) element.elementId, null, element.fileElement.filePath)
} }
}) }).then()
// 怎么拿到url呢 // 怎么拿到url呢
} else if (element.pttElement) { } else if (element.pttElement) {
message_data["type"] = OB11MessageDataType.voice; message_data["type"] = OB11MessageDataType.voice;
@ -174,11 +174,11 @@ export class OB11Constructor {
message_data["data"]["path"] = element.pttElement.filePath message_data["data"]["path"] = element.pttElement.filePath
// message_data["data"]["file_id"] = element.pttElement.fileUuid // message_data["data"]["file_id"] = element.pttElement.fileUuid
message_data["data"]["file_size"] = element.pttElement.fileSize message_data["data"]["file_size"] = element.pttElement.fileSize
fileCache.set(element.pttElement.fileName, { dbUtil.addFileCache(element.pttElement.fileName, {
fileName: element.pttElement.fileName, fileName: element.pttElement.fileName,
filePath: element.pttElement.filePath, filePath: element.pttElement.filePath,
fileSize: element.pttElement.fileSize, fileSize: element.pttElement.fileSize,
}) }).then()
// log("收到语音消息", msg) // log("收到语音消息", msg)
// window.LLAPI.Ptt2Text(message.raw.msgId, message.peer, messages).then(text => { // window.LLAPI.Ptt2Text(message.raw.msgId, message.peer, messages).then(text => {

View File

@ -1,8 +1,8 @@
import {DATA_DIR, isGIF, log} from "../common/utils"; import {DATA_DIR, isGIF, log} from "../common/utils";
import {v4 as uuidv4} from "uuid"; import {v4 as uuidv4} from "uuid";
import * as path from 'node:path'; import * as path from 'node:path';
import {fileCache} from "../common/data";
import * as fileType from 'file-type'; import * as fileType from 'file-type';
import {dbUtil} from "../common/db";
const fs = require("fs").promises; const fs = require("fs").promises;
@ -73,7 +73,7 @@ export async function uri2local(uri: string, fileName: string = null) {
filePath = pathname filePath = pathname
} }
} else { } else {
const cache = fileCache.get(uri) const cache = await dbUtil.getFileCache(uri);
if (cache) { if (cache) {
filePath = cache.filePath filePath = cache.filePath
} else { } else {