diff --git a/package-lock.json b/package-lock.json
index bad3962..3b11f41 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
       "devDependencies": {
         "@babel/preset-env": "^7.23.2",
         "@types/express": "^4.17.20",
+        "@types/uuid": "^9.0.8",
         "babel-loader": "^9.1.3",
         "electron": "^27.0.2",
         "ts-loader": "^9.5.0",
@@ -2305,6 +2306,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/uuid": {
+      "version": "9.0.8",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/@types/uuid/-/uuid-9.0.8.tgz",
+      "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
+      "dev": true
+    },
     "node_modules/@types/yauzl": {
       "version": "2.10.2",
       "resolved": "https://mirrors.cloud.tencent.com/npm/@types/yauzl/-/yauzl-2.10.2.tgz",
diff --git a/package.json b/package.json
index cd84ed2..f3327b2 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
   "devDependencies": {
     "@babel/preset-env": "^7.23.2",
     "@types/express": "^4.17.20",
+    "@types/uuid": "^9.0.8",
     "babel-loader": "^9.1.3",
     "electron": "^27.0.2",
     "ts-loader": "^9.5.0",
diff --git a/src/common/IPCChannel.ts b/src/common/channels.ts
similarity index 100%
rename from src/common/IPCChannel.ts
rename to src/common/channels.ts
diff --git a/src/main/config.ts b/src/common/config.ts
similarity index 94%
rename from src/main/config.ts
rename to src/common/config.ts
index 3ef443d..44b73d5 100644
--- a/src/main/config.ts
+++ b/src/common/config.ts
@@ -1,4 +1,4 @@
-import {Config} from "../common/types";
+import {Config} from "./types";
 
 const fs = require("fs")
 
diff --git a/src/common/data.ts b/src/common/data.ts
new file mode 100644
index 0000000..c9aa843
--- /dev/null
+++ b/src/common/data.ts
@@ -0,0 +1,30 @@
+import {Group, MessageElement, RawMessage, SelfInfo, User} from "./types";
+
+export let groups: Group[] = []
+export let friends: User[] = []
+
+export function getFriend(qq: string): User | undefined {
+    return friends.find(friend => friend.uin === qq)
+}
+
+export function getGroup(qq: string): Group | undefined {
+    return groups.find(group => group.uid === qq)
+}
+
+export function getGroupMember(groupQQ: string, memberQQ: string) {
+    const group = getGroup(groupQQ)
+    if (group) {
+        return group.members?.find(member => member.uin === memberQQ)
+    }
+}
+
+export let selfInfo: SelfInfo = {
+    user_id: "",
+    nickname: ""
+}
+
+export let msgHistory: Record<string, RawMessage> = {}
+
+export function getHistoryMsgBySeq(seq: string) {
+    return Object.values(msgHistory).find(msg => msg.msgSeq === seq)
+}
diff --git a/src/common/types.ts b/src/common/types.ts
index 1a00311..ae79f8a 100644
--- a/src/common/types.ts
+++ b/src/common/types.ts
@@ -1,3 +1,5 @@
+import {OB11ApiName, OB11MessageData} from "../onebot11/types";
+
 export enum AtType {
     notAt = 0,
     atAll = 1,
@@ -24,12 +26,6 @@ export interface GroupMemberInfo {
     uin: string; // QQ号
 }
 
-export const OnebotGroupMemberRole = {
-    4: 'owner',
-    3: 'admin',
-    2: 'member'
-}
-
 
 export interface SelfInfo {
     user_id: string;
@@ -79,42 +75,48 @@ export interface PttElement {
     waveAmplitudes: number[]
 }
 
-export interface ArkElement{
+export interface ArkElement {
     bytesData: string
 }
 
+export interface RawMessage {
+    msgId: string,
+    msgTime: string,
+    msgSeq: string,
+    senderUin: string; // 发送者QQ号
+    peerUid: string; // 群号 或者 QQ uid
+    peerUin: string; // 群号 或者 发送者QQ号
+    sendNickName: string;
+    sendMemberName?: string;  // 发送者群名片
+    chatType: ChatType,
+    elements: {
+        replyElement: {
+            senderUid: string,  // 原消息发送者QQ号
+            sourceMsgIsIncPic: boolean;  // 原消息是否有图片
+            sourceMsgText: string;
+            replayMsgSeq: string;  // 源消息的msgSeq,可以通过这个找到源消息的msgId
+        },
+        textElement: {
+            atType: AtType
+            atUid: string,
+            content: string,
+            atNtUid: string
+        },
+        picElement: {
+            sourcePath: string // 图片本地路径
+            picWidth: number
+            picHeight: number
+            fileSize: number
+            fileName: string
+            fileUuid: string
+        },
+        pttElement: PttElement,
+        arkElement: ArkElement
+    }[]
+}
+
 export interface MessageElement {
-    raw: {
-        msgId: string,
-        msgTime: string,
-        msgSeq: string,
-        senderUin: string; // 发送者QQ号
-        chatType: ChatType,
-        elements: {
-            replyElement: {
-                senderUid: string,  // 原消息发送者QQ号
-                sourceMsgIsIncPic: boolean;  // 原消息是否有图片
-                sourceMsgText: string;
-                replayMsgSeq: string;  // 源消息的msgSeq,可以通过这个找到源消息的msgId
-            },
-            textElement: {
-                atType: AtType
-                atUid: string,
-                content: string,
-                atNtUid: string
-            },
-            picElement: {
-                sourcePath: string // 图片本地路径
-                picWidth: number
-                picHeight: number
-                fileSize: number
-                fileName: string
-                fileUuid: string
-            },
-            pttElement: PttElement,
-            arkElement: ArkElement
-        }[]
-    }
+    raw: RawMessage
     peer: Peer,
     sender: {
         uid: string  // 一串加密的字符串
@@ -123,60 +125,17 @@ export interface MessageElement {
     }
 }
 
-export enum MessageType {
-    text = "text",
-    image = "image",
-    voice = "record",
-    at = "at",
-    reply = "reply",
-    json = "json"
-}
-
-export type SendMessage = {
-    type: MessageType.text,
-    content: string,
-    data?: {
-        text: string, // 纯文本
-    }
-} | {
-    type: "image" | "voice" | "record",
-    file: string, // 本地路径
-    data?: {
-        file: string // 本地路径
-    }
-} | {
-    type: MessageType.at,
-    atType?: AtType,
-    content?: string,
-    atUid?: string,
-    atNtUid?: string,
-    data?: {
-        qq: string // at的qq号
-    }
-} | {
-    type: MessageType.reply,
-    msgId: string,
-    msgSeq: string,
-    senderUin: string,
-    data: {
-        id: string,
-    }
-}
-
-export type PostDataAction = "send_private_msg" | "send_group_msg" | "get_group_list"
-    | "get_friend_list" | "delete_msg" | "get_login_info" | "get_group_member_list" | "get_group_member_info"
-
 export interface PostDataSendMsg {
-    action: PostDataAction
+    action: OB11ApiName
     message_type?: "private" | "group"
     params?: {
         user_id: string,
         group_id: string,
-        message: SendMessage[];
+        message: OB11MessageData[];
     },
     user_id: string,
     group_id: string,
-    message: SendMessage[];
+    message?: OB11MessageData[];
     ipc_uuid?: string
 }
 
@@ -189,9 +148,3 @@ export interface Config {
     log?: boolean
 }
 
-export interface SendMsgResult {
-    status: number
-    retcode: number
-    data: any
-    message: string
-}
\ No newline at end of file
diff --git a/src/main/utils.ts b/src/common/utils.ts
similarity index 60%
rename from src/main/utils.ts
rename to src/common/utils.ts
index bfafa66..04262b2 100644
--- a/src/main/utils.ts
+++ b/src/common/utils.ts
@@ -2,6 +2,7 @@ import * as path from "path";
 import {json} from "express";
 import {selfInfo} from "./data";
 import {ConfigUtil} from "./config";
+import util from "util";
 
 const fs = require('fs');
 
@@ -12,7 +13,7 @@ export function getConfigUtil() {
     return new ConfigUtil(configFilePath)
 }
 
-export function log(msg: any) {
+export function log(...msg: any[]) {
     if (!getConfigUtil().getConfig().log){
         return
     }
@@ -23,7 +24,17 @@ export function log(msg: any) {
     const day = date.getDate();
     const currentDate = `${year}-${month}-${day}`;
     const userInfo = selfInfo.user_id ? `${selfInfo.nickname}(${selfInfo.user_id})` : ""
-    fs.appendFile(path.join(CONFIG_DIR , `llonebot-${currentDate}.log`), currentDateTime + ` ${userInfo}:` + JSON.stringify(msg) + "\n", (err: any) => {
+    let logMsg = "";
+    for (let msgItem of msg){
+        // 判断是否是对象
+        if (typeof msgItem === "object"){
+            logMsg += JSON.stringify(msgItem) + " ";
+            continue;
+        }
+        logMsg += msgItem + " ";
+    }
+    logMsg = `${currentDateTime} ${userInfo}: ${logMsg}\n`
+    fs.appendFile(path.join(CONFIG_DIR , `llonebot-${currentDate}.log`), logMsg, (err: any) => {
 
     })
 }
@@ -55,3 +66,29 @@ export function checkFileReceived(path: string, timeout: number=3000): Promise<v
         check();
     });
 }
+
+export async function file2base64(path: string){
+    const readFile = util.promisify(fs.readFile);
+    let result = {
+        err: "",
+        data: ""
+    }
+    try {
+        // 读取文件内容
+        // if (!fs.existsSync(path)){
+        //     path = path.replace("\\Ori\\", "\\Thumb\\");
+        // }
+        try {
+            await checkFileReceived(path, 5000);
+        } catch (e: any) {
+            result.err = e.toString();
+            return result;
+        }
+        const data = await readFile(path);
+        // 转换为Base64编码
+        result.data = data.toString('base64');
+    } catch (err) {
+        result.err = err.toString();
+    }
+    return result;
+}
diff --git a/src/global.d.ts b/src/global.d.ts
index 221edbf..646642c 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -6,11 +6,13 @@ import {
     Peer,
     PostDataSendMsg, PttElement,
     SelfInfo,
-    SendMessage, SendMsgResult,
     User
 } from "./common/types";
 
 
+import {OB11Return, OB11MessageData} from "./onebot11/types";
+
+
 declare var LLAPI: {
     on(event: "new-messages" | "new-send-messages", callback: (data: MessageElement[]) => void): void;
     on(event: "context-msg-menu", callback: (event: any, target: any, msgIds:any) => void): void;
@@ -20,7 +22,7 @@ declare var LLAPI: {
     }>
 
     getUserInfo(uid: string): Promise<User>; // uid是一串加密的字符串
-    sendMessage(peer: Peer, message: SendMessage[]): Promise<any>;
+    sendMessage(peer: Peer, message: OB11MessageData[]): Promise<any>;
     recallMessage(peer: Peer, msgIds: string[]): Promise<void>;
     getGroupsList(forced: boolean): Promise<Group[]>
     getFriendsList(forced: boolean): Promise<User[]>
@@ -47,7 +49,7 @@ declare var llonebot: {
     downloadFile(arg: {uri: string, fileName: string}):Promise<{errMsg: string, path: string}>;
     deleteFile(path: string[]):Promise<void>;
     getRunningStatus(): Promise<boolean>;
-    sendSendMsgResult(sessionId: string, msgResult: SendMsgResult): void;
+    sendSendMsgResult(sessionId: string, msgResult: OB11Return): void;
     file2base64(path: string): Promise<{err: string, data: string}>;
 };
 
diff --git a/src/main/data.ts b/src/main/data.ts
deleted file mode 100644
index 49c09f8..0000000
--- a/src/main/data.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {Group, SelfInfo, User} from "../common/types";
-
-export let groups: Group[] = []
-export let friends: User[] = []
-
-export let selfInfo: SelfInfo = {
-    user_id: "",
-    nickname: ""
-}
\ No newline at end of file
diff --git a/src/main/IPCSend.ts b/src/main/ipcsend.ts
similarity index 74%
rename from src/main/IPCSend.ts
rename to src/main/ipcsend.ts
index 2bf00c4..1a026af 100644
--- a/src/main/IPCSend.ts
+++ b/src/main/ipcsend.ts
@@ -1,8 +1,11 @@
 import {ipcMain, webContents} from 'electron';
-import {PostDataSendMsg, SendMsgResult} from "../common/types";
-import {CHANNEL_RECALL_MSG, CHANNEL_SEND_MSG} from "../common/IPCChannel";
+import {PostDataSendMsg} from "../common/types";
+import {CHANNEL_RECALL_MSG, CHANNEL_SEND_MSG} from "../common/channels";
 import {v4 as uuid4} from "uuid";
-import {log} from "./utils";
+import {log} from "../common/utils";
+
+
+import {OB11Return} from "../onebot11/types";
 
 function sendIPCMsg(channel: string, data: any) {
     let contents = webContents.getAllWebContents();
@@ -16,10 +19,10 @@ function sendIPCMsg(channel: string, data: any) {
 }
 
 
-export function sendIPCSendQQMsg(postData: PostDataSendMsg, handleSendResult: (data: SendMsgResult) => void) {
+export function sendIPCSendQQMsg(postData: PostDataSendMsg, handleSendResult: (data: OB11Return<any>) => void) {
     const onceSessionId = "llonebot_send_msg_" + uuid4();
     postData.ipc_uuid = onceSessionId;
-    ipcMain.once(onceSessionId, (event: any, sendResult: SendMsgResult) => {
+    ipcMain.once(onceSessionId, (event: any, sendResult: OB11Return<any>) => {
         // log("llonebot send msg ipcMain.once:" + JSON.stringify(sendResult));
         try {
             handleSendResult(sendResult)
diff --git a/src/main/main.ts b/src/main/main.ts
index c0840cd..8bd3457 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -1,10 +1,10 @@
 // 运行在 Electron 主进程 下的插件入口
 
 import * as path from "path";
-import {ipcMain} from 'electron';
+import {BrowserWindow, ipcMain} from 'electron';
 import * as util from 'util';
 
-import {Config, Group, SelfInfo, User} from "../common/types";
+import {Config, Group, RawMessage, SelfInfo, User} from "../common/types";
 import {
     CHANNEL_DOWNLOAD_FILE,
     CHANNEL_GET_CONFIG,
@@ -15,12 +15,14 @@ import {
     CHANNEL_START_HTTP_SERVER,
     CHANNEL_UPDATE_FRIENDS,
     CHANNEL_UPDATE_GROUPS, CHANNEL_DELETE_FILE, CHANNEL_GET_RUNNING_STATUS, CHANNEL_FILE2BASE64
-} from "../common/IPCChannel";
-import {ConfigUtil} from "./config";
-import {startExpress} from "./HttpServer";
-import {checkFileReceived, CONFIG_DIR, getConfigUtil, isGIF, log} from "./utils";
-import {friends, groups, selfInfo} from "./data";
+} from "../common/channels";
+import {ConfigUtil} from "../common/config";
+import {postMsg, startExpress} from "../server/httpserver";
+import {checkFileReceived, CONFIG_DIR, file2base64, getConfigUtil, isGIF, log} from "../common/utils";
+import {friends, groups, msgHistory, selfInfo} from "../common/data";
 import {} from "../global";
+import {hookNTQQApiReceive, ReceiveCmd, registerReceiveHook} from "../ntqqapi/hook";
+import {OB11Construct} from "../onebot11/construct";
 
 const fs = require('fs');
 
@@ -29,7 +31,7 @@ let running = false;
 
 // 加载插件时触发
 function onLoad() {
-    log("main onLoaded");
+    log("llonebot main onLoad");
 
     // const config_dir = browserWindow.LiteLoader.plugins["LLOneBot"].path.data;
 
@@ -138,24 +140,7 @@ function onLoad() {
     })
 
     ipcMain.on(CHANNEL_POST_ONEBOT_DATA, (event: any, arg: any) => {
-        for (const host of getConfigUtil().getConfig().hosts) {
-            try {
-                fetch(host, {
-                    method: "POST",
-                    headers: {
-                        "Content-Type": "application/json",
-                        "x-self-id": selfInfo.user_id
-                    },
-                    body: JSON.stringify(arg)
-                }).then((res: any) => {
-                    log(`新消息事件上传成功: ${host} ` + JSON.stringify(arg));
-                }, (err: any) => {
-                    log(`新消息事件上传失败: ${host} ` + err + JSON.stringify(arg));
-                });
-            } catch (e: any) {
-                log(e.toString())
-            }
-        }
+        postMsg(arg);
     })
 
     ipcMain.on(CHANNEL_LOG, (event: any, arg: any) => {
@@ -179,36 +164,26 @@ function onLoad() {
     })
 
     ipcMain.handle(CHANNEL_FILE2BASE64, async (event: any, path: string): Promise<{err: string, data: string}> => {
-        const readFile = util.promisify(fs.readFile);
-        let result = {
-            err: "",
-            data: ""
+        return await file2base64(path);
+    })
+
+    registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, (payload) => {
+        for (const message of payload.msgList) {
+            OB11Construct.constructMessage(message).then((msg) => {
+                postMsg(msg);
+            });
         }
-        try {
-            // 读取文件内容
-            // if (!fs.existsSync(path)){
-            //     path = path.replace("\\Ori\\", "\\Thumb\\");
-            // }
-            try {
-                await checkFileReceived(path, 5000);
-            } catch (e: any) {
-                result.err = e.toString();
-                return result;
-            }
-            const data = await readFile(path);
-            // 转换为Base64编码
-            result.data = data.toString('base64');
-        } catch (err) {
-            result.err = err.toString();
-        }
-        return result;
     })
 }
 
 
 // 创建窗口时触发
-function onBrowserWindowCreated(window: any) {
-
+function onBrowserWindowCreated(window: BrowserWindow) {
+    try {
+        hookNTQQApiReceive(window);
+    } catch (e){
+        log("llonebot hook error: ", e.toString())
+    }
 }
 
 try {
diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts
new file mode 100644
index 0000000..9a077e9
--- /dev/null
+++ b/src/ntqqapi/hook.ts
@@ -0,0 +1,69 @@
+import {BrowserWindow} from 'electron';
+import {log} from "../common/utils";
+import {NTQQApiClass} from "./ntcall";
+import {RawMessage} from "../common/types";
+import {msgHistory} from "../common/data";
+
+export enum ReceiveCmd {
+    UPDATE_MSG = "nodeIKernelMsgListener/onMsgInfoListUpdate",
+    NEW_MSG = "nodeIKernelMsgListener/onRecvMsg"
+}
+
+interface NTQQApiReturnData extends Array<any> {
+    0: {
+        "type": "request",
+        "eventName": NTQQApiClass
+    },
+    1:
+        {
+            cmdName: ReceiveCmd,
+            cmdType: "event",
+            payload: unknown
+        }[]
+}
+
+let receiveHooks: Array<{
+    method: ReceiveCmd,
+    hookFunc: (payload: unknown) => void
+}> = []
+
+export function hookNTQQApiReceive(window: BrowserWindow) {
+    const originalSend = window.webContents.send;
+    const patchSend = (channel: string, ...args: NTQQApiReturnData) => {
+        // 判断是否是列表
+        if (args?.[1] instanceof Array) {
+            for (let receiveData of args?.[1]) {
+                const ntQQApiMethodName = receiveData.cmdName;
+                log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData))
+                for (let hook of receiveHooks) {
+                    if (hook.method === ntQQApiMethodName) {
+                        hook.hookFunc(receiveData.payload);
+                    }
+                }
+            }
+        }
+
+        return originalSend.call(window.webContents, channel, ...args);
+    }
+    window.webContents.send = patchSend;
+}
+
+export function registerReceiveHook<PayloadType>(method: ReceiveCmd, hookFunc: (payload: PayloadType) => void) {
+    receiveHooks.push({
+        method,
+        hookFunc
+    })
+}
+
+registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.UPDATE_MSG, (payload) => {
+    for (const message of payload.msgList) {
+        msgHistory[message.msgId] = message;
+    }
+})
+
+registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, (payload) => {
+    for (const message of payload.msgList) {
+        log("收到新消息,push到历史记录", message)
+        msgHistory[message.msgId] = message;
+    }
+})
\ No newline at end of file
diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts
new file mode 100644
index 0000000..dfdccff
--- /dev/null
+++ b/src/ntqqapi/ntcall.ts
@@ -0,0 +1,65 @@
+import {ipcMain} from "electron";
+import {v4 as uuidv4} from "uuid";
+
+interface IPCReceiveEvent {
+    eventName: string
+    callbackId: string
+}
+
+export type IPCReceiveDetail = [
+    {
+        cmdName: NTQQApiMethod
+        payload: unknown
+    },
+]
+
+export enum NTQQApiClass {
+    NT_API = "ns-ntApi",
+    NT_FS_API = "ns-FsApi",
+}
+
+export enum NTQQApiMethod {
+    LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike",
+    UPDATE_MSG = "nodeIKernelMsgListener/onMsgInfoListUpdate"
+}
+
+enum NTQQApiChannel {
+    IPC_UP_2 = "IPC_UP_2",
+    IPC_UP_3 = "IPC_UP_3",
+    IPC_UP_1 = "IPC_UP_1",
+}
+
+
+function callNTQQApi(channel: NTQQApiChannel, className: NTQQApiClass, methodName: NTQQApiMethod, args: unknown[]=[]) {
+    const uuid = uuidv4();
+    return new Promise((resolve, reject)  => {
+        ipcMain.emit(
+            channel,
+            {
+                sender: {
+                    send: (args: [string, IPCReceiveEvent, IPCReceiveDetail]) => {
+                        resolve(args)
+                    },
+                },
+            },
+            {type: 'request', callbackId: uuid, eventName: className + "-" + channel[channel.length - 1]},
+            [methodName, ...args],
+        )
+    })
+}
+
+
+export class NTQQApi {
+    // static likeFriend = defineNTQQApi<void>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.LIKE_FRIEND)
+    static likeFriend(uid: string, count = 1) {
+        return callNTQQApi(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.LIKE_FRIEND, [{
+                doLikeUserInfo: {
+                    friendUid: uid,
+                    sourceId: 71,
+                    doLikeCount: count,
+                    doLikeTollCount: 0
+                }
+            },
+            null])
+    }
+}
\ No newline at end of file
diff --git a/src/onebot11/construct.ts b/src/onebot11/construct.ts
new file mode 100644
index 0000000..fd32c18
--- /dev/null
+++ b/src/onebot11/construct.ts
@@ -0,0 +1,134 @@
+import {OB11MessageDataType, OB11GroupMemberRole, OB11Message, OB11MessageData} from "./types";
+import {AtType, ChatType, RawMessage} from "../common/types";
+import {getFriend, getGroupMember, getHistoryMsgBySeq, msgHistory, selfInfo} from "../common/data";
+import {file2base64, getConfigUtil} from "../common/utils";
+
+
+export class OB11Construct {
+    static async constructMessage(msg: RawMessage): Promise<OB11Message> {
+        const {debug, enableBase64} = getConfigUtil().getConfig()
+        const message_type = msg.chatType == ChatType.group ? "group" : "private";
+        const resMsg: OB11Message = {
+            self_id: selfInfo.user_id,
+            user_id: msg.senderUin,
+            time: parseInt(msg.msgTime) || 0,
+            message_id: msg.msgId,
+            real_id: msg.msgId,
+            message_type: msg.chatType == ChatType.group ? "group" : "private",
+            sender: {
+                user_id: msg.senderUin,
+                nickname: msg.sendNickName,
+                card: msg.sendMemberName || "",
+            },
+            raw_message: "",
+            font: 14,
+            sub_type: "friend",
+            message: [],
+            post_type: "message",
+        }
+        if (msg.chatType == ChatType.group) {
+            resMsg.group_id = msg.peerUin
+            const member = getGroupMember(msg.peerUin, msg.senderUin);
+            if (member) {
+                resMsg.sender.role = OB11Construct.constructGroupMemberRole(member.role);
+            }
+        } else if (msg.chatType == ChatType.friend) {
+            resMsg.sub_type = "friend"
+            const friend = getFriend(msg.senderUin);
+            if (friend) {
+                resMsg.sender.nickname = friend.nickName;
+            }
+        } else if (msg.chatType == ChatType.temp) {
+            resMsg.sub_type = "group"
+        }
+
+
+        if (debug) {
+            resMsg.raw = msg
+        }
+
+        for (let element of msg.elements) {
+            let message_data: any = {
+                data: {},
+                type: "unknown"
+            }
+            if (element.textElement && element.textElement?.atType !== AtType.notAt) {
+                message_data["type"] = OB11MessageDataType.at
+                if (element.textElement.atType == AtType.atAll) {
+                    message_data["data"]["mention"] = "all"
+                    message_data["data"]["qq"] = "all"
+                } else {
+                    let uid = element.textElement.atNtUid
+                    let atMember = getGroupMember(msg.peerUin, uid)
+                    message_data["data"]["mention"] = atMember!.uin
+                    message_data["data"]["qq"] = atMember!.uin
+                }
+            } else if (element.textElement) {
+                message_data["type"] = "text"
+                message_data["data"]["text"] = element.textElement.content
+            } else if (element.picElement) {
+                message_data["type"] = "image"
+                message_data["data"]["file_id"] = element.picElement.fileUuid
+                message_data["data"]["path"] = element.picElement.sourcePath
+                message_data["data"]["file"] = element.picElement.sourcePath
+            } else if (element.replyElement) {
+                message_data["type"] = "reply"
+                const replyMsg = getHistoryMsgBySeq(element.replyElement.replayMsgSeq)
+                if (replyMsg) {
+                    message_data["data"]["id"] = replyMsg.msgId
+                }
+                else{
+                    continue
+                }
+            } else if (element.pttElement) {
+                message_data["type"] = OB11MessageDataType.voice;
+                message_data["data"]["file"] = element.pttElement.filePath
+                message_data["data"]["file_id"] = element.pttElement.fileUuid
+                // console.log("收到语音消息", message.raw.msgId, message.peer, element.pttElement)
+                // window.LLAPI.Ptt2Text(message.raw.msgId, message.peer, messages).then(text => {
+                //     console.log("语音转文字结果", text);
+                // }).catch(err => {
+                //     console.log("语音转文字失败", err);
+                // })
+            } else if (element.arkElement) {
+                message_data["type"] = OB11MessageDataType.json;
+                message_data["data"]["data"] = element.arkElement.bytesData;
+            }
+            if (message_data.data.file) {
+                let filePath: string = message_data.data.file;
+                message_data.data.file = "file://" + filePath
+                if (enableBase64) {
+                    // filePath = filePath.replace("\\Ori\\", "\\Thumb\\")
+                    let {err, data} = await file2base64(filePath);
+                    if (err) {
+                        console.log("文件转base64失败", err)
+                    } else {
+                        message_data.data.file = "base64://" + data
+                    }
+                }
+            }
+            if (message_data.type !== "unknown" && message_data.data) {
+                resMsg.message.push(message_data);
+            }
+        }
+        // if (msgHistory.length > 10000) {
+        //     msgHistory.splice(0, 100)
+        // }
+        // msgHistory.push(message)
+        // if (!reportSelfMessage && onebot_message_data["user_id"] == self_qq) {
+        //     console.log("开启了不上传自己发送的消息,进行拦截 ", onebot_message_data);
+        // } else {
+        //     console.log("发送上传消息给ipc main", onebot_message_data);
+        //     window.llonebot.postData(onebot_message_data);
+        // }
+        return resMsg;
+    }
+
+    static constructGroupMemberRole(role: number): OB11GroupMemberRole {
+        return {
+            4: OB11GroupMemberRole.owner,
+            3: OB11GroupMemberRole.admin,
+            2: OB11GroupMemberRole.member
+        }[role]
+    }
+}
\ No newline at end of file
diff --git a/src/onebot11/types.ts b/src/onebot11/types.ts
new file mode 100644
index 0000000..34975d6
--- /dev/null
+++ b/src/onebot11/types.ts
@@ -0,0 +1,105 @@
+import {AtType, RawMessage} from "../common/types";
+
+export enum OB11UserSex{
+    male = "male",
+    female = "female",
+    unknown = "unknown"
+}
+
+export enum OB11GroupMemberRole{
+    owner = "owner",
+    admin = "admin",
+    member = "member",
+}
+
+interface OB11Sender {
+    user_id: string,
+    nickname: string,
+    sex?: OB11UserSex,
+    age?: number,
+    card?: string,  // 群名片
+    level?: string,  // 群等级
+    role?: OB11GroupMemberRole
+}
+
+export enum OB11MessageType {
+    private = "private",
+    group = "group"
+}
+
+export interface OB11Message {
+    self_id?: string,
+    time: number,
+    message_id: string,
+    real_id: string,
+    user_id: string,
+    group_id?: string,
+    message_type: "private" | "group",
+    sub_type?: "friend" | "group" | "other",
+    sender: OB11Sender,
+    message: OB11MessageData[],
+    raw_message: string,
+    font: number,
+    post_type?: "message",
+    raw?: RawMessage
+}
+
+export type OB11ApiName =
+    "send_private_msg"
+    | "send_group_msg"
+    | "get_group_list"
+    | "get_friend_list"
+    | "delete_msg"
+    | "get_login_info"
+    | "get_group_member_list"
+    | "get_group_member_info"
+    | "get_msg"
+
+export interface OB11Return<DataType> {
+    status: number
+    retcode: number
+    data: DataType
+    message: string
+}
+
+export interface OB11SendMsgReturn extends OB11Return<{message_id: string}>{}
+
+export enum OB11MessageDataType {
+    text = "text",
+    image = "image",
+    voice = "record",
+    at = "at",
+    reply = "reply",
+    json = "json"
+}
+
+export type OB11MessageData = {
+    type: OB11MessageDataType.text,
+    content: string,
+    data?: {
+        text: string, // 纯文本
+    }
+} | {
+    type: "image" | "voice" | "record",
+    file: string, // 本地路径
+    data?: {
+        file: string // 本地路径
+    }
+} | {
+    type: OB11MessageDataType.at,
+    atType?: AtType,
+    content?: string,
+    atUid?: string,
+    atNtUid?: string,
+    data?: {
+        qq: string // at的qq号
+    }
+} | {
+    type: OB11MessageDataType.reply,
+    msgId: string,
+    msgSeq: string,
+    senderUin: string,
+    data: {
+        id: string,
+    }
+}
\ No newline at end of file
diff --git a/src/preload.ts b/src/preload.ts
index 93be597..06922ab 100644
--- a/src/preload.ts
+++ b/src/preload.ts
@@ -1,6 +1,6 @@
 // Electron 主进程 与 渲染进程 交互的桥梁
 
-import {Config, Group, PostDataSendMsg, SelfInfo, SendMsgResult, User} from "./common/types";
+import {Config, Group, PostDataSendMsg, SelfInfo, User} from "./common/types";
 import {
     CHANNEL_DOWNLOAD_FILE,
     CHANNEL_GET_CONFIG,
@@ -15,7 +15,10 @@ import {
     CHANNEL_UPDATE_GROUPS,
     CHANNEL_DELETE_FILE,
     CHANNEL_GET_RUNNING_STATUS, CHANNEL_FILE2BASE64
-} from "./common/IPCChannel";
+} from "./common/channels";
+
+
+import {OB11Return, OB11SendMsgReturn} from "./onebot11/types";
 
 
 const {contextBridge} = require("electron");
@@ -33,7 +36,7 @@ contextBridge.exposeInMainWorld("llonebot", {
     updateFriends: (friends: User[]) => {
         ipcRenderer.send(CHANNEL_UPDATE_FRIENDS, friends);
     },
-    sendSendMsgResult: (sessionId: string, msgResult: SendMsgResult)=>{
+    sendSendMsgResult: (sessionId: string, msgResult: OB11SendMsgReturn)=>{
         ipcRenderer.send(sessionId, msgResult);
     },
     listenSendMessage: (handle: (jsonData: PostDataSendMsg) => void) => {
diff --git a/src/renderer.ts b/src/renderer.ts
index 9c50b0e..041ed23 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -4,14 +4,14 @@ import {
     AtType,
     ChatType,
     Group,
-    MessageElement, MessageType,
-    OnebotGroupMemberRole,
-    Peer,
+    MessageElement, Peer,
     PostDataSendMsg,
-    SendMsgResult,
     User
 } from "./common/types";
 
+
+import {OB11Return, OB11SendMsgReturn, OB11MessageDataType} from "./onebot11/types";
+
 let self_qq: string = ""
 let groups: Group[] = []
 let friends: User[] = []
@@ -120,142 +120,15 @@ async function getGroupMember(group_qq: string, member_uid: string) {
     }
 }
 
-async function handleNewMessage(messages: MessageElement[]) {
-    console.log("llonebot 收到消息:", messages);
-    const {debug, enableBase64, reportSelfMessage} = await window.llonebot.getConfig();
-    for (let message of messages) {
-        let onebot_message_data: any = {
-            self: {
-                platform: "qq",
-                user_id: self_qq
-            },
-            self_id: self_qq,
-            time: parseInt(message.raw.msgTime || "0"),
-            type: "message",
-            post_type: "message",
-            message_type: message.peer.chatType,
-            detail_type: message.peer.chatType,
-            message_id: message.raw.msgId,
-            sub_type: "",
-            message: [],
-            raw_message: "",
-            font: 14
-        }
-        if (debug) {
-            onebot_message_data.raw = JSON.parse(JSON.stringify(message))
-        }
-        if (message.raw.chatType == ChatType.group) {
-            let group_id = message.peer.uid
-            let group = (await getGroup(group_id))!
-            onebot_message_data.detail_type = onebot_message_data.message_type = onebot_message_data.sub_type = "group"
-            onebot_message_data["group_id"] = message.peer.uid
-            let groupMember = await getGroupMember(group_id, message.sender.uid)
-            onebot_message_data["user_id"] = groupMember!.uin
-            onebot_message_data.sender = {
-                user_id: groupMember!.uin,
-                nickname: groupMember!.nick,
-                card: groupMember!.cardName,
-                role: OnebotGroupMemberRole[groupMember!.role]
-            }
-            // console.log("收到群消息", onebot_message_data)
-        } else if (message.raw.chatType == ChatType.friend) {
-            onebot_message_data["user_id"] = message.raw.senderUin;
-            onebot_message_data.detail_type = onebot_message_data.message_type = "private"
-            onebot_message_data.sub_type = "friend"
-            let friend = await getFriend(message.raw.senderUin);
-            onebot_message_data.sender = {
-                user_id: friend!.uin,
-                nickname: friend!.nickName
-            }
-        } else if (message.raw.chatType == ChatType.temp) {
-            let senderQQ = message.raw.senderUin;
-            let senderUid = message.sender.uid;
-            let sender = await getUserInfo(senderUid);
-            onebot_message_data["user_id"] = senderQQ;
-            onebot_message_data.detail_type = onebot_message_data.message_type = "private"
-            onebot_message_data.sub_type = "group";
-            onebot_message_data.sender = {
-                user_id: senderQQ,
-                nickname: sender.nickName
-            }
-        }
-        for (let element of message.raw.elements) {
-            let message_data: any = {
-                data: {},
-                type: "unknown"
-            }
-            if (element.textElement && element.textElement?.atType !== AtType.notAt) {
-                message_data["type"] = "at"
-                if (element.textElement.atType == AtType.atAll) {
-                    message_data["data"]["mention"] = "all"
-                    message_data["data"]["qq"] = "all"
-                } else {
-                    let uid = element.textElement.atNtUid
-                    let atMember = await getGroupMember(message.peer.uid, uid)
-                    message_data["data"]["mention"] = atMember!.uin
-                    message_data["data"]["qq"] = atMember!.uin
-                }
-            } else if (element.textElement) {
-                message_data["type"] = "text"
-                message_data["data"]["text"] = element.textElement.content
-            } else if (element.picElement) {
-                message_data["type"] = "image"
-                message_data["data"]["file_id"] = element.picElement.fileUuid
-                message_data["data"]["path"] = element.picElement.sourcePath
-                message_data["data"]["file"] = element.picElement.sourcePath
-            } else if (element.replyElement) {
-                message_data["type"] = "reply"
-                message_data["data"]["id"] = msgHistory.find(msg => msg.raw.msgSeq == element.replyElement.replayMsgSeq)?.raw.msgId
-            } else if (element.pttElement) {
-                message_data["type"] = MessageType.voice;
-                message_data["data"]["file"] = element.pttElement.filePath
-                message_data["data"]["file_id"] = element.pttElement.fileUuid
-                // console.log("收到语音消息", message.raw.msgId, message.peer, element.pttElement)
-                // window.LLAPI.Ptt2Text(message.raw.msgId, message.peer, messages).then(text => {
-                //     console.log("语音转文字结果", text);
-                // }).catch(err => {
-                //     console.log("语音转文字失败", err);
-                // })
-            } else if (element.arkElement) {
-                message_data["type"] = MessageType.json;
-                message_data["data"]["data"] = element.arkElement.bytesData;
-            }
-            if (message_data.data.file) {
-                let filePath: string = message_data.data.file;
-                message_data.data.file = "file://" + filePath
-                if (enableBase64) {
-                    // filePath = filePath.replace("\\Ori\\", "\\Thumb\\")
-                    let {err, data} = await window.llonebot.file2base64(filePath);
-                    if (err) {
-                        console.log("文件转base64失败", err)
-                    } else {
-                        message_data.data.file = "base64://" + data
-                    }
-                }
-            }
-            if (message_data.type !== "unknown"){
-                onebot_message_data.message.push(message_data);
-            }
-        }
-        if (msgHistory.length > 10000) {
-            msgHistory.splice(0, 100)
-        }
-        msgHistory.push(message)
-        if (!reportSelfMessage && onebot_message_data["user_id"] == self_qq){
-            console.log("开启了不上传自己发送的消息,进行拦截 ", onebot_message_data);
-        } else {
-            console.log("发送上传消息给ipc main", onebot_message_data);
-            window.llonebot.postData(onebot_message_data);
-        }
-    }
-}
 
 async function listenSendMessage(postData: PostDataSendMsg) {
     console.log("收到发送消息请求", postData);
-    let sendMsgResult: SendMsgResult = {
+    let sendMsgResult: OB11SendMsgReturn = {
         retcode: 0,
         status: 0,
-        data: {},
+        data: {
+            message_id: ""
+        },
         message: "发送成功"
     }
     if (postData.action == "send_private_msg" || postData.action == "send_group_msg") {
@@ -319,8 +192,7 @@ async function listenSendMessage(postData: PostDataSendMsg) {
                         message.atType = AtType.atAll
                         atUid = "0";
                         message.content = `@全体成员`
-                    }
-                    else {
+                    } else {
                         let group = await getGroup(postData.params.group_id)
                         let atMember = group.members.find(member => member.uin == atUid)
                         message.atNtUid = atMember.uid
@@ -374,11 +246,13 @@ async function listenSendMessage(postData: PostDataSendMsg) {
                 window.llonebot.sendSendMsgResult(postData.ipc_uuid, sendMsgResult)
                 return;
             }
-            window.LLAPI.sendMessage(peer, postData.params.message).then(res => {
-                    console.log("消息发送成功:", peer, postData.params.message)
+            window.LLAPI.sendMessage(peer, postData.params.message).then(
+                (res: MessageElement) => {
+                    console.log("消息发送成功:", res, peer, postData.params.message)
                     if (sendFiles.length) {
                         window.llonebot.deleteFile(sendFiles);
                     }
+                    sendMsgResult.data.message_id = res.raw.msgId;
                     window.llonebot.sendSendMsgResult(postData.ipc_uuid, sendMsgResult)
                 },
                 err => {
@@ -421,19 +295,6 @@ async function getGroupsMembers(groupsArg: Group[]) {
     }
 }
 
-function onNewMessages(messages: MessageElement[]) {
-    async function func(messages: MessageElement[]) {
-        console.log("收到新消息", messages)
-        if (!self_qq) {
-            self_qq = (await window.LLAPI.getAccountInfo()).uin
-        }
-        await handleNewMessage(messages);
-    }
-
-    func(messages).then(() => {
-    })
-    // console.log("chatListEle", chatListEle)
-}
 
 async function initAccountInfo() {
     let accountInfo = await window.LLAPI.getAccountInfo();
@@ -471,8 +332,6 @@ function onLoad() {
                         });
                     });
                 }
-                window.LLAPI.on("new-messages", onNewMessages);
-                window.LLAPI.on("new-send-messages", onNewMessages);
                 window.llonebot.log("llonebot render start");
                 window.llonebot.startExpress();
 
diff --git a/src/main/HttpServer.ts b/src/server/httpserver.ts
similarity index 67%
rename from src/main/HttpServer.ts
rename to src/server/httpserver.ts
index f3fa148..105aa20 100644
--- a/src/main/HttpServer.ts
+++ b/src/server/httpserver.ts
@@ -1,13 +1,20 @@
-import {log} from "./utils";
+import {getConfigUtil, log} from "../common/utils";
+
+// const express = require("express");
+import express from "express";
+import {Request} from 'express';
+import {Response} from 'express';
 
-const express = require("express");
 const JSONbig = require('json-bigint');
-import {sendIPCRecallQQMsg, sendIPCSendQQMsg} from "./IPCSend";
-import {OnebotGroupMemberRole, PostDataAction, PostDataSendMsg, SendMessage, SendMsgResult} from "../common/types";
-import {friends, groups, selfInfo} from "./data";
+import {sendIPCRecallQQMsg, sendIPCSendQQMsg} from "../main/ipcsend";
+import {PostDataSendMsg} from "../common/types";
+import {friends, groups, msgHistory, selfInfo} from "../common/data";
+import {OB11ApiName, OB11Message, OB11Return, OB11MessageData} from "../onebot11/types";
+import {OB11Construct} from "../onebot11/construct";
+
 
 // @SiberianHusky 2021-08-15
-function checkSendMessage(sendMsgList: SendMessage[]) {
+function checkSendMessage(sendMsgList: OB11MessageData[]) {
     function checkUri(uri: string): boolean {
         const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/;
         return pattern.test(uri);
@@ -44,7 +51,17 @@ function checkSendMessage(sendMsgList: SendMessage[]) {
 
 // ==end==
 
-function handlePost(jsonData: any, handleSendResult: (data: SendMsgResult) => void) {
+function constructReturnData(status: number, data: any = {}, message: string = "") {
+    return {
+        status: status,
+        retcode: status,
+        data: data,
+        message: message
+    }
+
+}
+
+function handlePost(jsonData: any, handleSendResult: (data: OB11Return<any>) => void) {
     log("API receive post:" + JSON.stringify(jsonData))
     if (!jsonData.params) {
         jsonData.params = JSON.parse(JSON.stringify(jsonData));
@@ -109,7 +126,7 @@ function handlePost(jsonData: any, handleSendResult: (data: SendMsgResult) => vo
             user_display_name: member.cardName || member.nick,
             nickname: member.nick,
             card: member.cardName,
-            role: OnebotGroupMemberRole[member.role],
+            role: OB11Construct.constructGroupMemberRole(member.role),
         }
     } else if (jsonData.action == "get_group_member_list") {
         let group = groups.find(group => group.uid == jsonData.params.group_id)
@@ -121,7 +138,7 @@ function handlePost(jsonData: any, handleSendResult: (data: SendMsgResult) => vo
                     user_display_name: member.cardName || member.nick,
                     nickname: member.nick,
                     card: member.cardName,
-                    role: OnebotGroupMemberRole[member.role],
+                    role: OB11Construct.constructGroupMemberRole(member.role),
                 }
 
             }) || []
@@ -162,11 +179,24 @@ export function startExpress(port: number) {
         }
     });
 
-    function parseToOnebot12(action: PostDataAction) {
-        app.post('/' + action, (req: any, res: any) => {
+    async function registerRouter<PayloadType, ReturnDataType>(action: OB11ApiName, handle: (payload: PayloadType) => Promise<OB11Return<ReturnDataType>>) {
+        async function _handle(res: Response, payload: PayloadType) {
+            res.send(await handle(payload))
+        }
+
+        app.post('/' + action, (req: Request, res: Response) => {
+            _handle(res, req.body).then()
+        });
+        app.get('/' + action, (req: Request, res: Response) => {
+            _handle(res, req.query as any).then()
+        });
+    }
+
+    function parseToOnebot12(action: OB11ApiName) {
+        app.post('/' + action, (req: Request, res: Response) => {
             let jsonData: PostDataSendMsg = req.body;
             jsonData.action = action
-            let resData = handlePost(jsonData, (data: SendMsgResult) => {
+            let resData = handlePost(jsonData, (data: OB11Return<any>) => {
                 res.send(data)
             })
             if (resData) {
@@ -175,29 +205,29 @@ export function startExpress(port: number) {
         });
     }
 
-    const actionList: PostDataAction[] = ["get_login_info", "send_private_msg", "send_group_msg",
+    const actionList: OB11ApiName[] = ["get_login_info", "send_private_msg", "send_group_msg",
         "get_group_list", "get_friend_list", "delete_msg", "get_group_member_list", "get_group_member_info"]
 
     for (const action of actionList) {
-        parseToOnebot12(action as PostDataAction)
+        parseToOnebot12(action as OB11ApiName)
     }
 
-    app.get('/', (req: any, res: any) => {
+    app.get('/', (req: Request, res: Response) => {
         res.send('llonebot已启动');
     })
 
 
     // 处理POST请求的路由
-    app.post('/', (req: any, res: any) => {
+    app.post('/', (req: Request, res: Response) => {
         let jsonData: PostDataSendMsg = req.body;
-        let resData = handlePost(jsonData, (data: SendMsgResult) => {
+        let resData = handlePost(jsonData, (data: OB11Return<any>) => {
             res.send(data)
         })
         if (resData) {
             res.send(resData)
         }
     });
-    app.post('/send_msg', (req: any, res: any) => {
+    app.post('/send_msg', (req: Request, res: Response) => {
         let jsonData: PostDataSendMsg = req.body;
         if (jsonData.message_type == "private") {
             jsonData.action = "send_private_msg"
@@ -210,7 +240,7 @@ export function startExpress(port: number) {
                 jsonData.action = "send_private_msg"
             }
         }
-        let resData = handlePost(jsonData, (data: SendMsgResult) => {
+        let resData = handlePost(jsonData, (data: OB11Return<any>) => {
             res.send(data)
         })
         if (resData) {
@@ -218,7 +248,38 @@ export function startExpress(port: number) {
         }
     })
 
+    registerRouter<{ message_id: string }, OB11Message>("get_msg", async (payload) => {
+        const msg = msgHistory[payload.message_id.toString()]
+        if (msg) {
+            const msgData = await OB11Construct.constructMessage(msg);
+            return constructReturnData(0, msgData)
+        } else {
+            return constructReturnData(1, {}, "消息不存在")
+        }
+    }).then(()=>{
+
+    })
+
     app.listen(port, "0.0.0.0", () => {
         console.log(`llonebot started 0.0.0.0:${port}`);
     });
+}
+
+
+export function postMsg(msg: OB11Message) {
+    for (const host of getConfigUtil().getConfig().hosts) {
+        fetch(host, {
+            method: "POST",
+            headers: {
+                "Content-Type": "application/json",
+                "x-self-id": selfInfo.user_id
+            },
+            body: JSON.stringify(msg)
+        }).then((res: any) => {
+            log(`新消息事件上报成功: ${host} ` + JSON.stringify(msg));
+        }, (err: any) => {
+            log(`新消息事件上报失败: ${host} ` + err + JSON.stringify(msg));
+        });
+    }
+
 }
\ No newline at end of file