mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
commit
5c68d4de84
@ -4,7 +4,7 @@
|
|||||||
"name": "LLOneBot",
|
"name": "LLOneBot",
|
||||||
"slug": "LLOneBot",
|
"slug": "LLOneBot",
|
||||||
"description": "实现 OneBot 11 协议,用于 QQ 机器人开发",
|
"description": "实现 OneBot 11 协议,用于 QQ 机器人开发",
|
||||||
"version": "3.31.9",
|
"version": "3.31.10",
|
||||||
"icon": "./icon.webp",
|
"icon": "./icon.webp",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@ -1,234 +0,0 @@
|
|||||||
import { NodeIQQNTWrapperSession } from '@/ntqqapi/wrapper'
|
|
||||||
import { randomUUID } from 'node:crypto'
|
|
||||||
|
|
||||||
interface Internal_MapKey {
|
|
||||||
timeout: number
|
|
||||||
createtime: number
|
|
||||||
func: (...arg: any[]) => unknown
|
|
||||||
checker?: (...args: any[]) => boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ListenerClassBase {
|
|
||||||
[key: string]: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListenerIBase {
|
|
||||||
new(listener: unknown): ListenerClassBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/EventTask.ts#L20
|
|
||||||
export class NTEventWrapper {
|
|
||||||
private ListenerMap: { [key: string]: ListenerIBase } | undefined//ListenerName-Unique -> Listener构造函数
|
|
||||||
private WrapperSession: NodeIQQNTWrapperSession | undefined//WrapperSession
|
|
||||||
private ListenerManger: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>() //ListenerName-Unique -> Listener实例
|
|
||||||
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>()//tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
|
|
||||||
public initialised = false
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
createProxyDispatch(ListenerMainName: string) {
|
|
||||||
const current = this
|
|
||||||
return new Proxy({}, {
|
|
||||||
get(target: any, prop: string, receiver: unknown) {
|
|
||||||
// console.log('get', prop, typeof target[prop])
|
|
||||||
if (typeof target[prop] === 'undefined') {
|
|
||||||
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
|
||||||
return (...args: unknown[]) => {
|
|
||||||
current.dispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果方法存在,正常返回
|
|
||||||
return Reflect.get(target, prop, receiver)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
init({ ListenerMap, WrapperSession }: { ListenerMap: { [key: string]: typeof ListenerClassBase }, WrapperSession: NodeIQQNTWrapperSession }) {
|
|
||||||
this.ListenerMap = ListenerMap
|
|
||||||
this.WrapperSession = WrapperSession
|
|
||||||
this.initialised = true
|
|
||||||
}
|
|
||||||
|
|
||||||
createEventFunction<T extends (...args: any) => unknown>(eventName: string): T | undefined {
|
|
||||||
const eventNameArr = eventName.split('/')
|
|
||||||
type eventType = {
|
|
||||||
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> }
|
|
||||||
}
|
|
||||||
if (eventNameArr.length > 1) {
|
|
||||||
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '')
|
|
||||||
const eventName = eventNameArr[1]
|
|
||||||
//getNodeIKernelGroupListener,GroupService
|
|
||||||
//console.log('2', eventName)
|
|
||||||
const services = (this.WrapperSession as unknown as eventType)[serviceName]()
|
|
||||||
let event = services[eventName]
|
|
||||||
//重新绑定this
|
|
||||||
event = event.bind(services)
|
|
||||||
if (event) {
|
|
||||||
return event as T
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.createEventFunction<(listener: T) => number>(Service)
|
|
||||||
addfunc!(Listener as T)
|
|
||||||
//console.log(addfunc!(Listener as T))
|
|
||||||
this.ListenerManger.set(listenerMainName + uniqueCode, Listener)
|
|
||||||
}
|
|
||||||
return Listener as T
|
|
||||||
}
|
|
||||||
|
|
||||||
//统一回调清理事件
|
|
||||||
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: unknown[]) {
|
|
||||||
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args)
|
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
|
||||||
//console.log(task.func, uuid, task.createtime, task.timeout)
|
|
||||||
if (task.createtime + task.timeout < Date.now()) {
|
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (task.checker && task.checker(...args)) {
|
|
||||||
task.func(...args)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any>>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
|
||||||
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
|
||||||
const EventFunc = this.createEventFunction<EventType>(EventName)
|
|
||||||
let complete = false
|
|
||||||
const Timeouter = setTimeout(() => {
|
|
||||||
if (!complete) {
|
|
||||||
reject(new Error('NTEvent EventName:' + EventName + ' timeout'))
|
|
||||||
}
|
|
||||||
}, timeout)
|
|
||||||
const retData = await EventFunc!(...args)
|
|
||||||
complete = true
|
|
||||||
resolve(retData)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async RegisterListen<ListenerType extends (...args: any[]) => void>(ListenerName = '', waitTimes = 1, timeout = 5000, checker: (...args: Parameters<ListenerType>) => boolean) {
|
|
||||||
return new Promise<Parameters<ListenerType>>((resolve, reject) => {
|
|
||||||
const ListenerNameList = ListenerName.split('/')
|
|
||||||
const ListenerMainName = ListenerNameList[0]
|
|
||||||
const ListenerSubName = ListenerNameList[1]
|
|
||||||
const id = randomUUID()
|
|
||||||
let complete = 0
|
|
||||||
let retData: Parameters<ListenerType> | undefined = undefined
|
|
||||||
const databack = () => {
|
|
||||||
if (complete == 0) {
|
|
||||||
reject(new Error(' ListenerName:' + ListenerName + ' timeout'))
|
|
||||||
} else {
|
|
||||||
resolve(retData!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const Timeouter = setTimeout(databack, timeout)
|
|
||||||
const eventCallbak = {
|
|
||||||
timeout: timeout,
|
|
||||||
createtime: Date.now(),
|
|
||||||
checker: checker,
|
|
||||||
func: (...args: Parameters<ListenerType>) => {
|
|
||||||
complete++
|
|
||||||
retData = args
|
|
||||||
if (complete >= waitTimes) {
|
|
||||||
clearTimeout(Timeouter)
|
|
||||||
databack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.EventTask.get(ListenerMainName)) {
|
|
||||||
this.EventTask.set(ListenerMainName, new Map())
|
|
||||||
}
|
|
||||||
if (!(this.EventTask.get(ListenerMainName)?.get(ListenerSubName))) {
|
|
||||||
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map())
|
|
||||||
}
|
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak)
|
|
||||||
this.createListenerFunction(ListenerMainName)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>
|
|
||||||
(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, checker: (...args: Parameters<ListenerType>) => boolean, ...args: Parameters<EventType>) {
|
|
||||||
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
|
||||||
const id = randomUUID()
|
|
||||||
let complete = 0
|
|
||||||
let retData: Parameters<ListenerType> | undefined = undefined
|
|
||||||
let retEvent = {}
|
|
||||||
const databack = () => {
|
|
||||||
if (complete == 0) {
|
|
||||||
reject(new Error('Timeout: NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' EventRet:\n' + JSON.stringify(retEvent, null, 4) + '\n'))
|
|
||||||
} else {
|
|
||||||
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListenerNameList = ListenerName.split('/')
|
|
||||||
const ListenerMainName = ListenerNameList[0]
|
|
||||||
const ListenerSubName = ListenerNameList[1]
|
|
||||||
|
|
||||||
const Timeouter = setTimeout(databack, timeout)
|
|
||||||
|
|
||||||
const eventCallbak = {
|
|
||||||
timeout: timeout,
|
|
||||||
createtime: Date.now(),
|
|
||||||
checker: checker,
|
|
||||||
func: (...args: unknown[]) => {
|
|
||||||
complete++
|
|
||||||
//console.log('func', ...args)
|
|
||||||
retData = args as Parameters<ListenerType>
|
|
||||||
if (complete >= waitTimes) {
|
|
||||||
clearTimeout(Timeouter)
|
|
||||||
databack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.EventTask.get(ListenerMainName)) {
|
|
||||||
this.EventTask.set(ListenerMainName, new Map())
|
|
||||||
}
|
|
||||||
if (!(this.EventTask.get(ListenerMainName)?.get(ListenerSubName))) {
|
|
||||||
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map())
|
|
||||||
}
|
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak)
|
|
||||||
this.createListenerFunction(ListenerMainName)
|
|
||||||
const EventFunc = this.createEventFunction<EventType>(EventName)
|
|
||||||
retEvent = await EventFunc!(...args)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const NTEventDispatch = new NTEventWrapper()
|
|
||||||
|
|
||||||
// 示例代码 快速创建事件
|
|
||||||
// let NTEvent = new NTEventWrapper()
|
|
||||||
// let TestEvent = NTEvent.CreatEventFunction<(force: boolean) => Promise<Number>>('NodeIKernelProfileLikeService/GetTest')
|
|
||||||
// if (TestEvent) {
|
|
||||||
// TestEvent(true)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 示例代码 快速创建监听Listener类
|
|
||||||
// let NTEvent = new NTEventWrapper()
|
|
||||||
// NTEvent.CreatListenerFunction<NodeIKernelMsgListener>('NodeIKernelMsgListener', 'core')
|
|
||||||
|
|
||||||
|
|
||||||
// 调用接口
|
|
||||||
//let NTEvent = new NTEventWrapper()
|
|
||||||
//let ret = await NTEvent.CallNormalEvent<(force: boolean) => Promise<Number>, (data1: string, data2: number) => void>('NodeIKernelProfileLikeService/GetTest', 'NodeIKernelMsgListener/onAddSendMsg', 1, 3000, true)
|
|
||||||
|
|
||||||
// 注册监听 解除监听
|
|
||||||
// NTEventDispatch.RigisterListener('NodeIKernelMsgListener/onAddSendMsg','core',cb)
|
|
||||||
// NTEventDispatch.UnRigisterListener('NodeIKernelMsgListener/onAddSendMsg','core')
|
|
||||||
|
|
||||||
// let GetTest = NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode)
|
|
||||||
// GetTest('test')
|
|
||||||
|
|
||||||
// always模式
|
|
||||||
// NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode,(...args:any[])=>{ console.log(args) })
|
|
@ -10,154 +10,154 @@ import { DATA_DIR } from '../globalVars'
|
|||||||
import { FileCacheV2 } from '../types'
|
import { FileCacheV2 } from '../types'
|
||||||
|
|
||||||
interface SQLiteTables extends Tables {
|
interface SQLiteTables extends Tables {
|
||||||
message: {
|
message: {
|
||||||
shortId: number
|
shortId: number
|
||||||
msgId: string
|
msgId: string
|
||||||
chatType: number
|
chatType: number
|
||||||
peerUid: string
|
peerUid: string
|
||||||
}
|
}
|
||||||
file_v2: FileCacheV2
|
file_v2: FileCacheV2
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MsgIdAndPeerByShortId {
|
interface MsgIdAndPeerByShortId {
|
||||||
MsgId: string
|
MsgId: string
|
||||||
Peer: Peer
|
Peer: Peer
|
||||||
}
|
}
|
||||||
|
|
||||||
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/MessageUnique.ts#L84
|
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/MessageUnique.ts#L84
|
||||||
class MessageUniqueWrapper {
|
class MessageUniqueWrapper {
|
||||||
private msgDataMap: LimitedHashTable<string, number>
|
private msgDataMap: LimitedHashTable<string, number>
|
||||||
private msgIdMap: LimitedHashTable<string, number>
|
private msgIdMap: LimitedHashTable<string, number>
|
||||||
private db: Database<SQLiteTables> | undefined
|
private db: Database<SQLiteTables> | undefined
|
||||||
|
|
||||||
constructor(maxMap: number = 1000) {
|
constructor(maxMap: number = 1000) {
|
||||||
this.msgIdMap = new LimitedHashTable<string, number>(maxMap)
|
this.msgIdMap = new LimitedHashTable<string, number>(maxMap)
|
||||||
this.msgDataMap = new LimitedHashTable<string, number>(maxMap)
|
this.msgDataMap = new LimitedHashTable<string, number>(maxMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(uin: string) {
|
||||||
|
const dbDir = path.join(DATA_DIR, 'database')
|
||||||
|
if (!fs.existsSync(dbDir)) {
|
||||||
|
await fsPromise.mkdir(dbDir)
|
||||||
}
|
}
|
||||||
|
const database = new Database<SQLiteTables>()
|
||||||
|
await database.connect(SQLite, {
|
||||||
|
path: path.join(dbDir, `${uin}.db`)
|
||||||
|
})
|
||||||
|
database.extend('message', {
|
||||||
|
shortId: 'integer(10)',
|
||||||
|
chatType: 'unsigned',
|
||||||
|
msgId: 'string(24)',
|
||||||
|
peerUid: 'string(24)'
|
||||||
|
}, {
|
||||||
|
primary: 'shortId'
|
||||||
|
})
|
||||||
|
database.extend('file_v2', {
|
||||||
|
fileName: 'string',
|
||||||
|
fileSize: 'string',
|
||||||
|
fileUuid: 'string(128)',
|
||||||
|
msgId: 'string(24)',
|
||||||
|
msgTime: 'unsigned(10)',
|
||||||
|
peerUid: 'string(24)',
|
||||||
|
chatType: 'unsigned',
|
||||||
|
elementId: 'string(24)',
|
||||||
|
elementType: 'unsigned',
|
||||||
|
}, {
|
||||||
|
primary: 'fileUuid',
|
||||||
|
indexes: ['fileName']
|
||||||
|
})
|
||||||
|
this.db = database
|
||||||
|
}
|
||||||
|
|
||||||
async init(uin: string) {
|
async getRecentMsgIds(Peer: Peer, size: number): Promise<string[]> {
|
||||||
const dbDir = path.join(DATA_DIR, 'database')
|
const heads = this.msgIdMap.getHeads(size)
|
||||||
if (!fs.existsSync(dbDir)) {
|
if (!heads) {
|
||||||
await fsPromise.mkdir(dbDir)
|
return []
|
||||||
|
}
|
||||||
|
const data: (MsgIdAndPeerByShortId | undefined)[] = []
|
||||||
|
for (const t of heads) {
|
||||||
|
data.push(await MessageUnique.getMsgIdAndPeerByShortId(t.value))
|
||||||
|
}
|
||||||
|
const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid)
|
||||||
|
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
createMsg(peer: Peer, msgId: string): number {
|
||||||
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`
|
||||||
|
const hash = createHash('md5').update(key).digest()
|
||||||
|
//设置第一个bit为0 保证shortId为正数
|
||||||
|
hash[0] &= 0x7f
|
||||||
|
const shortId = hash.readInt32BE(0)
|
||||||
|
//减少性能损耗
|
||||||
|
// const isExist = this.msgIdMap.getKey(shortId)
|
||||||
|
// if (isExist && isExist === msgId) {
|
||||||
|
// return shortId
|
||||||
|
// }
|
||||||
|
this.msgIdMap.set(msgId, shortId)
|
||||||
|
this.msgDataMap.set(key, shortId)
|
||||||
|
this.db?.upsert('message', [{
|
||||||
|
msgId,
|
||||||
|
shortId,
|
||||||
|
chatType: peer.chatType,
|
||||||
|
peerUid: peer.peerUid
|
||||||
|
}], 'shortId').then()
|
||||||
|
return shortId
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMsgIdAndPeerByShortId(shortId: number): Promise<MsgIdAndPeerByShortId | undefined> {
|
||||||
|
const data = this.msgDataMap.getKey(shortId)
|
||||||
|
if (data) {
|
||||||
|
const [msgId, chatTypeStr, peerUid] = data.split('|')
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: parseInt(chatTypeStr),
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
|
}
|
||||||
|
return { MsgId: msgId, Peer: peer }
|
||||||
|
}
|
||||||
|
const items = await this.db?.get('message', { shortId })
|
||||||
|
if (items?.length) {
|
||||||
|
const { msgId, chatType, peerUid } = items[0]
|
||||||
|
return {
|
||||||
|
MsgId: msgId,
|
||||||
|
Peer: {
|
||||||
|
chatType,
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
}
|
}
|
||||||
const database = new Database<SQLiteTables>()
|
}
|
||||||
await database.connect(SQLite, {
|
|
||||||
path: path.join(dbDir, `${uin}.db`)
|
|
||||||
})
|
|
||||||
database.extend('message', {
|
|
||||||
shortId: 'integer(10)',
|
|
||||||
chatType: 'unsigned',
|
|
||||||
msgId: 'string(24)',
|
|
||||||
peerUid: 'string(24)'
|
|
||||||
}, {
|
|
||||||
primary: 'shortId'
|
|
||||||
})
|
|
||||||
database.extend('file_v2', {
|
|
||||||
fileName: 'string',
|
|
||||||
fileSize: 'string',
|
|
||||||
fileUuid: 'string(128)',
|
|
||||||
msgId: 'string(24)',
|
|
||||||
msgTime: 'unsigned(10)',
|
|
||||||
peerUid: 'string(24)',
|
|
||||||
chatType: 'unsigned',
|
|
||||||
elementId: 'string(24)',
|
|
||||||
elementType: 'unsigned',
|
|
||||||
}, {
|
|
||||||
primary: 'fileUuid',
|
|
||||||
indexes: ['fileName']
|
|
||||||
})
|
|
||||||
this.db = database
|
|
||||||
}
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
async getRecentMsgIds(Peer: Peer, size: number): Promise<string[]> {
|
getShortIdByMsgId(msgId: string): number | undefined {
|
||||||
const heads = this.msgIdMap.getHeads(size)
|
return this.msgIdMap.getValue(msgId)
|
||||||
if (!heads) {
|
}
|
||||||
return []
|
|
||||||
}
|
|
||||||
const data: (MsgIdAndPeerByShortId | undefined)[] = []
|
|
||||||
for (const t of heads) {
|
|
||||||
data.push(await MessageUnique.getMsgIdAndPeerByShortId(t.value))
|
|
||||||
}
|
|
||||||
const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid)
|
|
||||||
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined)
|
|
||||||
}
|
|
||||||
|
|
||||||
createMsg(peer: Peer, msgId: string): number {
|
async getPeerByMsgId(msgId: string) {
|
||||||
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`
|
const shortId = this.msgIdMap.getValue(msgId)
|
||||||
const hash = createHash('md5').update(key).digest()
|
if (!shortId) return undefined
|
||||||
//设置第一个bit为0 保证shortId为正数
|
return await this.getMsgIdAndPeerByShortId(shortId)
|
||||||
hash[0] &= 0x7f
|
}
|
||||||
const shortId = hash.readInt32BE(0)
|
|
||||||
//减少性能损耗
|
|
||||||
// const isExist = this.msgIdMap.getKey(shortId)
|
|
||||||
// if (isExist && isExist === msgId) {
|
|
||||||
// return shortId
|
|
||||||
// }
|
|
||||||
this.msgIdMap.set(msgId, shortId)
|
|
||||||
this.msgDataMap.set(key, shortId)
|
|
||||||
this.db?.upsert('message', [{
|
|
||||||
msgId,
|
|
||||||
shortId,
|
|
||||||
chatType: peer.chatType,
|
|
||||||
peerUid: peer.peerUid
|
|
||||||
}], 'shortId').then()
|
|
||||||
return shortId
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgIdAndPeerByShortId(shortId: number): Promise<MsgIdAndPeerByShortId | undefined> {
|
resize(maxSize: number): void {
|
||||||
const data = this.msgDataMap.getKey(shortId)
|
this.msgIdMap.resize(maxSize)
|
||||||
if (data) {
|
this.msgDataMap.resize(maxSize)
|
||||||
const [msgId, chatTypeStr, peerUid] = data.split('|')
|
}
|
||||||
const peer: Peer = {
|
|
||||||
chatType: parseInt(chatTypeStr),
|
|
||||||
peerUid,
|
|
||||||
guildId: '',
|
|
||||||
}
|
|
||||||
return { MsgId: msgId, Peer: peer }
|
|
||||||
}
|
|
||||||
const items = await this.db?.get('message', { shortId })
|
|
||||||
if (items?.length) {
|
|
||||||
const { msgId, chatType, peerUid } = items[0]
|
|
||||||
return {
|
|
||||||
MsgId: msgId,
|
|
||||||
Peer: {
|
|
||||||
chatType,
|
|
||||||
peerUid,
|
|
||||||
guildId: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
getShortIdByMsgId(msgId: string): number | undefined {
|
addFileCache(data: FileCacheV2) {
|
||||||
return this.msgIdMap.getValue(msgId)
|
return this.db?.upsert('file_v2', [data], 'fileUuid')
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPeerByMsgId(msgId: string) {
|
getFileCacheByName(fileName: string) {
|
||||||
const shortId = this.msgIdMap.getValue(msgId)
|
return this.db?.get('file_v2', { fileName }, {
|
||||||
if (!shortId) return undefined
|
sort: { msgTime: 'desc' }
|
||||||
return await this.getMsgIdAndPeerByShortId(shortId)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(maxSize: number): void {
|
getFileCacheById(fileUuid: string) {
|
||||||
this.msgIdMap.resize(maxSize)
|
return this.db?.get('file_v2', { fileUuid })
|
||||||
this.msgDataMap.resize(maxSize)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
addFileCache(data: FileCacheV2) {
|
|
||||||
return this.db?.upsert('file_v2', [data], 'fileUuid')
|
|
||||||
}
|
|
||||||
|
|
||||||
getFileCacheByName(fileName: string) {
|
|
||||||
return this.db?.get('file_v2', { fileName }, {
|
|
||||||
sort: { msgTime: 'desc' }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getFileCacheById(fileUuid: string) {
|
|
||||||
return this.db?.get('file_v2', { fileUuid })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper()
|
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper()
|
@ -1,72 +1,72 @@
|
|||||||
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/MessageUnique.ts#L5
|
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/MessageUnique.ts#L5
|
||||||
export class LimitedHashTable<K, V> {
|
export class LimitedHashTable<K, V> {
|
||||||
private keyToValue: Map<K, V> = new Map()
|
private keyToValue: Map<K, V> = new Map()
|
||||||
private valueToKey: Map<V, K> = new Map()
|
private valueToKey: Map<V, K> = new Map()
|
||||||
private maxSize: number
|
private maxSize: number
|
||||||
|
|
||||||
constructor(maxSize: number) {
|
constructor(maxSize: number) {
|
||||||
this.maxSize = maxSize
|
this.maxSize = maxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(count: number) {
|
resize(count: number) {
|
||||||
this.maxSize = count
|
this.maxSize = count
|
||||||
}
|
}
|
||||||
|
|
||||||
set(key: K, value: V): void {
|
set(key: K, value: V): void {
|
||||||
this.keyToValue.set(key, value)
|
this.keyToValue.set(key, value)
|
||||||
this.valueToKey.set(value, key)
|
this.valueToKey.set(value, key)
|
||||||
while (this.keyToValue.size !== this.valueToKey.size) {
|
while (this.keyToValue.size !== this.valueToKey.size) {
|
||||||
console.log('keyToValue.size !== valueToKey.size Error Atom')
|
console.log('keyToValue.size !== valueToKey.size Error Atom')
|
||||||
this.keyToValue.clear()
|
this.keyToValue.clear()
|
||||||
this.valueToKey.clear()
|
this.valueToKey.clear()
|
||||||
}
|
|
||||||
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
|
||||||
const oldestKey = this.keyToValue.keys().next().value
|
|
||||||
this.valueToKey.delete(this.keyToValue.get(oldestKey)!)
|
|
||||||
this.keyToValue.delete(oldestKey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
||||||
|
const oldestKey = this.keyToValue.keys().next().value
|
||||||
|
this.valueToKey.delete(this.keyToValue.get(oldestKey)!)
|
||||||
|
this.keyToValue.delete(oldestKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getValue(key: K): V | undefined {
|
getValue(key: K): V | undefined {
|
||||||
return this.keyToValue.get(key)
|
return this.keyToValue.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
getKey(value: V): K | undefined {
|
getKey(value: V): K | undefined {
|
||||||
return this.valueToKey.get(value)
|
return this.valueToKey.get(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteByValue(value: V): void {
|
deleteByValue(value: V): void {
|
||||||
const key = this.valueToKey.get(value)
|
const key = this.valueToKey.get(value)
|
||||||
if (key !== undefined) {
|
if (key !== undefined) {
|
||||||
this.keyToValue.delete(key)
|
this.keyToValue.delete(key)
|
||||||
this.valueToKey.delete(value)
|
this.valueToKey.delete(value)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deleteByKey(key: K): void {
|
deleteByKey(key: K): void {
|
||||||
const value = this.keyToValue.get(key)
|
const value = this.keyToValue.get(key)
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
this.keyToValue.delete(key)
|
this.keyToValue.delete(key)
|
||||||
this.valueToKey.delete(value)
|
this.valueToKey.delete(value)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getKeyList(): K[] {
|
getKeyList(): K[] {
|
||||||
return Array.from(this.keyToValue.keys())
|
return Array.from(this.keyToValue.keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取最近刚写入的几个值
|
//获取最近刚写入的几个值
|
||||||
getHeads(size: number): { key: K; value: V }[] | undefined {
|
getHeads(size: number): { key: K; value: V }[] | undefined {
|
||||||
const keyList = this.getKeyList()
|
const keyList = this.getKeyList()
|
||||||
if (keyList.length === 0) {
|
if (keyList.length === 0) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
|
||||||
const result: { key: K; value: V }[] = []
|
|
||||||
const listSize = Math.min(size, keyList.length)
|
|
||||||
for (let i = 0; i < listSize; i++) {
|
|
||||||
const key = keyList[listSize - i]
|
|
||||||
result.push({ key, value: this.keyToValue.get(key)! })
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
const result: { key: K; value: V }[] = []
|
||||||
|
const listSize = Math.min(size, keyList.length)
|
||||||
|
for (let i = 0; i < listSize; i++) {
|
||||||
|
const key = keyList[listSize - i]
|
||||||
|
result.push({ key, value: this.keyToValue.get(key)! })
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
@ -21,7 +21,6 @@ import { Peer } from '@/ntqqapi/types/msg'
|
|||||||
import { calculateFileMD5 } from '@/common/utils/file'
|
import { calculateFileMD5 } from '@/common/utils/file'
|
||||||
import { fileTypeFromFile } from 'file-type'
|
import { fileTypeFromFile } from 'file-type'
|
||||||
import fsPromise from 'node:fs/promises'
|
import fsPromise from 'node:fs/promises'
|
||||||
import { NTEventDispatch } from '@/common/utils/eventTask'
|
|
||||||
import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners'
|
import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners'
|
||||||
import { Time } from 'cosmokit'
|
import { Time } from 'cosmokit'
|
||||||
import { Service, Context } from 'cordis'
|
import { Service, Context } from 'cordis'
|
||||||
@ -143,76 +142,32 @@ export class NTQQFileApi extends Service {
|
|||||||
return sourcePath
|
return sourcePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let filePath: string
|
const data = await invoke<{ notifyInfo: OnRichMediaDownloadCompleteParams }>(
|
||||||
if (NTEventDispatch.initialised) {
|
'nodeIKernelMsgService/downloadRichMedia',
|
||||||
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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
fileModelId: '0',
|
getReq: {
|
||||||
downloadSourceType: 0,
|
fileModelId: '0',
|
||||||
triggerType: 1,
|
downloadSourceType: 0,
|
||||||
msgId: msgId,
|
triggerType: 1,
|
||||||
chatType: chatType,
|
msgId: msgId,
|
||||||
peerUid: peerUid,
|
chatType: chatType,
|
||||||
elementId: elementId,
|
peerUid: peerUid,
|
||||||
thumbSize: 0,
|
elementId: elementId,
|
||||||
downloadType: 1,
|
thumbSize: 0,
|
||||||
filePath: thumbPath
|
downloadType: 1,
|
||||||
}
|
filePath: thumbPath,
|
||||||
)
|
|
||||||
filePath = data[1].filePath
|
|
||||||
} else {
|
|
||||||
const data = await invoke<{ notifyInfo: OnRichMediaDownloadCompleteParams }>(
|
|
||||||
'nodeIKernelMsgService/downloadRichMedia',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
getReq: {
|
|
||||||
fileModelId: '0',
|
|
||||||
downloadSourceType: 0,
|
|
||||||
triggerType: 1,
|
|
||||||
msgId: msgId,
|
|
||||||
chatType: chatType,
|
|
||||||
peerUid: peerUid,
|
|
||||||
elementId: elementId,
|
|
||||||
thumbSize: 0,
|
|
||||||
downloadType: 1,
|
|
||||||
filePath: thumbPath,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
null,
|
},
|
||||||
],
|
null,
|
||||||
{
|
],
|
||||||
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
|
{
|
||||||
cmdCB: payload => payload.notifyInfo.msgId === msgId,
|
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
|
||||||
timeout
|
cmdCB: payload => payload.notifyInfo.msgId === msgId,
|
||||||
}
|
timeout
|
||||||
)
|
}
|
||||||
filePath = data.notifyInfo.filePath
|
)
|
||||||
}
|
let filePath = data.notifyInfo.filePath
|
||||||
if (filePath.startsWith('\\')) {
|
if (filePath.startsWith('\\')) {
|
||||||
const downloadPath = TEMP_DIR
|
const downloadPath = TEMP_DIR
|
||||||
filePath = path.join(downloadPath, filePath)
|
filePath = path.join(downloadPath, filePath)
|
||||||
@ -238,7 +193,7 @@ export class NTQQFileApi extends Service {
|
|||||||
const url: string = element.originImageUrl! // 没有域名
|
const url: string = element.originImageUrl! // 没有域名
|
||||||
const md5HexStr = element.md5HexStr
|
const md5HexStr = element.md5HexStr
|
||||||
const fileMd5 = element.md5HexStr
|
const fileMd5 = element.md5HexStr
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
const parsedUrl = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接
|
const parsedUrl = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接
|
||||||
const imageAppid = parsedUrl.searchParams.get('appid')
|
const imageAppid = parsedUrl.searchParams.get('appid')
|
||||||
|
@ -2,8 +2,7 @@ import { Friend, FriendV2, SimpleInfo, CategoryFriend } from '../types'
|
|||||||
import { ReceiveCmdS } from '../hook'
|
import { ReceiveCmdS } from '../hook'
|
||||||
import { invoke, NTMethod, NTClass } from '../ntcall'
|
import { invoke, NTMethod, NTClass } from '../ntcall'
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { BuddyListReqType, NodeIKernelProfileService } from '../services'
|
import { BuddyListReqType } from '../services'
|
||||||
import { NTEventDispatch } from '@/common/utils/eventTask'
|
|
||||||
import { Dict, pick } from 'cosmokit'
|
import { Dict, pick } from 'cosmokit'
|
||||||
import { Service, Context } from 'cordis'
|
import { Service, Context } from 'cordis'
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ export class NTQQFriendApi extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 大于或等于 26702 应使用 getBuddyV2 */
|
/** 大于或等于 26702 应使用 getBuddyV2 */
|
||||||
async getFriends(_forced = false) {
|
async getFriends() {
|
||||||
const data = await invoke<{
|
const data = await invoke<{
|
||||||
data: {
|
data: {
|
||||||
categoryId: number
|
categoryId: number
|
||||||
@ -75,9 +74,7 @@ export class NTQQFriendApi extends Service {
|
|||||||
const buddyService = session.getBuddyService()
|
const buddyService = session.getBuddyService()
|
||||||
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)
|
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)
|
||||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids))
|
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids))
|
||||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
const data = await session.getProfileService().getCoreAndBaseInfo('nodeStore', uids)
|
||||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
|
||||||
)
|
|
||||||
return Array.from(data.values())
|
return Array.from(data.values())
|
||||||
} else {
|
} else {
|
||||||
const data = await invoke<{
|
const data = await invoke<{
|
||||||
@ -92,11 +89,8 @@ export class NTQQFriendApi extends Service {
|
|||||||
afterFirstCmd: false,
|
afterFirstCmd: false,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const categoryUids: Map<number, string[]> = new Map()
|
const uids = data.buddyCategory.flatMap(item => item.buddyUids)
|
||||||
for (const item of data.buddyCategory) {
|
return Object.values(data.userSimpleInfos).filter(v => uids.includes(v.uid!))
|
||||||
categoryUids.set(item.categoryId, item.buddyUids)
|
|
||||||
}
|
|
||||||
return Object.values(data.userSimpleInfos).filter(v => v.baseInfo && categoryUids.get(v.baseInfo.categoryId)?.includes(v.uid!))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +100,10 @@ export class NTQQFriendApi extends Service {
|
|||||||
const session = getSession()
|
const session = getSession()
|
||||||
if (session) {
|
if (session) {
|
||||||
const uids: string[] = []
|
const uids: string[] = []
|
||||||
const buddyService = session?.getBuddyService()
|
const buddyService = session.getBuddyService()
|
||||||
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)
|
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)
|
||||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids))
|
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids))
|
||||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
const data = await session.getProfileService().getCoreAndBaseInfo('nodeStore', uids)
|
||||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
|
||||||
)
|
|
||||||
for (const [, item] of data) {
|
for (const [, item] of data) {
|
||||||
if (retMap.size > 5000) {
|
if (retMap.size > 5000) {
|
||||||
break
|
break
|
||||||
@ -155,9 +147,7 @@ export class NTQQFriendApi extends Service {
|
|||||||
})
|
})
|
||||||
return item.buddyUids
|
return item.buddyUids
|
||||||
}))
|
}))
|
||||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
const data = await session.getProfileService().getCoreAndBaseInfo('nodeStore', uids)
|
||||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
|
||||||
)
|
|
||||||
return Array.from(data).map(([key, value]) => {
|
return Array.from(data).map(([key, value]) => {
|
||||||
const category = categoryMap.get(key)
|
const category = categoryMap.get(key)
|
||||||
return category ? { ...value, categoryId: category.categoryId, categroyName: category.categroyName } : value
|
return category ? { ...value, categoryId: category.categoryId, categroyName: category.categroyName } : value
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReceiveCmdS } from '../hook'
|
import { ReceiveCmdS } from '../hook'
|
||||||
import { Group, GroupMember, GroupMemberRole, GroupNotifies, GroupRequestOperateTypes, GroupNotify, GetFileListParam } from '../types'
|
import { Group, GroupMember, GroupMemberRole, GroupNotifies, GroupRequestOperateTypes, GetFileListParam } from '../types'
|
||||||
import { invoke, NTClass, NTMethod } from '../ntcall'
|
import { invoke, NTClass, NTMethod } from '../ntcall'
|
||||||
import { GeneralCallResult } from '../services'
|
import { GeneralCallResult } from '../services'
|
||||||
import { NTQQWindows } from './window'
|
import { NTQQWindows } from './window'
|
||||||
@ -24,7 +24,7 @@ export class NTQQGroupApi extends Service {
|
|||||||
super(ctx, 'ntGroupApi', true)
|
super(ctx, 'ntGroupApi', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroups(forced = false): Promise<Group[]> {
|
async getGroups(): Promise<Group[]> {
|
||||||
const result = await invoke<{
|
const result = await invoke<{
|
||||||
updateType: number
|
updateType: number
|
||||||
groupList: Group[]
|
groupList: Group[]
|
||||||
|
@ -65,7 +65,7 @@ export class NTQQUserApi extends Service {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserDetailInfo(uid: string, _getLevel = false) {
|
async getUserDetailInfo(uid: string) {
|
||||||
if (getBuildVersion() >= 26702) {
|
if (getBuildVersion() >= 26702) {
|
||||||
return this.fetchUserDetailInfo(uid)
|
return this.fetchUserDetailInfo(uid)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ import fs from 'node:fs'
|
|||||||
import { Service, Context } from 'cordis'
|
import { Service, Context } from 'cordis'
|
||||||
import { registerCallHook, registerReceiveHook, ReceiveCmdS } from './hook'
|
import { registerCallHook, registerReceiveHook, ReceiveCmdS } from './hook'
|
||||||
import { MessageUnique } from '../common/utils/messageUnique'
|
import { MessageUnique } from '../common/utils/messageUnique'
|
||||||
import { NTEventDispatch } from '../common/utils/eventTask'
|
|
||||||
import { wrapperConstructor, getSession } from './wrapper'
|
|
||||||
import { Config as LLOBConfig } from '../common/types'
|
import { Config as LLOBConfig } from '../common/types'
|
||||||
import { llonebotError } from '../common/globalVars'
|
import { llonebotError } from '../common/globalVars'
|
||||||
import { isNumeric } from '../common/utils/misc'
|
import { isNumeric } from '../common/utils/misc'
|
||||||
@ -45,10 +43,6 @@ class Core extends Service {
|
|||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
llonebotError.otherError = ''
|
llonebotError.otherError = ''
|
||||||
const WrapperSession = getSession()
|
|
||||||
if (WrapperSession) {
|
|
||||||
NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession })
|
|
||||||
}
|
|
||||||
MessageUnique.init(selfInfo.uin)
|
MessageUnique.init(selfInfo.uin)
|
||||||
this.registerListener()
|
this.registerListener()
|
||||||
this.ctx.logger.info(`LLOneBot/${version}`)
|
this.ctx.logger.info(`LLOneBot/${version}`)
|
||||||
|
@ -201,7 +201,7 @@ export namespace SendElementEntities {
|
|||||||
// log("生成缩略图", _thumbPath)
|
// log("生成缩略图", _thumbPath)
|
||||||
thumbPath.set(0, _thumbPath)
|
thumbPath.set(0, _thumbPath)
|
||||||
const thumbMd5 = await calculateFileMD5(_thumbPath)
|
const thumbMd5 = await calculateFileMD5(_thumbPath)
|
||||||
let element: SendVideoElement = {
|
const element: SendVideoElement = {
|
||||||
elementType: ElementType.VIDEO,
|
elementType: ElementType.VIDEO,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
videoElement: {
|
videoElement: {
|
||||||
|
@ -34,7 +34,7 @@ export interface WrapperApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WrapperConstructor {
|
export interface WrapperConstructor {
|
||||||
[key: string]: any
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapperApi: WrapperApi = {}
|
const wrapperApi: WrapperApi = {}
|
||||||
|
@ -28,7 +28,7 @@ export class UploadGroupFile extends BaseAction<UploadGroupFilePayload, null> {
|
|||||||
}
|
}
|
||||||
const sendFileEle = await SendElementEntities.file(this.ctx, downloadResult.path, payload.name, payload.folder_id)
|
const sendFileEle = await SendElementEntities.file(this.ctx, downloadResult.path, payload.name, payload.folder_id)
|
||||||
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group)
|
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group)
|
||||||
await sendMsg(this.ctx, peer, [sendFileEle], [], true)
|
await sendMsg(this.ctx, peer, [sendFileEle], [])
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ export class UploadPrivateFile extends BaseAction<UploadPrivateFilePayload, null
|
|||||||
throw new Error(downloadResult.errMsg)
|
throw new Error(downloadResult.errMsg)
|
||||||
}
|
}
|
||||||
const sendFileEle: SendFileElement = await SendElementEntities.file(this.ctx, downloadResult.path, payload.name)
|
const sendFileEle: SendFileElement = await SendElementEntities.file(this.ctx, downloadResult.path, payload.name)
|
||||||
await sendMsg(this.ctx, peer, [sendFileEle], [], true)
|
await sendMsg(this.ctx, peer, [sendFileEle], [])
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ interface Payload {
|
|||||||
class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
||||||
actionName = ActionName.GetGroupList
|
actionName = ActionName.GetGroupList
|
||||||
|
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle() {
|
||||||
const groupList = await this.ctx.ntGroupApi.getGroups(payload?.no_cache === true || payload?.no_cache === 'true')
|
const groupList = await this.ctx.ntGroupApi.getGroups()
|
||||||
return OB11Entities.groups(groupList)
|
return OB11Entities.groups(groupList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
if (member) {
|
if (member) {
|
||||||
if (isNullable(member.sex)) {
|
if (isNullable(member.sex)) {
|
||||||
//log('获取群成员详细信息')
|
//log('获取群成员详细信息')
|
||||||
const info = await this.ctx.ntUserApi.getUserDetailInfo(member.uid, true)
|
const info = await this.ctx.ntUserApi.getUserDetailInfo(member.uid)
|
||||||
//log('群成员详细信息结果', info)
|
//log('群成员详细信息结果', info)
|
||||||
Object.assign(member, info)
|
Object.assign(member, info)
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnData> {
|
|||||||
}
|
}
|
||||||
// log("分割后的转发节点", sendElementsSplit)
|
// log("分割后的转发节点", sendElementsSplit)
|
||||||
for (const eles of sendElementsSplit) {
|
for (const eles of sendElementsSplit) {
|
||||||
const nodeMsg = await sendMsg(this.ctx, selfPeer, eles, [], true)
|
const nodeMsg = await sendMsg(this.ctx, selfPeer, eles, [])
|
||||||
if (!nodeMsg) {
|
if (!nodeMsg) {
|
||||||
this.ctx.logger.warn('转发节点生成失败', eles)
|
this.ctx.logger.warn('转发节点生成失败', eles)
|
||||||
continue
|
continue
|
||||||
|
@ -5,7 +5,7 @@ interface ReturnType {
|
|||||||
yes: boolean
|
yes: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CanSendRecord extends BaseAction<any, ReturnType> {
|
export default class CanSendRecord extends BaseAction<null, ReturnType> {
|
||||||
actionName = ActionName.CanSendRecord
|
actionName = ActionName.CanSendRecord
|
||||||
|
|
||||||
protected async _handle() {
|
protected async _handle() {
|
||||||
|
@ -16,7 +16,7 @@ export class GetFriendList extends BaseAction<Payload, OB11User[]> {
|
|||||||
if (getBuildVersion() >= 26702) {
|
if (getBuildVersion() >= 26702) {
|
||||||
return OB11Entities.friendsV2(await this.ctx.ntFriendApi.getBuddyV2(refresh))
|
return OB11Entities.friendsV2(await this.ctx.ntFriendApi.getBuddyV2(refresh))
|
||||||
}
|
}
|
||||||
return OB11Entities.friends(await this.ctx.ntFriendApi.getFriends(refresh))
|
return OB11Entities.friends(await this.ctx.ntFriendApi.getFriends())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ class OneBot11Adapter extends Service {
|
|||||||
this.handleRecallMsg(input)
|
this.handleRecallMsg(input)
|
||||||
})
|
})
|
||||||
this.ctx.on('nt/message-sent', input => {
|
this.ctx.on('nt/message-sent', input => {
|
||||||
this.handleRecallMsg(input)
|
this.handleMsg(input)
|
||||||
})
|
})
|
||||||
this.ctx.on('nt/group-notify', input => {
|
this.ctx.on('nt/group-notify', input => {
|
||||||
this.handleGroupNotify(input)
|
this.handleGroupNotify(input)
|
||||||
|
@ -173,8 +173,8 @@ export namespace OB11Entities {
|
|||||||
id: MessageUnique.createMsg(peer, replyMsg ? replyMsg.msgId : records.msgId).toString()
|
id: MessageUnique.createMsg(peer, replyMsg ? replyMsg.msgId : records.msgId).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e) {
|
||||||
ctx.logger.error('获取不到引用的消息', replyElement, e.stack)
|
ctx.logger.error('获取不到引用的消息', replyElement, (e as Error).stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,7 +378,7 @@ export namespace OB11Entities {
|
|||||||
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
//判断业务类型
|
//判断业务类型
|
||||||
//Poke事件
|
//Poke事件
|
||||||
const pokedetail: any[] = json.items
|
const pokedetail: Dict[] = json.items
|
||||||
//筛选item带有uid的元素
|
//筛选item带有uid的元素
|
||||||
const poke_uid = pokedetail.filter(item => item.uid)
|
const poke_uid = pokedetail.filter(item => item.uid)
|
||||||
if (poke_uid.length == 2) {
|
if (poke_uid.length == 2) {
|
||||||
|
@ -236,8 +236,7 @@ export async function sendMsg(
|
|||||||
ctx: Context,
|
ctx: Context,
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
sendElements: SendMessageElement[],
|
sendElements: SendMessageElement[],
|
||||||
deleteAfterSentFiles: string[],
|
deleteAfterSentFiles: string[]
|
||||||
waitComplete = true,
|
|
||||||
) {
|
) {
|
||||||
if (!sendElements.length) {
|
if (!sendElements.length) {
|
||||||
throw '消息体无法解析,请检查是否发送了不支持的消息类型'
|
throw '消息体无法解析,请检查是否发送了不支持的消息类型'
|
||||||
|
@ -88,7 +88,7 @@ async function handleMsg(ctx: Context, msg: OB11Message, quickAction: QuickOpera
|
|||||||
}
|
}
|
||||||
replyMessage = replyMessage.concat(convertMessage2List(reply, quickAction.auto_escape))
|
replyMessage = replyMessage.concat(convertMessage2List(reply, quickAction.auto_escape))
|
||||||
const { sendElements, deleteAfterSentFiles } = await createSendElements(ctx, replyMessage, peer)
|
const { sendElements, deleteAfterSentFiles } = await createSendElements(ctx, replyMessage, peer)
|
||||||
sendMsg(ctx, peer, sendElements, deleteAfterSentFiles, false).catch(e => ctx.logger.error(e))
|
sendMsg(ctx, peer, sendElements, deleteAfterSentFiles).catch(e => ctx.logger.error(e))
|
||||||
}
|
}
|
||||||
if (msg.message_type === 'group') {
|
if (msg.message_type === 'group') {
|
||||||
const groupMsgQuickAction = quickAction as QuickOperationGroupMessage
|
const groupMsgQuickAction = quickAction as QuickOperationGroupMessage
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CheckVersion, Config } from '../common/types'
|
import { CheckVersion, Config } from '../common/types'
|
||||||
import { SettingButton, SettingItem, SettingList, SettingSwitch, SettingSelect } from './components'
|
import { SettingButton, SettingItem, SettingList, SettingSwitch, SettingSelect } from './components'
|
||||||
import { version } from '../version'
|
import { version } from '../version'
|
||||||
// @ts-expect-error
|
// @ts-expect-error: Unreachable code error
|
||||||
import StyleRaw from './style.css?raw'
|
import StyleRaw from './style.css?raw'
|
||||||
|
|
||||||
type HostsType = 'httpHosts' | 'wsHosts'
|
type HostsType = 'httpHosts' | 'wsHosts'
|
||||||
|
@ -1 +1 @@
|
|||||||
export const version = '3.31.9'
|
export const version = '3.31.10'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user