fix: All images are the first image in single msg

fix: remote rkey
This commit is contained in:
linyuchen 2024-05-11 14:52:59 +08:00
parent fdf96b479c
commit 9b3916307a
6 changed files with 129 additions and 127 deletions

View File

@ -46,8 +46,8 @@ let config = {
{ src: './manifest.json', dest: 'dist' },
{ src: './icon.jpg', dest: 'dist' },
{ src: './src/ntqqapi/native/crychic/crychic-win32-x64.node', dest: 'dist/main/' },
{ src: './src/ntqqapi/native/moehook/MoeHoo-win32-x64.node', dest: 'dist/main/' },
{ src: './src/ntqqapi/native/moehook/MoeHoo-linux-x64.node', dest: 'dist/main/' },
// { src: './src/ntqqapi/native/moehook/MoeHoo-win32-x64.node', dest: 'dist/main/' },
// { src: './src/ntqqapi/native/moehook/MoeHoo-linux-x64.node', dest: 'dist/main/' },
],
}),
],

View File

@ -1,10 +1,10 @@
{
"manifest_version": 4,
"type": "extension",
"name": "LLOneBot v3.24.2",
"name": "LLOneBot v3.24.3",
"slug": "LLOneBot",
"description": "使你的NTQQ支持OneBot11协议进行QQ机器人开发, 不支持商店在线更新",
"version": "3.24.2",
"version": "3.24.3",
"icon": "./icon.jpg",
"authors": [
{
@ -20,10 +20,14 @@
"name": "LLOneBot.zip"
}
},
"platform": ["win32", "linux", "darwin"],
"platform": [
"win32",
"linux",
"darwin"
],
"injects": {
"renderer": "./renderer/index.js",
"main": "./main/main.cjs",
"preload": "./preload/preload.cjs"
}
}
}

View File

@ -9,22 +9,14 @@ import {
ChatType,
ElementType,
IMAGE_HTTP_HOST,
IMAGE_HTTP_HOST_NT,
IMAGE_HTTP_HOST_NT, PicElement,
RawMessage,
} from '../types'
import path from 'path'
import fs from 'fs'
import { ReceiveCmdS } from '../hook'
import { log } from '@/common/utils'
import https from 'https'
import { sleep } from '@/common/utils'
import { hookApi } from '../native/moehook/hook'
let privateImageRKey = ''
let groupImageRKey = ''
let lastGetPrivateRKeyTime = 0
let lastGetGroupRKeyTime = 0
const rkeyExpireTime = 1000 * 60 * 30
import { rkeyManager } from '@/ntqqapi/api/rkey'
export class NTQQFileApi {
static async getFileType(filePath: string) {
@ -161,16 +153,12 @@ export class NTQQFileApi {
})
}
static async getImageUrl(msg: RawMessage) {
const isPrivateImage = msg.chatType !== ChatType.group
const msgElement = msg.elements.find((e) => !!e.picElement)
if (!msgElement) {
return ''
}
const url = msgElement.picElement.originImageUrl // 没有域名
const md5HexStr = msgElement.picElement.md5HexStr
const fileMd5 = msgElement.picElement.md5HexStr
const fileUuid = msgElement.picElement.fileUuid
static async getImageUrl(picElement: PicElement, chatType: ChatType) {
const isPrivateImage = chatType !== ChatType.group
const url = picElement.originImageUrl // 没有域名
const md5HexStr = picElement.md5HexStr
const fileMd5 = picElement.md5HexStr
const fileUuid = picElement.fileUuid
if (url) {
if (url.startsWith('/download')) {
// console.log('rkey', rkey);
@ -178,81 +166,9 @@ export class NTQQFileApi {
return IMAGE_HTTP_HOST_NT + url
}
if (!hookApi.isAvailable()) {
log('hookApi is not available')
return ''
}
const saveRKey = (rkey: string) => {
if (isPrivateImage) {
privateImageRKey = rkey
lastGetPrivateRKeyTime = Date.now()
} else {
groupImageRKey = rkey
lastGetGroupRKeyTime = Date.now()
}
}
const refreshRKey = async () => {
log('获取图片rkey...')
NTQQFileApi.downloadMedia(
msg.msgId,
msg.chatType,
msg.peerUid,
msgElement.elementId,
'',
msgElement.picElement.sourcePath,
false,
)
.then()
.catch(() => {})
await sleep(1000)
const _rkey = hookApi.getRKey()
if (_rkey) {
const imageUrl = IMAGE_HTTP_HOST_NT + url + _rkey
// 验证_rkey是否有效
try {
await new Promise((res, rej) => {
https
.get(imageUrl, (response) => {
if (response.statusCode !== 200) {
rej('图片rkey获取失败')
} else {
res(response)
}
})
.on('error', (e) => {
rej(e)
})
})
log('图片rkey获取成功', _rkey)
saveRKey(_rkey)
return _rkey
} catch (e) {
log('图片rkey有误', imageUrl)
}
}
}
const existsRKey = isPrivateImage ? privateImageRKey : groupImageRKey
const lastGetRKeyTime = isPrivateImage ? lastGetPrivateRKeyTime : lastGetGroupRKeyTime
if (Date.now() - lastGetRKeyTime > rkeyExpireTime) {
// rkey过期
const newRKey = await refreshRKey()
if (newRKey) {
return IMAGE_HTTP_HOST_NT + url + `${newRKey}`
} else {
log('图片rkey获取失败', url)
if (existsRKey) {
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`
}
return ''
}
}
// 使用未过期的rkey
if (existsRKey) {
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`
}
const rkeyData = await rkeyManager.getRkey();
const existsRKey = isPrivateImage ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`
} else {
// 老的图片url不需要rkey
return IMAGE_HTTP_HOST + url
@ -261,7 +177,7 @@ export class NTQQFileApi {
// 没有url需要自己拼接
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`
}
log('图片url获取失败', msg)
log('图片url获取失败', picElement)
return ''
}
}

59
src/ntqqapi/api/rkey.ts Normal file
View File

@ -0,0 +1,59 @@
//远端rkey获取
import { log } from '@/common/utils'
interface ServerRkeyData{
group_rkey: string;
private_rkey: string;
expired_time: number;
}
class RkeyManager {
serverUrl: string = '';
private rkeyData: ServerRkeyData = {
group_rkey: '',
private_rkey: '',
expired_time: 0
};
constructor(serverUrl: string) {
this.serverUrl = serverUrl;
}
async getRkey(){
if (this.isExpired()) {
try {
await this.refreshRkey();
} catch (e) {
log('获取rkey失败', e);
}
}
return this.rkeyData;
}
isExpired(): boolean {
const now = new Date().getTime() / 1000;
// console.log(`now: ${now}, expired_time: ${this.rkeyData.expired_time}`);
return now > this.rkeyData.expired_time;
}
async refreshRkey(): Promise<any> {
//刷新rkey
this.rkeyData = await this.fetchServerRkey();
}
async fetchServerRkey(){
return new Promise<ServerRkeyData>((resolve, reject) => {
fetch(this.serverUrl)
.then(response => {
if (!response.ok) {
return reject(response.statusText); // 请求失败,返回错误信息
}
return response.json(); // 解析 JSON 格式的响应体
})
.then(data => {
resolve(data);
})
.catch(error => {
reject(error);
});
});
}
}
export const rkeyManager = new RkeyManager('http://napcat-sign.wumiao.wang:2082/rkey');

View File

@ -87,13 +87,15 @@ export class OB11Constructor {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role)
resMsg.sender.nickname = member.nick
}
} else if (msg.chatType == ChatType.friend) {
}
else if (msg.chatType == ChatType.friend) {
resMsg.sub_type = 'friend'
const friend = await getFriend(msg.senderUin)
if (friend) {
resMsg.sender.nickname = friend.nick
}
} else if (msg.chatType == ChatType.temp) {
}
else if (msg.chatType == ChatType.temp) {
resMsg.sub_type = 'group'
const tempGroupCode = tempGroupCodeMap[msg.peerUin]
if (tempGroupCode) {
@ -111,7 +113,8 @@ export class OB11Constructor {
if (element.textElement.atType == AtType.atAll) {
// message_data["data"]["mention"] = "all"
message_data['data']['qq'] = 'all'
} else {
}
else {
let atUid = element.textElement.atNtUid
let atQQ = element.textElement.atUid
if (!atQQ || atQQ === '0') {
@ -125,14 +128,16 @@ export class OB11Constructor {
message_data['data']['qq'] = atQQ
}
}
} else if (element.textElement) {
}
else if (element.textElement) {
message_data['type'] = 'text'
let text = element.textElement.content
if (!text.trim()) {
continue
}
message_data['data']['text'] = text
} else if (element.replyElement) {
}
else if (element.replyElement) {
message_data['type'] = 'reply'
// log("收到回复消息", element.replyElement.replayMsgSeq)
try {
@ -140,20 +145,22 @@ export class OB11Constructor {
// log("找到回复消息", replyMsg.msgShortId, replyMsg.msgId)
if (replyMsg) {
message_data['data']['id'] = replyMsg.msgShortId.toString()
} else {
}
else {
continue
}
} catch (e) {
log('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq)
}
} else if (element.picElement) {
}
else if (element.picElement) {
message_data['type'] = 'image'
// message_data["data"]["file"] = element.picElement.sourcePath
message_data['data']['file'] = element.picElement.fileName
// message_data["data"]["path"] = element.picElement.sourcePath
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
message_data['data']['url'] = await NTQQFileApi.getImageUrl(msg)
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement, msg.chatType);
// message_data["data"]["file_id"] = element.picElement.fileUuid
message_data['data']['file_size'] = element.picElement.fileSize
dbUtil
@ -175,7 +182,8 @@ export class OB11Constructor {
})
.then()
// 不在自动下载图片
} else if (element.videoElement || element.fileElement) {
}
else if (element.videoElement || element.fileElement) {
const videoOrFileElement = element.videoElement || element.fileElement
const ob11MessageDataType = element.videoElement ? OB11MessageDataType.video : OB11MessageDataType.file
message_data['type'] = ob11MessageDataType
@ -204,7 +212,8 @@ export class OB11Constructor {
})
.then()
// 怎么拿到url呢
} else if (element.pttElement) {
}
else if (element.pttElement) {
message_data['type'] = OB11MessageDataType.voice
message_data['data']['file'] = element.pttElement.fileName
message_data['data']['path'] = element.pttElement.filePath
@ -224,22 +233,27 @@ export class OB11Constructor {
// }).catch(err => {
// console.log("语音转文字失败", err);
// })
} else if (element.arkElement) {
}
else if (element.arkElement) {
message_data['type'] = OB11MessageDataType.json
message_data['data']['data'] = element.arkElement.bytesData
} else if (element.faceElement) {
}
else if (element.faceElement) {
const faceId = element.faceElement.faceIndex
if (faceId === FaceIndex.dice) {
message_data['type'] = OB11MessageDataType.dice
message_data['data']['result'] = element.faceElement.resultId
} else if (faceId === FaceIndex.RPS) {
}
else if (faceId === FaceIndex.RPS) {
message_data['type'] = OB11MessageDataType.RPS
message_data['data']['result'] = element.faceElement.resultId
} else {
}
else {
message_data['type'] = OB11MessageDataType.face
message_data['data']['id'] = element.faceElement.faceIndex.toString()
}
} else if (element.marketFaceElement) {
}
else if (element.marketFaceElement) {
message_data['type'] = OB11MessageDataType.mface
message_data['data']['summary'] = element.marketFaceElement.faceName
const md5 = element.marketFaceElement.emojiId
@ -253,10 +267,12 @@ export class OB11Constructor {
message_data['data']['emoji_package_id'] = String(element.marketFaceElement.emojiPackageId)
message_data['data']['key'] = element.marketFaceElement.key
mFaceCache.set(md5, element.marketFaceElement.faceName)
} else if (element.markdownElement) {
}
else if (element.markdownElement) {
message_data['type'] = OB11MessageDataType.markdown
message_data['data']['data'] = element.markdownElement.content
} else if (element.multiForwardMsgElement) {
}
else if (element.multiForwardMsgElement) {
message_data['type'] = OB11MessageDataType.forward
message_data['data']['id'] = msg.msgId
}
@ -264,7 +280,8 @@ export class OB11Constructor {
const cqCode = encodeCQCode(message_data)
if (messagePostFormat === 'string') {
;(resMsg.message as string) += cqCode
} else (resMsg.message as OB11MessageData[]).push(message_data)
}
else (resMsg.message as OB11MessageData[]).push(message_data)
resMsg.raw_message += cqCode
}
@ -313,7 +330,8 @@ export class OB11Constructor {
// log("构造群增加事件", event)
return event
}
} else if (groupElement.type === TipGroupElementType.ban) {
}
else if (groupElement.type === TipGroupElementType.ban) {
log('收到群群员禁言提示', groupElement)
const memberUid = groupElement.shutUp.member.uid
const adminUid = groupElement.shutUp.admin.uid
@ -324,7 +342,8 @@ export class OB11Constructor {
memberUin =
(await getGroupMember(msg.peerUid, memberUid))?.uin ||
(await NTQQUserApi.getUserDetailInfo(memberUid))?.uin
} else {
}
else {
memberUin = '0' // 0表示全员禁言
if (duration > 0) {
duration = -1
@ -341,7 +360,8 @@ export class OB11Constructor {
sub_type,
)
}
} else if (groupElement.type == TipGroupElementType.kicked) {
}
else if (groupElement.type == TipGroupElementType.kicked) {
log(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement)
deleteGroup(msg.peerUid)
NTQQGroupApi.quitGroup(msg.peerUid).then()
@ -361,7 +381,8 @@ export class OB11Constructor {
return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfInfo.uin), 0, 'leave')
}
}
} else if (element.fileElement) {
}
else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), {
id: element.fileElement.fileUuid,
name: element.fileElement.fileName,
@ -426,7 +447,8 @@ export class OB11Constructor {
return new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(invitee), parseInt(inviter), 'invite')
}
}
} else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
}
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
/*
{
@ -495,7 +517,8 @@ export class OB11Constructor {
parseInt(operator.uin),
msg.msgShortId,
)
} else {
}
else {
return new OB11FriendRecallNoticeEvent(parseInt(msg.senderUin), msg.msgShortId)
}
}

View File

@ -1 +1 @@
export const version = '3.24.2'
export const version = '3.24.3'