diff --git a/README.md b/README.md
index a560163..0fb8b43 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
## 安装方法
-1.安装[NTQQLiteLoader](https://liteloaderqqnt.github.io/guide/install.html)
+1.安装[LiteLoaderQQNT](https://liteloaderqqnt.github.io/guide/install.html)
2.安装修改后的[LiteLoaderQQNT-Plugin-LLAPI](https://github.com/linyuchen/LiteLoaderQQNT-Plugin-LLAPI/releases),原版的功能有缺陷
@@ -21,13 +21,16 @@
目前只支持http协议POST方法,不支持websocket,事件上报也是http协议
+主要功能:
+- [x] 发送好友消息
+- [x] 发送临时消息
+- [x] 发送群消息
+- [x] 获取好友列表
- [x] 获取群列表
- [x] 获取群成员列表
-- [x] 获取好友列表
-- [x] 发送群消息
-- [x] 发送好友消息
- [x] 撤回消息
- [x] 上报好友消息
+- [x] 上报临时消息
- [x] 上报群消息
消息格式支持:
@@ -58,5 +61,70 @@
*暂时不支持`"message": "hello"`这种message为字符串的形式*
+## 一些坑
+
+
+ 下载了插件但是没有看到在NTQQ中生效
+
+ 检查是否下载的是插件release的版本,如果是源码的话需要自行编译。依然不生效请查阅LiteLoaderQQNT的文档
+
+
+
+
+ 调用接口报404
+
+ 目前没有支持全部的onebot规范接口,请检查是否调用了不支持的接口,并且所有接口都只支持POST方法,调用GET方法会报404
+
+
+
+
+ 发送不了图片和语音
+
+ 检查当前操作用户是否有LiteLoaderQQNT/data/LLOneBot的写入权限,如Windows把QQ上安装到C盘有可能会出现无权限导致发送失败
+
+
+
+
+ 不支持cq码
+
+ cq码已经过时了,没有支持的打算(主要是我不用这玩意儿,加上我懒)
+
+
+
+
+ onebot 12对接不了
+
+ onebot 12只写了部分兼容,没有完整测试,不保证能用,慎用
+
+
+
+
+ QQ多开时事件没有上报
+
+ 小概率事件,有可能是IPC通信串台了(不确定),重启QQ可解决,目前正在想办法修复
+
+
+
+
+ QQ变得很卡
+
+ 这是你的群特别多导致的,因为启动后会批量获取群成员列表,获取完之后就正常了
+
+
+
+
+ 如何查看日志
+
+ LiteLoaderQQNT/data/LLOneBot/*.log
+
+
+
+## TODO
+
+- [x] 接口返回更详细的错误信息,目前消息发不出去也会返回发送成功(这河里吗)
+- [ ] 转发消息记录
+- [ ] 支持websocket,等个有缘人提PR实现
+- [ ] 重构摆脱LLAPI,目前调用LLAPI只能在renderer进程调用,需重构成在main进程调用
+
## onebot11文档
diff --git a/manifest.json b/manifest.json
index 5ff5984..8804270 100644
--- a/manifest.json
+++ b/manifest.json
@@ -4,20 +4,20 @@
"name": "LLOneBot",
"slug": "LLOneBot",
"description": "LiteLoaderQQNT的OneBotApi",
- "version": "2.0.4",
+ "version": "2.1.0",
"thumbnail": "./icon.png",
"authors": [{
"name": "linyuchen",
"link": "https://github.com/linyuchen"
}],
"repository": {
- "repo": "linyuchen/LiteLoaderQQNT-OneBotApi",
- "branch": "main",
- "release": {
- "tag": "latest",
- "name": "LLOneBot.zip"
- }
- },
+ "repo": "linyuchen/LiteLoaderQQNT-OneBotApi",
+ "branch": "main",
+ "release": {
+ "tag": "latest",
+ "name": "LLOneBot.zip"
+ }
+},
"platform": [
"win32",
"linux",
diff --git a/package-lock.json b/package-lock.json
index d3ac971..eee467c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,8 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
- "express": "^4.18.2"
+ "express": "^4.18.2",
+ "uuid": "^9.0.1"
},
"devDependencies": {
"@babel/preset-env": "^7.23.2",
@@ -5213,6 +5214,18 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://mirrors.cloud.tencent.com/npm/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/vary/-/vary-1.1.2.tgz",
diff --git a/package.json b/package.json
index 10924a8..4cdd843 100644
--- a/package.json
+++ b/package.json
@@ -17,16 +17,17 @@
"author": "",
"license": "ISC",
"dependencies": {
- "express": "^4.18.2"
+ "express": "^4.18.2",
+ "uuid": "^9.0.1"
},
"devDependencies": {
- "electron": "^27.0.2",
"@babel/preset-env": "^7.23.2",
"@types/express": "^4.17.20",
"babel-loader": "^9.1.3",
+ "electron": "^27.0.2",
"ts-loader": "^9.5.0",
+ "typescript": "^5.2.2",
"webpack": "^5.89.0",
- "webpack-cli": "^5.1.4",
- "typescript": "^5.2.2"
+ "webpack-cli": "^5.1.4"
}
}
diff --git a/src/common/types.ts b/src/common/types.ts
index baf228b..e9f7627 100644
--- a/src/common/types.ts
+++ b/src/common/types.ts
@@ -3,6 +3,12 @@ export enum AtType {
atUser = 2
}
+export enum ChatType {
+ friend = 1,
+ group = 2,
+ temp = 100
+}
+
export type GroupMemberInfo = {
avatarPath: string;
cardName: string;
@@ -44,7 +50,7 @@ export type Group = {
}
export type Peer = {
- chatType: "private" | "group" | "friend"
+ chatType: ChatType
name: string
uid: string // qq号
}
@@ -52,8 +58,10 @@ export type Peer = {
export type MessageElement = {
raw: {
msgId: string,
+ msgTime: string,
msgSeq: string,
senderUin: string; // 发送者QQ号
+ chatType: ChatType,
elements: {
replyElement: {
senderUid: string, // 原消息发送者QQ号
@@ -139,7 +147,7 @@ export type SendMessage = {
}
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"
+ | "get_friend_list" | "delete_msg" | "get_login_info" | "get_group_member_list" | "get_group_member_info"
export type PostDataSendMsg = {
action: PostDataAction
@@ -152,9 +160,17 @@ export type PostDataSendMsg = {
user_id: string,
group_id: string,
message: SendMessage[];
+ ipc_uuid?: string
}
export type Config = {
port: number,
hosts: string[],
}
+
+export type SendMsgResult = {
+ status: number,
+ retcode: number,
+ data: any,
+ message: string,
+}
\ No newline at end of file
diff --git a/src/global.d.ts b/src/global.d.ts
index 2ecbbb1..6f38bfb 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -6,7 +6,7 @@ import {
Peer,
PostDataSendMsg,
SelfInfo,
- SendMessage,
+ SendMessage, SendMsgResult,
User
} from "./common/types";
@@ -43,9 +43,10 @@ declare var llonebot: {
setConfig(config: Config):void;
getConfig():Promise;
setSelfInfo(selfInfo: SelfInfo):void;
- downloadFile(arg: {uri: string, localFilePath: string}):Promise;
+ downloadFile(arg: {uri: string, fileName: string}):Promise<{errMsg: string, path: string}>;
deleteFile(path: string[]):Promise;
getRunningStatus(): Promise;
+ sendSendMsgResult(sessionId: string, msgResult: SendMsgResult): void;
};
declare global {
diff --git a/src/main/HttpServer.ts b/src/main/HttpServer.ts
index 92ee868..4a891f7 100644
--- a/src/main/HttpServer.ts
+++ b/src/main/HttpServer.ts
@@ -2,7 +2,7 @@ import {log} from "./utils";
const express = require("express");
import {sendIPCRecallQQMsg, sendIPCSendQQMsg} from "./IPCSend";
-import {OnebotGroupMemberRole, PostDataAction, PostDataSendMsg, SendMessage} from "../common/types";
+import {OnebotGroupMemberRole, PostDataAction, PostDataSendMsg, SendMessage, SendMsgResult} from "../common/types";
import {friends, groups, selfInfo} from "./data";
// @SiberianHusky 2021-08-15
@@ -18,7 +18,7 @@ function checkSendMessage(sendMsgList: SendMessage[]) {
let data = msg["data"];
if (type === "text" && !data["text"]) {
return 400;
- } else if (["image", "voice"].includes(type)) {
+ } else if (["image", "voice", "record"].includes(type)) {
if (!data["file"]) {
return 400;
}
@@ -45,7 +45,7 @@ function checkSendMessage(sendMsgList: SendMessage[]) {
}
// ==end==
-function handlePost(jsonData: any) {
+function handlePost(jsonData: any, handleSendResult: (data: SendMsgResult)=>void) {
log("API receive post:" + JSON.stringify(jsonData))
if (!jsonData.params) {
jsonData.params = JSON.parse(JSON.stringify(jsonData));
@@ -57,6 +57,7 @@ function handlePost(jsonData: any) {
data: {},
message: ''
}
+
if (jsonData.action == "get_login_info") {
resData["data"] = selfInfo
} else if (jsonData.action == "send_private_msg" || jsonData.action == "send_group_msg") {
@@ -70,7 +71,8 @@ function handlePost(jsonData: any) {
if (resData.status == 200) {
resData.message = "发送成功";
resData.data = jsonData.message;
- sendIPCSendQQMsg(jsonData);
+ sendIPCSendQQMsg(jsonData, handleSendResult);
+ return;
} else {
resData.message = "发送失败, 请检查消息格式";
resData.data = jsonData.message;
@@ -151,8 +153,10 @@ export function startExpress(port: number) {
app.post('/' + action, (req: any, res: any) => {
let jsonData: PostDataSendMsg = req.body;
jsonData.action = action
- let resData = handlePost(jsonData)
- res.send(resData)
+ let resData = handlePost(jsonData, (data:SendMsgResult)=>{res.send(data)})
+ if (resData){
+ res.send(resData)
+ }
});
}
@@ -171,8 +175,10 @@ export function startExpress(port: number) {
// 处理POST请求的路由
app.post('/', (req: any, res: any) => {
let jsonData: PostDataSendMsg = req.body;
- let resData = handlePost(jsonData)
- res.send(resData)
+ let resData = handlePost(jsonData, (data:SendMsgResult)=>{res.send(data)})
+ if (resData){
+ res.send(resData)
+ }
});
app.post('/send_msg', (req: any, res: any) => {
let jsonData: PostDataSendMsg = req.body;
@@ -187,11 +193,13 @@ export function startExpress(port: number) {
jsonData.action = "send_private_msg"
}
}
- let resData = handlePost(jsonData)
- res.send(resData)
+ let resData = handlePost(jsonData, (data:SendMsgResult)=>{res.send(data)})
+ if (resData){
+ res.send(resData)
+ }
})
app.listen(port, "0.0.0.0", () => {
- console.log(`服务器已启动,监听端口 ${port}`);
+ console.log(`llonebot started 0.0.0.0:${port}`);
});
}
\ No newline at end of file
diff --git a/src/main/IPCSend.ts b/src/main/IPCSend.ts
index c69b45c..8d7d3bf 100644
--- a/src/main/IPCSend.ts
+++ b/src/main/IPCSend.ts
@@ -1,6 +1,8 @@
import {ipcMain, webContents} from 'electron';
-import {PostDataSendMsg} from "../common/types";
+import {PostDataSendMsg, SendMsgResult} from "../common/types";
import {CHANNEL_RECALL_MSG, CHANNEL_SEND_MSG} from "../common/IPCChannel";
+import {v4 as uuid4} from "uuid";
+import {log} from "./utils";
function sendIPCMsg(channel: string, data: any) {
let contents = webContents.getAllWebContents();
@@ -14,7 +16,17 @@ function sendIPCMsg(channel: string, data: any) {
}
-export function sendIPCSendQQMsg(postData: PostDataSendMsg) {
+export function sendIPCSendQQMsg(postData: PostDataSendMsg, handleSendResult: (data: SendMsgResult) => void) {
+ const onceSessionId = "llonebot_send_msg_" + uuid4();
+ postData.ipc_uuid = onceSessionId;
+ ipcMain.once(onceSessionId, (event: any, sendResult: SendMsgResult) => {
+ // log("llonebot send msg ipcMain.once:" + JSON.stringify(sendResult));
+ try {
+ handleSendResult(sendResult)
+ } catch (e) {
+ log("llonebot send msg ipcMain.once error:" + JSON.stringify(e))
+ }
+ })
sendIPCMsg(CHANNEL_SEND_MSG, postData);
}
diff --git a/src/main/main.ts b/src/main/main.ts
index c6653ff..de0a992 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -17,7 +17,7 @@ import {
} from "../common/IPCChannel";
import {ConfigUtil} from "./config";
import {startExpress} from "./HttpServer";
-import {isGIF, log} from "./utils";
+import {CONFIG_DIR, isGIF, log} from "./utils";
import {friends, groups, selfInfo} from "./data";
import {} from "../global";
@@ -29,40 +29,78 @@ let running = false;
// 加载插件时触发
function onLoad() {
log("main onLoaded");
+
// const config_dir = browserWindow.LiteLoader.plugins["LLOneBot"].path.data;
- const config_dir = global.LiteLoader.plugins["LLOneBot"].path.data;
function getConfigUtil() {
- const configFilePath = path.join(config_dir, `config_${selfInfo.user_id}.json`)
+ const configFilePath = path.join(CONFIG_DIR, `config_${selfInfo.user_id}.json`)
return new ConfigUtil(configFilePath)
}
- if (!fs.existsSync(config_dir)) {
- fs.mkdirSync(config_dir, {recursive: true});
+ if (!fs.existsSync(CONFIG_DIR)) {
+ fs.mkdirSync(CONFIG_DIR, {recursive: true});
}
ipcMain.handle(CHANNEL_GET_CONFIG, (event: any, arg: any) => {
return getConfigUtil().getConfig()
})
- ipcMain.handle(CHANNEL_DOWNLOAD_FILE, async (event: any, arg: {uri: string, localFilePath: string}) => {
+ ipcMain.handle(CHANNEL_DOWNLOAD_FILE, async (event: any, arg: { uri: string, fileName: string }): Promise<{
+ success: boolean,
+ errMsg: string,
+ path: string
+ }> => {
+ let filePath = path.join(CONFIG_DIR, arg.fileName)
let url = new URL(arg.uri);
- if (url.protocol == "base64:"){
+ if (url.protocol == "base64:") {
// base64转成文件
let base64Data = arg.uri.split("base64://")[1]
- const buffer = Buffer.from(base64Data, 'base64');
+ try {
+ const buffer = Buffer.from(base64Data, 'base64');
- fs.writeFileSync(arg.localFilePath, buffer);
- }
- else if (url.protocol == "http:" || url.protocol == "https:") {
+ fs.writeFileSync(filePath, buffer);
+ } catch (e: any) {
+ return {
+ success: false,
+ errMsg: `base64文件下载失败,` + e.toString(),
+ path: ""
+ }
+ }
+ } else if (url.protocol == "http:" || url.protocol == "https:") {
// 下载文件
let res = await fetch(url)
+ if (!res.ok) {
+ return {
+ success: false,
+ errMsg: `${url}下载失败,` + res.statusText,
+ path: ""
+ }
+ }
let blob = await res.blob();
let buffer = await blob.arrayBuffer();
- fs.writeFileSync(arg.localFilePath, Buffer.from(buffer));
+ try {
+ fs.writeFileSync(filePath, Buffer.from(buffer));
+ } catch (e: any) {
+ return {
+ success: false,
+ errMsg: `${url}下载失败,` + e.toString(),
+ path: ""
+ }
+ }
}
- if (isGIF(arg.localFilePath)) {
- fs.renameSync(arg.localFilePath, arg.localFilePath + ".gif");
- arg.localFilePath += ".gif";
+ else{
+ return {
+ success: false,
+ errMsg: `不支持的file协议,` + url.protocol,
+ path: ""
+ }
}
- return arg.localFilePath;
+ if (isGIF(filePath)) {
+ fs.renameSync(filePath, filePath + ".gif");
+ filePath += ".gif";
+ }
+ return {
+ success: true,
+ errMsg: "",
+ path: filePath
+ };
})
ipcMain.on(CHANNEL_SET_CONFIG, (event: any, arg: Config) => {
getConfigUtil().setConfig(arg)
@@ -103,7 +141,7 @@ function onLoad() {
})
ipcMain.on(CHANNEL_POST_ONEBOT_DATA, (event: any, arg: any) => {
- for(const host of getConfigUtil().getConfig().hosts) {
+ for (const host of getConfigUtil().getConfig().hosts) {
try {
fetch(host, {
method: "POST",
@@ -113,9 +151,9 @@ function onLoad() {
},
body: JSON.stringify(arg)
}).then((res: any) => {
- log("新消息事件上传");
+ log(`新消息事件上传成功: ${host} ` + JSON.stringify(arg));
}, (err: any) => {
- log("新消息事件上传失败:" + err + JSON.stringify(arg));
+ log(`新消息事件上传失败: ${host} ` + err + JSON.stringify(arg));
});
} catch (e: any) {
log(e.toString())
diff --git a/src/main/utils.ts b/src/main/utils.ts
index 727e07f..2a52378 100644
--- a/src/main/utils.ts
+++ b/src/main/utils.ts
@@ -1,8 +1,19 @@
+import * as path from "path";
+import {json} from "express";
+import {selfInfo} from "./data";
+
const fs = require('fs');
+export const CONFIG_DIR = global.LiteLoader.plugins["LLOneBot"].path.data;
export function log(msg: any) {
let currentDateTime = new Date().toLocaleString();
- fs.appendFile("./llonebot.log", currentDateTime + ":" + msg + "\n", (err: any) => {
+ const date = new Date();
+ const year = date.getFullYear();
+ const month = date.getMonth() + 1;
+ 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) => {
})
}
@@ -13,4 +24,5 @@ export function isGIF(path: string) {
fs.readSync(fd, buffer, 0, 4, 0);
fs.closeSync(fd);
return buffer.toString() === 'GIF8'
-}
\ No newline at end of file
+}
+
diff --git a/src/preload.ts b/src/preload.ts
index f19256a..8252323 100644
--- a/src/preload.ts
+++ b/src/preload.ts
@@ -1,6 +1,6 @@
// Electron 主进程 与 渲染进程 交互的桥梁
-import {Config, Group, PostDataSendMsg, SelfInfo, User} from "./common/types";
+import {Config, Group, PostDataSendMsg, SelfInfo, SendMsgResult, User} from "./common/types";
import {
CHANNEL_DOWNLOAD_FILE,
CHANNEL_GET_CONFIG,
@@ -33,6 +33,9 @@ contextBridge.exposeInMainWorld("llonebot", {
updateFriends: (friends: User[]) => {
ipcRenderer.send(CHANNEL_UPDATE_FRIENDS, friends);
},
+ sendSendMsgResult: (sessionId: string, msgResult: SendMsgResult)=>{
+ ipcRenderer.send(sessionId, msgResult);
+ },
listenSendMessage: (handle: (jsonData: PostDataSendMsg) => void) => {
ipcRenderer.send(CHANNEL_LOG, "发送消息API已注册");
ipcRenderer.on(CHANNEL_SEND_MSG, (event: any, args: PostDataSendMsg) => {
diff --git a/src/renderer.ts b/src/renderer.ts
index 545d670..ec16bcc 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -2,15 +2,31 @@
// import express from "express";
// const { ipcRenderer } = require('electron');
-import {AtType, Group, MessageElement, OnebotGroupMemberRole, Peer, PostDataSendMsg, User} from "./common/types";
-import * as stream from "stream";
-import {raw} from "express";
+import {
+ AtType,
+ ChatType,
+ Group,
+ MessageElement,
+ OnebotGroupMemberRole,
+ Peer,
+ PostDataSendMsg, SendMsgResult,
+ User
+} from "./common/types";
let self_qq: string = ""
let groups: Group[] = []
let friends: User[] = []
let msgHistory: MessageElement[] = []
let uid_maps: Record = {} // 一串加密的字符串 -> qq号
+
+function getStrangerByUin(uin: string) {
+ for (const key in uid_maps) {
+ if (uid_maps[key].uin === uin) {
+ return uid_maps[key];
+ }
+ }
+}
+
async function getUserInfo(uid: string): Promise {
let user = uid_maps[uid]
if (!user) {
@@ -80,6 +96,11 @@ async function getGroupMembers(group_qq: string, forced: boolean = false) {
if (!group!.members!.find(m => m.uid == member.uid)) {
group!.members!.push(member)
}
+ uid_maps[member.uid] = {
+ uin: member.uin,
+ uid: member.uid,
+ nickName: member.nick
+ };
}
window.llonebot.updateGroups(groups)
console.log(`更新群${group.name}成员列表`, group)
@@ -100,9 +121,8 @@ async function getGroupMember(group_qq: string, member_uid: string) {
}
}
-
async function handleNewMessage(messages: MessageElement[]) {
- // console.log("llonebot 收到消息:", messages);
+ console.log("llonebot 收到消息:", messages);
for (let message of messages) {
let onebot_message_data: any = {
self: {
@@ -110,7 +130,7 @@ async function handleNewMessage(messages: MessageElement[]) {
user_id: self_qq
},
self_id: self_qq,
- time: 0,
+ time: parseInt(message.raw.msgTime || "0"),
type: "message",
post_type: "message",
message_type: message.peer.chatType,
@@ -121,9 +141,10 @@ async function handleNewMessage(messages: MessageElement[]) {
raw_message: "",
font: 14
}
- if (message.peer.chatType == "group") {
+ if (message.raw.chatType == ChatType.group) {
let group_id = message.peer.uid
let group = (await getGroup(group_id))!
+ 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
@@ -134,13 +155,25 @@ async function handleNewMessage(messages: MessageElement[]) {
role: OnebotGroupMemberRole[groupMember!.role]
}
// console.log("收到群消息", onebot_message_data)
- } else if (message.peer.chatType == "private" || message.peer.chatType == "friend") {
+ } else if (message.raw.chatType == ChatType.friend) {
onebot_message_data["user_id"] = message.raw.senderUin;
- let friend = await getFriend(message.raw.senderUin)
+ onebot_message_data.message_type = 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.message_type = "friend"
+ 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 = {
@@ -168,6 +201,7 @@ async function handleNewMessage(messages: MessageElement[]) {
if (!element.picElement.sourcePath.startsWith("/")) {
startS += "/"
}
+ // todo: 转成base64
message_data["data"]["file"] = startS + element.picElement.sourcePath
} else if (element.replyElement) {
message_data["type"] = "reply"
@@ -175,6 +209,9 @@ async function handleNewMessage(messages: MessageElement[]) {
}
onebot_message_data.message.push(message_data)
}
+ if (msgHistory.length > 10000) {
+ msgHistory.splice(0, 100)
+ }
msgHistory.push(message)
console.log("发送上传消息给ipc main", onebot_message_data)
window.llonebot.postData(onebot_message_data);
@@ -183,6 +220,12 @@ async function handleNewMessage(messages: MessageElement[]) {
async function listenSendMessage(postData: PostDataSendMsg) {
console.log("收到发送消息请求", postData);
+ let sendMsgResult: SendMsgResult = {
+ retcode: 0,
+ status: 0,
+ data: {},
+ message: "发送成功"
+ }
if (postData.action == "send_private_msg" || postData.action == "send_group_msg") {
let peer: Peer | null = null;
if (!postData.params) {
@@ -195,22 +238,41 @@ async function listenSendMessage(postData: PostDataSendMsg) {
if (postData.action == "send_private_msg") {
let friend = await getFriend(postData.params.user_id)
if (friend) {
+ console.log("好友消息", postData)
peer = {
- chatType: "private",
+ chatType: ChatType.friend,
name: friend.nickName,
uid: friend.uid
}
+ } else {
+ // 临时消息
+ console.log("发送临时消息", postData)
+ let receiver = getStrangerByUin(postData.params.user_id);
+ if (receiver) {
+ peer = {
+ chatType: ChatType.temp,
+ name: receiver.nickName,
+ uid: receiver.uid
+ }
+ } else {
+ sendMsgResult.status = -1;
+ sendMsgResult.retcode = -1;
+ sendMsgResult.message = `发送失败,未找到对象${postData.params.user_id},检查他是否为好友或是群友`;
+ }
}
} else if (postData.action == "send_group_msg") {
let group = await getGroup(postData.params.group_id)
if (group) {
peer = {
- chatType: "group",
+ chatType: ChatType.group,
name: group.name,
uid: group.uid
}
} else {
+ sendMsgResult.status = -1;
+ sendMsgResult.retcode = -1;
+ sendMsgResult.message = `发送失败,未找到群${postData.params.group_id}`;
console.log("未找到群, 发送群消息失败", postData)
}
}
@@ -245,10 +307,23 @@ async function listenSendMessage(postData: PostDataSendMsg) {
if (uri.protocol == "file:") {
localFilePath = url.split("file://")[1]
} else {
- localFilePath = await window.llonebot.downloadFile({uri: url, localFilePath: localFilePath})
- sendFiles.push(localFilePath);
+ const {errMsg, path} = await window.llonebot.downloadFile({
+ uri: url,
+ fileName: `${Date.now()}${ext}`
+ })
+ console.log("下载文件结果", errMsg, path)
+ if (errMsg) {
+ console.log("下载文件失败", errMsg);
+ sendMsgResult.status = -1;
+ sendMsgResult.retcode = -1;
+ sendMsgResult.message = `发送失败,下载文件失败,${errMsg}`;
+ break;
+ } else {
+ localFilePath = path;
+ }
}
message.file = localFilePath
+ sendFiles.push(localFilePath);
} else if (message.type == "reply") {
let msgId = message.data?.id || message.msgId
let replyMessage = msgHistory.find(msg => msg.raw.msgId == msgId)
@@ -257,13 +332,27 @@ async function listenSendMessage(postData: PostDataSendMsg) {
}
}
console.log("发送消息", postData)
+ if (sendMsgResult.status !== 0) {
+ window.llonebot.sendSendMsgResult(postData.ipc_uuid, sendMsgResult)
+ return;
+ }
window.LLAPI.sendMessage(peer, postData.params.message).then(res => {
console.log("消息发送成功:", peer, postData.params.message)
if (sendFiles.length) {
window.llonebot.deleteFile(sendFiles);
}
+ window.llonebot.sendSendMsgResult(postData.ipc_uuid, sendMsgResult)
},
- err => console.log("消息发送失败", postData, err))
+ err => {
+ sendMsgResult.status = -1;
+ sendMsgResult.retcode = -1;
+ sendMsgResult.message = `发送失败,${err}`;
+ window.llonebot.sendSendMsgResult(postData.ipc_uuid, sendMsgResult)
+ console.log("消息发送失败", postData, err)
+ })
+ } else {
+ console.log(sendMsgResult, postData);
+ window.llonebot.sendSendMsgResult(postData.ipc_uuid, sendMsgResult)
}
}
}
@@ -308,7 +397,7 @@ function onNewMessages(messages: MessageElement[]) {
// console.log("chatListEle", chatListEle)
}
-async function initAccountInfo(){
+async function initAccountInfo() {
let accountInfo = await window.LLAPI.getAccountInfo();
window.llonebot.log("getAccountInfo " + JSON.stringify(accountInfo));
if (!accountInfo.uid) {
@@ -325,21 +414,23 @@ async function initAccountInfo(){
function onLoad() {
window.llonebot.log("llonebot render onLoad");
- window.llonebot.getRunningStatus().then(running=>{
+ window.llonebot.getRunningStatus().then(running => {
if (running) {
return;
}
initAccountInfo().then(
- (initSuccess)=>{
+ (initSuccess) => {
if (!initSuccess) {
return;
}
if (friends.length == 0) {
- getFriends().then(()=>{});
+ getFriends().then(() => {
+ });
}
if (groups.length == 0) {
- getGroups().then(()=>{
- getGroupsMembers(groups).then(()=>{});
+ getGroups().then(() => {
+ getGroupsMembers(groups).then(() => {
+ });
});
}
window.LLAPI.on("new-messages", onNewMessages);
@@ -354,93 +445,93 @@ function onLoad() {
recallMessage(arg.message_id)
})
window.llonebot.log("llonebot loaded");
- // window.LLAPI.add_qmenu((qContextMenu: Node) => {
- // let btn = document.createElement("a")
- // btn.className = "q-context-menu-item q-context-menu-item--normal vue-component"
- // btn.setAttribute("aria-disabled", "false")
- // btn.setAttribute("role", "menuitem")
- // btn.setAttribute("tabindex", "-1")
- // btn.onclick = () => {
- // // window.LLAPI.getPeer().then(peer => {
- // // // console.log("current peer", peer)
- // // if (peer && peer.chatType == "group") {
- // // getGroupMembers(peer.uid, true).then(()=> {
- // // console.log("获取群成员列表成功", groups);
- // // alert("获取群成员列表成功")
- // // })
- // // }
- // // })
- // async function func() {
- // for (const group of groups) {
- // await getGroupMembers(group.uid, true)
- // }
- // }
- //
- // func().then(() => {
- // console.log("获取群成员列表结果", groups);
- // // 找到members数量为空的群
- // groups.map(group => {
- // if (group.members.length == 0) {
- // console.log(`${group.name}群成员为空`)
- // }
- // })
- // window.llonebot.updateGroups(groups)
- // })
- // }
- // btn.innerText = "获取群成员列表"
- // console.log(qContextMenu)
- // // qContextMenu.appendChild(btn)
- // })
- //
- // window.LLAPI.on("context-msg-menu", (event, target, msgIds) => {
- // console.log("msg menu", event, target, msgIds);
- // })
- //
- // // console.log("getAccountInfo", LLAPI.getAccountInfo());
- // function getChatListEle() {
- // chatListEle = document.getElementsByClassName("viewport-list__inner")
- // console.log("chatListEle", chatListEle)
- // if (chatListEle.length == 0) {
- // setTimeout(getChatListEle, 500)
- // } else {
- // try {
- // // 选择要观察的目标节点
- // const targetNode = chatListEle[0];
- //
- // // 创建一个观察器实例并传入回调函数
- // const observer = new MutationObserver(function (mutations) {
- // mutations.forEach(function (mutation) {
- // // console.log("chat list changed", mutation.type); // 输出 mutation 的类型
- // // 获得当前聊天窗口
- // window.LLAPI.getPeer().then(peer => {
- // // console.log("current peer", peer)
- // if (peer && peer.chatType == "group") {
- // getGroupMembers(peer.uid, false).then()
- // }
- // })
- // });
- // });
- //
- // // 配置观察选项
- // const config = {attributes: true, childList: true, subtree: true};
- //
- // // 传入目标节点和观察选项
- // observer.observe(targetNode, config);
- //
- // } catch (e) {
- // window.llonebot.log(e)
- // }
- // }
- // }
- //
- // // getChatListEle();
+ // window.LLAPI.add_qmenu((qContextMenu: Node) => {
+ // let btn = document.createElement("a")
+ // btn.className = "q-context-menu-item q-context-menu-item--normal vue-component"
+ // btn.setAttribute("aria-disabled", "false")
+ // btn.setAttribute("role", "menuitem")
+ // btn.setAttribute("tabindex", "-1")
+ // btn.onclick = () => {
+ // // window.LLAPI.getPeer().then(peer => {
+ // // // console.log("current peer", peer)
+ // // if (peer && peer.chatType == "group") {
+ // // getGroupMembers(peer.uid, true).then(()=> {
+ // // console.log("获取群成员列表成功", groups);
+ // // alert("获取群成员列表成功")
+ // // })
+ // // }
+ // // })
+ // async function func() {
+ // for (const group of groups) {
+ // await getGroupMembers(group.uid, true)
+ // }
+ // }
+ //
+ // func().then(() => {
+ // console.log("获取群成员列表结果", groups);
+ // // 找到members数量为空的群
+ // groups.map(group => {
+ // if (group.members.length == 0) {
+ // console.log(`${group.name}群成员为空`)
+ // }
+ // })
+ // window.llonebot.updateGroups(groups)
+ // })
+ // }
+ // btn.innerText = "获取群成员列表"
+ // console.log(qContextMenu)
+ // // qContextMenu.appendChild(btn)
+ // })
+ //
+ // window.LLAPI.on("context-msg-menu", (event, target, msgIds) => {
+ // console.log("msg menu", event, target, msgIds);
+ // })
+ //
+ // // console.log("getAccountInfo", LLAPI.getAccountInfo());
+ // function getChatListEle() {
+ // chatListEle = document.getElementsByClassName("viewport-list__inner")
+ // console.log("chatListEle", chatListEle)
+ // if (chatListEle.length == 0) {
+ // setTimeout(getChatListEle, 500)
+ // } else {
+ // try {
+ // // 选择要观察的目标节点
+ // const targetNode = chatListEle[0];
+ //
+ // // 创建一个观察器实例并传入回调函数
+ // const observer = new MutationObserver(function (mutations) {
+ // mutations.forEach(function (mutation) {
+ // // console.log("chat list changed", mutation.type); // 输出 mutation 的类型
+ // // 获得当前聊天窗口
+ // window.LLAPI.getPeer().then(peer => {
+ // // console.log("current peer", peer)
+ // if (peer && peer.chatType == "group") {
+ // getGroupMembers(peer.uid, false).then()
+ // }
+ // })
+ // });
+ // });
+ //
+ // // 配置观察选项
+ // const config = {attributes: true, childList: true, subtree: true};
+ //
+ // // 传入目标节点和观察选项
+ // observer.observe(targetNode, config);
+ //
+ // } catch (e) {
+ // window.llonebot.log(e)
+ // }
+ // }
+ // }
+ //
+ // // getChatListEle();
}
);
});
}
// 打开设置界面时触发
-async function onSettingWindowCreated (view: Element) {
+async function onSettingWindowCreated(view: Element) {
window.llonebot.log("setting window created");
const {port, hosts} = await window.llonebot.getConfig()