/// // 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"; let self_qq: string = "" let groups: Group[] = [] let friends: User[] = [] let msgHistory: MessageElement[] = [] let uid_maps: Record = {} // 一串加密的字符串 -> qq号 async function getUserInfo(uid: string): Promise { let user = uid_maps[uid] if (!user) { // 从服务器获取用户信息 user = await window.LLAPI.getUserInfo(uid) uid_maps[uid] = user } return user } async function getFriends() { let _friends = await window.LLAPI.getFriendsList(false) for (let friend of _friends) { let existFriend = friends.find(f => f.uin == friend.uin) if (!existFriend) { friends.push(friend) } } window.llonebot.updateFriends(friends) return friends } async function getFriend(qq: string) { let friend = friends.find(friend => friend.uin == qq) if (!friend) { await getFriends(); friend = friends.find(friend => friend.uin == qq); } return friend; } async function getGroup(qq: string) { let group = groups.find(group => group.uid == qq) if (!group) { await getGroups(); group = groups.find(group => group.uid == qq) } return group } async function getGroups() { let __groups = await window.LLAPI.getGroupsList(false) for (let group of __groups) { group.members = []; let existGroup = groups.find(g => g.uid == group.uid) if (!existGroup) { // console.log("更新群列表", groups) groups.push(group) } } window.llonebot.updateGroups(groups) return groups } async function getGroupMembers(group_qq: string, forced: boolean = false) { let group = await getGroup(group_qq) if (!group?.members || group!.members!.length == 0 || forced) { let res = (await window.LLAPI.getGroupMemberList(group_qq, 5000)) // console.log(`更新群${group}成员列表 await`, _res) // window.LLAPI.getGroupMemberList(group_qq + "_groupMemberList_MainWindow", 5000).then(res =>{ let members = res.result.infos.values(); console.log("getGroupMemberList api response:", res) if (members && forced) { group.members = [] } for (const member of members) { if (!group!.members!.find(m => m.uid == member.uid)) { group!.members!.push(member) } } window.llonebot.updateGroups(groups) console.log(`更新群${group.name}成员列表`, group) // }) } return group?.members } async function getGroupMember(group_qq: string, member_uid: string) { let members = await getGroupMembers(group_qq) if (members) { let member = members.find(member => member.uid == member_uid) if (!member) { members = await getGroupMembers(group_qq, true) member = members?.find(member => member.uid == member_uid) } return member } } async function handleNewMessage(messages: MessageElement[]) { for (let message of messages) { let onebot_message_data: any = { self: { platform: "qq", user_id: self_qq }, self_id: self_qq, time: 0, type: "message", post_type: "message", message_type: message.peer.chatType, detail_type: message.peer.chatType, message_id: message.raw.msgId, sub_type: "", message: [] } if (message.peer.chatType == "group") { let group_id = message.peer.uid let group = (await getGroup(group_id))! 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.peer.chatType == "private") { onebot_message_data["user_id"] = message.peer.uid let friend = await getFriend(message.sender.uid) onebot_message_data.sender = { user_id: friend!.uin, nickname: friend!.nickName } } for (let element of message.raw.elements) { let message_data: any = { data: {}, type: "unknown" } if (element.textElement?.atType == AtType.atUser) { message_data["type"] = "at" if (element.textElement.atUid != "0") { message_data["data"]["mention"] = element.textElement.atUid } 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 let startS = "file://" if (!element.picElement.sourcePath.startsWith("/")) { startS += "/" } message_data["data"]["file"] = startS + 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 } onebot_message_data.message.push(message_data) } msgHistory.push(message) console.log("发送上传消息给ipc main", onebot_message_data) window.llonebot.postData(onebot_message_data); } } async function listenSendMessage(postData: PostDataSendMsg) { if (postData.action == "send_private_msg" || postData.action == "send_group_msg") { let peer: Peer | null = null; if (!postData.params) { postData.params = { message: postData.message, user_id: postData.user_id, group_id: postData.group_id } } if (postData.action == "send_private_msg") { let friend = await getFriend(postData.params.user_id) if (friend) { peer = { chatType: "private", name: friend.nickName, uid: friend.uid } } } else if (postData.action == "send_group_msg") { let group = await getGroup(postData.params.group_id) if (group) { peer = { chatType: "group", name: group.name, uid: group.uid } } else { console.log("未找到群, 发送群消息失败", postData) } } if (peer) { let sendFiles: string[] = []; for (let message of postData.params.message) { if (message.type == "at") { // @ts-ignore message.type = "text" message.atType = AtType.atUser let atUid = message.data?.qq || message.atUid let group = await getGroup(postData.params.group_id) let atMember = group.members.find(member => member.uin == atUid) message.atNtUid = atMember.uid message.atUid = atUid message.content = `@${atMember.cardName || atMember.nick}` } else if (message.type == "text") { message.content = message.data?.text || message.content } else if (message.type == "image" || message.type == "voice" || message.type == "record") { // todo: 收到的应该是uri格式的,需要转成本地的, uri格式有三种,http, file, base64 let url = message.data?.file || message.file let uri = new URL(url); let ext: string; if (message.type == "image") { ext = ".png" } if (message.type == "voice" || message.type == "record") { message.type = "voice" ext = ".amr" } let localFilePath = `${Date.now()}${ext}` if (uri.protocol == "file:") { localFilePath = url.split("file://")[1] } else { localFilePath = await window.llonebot.downloadFile({uri: url, localFilePath: localFilePath}) } 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) message.msgId = msgId message.msgSeq = replyMessage?.raw.msgSeq || "" } } console.log("发送消息", postData) window.LLAPI.sendMessage(peer, postData.params.message).then(res => { console.log("消息发送成功:", peer, postData.params.message) if (sendFiles.length) { window.llonebot.deleteFile(sendFiles); } }, err => console.log("消息发送失败", postData, err)) } } } function recallMessage(msgId: string) { let msg = msgHistory.find(msg => msg.raw.msgId == msgId) window.LLAPI.recallMessage(msg.peer, [msgId]).then() } let chatListEle: HTMLCollectionOf async function getGroupsMembers(groupsArg: Group[]) { // 批量获取群成员列表 let failedGroups: Group[] = [] for (const group of groupsArg) { let handledGroup = await getGroupMembers(group.uid, true) if (handledGroup.length == 0) { failedGroups.push(group) } } if (failedGroups.length > 0) { console.log("获取群成员列表失败,重试", failedGroups.map(group => group.name)) setTimeout(() => { getGroupsMembers(failedGroups).then() }, 1000) } else { window.llonebot.log("全部群成员获取完毕") } } 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(); window.llonebot.log("getAccountInfo " + JSON.stringify(accountInfo)); if (!accountInfo.uid) { return; } let selfInfo = await window.LLAPI.getUserInfo(accountInfo.uid); window.llonebot.setSelfInfo({ user_id: accountInfo.uin, nickname: selfInfo.nickName }); window.llonebot.log("selfInfo " + JSON.stringify(selfInfo)); } function onLoad() { window.llonebot.log("llonebot render onLoad"); window.llonebot.getRunningStatus().then(running=>{ if (running) { return; } initAccountInfo().then( ()=>{ if (friends.length == 0) { getFriends().then(()=>{}); } if (groups.length == 0) { getGroups().then(()=>{ getGroupsMembers(groups).then(()=>{}); }); } window.LLAPI.on("new-messages", onNewMessages); window.LLAPI.on("new-send-messages", onNewMessages); window.llonebot.log("llonebot render start"); window.llonebot.startExpress(); window.llonebot.listenSendMessage((postData: PostDataSendMsg) => { listenSendMessage(postData).then().catch(err => console.log("listenSendMessage err", err)) }) window.llonebot.listenRecallMessage((arg: { message_id: string }) => { 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(); } ); }); } // 打开设置界面时触发 async function onSettingWindowCreated (view: any) { window.llonebot.log("setting window created"); const {port, hosts} = await window.llonebot.getConfig() function creatHostEleStr(host: string) { let eleStr = `

事件上报地址(http)

` return eleStr } let hostsEleStr = "" for (const host of hosts) { hostsEleStr += creatHostEleStr(host); } const html = `

监听端口

${hostsEleStr}
` const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); function addHostEle(initValue: string = "") { let addressDoc = parser.parseFromString(creatHostEleStr(initValue), "text/html"); let addressEle = addressDoc.querySelector("div") let hostItemsEle = document.getElementById("hostItems"); hostItemsEle.appendChild(addressEle); } doc.getElementById("addHost").addEventListener("click", () => addHostEle()) doc.getElementById("save")?.addEventListener("click", () => { const portEle: HTMLInputElement = document.getElementById("port") as HTMLInputElement const hostEles: HTMLCollectionOf = document.getElementsByClassName("host") as HTMLCollectionOf; // const port = doc.querySelector("input[type=number]")?.value // const host = doc.querySelector("input[type=text]")?.value // 获取端口和host const port = portEle.value let hosts: string[] = []; for (const hostEle of hostEles) { if (hostEle.value) { hosts.push(hostEle.value); } } window.llonebot.setConfig({ port: parseInt(port), hosts: hosts }) alert("保存成功"); }) doc.querySelectorAll("section").forEach((node) => view.appendChild(node)); } onLoad() export { onSettingWindowCreated }