feat: 支持回复消息

This commit is contained in:
linyuchen
2023-11-10 21:21:27 +08:00
parent c7e41ad5c4
commit e17882d067
6 changed files with 72 additions and 8 deletions

8
src/global.d.ts vendored
View File

@@ -11,21 +11,21 @@ declare var LLAPI: {
// uid是一串加密的字符串, 收到群消息的时候可以用此函数获取群成员的qq号 // uid是一串加密的字符串, 收到群消息的时候可以用此函数获取群成员的qq号
getUserInfo(uid: string): Promise<User>; getUserInfo(uid: string): Promise<User>;
sendMessage(peer: Peer, message: SendMessage[]): Promise<void>; sendMessage(peer: Peer, message: SendMessage[]): Promise<any>;
recallMessage(peer: Peer, msgIds: string[]): Promise<void>;
getGroupsList(forced: boolean): Promise<Group[]> getGroupsList(forced: boolean): Promise<Group[]>
getFriendsList(forced: boolean): Promise<User[]> getFriendsList(forced: boolean): Promise<User[]>
getGroupMemberList(group_id: string, num: number): Promise<{result: { infos: Map<string, GroupMemberInfo> }}> getGroupMemberList(group_id: string, num: number): Promise<{result: { infos: Map<string, GroupMemberInfo> }}>
getPeer(): Promise<Peer> getPeer(): Promise<Peer>
add_qmenu(func: (qContextMenu: Node)=>void): void add_qmenu(func: (qContextMenu: Node)=>void): void
}; };
declare var llonebot: { declare var llonebot: {
postData: (data: any) => void postData: (data: any) => void
listenSendMessage: (handle: (msg: PostDataSendMsg) => void) => void listenSendMessage: (handle: (msg: PostDataSendMsg) => void) => void
listenRecallMessage: (handle: (msg: {message_id: string}) => void) => void
updateGroups: (groups: Group[]) => void updateGroups: (groups: Group[]) => void
updateFriends: (friends: User[]) => void updateFriends: (friends: User[]) => void
updateGroupMembers: (data: { groupMembers: User[], group_id: string }) => void updateGroupMembers: (data: { groupMembers: User[], group_id: string }) => void

View File

@@ -337,13 +337,14 @@ class Api extends EventEmitter {
}] }]
*/ */
async sendMessage(peer, elements) { async sendMessage(peer, elements) {
ntCall("ns-ntApi", "nodeIKernelMsgService/sendMsg", [ return ntCall("ns-ntApi", "nodeIKernelMsgService/sendMsg", [
{ {
msgId: "0", msgId: "0",
peer: destructor.destructPeer(peer), peer: destructor.destructPeer(peer),
msgElements: await Promise.all( msgElements: await Promise.all(
elements.map(async (element) => { elements.map(async (element) => {
if (element.type == "text") return destructor.destructTextElement(element); if (element.type == "text") return destructor.destructTextElement(element);
else if (element.type == "reply") return destructor.destructReplyElement(element);
else if (element.type == "image") return destructor.destructImageElement(element, await media.prepareImageElement(element.file)); else if (element.type == "image") return destructor.destructImageElement(element, await media.prepareImageElement(element.file));
else if (element.type == "voice") return destructor.destructPttElement(element, await media.prepareVoiceElement(element.file)); else if (element.type == "voice") return destructor.destructPttElement(element, await media.prepareVoiceElement(element.file));
else if (element.type == "face") return destructor.destructFaceElement(element); else if (element.type == "face") return destructor.destructFaceElement(element);
@@ -355,6 +356,15 @@ class Api extends EventEmitter {
null, null,
]); ]);
} }
async recallMessage(peer, msgIds) {
ntCall("ns-ntApi", "nodeIKernelMsgService/recallMsg", [
{
msgIds,
peer: destructor.destructPeer(peer),
},
null,
]);
}
/** /**
* @description 转发消息 * @description 转发消息
* @param {Peer} peer 对方的ID * @param {Peer} peer 对方的ID
@@ -654,6 +664,18 @@ class Destructor {
pttElement pttElement
} }
} }
destructReplyElement(element) {
return {
elementType: 7,
elementId: "",
replyElement: {
replayMsgSeq: element.msgSeq, // raw.msgSeq
replayMsgId: element.msgId, // raw.msgId
senderUin: element.senderUin,
senderUinStr: element.senderUinStr,
}
}
}
destructFaceElement(element) { destructFaceElement(element) {
return { return {

View File

@@ -6,6 +6,7 @@ const express = require("express");
import {Group, PostDataSendMsg, User} from "./types"; import {Group, PostDataSendMsg, User} from "./types";
const CHANNEL_SEND_MSG = "llonebot_sendMsg" const CHANNEL_SEND_MSG = "llonebot_sendMsg"
const CHANNEL_RECALL_MSG = "llonebot_recallMsg"
let groups: Group[] = [] let groups: Group[] = []
let friends: User[] = [] let friends: User[] = []
@@ -114,6 +115,9 @@ function startExpress(event: any) {
} }
}) })
} }
else if (jsonData.action == "delete_msg"){
sendIPCMsg(CHANNEL_RECALL_MSG, jsonData as {message_id: string})
}
return resData return resData
} }
// 处理POST请求的路由 // 处理POST请求的路由
@@ -153,6 +157,12 @@ function startExpress(event: any) {
let resData = handlePost(jsonData) let resData = handlePost(jsonData)
res.send(resData) res.send(resData)
}) })
app.post('/delete_msg', (req: any, res: any) => {
let jsonData: PostDataSendMsg = req.body;
jsonData.action = "delete_msg"
let resData = handlePost(jsonData)
res.send(resData)
})
app.listen(port,"0.0.0.0", () => { app.listen(port,"0.0.0.0", () => {
console.log(`服务器已启动,监听端口 ${port}`); console.log(`服务器已启动,监听端口 ${port}`);
}); });

View File

@@ -10,6 +10,7 @@ const {ipcRenderer} = require('electron');
// 在window对象下导出只读对象 // 在window对象下导出只读对象
contextBridge.exposeInMainWorld("llonebot", { contextBridge.exposeInMainWorld("llonebot", {
postData: (data: any) => { postData: (data: any) => {
ipcRenderer.send("postOnebotData", data); ipcRenderer.send("postOnebotData", data);
}, },
@@ -24,6 +25,11 @@ contextBridge.exposeInMainWorld("llonebot", {
handle(args) handle(args)
}) })
}, },
listenRecallMessage: (handle: (jsonData: {message_id: string}) => void) => {
ipcRenderer.on("llonebot_recallMsg", (event: any, args: {message_id: string}) => {
handle(args)
})
},
startExpress: () => { startExpress: () => {
ipcRenderer.send("startExpress"); ipcRenderer.send("startExpress");
}, },

View File

@@ -10,6 +10,7 @@ const host = "http://localhost:5000"
let self_qq: string = "" let self_qq: string = ""
let groups: Group[] = [] let groups: Group[] = []
let friends: User[] = [] let friends: User[] = []
let msgHistory: MessageElement[] = []
let uid_maps: Record<string, User> = {} // 一串加密的字符串 -> qq号 let uid_maps: Record<string, User> = {} // 一串加密的字符串 -> qq号
async function getUserInfo(uid: string): Promise<User> { async function getUserInfo(uid: string): Promise<User> {
let user = uid_maps[uid] let user = uid_maps[uid]
@@ -97,6 +98,7 @@ async function forwardMessage(message: MessageElement) {
post_type: "message", post_type: "message",
message_type: message.peer.chatType, message_type: message.peer.chatType,
detail_type: message.peer.chatType, detail_type: message.peer.chatType,
message_id: message.raw.msgId,
sub_type: "", sub_type: "",
message: [] message: []
} }
@@ -140,7 +142,7 @@ async function forwardMessage(message: MessageElement) {
} }
onebot_message_data.message.push(message_data) onebot_message_data.message.push(message_data)
} }
msgHistory.push(message)
console.log("发送上传消息给ipc main", onebot_message_data) console.log("发送上传消息给ipc main", onebot_message_data)
window.llonebot.postData(onebot_message_data); window.llonebot.postData(onebot_message_data);
} catch (e) { } catch (e) {
@@ -206,6 +208,12 @@ async function listenSendMessage(postData: PostDataSendMsg) {
else if (message.type == "image" || message.type == "voice"){ else if (message.type == "image" || message.type == "voice"){
message.file = message.data?.file || message.file message.file = message.data?.file || message.file
} }
else if (message.type == "reply"){
let msgId = message.data?.id || message.msgId
let replyMessage = msgHistory.find(msg => msg.raw.msgId == msgId)
message.msgId = msgId
message.msgSeq = replyMessage?.raw.msgSeq || ""
}
} }
console.log("发送消息", postData) console.log("发送消息", postData)
window.LLAPI.sendMessage(peer, postData.params.message).then(res => console.log("消息发送成功:", res), window.LLAPI.sendMessage(peer, postData.params.message).then(res => console.log("消息发送成功:", res),
@@ -214,6 +222,11 @@ async function listenSendMessage(postData: PostDataSendMsg) {
} }
} }
function recallMessage(msgId: string) {
let msg = msgHistory.find(msg => msg.raw.msgId == msgId)
window.LLAPI.recallMessage(msg.peer, [msgId]).then()
}
let chatListEle: HTMLCollectionOf<Element> let chatListEle: HTMLCollectionOf<Element>
function onLoad() { function onLoad() {
@@ -221,6 +234,9 @@ function onLoad() {
window.llonebot.listenSendMessage((postData: PostDataSendMsg) => { window.llonebot.listenSendMessage((postData: PostDataSendMsg) => {
listenSendMessage(postData).then() listenSendMessage(postData).then()
}); });
window.llonebot.listenRecallMessage((arg: { message_id: string }) => {
recallMessage(arg.message_id)
})
async function getGroupsMembers(groupsArg: Group[]) { async function getGroupsMembers(groupsArg: Group[]) {
// 批量获取群成员列表 // 批量获取群成员列表

View File

@@ -2,6 +2,7 @@ export enum AtType {
notAt = 0, notAt = 0,
atUser = 2 atUser = 2
} }
export type GroupMemberInfo = { export type GroupMemberInfo = {
avatarPath: string; avatarPath: string;
cardName: string; cardName: string;
@@ -38,8 +39,9 @@ export type Peer = {
export type MessageElement = { export type MessageElement = {
raw: { raw: {
msgId: string,
msgSeq: string,
elements: { elements: {
replyElement: { replyElement: {
senderUid: string, // 原消息发送者QQ号 senderUid: string, // 原消息发送者QQ号
sourceMsgIsIncPic: boolean; // 原消息是否有图片 sourceMsgIsIncPic: boolean; // 原消息是否有图片
@@ -114,10 +116,18 @@ export type SendMessage = {
data?: { data?: {
qq: string // at的qq号 qq: string // at的qq号
} }
} | {
type: "reply",
msgId: string,
msgSeq: string,
senderUin: string,
data: {
id: string,
}
} }
export type PostDataSendMsg = { export type PostDataSendMsg = {
action: "send_private_msg" | "send_group_msg" | "get_group_list", action: "send_private_msg" | "send_group_msg" | "get_group_list" | "get_friend_list" | "delete_msg",
message_type?: "private" | "group" message_type?: "private" | "group"
params?: { params?: {
user_id: string, user_id: string,