mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
sync
This commit is contained in:
@@ -36,7 +36,7 @@ import { OB11FriendRequestEvent } from '../onebot11/event/request/OB11FriendRequ
|
||||
import path from 'node:path'
|
||||
import { dbUtil } from '../common/db'
|
||||
import { setConfig } from './setConfig'
|
||||
import { NTQQUserApi, NTQQGroupApi, sentMessages } from '../ntqqapi/api'
|
||||
import { NTQQUserApi, NTQQGroupApi } from '../ntqqapi/api'
|
||||
import { checkNewVersion, upgradeLLOneBot } from '../common/utils/upgrade'
|
||||
import { log } from '../common/utils/log'
|
||||
import { getConfigUtil } from '../common/config'
|
||||
@@ -208,10 +208,6 @@ function onLoad() {
|
||||
const recallMsgIds: string[] = [] // 避免重复上报
|
||||
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.UPDATE_MSG], async (payload) => {
|
||||
for (const message of payload.msgList) {
|
||||
const sentMessage = sentMessages[message.msgId]
|
||||
if (sentMessage) {
|
||||
Object.assign(sentMessage, message)
|
||||
}
|
||||
log('message update', message.msgId, message)
|
||||
if (message.recallTime != '0') {
|
||||
if (recallMsgIds.includes(message.msgId)) {
|
||||
|
@@ -1,123 +1,50 @@
|
||||
import { callNTQQApi, GeneralCallResult, NTQQApiMethod } from '../ntcall'
|
||||
import { ChatType, RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
|
||||
import { dbUtil } from '../../common/db'
|
||||
import { RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
|
||||
import { selfInfo } from '../../common/data'
|
||||
import { ReceiveCmdS, registerReceiveHook } from '../hook'
|
||||
import { log } from '../../common/utils/log'
|
||||
import { sleep } from '../../common/utils/helper'
|
||||
import { getBuildVersion } from '../../common/utils'
|
||||
import { getSession } from '@/ntqqapi/wrapper'
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask'
|
||||
|
||||
export let sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void) | null> = {} // peerUid: callbackFunc
|
||||
|
||||
export let sentMessages: Record<string, RawMessage> = {} // msgId: RawMessage
|
||||
|
||||
async function sendWaiter(peer: Peer, waitComplete = true, timeout: number = 10000) {
|
||||
// 等待上一个相同的peer发送完
|
||||
const peerUid = peer.peerUid
|
||||
let checkLastSendUsingTime = 0
|
||||
const waitLastSend = async () => {
|
||||
if (checkLastSendUsingTime > timeout) {
|
||||
throw '发送超时'
|
||||
}
|
||||
let lastSending = sendMessagePool[peer.peerUid]
|
||||
if (lastSending) {
|
||||
// log("有正在发送的消息,等待中...")
|
||||
await sleep(500)
|
||||
checkLastSendUsingTime += 500
|
||||
return await waitLastSend()
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
}
|
||||
await waitLastSend()
|
||||
|
||||
let sentMessage: RawMessage | null = null
|
||||
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
|
||||
delete sendMessagePool[peerUid]
|
||||
sentMessage = rawMessage
|
||||
sentMessages[rawMessage.msgId] = rawMessage
|
||||
}
|
||||
|
||||
let checkSendCompleteUsingTime = 0
|
||||
const checkSendComplete = async (): Promise<RawMessage> => {
|
||||
if (sentMessage) {
|
||||
if (waitComplete) {
|
||||
if (sentMessage.sendStatus == 2) {
|
||||
delete sentMessages[sentMessage.msgId]
|
||||
return sentMessage
|
||||
}
|
||||
}
|
||||
else {
|
||||
delete sentMessages[sentMessage.msgId]
|
||||
return sentMessage
|
||||
}
|
||||
// log(`给${peerUid}发送消息成功`)
|
||||
}
|
||||
checkSendCompleteUsingTime += 500
|
||||
if (checkSendCompleteUsingTime > timeout) {
|
||||
throw '发送超时'
|
||||
}
|
||||
await sleep(500)
|
||||
return await checkSendComplete()
|
||||
}
|
||||
return checkSendComplete()
|
||||
}
|
||||
|
||||
export class NTQQMsgApi {
|
||||
static async getTempChatInfo(chatType: ChatType2, peerUid: string) {
|
||||
const session = getSession()
|
||||
return session?.getMsgService().getTempChatInfo(chatType, peerUid)!
|
||||
}
|
||||
|
||||
static async prepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
|
||||
//By Jadx/Ida Mlikiowa
|
||||
let TempGameSession = {
|
||||
nickname: '',
|
||||
gameAppId: '',
|
||||
selfTinyId: '',
|
||||
peerRoleId: '',
|
||||
peerOpenId: '',
|
||||
}
|
||||
const session = getSession()
|
||||
return session?.getMsgService().prepareTempChat({
|
||||
chatType: ChatType2.KCHATTYPETEMPC2CFROMGROUP,
|
||||
peerUid: toUserUid,
|
||||
peerNickname: nickname,
|
||||
fromGroupCode: GroupCode,
|
||||
sig: '',
|
||||
selfPhone: '',
|
||||
selfUid: selfInfo.uid,
|
||||
gameSession: TempGameSession
|
||||
})
|
||||
}
|
||||
|
||||
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
||||
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
||||
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
||||
// 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
|
||||
emojiId = emojiId.toString()
|
||||
return await callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.EMOJI_LIKE,
|
||||
args: [
|
||||
{
|
||||
peer,
|
||||
msgSeq,
|
||||
emojiId,
|
||||
emojiType: emojiId.length > 3 ? '2' : '1',
|
||||
setEmoji: set,
|
||||
},
|
||||
null,
|
||||
],
|
||||
})
|
||||
const session = getSession()
|
||||
return session?.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set)
|
||||
}
|
||||
|
||||
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) {
|
||||
return await callNTQQApi<GeneralCallResult & { msgList: RawMessage[] }>({
|
||||
methodName: NTQQApiMethod.GET_MULTI_MSG,
|
||||
args: [
|
||||
{
|
||||
peer,
|
||||
rootMsgId,
|
||||
parentMsgId,
|
||||
},
|
||||
null,
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
static async getMsgBoxInfo(peer: Peer) {
|
||||
return await callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.GET_MSG_BOX_INFO,
|
||||
args: [
|
||||
{
|
||||
contacts: [
|
||||
peer
|
||||
],
|
||||
},
|
||||
null,
|
||||
],
|
||||
})
|
||||
const session = getSession()
|
||||
return session?.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId)!
|
||||
}
|
||||
|
||||
static async activateChat(peer: Peer) {
|
||||
@@ -153,27 +80,6 @@ export class NTQQMsgApi {
|
||||
return session?.getMsgService().getMsgsIncludeSelf(peer, msgId, count, isReverseOrder)!
|
||||
}
|
||||
|
||||
static async fetchRecentContact() {
|
||||
await callNTQQApi({
|
||||
methodName: NTQQApiMethod.RECENT_CONTACT,
|
||||
args: [
|
||||
{
|
||||
fetchParam: {
|
||||
anchorPointContact: {
|
||||
contactId: '',
|
||||
sortField: '',
|
||||
pos: 0,
|
||||
},
|
||||
relativeMoveCount: 0,
|
||||
listType: 2, // 1普通消息,2群助手内的消息
|
||||
count: 200,
|
||||
fetchOld: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||
const session = getSession()
|
||||
return await session?.getMsgService().recallMsg({
|
||||
@@ -183,29 +89,57 @@ export class NTQQMsgApi {
|
||||
}
|
||||
|
||||
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
if (getBuildVersion() >= 26702) {
|
||||
return NTQQMsgApi.sendMsgV2(peer, msgElements, waitComplete, timeout)
|
||||
function generateMsgId() {
|
||||
const timestamp = Math.floor(Date.now() / 1000)
|
||||
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
||||
const buffer = Buffer.alloc(8)
|
||||
buffer.writeUInt32BE(timestamp, 0)
|
||||
buffer.writeUInt32BE(random, 4)
|
||||
const msgId = BigInt("0x" + buffer.toString('hex')).toString()
|
||||
return msgId
|
||||
}
|
||||
const waiter = sendWaiter(peer, waitComplete, timeout)
|
||||
callNTQQApi({
|
||||
methodName: NTQQApiMethod.SEND_MSG,
|
||||
args: [
|
||||
{
|
||||
msgId: '0',
|
||||
peer,
|
||||
msgElements,
|
||||
msgAttributeInfos: new Map(),
|
||||
},
|
||||
null,
|
||||
],
|
||||
}).then()
|
||||
return await waiter
|
||||
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||
let msgId: string
|
||||
try {
|
||||
msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime())
|
||||
} catch (error) {
|
||||
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
|
||||
//兜底识别策略V2
|
||||
msgId = generateMsgId()
|
||||
}
|
||||
peer.guildId = msgId
|
||||
const data = await NTEventDispatch.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (let msgRecord of msgRecords) {
|
||||
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
'0',
|
||||
peer,
|
||||
msgElements,
|
||||
new Map()
|
||||
)
|
||||
const retMsg = data[1].find(msgRecord => {
|
||||
if (msgRecord.guildId === msgId) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
return retMsg!
|
||||
}
|
||||
|
||||
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
if (peer.chatType === ChatType.temp) {
|
||||
//await NTQQMsgApi.PrepareTempChat().then().catch()
|
||||
}
|
||||
function generateMsgId() {
|
||||
const timestamp = Math.floor(Date.now() / 1000)
|
||||
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
||||
@@ -269,74 +203,65 @@ export class NTQQMsgApi {
|
||||
}
|
||||
|
||||
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||
const waiter = sendWaiter(destPeer, true, 10000)
|
||||
callNTQQApi<GeneralCallResult>({
|
||||
methodName: NTQQApiMethod.FORWARD_MSG,
|
||||
args: [
|
||||
{
|
||||
msgIds: msgIds,
|
||||
srcContact: srcPeer,
|
||||
dstContacts: [destPeer],
|
||||
commentElements: [],
|
||||
msgAttributeInfos: new Map(),
|
||||
},
|
||||
null,
|
||||
],
|
||||
}).then().catch(log)
|
||||
return await waiter
|
||||
const session = getSession()
|
||||
return session?.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], [])!
|
||||
}
|
||||
|
||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||
const msgInfos = msgIds.map((id) => {
|
||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||
const msgInfos = msgIds.map(id => {
|
||||
return { msgId: id, senderShowName: selfInfo.nick }
|
||||
})
|
||||
const apiArgs = [
|
||||
{
|
||||
msgInfos,
|
||||
srcContact: srcPeer,
|
||||
dstContact: destPeer,
|
||||
commentElements: [],
|
||||
msgAttributeInfos: new Map(),
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>,) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (let msgRecord of msgRecords) {
|
||||
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfInfo.uid) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
null,
|
||||
]
|
||||
return await new Promise<RawMessage>((resolve, reject) => {
|
||||
let complete = false
|
||||
setTimeout(() => {
|
||||
if (!complete) {
|
||||
reject('转发消息超时')
|
||||
}
|
||||
}, 5000)
|
||||
registerReceiveHook(ReceiveCmdS.SELF_SEND_MSG, async (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
|
||||
await dbUtil.addMsg(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))
|
||||
}
|
||||
})
|
||||
msgInfos,
|
||||
srcPeer,
|
||||
destPeer,
|
||||
[],
|
||||
new Map()
|
||||
)
|
||||
for (let msg of data[1]) {
|
||||
const arkElement = msg.elements.find(ele => ele.arkElement)
|
||||
if (!arkElement) {
|
||||
continue
|
||||
}
|
||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData)
|
||||
if (forwardData.app != 'com.tencent.multimsg') {
|
||||
continue
|
||||
}
|
||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
||||
return msg
|
||||
}
|
||||
}
|
||||
throw new Error('转发消息超时')
|
||||
}
|
||||
|
||||
static async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||
const session = getSession()
|
||||
const ret = await session?.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: false,
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: 1,
|
||||
})
|
||||
return ret!
|
||||
}
|
||||
|
||||
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import { NTQQApiClass, NTQQApiMethod } from './ntcall'
|
||||
import { NTQQMsgApi, sendMessagePool } from './api/msg'
|
||||
import { NTQQMsgApi } from './api/msg'
|
||||
import { CategoryFriend, ChatType, Group, GroupMember, GroupMemberRole, RawMessage } from './types'
|
||||
import {
|
||||
deleteGroup,
|
||||
@@ -454,18 +454,7 @@ export async function startHook() {
|
||||
|
||||
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
|
||||
const message = msgRecord
|
||||
const peerUid = message.peerUid
|
||||
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
|
||||
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
|
||||
dbUtil.addMsg(message).then()
|
||||
const sendCallback = sendMessagePool[peerUid]
|
||||
if (sendCallback) {
|
||||
try {
|
||||
sendCallback(message)
|
||||
} catch (e: any) {
|
||||
log('receive self msg error', e.stack)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
|
||||
|
@@ -462,6 +462,7 @@ export interface RawMessage {
|
||||
senderUin?: string // 发送者QQ号
|
||||
peerUid: string // 群号 或者 QQ uid
|
||||
peerUin: string // 群号 或者 发送者QQ号
|
||||
guildId: string
|
||||
sendNickName: string
|
||||
sendMemberName?: string // 发送者群名片
|
||||
chatType: ChatType
|
||||
|
@@ -11,11 +11,7 @@ interface Payload {
|
||||
user_id?: number | string
|
||||
}
|
||||
|
||||
interface Response {
|
||||
message_id: number
|
||||
}
|
||||
|
||||
abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
||||
abstract class ForwardSingleMsg extends BaseAction<Payload, null> {
|
||||
protected async getTargetPeer(payload: Payload): Promise<Peer> {
|
||||
if (payload.user_id) {
|
||||
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString())
|
||||
@@ -27,13 +23,13 @@ abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
||||
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() }
|
||||
}
|
||||
|
||||
protected async _handle(payload: Payload): Promise<Response> {
|
||||
protected async _handle(payload: Payload): Promise<null> {
|
||||
const msg = await dbUtil.getMsgByShortId(payload.message_id)
|
||||
if (!msg) {
|
||||
throw new Error(`无法找到消息${payload.message_id}`)
|
||||
}
|
||||
const peer = await this.getTargetPeer(payload)
|
||||
const sentMsg = await NTQQMsgApi.forwardMsg(
|
||||
const ret = await NTQQMsgApi.forwardMsg(
|
||||
{
|
||||
chatType: msg.chatType,
|
||||
peerUid: msg.peerUid,
|
||||
@@ -41,8 +37,10 @@ abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
||||
peer,
|
||||
[msg.msgId],
|
||||
)
|
||||
const ob11MsgId = await dbUtil.addMsg(sentMsg)
|
||||
return { message_id: ob11MsgId! }
|
||||
if (ret.result !== 0) {
|
||||
throw new Error(`转发消息失败 ${ret.errMsg}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -583,13 +583,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
if (nodeMsgIds.length === 0) {
|
||||
throw Error('转发消息失败,节点为空')
|
||||
}
|
||||
try {
|
||||
log('开发转发', nodeMsgIds)
|
||||
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
|
||||
} catch (e) {
|
||||
log('forward failed', e)
|
||||
return null
|
||||
}
|
||||
const returnMsg = await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
|
||||
returnMsg.msgShortId = await dbUtil.addMsg(returnMsg)
|
||||
return returnMsg
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user