mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
commit
f540f324a1
@ -4,7 +4,7 @@
|
|||||||
"name": "LLOneBot",
|
"name": "LLOneBot",
|
||||||
"slug": "LLOneBot",
|
"slug": "LLOneBot",
|
||||||
"description": "实现 OneBot 11 协议,用以 QQ 机器人开发",
|
"description": "实现 OneBot 11 协议,用以 QQ 机器人开发",
|
||||||
"version": "3.28.3",
|
"version": "3.28.4",
|
||||||
"icon": "./icon.webp",
|
"icon": "./icon.webp",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
@ -13,7 +13,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"repo": "linyuchen/LiteLoaderQQNT-OneBotApi",
|
"repo": "LLOneBot/LLOneBot",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"release": {
|
"release": {
|
||||||
"tag": "latest",
|
"tag": "latest",
|
||||||
|
@ -16,7 +16,7 @@ const manifest = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
repository: {
|
repository: {
|
||||||
repo: 'linyuchen/LiteLoaderQQNT-OneBotApi',
|
repo: 'LLOneBot/LLOneBot',
|
||||||
branch: 'main',
|
branch: 'main',
|
||||||
release: {
|
release: {
|
||||||
tag: 'latest',
|
tag: 'latest',
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import fs from 'node:fs'
|
|
||||||
import os from 'node:os'
|
import os from 'node:os'
|
||||||
import { systemPlatform } from './system'
|
import { systemPlatform } from './system'
|
||||||
|
|
||||||
@ -38,32 +37,6 @@ type QQPkgInfo = {
|
|||||||
platform: string
|
platform: string
|
||||||
eleArch: string
|
eleArch: string
|
||||||
}
|
}
|
||||||
type QQVersionConfigInfo = {
|
|
||||||
baseVersion: string
|
|
||||||
curVersion: string
|
|
||||||
prevVersion: string
|
|
||||||
onErrorVersions: Array<any>
|
|
||||||
buildId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
|
||||||
'baseVersion': '9.9.9-23361',
|
|
||||||
'curVersion': '9.9.9-23361',
|
|
||||||
'prevVersion': '',
|
|
||||||
'onErrorVersions': [],
|
|
||||||
'buildId': '23361',
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs.existsSync(configVersionInfoPath)) {
|
|
||||||
try {
|
|
||||||
const _ = JSON.parse(fs.readFileSync(configVersionInfoPath).toString())
|
|
||||||
_qqVersionConfigInfo = Object.assign(_qqVersionConfigInfo, _)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Load QQ version config info failed, Use default version', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo
|
|
||||||
|
|
||||||
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
|
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
|
||||||
// platform_type: 3,
|
// platform_type: 3,
|
||||||
@ -74,14 +47,6 @@ export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
|
|||||||
// platVer: '10.0.26100',
|
// platVer: '10.0.26100',
|
||||||
// clientVer: '9.9.9-23159',
|
// clientVer: '9.9.9-23159',
|
||||||
|
|
||||||
let _appid: string = '537213803' // 默认为 Windows 平台的 appid
|
|
||||||
if (systemPlatform === 'linux') {
|
|
||||||
_appid = '537213827'
|
|
||||||
}
|
|
||||||
// todo: mac 平台的 appid
|
|
||||||
export const appid = _appid
|
|
||||||
export const isQQ998: boolean = qqPkgInfo.buildVersion >= '22106'
|
|
||||||
|
|
||||||
export function getBuildVersion(): number {
|
export function getBuildVersion(): number {
|
||||||
return +qqPkgInfo.buildVersion
|
return +qqPkgInfo.buildVersion
|
||||||
}
|
}
|
@ -16,4 +16,3 @@ if (!fs.existsSync(TEMP_DIR)) {
|
|||||||
export { getVideoInfo } from './video'
|
export { getVideoInfo } from './video'
|
||||||
export { checkFfmpeg } from './video'
|
export { checkFfmpeg } from './video'
|
||||||
export { encodeSilk } from './audio'
|
export { encodeSilk } from './audio'
|
||||||
export { isQQ998 } from './QQBasicInfo'
|
|
@ -36,7 +36,7 @@ import { OB11FriendRequestEvent } from '../onebot11/event/request/OB11FriendRequ
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { dbUtil } from '../common/db'
|
import { dbUtil } from '../common/db'
|
||||||
import { setConfig } from './setConfig'
|
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 { checkNewVersion, upgradeLLOneBot } from '../common/utils/upgrade'
|
||||||
import { log } from '../common/utils/log'
|
import { log } from '../common/utils/log'
|
||||||
import { getConfigUtil } from '../common/config'
|
import { getConfigUtil } from '../common/config'
|
||||||
@ -208,10 +208,6 @@ function onLoad() {
|
|||||||
const recallMsgIds: string[] = [] // 避免重复上报
|
const recallMsgIds: string[] = [] // 避免重复上报
|
||||||
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.UPDATE_MSG], async (payload) => {
|
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.UPDATE_MSG], async (payload) => {
|
||||||
for (const message of payload.msgList) {
|
for (const message of payload.msgList) {
|
||||||
const sentMessage = sentMessages[message.msgId]
|
|
||||||
if (sentMessage) {
|
|
||||||
Object.assign(sentMessage, message)
|
|
||||||
}
|
|
||||||
log('message update', message.msgId, message)
|
log('message update', message.msgId, message)
|
||||||
if (message.recallTime != '0') {
|
if (message.recallTime != '0') {
|
||||||
if (recallMsgIds.includes(message.msgId)) {
|
if (recallMsgIds.includes(message.msgId)) {
|
||||||
|
@ -9,15 +9,21 @@ import {
|
|||||||
ChatType,
|
ChatType,
|
||||||
ElementType,
|
ElementType,
|
||||||
IMAGE_HTTP_HOST,
|
IMAGE_HTTP_HOST,
|
||||||
IMAGE_HTTP_HOST_NT, PicElement,
|
IMAGE_HTTP_HOST_NT,
|
||||||
|
PicElement,
|
||||||
} from '../types'
|
} from '../types'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import { ReceiveCmdS } from '../hook'
|
import { ReceiveCmdS } from '../hook'
|
||||||
import { log } from '@/common/utils'
|
import { log, TEMP_DIR } from '@/common/utils'
|
||||||
import { rkeyManager } from '@/ntqqapi/api/rkey'
|
import { rkeyManager } from '@/ntqqapi/api/rkey'
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { Peer } from '@/ntqqapi/types/msg'
|
import { Peer } from '@/ntqqapi/types/msg'
|
||||||
|
import { calculateFileMD5 } from '@/common/utils/file'
|
||||||
|
import { fileTypeFromFile } from 'file-type'
|
||||||
|
import fsPromise from 'node:fs/promises'
|
||||||
|
import { NTEventDispatch } from '@/common/utils/EventTask'
|
||||||
|
import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners'
|
||||||
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise<string> {
|
static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise<string> {
|
||||||
@ -30,19 +36,7 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async getFileType(filePath: string) {
|
static async getFileType(filePath: string) {
|
||||||
return await callNTQQApi<{ ext: string }>({
|
return fileTypeFromFile(filePath)
|
||||||
className: NTQQApiClass.FS_API,
|
|
||||||
methodName: NTQQApiMethod.FILE_TYPE,
|
|
||||||
args: [filePath],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getFileMd5(filePath: string) {
|
|
||||||
return await callNTQQApi<string>({
|
|
||||||
className: NTQQApiClass.FS_API,
|
|
||||||
methodName: NTQQApiMethod.FILE_MD5,
|
|
||||||
args: [filePath],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async copyFile(filePath: string, destPath: string) {
|
static async copyFile(filePath: string, destPath: string) {
|
||||||
@ -67,44 +61,35 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 上传文件到QQ的文件夹
|
// 上传文件到QQ的文件夹
|
||||||
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
|
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType = 0) {
|
||||||
const md5 = await NTQQFileApi.getFileMd5(filePath)
|
const fileMd5 = await calculateFileMD5(filePath)
|
||||||
let ext = (await NTQQFileApi.getFileType(filePath))?.ext
|
let ext = (await NTQQFileApi.getFileType(filePath))?.ext || ''
|
||||||
if (ext) {
|
if (ext) {
|
||||||
ext = '.' + ext
|
ext = '.' + ext
|
||||||
} else {
|
|
||||||
ext = ''
|
|
||||||
}
|
}
|
||||||
let fileName = `${path.basename(filePath)}`
|
let fileName = `${path.basename(filePath)}`
|
||||||
if (fileName.indexOf('.') === -1) {
|
if (fileName.indexOf('.') === -1) {
|
||||||
fileName += ext
|
fileName += ext
|
||||||
}
|
}
|
||||||
const mediaPath = await callNTQQApi<string>({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.MEDIA_FILE_PATH,
|
const mediaPath = session?.getMsgService().getRichMediaFilePathForGuild({
|
||||||
args: [
|
md5HexStr: fileMd5,
|
||||||
{
|
|
||||||
path_info: {
|
|
||||||
md5HexStr: md5,
|
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
elementType: elementType,
|
elementType: elementType,
|
||||||
elementSubType,
|
elementSubType,
|
||||||
thumbSize: 0,
|
thumbSize: 0,
|
||||||
needCreate: true,
|
needCreate: true,
|
||||||
downloadType: 1,
|
downloadType: 1,
|
||||||
file_uuid: '',
|
file_uuid: ''
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
log('media path', mediaPath)
|
await fsPromise.copyFile(filePath, mediaPath!)
|
||||||
await NTQQFileApi.copyFile(filePath, mediaPath)
|
const fileSize = (await fsPromise.stat(filePath)).size
|
||||||
const fileSize = await NTQQFileApi.getFileSize(filePath)
|
|
||||||
return {
|
return {
|
||||||
md5,
|
md5: fileMd5,
|
||||||
fileName,
|
fileName,
|
||||||
path: mediaPath,
|
path: mediaPath!,
|
||||||
fileSize,
|
fileSize,
|
||||||
ext,
|
ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,19 +100,48 @@ export class NTQQFileApi {
|
|||||||
elementId: string,
|
elementId: string,
|
||||||
thumbPath: string,
|
thumbPath: string,
|
||||||
sourcePath: string,
|
sourcePath: string,
|
||||||
force: boolean = false,
|
timeout = 1000 * 60 * 2,
|
||||||
|
force = false
|
||||||
) {
|
) {
|
||||||
// 用于下载收到的消息中的图片等
|
// 用于下载收到的消息中的图片等
|
||||||
if (sourcePath && fs.existsSync(sourcePath)) {
|
if (sourcePath && fs.existsSync(sourcePath)) {
|
||||||
if (force) {
|
if (force) {
|
||||||
fs.unlinkSync(sourcePath)
|
try {
|
||||||
|
await fsPromise.unlink(sourcePath)
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return sourcePath
|
return sourcePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const apiParams = [
|
const data = await NTEventDispatch.CallNormalEvent<
|
||||||
|
(
|
||||||
|
params: {
|
||||||
|
fileModelId: string,
|
||||||
|
downloadSourceType: number,
|
||||||
|
triggerType: number,
|
||||||
|
msgId: string,
|
||||||
|
chatType: ChatType,
|
||||||
|
peerUid: string,
|
||||||
|
elementId: string,
|
||||||
|
thumbSize: number,
|
||||||
|
downloadType: number,
|
||||||
|
filePath: string
|
||||||
|
}) => Promise<unknown>,
|
||||||
|
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/downloadRichMedia',
|
||||||
|
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||||
|
if (arg.msgId === msgId) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
getReq: {
|
|
||||||
fileModelId: '0',
|
fileModelId: '0',
|
||||||
downloadSourceType: 0,
|
downloadSourceType: 0,
|
||||||
triggerType: 1,
|
triggerType: 1,
|
||||||
@ -137,22 +151,16 @@ export class NTQQFileApi {
|
|||||||
elementId: elementId,
|
elementId: elementId,
|
||||||
thumbSize: 0,
|
thumbSize: 0,
|
||||||
downloadType: 1,
|
downloadType: 1,
|
||||||
filePath: thumbPath,
|
filePath: thumbPath
|
||||||
},
|
}
|
||||||
},
|
)
|
||||||
null,
|
let filePath = data[1].filePath
|
||||||
]
|
if (filePath.startsWith('\\')) {
|
||||||
// log("需要下载media", sourcePath);
|
const downloadPath = TEMP_DIR
|
||||||
await callNTQQApi({
|
filePath = path.join(downloadPath, filePath)
|
||||||
methodName: NTQQApiMethod.DOWNLOAD_MEDIA,
|
// 下载路径是下载文件夹的相对路径
|
||||||
args: apiParams,
|
}
|
||||||
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
|
return filePath
|
||||||
cmdCB: (payload: { notifyInfo: { filePath: string; msgId: string } }) => {
|
|
||||||
log('media 下载完成判断', payload.notifyInfo.msgId, msgId)
|
|
||||||
return payload.notifyInfo.msgId == msgId
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return sourcePath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getImageSize(filePath: string) {
|
static async getImageSize(filePath: string) {
|
||||||
@ -163,22 +171,27 @@ export class NTQQFileApi {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getImageUrl(picElement: PicElement, chatType: ChatType) {
|
static async getImageUrl(element: PicElement) {
|
||||||
const isPrivateImage = chatType !== ChatType.group
|
if (!element) {
|
||||||
const url = picElement.originImageUrl // 没有域名
|
return ''
|
||||||
const md5HexStr = picElement.md5HexStr
|
}
|
||||||
const fileMd5 = picElement.md5HexStr
|
const url: string = element.originImageUrl! // 没有域名
|
||||||
const fileUuid = picElement.fileUuid
|
const md5HexStr = element.md5HexStr
|
||||||
|
const fileMd5 = element.md5HexStr
|
||||||
|
const fileUuid = element.fileUuid
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
if (url.startsWith('/download')) {
|
const UrlParse = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接
|
||||||
// console.log('rkey', rkey);
|
const imageAppid = UrlParse.searchParams.get('appid')
|
||||||
if (url.includes('&rkey=')) {
|
const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid)
|
||||||
|
if (isNewPic) {
|
||||||
|
let UrlRkey = UrlParse.searchParams.get('rkey')
|
||||||
|
if (UrlRkey) {
|
||||||
return IMAGE_HTTP_HOST_NT + url
|
return IMAGE_HTTP_HOST_NT + url
|
||||||
}
|
}
|
||||||
|
const rkeyData = await rkeyManager.getRkey()
|
||||||
const rkeyData = await rkeyManager.getRkey();
|
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey
|
||||||
const existsRKey = isPrivateImage ? rkeyData.private_rkey : rkeyData.group_rkey;
|
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`
|
||||||
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`
|
|
||||||
} else {
|
} else {
|
||||||
// 老的图片url,不需要rkey
|
// 老的图片url,不需要rkey
|
||||||
return IMAGE_HTTP_HOST + url
|
return IMAGE_HTTP_HOST + url
|
||||||
@ -187,7 +200,7 @@ export class NTQQFileApi {
|
|||||||
// 没有url,需要自己拼接
|
// 没有url,需要自己拼接
|
||||||
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`
|
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`
|
||||||
}
|
}
|
||||||
log('图片url获取失败', picElement)
|
log('图片url获取失败', element)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,93 +1,35 @@
|
|||||||
import { callNTQQApi, GeneralCallResult, NTQQApiMethod } from '../ntcall'
|
import { callNTQQApi, GeneralCallResult, NTQQApiMethod } from '../ntcall'
|
||||||
import { ChatType, RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
|
import { RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
|
||||||
import { dbUtil } from '../../common/db'
|
|
||||||
import { selfInfo } from '../../common/data'
|
import { selfInfo } from '../../common/data'
|
||||||
import { ReceiveCmdS, registerReceiveHook } from '../hook'
|
import { getBuildVersion } from '../../common/utils'
|
||||||
import { log } from '../../common/utils/log'
|
|
||||||
import { sleep } from '../../common/utils/helper'
|
|
||||||
import { isQQ998, getBuildVersion } from '../../common/utils'
|
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask'
|
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 {
|
export class NTQQMsgApi {
|
||||||
static async getTempChatInfo(chatType: ChatType2, peerUid: string) {
|
static async getTempChatInfo(chatType: ChatType2, peerUid: string) {
|
||||||
const session = getSession()
|
const session = getSession()
|
||||||
return session?.getMsgService().getTempChatInfo(chatType, peerUid)!
|
return session?.getMsgService().getTempChatInfo(chatType, peerUid)!
|
||||||
}
|
}
|
||||||
|
|
||||||
static enterOrExitAIO(peer: Peer, enter: boolean) {
|
static async prepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
|
||||||
return callNTQQApi<GeneralCallResult>({
|
//By Jadx/Ida Mlikiowa
|
||||||
methodName: NTQQApiMethod.ENTER_OR_EXIT_AIO,
|
let TempGameSession = {
|
||||||
args: [
|
nickname: '',
|
||||||
{
|
gameAppId: '',
|
||||||
"info_list": [
|
selfTinyId: '',
|
||||||
{
|
peerRoleId: '',
|
||||||
peer,
|
peerOpenId: '',
|
||||||
"option": enter ? 1 : 2
|
|
||||||
}
|
}
|
||||||
]
|
const session = getSession()
|
||||||
},
|
return session?.getMsgService().prepareTempChat({
|
||||||
{
|
chatType: ChatType2.KCHATTYPETEMPC2CFROMGROUP,
|
||||||
"send": true
|
peerUid: toUserUid,
|
||||||
},
|
peerNickname: nickname,
|
||||||
],
|
fromGroupCode: GroupCode,
|
||||||
|
sig: '',
|
||||||
|
selfPhone: '',
|
||||||
|
selfUid: selfInfo.uid,
|
||||||
|
gameSession: TempGameSession
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,47 +38,13 @@ export class NTQQMsgApi {
|
|||||||
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
// 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
|
// 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
|
||||||
emojiId = emojiId.toString()
|
emojiId = emojiId.toString()
|
||||||
return await callNTQQApi<GeneralCallResult>({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.EMOJI_LIKE,
|
return session?.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set)
|
||||||
args: [
|
|
||||||
{
|
|
||||||
peer,
|
|
||||||
msgSeq,
|
|
||||||
emojiId,
|
|
||||||
emojiType: emojiId.length > 3 ? '2' : '1',
|
|
||||||
setEmoji: set,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) {
|
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) {
|
||||||
return await callNTQQApi<GeneralCallResult & { msgList: RawMessage[] }>({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.GET_MULTI_MSG,
|
return session?.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId)!
|
||||||
args: [
|
|
||||||
{
|
|
||||||
peer,
|
|
||||||
rootMsgId,
|
|
||||||
parentMsgId,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getMsgBoxInfo(peer: Peer) {
|
|
||||||
return await callNTQQApi<GeneralCallResult>({
|
|
||||||
methodName: NTQQApiMethod.GET_MSG_BOX_INFO,
|
|
||||||
args: [
|
|
||||||
{
|
|
||||||
contacts: [
|
|
||||||
peer
|
|
||||||
],
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async activateChat(peer: Peer) {
|
static async activateChat(peer: Peer) {
|
||||||
@ -158,80 +66,80 @@ export class NTQQMsgApi {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
static async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
|
||||||
// 消息时间从旧到新
|
if (!peer) throw new Error('peer is not allowed')
|
||||||
return await callNTQQApi<GeneralCallResult & { msgList: RawMessage[] }>({
|
if (!msgIds) throw new Error('msgIds is not allowed')
|
||||||
methodName: isQQ998 ? NTQQApiMethod.ACTIVE_CHAT_HISTORY : NTQQApiMethod.HISTORY_MSG,
|
const session = getSession()
|
||||||
args: [
|
//Mlikiowa: 参数不合规会导致NC异常崩溃 原因是TX未对进入参数判断 对应Android标记@NotNull AndroidJADX分析可得
|
||||||
{
|
return await session?.getMsgService().getMsgsByMsgId(peer, msgIds)!
|
||||||
peer,
|
|
||||||
msgId,
|
|
||||||
cnt: count,
|
|
||||||
queryOrder: true,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchRecentContact() {
|
static async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
|
||||||
await callNTQQApi({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.RECENT_CONTACT,
|
// 消息时间从旧到新
|
||||||
args: [
|
return session?.getMsgService().getMsgsIncludeSelf(peer, msgId, count, isReverseOrder)!
|
||||||
{
|
|
||||||
fetchParam: {
|
|
||||||
anchorPointContact: {
|
|
||||||
contactId: '',
|
|
||||||
sortField: '',
|
|
||||||
pos: 0,
|
|
||||||
},
|
|
||||||
relativeMoveCount: 0,
|
|
||||||
listType: 2, // 1普通消息,2群助手内的消息
|
|
||||||
count: 200,
|
|
||||||
fetchOld: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||||
return await callNTQQApi({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.RECALL_MSG,
|
return await session?.getMsgService().recallMsg({
|
||||||
args: [
|
chatType: peer.chatType,
|
||||||
{
|
peerUid: peer.peerUid
|
||||||
peer,
|
}, msgIds)
|
||||||
msgIds,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
if (getBuildVersion() >= 26702) {
|
function generateMsgId() {
|
||||||
return NTQQMsgApi.sendMsgV2(peer, msgElements, waitComplete, timeout)
|
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)
|
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||||
callNTQQApi({
|
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||||
methodName: NTQQApiMethod.SEND_MSG,
|
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||||
args: [
|
let msgId: string
|
||||||
{
|
try {
|
||||||
msgId: '0',
|
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,
|
peer,
|
||||||
msgElements,
|
msgElements,
|
||||||
msgAttributeInfos: new Map(),
|
new Map()
|
||||||
},
|
)
|
||||||
null,
|
const retMsg = data[1].find(msgRecord => {
|
||||||
],
|
if (msgRecord.guildId === msgId) {
|
||||||
}).then()
|
return true
|
||||||
return await waiter
|
}
|
||||||
|
})
|
||||||
|
return retMsg!
|
||||||
}
|
}
|
||||||
|
|
||||||
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
if (peer.chatType === ChatType.temp) {
|
|
||||||
//await NTQQMsgApi.PrepareTempChat().then().catch()
|
|
||||||
}
|
|
||||||
function generateMsgId() {
|
function generateMsgId() {
|
||||||
const timestamp = Math.floor(Date.now() / 1000)
|
const timestamp = Math.floor(Date.now() / 1000)
|
||||||
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
||||||
@ -295,74 +203,65 @@ export class NTQQMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||||
const waiter = sendWaiter(destPeer, true, 10000)
|
const session = getSession()
|
||||||
callNTQQApi<GeneralCallResult>({
|
return session?.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], [])!
|
||||||
methodName: NTQQApiMethod.FORWARD_MSG,
|
|
||||||
args: [
|
|
||||||
{
|
|
||||||
msgIds: msgIds,
|
|
||||||
srcContact: srcPeer,
|
|
||||||
dstContacts: [destPeer],
|
|
||||||
commentElements: [],
|
|
||||||
msgAttributeInfos: new Map(),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
}).then().catch(log)
|
|
||||||
return await waiter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||||
const msgInfos = msgIds.map((id) => {
|
const msgInfos = msgIds.map(id => {
|
||||||
return { msgId: id, senderShowName: selfInfo.nick }
|
return { msgId: id, senderShowName: selfInfo.nick }
|
||||||
})
|
})
|
||||||
const apiArgs = [
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
{
|
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>,) => Promise<unknown>,
|
||||||
msgInfos,
|
(msgList: RawMessage[]) => void
|
||||||
srcContact: srcPeer,
|
>(
|
||||||
dstContact: destPeer,
|
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||||
commentElements: [],
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
msgAttributeInfos: new Map(),
|
1,
|
||||||
},
|
5000,
|
||||||
null,
|
(msgRecords: RawMessage[]) => {
|
||||||
]
|
for (let msgRecord of msgRecords) {
|
||||||
return await new Promise<RawMessage>((resolve, reject) => {
|
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfInfo.uid) {
|
||||||
let complete = false
|
return true
|
||||||
setTimeout(() => {
|
|
||||||
if (!complete) {
|
|
||||||
reject('转发消息超时')
|
|
||||||
}
|
}
|
||||||
}, 5000)
|
}
|
||||||
registerReceiveHook(ReceiveCmdS.SELF_SEND_MSG, async (payload: { msgRecord: RawMessage }) => {
|
return false
|
||||||
const msg = payload.msgRecord
|
},
|
||||||
// 需要判断它是转发的消息,并且识别到是当前转发的这一条
|
msgInfos,
|
||||||
const arkElement = msg.elements.find((ele) => ele.arkElement)
|
srcPeer,
|
||||||
|
destPeer,
|
||||||
|
[],
|
||||||
|
new Map()
|
||||||
|
)
|
||||||
|
for (let msg of data[1]) {
|
||||||
|
const arkElement = msg.elements.find(ele => ele.arkElement)
|
||||||
if (!arkElement) {
|
if (!arkElement) {
|
||||||
// log("收到的不是转发消息")
|
continue
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData)
|
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData)
|
||||||
if (forwardData.app != 'com.tencent.multimsg') {
|
if (forwardData.app != 'com.tencent.multimsg') {
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
||||||
complete = true
|
return msg
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
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) {
|
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||||
|
@ -2,7 +2,7 @@ import { callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod } from '../
|
|||||||
import { SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '../types'
|
import { SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '../types'
|
||||||
import { ReceiveCmdS } from '../hook'
|
import { ReceiveCmdS } from '../hook'
|
||||||
import { selfInfo, friends, groupMembers } from '@/common/data'
|
import { selfInfo, friends, groupMembers } from '@/common/data'
|
||||||
import { CacheClassFuncAsync, isQQ998, log, sleep, getBuildVersion } from '@/common/utils'
|
import { CacheClassFuncAsync, log, getBuildVersion } from '@/common/utils'
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { RequestUtil } from '@/common/utils/request'
|
import { RequestUtil } from '@/common/utils/request'
|
||||||
import { NodeIKernelProfileService, UserDetailSource, ProfileBizType } from '../services'
|
import { NodeIKernelProfileService, UserDetailSource, ProfileBizType } from '../services'
|
||||||
@ -85,39 +85,25 @@ export class NTQQUserApi {
|
|||||||
if (getBuildVersion() >= 26702) {
|
if (getBuildVersion() >= 26702) {
|
||||||
return this.fetchUserDetailInfo(uid)
|
return this.fetchUserDetailInfo(uid)
|
||||||
}
|
}
|
||||||
// this.getUserInfo(uid)
|
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo']
|
||||||
let methodName = !isQQ998 ? NTQQApiMethod.USER_DETAIL_INFO : NTQQApiMethod.USER_DETAIL_INFO_WITH_BIZ_INFO
|
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged']
|
||||||
if (!withBizInfo) {
|
const [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||||
methodName = NTQQApiMethod.USER_DETAIL_INFO
|
<EventService, EventListener>
|
||||||
|
(
|
||||||
|
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||||
|
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||||
|
2,
|
||||||
|
5000,
|
||||||
|
(profile: User) => {
|
||||||
|
if (profile.uid === uid) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
const fetchInfo = async () => {
|
return false
|
||||||
const result = await callNTQQApi<{ info: User }>({
|
|
||||||
methodName,
|
|
||||||
cbCmd: ReceiveCmdS.USER_DETAIL_INFO,
|
|
||||||
afterFirstCmd: false,
|
|
||||||
cmdCB: (payload) => {
|
|
||||||
const success = payload.info.uid == uid
|
|
||||||
// log("get user detail info", success, uid, payload)
|
|
||||||
return success
|
|
||||||
},
|
},
|
||||||
args: [
|
|
||||||
{
|
|
||||||
uid,
|
uid,
|
||||||
},
|
[0]
|
||||||
null,
|
)
|
||||||
],
|
return profile
|
||||||
})
|
|
||||||
const info = result.info
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
// 首次请求两次才能拿到的等级信息
|
|
||||||
if (!userInfoCache[uid] && getLevel) {
|
|
||||||
await fetchInfo()
|
|
||||||
await sleep(1000)
|
|
||||||
}
|
|
||||||
const userInfo = await fetchInfo()
|
|
||||||
userInfoCache[uid] = userInfo
|
|
||||||
return userInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return 'p_uin=o0xxx; p_skey=orXDssiGF8axxxxxxxxxxxxxx_; skey='
|
// return 'p_uin=o0xxx; p_skey=orXDssiGF8axxxxxxxxxxxxxx_; skey='
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { BrowserWindow } from 'electron'
|
import type { BrowserWindow } from 'electron'
|
||||||
import { NTQQApiClass, NTQQApiMethod } from './ntcall'
|
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 { CategoryFriend, ChatType, Group, GroupMember, GroupMemberRole, RawMessage } from './types'
|
||||||
import {
|
import {
|
||||||
deleteGroup,
|
deleteGroup,
|
||||||
@ -454,18 +454,7 @@ export async function startHook() {
|
|||||||
|
|
||||||
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
|
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
|
||||||
const message = msgRecord
|
const message = msgRecord
|
||||||
const peerUid = message.peerUid
|
|
||||||
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
|
|
||||||
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
|
|
||||||
dbUtil.addMsg(message).then()
|
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) => {
|
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
|
||||||
|
@ -462,6 +462,7 @@ export interface RawMessage {
|
|||||||
senderUin?: string // 发送者QQ号
|
senderUin?: string // 发送者QQ号
|
||||||
peerUid: string // 群号 或者 QQ uid
|
peerUid: string // 群号 或者 QQ uid
|
||||||
peerUin: string // 群号 或者 发送者QQ号
|
peerUin: string // 群号 或者 发送者QQ号
|
||||||
|
guildId: string
|
||||||
sendNickName: string
|
sendNickName: string
|
||||||
sendMemberName?: string // 发送者群名片
|
sendMemberName?: string // 发送者群名片
|
||||||
chatType: ChatType
|
chatType: ChatType
|
||||||
|
@ -37,7 +37,7 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
|
|||||||
let element = this.getElement(msg, cache.elementId)
|
let element = this.getElement(msg, cache.elementId)
|
||||||
log('找到了文件 element', element)
|
log('找到了文件 element', element)
|
||||||
// 构建下载函数
|
// 构建下载函数
|
||||||
await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, cache.elementId, '', '', true)
|
await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, cache.elementId, '', '')
|
||||||
// 等待文件下载完成
|
// 等待文件下载完成
|
||||||
msg = await dbUtil.getMsgByLongId(cache.msgId)
|
msg = await dbUtil.getMsgByLongId(cache.msgId)
|
||||||
log('下载完成后的msg', msg)
|
log('下载完成后的msg', msg)
|
||||||
|
@ -11,11 +11,7 @@ interface Payload {
|
|||||||
user_id?: number | string
|
user_id?: number | string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
abstract class ForwardSingleMsg extends BaseAction<Payload, null> {
|
||||||
message_id: number
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
|
||||||
protected async getTargetPeer(payload: Payload): Promise<Peer> {
|
protected async getTargetPeer(payload: Payload): Promise<Peer> {
|
||||||
if (payload.user_id) {
|
if (payload.user_id) {
|
||||||
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString())
|
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() }
|
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)
|
const msg = await dbUtil.getMsgByShortId(payload.message_id)
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
throw new Error(`无法找到消息${payload.message_id}`)
|
throw new Error(`无法找到消息${payload.message_id}`)
|
||||||
}
|
}
|
||||||
const peer = await this.getTargetPeer(payload)
|
const peer = await this.getTargetPeer(payload)
|
||||||
const sentMsg = await NTQQMsgApi.forwardMsg(
|
const ret = await NTQQMsgApi.forwardMsg(
|
||||||
{
|
{
|
||||||
chatType: msg.chatType,
|
chatType: msg.chatType,
|
||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
@ -41,8 +37,10 @@ abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
|||||||
peer,
|
peer,
|
||||||
[msg.msgId],
|
[msg.msgId],
|
||||||
)
|
)
|
||||||
const ob11MsgId = await dbUtil.addMsg(sentMsg)
|
if (ret.result !== 0) {
|
||||||
return { message_id: ob11MsgId! }
|
throw new Error(`转发消息失败 ${ret.errMsg}`)
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,13 +583,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
if (nodeMsgIds.length === 0) {
|
if (nodeMsgIds.length === 0) {
|
||||||
throw Error('转发消息失败,节点为空')
|
throw Error('转发消息失败,节点为空')
|
||||||
}
|
}
|
||||||
try {
|
const returnMsg = await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
|
||||||
log('开发转发', nodeMsgIds)
|
returnMsg.msgShortId = await dbUtil.addMsg(returnMsg)
|
||||||
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
|
return returnMsg
|
||||||
} catch (e) {
|
|
||||||
log('forward failed', e)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ export class OB11Constructor {
|
|||||||
// message_data["data"]["path"] = element.picElement.sourcePath
|
// message_data["data"]["path"] = element.picElement.sourcePath
|
||||||
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
||||||
|
|
||||||
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement, msg.chatType)
|
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement)
|
||||||
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
||||||
message_data['data']['file_size'] = element.picElement.fileSize
|
message_data['data']['file_size'] = element.picElement.fileSize
|
||||||
dbUtil
|
dbUtil
|
||||||
|
@ -1 +1 @@
|
|||||||
export const version = '3.28.3'
|
export const version = '3.28.4'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user