mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
commit
26fc0c68b2
@ -4,7 +4,7 @@
|
||||
"name": "LLOneBot",
|
||||
"slug": "LLOneBot",
|
||||
"description": "实现 OneBot 11 协议,用以 QQ 机器人开发",
|
||||
"version": "3.29.0",
|
||||
"version": "3.29.1",
|
||||
"icon": "./icon.webp",
|
||||
"authors": [
|
||||
{
|
||||
|
@ -41,11 +41,10 @@ export interface LLOneBotError {
|
||||
|
||||
export interface FileCache {
|
||||
fileName: string
|
||||
filePath: string
|
||||
fileSize: string
|
||||
fileUuid?: string
|
||||
url?: string
|
||||
msgId?: string
|
||||
msgId: string
|
||||
peerUid: string
|
||||
chatType: number
|
||||
elementId: string
|
||||
downloadFunc?: () => Promise<void>
|
||||
elementType: number
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ export class NTEventWrapper {
|
||||
if (typeof target[prop] === 'undefined') {
|
||||
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
||||
return (...args: any[]) => {
|
||||
current.DispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then()
|
||||
current.dispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then()
|
||||
}
|
||||
}
|
||||
// 如果方法存在,正常返回
|
||||
@ -48,7 +48,7 @@ export class NTEventWrapper {
|
||||
this.WrapperSession = WrapperSession
|
||||
}
|
||||
|
||||
CreatEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
|
||||
createEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
|
||||
const eventNameArr = eventName.split('/')
|
||||
type eventType = {
|
||||
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> }
|
||||
@ -69,16 +69,14 @@ export class NTEventWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
createEventFunction = this.CreatEventFunction
|
||||
|
||||
CreatListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
|
||||
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
|
||||
const ListenerType = this.ListenerMap![listenerMainName]
|
||||
let Listener = this.ListenerManger.get(listenerMainName + uniqueCode)
|
||||
if (!Listener && ListenerType) {
|
||||
Listener = new ListenerType(this.createProxyDispatch(listenerMainName))
|
||||
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1]
|
||||
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener'
|
||||
const addfunc = this.CreatEventFunction<(listener: T) => number>(Service)
|
||||
const addfunc = this.createEventFunction<(listener: T) => number>(Service)
|
||||
addfunc!(Listener as T)
|
||||
//console.log(addfunc!(Listener as T))
|
||||
this.ListenerManger.set(listenerMainName + uniqueCode, Listener)
|
||||
@ -87,7 +85,7 @@ export class NTEventWrapper {
|
||||
}
|
||||
|
||||
//统一回调清理事件
|
||||
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args)
|
||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
||||
//console.log(task.func, uuid, task.createtime, task.timeout)
|
||||
@ -103,7 +101,7 @@ export class NTEventWrapper {
|
||||
|
||||
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
||||
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||
const EventFunc = this.CreatEventFunction<EventType>(EventName)
|
||||
const EventFunc = this.createEventFunction<EventType>(EventName)
|
||||
let complete = false
|
||||
const Timeouter = setTimeout(() => {
|
||||
if (!complete) {
|
||||
@ -152,7 +150,7 @@ export class NTEventWrapper {
|
||||
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map())
|
||||
}
|
||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak)
|
||||
this.CreatListenerFunction(ListenerMainName)
|
||||
this.createListenerFunction(ListenerMainName)
|
||||
})
|
||||
}
|
||||
|
||||
@ -198,8 +196,8 @@ export class NTEventWrapper {
|
||||
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map())
|
||||
}
|
||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak)
|
||||
this.CreatListenerFunction(ListenerMainName)
|
||||
const EventFunc = this.CreatEventFunction<EventType>(EventName)
|
||||
this.createListenerFunction(ListenerMainName)
|
||||
const EventFunc = this.createEventFunction<EventType>(EventName)
|
||||
retEvent = await EventFunc!(...(args as any[]))
|
||||
})
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import SQLite from '@minatojs/driver-sqlite'
|
||||
import fsPromise from 'node:fs/promises'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { FileCache } from '../types'
|
||||
|
||||
interface SQLiteTables extends Tables {
|
||||
message: {
|
||||
@ -15,6 +16,7 @@ interface SQLiteTables extends Tables {
|
||||
chatType: number
|
||||
peerUid: string
|
||||
}
|
||||
file: FileCache
|
||||
}
|
||||
|
||||
interface MsgIdAndPeerByShortId {
|
||||
@ -50,6 +52,17 @@ class MessageUniqueWrapper {
|
||||
}, {
|
||||
primary: 'shortId'
|
||||
})
|
||||
database.extend('file', {
|
||||
fileName: 'string',
|
||||
fileSize: 'string',
|
||||
msgId: 'string(24)',
|
||||
peerUid: 'string(24)',
|
||||
chatType: 'unsigned',
|
||||
elementId: 'string(24)',
|
||||
elementType: 'unsigned',
|
||||
}, {
|
||||
primary: 'fileName'
|
||||
})
|
||||
this.db = database
|
||||
}
|
||||
|
||||
@ -128,6 +141,14 @@ class MessageUniqueWrapper {
|
||||
this.msgIdMap.resize(maxSize)
|
||||
this.msgDataMap.resize(maxSize)
|
||||
}
|
||||
|
||||
addFileCache(data: FileCache) {
|
||||
return this.db?.upsert('file', [data], 'fileName')
|
||||
}
|
||||
|
||||
getFileCache(fileName: string) {
|
||||
return this.db?.get('file', { fileName })
|
||||
}
|
||||
}
|
||||
|
||||
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper()
|
@ -179,7 +179,6 @@ export class NTQQFileApi {
|
||||
const url: string = element.originImageUrl! // 没有域名
|
||||
const md5HexStr = element.md5HexStr
|
||||
const fileMd5 = element.md5HexStr
|
||||
const fileUuid = element.fileUuid
|
||||
|
||||
if (url) {
|
||||
const UrlParse = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接
|
||||
|
@ -3,9 +3,9 @@ import fsPromise from 'node:fs/promises'
|
||||
import { getConfigUtil } from '@/common/config'
|
||||
import { NTQQFileApi, NTQQGroupApi, NTQQUserApi, NTQQFriendApi, NTQQMsgApi } from '@/ntqqapi/api'
|
||||
import { ActionName } from '../types'
|
||||
import { RawMessage } from '@/ntqqapi/types'
|
||||
import { UUIDConverter } from '@/common/utils/helper'
|
||||
import { Peer, ChatType, ElementType } from '@/ntqqapi/types'
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique'
|
||||
|
||||
export interface GetFilePayload {
|
||||
file: string // 文件名或者fileUuid
|
||||
@ -51,10 +51,10 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
|
||||
throw new Error('chattype not support')
|
||||
}
|
||||
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId])
|
||||
if (msgList.msgList.length == 0) {
|
||||
if (msgList.msgList.length === 0) {
|
||||
throw new Error('msg not found')
|
||||
}
|
||||
const msg = msgList.msgList[0];
|
||||
const msg = msgList.msgList[0]
|
||||
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT)
|
||||
if (!findEle) {
|
||||
throw new Error('element not found')
|
||||
@ -68,7 +68,7 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
|
||||
file_size: fileSize,
|
||||
file_name: fileName,
|
||||
}
|
||||
if (enableLocalFile2Url) {
|
||||
if (enableLocalFile2Url && downloadPath) {
|
||||
try {
|
||||
res.base64 = await fsPromise.readFile(downloadPath, 'base64')
|
||||
} catch (e) {
|
||||
@ -82,33 +82,42 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
|
||||
|
||||
}
|
||||
|
||||
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems
|
||||
if (NTSearchNameResult.length !== 0) {
|
||||
const MsgId = NTSearchNameResult[0].msgId
|
||||
let peer: Peer | undefined = undefined
|
||||
if (NTSearchNameResult[0].chatType == ChatType.group) {
|
||||
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode }
|
||||
}
|
||||
if (!peer) {
|
||||
throw new Error('chattype not support')
|
||||
}
|
||||
const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList
|
||||
if (!msgList || msgList.length == 0) {
|
||||
throw new Error('msg not found')
|
||||
}
|
||||
const msg = msgList[0]
|
||||
const file = msg.elements.filter(e => e.elementType == NTSearchNameResult[0].elemType)
|
||||
if (file.length == 0) {
|
||||
throw new Error('file not found')
|
||||
}
|
||||
const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, file[0].elementId, '', '')
|
||||
const fileCache = await MessageUnique.getFileCache(String(payload.file))
|
||||
if (fileCache?.length) {
|
||||
const downloadPath = await NTQQFileApi.downloadMedia(
|
||||
fileCache[0].msgId,
|
||||
fileCache[0].chatType,
|
||||
fileCache[0].peerUid,
|
||||
fileCache[0].elementId,
|
||||
'',
|
||||
''
|
||||
)
|
||||
const res: GetFileResponse = {
|
||||
file: downloadPath,
|
||||
url: downloadPath,
|
||||
file_size: NTSearchNameResult[0].fileSize.toString(),
|
||||
file_name: NTSearchNameResult[0].fileName,
|
||||
file_size: fileCache[0].fileSize,
|
||||
file_name: fileCache[0].fileName,
|
||||
}
|
||||
if (enableLocalFile2Url) {
|
||||
const peer: Peer = {
|
||||
chatType: fileCache[0].chatType,
|
||||
peerUid: fileCache[0].peerUid,
|
||||
guildId: ''
|
||||
}
|
||||
if (fileCache[0].elementType === ElementType.PIC) {
|
||||
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [fileCache[0].msgId])
|
||||
if (msgList.msgList.length === 0) {
|
||||
throw new Error('msg not found')
|
||||
}
|
||||
const msg = msgList.msgList[0]
|
||||
const findEle = msg.elements.find(e => e.elementId === fileCache[0].elementId)
|
||||
if (!findEle) {
|
||||
throw new Error('element not found')
|
||||
}
|
||||
res.url = await NTQQFileApi.getImageUrl(findEle.picElement)
|
||||
} else if (fileCache[0].elementType === ElementType.VIDEO) {
|
||||
res.url = await NTQQFileApi.getVideoUrl(peer, fileCache[0].msgId, fileCache[0].elementId)
|
||||
}
|
||||
if (enableLocalFile2Url && downloadPath && res.file === res.url) {
|
||||
try {
|
||||
res.base64 = await fsPromise.readFile(downloadPath, 'base64')
|
||||
} catch (e) {
|
||||
|
@ -3,4 +3,11 @@ import { ActionName } from '../types'
|
||||
|
||||
export default class GetImage extends GetFileBase {
|
||||
actionName = ActionName.GetImage
|
||||
|
||||
protected async _handle(payload: { file: string }) {
|
||||
if (!payload.file) {
|
||||
throw new Error('参数 file 不能为空')
|
||||
}
|
||||
return super._handle(payload)
|
||||
}
|
||||
}
|
||||
|
@ -184,21 +184,30 @@ export class OB11Constructor {
|
||||
}
|
||||
else if (element.picElement) {
|
||||
message_data['type'] = OB11MessageDataType.image
|
||||
let fileName = element.picElement.fileName
|
||||
const isGif = element.picElement.picType === PicType.gif
|
||||
const { picElement } = element
|
||||
/*let fileName = picElement.fileName
|
||||
const isGif = picElement.picType === PicType.gif
|
||||
if (isGif && !fileName.endsWith('.gif')) {
|
||||
fileName += '.gif'
|
||||
}
|
||||
message_data['data']['file'] = fileName
|
||||
message_data['data']['subType'] = element.picElement.picSubType
|
||||
}*/
|
||||
message_data['data']['file'] = picElement.fileName
|
||||
message_data['data']['subType'] = picElement.picSubType
|
||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId)
|
||||
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement)
|
||||
message_data['data']['file_size'] = element.picElement.fileSize
|
||||
message_data['data']['url'] = await NTQQFileApi.getImageUrl(picElement)
|
||||
message_data['data']['file_size'] = picElement.fileSize
|
||||
MessageUnique.addFileCache({
|
||||
peerUid: msg.peerUid,
|
||||
msgId: msg.msgId,
|
||||
chatType: msg.chatType,
|
||||
elementId: element.elementId,
|
||||
elementType: element.elementType,
|
||||
fileName: picElement.fileName,
|
||||
fileSize: String(picElement.fileSize || '0'),
|
||||
})
|
||||
}
|
||||
else if (element.videoElement || element.fileElement) {
|
||||
const videoOrFileElement = element.videoElement || element.fileElement
|
||||
const ob11MessageDataType = element.videoElement ? OB11MessageDataType.video : OB11MessageDataType.file
|
||||
message_data['type'] = ob11MessageDataType
|
||||
message_data['type'] = element.videoElement ? OB11MessageDataType.video : OB11MessageDataType.file
|
||||
message_data['data']['file'] = videoOrFileElement.fileName
|
||||
message_data['data']['path'] = videoOrFileElement.filePath
|
||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId)
|
||||
@ -210,40 +219,32 @@ export class OB11Constructor {
|
||||
}, msg.msgId, element.elementId,
|
||||
)
|
||||
}
|
||||
NTQQFileApi.addFileCache(
|
||||
{
|
||||
peerUid: msg.peerUid,
|
||||
chatType: msg.chatType,
|
||||
guildId: '',
|
||||
},
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
videoOrFileElement.fileSize || '0',
|
||||
videoOrFileElement.fileName,
|
||||
)
|
||||
MessageUnique.addFileCache({
|
||||
peerUid: msg.peerUid,
|
||||
msgId: msg.msgId,
|
||||
chatType: msg.chatType,
|
||||
elementId: element.elementId,
|
||||
elementType: element.elementType,
|
||||
fileName: videoOrFileElement.fileName,
|
||||
fileSize: String(videoOrFileElement.fileSize || '0')
|
||||
})
|
||||
}
|
||||
else if (element.pttElement) {
|
||||
message_data['type'] = OB11MessageDataType.voice
|
||||
message_data['data']['file'] = element.pttElement.fileName
|
||||
message_data['data']['path'] = element.pttElement.filePath
|
||||
const { pttElement } = element
|
||||
message_data['data']['file'] = pttElement.fileName
|
||||
message_data['data']['path'] = pttElement.filePath
|
||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId)
|
||||
message_data['data']['file_size'] = element.pttElement.fileSize
|
||||
NTQQFileApi.addFileCache({
|
||||
message_data['data']['file_size'] = pttElement.fileSize
|
||||
MessageUnique.addFileCache({
|
||||
peerUid: msg.peerUid,
|
||||
msgId: msg.msgId,
|
||||
chatType: msg.chatType,
|
||||
guildId: '',
|
||||
},
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
element.pttElement.fileSize || '0',
|
||||
element.pttElement.fileUuid || '',
|
||||
)
|
||||
elementId: element.elementId,
|
||||
elementType: element.elementType,
|
||||
fileName: pttElement.fileName,
|
||||
fileSize: String(pttElement.fileSize || '0')
|
||||
})
|
||||
}
|
||||
else if (element.arkElement) {
|
||||
message_data['type'] = OB11MessageDataType.json
|
||||
|
@ -1 +1 @@
|
||||
export const version = '3.29.0'
|
||||
export const version = '3.29.1'
|
||||
|
Loading…
x
Reference in New Issue
Block a user