diff --git a/electron.vite.config.ts b/electron.vite.config.ts
index a30979c..5165206 100644
--- a/electron.vite.config.ts
+++ b/electron.vite.config.ts
@@ -1,7 +1,9 @@
 import cp from 'vite-plugin-cp';
 import "./scripts/gen-version"
 
-const external = ["silk-wasm", "ws"];
+const external = ["silk-wasm", "ws",
+    "level", "classic-level", "abstract-level", "level-supports", "level-transcoder",
+    "module-error", "catering", "node-gyp-build"];
 
 function genCpModule(module: string) {
     return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false }
diff --git a/manifest.json b/manifest.json
index 03f54f0..8abe04d 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,10 +1,10 @@
 {
   "manifest_version": 4,
   "type": "extension",
-  "name": "LLOneBot v3.11.2",
+  "name": "LLOneBot v3.12.0",
   "slug": "LLOneBot",
   "description": "LiteLoaderQQNT的OneBotApi",
-  "version": "3.11.2",
+  "version": "3.12.0",
   "thumbnail": "./icon.png",
   "authors": [
     {
diff --git a/package-lock.json b/package-lock.json
index 51a5acb..44df5c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
         "express": "^4.18.2",
         "file-type": "^19.0.0",
         "fluent-ffmpeg": "^2.1.2",
+        "level": "^8.0.1",
         "silk-wasm": "^3.2.3",
         "utf-8-validate": "^6.0.3",
         "uuid": "^9.0.1",
@@ -1892,6 +1893,23 @@
       "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
       "dev": true
     },
+    "node_modules/abstract-level": {
+      "version": "1.0.4",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/abstract-level/-/abstract-level-1.0.4.tgz",
+      "integrity": "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==",
+      "dependencies": {
+        "buffer": "^6.0.3",
+        "catering": "^2.1.0",
+        "is-buffer": "^2.0.5",
+        "level-supports": "^4.0.0",
+        "level-transcoder": "^1.0.1",
+        "module-error": "^1.0.1",
+        "queue-microtask": "^1.2.3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/accepts": {
       "version": "1.3.8",
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -2157,6 +2175,25 @@
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
     "node_modules/body-parser": {
       "version": "1.20.2",
       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
@@ -2221,6 +2258,17 @@
         "node": ">=8"
       }
     },
+    "node_modules/browser-level": {
+      "version": "1.0.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/browser-level/-/browser-level-1.0.1.tgz",
+      "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==",
+      "dependencies": {
+        "abstract-level": "^1.0.2",
+        "catering": "^2.1.1",
+        "module-error": "^1.0.2",
+        "run-parallel-limit": "^1.1.0"
+      }
+    },
     "node_modules/browserslist": {
       "version": "4.23.0",
       "resolved": "https://mirrors.cloud.tencent.com/npm/browserslist/-/browserslist-4.23.0.tgz",
@@ -2253,6 +2301,29 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
     "node_modules/buffer-crc32": {
       "version": "0.2.13",
       "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -2397,6 +2468,14 @@
         }
       ]
     },
+    "node_modules/catering": {
+      "version": "2.1.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/catering/-/catering-2.1.1.tgz",
+      "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/chalk": {
       "version": "4.1.2",
       "resolved": "https://mirrors.cloud.tencent.com/npm/chalk/-/chalk-4.1.2.tgz",
@@ -2425,6 +2504,22 @@
         "node": ">=8"
       }
     },
+    "node_modules/classic-level": {
+      "version": "1.4.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/classic-level/-/classic-level-1.4.1.tgz",
+      "integrity": "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "abstract-level": "^1.0.2",
+        "catering": "^2.1.0",
+        "module-error": "^1.0.1",
+        "napi-macros": "^2.2.2",
+        "node-gyp-build": "^4.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/clone-response": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
@@ -4319,6 +4414,28 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-buffer": {
+      "version": "2.0.5",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/is-buffer/-/is-buffer-2.0.5.tgz",
+      "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/is-builtin-module": {
       "version": "3.2.1",
       "resolved": "https://mirrors.cloud.tencent.com/npm/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
@@ -4623,6 +4740,43 @@
         "json-buffer": "3.0.1"
       }
     },
+    "node_modules/level": {
+      "version": "8.0.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/level/-/level-8.0.1.tgz",
+      "integrity": "sha512-oPBGkheysuw7DmzFQYyFe8NAia5jFLAgEnkgWnK3OXAuJr8qFT+xBQIwokAZPME2bhPFzS8hlYcL16m8UZrtwQ==",
+      "dependencies": {
+        "abstract-level": "^1.0.4",
+        "browser-level": "^1.0.1",
+        "classic-level": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/level"
+      }
+    },
+    "node_modules/level-supports": {
+      "version": "4.0.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/level-supports/-/level-supports-4.0.1.tgz",
+      "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/level-transcoder": {
+      "version": "1.0.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/level-transcoder/-/level-transcoder-1.0.1.tgz",
+      "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==",
+      "dependencies": {
+        "buffer": "^6.0.3",
+        "module-error": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/levn": {
       "version": "0.4.1",
       "resolved": "https://mirrors.cloud.tencent.com/npm/levn/-/levn-0.4.1.tgz",
@@ -4777,6 +4931,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/module-error": {
+      "version": "1.0.2",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/module-error/-/module-error-1.0.2.tgz",
+      "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -4801,6 +4963,11 @@
         "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
       }
     },
+    "node_modules/napi-macros": {
+      "version": "2.2.2",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/napi-macros/-/napi-macros-2.2.2.tgz",
+      "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g=="
+    },
     "node_modules/natural-compare": {
       "version": "1.4.0",
       "resolved": "https://mirrors.cloud.tencent.com/npm/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -5178,7 +5345,6 @@
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
       "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5458,6 +5624,28 @@
         "queue-microtask": "^1.2.2"
       }
     },
+    "node_modules/run-parallel-limit": {
+      "version": "1.1.0",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz",
+      "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
     "node_modules/safe-array-concat": {
       "version": "1.1.0",
       "resolved": "https://mirrors.cloud.tencent.com/npm/safe-array-concat/-/safe-array-concat-1.1.0.tgz",
diff --git a/package.json b/package.json
index b7ac7d1..7fff19b 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
     "express": "^4.18.2",
     "file-type": "^19.0.0",
     "fluent-ffmpeg": "^2.1.2",
+    "level": "^8.0.1",
     "silk-wasm": "^3.2.3",
     "utf-8-validate": "^6.0.3",
     "uuid": "^9.0.1",
diff --git a/src/common/data.ts b/src/common/data.ts
index df7f921..32ebfe7 100644
--- a/src/common/data.ts
+++ b/src/common/data.ts
@@ -9,6 +9,8 @@ import {
     type SelfInfo
 } from '../ntqqapi/types'
 import {type FileCache, type LLOneBotError} from './types'
+import {dbUtil} from "./db";
+import {raw} from "express";
 
 export const selfInfo: SelfInfo = {
     uid: '',
@@ -18,32 +20,19 @@ export const selfInfo: SelfInfo = {
 }
 export let groups: Group[] = []
 export let friends: Friend[] = []
-export let msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
 export let groupNotifies: Map<string, GroupNotify> = new Map<string, GroupNotify>()
 export let friendRequests: Map<number, FriendRequest> = new Map<number, FriendRequest>()
 export const llonebotError: LLOneBotError = {
     ffmpegError: '',
     otherError: ''
 }
-let globalMsgId = Math.floor(Date.now() / 1000)
 
 export const fileCache = new Map<string, FileCache>()
 
-export function addHistoryMsg(msg: RawMessage): boolean {
-    const existMsg = msgHistory[msg.msgId]
-    if (existMsg) {
-        Object.assign(existMsg, msg)
-        msg.msgShortId = existMsg.msgShortId
-        return false
-    }
-    msg.msgShortId = ++globalMsgId
-    msgHistory[msg.msgId] = msg
-    return true
-}
 
-export function getHistoryMsgByShortId(shortId: number | string) {
+export async function getHistoryMsgByShortId(shortId: number) {
     // log("getHistoryMsgByShortId", shortId, Object.values(msgHistory).map(m=>m.msgShortId))
-    return Object.values(msgHistory).find(msg => msg.msgShortId.toString() == shortId.toString())
+    return await dbUtil.getMsgByShortId(shortId);
 }
 
 export async function getFriend(qq: string): Promise<Friend | undefined> {
@@ -58,8 +47,11 @@ export async function getFriend(qq: string): Promise<Friend | undefined> {
 export async function getGroup(qq: string): Promise<Group | undefined> {
     let group = groups.find(group => group.groupCode === qq)
     if (!group){
-        groups = await NTQQApi.getGroups(true);
-        group = groups.find(group => group.groupCode === qq)
+        const _groups = await NTQQApi.getGroups(true);
+        group = _groups.find(group => group.groupCode === qq)
+        if (group){
+            groups.push(group)
+        }
     }
     return group
 }
@@ -96,10 +88,6 @@ export async function refreshGroupMembers(groupQQ: string) {
     }
 }
 
-export function getHistoryMsgBySeq(seq: string) {
-    return Object.values(msgHistory).find(msg => msg.msgSeq === seq)
-}
-
 export const uidMaps: Record<string, string> = {} // 一串加密的字符串(uid) -> qq号
 
 export function getUidByUin(uin: string) {
diff --git a/src/common/db.ts b/src/common/db.ts
new file mode 100644
index 0000000..4c1509f
--- /dev/null
+++ b/src/common/db.ts
@@ -0,0 +1,148 @@
+// import {DATA_DIR} from "./utils";
+import {Level} from "level";
+import {RawMessage} from "../ntqqapi/types";
+import {DATA_DIR} from "./utils";
+import {selfInfo} from "./data";
+
+
+class DBUtil {
+    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_SEQ_ID = "msg_seq_id_";
+    private db: Level;
+    private cache: Record<string, RawMessage> = {}  // <msg_id_ | msg_short_id_ | msg_seq_id_><id>: RawMessage
+
+    /*
+    * 数据库结构
+    * msg_id_101231230999: {} // 长id: RawMessage
+    * msg_short_id_1: 101231230999  // 短id: 长id
+    * msg_seq_id_1: 101231230999  // 序列id: 长id
+    * */
+
+    constructor() {
+        let initCount = 0;
+        new Promise((resolve, reject) => {
+            const initDB = () => {
+                initCount++;
+                if (initCount > 50) {
+                    return reject("init db fail")
+                }
+
+                try {
+                    if (!selfInfo.uin) {
+                        setTimeout(initDB, 300);
+                        return
+                    }
+                    const DB_PATH = DATA_DIR + `/msg_${selfInfo.uin}`;
+                    this.db = new Level(DB_PATH, {valueEncoding: 'json'});
+                    console.log("llonebot init db success")
+                    resolve(null)
+                } catch (e) {
+                    // console.log("init db fail", e.stack.toString())
+                    setTimeout(initDB, 300);
+                }
+            }
+            initDB();
+        }).then()
+        setInterval(() => {
+            this.cache = {}
+        }, 1000 * 60 * 10)
+    }
+
+    private addCache(msg: RawMessage) {
+        const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId
+        const shortIdKey = this.DB_KEY_PREFIX_MSG_SHORT_ID + msg.msgShortId
+        const seqIdKey = this.DB_KEY_PREFIX_MSG_SEQ_ID + msg.msgSeq
+        this.cache[longIdKey] = this.cache[shortIdKey] = this.cache[seqIdKey] = msg
+    }
+
+    async getMsgByShortId(shortMsgId: number): Promise<RawMessage> {
+        const shortMsgIdKey = this.DB_KEY_PREFIX_MSG_SHORT_ID + shortMsgId;
+        if (this.cache[shortMsgIdKey]) {
+            return this.cache[shortMsgIdKey]
+        }
+        const longId = await this.db.get(shortMsgIdKey);
+        const msg = await this.getMsgByLongId(longId)
+        this.addCache(msg)
+        return msg
+    }
+
+    async getMsgByLongId(longId: string): Promise<RawMessage> {
+        const longIdKey = this.DB_KEY_PREFIX_MSG_ID + longId;
+        if (this.cache[longIdKey]) {
+            return this.cache[longIdKey]
+        }
+        const data = await this.db.get(longIdKey)
+        const msg = JSON.parse(data)
+        this.addCache(msg)
+        return msg
+    }
+
+    async getMsgBySeqId(seqId: string): Promise<RawMessage> {
+        const seqIdKey = this.DB_KEY_PREFIX_MSG_SEQ_ID + seqId;
+        if (this.cache[seqIdKey]) {
+            return this.cache[seqIdKey]
+        }
+        const longId = await this.db.get(seqIdKey);
+        const msg = await this.getMsgByLongId(longId)
+        this.addCache(msg)
+        return msg
+    }
+
+    async addMsg(msg: RawMessage) {
+        // 有则更新,无则添加
+        const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId
+        let existMsg = this.cache[longIdKey]
+        if (!existMsg){
+            try {
+                existMsg = await this.getMsgByLongId(msg.msgId)
+            }catch (e) {
+
+            }
+        }
+        if (existMsg){
+            this.updateMsg(msg).then()
+            return existMsg.msgShortId
+        }
+
+        const shortMsgId = await this.genMsgShortId();
+        const shortIdKey = this.DB_KEY_PREFIX_MSG_SHORT_ID + shortMsgId;
+        const seqIdKey = this.DB_KEY_PREFIX_MSG_SEQ_ID + msg.msgSeq;
+        await this.db.put(shortIdKey, msg.msgId);
+        msg.msgShortId = shortMsgId;
+        await this.db.put(longIdKey, JSON.stringify(msg));
+        await this.db.put(seqIdKey, msg.msgId);
+        this.cache[seqIdKey] = this.cache[shortIdKey] = this.cache[longIdKey] = msg;
+        return shortMsgId
+    }
+
+    async updateMsg(msg: RawMessage) {
+        const longIdKey = this.DB_KEY_PREFIX_MSG_ID + msg.msgId
+        let existMsg = this.cache[longIdKey]
+        if (!existMsg) {
+            try {
+                existMsg = await this.getMsgByLongId(msg.msgId)
+            } catch (e) {
+                return
+            }
+        }
+
+        Object.assign(existMsg, msg)
+        await this.db.put(longIdKey, JSON.stringify(existMsg));
+    }
+
+    private async genMsgShortId(): Promise<number> {
+        let shortId = -2147483640
+        const key = "msg_current_short_id";
+        try {
+            let id: string = await this.db.get(key);
+            shortId = parseInt(id);
+        } catch (e) {
+        }
+        shortId++;
+        await this.db.put(key, shortId.toString());
+        return shortId;
+    }
+}
+
+export const dbUtil = new DBUtil();
\ No newline at end of file
diff --git a/src/common/utils.ts b/src/common/utils.ts
index 668727f..c6a1c75 100644
--- a/src/common/utils.ts
+++ b/src/common/utils.ts
@@ -7,10 +7,10 @@ import fs from 'fs';
 import {v4 as uuidv4} from "uuid";
 import ffmpeg from "fluent-ffmpeg"
 
-export const CONFIG_DIR = global.LiteLoader.plugins["LLOneBot"].path.data;
+export const DATA_DIR = global.LiteLoader.plugins["LLOneBot"].path.data;
 
 export function getConfigUtil() {
-    const configFilePath = path.join(CONFIG_DIR, `config_${selfInfo.uin}.json`)
+    const configFilePath = path.join(DATA_DIR, `config_${selfInfo.uin}.json`)
     return new ConfigUtil(configFilePath)
 }
 
@@ -55,7 +55,7 @@ export function log(...msg: any[]) {
     logMsg = `${currentDateTime} ${userInfo}: ${logMsg}\n\n`
     // sendLog(...msg);
     // console.log(msg)
-    fs.appendFile(path.join(CONFIG_DIR, `llonebot-${currentDate}.log`), logMsg, (err: any) => {
+    fs.appendFile(path.join(DATA_DIR, `llonebot-${currentDate}.log`), logMsg, (err: any) => {
 
     })
 }
@@ -192,7 +192,7 @@ export async function encodeSilk(filePath: string) {
 
     try {
         const fileName = path.basename(filePath);
-        const pttPath = path.join(CONFIG_DIR, uuidv4());
+        const pttPath = path.join(DATA_DIR, uuidv4());
         if (getFileHeader(filePath) !== "02232153494c4b") {
             log(`语音文件${filePath}需要转换成silk`)
             const _isWav = await isWavFile(filePath);
diff --git a/src/main/main.ts b/src/main/main.ts
index 8e020c4..4cb43a5 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -11,15 +11,13 @@ import {
     CHANNEL_SET_CONFIG,
 } from "../common/channels";
 import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer";
-import {checkFfmpeg, CONFIG_DIR, getConfigUtil, log} from "../common/utils";
+import {checkFfmpeg, DATA_DIR, getConfigUtil, log} from "../common/utils";
 import {
-    addHistoryMsg,
     friendRequests,
     getGroup,
     getGroupMember,
     groupNotifies,
-    llonebotError,
-    msgHistory, refreshGroupMembers,
+    llonebotError, refreshGroupMembers,
     selfInfo
 } from "../common/data";
 import {hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmd, registerReceiveHook} from "../ntqqapi/hook";
@@ -43,6 +41,7 @@ import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecrease
 import {OB11GroupRequestEvent} from "../onebot11/event/request/OB11GroupRequest";
 import {OB11FriendRequestEvent} from "../onebot11/event/request/OB11FriendRequest";
 import * as path from "node:path";
+import {dbUtil} from "../common/db";
 
 
 let running = false;
@@ -82,8 +81,8 @@ function onLoad() {
             return ""
         }
     })
-    if (!fs.existsSync(CONFIG_DIR)) {
-        fs.mkdirSync(CONFIG_DIR, {recursive: true});
+    if (!fs.existsSync(DATA_DIR)) {
+        fs.mkdirSync(DATA_DIR, {recursive: true});
     }
     ipcMain.handle(CHANNEL_ERROR, (event, arg) => {
         return llonebotError;
@@ -153,14 +152,12 @@ function onLoad() {
         log(arg);
     })
 
-    function postReceiveMsg(msgList: RawMessage[]) {
+    async function postReceiveMsg(msgList: RawMessage[]) {
         const {debug, reportSelfMessage} = getConfigUtil().getConfig();
         for (let message of msgList) {
             // log("收到新消息", message)
-            message.msgShortId = msgHistory[message.msgId]?.msgShortId
-            if (!message.msgShortId) {
-                addHistoryMsg(message);
-            }
+            message.msgShortId = await dbUtil.addMsg(message)
+
             OB11Constructor.message(message).then((msg) => {
                 if (debug) {
                     msg.raw = message;
@@ -171,14 +168,14 @@ function onLoad() {
                 }
                 postOB11Event(msg);
                 // log("post msg", msg)
-            }).catch(e => log("constructMessage error: ", e.toString()));
+            }).catch(e => log("constructMessage error: ", e.stack.toString()));
         }
     }
 
     async function startReceiveHook() {
-        registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, (payload) => {
+        registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, async (payload) => {
             try {
-                postReceiveMsg(payload.msgList);
+                await postReceiveMsg(payload.msgList);
             } catch (e) {
                 log("report message error: ", e.toString());
             }
@@ -188,10 +185,12 @@ function onLoad() {
                 // log("message update", message.sendStatus, message)
                 if (message.recallTime != "0") {
                     // 撤回消息上报
-                    const oriMessage = msgHistory[message.msgId]
+                    const oriMessage = await dbUtil.getMsgByLongId(message.msgId)
                     if (!oriMessage) {
                         continue
                     }
+                    oriMessage.recallTime = message.recallTime
+                    dbUtil.updateMsg(oriMessage).then();
                     if (message.chatType == ChatType.friend) {
                         const friendRecallEvent = new OB11FriendRecallNoticeEvent(parseInt(message.senderUin), oriMessage.msgShortId);
                         postOB11Event(friendRecallEvent);
@@ -211,21 +210,22 @@ function onLoad() {
 
                         postOB11Event(groupRecallEvent);
                     }
+                    // 不让入库覆盖原来消息,不然就获取不到撤回的消息内容了
                     continue
                 }
-                addHistoryMsg(message)
+                dbUtil.addMsg(message).then();
             }
         })
-        registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.SELF_SEND_MSG, (payload) => {
+        registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.SELF_SEND_MSG, async (payload) => {
             const {reportSelfMessage} = getConfigUtil().getConfig();
             if (!reportSelfMessage) {
                 return
             }
             // log("reportSelfMessage", payload)
             try {
-                postReceiveMsg([payload.msgRecord]);
+                await postReceiveMsg([payload.msgRecord]);
             } catch (e) {
-                log("report self message error: ", e.toString());
+                log("report self message error: ", e.stack.toString());
             }
         })
         registerReceiveHook<{
diff --git a/src/ntqqapi/constructor.ts b/src/ntqqapi/constructor.ts
index 06989f1..ed637e0 100644
--- a/src/ntqqapi/constructor.ts
+++ b/src/ntqqapi/constructor.ts
@@ -1,6 +1,6 @@
 import {
     AtType,
-    ElementType, PicType,
+    ElementType, PicType, SendArkElement,
     SendFaceElement,
     SendFileElement,
     SendPicElement,
@@ -150,4 +150,12 @@ export class SendMsgElementConstructor {
             }
         }
     }
+
+    static ark(data: any): SendArkElement {
+        return {
+            elementType: ElementType.ARK,
+            elementId: "",
+            arkElement: data
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts
index 8488587..dac68ac 100644
--- a/src/ntqqapi/hook.ts
+++ b/src/ntqqapi/hook.ts
@@ -2,13 +2,14 @@ import {type BrowserWindow} from 'electron'
 import {getConfigUtil, log, sleep} from '../common/utils'
 import {NTQQApi, type NTQQApiClass, sendMessagePool} from './ntcall'
 import {type Group, type RawMessage, type User} from './types'
-import {addHistoryMsg, friends, groups, msgHistory, selfInfo, tempGroupCodeMap} from '../common/data'
+import {friends, groups, selfInfo, tempGroupCodeMap} from '../common/data'
 import {OB11GroupDecreaseEvent} from '../onebot11/event/notice/OB11GroupDecreaseEvent'
 import {OB11GroupIncreaseEvent} from '../onebot11/event/notice/OB11GroupIncreaseEvent'
 import {v4 as uuidv4} from 'uuid'
 import {postOB11Event} from '../onebot11/server/postOB11Event'
 import {HOOK_LOG} from '../common/config'
 import fs from 'fs'
+import {dbUtil} from "../common/db";
 
 export const hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
 
@@ -233,7 +234,7 @@ registerReceiveHook<{ msgList: RawMessage[] }>(ReceiveCmd.NEW_MSG, (payload) =>
     const {autoDeleteFile, autoDeleteFileSecond} = getConfigUtil().getConfig()
     for (const message of payload.msgList) {
         // log("收到新消息,push到历史记录", message)
-        addHistoryMsg(message)
+        dbUtil.addMsg(message).then()
         // 清理文件
         if (!autoDeleteFile) {
             continue
@@ -261,10 +262,6 @@ registerReceiveHook<{ msgList: RawMessage[] }>(ReceiveCmd.NEW_MSG, (payload) =>
             }, autoDeleteFileSecond * 1000)
         }
     }
-    const msgIds = Object.keys(msgHistory)
-    if (msgIds.length > 30000) {
-        delete msgHistory[msgIds.sort()[0]]
-    }
 })
 
 registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.SELF_SEND_MSG, ({msgRecord}) => {
diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts
index 777ae8e..dbc06c1 100644
--- a/src/ntqqapi/ntcall.ts
+++ b/src/ntqqapi/ntcall.ts
@@ -17,9 +17,10 @@ import {
     type User
 } from './types'
 import * as fs from 'node:fs'
-import {addHistoryMsg, friendRequests, groupNotifies, msgHistory, selfInfo} from '../common/data'
+import {friendRequests, groupNotifies, selfInfo} from '../common/data'
 import {v4 as uuidv4} from 'uuid'
 import path from 'path'
+import {dbUtil} from "../common/db";
 
 interface IPCReceiveEvent {
     eventName: string
@@ -57,6 +58,7 @@ export enum NTQQApiMethod {
     RECALL_MSG = 'nodeIKernelMsgService/recallMsg',
     SEND_MSG = 'nodeIKernelMsgService/sendMsg',
     DOWNLOAD_MEDIA = 'nodeIKernelMsgService/downloadRichMedia',
+    FORWARD_MSG = "nodeIKernelMsgService/forwardMsgWithComment",  // 逐条转发
     MULTI_FORWARD_MSG = 'nodeIKernelMsgService/multiForwardMsgWithComment', // 合并转发
     GET_GROUP_NOTICE = 'nodeIKernelGroupService/getSingleScreenNotifies',
     HANDLE_GROUP_REQUEST = 'nodeIKernelGroupService/operateSysNotify',
@@ -449,7 +451,14 @@ export class NTQQApi {
 
         let checkSendCompleteUsingTime = 0
         const checkSendComplete = async (): Promise<RawMessage> => {
-            if (sentMessage && msgHistory[sentMessage.msgId]?.sendStatus == 2) {
+            if (sentMessage) {
+                if (waitComplete) {
+                    if ((await dbUtil.getMsgByLongId(sentMessage.msgId)).sendStatus == 2) {
+                        return sentMessage
+                    } else {
+                        return await checkSendComplete()
+                    }
+                }
                 // log(`给${peerUid}发送消息成功`)
                 return sentMessage
             } else {
@@ -474,6 +483,24 @@ export class NTQQApi {
         return await checkSendComplete()
     }
 
+    static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
+        return await callNTQQApi<GeneralCallResult>({
+            methodName: NTQQApiMethod.FORWARD_MSG,
+            args:[
+                {
+                    msgIds: msgIds,
+                    srcContact: srcPeer,
+                    dstContacts: [
+                        destPeer
+                    ],
+                    commentElements: [],
+                    msgAttributeInfos: new Map()
+                },
+                null,
+            ]
+        })
+
+    }
     static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
         const msgInfos = msgIds.map(id => {
             return {msgId: id, senderShowName: selfInfo.nick}
@@ -495,7 +522,7 @@ export class NTQQApi {
                     reject('转发消息超时')
                 }
             }, 5000)
-            registerReceiveHook(ReceiveCmd.SELF_SEND_MSG, (payload: { msgRecord: RawMessage }) => {
+            registerReceiveHook(ReceiveCmd.SELF_SEND_MSG, async (payload: { msgRecord: RawMessage }) => {
                 const msg = payload.msgRecord
                 // 需要判断它是转发的消息,并且识别到是当前转发的这一条
                 const arkElement = msg.elements.find(ele => ele.arkElement)
@@ -509,7 +536,7 @@ export class NTQQApi {
                 }
                 if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
                     complete = true
-                    addHistoryMsg(msg)
+                    await dbUtil.addMsg(msg)
                     resolve(msg)
                     log('转发消息成功:', payload)
                 }
diff --git a/src/ntqqapi/types.ts b/src/ntqqapi/types.ts
index edebdc9..a3c2624 100644
--- a/src/ntqqapi/types.ts
+++ b/src/ntqqapi/types.ts
@@ -73,6 +73,7 @@ export enum ElementType {
     PTT = 4,
     FACE = 6,
     REPLY = 7,
+    ARK = 10,
 }
 
 export interface SendTextElement {
@@ -165,13 +166,20 @@ export interface FileElement {
 }
 
 export interface SendFileElement {
-    "elementType": ElementType.FILE,
-    "elementId": "",
-    "fileElement": FileElement
+    elementType: ElementType.FILE,
+    elementId: "",
+    fileElement: FileElement
+}
+
+export interface SendArkElement {
+    elementType: ElementType.ARK,
+    elementId: "",
+    arkElement: ArkElement
+
 }
 
 export type SendMessageElement = SendTextElement | SendPttElement |
-    SendPicElement | SendReplyElement | SendFaceElement | SendFileElement
+    SendPicElement | SendReplyElement | SendFaceElement | SendFileElement | SendArkElement
 
 export enum AtType {
     notAt = 0,
@@ -210,6 +218,8 @@ export interface PttElement {
 
 export interface ArkElement {
     bytesData: string;
+    linkInfo:null,
+    subElementType:null
 }
 
 export const IMAGE_HTTP_HOST = "https://gchat.qpic.cn"
diff --git a/src/onebot11/action/BaseAction.ts b/src/onebot11/action/BaseAction.ts
index c30b677..1f6116a 100644
--- a/src/onebot11/action/BaseAction.ts
+++ b/src/onebot11/action/BaseAction.ts
@@ -1,6 +1,7 @@
 import {ActionName, BaseCheckResult} from "./types"
 import {OB11Response} from "./utils"
 import {OB11Return} from "../types";
+import {log} from "../../common/utils";
 
 class BaseAction<PayloadType, ReturnDataType> {
     actionName: ActionName
@@ -20,6 +21,7 @@ class BaseAction<PayloadType, ReturnDataType> {
             const resData = await this._handle(payload);
             return OB11Response.ok(resData);
         } catch (e) {
+            log("发送错误", e.stack)
             return OB11Response.error(e.toString(), 200);
         }
     }
@@ -33,6 +35,7 @@ class BaseAction<PayloadType, ReturnDataType> {
             const resData = await this._handle(payload)
             return OB11Response.ok(resData, echo);
         } catch (e) {
+            log("发生错误", e.stack.toString())
             return OB11Response.error(e.toString(), 1200, echo)
         }
     }
diff --git a/src/onebot11/action/DeleteMsg.ts b/src/onebot11/action/DeleteMsg.ts
index 9e8676f..900d350 100644
--- a/src/onebot11/action/DeleteMsg.ts
+++ b/src/onebot11/action/DeleteMsg.ts
@@ -1,7 +1,7 @@
 import {ActionName} from "./types";
 import BaseAction from "./BaseAction";
 import {NTQQApi} from "../../ntqqapi/ntcall";
-import {getHistoryMsgByShortId} from "../../common/data";
+import {dbUtil} from "../../common/db";
 
 interface Payload {
     message_id: number
@@ -11,7 +11,7 @@ class DeleteMsg extends BaseAction<Payload, void> {
     actionName = ActionName.DeleteMsg
 
     protected async _handle(payload: Payload) {
-        let msg = getHistoryMsgByShortId(payload.message_id)
+        let msg = await dbUtil.getMsgByShortId(payload.message_id)
         await NTQQApi.recallMsg({
             chatType: msg.chatType,
             peerUid: msg.peerUid
diff --git a/src/onebot11/action/GetMsg.ts b/src/onebot11/action/GetMsg.ts
index 8383891..a55bf64 100644
--- a/src/onebot11/action/GetMsg.ts
+++ b/src/onebot11/action/GetMsg.ts
@@ -1,8 +1,8 @@
-import {getHistoryMsgByShortId} from "../../common/data";
 import {OB11Message} from '../types';
 import {OB11Constructor} from "../constructor";
 import BaseAction from "./BaseAction";
 import {ActionName} from "./types";
+import {dbUtil} from "../../common/db";
 
 
 export interface PayloadType {
@@ -19,7 +19,7 @@ class GetMsg extends BaseAction<PayloadType, OB11Message> {
         if (!payload.message_id) {
             throw ("参数message_id不能为空")
         }
-        const msg = getHistoryMsgByShortId(payload.message_id)
+        const msg = await dbUtil.getMsgByShortId(payload.message_id)
         if (msg) {
             const msgData = await OB11Constructor.message(msg);
             return msgData
diff --git a/src/onebot11/action/SendMsg.ts b/src/onebot11/action/SendMsg.ts
index 2b6362c..f4db617 100644
--- a/src/onebot11/action/SendMsg.ts
+++ b/src/onebot11/action/SendMsg.ts
@@ -1,14 +1,13 @@
-import {AtType, ChatType, Group, RawMessage, SendMessageElement} from "../../ntqqapi/types";
+import {AtType, ChatType, Group, RawMessage, SendArkElement, SendMessageElement} from "../../ntqqapi/types";
+import {friends, getGroup, getGroupMember, getUidByUin, selfInfo,} from "../../common/data";
 import {
-    addHistoryMsg,
-    friends,
-    getGroup,
-    getGroupMember,
-    getHistoryMsgByShortId,
-    getUidByUin,
-    selfInfo,
-} from "../../common/data";
-import {OB11MessageData, OB11MessageDataType, OB11MessageMixType, OB11MessageNode, OB11PostSendMsg} from '../types';
+    OB11MessageCustomMusic,
+    OB11MessageData,
+    OB11MessageDataType,
+    OB11MessageMixType,
+    OB11MessageNode,
+    OB11PostSendMsg
+} from '../types';
 import {NTQQApi, Peer} from "../../ntqqapi/ntcall";
 import {SendMsgElementConstructor} from "../../ntqqapi/constructor";
 import {uri2local} from "../utils";
@@ -17,6 +16,7 @@ import {ActionName, BaseCheckResult} from "./types";
 import * as fs from "node:fs";
 import {log} from "../../common/utils";
 import {decodeCQCode} from "../cqcode";
+import {dbUtil} from "../../common/db";
 
 function checkSendMessage(sendMsgList: OB11MessageData[]) {
     function checkUri(uri: string): boolean {
@@ -62,7 +62,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
 
     protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
         const messages = this.convertMessage2List(payload.message);
-        const fmNum = this.forwardMsgNum(payload)
+        const fmNum = this.getSpecialMsgNum(payload, OB11MessageDataType.node)
         if (fmNum && fmNum != messages.length) {
             return {
                 valid: false,
@@ -105,13 +105,27 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
             }
         }
         const messages = this.convertMessage2List(payload.message);
-        if (this.forwardMsgNum(payload)) {
+        if (this.getSpecialMsgNum(payload, OB11MessageDataType.node)) {
             try {
                 const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[], group)
                 return {message_id: returnMsg.msgShortId}
             } catch (e) {
                 throw ("发送转发消息失败 " + e.toString())
             }
+        } else {
+            if (this.getSpecialMsgNum(payload, OB11MessageDataType.music)) {
+                const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic
+                if (music) {
+                    const {url, audio, title, content, image} = music.data;
+                    const selfPeer: Peer = {peerUid: selfInfo.uid, chatType: ChatType.friend}
+                    // 搞不定!
+                    // const musicMsg = await this.send(selfPeer, [this.genMusicElement(url, audio, title, content, image)], [], false)
+                    // 转发
+                    // const res = await NTQQApi.forwardMsg(selfPeer, peer, [musicMsg.msgId])
+                    // log("转发音乐消息成功", res);
+                    // return {message_id: musicMsg.msgShortId}
+                }
+            }
         }
         // log("send msg:", peer, sendElements)
         const {sendElements, deleteAfterSentFiles} = await this.createSendElements(messages, group)
@@ -138,9 +152,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
         return message;
     }
 
-    private forwardMsgNum(payload: OB11PostSendMsg): number {
+    private getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number {
         if (Array.isArray(payload.message)) {
-            return payload.message.filter(msg => msg.type == OB11MessageDataType.node).length
+            return payload.message.filter(msg => msg.type == msgType).length
         }
         return 0
     }
@@ -158,7 +172,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
             let nodeId = messageNode.data.id;
             // 有nodeId表示一个子转发消息卡片
             if (nodeId) {
-                let nodeMsg = getHistoryMsgByShortId(nodeId);
+                let nodeMsg = await dbUtil.getMsgByShortId(parseInt(nodeId));
                 if (nodeMsg) {
                     originalNodeMsgList.push(nodeMsg);
                 }
@@ -263,8 +277,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
                 case OB11MessageDataType.reply: {
                     let replyMsgId = sendMsg.data.id;
                     if (replyMsgId) {
-                        replyMsgId = replyMsgId.toString()
-                        const replyMsg = getHistoryMsgByShortId(replyMsgId)
+                        const replyMsg = await dbUtil.getMsgByShortId(parseInt(replyMsgId))
                         if (replyMsg) {
                             sendElements.push(SendMsgElementConstructor.reply(replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin, replyMsg.senderUin))
                         }
@@ -278,6 +291,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
                     }
                 }
                     break;
+
                 case OB11MessageDataType.image:
                 case OB11MessageDataType.file:
                 case OB11MessageDataType.video:
@@ -315,11 +329,47 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
             throw ("消息体无法解析")
         }
         const returnMsg = await NTQQApi.sendMsg(peer, sendElements, waitComplete, 20000);
-        addHistoryMsg(returnMsg)
+        await dbUtil.addMsg(returnMsg)
         deleteAfterSentFiles.map(f => fs.unlink(f, () => {
         }))
         return returnMsg
     }
+
+    private genMusicElement(url: string, audio: string, title: string, content: string, image: string): SendArkElement {
+        const musicJson = {
+            app: 'com.tencent.structmsg',
+            config: {
+                ctime: 1709689928,
+                forward: 1,
+                token: '5c1e4905f926dd3a64a4bd3841460351',
+                type: 'normal'
+            },
+            extra: {app_type: 1, appid: 100497308, uin: selfInfo.uin},
+            meta: {
+                news: {
+                    action: '',
+                    android_pkg_name: '',
+                    app_type: 1,
+                    appid: 100497308,
+                    ctime: 1709689928,
+                    desc: content || title,
+                    jumpUrl: url,
+                    musicUrl: audio,
+                    preview: image,
+                    source_icon: 'https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0',
+                    source_url: '',
+                    tag: 'QQ音乐',
+                    title: title,
+                    uin: selfInfo.uin,
+                }
+            },
+            prompt: content || title,
+            ver: '0.0.0.1',
+            view: 'news'
+        }
+
+        return SendMsgElementConstructor.ark(musicJson)
+    }
 }
 
 export default SendMsg
\ No newline at end of file
diff --git a/src/onebot11/constructor.ts b/src/onebot11/constructor.ts
index ff4c614..2baedf4 100644
--- a/src/onebot11/constructor.ts
+++ b/src/onebot11/constructor.ts
@@ -9,11 +9,12 @@ import {
     OB11UserSex
 } from "./types";
 import {AtType, ChatType, Group, GroupMember, IMAGE_HTTP_HOST, RawMessage, SelfInfo, User} from '../ntqqapi/types';
-import {fileCache, getFriend, getGroupMember, getHistoryMsgBySeq, selfInfo, tempGroupCodeMap} from '../common/data';
+import {fileCache, getFriend, getGroupMember, selfInfo, tempGroupCodeMap} from '../common/data';
 import {getConfigUtil, log} from "../common/utils";
 import {NTQQApi} from "../ntqqapi/ntcall";
 import {EventType} from "./event/OB11BaseEvent";
 import {encodeCQCode} from "./cqcode";
+import {dbUtil} from "../common/db";
 
 
 export class OB11Constructor {
@@ -95,7 +96,7 @@ export class OB11Constructor {
                 message_data["data"]["text"] = text
             } else if (element.replyElement) {
                 message_data["type"] = "reply"
-                const replyMsg = getHistoryMsgBySeq(element.replyElement.replayMsgSeq)
+                const replyMsg = await dbUtil.getMsgBySeqId(element.replyElement.replayMsgSeq)
                 if (replyMsg) {
                     message_data["data"]["id"] = replyMsg.msgShortId.toString()
                 } else {
diff --git a/src/onebot11/types.ts b/src/onebot11/types.ts
index fd1f1af..25015aa 100644
--- a/src/onebot11/types.ts
+++ b/src/onebot11/types.ts
@@ -93,6 +93,7 @@ export interface OB11Return<DataType> {
 export enum OB11MessageDataType {
     text = "text",
     image = "image",
+    music = "music",
     video = "video",
     voice = "record",
     file = "file",
@@ -100,7 +101,7 @@ export enum OB11MessageDataType {
     reply = "reply",
     json = "json",
     face = "face",
-    node = "node"  // 合并转发消息
+    node = "node",  // 合并转发消息
 }
 
 export interface OB11MessageText {
@@ -166,12 +167,24 @@ export interface OB11MessageNode {
     }
 }
 
+export interface OB11MessageCustomMusic{
+    type: OB11MessageDataType.music
+    data: {
+        type: "custom"
+        url: string,
+        audio: string,
+        title: string,
+        content?: string,
+        image?: string
+    }
+}
+
 export type OB11MessageData =
     OB11MessageText |
     OB11MessageFace |
     OB11MessageAt | OB11MessageReply |
     OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo |
-    OB11MessageNode
+    OB11MessageNode | OB11MessageCustomMusic
 
 export interface OB11PostSendMsg {
     message_type?: "private" | "group"
diff --git a/src/onebot11/utils.ts b/src/onebot11/utils.ts
index 0eaa083..68cab36 100644
--- a/src/onebot11/utils.ts
+++ b/src/onebot11/utils.ts
@@ -1,4 +1,4 @@
-import {CONFIG_DIR, isGIF, log} from "../common/utils";
+import {DATA_DIR, isGIF, log} from "../common/utils";
 import {v4 as uuidv4} from "uuid";
 import * as path from 'node:path';
 import {fileCache} from "../common/data";
@@ -10,7 +10,7 @@ export async function uri2local(uri: string, fileName: string = null) {
     if (!fileName) {
         fileName = uuidv4();
     }
-    let filePath = path.join(CONFIG_DIR, fileName)
+    let filePath = path.join(DATA_DIR, fileName)
     let url = new URL(uri);
     let res = {
         success: false,
@@ -40,7 +40,7 @@ export async function uri2local(uri: string, fileName: string = null) {
         let buffer = await blob.arrayBuffer();
         try {
             fileName = path.parse(url.pathname).name || fileName
-            filePath = path.join(CONFIG_DIR, uuidv4() + fileName)
+            filePath = path.join(DATA_DIR, uuidv4() + fileName)
             await fs.writeFile(filePath, Buffer.from(buffer));
         } catch (e: any) {
             res.errMsg = `${url}下载失败,` + e.toString()
diff --git a/src/version.ts b/src/version.ts
index 1620de1..d1ef5ec 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const version = "3.11.2"
\ No newline at end of file
+export const version = "3.12.0"
\ No newline at end of file