群成员获取研究

This commit is contained in:
linyuchen 2023-11-01 17:46:46 +08:00
parent 7a7d21d533
commit e2759417f5
6 changed files with 209 additions and 153 deletions

View File

@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc"
"build-mac": "tsc && npm run deploy-mac",
"deploy-mac": "cp dist/* ~/Library/Containers/com.tencent.qq/Data/Documents/LiteLoaderQQNT/plugins/LLOnebot/"
},
"author": "",
"license": "ISC",

85
src/global.d.ts vendored
View File

@ -1,68 +1,6 @@
declare enum AtType {
notAt = 0,
atUser = 2
}
import {Group, GroupMemberInfo, MessageElement, Peer, PostDataSendMsg, SendMessage, User} from "./types";
declare type Peer = {
chatType: "private" | "group"
name: string
uid: string // qq号
}
interface MessageElement {
raw: {
elements: {
replyElement: {
senderUid: string, // 原消息发送者QQ号
sourceMsgIsIncPic: boolean; // 原消息是否有图片
sourceMsgText: string;
sourceMsgIdInRecords: string; // 原消息id
},
textElement: {
atType: AtType
atUid: string,
content: string
},
picElement: {
sourcePath: string // 图片本地路径
picWidth: number
picHeight: number
fileSize: number
fileName: string
fileUuid: string
}
}[]
}
peer: Peer,
sender: {
uid: string // 一串加密的字符串
memberName: string
nickname: string
}
}
declare type User = {
avatarUrl?: string;
bio?: string; // 签名
nickName: string;
uid?: string; // 加密的字符串
uin: string; // QQ号
}
declare type Group = {
uid: string; // 群号
name: string;
}
declare type SendMessage = {
type: "text",
content: string,
} | {
type: "image",
file: string, // 这是本地路径?
}
declare var LLAPI: {
on(event: "new-messages", callback: (data: MessageElement[]) => void): void;
@ -76,16 +14,12 @@ declare var LLAPI: {
sendMessage(peer: Peer, message: SendMessage[]): Promise<void>;
getGroupsList(forced: boolean): Promise<Group[]>
getFriendsList(forced: boolean): Promise<User[]>
getGroupMemberList(group_id: string, num: number): Promise<{result: { infos: Record<string, GroupMemberInfo> }}>
};
declare type PostDataSendMsg = {
action: "send_private_msg" | "send_group_msg" | "get_group_list",
params: {
user_id: string,
group_id: string,
message: SendMessage[];
}
}
declare var llonebot: {
postData: (data: any) => void
@ -94,4 +28,11 @@ declare var llonebot: {
updateFriends: (friends: User[]) => void
updateGroupMembers: (data: { groupMembers: User[], group_id: string }) => void
startExpress: () => void
};
};
declare global {
interface Window {
LLAPI: typeof LLAPI;
llonebot: typeof llonebot;
}
}

View File

@ -1,5 +1,7 @@
// 运行在 Electron 主进程 下的插件入口
import {Group, PostDataSendMsg, User} from "./types";
const express = require("express")
const {ipcMain, webContents} = require('electron');
const fs = require('fs');
@ -24,8 +26,9 @@ function sendIPCCallSendQQMsg(postData: PostDataSendMsg) {
sendIPCMsg(CHANNEL_SEND_MSG, postData);
}
function log(msg: string){
fs.appendFile("d:\\llonebot.log", msg + "\n", (err: any) => {
function log(msg: any){
let currentDateTime = new Date().toLocaleString();
fs.appendFile("./llonebot.log", currentDateTime + ":" + msg + "\n", (err: any) => {
})
}
@ -143,6 +146,10 @@ function onLoad(plugin: any) {
log(e.toString())
}
})
ipcMain.on("llonebot_log", (event: any, arg: any) => {
log(arg)
})
}

View File

@ -1,5 +1,7 @@
// Electron 主进程 与 渲染进程 交互的桥梁
import {Group, PostDataSendMsg, User} from "./types";
const {contextBridge} = require("electron");
const {ipcRenderer} = require('electron');
@ -24,6 +26,9 @@ contextBridge.exposeInMainWorld("llonebot", {
},
startExpress: () => {
ipcRenderer.send("startExpress");
},
log: (data: any) => {
ipcRenderer.send("log", data);
}
// startExpress,
});

View File

@ -2,16 +2,23 @@
// import express from "express";
// const { ipcRenderer } = require('electron');
enum AtType {
notAt = 0,
atUser = 2
}
import {AtType, Group, MessageElement, Peer, PostDataSendMsg, User} from "./types";
const host = "http://localhost:5000"
let self_qq: string = ""
let groups: Group[] = []
let friends: User[] = []
let groupMembers: { group_id: string, groupMembers: User[] }[] = []
let uid_maps: Record<string, User> = {} // 一串加密的字符串 -> qq号
async function getUserInfo(uid: string): Promise<User>{
let user = uid_maps[uid]
if (!user){
// 从服务器获取用户信息
user = await window.LLAPI.getUserInfo(uid)
uid_maps[uid] = user
}
return user
}
function getFriend(qq: string) {
return friends.find(friend => friend.uid == qq)
@ -21,9 +28,7 @@ function getGroup(qq: string) {
return groups.find(group => group.uid == qq)
}
let self_qq: string = ""
let uid_maps: Record<string, string> = {} // 一串加密的字符串 -> qq号
function forwardMessage(message: MessageElement) {
try {
@ -36,79 +41,92 @@ function forwardMessage(message: MessageElement) {
type: "message",
detail_type: message.peer.chatType,
sub_type: "",
message: message.raw.elements.map(element => {
let message_data: any = {
data: {}
}
if (element.textElement?.atType == AtType.atUser) {
message_data["type"] = "at"
message_data["data"]["mention"] = element.textElement.atUid
} 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
} else if (element.replyElement) {
message_data["type"] = "reply"
message_data["data"]["reply"] = element.replyElement.sourceMsgIdInRecords
}
return message_data
})
message: []
}
let group: Group
if (message.peer.chatType == "group") {
let group_id = message.peer.uid
group = groups.find(group => group.uid == group_id)!
onebot_message_data["group_id"] = message.peer.uid
// todo: 将加密的uid转成qq号
let groupMember = groupMembers.find(group => group.group_id == message.peer.uid)?.groupMembers.find(member => member.uid == message.sender.uid)
onebot_message_data["user_id"] = groupMember!.uin
let groupMember = group.members!.find(member => member.uid == message.sender.uid)
if (groupMember) {
console.log("群成员信息存在,使用群成员信息")
onebot_message_data["user_id"] = groupMember.uin
}
else{
console.log("群成员信息不存在使用原始uid")
onebot_message_data["user_id"] = message.sender.uid
}
console.log("收到群消息", onebot_message_data)
} else if (message.peer.chatType == "private") {
onebot_message_data["user_id"] = message.peer.uid
}
console.log("发送上传消息给ipcmain", onebot_message_data)
llonebot.postData(onebot_message_data);
for (let element of message.raw.elements) {
let message_data: any = {
data: {}
}
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 = group!.members!.find(member => member.uid == uid)
message_data["data"]["mention"] = 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
} else if (element.replyElement) {
message_data["type"] = "reply"
message_data["data"]["reply"] = element.replyElement.sourceMsgIdInRecords
}
onebot_message_data.message.push(message_data)
}
console.log("发送上传消息给ipc main", onebot_message_data)
window.llonebot.postData(onebot_message_data);
} catch (e) {
console.log("上传消息事件失败", e)
}
}
function handleNewMessage(messages: MessageElement[]) {
messages.forEach(message => {
async function handleNewMessage(messages: MessageElement[]) {
for (let message of messages) {
if (message.peer.chatType == "group") {
let group = groupMembers.find(group => group.group_id == message.peer.uid)
let group = groups.find(group => group.uid == message.peer.uid)
if (!group) {
let members = (await window.LLAPI.getGroupMemberList(message.peer.uid + "_groupMemberList_MainWindow", 5000)).result.infos
let membersList = Object.values(members)
group = {
group_id: message.peer.uid,
groupMembers: []
name: message.peer.name,
uid: message.peer.uid,
members: membersList
}
groupMembers.push(group)
}
let existMember = group!.groupMembers.find(member => member.uid == message.sender.uid)
if (!existMember) {
window.LLAPI.getUserInfo(message.sender.uid).then(user => {
let member = {memberName: message.sender.memberName, uid: user.uin, nickName: user.nickName}
// group!.groupMembers.push(member)
group!.groupMembers.push(user)
llonebot.updateGroupMembers(group!)
forwardMessage(message)
}).catch(err => {
console.log("获取群成员信息失败", err)
})
}else{
forwardMessage(message)
groups.push(group)
window.llonebot.updateGroups(groups);
}
}
else{
forwardMessage(message);
}
})
forwardMessage(message);
}
}
async function getGroups(){
groups = await window.LLAPI.getGroupsList(false)
for (let group of groups) {
group.members = [];
}
window.llonebot.updateGroups(groups)
}
function onLoad() {
llonebot.startExpress();
llonebot.listenSendMessage((postData: PostDataSendMsg) => {
window.llonebot.startExpress();
window.llonebot.listenSendMessage((postData: PostDataSendMsg) => {
if (postData.action == "send_private_msg" || postData.action == "send_group_msg") {
let peer: Peer | null = null;
if (postData.action == "send_private_msg") {
@ -131,30 +149,25 @@ function onLoad() {
}
}
if (peer) {
LLAPI.sendMessage(peer, postData.params.message).then(res => console.log("消息发送成功:", res),
window.LLAPI.sendMessage(peer, postData.params.message).then(res => console.log("消息发送成功:", res),
err => console.log("消息发送失败", postData, err))
}
}
});
window.LLAPI.getGroupsList(false).then(groupsList => {
groups = groupsList
llonebot.updateGroups(groupsList)
})
window.LLAPI.on("new-messages", (messages) => {
console.log("收到新消息", messages)
// 往groupMembers里面添加群成员
if (!self_qq){
window.LLAPI.getAccountInfo().then(accountInfo => {
console.log("getAccountInfo", accountInfo)
self_qq = accountInfo.uin
handleNewMessage(messages)
})
}else{
handleNewMessage(messages)
getGroups().then()
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(() => {})
}
window.LLAPI.on("new-messages", onNewMessages);
// console.log("getAccountInfo", LLAPI.getAccountInfo());
}

89
src/types.ts Normal file
View File

@ -0,0 +1,89 @@
export enum AtType {
notAt = 0,
atUser = 2
}
export type GroupMemberInfo = {
avatarPath: string;
cardName: string;
cardType: number;
isDelete: boolean;
nick: string;
qid: string;
remark: string;
role: number; // 群主:4, 管理员:3群员:2
shutUpTime: number; // 禁言时间,单位是什么暂时不清楚
uid: string; // 加密的字符串
uin: string; // QQ号
}
export type User = {
avatarUrl?: string;
bio?: string; // 签名
nickName: string;
uid?: string; // 加密的字符串
uin: string; // QQ号
}
export type Group = {
uid: string; // 群号
name: string;
members?: GroupMemberInfo[];
}
export type Peer = {
chatType: "private" | "group"
name: string
uid: string // qq号
}
export type MessageElement = {
raw: {
elements: {
replyElement: {
senderUid: string, // 原消息发送者QQ号
sourceMsgIsIncPic: boolean; // 原消息是否有图片
sourceMsgText: string;
sourceMsgIdInRecords: string; // 原消息id
},
textElement: {
atType: AtType
atUid: string,
content: string,
atNtUid: string
},
picElement: {
sourcePath: string // 图片本地路径
picWidth: number
picHeight: number
fileSize: number
fileName: string
fileUuid: string
}
}[]
}
peer: Peer,
sender: {
uid: string // 一串加密的字符串
memberName: string
nickname: string
}
}
export type SendMessage = {
type: "text",
content: string,
} | {
type: "image",
file: string, // 本地路径
}
export type PostDataSendMsg = {
action: "send_private_msg" | "send_group_msg" | "get_group_list",
params: {
user_id: string,
group_id: string,
message: SendMessage[];
}
}