mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
fix: send multi forward msg
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { ReceiveCmd, hookApiCallbacks, registerReceiveHook, removeReceiveHook } from "./hook";
|
||||
import { log } from "../common/utils";
|
||||
import { ChatType, Friend, PicElement, SelfInfo, User } from "./types";
|
||||
import { Group } from "./types";
|
||||
import { GroupMember } from "./types";
|
||||
import { RawMessage } from "./types";
|
||||
import { SendMessageElement } from "./types";
|
||||
import {ipcMain} from "electron";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from "./hook";
|
||||
import {log} from "../common/utils";
|
||||
import {ChatType, Friend, Group, GroupMember, RawMessage, SelfInfo, SendMessageElement, User} from "./types";
|
||||
import * as fs from "fs";
|
||||
import {addHistoryMsg, msgHistory, selfInfo} from "../common/data";
|
||||
|
||||
interface IPCReceiveEvent {
|
||||
eventName: string
|
||||
@@ -29,7 +26,6 @@ export enum NTQQApiClass {
|
||||
|
||||
export enum NTQQApiMethod {
|
||||
LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike",
|
||||
UPDATE_MSG = "nodeIKernelMsgListener/onMsgInfoListUpdate",
|
||||
SELF_INFO = "fetchAuthData",
|
||||
FRIENDS = "nodeIKernelBuddyService/getBuddyList",
|
||||
GROUPS = "nodeIKernelGroupService/getGroupList",
|
||||
@@ -44,7 +40,8 @@ export enum NTQQApiMethod {
|
||||
MEDIA_FILE_PATH = "nodeIKernelMsgService/getRichMediaFilePathForGuild",
|
||||
RECALL_MSG = "nodeIKernelMsgService/recallMsg",
|
||||
SEND_MSG = "nodeIKernelMsgService/sendMsg",
|
||||
DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia"
|
||||
DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia",
|
||||
MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment" // 合并转发
|
||||
}
|
||||
|
||||
enum NTQQApiChannel {
|
||||
@@ -64,8 +61,24 @@ enum CallBackType {
|
||||
METHOD
|
||||
}
|
||||
|
||||
interface NTQQApiParams {
|
||||
methodName: NTQQApiMethod,
|
||||
className?: NTQQApiClass,
|
||||
channel?: NTQQApiChannel,
|
||||
args?: unknown[],
|
||||
cbCmd?: ReceiveCmd | null
|
||||
timeoutSecond?: number,
|
||||
}
|
||||
|
||||
function callNTQQApi<ReturnType>(channel: NTQQApiChannel, className: NTQQApiClass, methodName: NTQQApiMethod, args: unknown[] = [], cbCmd: ReceiveCmd | null = null, timeout = 5) {
|
||||
function callNTQQApi<ReturnType>(params: NTQQApiParams) {
|
||||
let {
|
||||
className, methodName, channel, args,
|
||||
cbCmd, timeoutSecond: timeout
|
||||
} = params;
|
||||
className = className ?? NTQQApiClass.NT_API;
|
||||
channel = channel ?? NTQQApiChannel.IPC_UP_2;
|
||||
args = args ?? [];
|
||||
timeout = timeout ?? 5;
|
||||
const uuid = uuidv4();
|
||||
// log("callNTQQApi", channel, className, methodName, args, uuid)
|
||||
return new Promise((resolve: (data: ReturnType) => void, reject) => {
|
||||
@@ -102,16 +115,18 @@ function callNTQQApi<ReturnType>(channel: NTQQApiChannel, className: NTQQApiClas
|
||||
reject(`ntqq api timeout ${channel}, ${className}, ${methodName}`)
|
||||
}
|
||||
}, _timeout)
|
||||
|
||||
const eventName = className + "-" + channel[channel.length - 1];
|
||||
const apiArgs = [methodName, ...args]
|
||||
ipcMain.emit(
|
||||
channel,
|
||||
{},
|
||||
{type: 'request', callbackId: uuid, eventName: className + "-" + channel[channel.length - 1]},
|
||||
[methodName, ...args],
|
||||
{type: 'request', callbackId: uuid, eventName},
|
||||
apiArgs
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export let sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void) | null> = {}// peerUid: callbackFunnc
|
||||
|
||||
interface GeneralCallResult {
|
||||
@@ -123,34 +138,49 @@ interface GeneralCallResult {
|
||||
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])
|
||||
return callNTQQApi({
|
||||
methodName: NTQQApiMethod.LIKE_FRIEND,
|
||||
args: [{
|
||||
doLikeUserInfo: {
|
||||
friendUid: uid,
|
||||
sourceId: 71,
|
||||
doLikeCount: count,
|
||||
doLikeTollCount: 0
|
||||
}
|
||||
}, null]
|
||||
})
|
||||
}
|
||||
|
||||
static getSelfInfo() {
|
||||
return callNTQQApi<SelfInfo>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.GLOBAL_DATA, NTQQApiMethod.SELF_INFO, [], null, 2)
|
||||
|
||||
return callNTQQApi<SelfInfo>({
|
||||
className: NTQQApiClass.GLOBAL_DATA,
|
||||
methodName: NTQQApiMethod.SELF_INFO, timeoutSecond: 2
|
||||
})
|
||||
}
|
||||
|
||||
static async getUserInfo(uid: string) {
|
||||
const result = await callNTQQApi<{
|
||||
profiles: Map<string, User>
|
||||
}>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.USER_INFO,
|
||||
[{force: true, uids: [uid]}, undefined], ReceiveCmd.USER_INFO)
|
||||
const result = await callNTQQApi<{ profiles: Map<string, User> }>({
|
||||
methodName: NTQQApiMethod.USER_INFO,
|
||||
args: [{force: true, uids: [uid]}, undefined],
|
||||
cbCmd: ReceiveCmd.USER_INFO
|
||||
})
|
||||
return result.profiles.get(uid)
|
||||
}
|
||||
|
||||
static async getFriends(forced = false) {
|
||||
const data = await callNTQQApi<{
|
||||
data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: Friend[] }[]
|
||||
}>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.FRIENDS, [{force_update: forced}, undefined], ReceiveCmd.FRIENDS)
|
||||
data: {
|
||||
categoryId: number,
|
||||
categroyName: string,
|
||||
categroyMbCount: number,
|
||||
buddyList: Friend[]
|
||||
}[]
|
||||
}>(
|
||||
{
|
||||
methodName: NTQQApiMethod.FRIENDS,
|
||||
args: [{force_update: forced}, undefined],
|
||||
cbCmd: ReceiveCmd.FRIENDS
|
||||
})
|
||||
let _friends: Friend[] = [];
|
||||
for (const fData of data.data) {
|
||||
_friends.push(...fData.buddyList)
|
||||
@@ -166,26 +196,31 @@ export class NTQQApi {
|
||||
const result = await callNTQQApi<{
|
||||
updateType: number,
|
||||
groupList: Group[]
|
||||
}>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.GROUPS, [{force_update: forced}, undefined], cbCmd)
|
||||
}>({methodName: NTQQApiMethod.GROUPS, args: [{force_update: forced}, undefined], cbCmd})
|
||||
return result.groupList
|
||||
}
|
||||
|
||||
static async getGroupMembers(groupQQ: string, num = 3000) {
|
||||
const sceneId = await callNTQQApi(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.GROUP_MEMBER_SCENE, [{
|
||||
groupCode: groupQQ,
|
||||
scene: "groupMemberList_MainWindow"
|
||||
}])
|
||||
const sceneId = await callNTQQApi({
|
||||
methodName: NTQQApiMethod.GROUP_MEMBER_SCENE,
|
||||
args: [{
|
||||
groupCode: groupQQ,
|
||||
scene: "groupMemberList_MainWindow"
|
||||
}]
|
||||
})
|
||||
// log("get group member sceneId", sceneId);
|
||||
try {
|
||||
const result = await callNTQQApi<{
|
||||
result: { infos: any }
|
||||
}>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.GROUP_MEMBERS,
|
||||
[{
|
||||
}>({
|
||||
methodName: NTQQApiMethod.GROUP_MEMBERS,
|
||||
args: [{
|
||||
sceneId: sceneId,
|
||||
num: num
|
||||
},
|
||||
null
|
||||
])
|
||||
]
|
||||
})
|
||||
// log("members info", typeof result.result.infos, Object.keys(result.result.infos))
|
||||
let values = result.result.infos.values()
|
||||
|
||||
@@ -200,31 +235,38 @@ export class NTQQApi {
|
||||
|
||||
|
||||
static getFileType(filePath: string) {
|
||||
return callNTQQApi<{
|
||||
ext: string
|
||||
}>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.FS_API, NTQQApiMethod.FILE_TYPE, [filePath])
|
||||
return callNTQQApi<{ ext: string }>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_TYPE, args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
static getFileMd5(filePath: string) {
|
||||
return callNTQQApi<string>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.FS_API, NTQQApiMethod.FILE_MD5, [filePath])
|
||||
return callNTQQApi<string>({
|
||||
className: NTQQApiClass.FS_API,
|
||||
methodName: NTQQApiMethod.FILE_MD5,
|
||||
args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
static copyFile(filePath: string, destPath: string) {
|
||||
return callNTQQApi<string>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.FS_API, NTQQApiMethod.FILE_COPY, [{
|
||||
fromPath: filePath,
|
||||
toPath: destPath
|
||||
}])
|
||||
return callNTQQApi<string>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_COPY, args: [{
|
||||
fromPath: filePath,
|
||||
toPath: destPath
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
static getImageSize(filePath: string) {
|
||||
return callNTQQApi<{
|
||||
width: number,
|
||||
height: number
|
||||
}>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.FS_API, NTQQApiMethod.IMAGE_SIZE, [filePath])
|
||||
return callNTQQApi<{ width: number, height: number }>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.IMAGE_SIZE, args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
static getFileSize(filePath: string) {
|
||||
return callNTQQApi<number>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.FS_API, NTQQApiMethod.FILE_SIZE, [filePath])
|
||||
return callNTQQApi<number>({
|
||||
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_SIZE, args: [filePath]
|
||||
})
|
||||
}
|
||||
|
||||
// 上传文件到QQ的文件夹
|
||||
@@ -233,23 +275,25 @@ export class NTQQApi {
|
||||
let ext = (await NTQQApi.getFileType(filePath))?.ext
|
||||
if (ext) {
|
||||
ext = "." + ext
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
ext = ""
|
||||
}
|
||||
const fileName = `${md5}${ext}`;
|
||||
const mediaPath = await callNTQQApi<string>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.MEDIA_FILE_PATH, [{
|
||||
path_info: {
|
||||
md5HexStr: md5,
|
||||
fileName: fileName,
|
||||
elementType: 2,
|
||||
elementSubType: 0,
|
||||
thumbSize: 0,
|
||||
needCreate: true,
|
||||
downloadType: 1,
|
||||
file_uuid: ""
|
||||
}
|
||||
}])
|
||||
const mediaPath = await callNTQQApi<string>({
|
||||
methodName: NTQQApiMethod.MEDIA_FILE_PATH,
|
||||
args: [{
|
||||
path_info: {
|
||||
md5HexStr: md5,
|
||||
fileName: fileName,
|
||||
elementType: 2,
|
||||
elementSubType: 0,
|
||||
thumbSize: 0,
|
||||
needCreate: true,
|
||||
downloadType: 1,
|
||||
file_uuid: ""
|
||||
}
|
||||
}]
|
||||
})
|
||||
log("media path", mediaPath)
|
||||
await NTQQApi.copyFile(filePath, mediaPath);
|
||||
const fileSize = await NTQQApi.getFileSize(filePath);
|
||||
@@ -280,28 +324,32 @@ export class NTQQApi {
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
await callNTQQApi(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.DOWNLOAD_MEDIA, apiParams)
|
||||
await callNTQQApi({methodName: NTQQApiMethod.DOWNLOAD_MEDIA, args: apiParams})
|
||||
return sourcePath
|
||||
}
|
||||
|
||||
static recallMsg(peer: Peer, msgIds: string[]) {
|
||||
return callNTQQApi(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.RECALL_MSG, [{
|
||||
peer,
|
||||
msgIds
|
||||
}, null])
|
||||
return callNTQQApi({
|
||||
methodName: NTQQApiMethod.RECALL_MSG, args: [{
|
||||
peer,
|
||||
msgIds
|
||||
}, null]
|
||||
})
|
||||
}
|
||||
|
||||
static sendMsg(peer: Peer, msgElements: SendMessageElement[]) {
|
||||
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false) {
|
||||
const sendTimeout = 10 * 1000
|
||||
|
||||
return new Promise<RawMessage>((resolve, reject) => {
|
||||
const peerUid = peer.peerUid;
|
||||
let usingTime = 0;
|
||||
let success = false;
|
||||
let isTimeout = false;
|
||||
|
||||
const checkSuccess = () => {
|
||||
if (!success) {
|
||||
sendMessagePool[peerUid] = null;
|
||||
isTimeout = true;
|
||||
reject("发送超时")
|
||||
}
|
||||
}
|
||||
@@ -311,29 +359,99 @@ export class NTQQApi {
|
||||
let lastSending = sendMessagePool[peerUid]
|
||||
if (sendTimeout < usingTime) {
|
||||
sendMessagePool[peerUid] = null;
|
||||
isTimeout = true;
|
||||
reject("发送超时")
|
||||
}
|
||||
if (!!lastSending) {
|
||||
// log("有正在发送的消息,等待中...")
|
||||
usingTime += 100;
|
||||
setTimeout(checkLastSend, 100);
|
||||
usingTime += 500;
|
||||
setTimeout(checkLastSend, 500);
|
||||
} else {
|
||||
log("可以进行发送消息,设置发送成功回调", sendMessagePool)
|
||||
sendMessagePool[peerUid] = (rawMessage: RawMessage) => {
|
||||
success = true;
|
||||
sendMessagePool[peerUid] = null;
|
||||
resolve(rawMessage);
|
||||
const checkSendComplete = () => {
|
||||
if (isTimeout) {
|
||||
return reject("发送超时")
|
||||
}
|
||||
if (msgHistory[rawMessage.msgId]?.sendStatus == 2) {
|
||||
success = true;
|
||||
resolve(rawMessage);
|
||||
} else {
|
||||
setTimeout(checkSendComplete, 500)
|
||||
}
|
||||
}
|
||||
if (waitComplete) {
|
||||
checkSendComplete();
|
||||
} else {
|
||||
success = true;
|
||||
resolve(rawMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkLastSend()
|
||||
callNTQQApi(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.SEND_MSG, [{
|
||||
msgId: "0",
|
||||
peer, msgElements,
|
||||
msgAttributeInfos: new Map(),
|
||||
}, null]).then()
|
||||
callNTQQApi({
|
||||
methodName: NTQQApiMethod.SEND_MSG,
|
||||
args: [{
|
||||
msgId: "0",
|
||||
peer, msgElements,
|
||||
msgAttributeInfos: new Map(),
|
||||
}, null]
|
||||
}).then()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||
let msgInfos = msgIds.map(id => {
|
||||
return {msgId: id, senderShowName: "LLOneBot"}
|
||||
})
|
||||
const apiArgs = [
|
||||
{
|
||||
msgInfos,
|
||||
srcContact: srcPeer,
|
||||
dstContact: destPeer,
|
||||
commentElements: [],
|
||||
msgAttributeInfos: new Map()
|
||||
},
|
||||
null,
|
||||
]
|
||||
return new Promise<RawMessage>((resolve, reject) => {
|
||||
let complete = false
|
||||
setTimeout(() => {
|
||||
if (!complete) {
|
||||
reject("转发消息超时");
|
||||
}
|
||||
}, 5000)
|
||||
registerReceiveHook(ReceiveCmd.SELF_SEND_MSG, (payload: { msgRecord: RawMessage }) => {
|
||||
const msg = payload.msgRecord;
|
||||
// 需要判断它是转发的消息,并且识别到是当前转发的这一条
|
||||
const arkElement = msg.elements.find(ele => ele.arkElement)
|
||||
if (!arkElement) {
|
||||
log("收到的不是转发消息")
|
||||
return
|
||||
}
|
||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
||||
if (forwardData.app != "com.tencent.multimsg") {
|
||||
return
|
||||
}
|
||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
||||
complete = true;
|
||||
addHistoryMsg(msg)
|
||||
resolve(msg);
|
||||
log("收到转发消息:", payload)
|
||||
}
|
||||
})
|
||||
callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.MULTI_FORWARD_MSG,
|
||||
args: apiArgs
|
||||
}).then(result => {
|
||||
log("转发消息结果:", result, apiArgs)
|
||||
if (result.result !== 0) {
|
||||
complete = true;
|
||||
reject("转发消息失败," + JSON.stringify(result));
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user