Compare commits

...

31 Commits

Author SHA1 Message Date
idranme
1c6364d98f Merge pull request #435 from LLOneBot/dev
release: 3.33.3
2024-09-21 21:52:54 +08:00
idranme
8a268c3968 chore: v3.33.3 2024-09-21 21:39:30 +08:00
idranme
806798bd48 refactor 2024-09-21 21:32:40 +08:00
idranme
0c456e2160 optimize 2024-09-21 20:19:26 +08:00
idranme
08e7e471d6 fix: delete_group_file API 2024-09-21 20:19:10 +08:00
idranme
13299c4631 chore: improve code quality 2024-09-21 09:21:08 +08:00
idranme
390e20c2ef optimize 2024-09-21 03:30:01 +08:00
idranme
d8433e22d2 chore: improve code quality 2024-09-21 01:06:49 +08:00
idranme
ac07c98ae1 Merge pull request #434 from LLOneBot/dev
release: 3.33.2
2024-09-20 23:00:09 +08:00
idranme
6a19d6f234 chore: v3.33.2 2024-09-20 22:57:00 +08:00
idranme
ab0b8ae663 feat: get_essence_msg_list API 2024-09-20 22:55:29 +08:00
idranme
96aa5e264a refactor 2024-09-20 22:13:26 +08:00
idranme
15b85f735d fix: friend_add event 2024-09-20 19:08:22 +08:00
idranme
4dd6d12168 feat 2024-09-20 18:00:32 +08:00
idranme
44febed486 optimize 2024-09-20 03:19:43 +08:00
idranme
6c66dab3dc Merge pull request #433 from LLOneBot/dev
release: 3.33.1
2024-09-19 18:31:01 +08:00
idranme
0f7939fe5e chore: v3.33.1 2024-09-19 18:29:16 +08:00
idranme
73a2b4e35f fix 2024-09-19 18:29:12 +08:00
idranme
936b1d911c Merge pull request #428 from LLOneBot/dev
release: 3.33.0
2024-09-18 20:59:57 +08:00
idranme
58817d1c02 chore: v3.33.0 2024-09-18 20:53:28 +08:00
idranme
2c24422478 feat: support setting remark when agreeing to a friend request 2024-09-18 20:47:45 +08:00
idranme
c2a723380a fix: get_group_member_list API 2024-09-18 19:35:58 +08:00
idranme
156bbaea33 feat: get_group_files_by_folder API 2024-09-18 17:22:09 +08:00
idranme
6c485634e1 feat: get_friend_msg_history API 2024-09-18 16:56:15 +08:00
idranme
f39a9aeafb feat: fetch_custom_face API 2024-09-18 16:11:08 +08:00
idranme
1160cd4b26 feat: fetch_emoji_like API 2024-09-18 15:49:37 +08:00
idranme
9a7ff523dd optimize 2024-09-18 14:07:42 +08:00
idranme
f49995ea97 refactor 2024-09-17 21:04:36 +08:00
idranme
1876dd29ac Merge pull request #423 from LLOneBot/dev
release: 3.32.8
2024-09-17 11:59:57 +08:00
idranme
9944b53266 chore: v3.32.8 2024-09-17 11:55:50 +08:00
idranme
9a791e3a21 fix 2024-09-17 02:17:16 +08:00
86 changed files with 1299 additions and 2906 deletions

7
.gitattributes vendored Normal file
View File

@@ -0,0 +1,7 @@
* text eol=lf
*.png -text
*.jpg -text
*.ico -text
*.gif -text
*.webp -text

View File

@@ -31,7 +31,6 @@ const config: ElectronViteConfig = {
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'./lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg',
},
},
plugins: [

View File

@@ -4,7 +4,7 @@
"name": "LLOneBot",
"slug": "LLOneBot",
"description": "实现 OneBot 11 协议,用于 QQ 机器人开发",
"version": "3.32.7",
"version": "3.33.3",
"icon": "./icon.webp",
"authors": [
{

View File

@@ -17,16 +17,16 @@
"author": "",
"license": "MIT",
"dependencies": {
"@minatojs/driver-sqlite": "^4.5.0",
"@minatojs/driver-sqlite": "^4.6.0",
"compressing": "^1.10.1",
"cordis": "^3.18.0",
"cordis": "^3.18.1",
"cors": "^2.8.5",
"cosmokit": "^1.6.2",
"express": "^5.0.0",
"fast-xml-parser": "^4.5.0",
"file-type": "^19.5.0",
"fluent-ffmpeg": "^2.1.3",
"minato": "^3.5.1",
"minato": "^3.6.0",
"protobufjs": "^7.4.0",
"silk-wasm": "^3.6.1",
"ws": "^8.18.0"
@@ -41,8 +41,8 @@
"electron-vite": "^2.3.0",
"protobufjs-cli": "^1.1.3",
"typescript": "^5.6.2",
"vite": "^5.4.5",
"vite": "^5.4.7",
"vite-plugin-cp": "^4.0.8"
},
"packageManager": "yarn@4.4.1"
"packageManager": "yarn@4.5.0"
}

View File

@@ -6,14 +6,6 @@ import { randomUUID, createHash } from 'node:crypto'
import { fileURLToPath } from 'node:url'
import { fileTypeFromFile } from 'file-type'
export function isGIF(path: string) {
const buffer = Buffer.alloc(4)
const fd = fs.openSync(path, 'r')
fs.readSync(fd, buffer, 0, 4, 0)
fs.closeSync(fd)
return buffer.toString() === 'GIF8'
}
// 定义一个异步函数来检查文件是否存在
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
return new Promise((resolve, reject) => {
@@ -91,17 +83,26 @@ interface FetchFileRes {
}
export async function fetchFile(url: string, headersInit?: Record<string, string>): Promise<FetchFileRes> {
const headers: Record<string, string> = {
const headers = new Headers({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
'Host': new URL(url).hostname,
...headersInit
}
const raw = await fetch(url, { headers }).catch((err) => {
})
let raw = await fetch(url, { headers }).catch((err) => {
if (err.cause) {
throw err.cause
}
throw err
})
if (raw.status === 403 && !headers.has('Referer')) {
headers.set('Referer', url)
raw = await fetch(url, { headers }).catch((err) => {
if (err.cause) {
throw err.cause
}
throw err
})
}
if (!raw.ok) throw new Error(`statusText: ${raw.statusText}`)
return {
data: Buffer.from(await raw.arrayBuffer()),
@@ -133,7 +134,7 @@ export async function uri2local(uri: string, filename?: string, needExt?: boolea
if (type === FileUriType.RemoteURL) {
try {
const res = await fetchFile(uri, { 'Referer': uri })
const res = await fetchFile(uri)
const match = res.url.match(/.+\/([^/?]*)(?=\?)?/)
if (match?.[1]) {
filename ??= match[1].replace(/[/\\:*?"<>|]/g, '_')

View File

@@ -1,163 +0,0 @@
import fsPromise from 'node:fs/promises'
import fs from 'node:fs'
import path from 'node:path'
import Database, { Tables } from 'minato'
import SQLite from '@minatojs/driver-sqlite'
import { Peer } from '@/ntqqapi/types'
import { createHash } from 'node:crypto'
import { LimitedHashTable } from './table'
import { DATA_DIR } from '../globalVars'
import { FileCacheV2 } from '../types'
interface SQLiteTables extends Tables {
message: {
shortId: number
msgId: string
chatType: number
peerUid: string
}
file_v2: FileCacheV2
}
interface MsgIdAndPeerByShortId {
MsgId: string
Peer: Peer
}
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/MessageUnique.ts#L84
class MessageUniqueWrapper {
private msgDataMap: LimitedHashTable<string, number>
private msgIdMap: LimitedHashTable<string, number>
private db: Database<SQLiteTables> | undefined
constructor(maxMap: number = 1000) {
this.msgIdMap = 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 getRecentMsgIds(Peer: Peer, size: number): Promise<string[]> {
const heads = this.msgIdMap.getHeads(size)
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 {
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: '',
}
}
}
return undefined
}
getShortIdByMsgId(msgId: string): number | undefined {
return this.msgIdMap.getValue(msgId)
}
async getPeerByMsgId(msgId: string) {
const shortId = this.msgIdMap.getValue(msgId)
if (!shortId) return undefined
return await this.getMsgIdAndPeerByShortId(shortId)
}
resize(maxSize: number): void {
this.msgIdMap.resize(maxSize)
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()

View File

@@ -12,8 +12,9 @@ export function calcQQLevel(level: QQLevel) {
/** QQ Build Version */
export function getBuildVersion(): number {
const version: string = globalThis.LiteLoader.versions.qqnt
return +version.split('-')[1]
//const version: string = globalThis.LiteLoader.versions.qqnt
//return +version.split('-')[1]
return +globalThis.LiteLoader.package.qqnt.buildVersion
}
/** 在保证老对象已有的属性不变化的情况下将新对象的属性复制到老对象 */

View File

@@ -1,5 +1,4 @@
import path from 'node:path'
import fs from 'node:fs'
import Log from './log'
import Core from '../ntqqapi/core'
import OneBot11Adapter from '../onebot11/adapter'
@@ -34,6 +33,11 @@ import {
NTQQWebApi,
NTQQWindowApi
} from '../ntqqapi/api'
import { mkdir } from 'node:fs/promises'
import { existsSync, mkdirSync } from 'node:fs'
import Database from 'minato'
import SQLiteDriver from '@minatojs/driver-sqlite'
import Store from './store'
declare module 'cordis' {
interface Events {
@@ -45,12 +49,12 @@ let mainWindow: BrowserWindow | null = null
// 加载插件时触发
function onLoad() {
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true })
if (!existsSync(DATA_DIR)) {
mkdirSync(DATA_DIR, { recursive: true })
}
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR)
if (!existsSync(LOG_DIR)) {
mkdirSync(LOG_DIR)
}
ipcMain.handle(CHANNEL_CHECK_VERSION, async () => {
@@ -151,8 +155,12 @@ function onLoad() {
log('LLOneBot 开关设置为关闭不启动LLOneBot')
return
}
if (!fs.existsSync(TEMP_DIR)) {
fs.mkdirSync(TEMP_DIR)
if (!existsSync(TEMP_DIR)) {
await mkdir(TEMP_DIR)
}
const dbDir = path.join(DATA_DIR, 'database')
if (!existsSync(dbDir)) {
await mkdir(dbDir)
}
const ctx = new Context()
ctx.plugin(Log, {
@@ -179,6 +187,11 @@ function onLoad() {
enableLocalFile2Url: config.enableLocalFile2Url!,
ffmpeg: config.ffmpeg,
})
ctx.plugin(Database)
ctx.plugin(SQLiteDriver, {
path: path.join(dbDir, `${selfInfo.uin}.db`)
})
ctx.plugin(Store)
ctx.start()
ipcMain.on(CHANNEL_SET_CONFIG_CONFIRMED, (event, config: LLOBConfig) => {
ctx.parallel('llonebot/config-updated', config)

126
src/main/store.ts Normal file
View File

@@ -0,0 +1,126 @@
import { Peer } from '@/ntqqapi/types'
import { createHash } from 'node:crypto'
import { LimitedHashTable } from '@/common/utils/table'
import { FileCacheV2 } from '@/common/types'
import { Context, Service } from 'cordis'
declare module 'cordis' {
interface Context {
store: Store
}
interface Tables {
message: {
shortId: number
msgId: string
chatType: number
peerUid: string
}
file_v2: FileCacheV2
}
}
interface MsgInfo {
msgId: string
peer: Peer
}
export default class Store extends Service {
static inject = ['database', 'model']
private cache: LimitedHashTable<string, number>
constructor(protected ctx: Context) {
super(ctx, 'store', true)
this.cache = new LimitedHashTable<string, number>(1000)
this.initDatabase()
}
private async initDatabase() {
this.ctx.model.extend('message', {
shortId: 'integer(10)',
chatType: 'unsigned',
msgId: 'string(24)',
peerUid: 'string(24)'
}, {
primary: 'shortId'
})
this.ctx.model.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']
})
}
createMsgShortId(peer: Peer, msgId: string): number {
const cacheKey = `${msgId}|${peer.chatType}|${peer.peerUid}`
const hash = createHash('md5').update(cacheKey).digest()
hash[0] &= 0x7f //设置第一个bit为0 保证shortId为正数
const shortId = hash.readInt32BE()
this.cache.set(cacheKey, shortId)
this.ctx.database.upsert('message', [{
msgId,
shortId,
chatType: peer.chatType,
peerUid: peer.peerUid
}], 'shortId').then()
return shortId
}
async getMsgInfoByShortId(shortId: number): Promise<MsgInfo | undefined> {
const data = this.cache.getKey(shortId)
if (data) {
const [msgId, chatTypeStr, peerUid] = data.split('|')
return {
msgId,
peer: {
chatType: +chatTypeStr,
peerUid,
guildId: ''
}
}
}
const items = await this.ctx.database.get('message', { shortId })
if (items?.length) {
const { msgId, chatType, peerUid } = items[0]
return {
msgId,
peer: {
chatType,
peerUid,
guildId: ''
}
}
}
}
async getShortIdByMsgId(msgId: string): Promise<number | undefined> {
return (await this.ctx.database.get('message', { msgId }))[0]?.shortId
}
getShortIdByMsgInfo(peer: Peer, msgId: string) {
const cacheKey = `${msgId}|${peer.chatType}|${peer.peerUid}`
return this.cache.getValue(cacheKey)
}
addFileCache(data: FileCacheV2) {
return this.ctx.database.upsert('file_v2', [data], 'fileUuid')
}
getFileCacheByName(fileName: string) {
return this.ctx.database.get('file_v2', { fileName }, {
sort: { msgTime: 'desc' }
})
}
getFileCacheById(fileUuid: string) {
return this.ctx.database.get('file_v2', { fileUuid })
}
}

View File

@@ -13,15 +13,14 @@ import {
PicElement,
} from '../types'
import path from 'node:path'
import fs from 'node:fs'
import { existsSync } from 'node:fs'
import { ReceiveCmdS } from '../hook'
import { RkeyManager } from '@/ntqqapi/helper/rkey'
import { getSession } from '@/ntqqapi/wrapper'
import { Peer } from '@/ntqqapi/types/msg'
import { OnRichMediaDownloadCompleteParams, Peer } from '@/ntqqapi/types/msg'
import { calculateFileMD5 } from '@/common/utils/file'
import { fileTypeFromFile } from 'file-type'
import fsPromise from 'node:fs/promises'
import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners'
import { copyFile, stat, unlink } from 'node:fs/promises'
import { Time } from 'cosmokit'
import { Service, Context } from 'cordis'
import { TEMP_DIR } from '@/common/globalVars'
@@ -76,29 +75,12 @@ export class NTQQFileApi extends Service {
// 上传文件到QQ的文件夹
async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType = 0) {
const fileMd5 = await calculateFileMD5(filePath)
let ext = (await this.getFileType(filePath))?.ext || ''
if (ext) {
ext = '.' + ext
let fileName = path.basename(filePath)
if (!fileName.includes('.')) {
const ext = (await this.getFileType(filePath))?.ext
fileName += ext ? '.' + ext : ''
}
let fileName = `${path.basename(filePath)}`
if (fileName.indexOf('.') === -1) {
fileName += ext
}
const session = getSession()
let mediaPath: string
if (session) {
mediaPath = session?.getMsgService().getRichMediaFilePathForGuild({
md5HexStr: fileMd5,
fileName: fileName,
elementType: elementType,
elementSubType,
thumbSize: 0,
needCreate: true,
downloadType: 1,
file_uuid: ''
})
} else {
mediaPath = await invoke(NTMethod.MEDIA_FILE_PATH, [{
const mediaPath = await invoke(NTMethod.MEDIA_FILE_PATH, [{
path_info: {
md5HexStr: fileMd5,
fileName: fileName,
@@ -110,15 +92,13 @@ export class NTQQFileApi extends Service {
file_uuid: '',
},
}])
}
await fsPromise.copyFile(filePath, mediaPath)
const fileSize = (await fsPromise.stat(filePath)).size
await copyFile(filePath, mediaPath)
const fileSize = (await stat(filePath)).size
return {
md5: fileMd5,
fileName,
path: mediaPath,
fileSize,
ext
}
}
@@ -133,10 +113,10 @@ export class NTQQFileApi extends Service {
force = false
) {
// 用于下载收到的消息中的图片等
if (sourcePath && fs.existsSync(sourcePath)) {
if (sourcePath && existsSync(sourcePath)) {
if (force) {
try {
await fsPromise.unlink(sourcePath)
await unlink(sourcePath)
} catch { }
} else {
return sourcePath
@@ -177,7 +157,11 @@ export class NTQQFileApi extends Service {
}
async getImageSize(filePath: string) {
return await invoke<{ width: number; height: number }>(
return await invoke<{
width: number
height: number
type: string
}>(
NTMethod.IMAGE_SIZE,
[filePath],
{

View File

@@ -42,13 +42,7 @@ export class NTQQFriendApi extends Service {
return _friends
}
async handleFriendRequest(flag: string, accept: boolean) {
const data = flag.split('|')
if (data.length < 2) {
return
}
const friendUid = data[0]
const reqTime = data[1]
async handleFriendRequest(friendUid: string, reqTime: string, accept: boolean) {
const session = getSession()
if (session) {
return session.getBuddyService().approvalFriendRequest({
@@ -68,15 +62,6 @@ export class NTQQFriendApi extends Service {
}
async getBuddyV2(refresh = false): Promise<FriendV2[]> {
const session = getSession()
if (session) {
const uids: string[] = []
const buddyService = session.getBuddyService()
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids))
const data = await session.getProfileService().getCoreAndBaseInfo('nodeStore', uids)
return Array.from(data.values())
} else {
const data = await invoke<{
buddyCategory: CategoryFriend[]
userSimpleInfos: Record<string, SimpleInfo>
@@ -92,25 +77,10 @@ export class NTQQFriendApi extends Service {
const uids = data.buddyCategory.flatMap(item => item.buddyUids)
return Object.values(data.userSimpleInfos).filter(v => uids.includes(v.uid!))
}
}
/** uid => uin */
async getBuddyIdMap(refresh = false): Promise<Map<string, string>> {
const retMap: Map<string, string> = new Map()
const session = getSession()
if (session) {
const uids: string[] = []
const buddyService = session.getBuddyService()
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids))
const data = await session.getProfileService().getCoreAndBaseInfo('nodeStore', uids)
for (const [, item] of data) {
if (retMap.size > 5000) {
break
}
retMap.set(item.uid!, item.uin!)
}
} else {
const data = await invoke<{
buddyCategory: CategoryFriend[]
userSimpleInfos: Record<string, SimpleInfo>
@@ -129,7 +99,6 @@ export class NTQQFriendApi extends Service {
}
retMap.set(item.uid!, item.uin!)
}
}
return retMap
}
@@ -194,4 +163,10 @@ export class NTQQFriendApi extends Service {
const ret = await invoke('nodeIKernelBuddyService/getBuddyRecommendContactArkJson', [{ uin }, null])
return ret.arkMsg
}
async setBuddyRemark(uid: string, remark: string) {
return await invoke('nodeIKernelBuddyService/setBuddyRemark', [{
remarkParams: { uid, remark }
}, null])
}
}

View File

@@ -1,10 +1,18 @@
import { ReceiveCmdS } from '../hook'
import { Group, GroupMember, GroupMemberRole, GroupNotifies, GroupRequestOperateTypes, GetFileListParam, PublishGroupBulletinReq } from '../types'
import {
Group,
GroupMember,
GroupMemberRole,
GroupNotifies,
GroupRequestOperateTypes,
GetFileListParam,
OnGroupFileInfoUpdateParams,
PublishGroupBulletinReq
} from '../types'
import { invoke, NTClass, NTMethod } from '../ntcall'
import { GeneralCallResult } from '../services'
import { NTQQWindows } from './window'
import { getSession } from '../wrapper'
import { OnGroupFileInfoUpdateParams } from '../listeners'
import { NodeIKernelGroupService } from '../services'
import { Service, Context } from 'cordis'
import { isNumeric } from '@/common/utils/misc'
@@ -103,7 +111,6 @@ export class NTQQGroupApi extends Service {
'nodeIKernelGroupService/getSingleScreenNotifies',
[{ doubt: false, startSeq: '', number: num }, null],
{
cbCmd: ReceiveCmdS.GROUP_NOTIFY,
afterFirstCmd: false,
}
@@ -212,47 +219,51 @@ export class NTQQGroupApi extends Service {
}
async getGroupRemainAtTimes(groupCode: string) {
return await invoke<
GeneralCallResult & {
atInfo: {
canAtAll: boolean
RemainAtAllCountForUin: number
RemainAtAllCountForGroup: number
atTimesMsg: string
canNotAtAllMsg: ''
}
}
>(NTMethod.GROUP_AT_ALL_REMAIN_COUNT, [{ groupCode }, null])
return await invoke(NTMethod.GROUP_AT_ALL_REMAIN_COUNT, [{ groupCode }, null])
}
/** 27187 TODO */
async removeGroupEssence(groupCode: string, msgId: string) {
const session = getSession()
// 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
const data = await session?.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: groupCode }, msgId, 1, false)
const param = {
if (session) {
const data = await session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: groupCode }, msgId, 1, false)
return session.getGroupService().removeGroupEssence({
groupCode: groupCode,
msgRandom: Number(data?.msgList[0].msgRandom),
msgSeq: Number(data?.msgList[0].msgSeq)
})
} else {
const ntMsgApi = this.ctx.get('ntMsgApi')!
const data = await ntMsgApi.getMsgHistory({ chatType: 2, guildId: '', peerUid: groupCode }, msgId, 1, false)
return await invoke('nodeIKernelGroupService/removeGroupEssence', [{
req: {
groupCode: groupCode,
msgRandom: Number(data?.msgList[0].msgRandom),
msgSeq: Number(data?.msgList[0].msgSeq)
}
// GetMsgByShoretID(ShoretID) -> MsgService.getMsgs(Peer,MsgId,1,false) -> 组出参数
return session?.getGroupService().removeGroupEssence(param)
}, null])
}
}
/** 27187 TODO */
async addGroupEssence(groupCode: string, msgId: string) {
const session = getSession()
// 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
const data = await session?.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: groupCode }, msgId, 1, false)
const param = {
if (session) {
const data = await session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: groupCode }, msgId, 1, false)
return session.getGroupService().addGroupEssence({
groupCode: groupCode,
msgRandom: Number(data?.msgList[0].msgRandom),
msgSeq: Number(data?.msgList[0].msgSeq)
})
} else {
const ntMsgApi = this.ctx.get('ntMsgApi')!
const data = await ntMsgApi.getMsgHistory({ chatType: 2, guildId: '', peerUid: groupCode }, msgId, 1, false)
return await invoke('nodeIKernelGroupService/addGroupEssence', [{
req: {
groupCode: groupCode,
msgRandom: Number(data?.msgList[0].msgRandom),
msgSeq: Number(data?.msgList[0].msgSeq)
}
// GetMsgByShoretID(ShoretID) -> MsgService.getMsgs(Peer,MsgId,1,false) -> 组出参数
return session?.getGroupService().addGroupEssence(param)
}, null])
}
}
async createGroupFileFolder(groupId: string, folderName: string) {
@@ -303,4 +314,23 @@ export class NTQQGroupApi extends Service {
const ret = await invoke('nodeIKernelGroupService/getGroupRecommendContactArkJson', [{ groupCode }, null])
return ret.arkJson
}
async queryCachedEssenceMsg(groupCode: string, msgSeq = '0', msgRandom = '0') {
return await invoke('nodeIKernelGroupService/queryCachedEssenceMsg', [{
key: {
groupCode,
msgSeq: +msgSeq,
msgRandom: +msgRandom
}
}, null])
}
async getGroupHonorList(groupCode: string) {
// 还缺点东西
return await invoke('nodeIKernelGroupService/getGroupHonorList', [{
req: {
groupCode: [+groupCode]
}
}, null])
}
}

View File

@@ -1,6 +1,6 @@
import { invoke, NTMethod } from '../ntcall'
import { GeneralCallResult } from '../services'
import { RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
import { RawMessage, SendMessageElement, Peer, ChatType } from '../types'
import { getSession } from '@/ntqqapi/wrapper'
import { Service, Context } from 'cordis'
import { selfInfo } from '@/common/globalVars'
@@ -11,16 +11,6 @@ declare module 'cordis' {
}
}
function generateMsgId() {
const timestamp = Math.floor(Date.now() / 1000)
const random = Math.floor(Math.random() * Math.pow(2, 32))
const buffer = Buffer.alloc(8)
buffer.writeUInt32BE(timestamp, 0)
buffer.writeUInt32BE(random, 4)
const msgId = BigInt('0x' + buffer.toString('hex')).toString()
return msgId
}
export class NTQQMsgApi extends Service {
static inject = ['ntUserApi']
@@ -28,7 +18,7 @@ export class NTQQMsgApi extends Service {
super(ctx, 'ntMsgApi', true)
}
async getTempChatInfo(chatType: ChatType2, peerUid: string) {
async getTempChatInfo(chatType: ChatType, peerUid: string) {
const session = getSession()
if (session) {
return session.getMsgService().getTempChatInfo(chatType, peerUid)
@@ -60,7 +50,7 @@ export class NTQQMsgApi extends Service {
}
async activateChat(peer: Peer) {
return await invoke<GeneralCallResult>(NTMethod.ACTIVE_CHAT_PREVIEW, [{ peer, cnt: 20 }, null])
return await invoke<GeneralCallResult>(NTMethod.ACTIVE_CHAT_PREVIEW, [{ peer, cnt: 1 }, null])
}
async activateChatAndGetHistory(peer: Peer) {
@@ -102,7 +92,7 @@ export class NTQQMsgApi extends Service {
}
async sendMsg(peer: Peer, msgElements: SendMessageElement[], timeout = 10000) {
const msgId = generateMsgId()
const msgId = await this.generateMsgUniqueId(peer.chatType)
peer.guildId = msgId
const data = await invoke<{ msgList: RawMessage[] }>(
'nodeIKernelMsgService/sendMsg',
@@ -235,7 +225,7 @@ export class NTQQMsgApi extends Service {
}, null])
}
async queryMsgsWithFilterExBySeq(peer: Peer, msgSeq: string, filterMsgTime: string, filterSendersUid: string[]) {
async queryMsgsWithFilterExBySeq(peer: Peer, msgSeq: string, filterMsgTime: string, filterSendersUid: string[] = []) {
return await invoke('nodeIKernelMsgService/queryMsgsWithFilterEx', [{
msgId: '0',
msgTime: '0',
@@ -256,4 +246,27 @@ export class NTQQMsgApi extends Service {
async setMsgRead(peer: Peer) {
return await invoke('nodeIKernelMsgService/setMsgRead', [{ peer }, null])
}
async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number) {
return await invoke('nodeIKernelMsgService/getMsgEmojiLikesList', [{
peer,
msgSeq,
emojiId,
emojiType,
cnt: count
}, null])
}
async fetchFavEmojiList(count: number) {
return await invoke('nodeIKernelMsgService/fetchFavEmojiList', [{
resId: '',
count,
backwardFetch: true,
forceRefresh: true
}, null])
}
async generateMsgUniqueId(chatType: number) {
return await invoke('nodeIKernelMsgService/generateMsgUniqueId', [{ chatType }])
}
}

View File

@@ -102,18 +102,6 @@ export class NTQQUserApi extends Service {
return await invoke('nodeIKernelTipOffService/getPskey', [{ domains, isForNewPCQQ: true }, null])
}
genBkn(sKey: string) {
sKey = sKey || ''
let hash = 5381
for (let i = 0; i < sKey.length; i++) {
const code = sKey.charCodeAt(i)
hash = hash + (hash << 5) + code
}
return (hash & 0x7fffffff).toString()
}
async like(uid: string, count = 1) {
const session = getSession()
if (session) {

View File

@@ -17,88 +17,6 @@ export enum WebHonorType {
EMOTION = 'emotion'
}
export interface WebApiGroupMember {
uin: number
role: number
g: number
join_time: number
last_speak_time: number
lv: {
point: number
level: number
}
card: string
tags: string
flag: number
nick: string
qage: number
rm: number
}
interface WebApiGroupMemberRet {
ec: number
errcode: number
em: string
cache: number
adm_num: number
levelname: unknown
mems: WebApiGroupMember[]
count: number
svr_time: number
max_count: number
search_count: number
extmode: number
}
interface GroupEssenceMsg {
group_code: string
msg_seq: number
msg_random: number
sender_uin: string
sender_nick: string
sender_time: number
add_digest_uin: string
add_digest_nick: string
add_digest_time: number
msg_content: unknown[]
can_be_removed: true
}
export interface GroupEssenceMsgRet {
retcode: number
retmsg: string
data: {
msg_list: GroupEssenceMsg[]
is_end: boolean
group_role: number
config_page_url: string
}
}
interface SetGroupNoticeParams {
groupCode: string
content: string
pinned: number
type: number
isShowEditCard: number
tipWindowType: number
confirmRequired: number
picId: string
imgWidth?: number
imgHeight?: number
}
interface SetGroupNoticeRet {
ec: number
em: string
id: number
ltsm: number
new_fid: string
read_only: number
role: number
srv_code: number
}
export class NTQQWebApi extends Service {
static inject = ['ntUserApi']
@@ -106,47 +24,6 @@ export class NTQQWebApi extends Service {
super(ctx, 'ntWebApi', true)
}
async getGroupMembers(groupCode: string): Promise<WebApiGroupMember[]> {
const memberData: Array<WebApiGroupMember> = new Array<WebApiGroupMember>()
const cookieObject = await this.ctx.ntUserApi.getCookies('qun.qq.com')
const cookieStr = this.cookieToString(cookieObject)
const retList: Promise<WebApiGroupMemberRet>[] = []
const params = new URLSearchParams({
st: '0',
end: '40',
sort: '1',
gc: groupCode,
bkn: this.genBkn(cookieObject.skey)
})
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${params}`, 'POST', '', { 'Cookie': cookieStr })
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
return []
} else {
for (const member of fastRet.mems) {
memberData.push(member)
}
}
const pageNum = Math.ceil(fastRet.count / 40)
//遍历批量请求
for (let i = 2; i <= pageNum; i++) {
params.set('st', String((i - 1) * 40))
params.set('end', String(i * 40))
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${params}`, 'POST', '', { 'Cookie': cookieStr })
retList.push(ret)
}
//批量等待
for (let i = 1; i <= pageNum; i++) {
const ret = await (retList[i])
if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) {
continue
}
for (const member of ret.mems) {
memberData.push(member)
}
}
return memberData
}
genBkn(sKey: string) {
sKey = sKey || ''
let hash = 5381
@@ -157,10 +34,9 @@ export class NTQQWebApi extends Service {
return (hash & 0x7FFFFFFF).toString()
}
//实现未缓存 考虑2h缓存
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
const url = 'https://qun.qq.com/interactive/honorlist?gc=' + Internal_groupCode + '&type=' + Internal_type.toString()
async getGroupHonorInfo(groupCode: string, getType: string) {
const getDataInternal = async (groupCode: string, type: number) => {
const url = 'https://qun.qq.com/interactive/honorlist?gc=' + groupCode + '&type=' + type
let resJson
try {
const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': cookieStr })
@@ -168,7 +44,7 @@ export class NTQQWebApi extends Service {
if (match) {
resJson = JSON.parse(match[1].trim())
}
if (Internal_type === 1) {
if (type === 1) {
return resJson?.talkativeList
} else {
return resJson?.actorList
@@ -274,34 +150,6 @@ export class NTQQWebApi extends Service {
return honorInfo
}
async setGroupNotice(params: SetGroupNoticeParams): Promise<SetGroupNoticeRet> {
const cookieObject = await this.ctx.ntUserApi.getCookies('qun.qq.com')
const settings = JSON.stringify({
is_show_edit_card: params.isShowEditCard,
tip_window_type: params.tipWindowType,
confirm_required: params.confirmRequired
})
return await RequestUtil.HttpGetJson<SetGroupNoticeRet>(
`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?${new URLSearchParams({
bkn: this.genBkn(cookieObject.skey),
qid: params.groupCode,
text: params.content,
pinned: params.pinned.toString(),
type: params.type.toString(),
settings: settings,
...(params.picId !== '' && {
pic: params.picId,
imgWidth: params.imgWidth?.toString(),
imgHeight: params.imgHeight?.toString(),
})
})}`,
'POST',
'',
{ 'Cookie': this.cookieToString(cookieObject) }
)
}
private cookieToString(cookieObject: Dict) {
return Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ')
}

View File

@@ -1,7 +1,6 @@
import fs from 'node:fs'
import { unlink } from 'node:fs/promises'
import { Service, Context } from 'cordis'
import { registerCallHook, registerReceiveHook, ReceiveCmdS } from './hook'
import { MessageUnique } from '../common/utils/messageUnique'
import { Config as LLOBConfig } from '../common/types'
import { llonebotError } from '../common/globalVars'
import { isNumeric } from '../common/utils/misc'
@@ -14,7 +13,6 @@ import {
GroupMember,
CategoryFriend,
SimpleInfo,
User,
ChatType
} from './types'
import { selfInfo } from '../common/globalVars'
@@ -45,7 +43,6 @@ class Core extends Service {
public start() {
llonebotError.otherError = ''
MessageUnique.init(selfInfo.uin)
this.registerListener()
this.ctx.logger.info(`LLOneBot/${version}`)
this.ctx.on('llonebot/config-updated', input => {
@@ -55,29 +52,24 @@ class Core extends Service {
private registerListener() {
registerReceiveHook<{
data: CategoryFriend[]
data?: CategoryFriend[]
userSimpleInfos?: Map<string, SimpleInfo> //V2
buddyCategory?: CategoryFriend[] //V2
}>(ReceiveCmdS.FRIENDS, (payload) => {
type V2data = { userSimpleInfos: Map<string, SimpleInfo> }
let friendList: User[] = []
if ('userSimpleInfos' in payload) {
friendList = Object.values((payload as unknown as V2data).userSimpleInfos).map((v: SimpleInfo) => {
return {
...v.coreInfo,
let uids: string[] = []
if (payload.buddyCategory) {
uids = payload.buddyCategory.flatMap(item => item.buddyUids)
} else if (payload.data) {
uids = payload.data.flatMap(item => item.buddyList.map(e => e.uid))
}
})
} else {
for (const fData of payload.data) {
friendList.push(...fData.buddyList)
}
}
this.ctx.logger.info('好友列表变动', friendList.length)
for (const friend of friendList) {
this.ctx.ntMsgApi.activateChat({ peerUid: friend.uid, chatType: ChatType.friend })
for (const uid of uids) {
this.ctx.ntMsgApi.activateChat({ peerUid: uid, chatType: ChatType.friend })
}
this.ctx.logger.info('好友列表变动', uids.length)
})
// 自动清理新消息文件
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => {
registerReceiveHook<{ msgList: RawMessage[] }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => {
if (!this.config.autoDeleteFile) {
return
}
@@ -96,9 +88,7 @@ class Core extends Service {
}
for (const path of pathList) {
if (path) {
fs.unlink(path, () => {
this.ctx.logger.info('删除文件成功', path)
})
unlink(path).then(() => this.ctx.logger.info('删除文件成功', path))
}
}
}, this.config.autoDeleteFileSecond! * 1000)

View File

@@ -16,7 +16,7 @@ import {
SendVideoElement,
} from './types'
import { stat, writeFile, copyFile, unlink } from 'node:fs/promises'
import { calculateFileMD5, isGIF } from '../common/utils/file'
import { calculateFileMD5 } from '../common/utils/file'
import { defaultVideoThumb, getVideoInfo } from '../common/utils/video'
import { encodeSilk } from '../common/utils/audio'
import { Context } from 'cordis'
@@ -66,14 +66,10 @@ export namespace SendElementEntities {
}
}
export async function pic(ctx: Context, picPath: string, summary: string = '', subType: 0 | 1 = 0): Promise<SendPicElement> {
export async function pic(ctx: Context, picPath: string, summary = '', subType: 0 | 1 = 0, isFlashPic?: boolean): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await ctx.ntFileApi.uploadFile(picPath, ElementType.PIC, subType)
if (fileSize === 0) {
throw '文件异常大小为0'
}
const maxMB = 30;
if (fileSize > 1024 * 1024 * 30) {
throw `图片过大,最大支持${maxMB}MB当前文件大小${fileSize}B`
throw '文件异常,大小为 0'
}
const imageSize = await ctx.ntFileApi.getImageSize(picPath)
const picElement = {
@@ -84,12 +80,13 @@ export namespace SendElementEntities {
fileName: fileName,
sourcePath: path,
original: true,
picType: isGIF(picPath) ? PicType.gif : PicType.jpg,
picType: imageSize.type === 'gif' ? PicType.gif : PicType.jpg,
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary,
isFlashPic,
}
ctx.logger.info('图片信息', picElement)
return {
@@ -365,4 +362,16 @@ export namespace SendElementEntities {
},
}
}
export function shake(): SendFaceElement {
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: 1,
faceType: 5,
pokeType: 1,
},
}
}
}

View File

@@ -1,58 +0,0 @@
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/ntqqapi/types'
export interface IGroupListener {
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void
onGroupExtListUpdate(...args: unknown[]): void
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]): void
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): void
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void
onGroupDetailInfoChange(...args: unknown[]): void
onGroupAllInfoChange(...args: unknown[]): void
onGroupsMsgMaskResult(...args: unknown[]): void
onGroupConfMemberChange(...args: unknown[]): void
onGroupBulletinChange(...args: unknown[]): void
onGetGroupBulletinListResult(...args: unknown[]): void
onMemberListChange(arg: {
sceneId: string,
ids: string[],
infos: Map<string, GroupMember>,
finish: boolean,
hasRobot: boolean
}): void
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void
onSearchMemberChange(...args: unknown[]): void
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void
onGroupStatisticInfoChange(...args: unknown[]): void
onJoinGroupNotify(...args: unknown[]): void
onShutUpMemberListChanged(...args: unknown[]): void
onGroupBulletinRemindNotify(...args: unknown[]): void
onGroupFirstBulletinNotify(...args: unknown[]): void
onJoinGroupNoVerifyFlag(...args: unknown[]): void
onGroupArkInviteStateResult(...args: unknown[]): void
// 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void
}

View File

@@ -1,268 +0,0 @@
import { ChatType, RawMessage } from '@/ntqqapi/types'
export interface OnRichMediaDownloadCompleteParams {
fileModelId: string,
msgElementId: string,
msgId: string,
fileId: string,
fileProgress: string, // '0'
fileSpeed: string, // '0'
fileErrCode: string, // '0'
fileErrMsg: string,
fileDownType: number, // 暂时未知
thumbSize: number,
filePath: string,
totalSize: string,
trasferStatus: number,
step: number,
commonFileInfo: unknown | null,
fileSrvErrCode: string,
clientMsg: string,
businessId: number,
userTotalSpacePerDay: unknown | null,
userUsedSpacePerDay: unknown | null
}
export interface OnGroupFileInfoUpdateParams {
retCode: number
retMsg: string
clientWording: string
isEnd: boolean
item: {
peerId: string
type: number
folderInfo?: {
folderId: string
parentFolderId: string
folderName: string
createTime: number
modifyTime: number
createUin: string
creatorName: string
totalFileCount: number
modifyUin: string
modifyName: string
usedSpace: string
}
fileInfo?: {
fileModelId: string
fileId: string
fileName: string
fileSize: string
busId: number
uploadedSize: string
uploadTime: number
deadTime: number
modifyTime: number
downloadTimes: number
sha: string
sha3: string
md5: string
uploaderLocalPath: string
uploaderName: string
uploaderUin: string
parentFolderId: string
localPath: string
transStatus: number
transType: number
elementId: string
isFolder: boolean
}
}[]
allFileCount: number
nextIndex: number
reqId: number
}
// {
// sessionType: 1,
// chatType: 100,
// peerUid: 'u_PVQ3tl6K78xxxx',
// groupCode: '809079648',
// fromNick: '拾xxxx,
// sig: '0x'
// }
export interface TempOnRecvParams {
sessionType: number,//1
chatType: ChatType,//100
peerUid: string,//uid
groupCode: string,//gc
fromNick: string,//gc name
sig: string,
}
export interface IKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage): void
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): void
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): void
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): void
onContactUnreadCntUpdate(hashMap: unknown): void
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): void
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): void
onEmojiDownloadComplete(emojiNotifyInfo: unknown): void
onEmojiResourceUpdate(emojiResourceInfo: unknown): void
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void
onFileMsgCome(arrayList: unknown): void
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): void
onFirstViewGroupGuildMapping(arrayList: unknown): void
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): void
onGroupFileInfoAdd(groupItem: unknown): void
onGroupFileInfoUpdate(groupFileListResult: OnGroupFileInfoUpdateParams): void
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): void
onGroupTransferInfoAdd(groupItem: unknown): void
onGroupTransferInfoUpdate(groupFileListResult: unknown): void
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): void
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): void
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): void
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): void
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): void
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): void
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): void
onInputStatusPush(inputStatusInfo: unknown): void
onKickedOffLine(kickedInfo: unknown): void
onLineDev(arrayList: unknown): void
onLogLevelChanged(j2: unknown): void
onMsgAbstractUpdate(arrayList: unknown): void
onMsgBoxChanged(arrayList: unknown): void
onMsgDelete(contact: unknown, arrayList: unknown): void
onMsgEventListUpdate(hashMap: unknown): void
onMsgInfoListAdd(arrayList: unknown): void
onMsgInfoListUpdate(msgList: RawMessage[]): void
onMsgQRCodeStatusChanged(i2: unknown): void
onMsgRecall(i2: unknown, str: unknown, j2: unknown): void
onMsgSecurityNotify(msgRecord: unknown): void
onMsgSettingUpdate(msgSetting: unknown): void
onNtFirstViewMsgSyncEnd(): void
onNtMsgSyncEnd(): void
onNtMsgSyncStart(): void
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void
onRecvGroupGuildFlag(i2: unknown): void
onRecvMsg(...arrayList: unknown[]): void
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): void
onRecvOnlineFileMsg(arrayList: unknown): void
onRecvS2CMsg(arrayList: unknown): void
onRecvSysMsg(arrayList: unknown): void
onRecvUDCFlag(i2: unknown): void
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): void
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): void
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void
onSearchGroupFileInfoUpdate(searchGroupFileResult:
{
result: {
retCode: number,
retMsg: string,
clientWording: string
},
syncCookie: string,
totalMatchCount: number,
ownerMatchCount: number,
isEnd: boolean,
reqId: number,
item: Array<{
groupCode: string,
groupName: string,
uploaderUin: string,
uploaderName: string,
matchUin: string,
matchWords: Array<unknown>,
fileNameHits: Array<{
start: number,
end: number
}>,
fileModelId: string,
fileId: string,
fileName: string,
fileSize: string,
busId: number,
uploadTime: number,
modifyTime: number,
deadTime: number,
downloadTimes: number,
localPath: string
}>
}): void
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): void
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): void
onUnreadCntAfterFirstView(hashMap: unknown): void
onUnreadCntUpdate(hashMap: unknown): void
onUserChannelTabStatusChanged(z: unknown): void
onUserOnlineStatusChanged(z: unknown): void
onUserTabStatusChanged(arrayList: unknown): void
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void
// 第一次发现于Linux
onUserSecQualityChanged(...args: unknown[]): void
onMsgWithRichLinkInfoUpdate(...args: unknown[]): void
onRedTouchChanged(...args: unknown[]): void
// 第一次发现于Win 9.9.9 23159
onBroadcastHelperProgerssUpdate(...args: unknown[]): void
}

View File

@@ -1,15 +0,0 @@
import { User, UserDetailInfoListenerArg } from '@/ntqqapi/types'
export interface IProfileListener {
onProfileSimpleChanged(...args: unknown[]): void
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void
onProfileDetailInfoChanged(profile: User): void
onStatusUpdate(...args: unknown[]): void
onSelfStatusChanged(...args: unknown[]): void
onStrangerRemarkChanged(...args: unknown[]): void
}

View File

@@ -1,3 +0,0 @@
export * from './NodeIKernelProfileListener'
export * from './NodeIKernelGroupListener'
export * from './NodeIKernelMsgListener'

View File

@@ -13,8 +13,7 @@ import {
NodeIKernelUixConvertService,
NodeIKernelRichMediaService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
NodeIKernelSearchService,
NodeIKernelTipOffService
} from './services'
export enum NTClass {
@@ -94,7 +93,6 @@ interface NTService {
nodeIKernelRichMediaService: NodeIKernelRichMediaService
nodeIKernelTicketService: NodeIKernelTicketService
nodeIKernelTipOffService: NodeIKernelTipOffService
nodeIKernelSearchService: NodeIKernelSearchService
}
interface InvokeOptions<ReturnType> {
@@ -123,11 +121,15 @@ export function invoke<
return new Promise<R>((resolve, reject) => {
const apiArgs = [method, ...args]
const callbackId = randomUUID()
let success = false
const timeoutId = setTimeout(() => {
log(`ntqq api timeout ${channel}, ${eventName}, ${method}`, apiArgs)
reject(`ntqq api timeout ${channel}, ${eventName}, ${method}, ${apiArgs}`)
}, timeout)
if (!options.cbCmd) {
// QQ后端会返回结果并且可以根据uuid识别
hookApiCallbacks[callbackId] = res => {
success = true
clearTimeout(timeoutId)
resolve(res)
}
}
@@ -139,13 +141,13 @@ export function invoke<
if (options.cmdCB) {
if (options.cmdCB(payload, result)) {
removeReceiveHook(hookId)
success = true
clearTimeout(timeoutId)
resolve(payload)
}
}
else {
removeReceiveHook(hookId)
success = true
clearTimeout(timeoutId)
resolve(payload)
}
})
@@ -158,16 +160,11 @@ export function invoke<
}
else {
log('ntqq api call failed,', method, res)
clearTimeout(timeoutId)
reject(`ntqq api call failed, ${method}, ${res.errMsg}`)
}
}
}
setTimeout(() => {
if (!success) {
log(`ntqq api timeout ${channel}, ${eventName}, ${method}`, apiArgs)
reject(`ntqq api timeout ${channel}, ${eventName}, ${method}, ${apiArgs}`)
}
}, timeout)
ipcMain.emit(
channel,

View File

@@ -6,120 +6,26 @@ export enum BuddyListReqType {
}
export interface NodeIKernelBuddyService {
// 26702 以上
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult & {
data: Array<{
categoryId: number,
categorySortId: number,
categroyName: string,
categroyMbCount: number,
onlineCount: number,
buddyUids: Array<string>
data: {
categoryId: number
categorySortId: number
categroyName: string
categroyMbCount: number
onlineCount: number
buddyUids: string[]
}[]
}>
}>
//26702 以上
getBuddyListFromCache(callFrom: string): Promise<Array<
{
categoryId: number,//9999应该跳过 那是兜底数据吧
categorySortId: number,//排序方式
categroyName: string,//分类名
categroyMbCount: number,//不懂
onlineCount: number,//在线数目
buddyUids: Array<string>//Uids
}>>
addKernelBuddyListener(listener: unknown): number
getAllBuddyCount(): number
removeKernelBuddyListener(listener: unknown): void
getBuddyList(nocache: boolean): Promise<GeneralCallResult>
getBuddyNick(uid: number): string
getBuddyRemark(uid: number): string
setBuddyRemark(uid: number, remark: string): void
getAvatarUrl(uid: number): string
isBuddy(uid: string): boolean
getCategoryNameWithUid(uid: number): string
getTargetBuddySetting(uid: number): unknown
getTargetBuddySettingByType(uid: number, type: number): unknown
getBuddyReqUnreadCnt(): number
getBuddyReq(): unknown
delBuddyReq(uid: number): void
clearBuddyReqUnreadCnt(): void
reqToAddFriends(uid: number, msg: string): void
setSpacePermission(uid: number, permission: number): void
approvalFriendRequest(arg: {
friendUid: string
reqTime: string
accept: boolean
}): Promise<void>
delBuddy(uid: number): void
delBatchBuddy(uids: number[]): void
getSmartInfos(uid: number): unknown
setBuddyCategory(uid: number, category: number): void
setBatchBuddyCategory(uids: number[], category: number): void
addCategory(category: string): void
delCategory(category: string): void
renameCategory(oldCategory: string, newCategory: string): void
resortCategory(categorys: string[]): void
pullCategory(uid: number, category: string): void
setTop(uid: number, isTop: boolean): void
SetSpecialCare(uid: number, isSpecialCare: boolean): void
setMsgNotify(uid: number, isNotify: boolean): void
hasBuddyList(): boolean
setBlock(uid: number, isBlock: boolean): void
isBlocked(uid: number): boolean
modifyAddMeSetting(setting: unknown): void
getAddMeSetting(): unknown
getDoubtBuddyReq(): unknown
getDoubtBuddyUnreadNum(): number
approvalDoubtBuddyReq(uid: number, isAgree: boolean): void
delDoubtBuddyReq(uid: number): void
delAllDoubtBuddyReq(): void
reportDoubtBuddyReqUnread(): void
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<GeneralCallResult & { arkMsg: string }>
isNull(): boolean
}

View File

@@ -1,207 +1,99 @@
import {
GroupExtParam,
GroupMember,
GroupMemberRole,
GroupNotifyType,
GroupRequestOperateTypes,
} from '@/ntqqapi/types'
import { GeneralCallResult } from './common'
import { Dict } from 'cosmokit'
export interface NodeIKernelGroupService {
getMemberCommonInfo(Req: {
groupCode: string,
startUin: string,
identifyFlag: string,
uinList: string[],
memberCommonFilter: {
memberUin: number,
uinFlag: number,
uinFlagExt: number,
uinMobileFlag: number,
shutUpTime: number,
privilege: number,
},
memberNum: number,
filterMethod: string,
onlineFlag: string,
realSpecialTitleFlag: number
}): Promise<unknown>
//26702
getGroupMemberLevelInfo(groupCode: string): Promise<unknown>
//26702
getGroupHonorList(groupCodes: Array<string>): unknown
getGroupHonorList(req: { groupCode: number[] }): Promise<{
errCode: number
errMsg: string
groupMemberHonorList: {
honorList: {
groupCode: string
id: number[]
isGray: number
}[]
cacheTs: number
honorInfos: unknown[]
joinTime: number
}
}>
getUinByUids(uins: string[]): Promise<{
errCode: number,
errMsg: string,
errCode: number
errMsg: string
uins: Map<string, string>
}>
getUidByUins(uins: string[]): Promise<{
errCode: number,
errMsg: string,
errCode: number
errMsg: string
uids: Map<string, string>
}>
//26702(其实更早 但是我不知道)
checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>
//26702(其实更早 但是我不知道)
getGroupLatestEssenceList(groupCode: string): Promise<unknown>
//26702(其实更早 但是我不知道)
shareDigest(Req: {
appId: string,
appType: number,
msgStyle: number,
recvUin: string,
sendType: number,
clientInfo: {
platform: number
},
richMsg: {
usingArk: boolean,
title: string,
summary: string,
url: string,
pictureUrl: string,
brief: string
}
}): Promise<unknown>
//26702(其实更早 但是我不知道)
isEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>
//26702(其实更早 但是我不知道)
queryCachedEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>
//26702(其实更早 但是我不知道)
fetchGroupEssenceList(Req: { groupCode: string, pageStart: number, pageLimit: number }, Arg: unknown): Promise<unknown>
//26702
getAllMemberList(groupCode: string, forceFetch: boolean): Promise<{
errCode: number,
errMsg: string,
result: {
ids: Array<{
uid: string,
index: number//0
}>,
infos: Dict,
finish: true,
hasRobot: false
}
queryCachedEssenceMsg(req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<{
items: {
groupCode: string
msgSeq: number
msgRandom: number
msgSenderUin: string
msgSenderNick: string
opType: number
opUin: string
opNick: string
opTime: number
grayTipSeq: string
}[]
}>
setHeader(uid: string, path: string): unknown
addKernelGroupListener(listener: unknown): number
removeKernelGroupListener(listenerId: unknown): void
createMemberListScene(groupCode: string, scene: string): string
destroyMemberListScene(SceneId: string): void
//About Arg (a) name: lastId 根据手Q来看为object {index:?(number),uid:string}
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
errCode: number, errMsg: string,
result: { ids: string[], infos: Map<string, GroupMember>, finish: boolean, hasRobot: boolean }
errCode: number
errMsg: string
result: {
ids: {
uid: string
index: number
}[]
infos: Map<string, GroupMember>
finish: boolean
hasRobot: boolean
}
}>
getPrevMemberList(): unknown
monitorMemberList(): unknown
searchMember(sceneId: string, keywords: string[]): unknown
getMemberInfo(group_id: string, uids: string[], forceFetch: boolean): Promise<GeneralCallResult>
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>
modifyMemberRole(groupCode: string, uid: string, role: GroupMemberRole): void
modifyMemberCardName(groupCode: string, uid: string, cardName: string): void
getTransferableMemberInfo(groupCode: string): unknown//获取整个群的
transferGroup(uid: string): void
getGroupList(force: boolean): Promise<GeneralCallResult>
getGroupExtList(force: boolean): Promise<GeneralCallResult>
getGroupDetailInfo(groupCode: string): unknown
getMemberExtInfo(param: GroupExtParam): Promise<unknown>//req
getGroupAllInfo(): unknown
getDiscussExistInfo(): unknown
getGroupConfMember(): unknown
getGroupMsgMask(): unknown
getGroupPortrait(): void
modifyGroupName(groupCode: string, groupName: string, arg: false): void
modifyGroupRemark(groupCode: string, remark: string): void
modifyGroupDetailInfo(groupCode: string, arg: unknown): void
setGroupMsgMask(groupCode: string, arg: unknown): void
changeGroupShieldSettingTemp(groupCode: string, arg: unknown): void
inviteToGroup(arg: unknown): void
inviteMembersToGroup(args: unknown[]): void
inviteMembersToGroupWithMsg(args: unknown): void
createGroup(arg: unknown): void
createGroupWithMembers(arg: unknown): void
quitGroup(groupCode: string): void
destroyGroup(groupCode: string): void
//获取单屏群通知列表
getSingleScreenNotifies(force: boolean, start_seq: string, num: number): Promise<GeneralCallResult>
clearGroupNotifies(groupCode: string): void
getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>
clearGroupNotifiesUnreadCount(groupCode: string): void
getSingleScreenNotifies(force: boolean, startSeq: string, num: number): Promise<GeneralCallResult>
operateSysNotify(
doubt: boolean,
operateMsg: {
operateType: GroupRequestOperateTypes, // 2 拒绝
operateType: GroupRequestOperateTypes // 2 拒绝
targetMsg: {
seq: string, // 通知序列号
type: GroupNotifyType,
groupCode: string,
seq: string // 通知序列号
type: GroupNotifyType
groupCode: string
postscript: string
}
}): Promise<void>
setTop(groupCode: string, isTop: boolean): void
getGroupBulletin(groupCode: string): unknown
deleteGroupBulletin(groupCode: string, seq: string): void
}
): Promise<void>
publishGroupBulletin(groupCode: string, pskey: string, data: unknown): Promise<GeneralCallResult>
publishInstructionForNewcomers(groupCode: string, arg: unknown): void
uploadGroupBulletinPic(groupCode: string, pskey: string, imagePath: string): Promise<{
errCode: number
errMsg: string
@@ -212,45 +104,23 @@ export interface NodeIKernelGroupService {
}
}>
downloadGroupBulletinRichMedia(groupCode: string): unknown
getGroupBulletinList(groupCode: string): unknown
getGroupStatisticInfo(groupCode: string): unknown
getGroupRemainAtTimes(groupCode: string): number
getJoinGroupNoVerifyFlag(groupCode: string): unknown
getGroupArkInviteState(groupCode: string): unknown
reqToJoinGroup(groupCode: string, arg: unknown): void
getGroupRemainAtTimes(groupCode: string): Promise<GeneralCallResult & {
atInfo: {
canAtAll: boolean
RemainAtAllCountForUin: number
RemainAtAllCountForGroup: number
atTimesMsg: string
canNotAtAllMsg: ''
}
}>
setGroupShutUp(groupCode: string, shutUp: boolean): void
getGroupShutUpMemberList(groupCode: string): unknown[]
setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<void>
getGroupRecommendContactArkJson(groupCode: string): Promise<GeneralCallResult & { arkJson: string }>
getJoinGroupLink(groupCode: string): unknown
addGroupEssence(param: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>
modifyGroupExtInfo(groupCode: string, arg: unknown): void
//需要提前判断是否存在 高版本新增
addGroupEssence(param: {
groupCode: string
msgRandom: number,
msgSeq: number
}): Promise<unknown>
//需要提前判断是否存在 高版本新增
removeGroupEssence(param: {
groupCode: string
msgRandom: number,
msgSeq: number
}): Promise<unknown>
isNull(): boolean
removeGroupEssence(param: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>
}

View File

@@ -1,15 +1,14 @@
import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/ntqqapi/types'
import { GeneralCallResult } from './common'
import { Dict } from 'cosmokit'
export interface QueryMsgsParams {
chatInfo: Peer,
filterMsgType: [],
filterSendersUid: string[],
filterMsgFromTime: string,
filterMsgToTime: string,
pageLimit: number,
isReverseOrder: boolean,
chatInfo: Peer
filterMsgType: []
filterSendersUid: string[]
filterMsgFromTime: string
filterMsgToTime: string
pageLimit: number
isReverseOrder: boolean
isIncludeCurrent: boolean
}
@@ -31,713 +30,89 @@ export interface TmpChatInfo {
export interface NodeIKernelMsgService {
generateMsgUniqueId(chatType: number, time: string): string
addKernelMsgListener(nodeIKernelMsgListener: unknown): number
sendMsg(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<unknown, unknown>): Promise<GeneralCallResult>
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>
addKernelMsgImportToolListener(arg: Dict): unknown
removeKernelMsgListener(args: unknown): unknown
addKernelTempChatSigListener(...args: unknown[]): unknown
removeKernelTempChatSigListener(...args: unknown[]): unknown
setAutoReplyTextList(AutoReplyText: Array<unknown>, i2: number): unknown
getAutoReplyTextList(...args: unknown[]): unknown
getOnLineDev(): void
kickOffLine(DevInfo: Dict): unknown
setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>
fetchStatusMgrInfo(): unknown
fetchStatusUnitedConfigInfo(): unknown
getOnlineStatusSmallIconBasePath(): unknown
getOnlineStatusSmallIconFileNameByUrl(Url: string): unknown
downloadOnlineStatusSmallIconByUrl(arg0: number, arg1: string): unknown
getOnlineStatusBigIconBasePath(): unknown
downloadOnlineStatusBigIconByUrl(arg0: number, arg1: string): unknown
getOnlineStatusCommonPath(arg: string): unknown
getOnlineStatusCommonFileNameByUrl(Url: string): unknown
downloadOnlineStatusCommonByUrl(arg0: string, arg1: string): unknown
// this.tokenType = i2
// this.apnsToken = bArr
// this.voipToken = bArr2
// this.profileId = str
setToken(arg: Dict): unknown
switchForeGround(): unknown
switchBackGround(arg: Dict): unknown
//hex
setTokenForMqq(token: string): unknown
switchForeGroundForMqq(...args: unknown[]): unknown
switchBackGroundForMqq(...args: unknown[]): unknown
getMsgSetting(...args: unknown[]): unknown
setMsgSetting(...args: unknown[]): unknown
addSendMsg(...args: unknown[]): unknown
cancelSendMsg(...args: unknown[]): unknown
switchToOfflineSendMsg(peer: Peer, MsgId: string): unknown
reqToOfflineSendMsg(...args: unknown[]): unknown
refuseReceiveOnlineFileMsg(peer: Peer, MsgId: string): unknown
resendMsg(...args: unknown[]): unknown
reeditRecallMsg(...args: unknown[]): unknown
//调用请检查除开commentElements其余参数不能为null
forwardMsg(msgIds: string[], srcContact: Peer, dstContacts: Peer[], commentElements: MessageElement[]): Promise<GeneralCallResult>
forwardMsgWithComment(...args: unknown[]): Promise<GeneralCallResult>
forwardSubMsgWithComment(...args: unknown[]): unknown
forwardRichMsgInVist(...args: unknown[]): unknown
forwardFile(...args: unknown[]): unknown
//Array<Msg>, Peer from, Peer to
multiForwardMsg(...args: unknown[]): unknown
multiForwardMsgWithComment(...args: unknown[]): unknown
deleteRecallMsg(...args: unknown[]): unknown
getAioFirstViewLatestMsgs(peer: Peer, num: number): Promise<GeneralCallResult & { msgList: RawMessage[] }>
deleteRecallMsgForLocal(...args: unknown[]): unknown
addLocalGrayTipMsg(...args: unknown[]): unknown
addLocalJsonGrayTipMsg(...args: unknown[]): unknown
addLocalJsonGrayTipMsgExt(...args: unknown[]): unknown
IsLocalJsonTipValid(...args: unknown[]): unknown
addLocalAVRecordMsg(...args: unknown[]): unknown
addLocalTofuRecordMsg(...args: unknown[]): unknown
addLocalRecordMsg(Peer: Peer, msgId: string, ele: MessageElement, attr: Array<unknown> | number, front: boolean): Promise<unknown>
deleteMsg(Peer: Peer, msgIds: Array<string>): Promise<unknown>
updateElementExtBufForUI(...args: unknown[]): unknown
updateMsgRecordExtPbBufForUI(...args: unknown[]): unknown
startMsgSync(...args: unknown[]): unknown
startGuildMsgSync(...args: unknown[]): unknown
isGuildChannelSync(...args: unknown[]): unknown
getMsgUniqueId(UniqueId: string): string
isMsgMatched(...args: unknown[]): unknown
getOnlineFileMsgs(...args: unknown[]): unknown
getAllOnlineFileMsgs(...args: unknown[]): unknown
getLatestDbMsgs(peer: Peer, cnt: number): Promise<unknown>
getLastMessageList(peer: Peer[]): Promise<unknown>
getAioFirstViewLatestMsgs(peer: Peer, num: number): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>
// this.$peer = contact
// this.$msgTime = j2
// this.$clientSeq = j3
// this.$cnt = i2
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getMsgsWithStatus(params: {
peer: Peer
msgId: string
msgTime: unknown
cnt: unknown
queryOrder: boolean
isIncludeSelf: boolean
appid: unknown
}): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getMsgsByMsgId(peer: Peer, ids: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getRecallMsgsByMsgId(peer: Peer, MsgId: string[]): Promise<unknown>
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getSingleMsg(peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown
getSourceOfReplyMsgV2(peer: Peer, RootMsgId: string, ReplyMsgId: string): unknown
getMsgByClientSeqAndTime(peer: Peer, clientSeq: string, time: string): unknown
getSourceOfReplyMsgByClientSeqAndTime(peer: Peer, clientSeq: string, time: string): unknown
//cnt clientSeq?并不是吧
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilter: { type: number, subtype: Array<number> }): unknown
getMsgsByTypeFilters(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: Array<{ type: number, subtype: Array<number> }>): unknown
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown
queryMsgsWithFilter(...args: unknown[]): unknown
/**
* @deprecated 该函数已被标记为废弃,请使用新的替代方法。
* 使用过滤条件查询消息列表的版本2接口。
*
* 该函数通过一系列过滤条件来查询特定聊天中的消息列表。这些条件包括消息类型、发送者、时间范围等。
* 函数返回一个Promise解析为查询结果的未知类型对象。
*
* @param MsgId 消息ID用于特定消息的查询。
* @param MsgTime 消息时间,用于指定消息的时间范围。
* @param param 查询参数对象,包含详细的过滤条件和分页信息。
* @param param.chatInfo 聊天信息包括聊天类型和对方用户ID。
* @param param.filterMsgType 需要过滤的消息类型数组,留空表示不过滤。
* @param param.filterSendersUid 需要过滤的发送者用户ID数组。
* @param param.filterMsgFromTime 查询消息的起始时间。
* @param param.filterMsgToTime 查询消息的结束时间。
* @param param.pageLimit 每页的消息数量限制。
* @param param.isReverseOrder 是否按时间顺序倒序返回消息。
* @param param.isIncludeCurrent 是否包含当前页码。
* @returns 返回一个Promise解析为查询结果的未知类型对象。
*/
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: QueryMsgsParams): Promise<unknown>
// this.chatType = i2
// this.peerUid = str
// this.chatInfo = new ChatInfo()
// this.filterMsgType = new ArrayList<>()
// this.filterSendersUid = new ArrayList<>()
// this.chatInfo = chatInfo
// this.filterMsgType = arrayList
// this.filterSendersUid = arrayList2
// this.filterMsgFromTime = j2
// this.filterMsgToTime = j3
// this.pageLimit = i2
// this.isReverseOrder = z
// this.isIncludeCurrent = z2
//queryMsgsWithFilterEx(0L, 0L, 0L, new QueryMsgsParams(new ChatInfo(2, str), new ArrayList(), new ArrayList(), 0L, 0L, 250, false, true))
queryMsgsWithFilterEx(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
queryFileMsgsDesktop(...args: unknown[]): unknown
setMsgRichInfoFlag(...args: unknown[]): unknown
queryPicOrVideoMsgs(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<unknown>
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown
queryEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>
queryTroopEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>
queryMsgsAndAbstractsWithFilter(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): unknown
setFocusOnGuild(...args: unknown[]): unknown
setFocusSession(...args: unknown[]): unknown
enableFilterUnreadInfoNotify(...args: unknown[]): unknown
enableFilterMsgAbstractNotify(...args: unknown[]): unknown
onScenesChangeForSilenceMode(...args: unknown[]): unknown
getContactUnreadCnt(...args: unknown[]): unknown
getUnreadCntInfo(...args: unknown[]): unknown
getGuildUnreadCntInfo(...args: unknown[]): unknown
getGuildUnreadCntTabInfo(...args: unknown[]): unknown
getAllGuildUnreadCntInfo(...args: unknown[]): unknown
getAllJoinGuildCnt(...args: unknown[]): unknown
getAllDirectSessionUnreadCntInfo(...args: unknown[]): unknown
getCategoryUnreadCntInfo(...args: unknown[]): unknown
getGuildFeedsUnreadCntInfo(...args: unknown[]): unknown
setUnVisibleChannelCntInfo(...args: unknown[]): unknown
setUnVisibleChannelTypeCntInfo(...args: unknown[]): unknown
setVisibleGuildCntInfo(...args: unknown[]): unknown
setMsgRead(peer: Peer): Promise<GeneralCallResult>
setAllC2CAndGroupMsgRead(): Promise<unknown>
setGuildMsgRead(...args: unknown[]): unknown
setAllGuildMsgRead(...args: unknown[]): unknown
setMsgReadAndReport(...args: unknown[]): unknown
setSpecificMsgReadAndReport(...args: unknown[]): unknown
setLocalMsgRead(...args: unknown[]): unknown
setGroupGuildMsgRead(...args: unknown[]): unknown
getGuildGroupTransData(...args: unknown[]): unknown
setGroupGuildBubbleRead(...args: unknown[]): unknown
getGuildGroupBubble(...args: unknown[]): unknown
fetchGroupGuildUnread(...args: unknown[]): unknown
setGroupGuildFlag(...args: unknown[]): unknown
setGuildUDCFlag(...args: unknown[]): unknown
setGuildTabUserFlag(...args: unknown[]): unknown
setBuildMode(flag: number/*0 1 3*/): unknown
setConfigurationServiceData(...args: unknown[]): unknown
setMarkUnreadFlag(...args: unknown[]): unknown
getChannelEventFlow(...args: unknown[]): unknown
getMsgEventFlow(...args: unknown[]): unknown
getRichMediaFilePathForMobileQQSend(...args: unknown[]): unknown
getRichMediaFilePathForGuild(arg: {
md5HexStr: string,
fileName: string,
elementType: ElementType,
elementSubType: number,
thumbSize: 0,
needCreate: true,
downloadType: 1,
md5HexStr: string
fileName: string
elementType: ElementType
elementSubType: number
thumbSize: 0
needCreate: true
downloadType: 1
file_uuid: ''
}): string
assembleMobileQQRichMediaFilePath(...args: unknown[]): unknown
getFileThumbSavePathForSend(...args: unknown[]): unknown
getFileThumbSavePath(...args: unknown[]): unknown
//猜测居多
translatePtt2Text(MsgId: string, Peer: Dict, MsgElement: Dict): unknown
setPttPlayedState(...args: unknown[]): unknown
// NodeIQQNTWrapperSession fetchFavEmojiList [
// "",
// 48,
// true,
// true
// ]
fetchFavEmojiList(str: string, num: number, uk1: boolean, uk2: boolean): Promise<GeneralCallResult & {
emojiInfoList: Array<{
uin: string,
emoId: number,
emoPath: string,
isExist: boolean,
resId: string,
url: string,
md5: string,
emoOriginalPath: string,
thumbPath: string,
RomaingType: string,
isAPNG: false,
isMarkFace: false,
eId: string,
epId: string,
ocrWord: string,
modifyWord: string,
exposeNum: number,
clickNum: number,
emojiInfoList: {
uin: string
emoId: number
emoPath: string
isExist: boolean
resId: string
url: string
md5: string
emoOriginalPath: string
thumbPath: string
RomaingType: string
isAPNG: false
isMarkFace: false
eId: string
epId: string
ocrWord: string
modifyWord: string
exposeNum: number
clickNum: number
desc: string
}[]
}>
}>
addFavEmoji(...args: unknown[]): unknown
fetchMarketEmoticonList(...args: unknown[]): unknown
fetchMarketEmoticonShowImage(...args: unknown[]): unknown
fetchMarketEmoticonAioImage(...args: unknown[]): unknown
fetchMarketEmotionJsonFile(...args: unknown[]): unknown
getMarketEmoticonPath(...args: unknown[]): unknown
getMarketEmoticonPathBySync(...args: unknown[]): unknown
fetchMarketEmoticonFaceImages(...args: unknown[]): unknown
fetchMarketEmoticonAuthDetail(...args: unknown[]): unknown
getFavMarketEmoticonInfo(...args: unknown[]): unknown
addRecentUsedFace(...args: unknown[]): unknown
getRecentUsedFaceList(...args: unknown[]): unknown
getMarketEmoticonEncryptKeys(...args: unknown[]): unknown
downloadEmojiPic(...args: unknown[]): unknown
deleteFavEmoji(...args: unknown[]): unknown
modifyFavEmojiDesc(...args: unknown[]): unknown
queryFavEmojiByDesc(...args: unknown[]): unknown
getHotPicInfoListSearchString(...args: unknown[]): unknown
getHotPicSearchResult(...args: unknown[]): unknown
getHotPicHotWords(...args: unknown[]): unknown
getHotPicJumpInfo(...args: unknown[]): unknown
getEmojiResourcePath(...args: unknown[]): unknown
JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: unknown): unknown
getMsgAbstracts(...args: unknown[]): unknown
getMsgAbstract(...args: unknown[]): unknown
getMsgAbstractList(...args: unknown[]): unknown
getMsgAbstractListBySeqRange(...args: unknown[]): unknown
refreshMsgAbstracts(...args: unknown[]): unknown
refreshMsgAbstractsByGuildIds(...args: unknown[]): unknown
getRichMediaElement(...args: unknown[]): unknown
cancelGetRichMediaElement(...args: unknown[]): unknown
refuseGetRichMediaElement(...args: unknown[]): unknown
switchToOfflineGetRichMediaElement(...args: unknown[]): unknown
downloadRichMedia(...args: unknown[]): unknown
getFirstUnreadMsgSeq(args: {
peerUid: string
guildId: string
}): unknown
getFirstUnreadCommonMsg(...args: unknown[]): unknown
getFirstUnreadAtmeMsg(...args: unknown[]): unknown
getFirstUnreadAtallMsg(...args: unknown[]): unknown
getNavigateInfo(...args: unknown[]): unknown
getChannelFreqLimitInfo(...args: unknown[]): unknown
getRecentUseEmojiList(...args: unknown[]): unknown
getRecentEmojiList(...args: unknown[]): unknown
setMsgEmojiLikes(...args: unknown[]): unknown
getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string, bForward: boolean, number: number): Promise<{
result: number,
errMsg: string,
emojiLikesList:
Array<{
tinyId: string,
nickName: string,
result: number
errMsg: string
emojiLikesList: {
tinyId: string
nickName: string
headUrl: string
}>,
cookie: string,
isLastPage: boolean,
}[]
cookie: string
isLastPage: boolean
isFirstPage: boolean
}>
setMsgEmojiLikesForRole(...args: unknown[]): unknown
clickInlineKeyboardButton(...args: unknown[]): unknown
setCurOnScreenMsg(...args: unknown[]): unknown
setCurOnScreenMsgForMsgEvent(...args: unknown[]): unknown
getMiscData(key: string): unknown
setMiscData(key: string, value: string): unknown
getBookmarkData(...args: unknown[]): unknown
setBookmarkData(...args: unknown[]): unknown
sendShowInputStatusReq(ChatType: number, EventType: number, toUid: string): Promise<unknown>
queryCalendar(...args: unknown[]): unknown
queryFirstMsgSeq(peer: Peer, ...args: unknown[]): unknown
queryRoamCalendar(...args: unknown[]): unknown
queryFirstRoamMsg(...args: unknown[]): unknown
fetchLongMsg(peer: Peer, msgId: string): unknown
fetchLongMsgWithCb(...args: unknown[]): unknown
setIsStopKernelFetchLongMsg(...args: unknown[]): unknown
insertGameResultAsMsgToDb(...args: unknown[]): unknown
getMultiMsg(...args: unknown[]): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>
setDraft(...args: unknown[]): unknown
getDraft(...args: unknown[]): unknown
deleteDraft(...args: unknown[]): unknown
getRecentHiddenSesionList(...args: unknown[]): unknown
setRecentHiddenSession(...args: unknown[]): unknown
delRecentHiddenSession(...args: unknown[]): unknown
getCurHiddenSession(...args: unknown[]): unknown
setCurHiddenSession(...args: unknown[]): unknown
setReplyDraft(...args: unknown[]): unknown
getReplyDraft(...args: unknown[]): unknown
deleteReplyDraft(...args: unknown[]): unknown
getFirstUnreadAtMsg(peer: Peer): unknown
clearMsgRecords(...args: unknown[]): unknown//设置已读后调用我觉得比较好 清理记录 现在别了
IsExistOldDb(...args: unknown[]): unknown
canImportOldDbMsg(...args: unknown[]): unknown
setPowerStatus(z: boolean): unknown
canProcessDataMigration(...args: unknown[]): unknown
importOldDbMsg(...args: unknown[]): unknown
stopImportOldDbMsgAndroid(...args: unknown[]): unknown
isMqqDataImportFinished(...args: unknown[]): unknown
getMqqDataImportTableNames(...args: unknown[]): unknown
getCurChatImportStatusByUin(...args: unknown[]): unknown
getDataImportUserLevel(): unknown
getMsgQRCode(...args: unknown[]): unknown
getGuestMsgAbstracts(...args: unknown[]): unknown
getGuestMsgByRange(...args: unknown[]): unknown
getGuestMsgAbstractByRange(...args: unknown[]): unknown
registerSysMsgNotification(...args: unknown[]): unknown
unregisterSysMsgNotification(...args: unknown[]): unknown
enterOrExitAio(...args: unknown[]): unknown
// this.peerUid = ""
// this.peerNickname = ""
// this.fromGroupCode = ""
// this.sig = new byte[0]
// this.selfUid = ""
// this.selfPhone = ""
// this.chatType = i2
// this.peerUid = str
// this.peerNickname = str2
// this.fromGroupCode = str3
// this.sig = bArr
// this.selfUid = str4
// this.selfPhone = str5
// this.gameSession = tempChatGameSession
prepareTempChat(args: unknown): unknown//主动临时消息 不做
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>
getTempChatInfo(ChatType: number, Uid: string): Promise<TmpChatInfoApi>
setContactLocalTop(...args: unknown[]): unknown
switchAnonymousChat(...args: unknown[]): unknown
renameAnonyChatNick(...args: unknown[]): unknown
getAnonymousInfo(...args: unknown[]): unknown
updateAnonymousInfo(...args: unknown[]): unknown
sendSummonMsg(peer: Peer, MsgElement: unknown, MsgAttributeInfo: unknown): Promise<unknown>//频道的东西
outputGuildUnreadInfo(...args: unknown[]): unknown
checkMsgWithUrl(...args: unknown[]): unknown
checkTabListStatus(...args: unknown[]): unknown
getABatchOfContactMsgBoxInfo(...args: unknown[]): unknown
insertMsgToMsgBox(peer: Peer, msgId: string, arg: 2006): unknown
isHitEmojiKeyword(...args: unknown[]): unknown
getKeyWordRelatedEmoji(...args: unknown[]): unknown
recordEmoji(...args: unknown[]): unknown
fetchGetHitEmotionsByWord(args: Dict): Promise<unknown>//表情推荐?
deleteAllRoamMsgs(...args: unknown[]): unknown//漫游消息?
packRedBag(...args: unknown[]): unknown
grabRedBag(...args: unknown[]): unknown
pullDetail(...args: unknown[]): unknown
selectPasswordRedBag(...args: unknown[]): unknown
pullRedBagPasswordList(...args: unknown[]): unknown
requestTianshuAdv(...args: unknown[]): unknown
tianshuReport(...args: unknown[]): unknown
tianshuMultiReport(...args: unknown[]): unknown
GetMsgSubType(a0: number, a1: number): unknown
setIKernelPublicAccountAdapter(...args: unknown[]): unknown
//tempChatGameSession有关
createUidFromTinyId(fromTinyId: string, toTinyId: string): unknown
dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown
dataMigrationGetMsgList(...args: unknown[]): unknown
dataMigrationStopOperation(...args: unknown[]): unknown
//新的希望
dataMigrationImportMsgPbRecord(DataMigrationMsgInfo: Array<{
extensionData: string//"Hex"
extraData: string //""
chatType: number
chatUin: string
msgType: number
msgTime: string
msgSeq: string
msgRandom: string
}>, DataMigrationResourceInfo: {
extraData: string
filePath: string
fileSize: string
msgRandom: string
msgSeq: string
msgSubType: number
msgType: number
}): unknown
dataMigrationGetResourceLocalDestinyPath(...args: unknown[]): unknown
dataMigrationSetIOSPathPrefix(...args: unknown[]): unknown
getServiceAssistantSwitch(...args: unknown[]): unknown
setServiceAssistantSwitch(...args: unknown[]): unknown
setSubscribeFolderUsingSmallRedPoint(...args: unknown[]): unknown
clearGuildNoticeRedPoint(...args: unknown[]): unknown
clearFeedNoticeRedPoint(...args: unknown[]): unknown
clearFeedSquareRead(...args: unknown[]): unknown
IsC2CStyleChatType(...args: unknown[]): unknown
IsTempChatType(uin: number): unknown//猜的
getGuildInteractiveNotification(...args: unknown[]): unknown
getGuildNotificationAbstract(...args: unknown[]): unknown
setFocusOnBase(...args: unknown[]): unknown
queryArkInfo(...args: unknown[]): unknown
queryUserSecQuality(...args: unknown[]): unknown
getGuildMsgAbFlag(...args: unknown[]): unknown
getGroupMsgStorageTime(): unknown//这是嘛啊
getMultiMsg(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getTempChatInfo(chatType: number, uid: string): Promise<TmpChatInfoApi>
}

View File

@@ -3,10 +3,6 @@ import { GeneralCallResult } from './common'
import { Dict } from 'cosmokit'
export interface NodeIKernelProfileLikeService {
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void
removeKernelProfileLikeListener(listener: unknown): void
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number }
getBuddyProfileLike(req: BuddyProfileLikeReq): Promise<GeneralCallResult & {
@@ -26,8 +22,4 @@ export interface NodeIKernelProfileLikeService {
start: number
}
}>
getProfileLikeScidResourceInfo(...args: unknown[]): void
isNull(): boolean
}

View File

@@ -1,4 +1,3 @@
import { AnyCnameRecord } from 'node:dns'
import { SimpleInfo } from '../types'
import { GeneralCallResult } from './common'
@@ -16,91 +15,17 @@ export enum ProfileBizType {
}
export interface NodeIKernelProfileService {
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string, string>>//uin->uid
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string, string>>
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string, string>>
// {
// coreInfo: CoreInfo,
// baseInfo: BaseInfo,
// status: null,
// vasInfo: null,
// relationFlags: null,
// otherFlags: null,
// intimate: null
// }
getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>
addKernelProfileListener(listener: unknown): number
removeKernelProfileListener(listenerId: number): void
prepareRegionConfig(...args: unknown[]): unknown
getLocalStrangerRemark(): Promise<AnyCnameRecord>
enumCountryOptions(): Array<string>
enumProvinceOptions(Country: string): Array<string>
enumCityOptions(Country: string, Province: string): unknown
enumAreaOptions(...args: unknown[]): unknown
//SimpleInfo
// this.uid = ""
// this.uid = str
// this.uin = j2
// this.isBuddy = z
// this.coreInfo = coreInfo
// this.baseInfo = baseInfo
// this.status = statusInfo
// this.vasInfo = vasInfo
// this.relationFlags = relationFlag
// this.otherFlags = otherFlag
// this.intimate = intimate
modifySelfProfile(...args: unknown[]): Promise<unknown>
modifyDesktopMiniProfile(param: unknown): Promise<GeneralCallResult>
setNickName(NickName: string): Promise<unknown>
setLongNick(longNick: string): Promise<unknown>
setBirthday(...args: unknown[]): Promise<unknown>
setGander(...args: unknown[]): Promise<unknown>
setHeader(arg: string): Promise<GeneralCallResult>
setRecommendImgFlag(...args: unknown[]): Promise<unknown>
getUserSimpleInfo(force: boolean, uids: string[],): Promise<unknown>
getUserDetailInfo(uid: string): Promise<unknown>
getUserDetailInfoWithBizInfo(uid: string, Biz: unknown[]): Promise<GeneralCallResult>
getUserDetailInfoWithBizInfo(uid: string, biz: unknown[]): Promise<GeneralCallResult>
getUserDetailInfoByUin(uin: string): Promise<unknown>
getZplanAvatarInfos(args: string[]): Promise<unknown>
getStatus(uid: string): Promise<unknown>
startStatusPolling(isForceReset: boolean): Promise<unknown>
getSelfStatus(): Promise<unknown>
setdisableEmojiShortCuts(...args: unknown[]): unknown
getProfileQzonePicInfo(uid: string, type: number, force: boolean): Promise<unknown>
//profileService.getCoreInfo("UserRemarkServiceImpl::getStrangerRemarkByUid", arrayList)
getCoreInfo(name: string, arg: unknown[]): unknown
//m429253e12.getOtherFlag("FriendListInfoCache_getKernelDataAndPutCache", new ArrayList<>())
isNull(): boolean
}

View File

@@ -1,191 +1,23 @@
import { GetFileListParam, MessageElement, Peer } from '../types'
import { GetFileListParam, Peer } from '../types'
import { GeneralCallResult } from './common'
export enum UrlFileDownloadType {
KUNKNOWN,
KURLFILEDOWNLOADPRIVILEGEICON,
KURLFILEDOWNLOADPHOTOWALL,
KURLFILEDOWNLOADQZONE,
KURLFILEDOWNLOADCOMMON,
KURLFILEDOWNLOADINSTALLAPP
}
export enum RMBizTypeEnum {
KUNKNOWN,
KC2CFILE,
KGROUPFILE,
KC2CPIC,
KGROUPPIC,
KDISCPIC,
KC2CVIDEO,
KGROUPVIDEO,
KC2CPTT,
KGROUPPTT,
KFEEDCOMMENTPIC,
KGUILDFILE,
KGUILDPIC,
KGUILDPTT,
KGUILDVIDEO
}
export interface CommonFileInfo {
bizType: number
chatType: number
elemId: string
favId: string
fileModelId: string
fileName: string
fileSize: string
md5: string
md510m: string
msgId: string
msgTime: string
parent: string
peerUid: string
picThumbPath: Array<string>
sha: string
sha3: string
subId: string
uuid: string
}
export interface NodeIKernelRichMediaService {
//getVideoPlayUrl(peer, msgId, elemId, videoCodecFormat, VideoRequestWay.KHAND, cb)
// public enum VideoCodecFormatType {
// KCODECFORMATH264,
// KCODECFORMATH265,
// KCODECFORMATH266,
// KCODECFORMATAV1
// }
// public enum VideoRequestWay {
// KUNKNOW,
// KHAND,
// KAUTO
// }
getVideoPlayUrl(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, VideoRequestWay: number): Promise<unknown>
//exParams (RMReqExParams)
// this.downSourceType = i2
// this.triggerType = i3
//peer, msgId, elemId, videoCodecFormat, exParams
// 1 0 频道在用
// 1 1
// 0 2
// public static final int KCOMMONREDENVELOPEMSGTYPEINMSGBOX = 1007
// public static final int KDOWNSOURCETYPEAIOINNER = 1
// public static final int KDOWNSOURCETYPEBIGSCREEN = 2
// public static final int KDOWNSOURCETYPEHISTORY = 3
// public static final int KDOWNSOURCETYPEUNKNOWN = 0
// public static final int KTRIGGERTYPEAUTO = 1
// public static final int KTRIGGERTYPEMANUAL = 0
getVideoPlayUrlV2(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, exParams: { downSourceType: number, triggerType: number }): Promise<GeneralCallResult & {
urlResult: {
v4IpUrl: [],
v6IpUrl: [],
domainUrl: Array<{
url: string,
isHttps: boolean,
v4IpUrl: []
v6IpUrl: []
domainUrl: {
url: string
isHttps: boolean
httpsDomain: string
}>,
}[]
videoCodecFormat: number
}
}>
getRichMediaFileDir(elementType: number, downType: number, isTemp: boolean): unknown
deleteGroupFolder(groupCode: string, folderId: string): Promise<GeneralCallResult & { groupFileCommonResult: { retCode: number, retMsg: string, clientWording: string } }>
// this.senderUid = ""
// this.peerUid = ""
// this.guildId = ""
// this.elem = new MsgElement()
// this.downloadType = i2
// this.thumbSize = i3
// this.msgId = j2
// this.msgRandom = j3
// this.msgSeq = j4
// this.msgTime = j5
// this.chatType = i4
// this.senderUid = str
// this.peerUid = str2
// this.guildId = str3
// this.elem = msgElement
// this.useHttps = num
getVideoPlayUrlInVisit(arg: {
downloadType: number,
thumbSize: number,
msgId: string,
msgRandom: string,
msgSeq: string,
msgTime: string,
chatType: number,
senderUid: string,
peerUid: string,
guildId: string,
ele: MessageElement,
useHttps: boolean
}): Promise<unknown>
//arg双端number
isFileExpired(arg: number): unknown
deleteGroupFolder(GroupCode: string, FolderId: string): Promise<GeneralCallResult & { groupFileCommonResult: { retCode: number, retMsg: string, clientWording: string } }>
//参数与getVideoPlayUrlInVisit一样
downloadRichMediaInVisit(arg: {
downloadType: number,
thumbSize: number,
msgId: string,
msgRandom: string,
msgSeq: string,
msgTime: string,
chatType: number,
senderUid: string,
peerUid: string,
guildId: string,
ele: MessageElement,
useHttps: boolean
}): unknown
//arg3为“”
downloadFileForModelId(peer: Peer, ModelId: string[], arg3: string): unknown
//第三个参数 Array<Type>
// this.fileId = ""
// this.fileName = ""
// this.fileId = str
// this.fileName = str2
// this.fileSize = j2
// this.fileModelId = j3
downloadFileForFileUuid(peer: Peer, uuid: string, arg3: {
fileId: string,
fileName: string,
fileSize: string,
fileModelId: string
}[]): Promise<unknown>
downloadFileByUrlList(fileDownloadTyp: UrlFileDownloadType, urlList: Array<string>): unknown
downloadFileForFileInfo(fileInfo: CommonFileInfo[], savePath: string): unknown
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: unknown, groupItem: Array<unknown> } }>
downloadFile(commonFile: CommonFileInfo, arg2: unknown, arg3: unknown, savePath: string): unknown
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown
deleteTransferInfo(arg1: unknown, arg2: unknown): unknown
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown
cancelUrlDownload(arg: unknown): unknown
updateOnlineVideoElemStatus(arg: unknown): unknown
getGroupSpace(arg: unknown): unknown
createGroupFolder(groupCode: string, folderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: unknown, groupItem: unknown[] } }>
getGroupFileList(groupCode: string, params: GetFileListParam): Promise<GeneralCallResult & {
groupSpaceResult: {
@@ -198,69 +30,14 @@ export interface NodeIKernelRichMediaService {
}
}>
getGroupFileInfo(arg1: unknown, arg2: unknown): unknown
getGroupTransferList(arg1: unknown, arg2: unknown): unknown
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown
moveGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown
transGroupFile(arg1: unknown, arg2: unknown): unknown
searchGroupFile(
keywords: Array<string>,
param: {
groupIds: Array<string>,
fileType: number,
context: string,
count: number,
sortType: number,
groupNames: Array<string>
}): Promise<unknown>
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
deleteGroupFile(groupCode: string, params: Array<number>, files: Array<string>): Promise<GeneralCallResult & {
transGroupFileResult: {
result: unknown
successFileIdList: Array<unknown>
failFileIdList: Array<unknown>
}
}>
translateEnWordToZn(words: string[]): Promise<GeneralCallResult & { words: string[] }>
getScreenOCR(path: string): Promise<unknown>
batchGetGroupFileCount(Gids: Array<string>): Promise<GeneralCallResult & { groupCodes: Array<string>, groupFileCounts: Array<number> }>
queryPicDownloadSize(arg: unknown): unknown
searchGroupFile(arg1: unknown, arg2: unknown): unknown
searchMoreGroupFile(arg: unknown): unknown
cancelSearcheGroupFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown
onlyDownloadFile(peer: Peer, arg2: unknown, arg3: Array<{
fileId: string,
fileName: string,
fileSize: string,
fileModelId: string
}
>): unknown
onlyUploadFile(arg1: unknown, arg2: unknown): unknown
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown
uploadRMFileWithoutMsg(arg: {
bizType: RMBizTypeEnum,
filePath: string,
peerUid: string,
transferId: string
useNTV2: string
}): Promise<unknown>
isNull(): boolean
}

View File

@@ -1,128 +0,0 @@
import { ChatType } from '../types'
export interface NodeIKernelSearchService {
addKernelSearchListener(...args: unknown[]): unknown// needs 1 arguments
removeKernelSearchListener(...args: unknown[]): unknown// needs 1 arguments
searchStranger(...args: unknown[]): unknown// needs 3 arguments
searchGroup(...args: unknown[]): unknown// needs 1 arguments
searchLocalInfo(keywords: string, unknown: number/*4*/): unknown
cancelSearchLocalInfo(...args: unknown[]): unknown// needs 3 arguments
searchBuddyChatInfo(...args: unknown[]): unknown// needs 2 arguments
searchMoreBuddyChatInfo(...args: unknown[]): unknown// needs 1 arguments
cancelSearchBuddyChatInfo(...args: unknown[]): unknown// needs 3 arguments
searchContact(...args: unknown[]): unknown// needs 2 arguments
searchMoreContact(...args: unknown[]): unknown// needs 1 arguments
cancelSearchContact(...args: unknown[]): unknown// needs 3 arguments
searchGroupChatInfo(...args: unknown[]): unknown// needs 3 arguments
resetSearchGroupChatInfoSortType(...args: unknown[]): unknown// needs 3 arguments
resetSearchGroupChatInfoFilterMembers(...args: unknown[]): unknown// needs 3 arguments
searchMoreGroupChatInfo(...args: unknown[]): unknown// needs 1 arguments
cancelSearchGroupChatInfo(...args: unknown[]): unknown// needs 3 arguments
searchChatsWithKeywords(...args: unknown[]): unknown// needs 3 arguments
searchMoreChatsWithKeywords(...args: unknown[]): unknown// needs 1 arguments
cancelSearchChatsWithKeywords(...args: unknown[]): unknown// needs 3 arguments
searchChatMsgs(...args: unknown[]): unknown// needs 2 arguments
searchMoreChatMsgs(...args: unknown[]): unknown// needs 1 arguments
cancelSearchChatMsgs(...args: unknown[]): unknown// needs 3 arguments
searchMsgWithKeywords(...args: unknown[]): unknown// needs 2 arguments
searchMoreMsgWithKeywords(...args: unknown[]): unknown// needs 1 arguments
cancelSearchMsgWithKeywords(...args: unknown[]): unknown// needs 3 arguments
searchFileWithKeywords(keywords: string[], source: number): Promise<string>// needs 2 arguments
searchMoreFileWithKeywords(...args: unknown[]): unknown// needs 1 arguments
cancelSearchFileWithKeywords(...args: unknown[]): unknown// needs 3 arguments
searchAtMeChats(...args: unknown[]): unknown// needs 3 arguments
searchMoreAtMeChats(...args: unknown[]): unknown// needs 1 arguments
cancelSearchAtMeChats(...args: unknown[]): unknown// needs 3 arguments
searchChatAtMeMsgs(...args: unknown[]): unknown// needs 1 arguments
searchMoreChatAtMeMsgs(...args: unknown[]): unknown// needs 1 arguments
cancelSearchChatAtMeMsgs(...args: unknown[]): unknown// needs 3 arguments
addSearchHistory(param: {
type: number,//4
contactList: [],
id: number,//-1
groupInfos: [],
msgs: [],
fileInfos: [
{
chatType: ChatType,
buddyChatInfo: Array<{ category_name: string, peerUid: string, peerUin: string, remark: string }>,
discussChatInfo: [],
groupChatInfo: Array<
{
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}>,
dataLineChatInfo: [],
tmpChatInfo: [],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: string,//3
fileSize: string,
filePath: string,
fileName: string,
hits: Array<
{
start: 12,
end: 14
}
>
}
]
}): Promise<{
result: number,
errMsg: string,
id?: number
}>
removeSearchHistory(...args: unknown[]): unknown// needs 1 arguments
searchCache(...args: unknown[]): unknown// needs 3 arguments
clearSearchCache(...args: unknown[]): unknown// needs 1 arguments
}

View File

@@ -1,11 +1,10 @@
import { forceFetchClientKeyRetType } from './common'
import { GeneralCallResult } from './common'
export interface NodeIKernelTicketService {
addKernelTicketListener(listener: unknown): void
removeKernelTicketListener(listenerId: unknown): void
forceFetchClientKey(arg: string): Promise<forceFetchClientKeyRetType>
isNull(): boolean
forceFetchClientKey(arg: string): Promise<GeneralCallResult & {
url: string
keyIndex: string
clientKey: string
expireTime: string
}>
}

View File

@@ -1,19 +1,5 @@
import { GeneralCallResult } from './common'
export interface NodeIKernelTipOffService {
addKernelTipOffListener(listener: unknown): void
removeKernelTipOffListener(listenerId: unknown): void
tipOffSendJsData(args: unknown[]): Promise<unknown> //2
getPskey(domainList: string[], nocache: boolean): Promise<GeneralCallResult & { domainPskeyMap: Map<string, string> }> //2
tipOffSendJsData(args: unknown[]): Promise<unknown> //2
tipOffMsgs(args: unknown[]): Promise<unknown> //1
encodeUinAesInfo(args: unknown[]): Promise<unknown> //2
isNull(): boolean
getPskey(domainList: string[], nocache: boolean): Promise<GeneralCallResult & { domainPskeyMap: Map<string, string> }>
}

View File

@@ -1,16 +1,8 @@
export enum GeneralCallResultStatus {
OK = 0
// ERROR = 1
}
export interface GeneralCallResult {
result: GeneralCallResultStatus
errMsg: string
}
export interface forceFetchClientKeyRetType extends GeneralCallResult {
url: string
keyIndex: string
clientKey: string
expireTime: string
}

View File

@@ -9,4 +9,3 @@ export * from './NodeIKernelUixConvertService'
export * from './NodeIKernelRichMediaService'
export * from './NodeIKernelTicketService'
export * from './NodeIKernelTipOffService'
export * from './NodeIKernelSearchService'

View File

@@ -62,8 +62,9 @@ export interface GroupMember {
sex?: Sex
qqLevel?: QQLevel
isChangeRole: boolean
joinTime: string
lastSpeakTime: string
joinTime: number
lastSpeakTime: number
memberLevel: number
}
export interface PublishGroupBulletinReq {

View File

@@ -6,6 +6,7 @@ export interface GetFileListParam {
startIndex: number
sortOrder: number
showOnlinedocFolder: number
folderId?: string
}
export enum ElementType {
@@ -196,50 +197,6 @@ export enum ChatType {
temp = 100,
}
// 来自Android分析
export enum ChatType2 {
KCHATTYPEADELIE = 42,
KCHATTYPEBUDDYNOTIFY = 5,
KCHATTYPEC2C = 1,
KCHATTYPECIRCLE = 113,
KCHATTYPEDATALINE = 8,
KCHATTYPEDATALINEMQQ = 134,
KCHATTYPEDISC = 3,
KCHATTYPEFAV = 41,
KCHATTYPEGAMEMESSAGE = 105,
KCHATTYPEGAMEMESSAGEFOLDER = 116,
KCHATTYPEGROUP = 2,
KCHATTYPEGROUPBLESS = 133,
KCHATTYPEGROUPGUILD = 9,
KCHATTYPEGROUPHELPER = 7,
KCHATTYPEGROUPNOTIFY = 6,
KCHATTYPEGUILD = 4,
KCHATTYPEGUILDMETA = 16,
KCHATTYPEMATCHFRIEND = 104,
KCHATTYPEMATCHFRIENDFOLDER = 109,
KCHATTYPENEARBY = 106,
KCHATTYPENEARBYASSISTANT = 107,
KCHATTYPENEARBYFOLDER = 110,
KCHATTYPENEARBYHELLOFOLDER = 112,
KCHATTYPENEARBYINTERACT = 108,
KCHATTYPEQQNOTIFY = 132,
KCHATTYPERELATEACCOUNT = 131,
KCHATTYPESERVICEASSISTANT = 118,
KCHATTYPESERVICEASSISTANTSUB = 201,
KCHATTYPESQUAREPUBLIC = 115,
KCHATTYPESUBSCRIBEFOLDER = 30,
KCHATTYPETEMPADDRESSBOOK = 111,
KCHATTYPETEMPBUSSINESSCRM = 102,
KCHATTYPETEMPC2CFROMGROUP = 100,
KCHATTYPETEMPC2CFROMUNKNOWN = 99,
KCHATTYPETEMPFRIENDVERIFY = 101,
KCHATTYPETEMPNEARBYPRO = 119,
KCHATTYPETEMPPUBLICACCOUNT = 103,
KCHATTYPETEMPWPA = 117,
KCHATTYPEUNKNOWN = 0,
KCHATTYPEWEIYUN = 40,
}
export interface PttElement {
canConvert2Text: boolean
duration: number // 秒数
@@ -348,6 +305,7 @@ export interface FaceElement {
resultId?: string
surpriseId?: string
randomType?: number
pokeType?: number
}
export interface MarketFaceElement {
@@ -481,7 +439,7 @@ export interface RawMessage {
msgSeq: string
msgRandom: string
senderUid: string
senderUin?: string // 发送者QQ号
senderUin: string // 发送者QQ号
peerUid: string // 群号 或者 QQ uid
peerUin: string // 群号 或者 发送者QQ号
guildId: string
@@ -533,3 +491,77 @@ export interface MessageElement {
recommendedMsgElement?: unknown
actionBarElement?: unknown
}
export interface OnRichMediaDownloadCompleteParams {
fileModelId: string
msgElementId: string
msgId: string
fileId: string
fileProgress: string // '0'
fileSpeed: string // '0'
fileErrCode: string // '0'
fileErrMsg: string
fileDownType: number // 暂时未知
thumbSize: number
filePath: string
totalSize: string
trasferStatus: number
step: number
commonFileInfo: unknown
fileSrvErrCode: string
clientMsg: string
businessId: number
userTotalSpacePerDay: unknown
userUsedSpacePerDay: unknown
}
export interface OnGroupFileInfoUpdateParams {
retCode: number
retMsg: string
clientWording: string
isEnd: boolean
item: {
peerId: string
type: number
folderInfo?: {
folderId: string
parentFolderId: string
folderName: string
createTime: number
modifyTime: number
createUin: string
creatorName: string
totalFileCount: number
modifyUin: string
modifyName: string
usedSpace: string
}
fileInfo?: {
fileModelId: string
fileId: string
fileName: string
fileSize: string
busId: number
uploadedSize: string
uploadTime: number
deadTime: number
modifyTime: number
downloadTimes: number
sha: string
sha3: string
md5: string
uploaderLocalPath: string
uploaderName: string
uploaderUin: string
parentFolderId: string
localPath: string
transStatus: number
transType: number
elementId: string
isFolder: boolean
}
}[]
allFileCount: number
nextIndex: number
reqId: number
}

View File

@@ -8,8 +8,7 @@ import {
NodeIKernelUixConvertService,
NodeIKernelRichMediaService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
NodeIKernelSearchService
NodeIKernelTipOffService
} from './services'
import { constants } from 'node:os'
import { Dict } from 'cosmokit'
@@ -26,37 +25,14 @@ export interface NodeIQQNTWrapperSession {
getRichMediaService(): NodeIKernelRichMediaService
getTicketService(): NodeIKernelTicketService
getTipOffService(): NodeIKernelTipOffService
getSearchService(): NodeIKernelSearchService
}
export interface WrapperApi {
NodeIQQNTWrapperSession?: NodeIQQNTWrapperSession
}
export interface WrapperConstructor {
[key: string]: unknown
}
const wrapperApi: WrapperApi = {}
export const wrapperConstructor: WrapperConstructor = {}
const constructor = [
'NodeIKernelBuddyListener',
'NodeIKernelGroupListener',
'NodeQQNTWrapperUtil',
'NodeIKernelMsgListener',
'NodeIQQNTWrapperEngine',
'NodeIGlobalAdapter',
'NodeIDependsAdapter',
'NodeIDispatcherAdapter',
'NodeIKernelSessionListener',
'NodeIKernelLoginService',
'NodeIKernelLoginListener',
'NodeIKernelProfileService',
'NodeIKernelProfileListener',
]
Process.dlopenOrig = Process.dlopen
Process.dlopen = function (module: Dict, filename: string, flags = constants.dlopen.RTLD_LAZY) {
@@ -69,9 +45,6 @@ Process.dlopen = function (module: Dict, filename: string, flags = constants.dlo
return ret
}
})
if (constructor.includes(export_name)) {
wrapperConstructor[export_name] = module.exports[export_name]
}
}
return dlopenRet
}

View File

@@ -2,7 +2,6 @@ import { BaseAction, Schema } from '../BaseAction'
import { readFile } from 'node:fs/promises'
import { ActionName } from '../types'
import { Peer, ElementType } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique'
export interface GetFilePayload {
file: string // 文件名或者fileUuid
@@ -24,9 +23,9 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const { enableLocalFile2Url } = this.adapter.config
let fileCache = await MessageUnique.getFileCacheById(payload.file)
let fileCache = await this.ctx.store.getFileCacheById(payload.file)
if (!fileCache?.length) {
fileCache = await MessageUnique.getFileCacheByName(payload.file)
fileCache = await this.ctx.store.getFileCacheByName(payload.file)
}
if (fileCache?.length) {

View File

@@ -1,25 +1,24 @@
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload {
message_id: number | string
}
export class DelEssenceMsg extends BaseAction<Payload, unknown> {
export class DeleteEssenceMsg extends BaseAction<Payload, unknown> {
actionName = ActionName.GoCQHTTP_DelEssenceMsg
payloadSchema = Schema.object({
message_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
const msg = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msg) {
throw new Error('msg not found')
}
return await this.ctx.ntGroupApi.removeGroupEssence(
msg.Peer.peerUid,
msg.MsgId,
msg.peer.peerUid,
msg.msgId,
)
}
}

View File

@@ -2,9 +2,9 @@ import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
interface Payload {
group_id: string | number
group_id: number | string
file_id: string
busid: number
busid: number | string
}
export class DelGroupFile extends BaseAction<Payload, null> {
@@ -12,11 +12,11 @@ export class DelGroupFile extends BaseAction<Payload, null> {
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
file_id: Schema.string().required(),
busid: Schema.number().default(102)
busid: Schema.union([Number, String]).default(102)
})
async _handle(payload: Payload) {
await this.ctx.ntGroupApi.deleteGroupFile(payload.group_id.toString(), [payload.file_id], [payload.busid])
await this.ctx.ntGroupApi.deleteGroupFile(payload.group_id.toString(), [payload.file_id], [+payload.busid])
return null
}
}

View File

@@ -1,8 +1,7 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { OB11ForwardMessage } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique'
import { filterNullable } from '@/common/utils/misc'
interface Payload {
@@ -16,17 +15,22 @@ interface Response {
export class GetForwardMsg extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetForwardMsg
payloadSchema = Schema.object({
message_id: String,
id: String
})
protected async _handle(payload: Payload) {
const msgId = payload.id || payload.message_id
if (!msgId) {
throw Error('message_id不能为空')
}
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId)
const rootMsg = await MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || +msgId)
const rootMsgId = await this.ctx.store.getShortIdByMsgId(msgId)
const rootMsg = await this.ctx.store.getMsgInfoByShortId(rootMsgId || +msgId)
if (!rootMsg) {
throw Error('msg not found')
}
const data = await this.ctx.ntMsgApi.getMultiMsg(rootMsg.Peer, rootMsg.MsgId, rootMsg.MsgId)
const data = await this.ctx.ntMsgApi.getMultiMsg(rootMsg.peer, rootMsg.msgId, rootMsg.msgId)
if (data?.result !== 0) {
throw Error('找不到相关的聊天记录' + data?.errMsg)
}
@@ -35,7 +39,7 @@ export class GetForwardMsg extends BaseAction<Payload, Response> {
msgList.map(async (msg) => {
const resMsg = await OB11Entities.message(this.ctx, msg)
if (!resMsg) return
resMsg.message_id = MessageUnique.createMsg({
resMsg.message_id = this.ctx.store.createMsgShortId({
chatType: msg.chatType,
peerUid: msg.peerUid,
}, msg.msgId)

View File

@@ -1,4 +1,4 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
interface Payload {
@@ -13,6 +13,9 @@ interface Response {
export class GetGroupAtAllRemain extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetGroupAtAllRemain
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required()
})
async _handle(payload: Payload) {
const data = await this.ctx.ntGroupApi.getGroupRemainAtTimes(payload.group_id.toString())

View File

@@ -0,0 +1,50 @@
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { ChatType } from '@/ntqqapi/types'
interface Payload {
group_id: number | string
}
interface EssenceMsg {
sender_id: number
sender_nick: string
sender_time: number
operator_id: number
operator_nick: string
operator_time: number
message_id: number
}
export class GetEssenceMsgList extends BaseAction<Payload, EssenceMsg[]> {
actionName = ActionName.GoCQHTTP_GetEssenceMsgList
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
const groupCode = payload.group_id.toString()
const peer = {
guildId: '',
chatType: ChatType.group,
peerUid: groupCode
}
const essence = await this.ctx.ntGroupApi.queryCachedEssenceMsg(groupCode)
const data: EssenceMsg[] = []
for (const item of essence.items) {
const { msgList } = await this.ctx.ntMsgApi.queryMsgsWithFilterExBySeq(peer, String(item.msgSeq), '0')
const sourceMsg = msgList.find(e => e.msgRandom === String(item.msgRandom))
if (!sourceMsg) continue
data.push({
sender_id: +item.msgSenderUin,
sender_nick: item.msgSenderNick,
sender_time: +sourceMsg.msgTime,
operator_id: +item.opUin,
operator_nick: item.opNick,
operator_time: item.opTime,
message_id: this.ctx.store.createMsgShortId(peer, sourceMsg.msgId)
})
}
return data
}
}

View File

@@ -0,0 +1,54 @@
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { OB11GroupFile, OB11GroupFileFolder } from '@/onebot11/types'
interface Payload {
group_id: string | number
folder_id: string
file_count: string | number
}
interface Response {
files: OB11GroupFile[]
folders: OB11GroupFileFolder[]
}
export class GetGroupFilesByFolder extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetGroupFilesByFolder
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
folder_id: Schema.string().required(),
file_count: Schema.union([Number, String]).default(50)
})
async _handle(payload: Payload) {
const data = await this.ctx.ntGroupApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1,
fileCount: +payload.file_count,
startIndex: 0,
sortOrder: 2,
showOnlinedocFolder: 0,
folderId: payload.folder_id
})
return {
files: data.filter(item => item.fileInfo)
.map(item => {
const file = item.fileInfo!
return {
group_id: +item.peerId,
file_id: file.fileId,
file_name: file.fileName,
busid: file.busId,
file_size: +file.fileSize,
upload_time: file.uploadTime,
dead_time: file.deadTime,
modify_time: file.modifyTime,
download_times: file.downloadTimes,
uploader: +file.uploaderUin,
uploader_name: file.uploaderName
}
}),
folders: []
}
}
}

View File

@@ -1,17 +1,16 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { OB11Message } from '../../types'
import { ActionName } from '../types'
import { ChatType } from '@/ntqqapi/types'
import { OB11Entities } from '../../entities'
import { RawMessage } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique'
import { filterNullable } from '@/common/utils/misc'
interface Payload {
group_id: number | string
message_seq?: number | string
count?: number | string
reverseOrder?: boolean
count: number | string
reverseOrder: boolean
}
interface Response {
@@ -20,25 +19,30 @@ interface Response {
export class GetGroupMsgHistory extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetGroupMsgHistory
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
message_seq: Schema.union([Number, String]),
count: Schema.union([Number, String]).default(20),
reverseOrder: Schema.boolean().default(false),
})
protected async _handle(payload: Payload): Promise<Response> {
const count = payload.count || 20
const isReverseOrder = payload.reverseOrder || true
const { count, reverseOrder } = payload
const peer = { chatType: ChatType.group, peerUid: payload.group_id.toString() }
let msgList: RawMessage[] | undefined
// 包含 message_seq 0
if (!payload.message_seq) {
msgList = (await this.ctx.ntMsgApi.getAioFirstViewLatestMsgs(peer, +count)).msgList
} else {
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq))?.MsgId
const startMsgId = (await this.ctx.store.getMsgInfoByShortId(+payload.message_seq))?.msgId
if (!startMsgId) throw new Error(`消息${payload.message_seq}不存在`)
msgList = (await this.ctx.ntMsgApi.getMsgHistory(peer, startMsgId, +count)).msgList
}
if (!msgList?.length) throw new Error('未找到消息')
if (isReverseOrder) msgList.reverse()
if (reverseOrder) msgList.reverse()
await Promise.all(
msgList.map(async msg => {
msg.msgShortId = MessageUnique.createMsg({ chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId)
msg.msgShortId = this.ctx.store.createMsgShortId({ chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId)
})
)
const ob11MsgList = await Promise.all(msgList.map((msg) => OB11Entities.message(this.ctx, msg)))

View File

@@ -1,4 +1,4 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { OB11GroupFile, OB11GroupFileFolder } from '../../types'
@@ -14,18 +14,19 @@ interface Response {
export class GetGroupRootFiles extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetGroupRootFiles
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
file_count: Schema.union([Number, String]).default(50),
})
async _handle(payload: Payload) {
const data = await this.ctx.ntGroupApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1,
fileCount: +(payload.file_count ?? 50),
fileCount: +payload.file_count,
startIndex: 0,
sortOrder: 2,
showOnlinedocFolder: 0,
})
this.ctx.logger.info(data)
return {
files: data.filter(item => item.fileInfo)
.map(item => {

View File

@@ -1,4 +1,4 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { OB11User } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
@@ -12,6 +12,9 @@ interface Payload {
export class GetStrangerInfo extends BaseAction<Payload, OB11User> {
actionName = ActionName.GoCQHTTP_GetStrangerInfo
payloadSchema = Schema.object({
user_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload): Promise<OB11User> {
if (!(getBuildVersion() >= 26702)) {

View File

@@ -1,6 +1,5 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload {
message_id: number | string
@@ -8,16 +7,16 @@ interface Payload {
export class MarkMsgAsRead extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_MarkMsgAsRead
payloadSchema = Schema.object({
message_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
if (!payload.message_id) {
throw new Error('参数 message_id 不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
const msg = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msg) {
throw new Error('msg not found')
}
await this.ctx.ntMsgApi.setMsgRead(msg.Peer)
await this.ctx.ntMsgApi.setMsgRead(msg.peer)
return null
}
}

View File

@@ -9,6 +9,7 @@ interface Payload {
export class HandleQuickOperation extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_HandleQuickOperation
protected async _handle(payload: Payload): Promise<null> {
handleQuickOperation(this.ctx, payload.context, payload.operation).catch(e => this.ctx.logger.error(e))
return null

View File

@@ -1,20 +1,181 @@
import SendMsg from '../msg/SendMsg'
import { OB11PostSendMsg } from '../../types'
import { unlink } from 'node:fs/promises'
import { OB11MessageNode } from '../../types'
import { ActionName } from '../types'
import { BaseAction, Schema } from '../BaseAction'
import { Peer } from '@/ntqqapi/types/msg'
import { ChatType, ElementType, RawMessage, SendMessageElement } from '@/ntqqapi/types'
import { selfInfo } from '@/common/globalVars'
import { convertMessage2List, createSendElements, sendMsg, createPeer, CreatePeerMode } from '../../helper/createMessage'
export class SendForwardMsg extends SendMsg {
interface Payload {
user_id?: string | number
group_id?: string | number
messages: OB11MessageNode[]
message_type?: 'group' | 'private'
}
interface Response {
message_id: number
forward_id?: string
}
export class SendForwardMsg extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_SendForwardMsg
payloadSchema = Schema.object({
user_id: Schema.union([Number, String]),
group_id: Schema.union([Number, String]),
messages: Schema.array(Schema.any()).required(),
message_type: Schema.union(['group', 'private'])
})
protected async _handle(payload: OB11PostSendMsg) {
if (payload.messages) payload.message = payload.messages
return super._handle(payload)
protected async _handle(payload: Payload) {
let contextMode = CreatePeerMode.Normal
if (payload.message_type === 'group') {
contextMode = CreatePeerMode.Group
} else if (payload.message_type === 'private') {
contextMode = CreatePeerMode.Private
}
const peer = await createPeer(this.ctx, payload, contextMode)
const returnMsg = await this.handleForwardNode(peer, payload.messages)
return { message_id: returnMsg.msgShortId! }
}
private async cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
this.ctx.logger.info('克隆的目标消息', msg)
const sendElements: SendMessageElement[] = []
for (const ele of msg.elements) {
sendElements.push(ele as SendMessageElement)
}
if (sendElements.length === 0) {
this.ctx.logger.warn('需要clone的消息无法解析将会忽略掉', msg)
}
this.ctx.logger.info('克隆消息', sendElements)
try {
const peer = {
chatType: ChatType.friend,
peerUid: selfInfo.uid
}
const nodeMsg = await this.ctx.ntMsgApi.sendMsg(peer, sendElements)
await this.ctx.sleep(300)
return nodeMsg
} catch (e) {
this.ctx.logger.warn(e, '克隆转发消息失败,将忽略本条消息', msg)
}
}
// 返回一个合并转发的消息id
private async handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[]) {
const selfPeer = {
chatType: ChatType.friend,
peerUid: selfInfo.uid,
}
const nodeMsgIds: { msgId: string, peer: Peer }[] = []
// 先判断一遍是不是id和自定义混用
for (const messageNode of messageNodes) {
// 一个node表示一个人的消息
const nodeId = messageNode.data.id
// 有nodeId表示一个子转发消息卡片
if (nodeId) {
const nodeMsg = await this.ctx.store.getMsgInfoByShortId(+nodeId)
if (!nodeMsg) {
this.ctx.logger.warn('转发消息失败,未找到消息', nodeId)
continue
}
nodeMsgIds.push(nodeMsg)
}
else {
// 自定义的消息
// 提取消息段发给自己生成消息id
try {
const { sendElements, deleteAfterSentFiles } = await createSendElements(
this.ctx,
convertMessage2List(messageNode.data.content),
destPeer
)
this.ctx.logger.info('开始生成转发节点', sendElements)
const sendElementsSplit: SendMessageElement[][] = []
let splitIndex = 0
for (const ele of sendElements) {
if (!sendElementsSplit[splitIndex]) {
sendElementsSplit[splitIndex] = []
}
if (ele.elementType === ElementType.FILE || ele.elementType === ElementType.VIDEO) {
if (sendElementsSplit[splitIndex].length > 0) {
splitIndex++
}
sendElementsSplit[splitIndex] = [ele]
splitIndex++
}
else {
sendElementsSplit[splitIndex].push(ele)
}
}
this.ctx.logger.info('分割后的转发节点', sendElementsSplit)
for (const eles of sendElementsSplit) {
const nodeMsg = await sendMsg(this.ctx, selfPeer, eles, [])
if (!nodeMsg) {
this.ctx.logger.warn('转发节点生成失败', eles)
continue
}
nodeMsgIds.push({ msgId: nodeMsg.msgId, peer: selfPeer })
await this.ctx.sleep(300)
}
deleteAfterSentFiles.map(path => unlink(path))
} catch (e) {
this.ctx.logger.error('生成转发消息节点失败', e)
}
}
}
// 检查srcPeer是否一致不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的使其保持一致才能够转发
const nodeMsgArray: RawMessage[] = []
let srcPeer: Peer
let needSendSelf = false
for (const { msgId, peer } of nodeMsgIds) {
const nodeMsg = (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgId])).msgList[0]
srcPeer ??= { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid }
if (srcPeer.peerUid !== nodeMsg.peerUid) {
needSendSelf = true
}
nodeMsgArray.push(nodeMsg)
}
let retMsgIds: string[] = []
if (needSendSelf) {
for (const msg of nodeMsgArray) {
if (msg.peerUid === selfPeer.peerUid) {
retMsgIds.push(msg.msgId)
continue
}
const clonedMsg = await this.cloneMsg(msg)
if (clonedMsg) retMsgIds.push(clonedMsg.msgId)
}
} else {
retMsgIds = nodeMsgArray.map(msg => msg.msgId)
}
if (retMsgIds.length === 0) {
throw Error('转发消息失败,节点为空')
}
const returnMsg = await this.ctx.ntMsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds)
returnMsg.msgShortId = this.ctx.store.createMsgShortId(destPeer, returnMsg.msgId)
return returnMsg
}
}
export class SendPrivateForwardMsg extends SendForwardMsg {
actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg
protected _handle(payload: Payload) {
payload.message_type = 'private'
return super._handle(payload)
}
}
export class SendGroupForwardMsg extends SendForwardMsg {
actionName = ActionName.GoCQHTTP_SendGroupForwardMsg
protected _handle(payload: Payload) {
payload.message_type = 'group'
return super._handle(payload)
}
}

View File

@@ -1,4 +1,4 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { unlink } from 'fs/promises'
import { checkFileReceived, uri2local } from '@/common/utils/file'
@@ -7,20 +7,24 @@ interface Payload {
group_id: number | string
content: string
image?: string
pinned?: number | string //扩展
confirm_required?: number | string //扩展
pinned: number | string //扩展
confirm_required: number | string //扩展
}
export class SendGroupNotice extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_SendGroupNotice
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
content: Schema.string().required(),
image: Schema.string(),
pinned: Schema.union([Number, String]).default(0),
confirm_required: Schema.union([Number, String]).default(1)
})
async _handle(payload: Payload) {
if (!payload.content) {
throw new Error('参数 content 不能为空')
}
const groupCode = payload.group_id.toString()
const pinned = Number(payload.pinned ?? 0)
const confirmRequired = Number(payload.confirm_required ?? 1)
const pinned = +payload.pinned
const confirmRequired = +payload.confirm_required
let picInfo: { id: string, width: number, height: number } | undefined
if (payload.image) {

View File

@@ -1,25 +1,22 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload {
message_id: number | string
}
export class SetEssenceMsg extends BaseAction<Payload, unknown> {
export class SetEssenceMsg extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_SetEssenceMsg
payloadSchema = Schema.object({
message_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
if (!payload.message_id) {
throw Error('message_id不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
const msg = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msg) {
throw new Error('msg not found')
}
return await this.ctx.ntGroupApi.addGroupEssence(
msg.Peer.peerUid,
msg.MsgId
)
await this.ctx.ntGroupApi.addGroupEssence(msg.peer.peerUid, msg.msgId)
return null
}
}

View File

@@ -0,0 +1,35 @@
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { SendElementEntities } from '@/ntqqapi/entities'
import { uri2local } from '@/common/utils'
import { sendMsg, createPeer, CreatePeerMode } from '../../helper/createMessage'
interface Payload {
group_id: number | string
file: string
name: string
folder?: string
folder_id?: string
}
export class UploadGroupFile extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_UploadGroupFile
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
file: Schema.string().required(),
name: Schema.string(),
folder: Schema.string(),
folder_id: Schema.string()
})
protected async _handle(payload: Payload): Promise<null> {
const { success, errMsg, path, fileName } = await uri2local(payload.file)
if (!success) {
throw new Error(errMsg)
}
const file = await SendElementEntities.file(this.ctx, path, payload.name || fileName, payload.folder ?? payload.folder_id)
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group)
await sendMsg(this.ctx, peer, [file], [])
return null
}
}

View File

@@ -1,32 +1,9 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { SendElementEntities } from '@/ntqqapi/entities'
import { uri2local } from '@/common/utils'
import { sendMsg, createPeer, CreatePeerMode } from '../../helper/createMessage'
interface UploadGroupFilePayload {
group_id: number | string
file: string
name: string
folder?: string
folder_id?: string
}
export class UploadGroupFile extends BaseAction<UploadGroupFilePayload, null> {
actionName = ActionName.GoCQHTTP_UploadGroupFile
protected async _handle(payload: UploadGroupFilePayload): Promise<null> {
const { success, errMsg, path, fileName } = await uri2local(payload.file)
if (!success) {
throw new Error(errMsg)
}
const file = await SendElementEntities.file(this.ctx, path, payload.name || fileName, payload.folder ?? payload.folder_id)
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group)
await sendMsg(this.ctx, peer, [file], [])
return null
}
}
interface UploadPrivateFilePayload {
user_id: number | string
file: string
@@ -35,6 +12,11 @@ interface UploadPrivateFilePayload {
export class UploadPrivateFile extends BaseAction<UploadPrivateFilePayload, null> {
actionName = ActionName.GoCQHTTP_UploadPrivateFile
payloadSchema = Schema.object({
user_id: Schema.union([Number, String]).required(),
file: Schema.string().required(),
name: Schema.string()
})
protected async _handle(payload: UploadPrivateFilePayload): Promise<null> {
const { success, errMsg, path, fileName } = await uri2local(payload.file)

View File

@@ -1,16 +0,0 @@
import { GroupEssenceMsgRet } from '@/ntqqapi/api'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
interface PayloadType {
group_id: number
pages?: number
}
export class GetGroupEssence extends BaseAction<PayloadType, GroupEssenceMsgRet | void> {
actionName = ActionName.GoCQHTTP_GetEssenceMsg
protected async _handle() {
throw '此 api 暂不支持'
}
}

View File

@@ -1,22 +1,19 @@
import { WebHonorType } from '@/ntqqapi/api'
import { ActionName } from '../types'
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
interface Payload {
group_id: number
type?: WebHonorType
group_id: number | string
type: 'talkative' | 'performer' | 'legend' | 'strong_newbie' | 'emotion' | 'all'
}
export class GetGroupHonorInfo extends BaseAction<Payload, unknown> {
actionName = ActionName.GetGroupHonorInfo
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
type: Schema.union(['talkative', 'performer', 'legend', 'strong_newbie', 'emotion', 'all']).default('all')
})
protected async _handle(payload: Payload) {
if (!payload.group_id) {
throw '缺少参数group_id'
}
if (!payload.type) {
payload.type = WebHonorType.ALL
}
return await this.ctx.ntWebApi.getGroupHonorInfo(payload.group_id.toString(), payload.type)
}
}

View File

@@ -1,6 +1,6 @@
import { OB11Group } from '../../types'
import { OB11Entities } from '../../entities'
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
interface Payload {
@@ -9,14 +9,17 @@ interface Payload {
class GetGroupInfo extends BaseAction<Payload, OB11Group> {
actionName = ActionName.GetGroupInfo
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
const group = (await this.ctx.ntGroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString())
const groupCode = payload.group_id.toString()
const group = (await this.ctx.ntGroupApi.getGroups()).find(e => e.groupCode === groupCode)
if (group) {
return OB11Entities.group(group)
} else {
throw `${payload.group_id}不存在`
}
throw new Error(`${payload.group_id}不存在`)
}
}

View File

@@ -1,8 +1,7 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { OB11GroupMember } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars'
import { isNullable } from 'cosmokit'
interface Payload {
@@ -12,35 +11,26 @@ interface Payload {
class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
actionName = ActionName.GetGroupMemberInfo
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
user_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
const member = await this.ctx.ntGroupApi.getGroupMember(payload.group_id.toString(), payload.user_id.toString())
const groupCode = payload.group_id.toString()
const member = await this.ctx.ntGroupApi.getGroupMember(groupCode, payload.user_id.toString())
if (member) {
if (isNullable(member.sex)) {
//log('获取群成员详细信息')
const info = await this.ctx.ntUserApi.getUserDetailInfo(member.uid)
//log('群成员详细信息结果', info)
Object.assign(member, info)
}
const ret = OB11Entities.groupMember(payload.group_id.toString(), member)
const self = await this.ctx.ntGroupApi.getGroupMember(payload.group_id.toString(), selfInfo.uid)
if (self?.role === 3 || self?.role === 4) {
const webGroupMembers = await this.ctx.ntWebApi.getGroupMembers(payload.group_id.toString())
const target = webGroupMembers.find(e => e?.uin && e.uin === ret.user_id)
if (target) {
ret.join_time = target.join_time
ret.last_sent_time = target.last_speak_time
ret.qage = target.qage
ret.level = target.lv.level.toString()
}
}
const ret = OB11Entities.groupMember(groupCode, member)
const date = Math.round(Date.now() / 1000)
ret.last_sent_time ||= Number(member.lastSpeakTime || date)
ret.join_time ||= Number(member.joinTime || date)
ret.last_sent_time ??= date
ret.join_time ??= date
return ret
} else {
throw `群成员${payload.user_id}不存在`
}
throw new Error(`群成员${payload.user_id}不存在`)
}
}

View File

@@ -1,57 +1,35 @@
import { OB11GroupMember } from '../../types'
import { OB11Entities } from '../../entities'
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars'
interface Payload {
group_id: number | string
no_cache: boolean | string
no_cache?: boolean | string
}
class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
actionName = ActionName.GetGroupMemberList
protected async _handle(payload: Payload) {
const groupMembers = await this.ctx.ntGroupApi.getGroupMembers(payload.group_id.toString())
const groupMembersArr = Array.from(groupMembers.values())
let _groupMembers = groupMembersArr.map(item => {
return OB11Entities.groupMember(payload.group_id.toString(), item)
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required()
})
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>()
protected async _handle(payload: Payload) {
const groupCode = payload.group_id.toString()
let groupMembers = await this.ctx.ntGroupApi.getGroupMembers(groupCode)
if (groupMembers.size === 0) {
await this.ctx.sleep(100)
groupMembers = await this.ctx.ntGroupApi.getGroupMembers(groupCode)
}
const groupMembersArr = Array.from(groupMembers.values())
const date = Math.round(Date.now() / 1000)
for (let i = 0, len = _groupMembers.length; i < len; i++) {
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
_groupMembers[i].join_time = date
_groupMembers[i].last_sent_time = date
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i])
}
const selfRole = groupMembers.get(selfInfo.uid)?.role
const isPrivilege = selfRole === 3 || selfRole === 4
if (isPrivilege) {
const webGroupMembers = await this.ctx.ntWebApi.getGroupMembers(payload.group_id.toString())
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
if (!webGroupMembers[i]?.uin) {
continue
}
const MemberData = MemberMap.get(webGroupMembers[i]?.uin)
if (MemberData) {
MemberData.join_time = webGroupMembers[i]?.join_time
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time
MemberData.qage = webGroupMembers[i]?.qage
MemberData.level = webGroupMembers[i]?.lv.level.toString()
MemberMap.set(webGroupMembers[i]?.uin, MemberData)
}
}
}
_groupMembers = Array.from(MemberMap.values())
return _groupMembers
return groupMembersArr.map(item => {
const member = OB11Entities.groupMember(groupCode, item)
member.join_time ??= date
member.last_sent_time ??= date
return member
})
}
}

View File

@@ -6,7 +6,6 @@ class SendGroupMsg extends SendMsg {
actionName = ActionName.SendGroupMsg
protected _handle(payload: OB11PostSendMsg) {
delete (payload as Partial<OB11PostSendMsg>).user_id
payload.message_type = 'group'
return super._handle(payload)
}

View File

@@ -1,7 +1,7 @@
import type Adapter from '../adapter'
import GetMsg from './msg/GetMsg'
import GetLoginInfo from './system/GetLoginInfo'
import { GetFriendList, GetFriendWithCategory } from './user/GetFriendList'
import { GetFriendList } from './user/GetFriendList'
import GetGroupList from './group/GetGroupList'
import GetGroupInfo from './group/GetGroupInfo'
import GetGroupMemberList from './group/GetGroupMemberList'
@@ -37,7 +37,6 @@ import GetImage from './file/GetImage'
import GetRecord from './file/GetRecord'
import { MarkMsgAsRead } from './go-cqhttp/MarkMsgAsRead'
import CleanCache from './system/CleanCache'
import { UploadGroupFile, UploadPrivateFile } from './go-cqhttp/UploadFile'
import { GetConfigAction, SetConfigAction } from './llonebot/Config'
import GetGroupAddRequest from './llonebot/GetGroupAddRequest'
import SetQQAvatar from './llonebot/SetQQAvatar'
@@ -48,12 +47,12 @@ import { GetForwardMsg } from './go-cqhttp/GetForwardMsg'
import { GetCookies } from './user/GetCookie'
import { SetMsgEmojiLike } from './msg/SetMsgEmojiLike'
import { ForwardFriendSingleMsg, ForwardGroupSingleMsg } from './msg/ForwardSingleMsg'
import { GetGroupEssence } from './group/GetGroupEssence'
import { GetEssenceMsgList } from './go-cqhttp/GetGroupEssence'
import { GetGroupHonorInfo } from './group/GetGroupHonorInfo'
import { HandleQuickOperation } from './go-cqhttp/QuickOperation'
import { SetEssenceMsg } from './go-cqhttp/SetEssenceMsg'
import { DelEssenceMsg } from './go-cqhttp/DelEssenceMsg'
import GetEvent from './llonebot/GetEvent'
import { DeleteEssenceMsg } from './go-cqhttp/DelEssenceMsg'
import { GetEvent } from './llonebot/GetEvent'
import { DelGroupFile } from './go-cqhttp/DelGroupFile'
import { GetGroupSystemMsg } from './go-cqhttp/GetGroupSystemMsg'
import { CreateGroupFileFolder } from './go-cqhttp/CreateGroupFileFolder'
@@ -63,6 +62,13 @@ import { GetGroupRootFiles } from './go-cqhttp/GetGroupRootFiles'
import { SetOnlineStatus } from './llonebot/SetOnlineStatus'
import { SendGroupNotice } from './go-cqhttp/SendGroupNotice'
import { GetProfileLike } from './llonebot/GetProfileLike'
import { FetchEmojiLike } from './llonebot/FetchEmojiLike'
import { FetchCustomFace } from './llonebot/FetchCustomFace'
import { GetFriendMsgHistory } from './llonebot/GetFriendMsgHistory'
import { GetGroupFilesByFolder } from './go-cqhttp/GetGroupFilesByFolder'
import { GetFriendWithCategory } from './llonebot/GetFriendWithCategory'
import { UploadGroupFile } from './go-cqhttp/UploadGroupFile'
import { UploadPrivateFile } from './go-cqhttp/UploadPrivateFile'
export function initActionMap(adapter: Adapter) {
const actionHandlers = [
@@ -76,6 +82,9 @@ export function initActionMap(adapter: Adapter) {
new GetEvent(adapter),
new SetOnlineStatus(adapter),
new GetProfileLike(adapter),
new GetFriendMsgHistory(adapter),
new FetchEmojiLike(adapter),
new FetchCustomFace(adapter),
// onebot11
new SendLike(adapter),
new GetMsg(adapter),
@@ -109,8 +118,8 @@ export function initActionMap(adapter: Adapter) {
new SetMsgEmojiLike(adapter),
new ForwardFriendSingleMsg(adapter),
new ForwardGroupSingleMsg(adapter),
//以下为go-cqhttp api
new GetGroupEssence(adapter),
// go-cqhttp
new GetEssenceMsgList(adapter),
new GetGroupHonorInfo(adapter),
new SendForwardMsg(adapter),
new SendGroupForwardMsg(adapter),
@@ -125,14 +134,15 @@ export function initActionMap(adapter: Adapter) {
new GetForwardMsg(adapter),
new HandleQuickOperation(adapter),
new SetEssenceMsg(adapter),
new DelEssenceMsg(adapter),
new DeleteEssenceMsg(adapter),
new DelGroupFile(adapter),
new GetGroupSystemMsg(adapter),
new CreateGroupFileFolder(adapter),
new DelGroupFolder(adapter),
new GetGroupAtAllRemain(adapter),
new GetGroupRootFiles(adapter),
new SendGroupNotice(adapter)
new SendGroupNotice(adapter),
new GetGroupFilesByFolder(adapter),
]
const actionMap = new Map<string, BaseAction<any, unknown>>()
for (const action of actionHandlers) {

View File

@@ -0,0 +1,18 @@
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
interface Payload {
count: number | string
}
export class FetchCustomFace extends BaseAction<Payload, string[]> {
actionName = ActionName.FetchCustomFace
payloadSchema = Schema.object({
count: Schema.union([Number, String]).default(48)
})
async _handle(payload: Payload) {
const ret = await this.ctx.ntMsgApi.fetchFavEmojiList(+payload.count)
return ret.emojiInfoList.map(e => e.url)
}
}

View File

@@ -0,0 +1,27 @@
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { Dict } from 'cosmokit'
interface Payload {
emojiId: string
emojiType: string
message_id: string | number
count: string | number
}
export class FetchEmojiLike extends BaseAction<Payload, Dict> {
actionName = ActionName.FetchEmojiLike
payloadSchema = Schema.object({
emojiId: Schema.string().required(),
emojiType: Schema.string().required(),
message_id: Schema.union([Number, String]).required(),
count: Schema.union([Number, String]).default(20)
})
async _handle(payload: Payload) {
const msgInfo = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msgInfo) throw new Error('消息不存在')
const { msgSeq } = (await this.ctx.ntMsgApi.getMsgsByMsgId(msgInfo.peer, [msgInfo.msgId])).msgList[0]
return await this.ctx.ntMsgApi.getMsgEmojiLikesList(msgInfo.peer, msgSeq, payload.emojiId, payload.emojiType, +payload.count)
}
}

View File

@@ -11,7 +11,7 @@ interface Payload {
timeout: number
}
export default class GetEvent extends BaseAction<Payload, PostEventType[]> {
export class GetEvent extends BaseAction<Payload, PostEventType[]> {
actionName = ActionName.GetEvent
protected async _handle(payload: Payload): Promise<PostEventType[]> {

View File

@@ -0,0 +1,52 @@
import { BaseAction, Schema } from '../BaseAction'
import { OB11Message } from '@/onebot11/types'
import { ActionName } from '../types'
import { ChatType, RawMessage } from '@/ntqqapi/types'
import { OB11Entities } from '@/onebot11/entities'
import { filterNullable } from '@/common/utils/misc'
interface Payload {
user_id: number | string
message_seq?: number | string
message_id?: number | string
count: number | string
reverseOrder: boolean
}
interface Response {
messages: OB11Message[]
}
export class GetFriendMsgHistory extends BaseAction<Payload, Response> {
actionName = ActionName.GetFriendMsgHistory
payloadSchema = Schema.object({
user_id: Schema.union([Number, String]).required(),
message_seq: Schema.union([Number, String]),
message_id: Schema.union([Number, String]),
count: Schema.union([Number, String]).default(20),
reverseOrder: Schema.boolean().default(false)
})
async _handle(payload: Payload): Promise<Response> {
const startMsgId = payload.message_seq ?? payload.message_id
let msgList: RawMessage[]
if (startMsgId) {
const msgInfo = await this.ctx.store.getMsgInfoByShortId(+startMsgId)
if (!msgInfo) throw new Error(`消息${startMsgId}不存在`)
msgList = (await this.ctx.ntMsgApi.getMsgHistory(msgInfo.peer, msgInfo.msgId, +payload.count)).msgList
} else {
const uid = await this.ctx.ntUserApi.getUidByUin(payload.user_id.toString())
if (!uid) throw new Error(`记录${payload.user_id}不存在`)
const isBuddy = await this.ctx.ntFriendApi.isBuddy(uid)
const peer = { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid: uid }
msgList = (await this.ctx.ntMsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList
}
if (msgList.length === 0) throw new Error('未找到消息')
if (payload.reverseOrder) msgList.reverse()
for (const msg of msgList) {
msg.msgShortId = this.ctx.store.createMsgShortId({ chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId)
}
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Entities.message(this.ctx, msg)))
return { messages: filterNullable(ob11MsgList) }
}
}

View File

@@ -0,0 +1,17 @@
import { BaseAction } from '../BaseAction'
import { OB11User } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
import { getBuildVersion } from '@/common/utils'
export class GetFriendWithCategory extends BaseAction<void, OB11User[]> {
actionName = ActionName.GetFriendsWithCategory
protected async _handle() {
if (getBuildVersion() >= 26702) {
return OB11Entities.friendsV2(await this.ctx.ntFriendApi.getBuddyV2ExWithCate(true))
} else {
throw new Error('this ntqq version not support, must be 26702 or later')
}
}
}

View File

@@ -1,5 +1,5 @@
import { BaseAction } from '../BaseAction'
import { GroupNotify, GroupNotifyStatus } from '@/ntqqapi/types'
import { GroupNotifyStatus } from '@/ntqqapi/types'
import { ActionName } from '../types'
interface OB11GroupRequestNotify {
@@ -13,7 +13,7 @@ export default class GetGroupAddRequest extends BaseAction<null, OB11GroupReques
protected async _handle(): Promise<OB11GroupRequestNotify[]> {
const data = await this.ctx.ntGroupApi.getGroupIgnoreNotifies()
const notifies: GroupNotify[] = data.notifies.filter((notify) => notify.status === GroupNotifyStatus.KUNHANDLE)
const notifies = data.notifies.filter(notify => notify.status === GroupNotifyStatus.KUNHANDLE)
const returnData: OB11GroupRequestNotify[] = []
for (const notify of notifies) {
const uin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid)

View File

@@ -8,10 +8,10 @@ export class GetProfileLike extends BaseAction<void, Dict[]> {
async _handle() {
const ret = await this.ctx.ntUserApi.getProfileLike(selfInfo.uid)
const listdata = ret.info.userLikeInfos[0].favoriteInfo.userInfos
for (const item of listdata) {
const data = ret.info.userLikeInfos[0].favoriteInfo.userInfos
for (const item of data) {
item.uin = Number(await this.ctx.ntUserApi.getUinByUid(item.uid)) || 0
}
return listdata
return data
}
}

View File

@@ -1,4 +1,4 @@
import fs from 'node:fs'
import { unlink } from 'node:fs/promises'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
import { checkFileReceived, uri2local } from '../../../common/utils/file'
@@ -13,18 +13,17 @@ export default class SetAvatar extends BaseAction<Payload, null> {
protected async _handle(payload: Payload): Promise<null> {
const { path, isLocal, errMsg } = await uri2local(payload.file)
if (errMsg) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`
throw new Error(errMsg)
}
if (path) {
await checkFileReceived(path, 5000) // 文件不存在QQ会崩溃需要提前判断
const ret = await this.ctx.ntUserApi.setQQAvatar(path)
if (!isLocal) {
fs.unlink(path, () => { })
unlink(path)
}
if (!ret) {
throw `头像${payload.file}设置失败,api无返回`
}
// log(`头像设置返回:${JSON.stringify(ret)}`)
if ((ret.result as number) === 1004022) {
throw `头像${payload.file}设置失败,文件可能不是图片格式`
} else if (ret.result !== 0) {
@@ -32,7 +31,7 @@ export default class SetAvatar extends BaseAction<Payload, null> {
}
} else {
if (!isLocal) {
fs.unlink(path, () => { })
unlink(path)
}
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`
}

View File

@@ -1,6 +1,5 @@
import { ActionName } from '../types'
import { BaseAction } from '../BaseAction'
import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload {
message_id: number | string
@@ -13,11 +12,11 @@ class DeleteMsg extends BaseAction<Payload, void> {
if (!payload.message_id) {
throw new Error('参数message_id不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
const msg = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msg) {
throw new Error(`消息${payload.message_id}不存在`)
}
const data = await this.ctx.ntMsgApi.recallMsg(msg.Peer, [msg.MsgId])
const data = await this.ctx.ntMsgApi.recallMsg(msg.peer, [msg.msgId])
if (data.result !== 0) {
this.ctx.logger.error('delete_msg', payload.message_id, data)
throw new Error(`消息撤回失败`)

View File

@@ -2,7 +2,6 @@ import { BaseAction } from '../BaseAction'
import { ChatType } from '@/ntqqapi/types'
import { ActionName } from '../types'
import { Peer } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload {
message_id: number | string
@@ -26,12 +25,12 @@ abstract class ForwardSingleMsg extends BaseAction<Payload, null> {
if (!payload.message_id) {
throw Error('message_id不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
const msg = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msg) {
throw new Error(`无法找到消息${payload.message_id}`)
}
const peer = await this.getTargetPeer(payload)
const ret = await this.ctx.ntMsgApi.forwardMsg(msg.Peer, peer, [msg.MsgId])
const ret = await this.ctx.ntMsgApi.forwardMsg(msg.peer, peer, [msg.msgId])
if (ret.result !== 0) {
throw new Error(`转发消息失败 ${ret.errMsg}`)
}

View File

@@ -2,7 +2,6 @@ import { BaseAction } from '../BaseAction'
import { OB11Message } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique'
export interface PayloadType {
message_id: number | string
@@ -17,22 +16,21 @@ class GetMsg extends BaseAction<PayloadType, OB11Message> {
if (!payload.message_id) {
throw new Error('参数message_id不能为空')
}
const msgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString())
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(msgShortId || +payload.message_id)
if (!msgIdWithPeer) {
const msgInfo = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msgInfo) {
throw new Error('消息不存在')
}
const peer = {
guildId: '',
peerUid: msgIdWithPeer.Peer.peerUid,
chatType: msgIdWithPeer.Peer.chatType
peerUid: msgInfo.peer.peerUid,
chatType: msgInfo.peer.chatType
}
const msg = this.adapter.getMsgCache(msgIdWithPeer.MsgId) ?? (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgIdWithPeer.MsgId])).msgList[0]
const msg = this.adapter.getMsgCache(msgInfo.msgId) ?? (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgInfo.msgId])).msgList[0]
const retMsg = await OB11Entities.message(this.ctx, msg)
if (!retMsg) {
throw new Error('消息为空')
}
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgId)!
retMsg.message_id = this.ctx.store.createMsgShortId(peer, msg.msgId)
retMsg.message_seq = retMsg.message_id
retMsg.real_id = retMsg.message_id
return retMsg

View File

@@ -1,25 +1,14 @@
import {
ChatType,
ElementType,
RawMessage,
SendMessageElement,
} from '@/ntqqapi/types'
import {
OB11MessageCustomMusic,
OB11MessageData,
OB11MessageDataType,
OB11MessageJson,
OB11MessageMusic,
OB11MessageNode,
OB11PostSendMsg,
} from '../../types'
import fs from 'node:fs'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
import { CustomMusicSignPostData, IdMusicSignPostData, MusicSign, MusicSignPostData } from '@/common/utils/sign'
import { Peer } from '@/ntqqapi/types/msg'
import { MessageUnique } from '@/common/utils/messageUnique'
import { selfInfo } from '@/common/globalVars'
import { convertMessage2List, createSendElements, sendMsg, createPeer, CreatePeerMode } from '../../helper/createMessage'
interface ReturnData {
@@ -42,12 +31,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnData> {
payload.auto_escape === true || payload.auto_escape === 'true',
)
if (this.getSpecialMsgNum(messages, OB11MessageDataType.node)) {
try {
const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[])
return { message_id: returnMsg.msgShortId! }
} catch (e) {
throw '发送转发消息失败 ' + e
}
throw new Error('请使用 /send_group_forward_msg 或 /send_private_forward_msg 进行合并转发')
}
else if (this.getSpecialMsgNum(messages, OB11MessageDataType.music)) {
const music = messages[0] as OB11MessageMusic
@@ -114,140 +98,10 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnData> {
private getSpecialMsgNum(message: OB11MessageData[], msgType: OB11MessageDataType): number {
if (Array.isArray(message)) {
return message.filter((msg) => msg.type == msgType).length
return message.filter((msg) => msg.type === msgType).length
}
return 0
}
private async cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
this.ctx.logger.info('克隆的目标消息', msg)
const sendElements: SendMessageElement[] = []
for (const ele of msg.elements) {
sendElements.push(ele as SendMessageElement)
}
if (sendElements.length === 0) {
this.ctx.logger.warn('需要clone的消息无法解析将会忽略掉', msg)
}
this.ctx.logger.info('克隆消息', sendElements)
try {
const peer = {
chatType: ChatType.friend,
peerUid: selfInfo.uid
}
const nodeMsg = await this.ctx.ntMsgApi.sendMsg(peer, sendElements)
await this.ctx.sleep(400)
return nodeMsg
} catch (e) {
this.ctx.logger.warn(e, '克隆转发消息失败,将忽略本条消息', msg)
}
}
// 返回一个合并转发的消息id
private async handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[]) {
const selfPeer = {
chatType: ChatType.friend,
peerUid: selfInfo.uid,
}
let nodeMsgIds: string[] = []
// 先判断一遍是不是id和自定义混用
for (const messageNode of messageNodes) {
// 一个node表示一个人的消息
const nodeId = messageNode.data.id
// 有nodeId表示一个子转发消息卡片
if (nodeId) {
const nodeMsg = await MessageUnique.getMsgIdAndPeerByShortId(+nodeId) || await MessageUnique.getPeerByMsgId(nodeId)
if (!nodeMsg) {
this.ctx.logger.warn('转发消息失败,未找到消息', nodeId)
continue
}
nodeMsgIds.push(nodeMsg.MsgId)
}
else {
// 自定义的消息
// 提取消息段发给自己生成消息id
try {
const { sendElements, deleteAfterSentFiles } = await createSendElements(
this.ctx,
convertMessage2List(messageNode.data.content),
destPeer
)
this.ctx.logger.info('开始生成转发节点', sendElements)
const sendElementsSplit: SendMessageElement[][] = []
let splitIndex = 0
for (const ele of sendElements) {
if (!sendElementsSplit[splitIndex]) {
sendElementsSplit[splitIndex] = []
}
if (ele.elementType === ElementType.FILE || ele.elementType === ElementType.VIDEO) {
if (sendElementsSplit[splitIndex].length > 0) {
splitIndex++
}
sendElementsSplit[splitIndex] = [ele]
splitIndex++
}
else {
sendElementsSplit[splitIndex].push(ele)
}
this.ctx.logger.info(sendElementsSplit)
}
// log("分割后的转发节点", sendElementsSplit)
for (const eles of sendElementsSplit) {
const nodeMsg = await sendMsg(this.ctx, selfPeer, eles, [])
if (!nodeMsg) {
this.ctx.logger.warn('转发节点生成失败', eles)
continue
}
nodeMsgIds.push(nodeMsg.msgId)
await this.ctx.sleep(400)
}
deleteAfterSentFiles.map((f) => fs.unlink(f, () => {
}))
} catch (e) {
this.ctx.logger.error('生成转发消息节点失败', e)
}
}
}
// 检查srcPeer是否一致不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的使其保持一致才能够转发
const nodeMsgArray: RawMessage[] = []
let srcPeer: Peer | null = null
let needSendSelf = false
for (const msgId of nodeMsgIds) {
const nodeMsgPeer = await MessageUnique.getPeerByMsgId(msgId)
if (nodeMsgPeer) {
const nodeMsg = (await this.ctx.ntMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0]
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid }
if (srcPeer.peerUid !== nodeMsg.peerUid) {
needSendSelf = true
}
nodeMsgArray.push(nodeMsg)
}
}
nodeMsgIds = nodeMsgArray.map((msg) => msg.msgId)
if (needSendSelf) {
for (const msg of nodeMsgArray) {
if (msg.peerUid === selfPeer.peerUid) continue
await this.cloneMsg(msg)
}
}
// elements之间用换行符分隔
// let _sendForwardElements: SendMessageElement[] = []
// for(let i = 0; i < sendForwardElements.length; i++){
// _sendForwardElements.push(sendForwardElements[i])
// _sendForwardElements.push(SendMsgElementConstructor.text("\n\n"))
// }
// const nodeMsg = await NTQQApi.sendMsg(selfPeer, _sendForwardElements, true);
// nodeIds.push(nodeMsg.msgId)
// await sleep(500);
// 开发转发
if (nodeMsgIds.length === 0) {
throw Error('转发消息失败,节点为空')
}
const returnMsg = await this.ctx.ntMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
returnMsg.msgShortId = MessageUnique.createMsg(destPeer, returnMsg.msgId)
return returnMsg
}
}
export default SendMsg

View File

@@ -1,6 +1,5 @@
import { ActionName } from '../types'
import { BaseAction } from '../BaseAction'
import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload {
message_id: number | string
@@ -14,19 +13,19 @@ export class SetMsgEmojiLike extends BaseAction<Payload, unknown> {
if (!payload.message_id) {
throw Error('message_id不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
const msg = await this.ctx.store.getMsgInfoByShortId(+payload.message_id)
if (!msg) {
throw new Error('msg not found')
}
if (!payload.emoji_id) {
throw new Error('emojiId not found')
}
const msgData = (await this.ctx.ntMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId])).msgList
const msgData = (await this.ctx.ntMsgApi.getMsgsByMsgId(msg.peer, [msg.msgId])).msgList
if (!msgData || msgData.length == 0 || !msgData[0].msgSeq) {
throw new Error('find msg by msgid error')
}
return await this.ctx.ntMsgApi.setEmojiLike(
msg.Peer,
msg.peer,
msgData[0].msgSeq,
payload.emoji_id.toString(),
true

View File

@@ -21,6 +21,9 @@ export enum ActionName {
GetEvent = 'get_event',
SetOnlineStatus = 'set_online_status',
GetProfileLike = 'get_profile_like',
FetchEmojiLike = 'fetch_emoji_like',
FetchCustomFace = 'fetch_custom_face',
GetFriendMsgHistory = 'get_friend_msg_history',
// onebot 11
SendLike = 'send_like',
GetLoginInfo = 'get_login_info',
@@ -66,7 +69,7 @@ export enum ActionName {
GoCQHTTP_DownloadFile = 'download_file',
GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history',
GoCQHTTP_GetForwardMsg = 'get_forward_msg',
GoCQHTTP_GetEssenceMsg = 'get_essence_msg_list',
GoCQHTTP_GetEssenceMsgList = 'get_essence_msg_list',
GoCQHTTP_HandleQuickOperation = '.handle_quick_operation',
GetGroupHonorInfo = 'get_group_honor_info',
GoCQHTTP_SetEssenceMsg = 'set_essence_msg',
@@ -78,4 +81,5 @@ export enum ActionName {
GoCQHTTP_GetGroupAtAllRemain = 'get_group_at_all_remain',
GoCQHTTP_GetGroupRootFiles = 'get_group_root_files',
GoCQHTTP_SendGroupNotice = '_send_group_notice',
GoCQHTTP_GetGroupFilesByFolder = 'get_group_files_by_folder'
}

View File

@@ -1,35 +1,24 @@
import { BaseAction } from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { OB11User } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
import { getBuildVersion } from '@/common/utils'
interface Payload {
no_cache: boolean | string
no_cache: boolean
}
export class GetFriendList extends BaseAction<Payload, OB11User[]> {
actionName = ActionName.GetFriendList
payloadSchema = Schema.object({
no_cache: Schema.boolean().default(false)
})
protected async _handle(payload: Payload) {
const refresh = payload?.no_cache === true || payload?.no_cache === 'true'
const refresh = payload.no_cache
if (getBuildVersion() >= 26702) {
return OB11Entities.friendsV2(await this.ctx.ntFriendApi.getBuddyV2(refresh))
}
return OB11Entities.friends(await this.ctx.ntFriendApi.getFriends())
}
}
// extend
export class GetFriendWithCategory extends BaseAction<void, OB11User[]> {
actionName = ActionName.GetFriendsWithCategory
protected async _handle() {
if (getBuildVersion() >= 26702) {
//全新逻辑
return OB11Entities.friendsV2(await this.ctx.ntFriendApi.getBuddyV2ExWithCate(true))
} else {
throw new Error('this ntqq version not support, must be 26702 or later')
}
}
}

View File

@@ -10,15 +10,12 @@ export default class SendLike extends BaseAction<Payload, null> {
actionName = ActionName.SendLike
protected async _handle(payload: Payload): Promise<null> {
try {
const qq = payload.user_id.toString()
const uid: string = await this.ctx.ntUserApi.getUidByUin(qq) || ''
const uin = payload.user_id.toString()
const uid = await this.ctx.ntUserApi.getUidByUin(uin)
if (!uid) throw new Error('无法获取用户信息')
const result = await this.ctx.ntUserApi.like(uid, +payload.times || 1)
if (result?.result !== 0) {
throw Error(result?.errMsg)
}
} catch (e) {
throw `点赞失败 ${e}`
if (result.result !== 0) {
throw new Error(result.errMsg)
}
return null
}

View File

@@ -12,7 +12,16 @@ export default class SetFriendAddRequest extends BaseAction<Payload, null> {
protected async _handle(payload: Payload): Promise<null> {
const approve = payload.approve?.toString() !== 'false'
await this.ctx.ntFriendApi.handleFriendRequest(payload.flag, approve)
const data = payload.flag.split('|')
if (data.length < 2) {
throw new Error('无效的flag')
}
const uid = data[0]
const reqTime = data[1]
await this.ctx.ntFriendApi.handleFriendRequest(uid, reqTime, approve)
if (payload.remark) {
await this.ctx.ntFriendApi.setBuddyRemark(uid, payload.remark)
}
return null
}
}

View File

@@ -13,7 +13,6 @@ import {
} from '../ntqqapi/types'
import { OB11GroupRequestEvent } from './event/request/OB11GroupRequest'
import { OB11FriendRequestEvent } from './event/request/OB11FriendRequest'
import { MessageUnique } from '../common/utils/messageUnique'
import { GroupDecreaseSubType, OB11GroupDecreaseEvent } from './event/notice/OB11GroupDecreaseEvent'
import { selfInfo } from '../common/globalVars'
import { OB11Config, Config as LLOBConfig } from '../common/types'
@@ -37,7 +36,7 @@ declare module 'cordis' {
}
class OneBot11Adapter extends Service {
static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi', 'ntWebApi']
static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi', 'ntWebApi', 'store']
public messages: Map<string, RawMessage> = new Map()
public startTime = 0
@@ -190,7 +189,7 @@ class OneBot11Adapter extends Service {
chatType: message.chatType,
peerUid: message.peerUid
}
message.msgShortId = MessageUnique.createMsg(peer, message.msgId)
message.msgShortId = this.ctx.store.createMsgShortId(peer, message.msgId)
this.addMsgCache(message)
OB11Entities.message(this.ctx, message)
@@ -227,7 +226,11 @@ class OneBot11Adapter extends Service {
}
private handleRecallMsg(message: RawMessage) {
const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId)
const peer = {
peerUid: message.peerUid,
chatType: message.chatType
}
const oriMessageId = this.ctx.store.getShortIdByMsgInfo(peer, message.msgId)
if (!oriMessageId) {
return
}

View File

@@ -15,18 +15,15 @@ import {
FaceIndex,
GrayTipElementSubType,
Group,
Peer,
GroupMember,
RawMessage,
Sex,
TipGroupElementType,
User,
FriendV2,
ChatType2
FriendV2
} from '../ntqqapi/types'
import { EventType } from './event/OB11BaseEvent'
import { encodeCQCode } from './cqcode'
import { MessageUnique } from '../common/utils/messageUnique'
import { OB11GroupIncreaseEvent } from './event/notice/OB11GroupIncreaseEvent'
import { OB11GroupBanEvent } from './event/notice/OB11GroupBanEvent'
import { OB11GroupUploadNoticeEvent } from './event/notice/OB11GroupUploadNoticeEvent'
@@ -93,11 +90,11 @@ export namespace OB11Entities {
resMsg.sub_type = 'friend'
resMsg.sender.nickname = (await ctx.ntUserApi.getUserDetailInfo(msg.senderUid)).nick
}
else if (msg.chatType as unknown as ChatType2 === ChatType2.KCHATTYPETEMPC2CFROMGROUP) {
else if (msg.chatType === ChatType.temp) {
resMsg.sub_type = 'group'
resMsg.temp_source = 0 //群聊
resMsg.sender.nickname = (await ctx.ntUserApi.getUserDetailInfo(msg.senderUid)).nick
const ret = await ctx.ntMsgApi.getTempChatInfo(ChatType2.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid)
const ret = await ctx.ntMsgApi.getTempChatInfo(ChatType.temp, msg.senderUid)
if (ret?.result === 0) {
resMsg.sender.group_id = Number(ret.tmpChatInfo?.groupCode)
} else {
@@ -155,13 +152,21 @@ export namespace OB11Entities {
guildId: ''
}
try {
const { replayMsgSeq, replyMsgTime, senderUidStr } = replyElement
const { replayMsgSeq, replyMsgTime } = replyElement
const records = msg.records.find(msgRecord => msgRecord.msgId === replyElement.sourceMsgIdInRecords)
if (!records || !replyMsgTime || !senderUidStr) {
const senderUid = replyElement.senderUidStr || records?.senderUid
if (!records || !replyMsgTime || !senderUid) {
throw new Error('找不到回复消息')
}
const { msgList } = await ctx.ntMsgApi.queryMsgsWithFilterExBySeq(peer, replayMsgSeq, replyMsgTime, [senderUidStr])
const replyMsg = msgList.find(msg => msg.msgRandom === records.msgRandom)
const { msgList } = await ctx.ntMsgApi.queryMsgsWithFilterExBySeq(peer, replayMsgSeq, replyMsgTime, [senderUid])
let replyMsg: RawMessage | undefined
if (records.msgRandom !== '0') {
replyMsg = msgList.find(msg => msg.msgRandom === records.msgRandom)
} else {
ctx.logger.info('msgRandom is missing', replyElement, records)
replyMsg = msgList[0]
}
// 284840486: 合并消息内侧 消息具体定位不到
if (!replyMsg && msg.peerUin !== '284840486') {
@@ -171,7 +176,7 @@ export namespace OB11Entities {
messageSegment = {
type: OB11MessageDataType.reply,
data: {
id: MessageUnique.createMsg(peer, replyMsg ? replyMsg.msgId : records.msgId).toString()
id: ctx.store.createMsgShortId(peer, replyMsg ? replyMsg.msgId : records.msgId).toString()
}
}
} catch (e) {
@@ -191,7 +196,7 @@ export namespace OB11Entities {
file_size: fileSize,
}
}
MessageUnique.addFileCache({
ctx.store.addFileCache({
peerUid: msg.peerUid,
msgId: msg.msgId,
msgTime: +msg.msgTime,
@@ -219,7 +224,7 @@ export namespace OB11Entities {
file_size: fileSize,
}
}
MessageUnique.addFileCache({
ctx.store.addFileCache({
peerUid: msg.peerUid,
msgId: msg.msgId,
msgTime: +msg.msgTime,
@@ -244,7 +249,7 @@ export namespace OB11Entities {
file_size: fileSize,
}
}
MessageUnique.addFileCache({
ctx.store.addFileCache({
peerUid: msg.peerUid,
msgId: msg.msgId,
msgTime: +msg.msgTime,
@@ -268,7 +273,7 @@ export namespace OB11Entities {
file_size: fileSize,
}
}
MessageUnique.addFileCache({
ctx.store.addFileCache({
peerUid: msg.peerUid,
msgId: msg.msgId,
msgTime: +msg.msgTime,
@@ -388,7 +393,8 @@ export namespace OB11Entities {
)
}
}
if (grayTipElement.xmlElement?.templId === '10229') {
if (grayTipElement.xmlElement?.templId === '10229' || grayTipElement.jsonGrayTipElement?.busiId === '19324') {
ctx.logger.info('收到好友添加消息', msg.peerUid)
const uin = +msg.peerUin || +(await ctx.ntUserApi.getUinByUid(msg.peerUid))
return new OB11FriendAddNoticeEvent(uin)
}
@@ -497,14 +503,6 @@ export namespace OB11Entities {
const xmlElement = grayTipElement.xmlElement
if (xmlElement?.templId === '10382') {
// 表情回应消息
// "content":
// "<gtip align=\"center\">
// <qq uin=\"u_snYxnEfja-Po_\" col=\"3\" jp=\"3794\"/>
// <nor txt=\"回应了你的\"/>
// <url jp= \"\" msgseq=\"74711\" col=\"3\" txt=\"消息:\"/>
// <face type=\"1\" id=\"76\"> </face>
// </gtip>",
const emojiLikeData = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '',
@@ -523,7 +521,7 @@ export namespace OB11Entities {
if (!replyMsgList?.length) {
return
}
const shortId = MessageUnique.getShortIdByMsgId(replyMsgList[0].msgId)
const shortId = ctx.store.getShortIdByMsgInfo(peer, replyMsgList[0].msgId)
return new OB11GroupMsgEmojiLikeEvent(
parseInt(msg.peerUid),
parseInt(senderUin),
@@ -574,32 +572,28 @@ export namespace OB11Entities {
)
}
}
if (grayTipElement.jsonGrayTipElement?.busiId === '2401') {
if (grayTipElement.jsonGrayTipElement?.busiId === '2401' && json.items[2]) {
ctx.logger.info('收到群精华消息', json)
const searchParams = new URL(json.items[0].jp).searchParams
const msgSeq = searchParams.get('msgSeq')!
const Group = searchParams.get('groupCode')
const Peer: Peer = {
const searchParams = new URL(json.items[2].jp).searchParams
const msgSeq = searchParams.get('seq')
const groupCode = searchParams.get('gc')
const msgRandom = searchParams.get('random')
if (!groupCode || !msgSeq || !msgRandom) return
const peer = {
guildId: '',
chatType: ChatType.group,
peerUid: Group!
peerUid: groupCode
}
const msgList = (await ctx.ntMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true))?.msgList
if (!msgList?.length) {
return
}
//const origMsg = await dbUtil.getMsgByLongId(msgList[0].msgId)
//const postMsg = await dbUtil.getMsgBySeqId(origMsg?.msgSeq!) ?? origMsg
// 如果 senderUin 为 0可能是 历史消息 或 自身消息
//if (msgList[0].senderUin === '0') {
//msgList[0].senderUin = postMsg?.senderUin ?? getSelfUin()
//}
const essence = await ctx.ntGroupApi.queryCachedEssenceMsg(groupCode, msgSeq, msgRandom)
const { msgList } = await ctx.ntMsgApi.queryMsgsWithFilterExBySeq(peer, msgSeq, '0')
const sourceMsg = msgList.find(e => e.msgRandom === msgRandom)
if (!sourceMsg) return
return new OB11GroupEssenceEvent(
parseInt(msg.peerUid),
MessageUnique.getShortIdByMsgId(msgList[0].msgId)!,
parseInt(msgList[0].senderUin!)
ctx.store.getShortIdByMsgInfo(peer, sourceMsg.msgId)!,
parseInt(essence.items[0]?.msgSenderUin ?? sourceMsg.senderUin),
parseInt(essence.items[0]?.opUin ?? '0'),
)
// 获取MsgSeq+Peer可获取具体消息
}
if (grayTipElement.jsonGrayTipElement?.busiId === '2407') {
const memberUin = json.items[1].param[0]
@@ -702,10 +696,10 @@ export namespace OB11Entities {
sex: sex(member.sex!),
age: 0,
area: '',
level: '0',
level: String(member.memberLevel ?? 0),
qq_level: (member.qqLevel && calcQQLevel(member.qqLevel)) || 0,
join_time: 0, // 暂时没法获取
last_sent_time: 0, // 暂时没法获取
join_time: member.joinTime,
last_sent_time: member.lastSpeakTime,
title_expire_time: 0,
unfriendly: false,
card_changeable: true,

View File

@@ -1,16 +1,20 @@
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent'
export class OB11GroupEssenceEvent extends OB11GroupNoticeEvent {
notice_type = 'essence'
message_id: number
sender_id: number
sub_type: 'add' | 'delete' = 'add'
group_id: number
user_id: number = 0
user_id: number
operator_id: number
constructor(groupId: number, message_id: number, sender_id: number) {
constructor(groupId: number, messageId: number, senderId: number, operatorId: number) {
super()
this.group_id = groupId
this.message_id = message_id
this.sender_id = sender_id
this.user_id = senderId
this.message_id = messageId
this.sender_id = senderId
this.operator_id = operatorId
}
}

View File

@@ -16,7 +16,6 @@ import {
import { decodeCQCode } from '../cqcode'
import { Peer } from '@/ntqqapi/types/msg'
import { SendElementEntities } from '@/ntqqapi/entities'
import { MessageUnique } from '@/common/utils/messageUnique'
import { selfInfo } from '@/common/globalVars'
import { uri2local } from '@/common/utils'
import { Context } from 'cordis'
@@ -87,14 +86,14 @@ export async function createSendElements(
break
case OB11MessageDataType.reply: {
if (sendMsg.data?.id) {
const replyMsgId = await MessageUnique.getMsgIdAndPeerByShortId(+sendMsg.data.id)
const replyMsgId = await ctx.store.getMsgInfoByShortId(+sendMsg.data.id)
if (!replyMsgId) {
ctx.logger.warn('回复消息不存在', replyMsgId)
continue
}
const replyMsg = (await ctx.ntMsgApi.getMsgsByMsgId(
replyMsgId.Peer,
[replyMsgId.MsgId!]
replyMsgId.peer,
[replyMsgId.msgId!]
)).msgList[0]
if (replyMsg) {
sendElements.push(
@@ -132,7 +131,8 @@ export async function createSendElements(
ctx,
(await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })).path,
sendMsg.data.summary || '',
sendMsg.data.subType || 0
sendMsg.data.subType || 0,
sendMsg.data.type === 'flash'
)
deleteAfterSentFiles.push(res.picElement.sourcePath)
sendElements.push(res)
@@ -180,6 +180,10 @@ export async function createSendElements(
sendElements.push(SendElementEntities.ark(await data))
}
break
case OB11MessageDataType.shake: {
sendElements.push(SendElementEntities.shake())
}
break
}
}
@@ -272,7 +276,7 @@ export async function sendMsg(
//log('设置消息超时时间', timeout)
const returnMsg = await ctx.ntMsgApi.sendMsg(peer, sendElements, timeout)
if (returnMsg) {
returnMsg.msgShortId = MessageUnique.createMsg(peer, returnMsg.msgId)
returnMsg.msgShortId = ctx.store.createMsgShortId(peer, returnMsg.msgId)
ctx.logger.info('消息发送', returnMsg.msgShortId)
deleteAfterSentFiles.map(path => fsPromise.unlink(path))
return returnMsg

View File

@@ -3,7 +3,6 @@ import { OB11FriendRequestEvent } from '../event/request/OB11FriendRequest'
import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest'
import { GroupRequestOperateTypes } from '@/ntqqapi/types'
import { convertMessage2List, createSendElements, sendMsg, createPeer, CreatePeerMode } from '../helper/createMessage'
import { MessageUnique } from '@/common/utils/messageUnique'
import { isNullable } from 'cosmokit'
import { Context } from 'cordis'
@@ -88,18 +87,18 @@ async function handleMsg(ctx: Context, msg: OB11Message, quickAction: QuickOpera
}
if (msg.message_type === 'group') {
const groupMsgQuickAction = quickAction as QuickOperationGroupMessage
const rawMessage = await MessageUnique.getMsgIdAndPeerByShortId(+(msg.message_id ?? 0))
const rawMessage = await ctx.store.getMsgInfoByShortId(+(msg.message_id ?? 0))
if (!rawMessage) return
// handle group msg
if (groupMsgQuickAction.delete) {
ctx.ntMsgApi.recallMsg(peer, [rawMessage.MsgId]).catch(e => ctx.logger.error(e))
ctx.ntMsgApi.recallMsg(peer, [rawMessage.msgId]).catch(e => ctx.logger.error(e))
}
if (groupMsgQuickAction.kick) {
const { msgList } = await ctx.ntMsgApi.getMsgsByMsgId(peer, [rawMessage.MsgId])
const { msgList } = await ctx.ntMsgApi.getMsgsByMsgId(peer, [rawMessage.msgId])
ctx.ntGroupApi.kickMember(peer.peerUid, [msgList[0].senderUid]).catch(e => ctx.logger.error(e))
}
if (groupMsgQuickAction.ban) {
const { msgList } = await ctx.ntMsgApi.getMsgsByMsgId(peer, [rawMessage.MsgId])
const { msgList } = await ctx.ntMsgApi.getMsgsByMsgId(peer, [rawMessage.msgId])
ctx.ntGroupApi.banMember(peer.peerUid, [
{
uid: msgList[0].senderUid,
@@ -112,8 +111,16 @@ async function handleMsg(ctx: Context, msg: OB11Message, quickAction: QuickOpera
async function handleFriendRequest(ctx: Context, request: OB11FriendRequestEvent, quickAction: QuickOperationFriendRequest) {
if (!isNullable(quickAction.approve)) {
// todo: set remark
ctx.ntFriendApi.handleFriendRequest(request.flag, quickAction.approve).catch(e => ctx.logger.error(e))
const data = request.flag.split('|')
if (data.length < 2) {
return
}
const uid = data[0]
const reqTime = data[1]
await ctx.ntFriendApi.handleFriendRequest(uid, reqTime, quickAction.approve).catch(e => ctx.logger.error(e))
if (!isNullable(quickAction.remark)) {
ctx.ntFriendApi.setBuddyRemark(uid, quickAction.remark).catch(e => ctx.logger.error(e))
}
}
}

View File

@@ -131,6 +131,7 @@ export enum OB11MessageDataType {
dice = 'dice',
RPS = 'rps',
contact = 'contact',
shake = 'shake',
}
export interface OB11MessageMFace {
@@ -187,6 +188,7 @@ export interface OB11MessageImage extends OB11MessageFileBase {
data: OB11MessageFileBase['data'] & {
summary?: string // 图片摘要
subType?: PicSubType
type?: 'flash' | 'show'
}
}
@@ -285,6 +287,11 @@ export interface OB11MessageContact {
}
}
export interface OB11MessageShake {
type: OB11MessageDataType.shake
data: Record<string, never>
}
export type OB11MessageData =
| OB11MessageText
| OB11MessageFace
@@ -305,11 +312,12 @@ export type OB11MessageData =
| OB11MessageMarkdown
| OB11MessageForward
| OB11MessageContact
| OB11MessageShake
export interface OB11PostSendMsg {
message_type?: 'private' | 'group'
user_id: string
group_id?: string
user_id?: string | number
group_id?: string | number
message: OB11MessageMixType
messages?: OB11MessageMixType // 兼容 go-cqhttp
auto_escape?: boolean | string

View File

@@ -1 +1 @@
export const version = '3.32.7'
export const version = '3.33.3'

View File

@@ -1,18 +0,0 @@
import http from 'https'
function checkUrl(imageUrl) {
http
.get(imageUrl, (response) => {
console.log(response.statusCode)
})
.on('error', (e) => {
console.log(e)
})
}
checkUrl(
'https://gchat.qpic.cn/download?appid=1407&fileid=CgoxMzMyNTI0MjIxEhRrdaUgQP5MjweWa4uR8pviUDaGQhjcxQUg_wooiYTj39fphQNQgL2jAQ&spec=0&rkey=CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64',
)
checkUrl(
'https://multimedia.nt.qq.com.cn/download?appid=1407&fileid=CgoxMzMyNTI0MjIxEhRrdaUgQP5MjweWa4uR8pviUDaGQhjcxQUg_wooiYTj39fphQNQgL2jAQ&spec=0&rkey=CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64',
)

View File

@@ -15,7 +15,7 @@
"./src/common/*"
],
"@/onebot11/*": [
"./src/onebot11"
"./src/onebot11/*"
],
"@/ntqqapi/*": [
"./src/ntqqapi/*"