This commit is contained in:
idranme 2024-10-06 10:28:52 +08:00
parent 4d816b498a
commit 8c0cc8beba
30 changed files with 385 additions and 555 deletions

View File

@ -18,16 +18,19 @@
"license": "MIT",
"dependencies": {
"@minatojs/driver-sqlite": "^4.6.0",
"@satorijs/element": "^3.1.7",
"@satorijs/protocol": "^1.4.2",
"compare-versions": "^6.1.1",
"cordis": "^3.18.1",
"cors": "^2.8.5",
"cosmokit": "^1.6.2",
"cosmokit": "^1.6.3",
"express": "^5.0.0",
"fast-xml-parser": "^4.5.0",
"file-type": "^19.5.0",
"fluent-ffmpeg": "^2.1.3",
"minato": "^3.6.0",
"protobufjs": "^7.4.0",
"silk-wasm": "^3.6.1",
"ts-case-convert": "^2.1.0",
"ws": "^8.18.0"
},
"devDependencies": {

View File

@ -4,7 +4,7 @@ import path from 'node:path'
import { TEMP_DIR } from '../globalVars'
import { randomUUID, createHash } from 'node:crypto'
import { fileURLToPath } from 'node:url'
import { fileTypeFromFile } from 'file-type'
import { Context } from 'cordis'
// 定义一个异步函数来检查文件是否存在
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
@ -118,7 +118,7 @@ type Uri2LocalRes = {
isLocal: boolean
}
export async function uri2local(uri: string, filename?: string, needExt?: boolean): Promise<Uri2LocalRes> {
export async function uri2local(ctx: Context, uri: string, needExt?: boolean): Promise<Uri2LocalRes> {
const { type } = checkUriType(uri)
if (type === FileUriType.FileURL) {
@ -136,15 +136,16 @@ export async function uri2local(uri: string, filename?: string, needExt?: boolea
try {
const res = await fetchFile(uri)
const match = res.url.match(/.+\/([^/?]*)(?=\?)?/)
let filename: string
if (match?.[1]) {
filename ??= match[1].replace(/[/\\:*?"<>|]/g, '_')
filename = match[1].replace(/[/\\:*?"<>|]/g, '_')
} else {
filename ??= randomUUID()
filename = randomUUID()
}
let filePath = path.join(TEMP_DIR, filename)
await fsPromise.writeFile(filePath, res.data)
if (needExt && !path.extname(filePath)) {
const ext = (await fileTypeFromFile(filePath))?.ext
const ext = (await ctx.ntFileApi.getFileType(filePath)).ext
filename += `.${ext}`
await fsPromise.rename(filePath, `${filePath}.${ext}`)
filePath = `${filePath}.${ext}`
@ -157,12 +158,12 @@ export async function uri2local(uri: string, filename?: string, needExt?: boolea
}
if (type === FileUriType.OneBotBase64) {
filename ??= randomUUID()
let filename = randomUUID()
let filePath = path.join(TEMP_DIR, filename)
const base64 = uri.replace(/^base64:\/\//, '')
await fsPromise.writeFile(filePath, base64, 'base64')
if (needExt) {
const ext = (await fileTypeFromFile(filePath))?.ext
const ext = (await ctx.ntFileApi.getFileType(filePath)).ext
filename += `.${ext}`
await fsPromise.rename(filePath, `${filePath}.${ext}`)
filePath = `${filePath}.${ext}`
@ -174,12 +175,12 @@ export async function uri2local(uri: string, filename?: string, needExt?: boolea
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
const capture = /^data:([\w/.+-]+);base64,(.*)$/.exec(uri)
if (capture) {
filename ??= randomUUID()
let filename = randomUUID()
const [, _type, base64] = capture
let filePath = path.join(TEMP_DIR, filename)
await fsPromise.writeFile(filePath, base64, 'base64')
if (needExt) {
const ext = (await fileTypeFromFile(filePath))?.ext
const ext = (await ctx.ntFileApi.getFileType(filePath)).ext
filename += `.${ext}`
await fsPromise.rename(filePath, `${filePath}.${ext}`)
filePath = `${filePath}.${ext}`

View File

@ -13,10 +13,15 @@ export function log(...msg: unknown[]) {
let logMsg = ''
for (const msgItem of msg) {
if (typeof msgItem === 'object') {
logMsg += inspect(msgItem, { depth: 10, compact: true, breakLength: Infinity }) + ' '
continue
logMsg += inspect(msgItem, {
depth: 10,
compact: true,
breakLength: Infinity,
maxArrayLength: 220
}) + ' '
} else {
logMsg += msgItem + ' '
}
logMsg += msgItem + ' '
}
const currentDateTime = new Date().toLocaleString()
logMsg = `${currentDateTime} ${logMsg}\n\n`

View File

@ -3,25 +3,19 @@ import { writeFile } from 'node:fs/promises'
import { version } from '../../version'
import { log, fetchFile } from '.'
import { TEMP_DIR } from '../globalVars'
import { compare } from 'compare-versions'
const downloadMirrorHosts = ['https://ghp.ci/']
const releasesMirrorHosts = ['https://kkgithub.com']
export async function checkNewVersion() {
const latestVersionText = await getRemoteVersion()
const latestVersion = latestVersionText.split('.')
const latestVersion = await getRemoteVersion()
log('LLOneBot latest version', latestVersion)
const currentVersion = version.split('.')
//log('llonebot current version', currentVersion)
for (const k of [0, 1, 2]) {
const latest = parseInt(latestVersion[k])
const current = parseInt(currentVersion[k])
if (latest > current) {
log('')
return { result: true, version: latestVersionText }
} else if (latest < current) {
break
}
if (latestVersion === '') {
return { result: false, version: latestVersion }
}
if (compare(latestVersion, version, '>')) {
return { result: true, version: latestVersion }
}
return { result: false, version: version }
}

View File

@ -18,7 +18,6 @@ import { ReceiveCmdS } from '../hook'
import { RkeyManager } from '@/ntqqapi/helper/rkey'
import { OnRichMediaDownloadCompleteParams, Peer } from '@/ntqqapi/types/msg'
import { calculateFileMD5 } from '@/common/utils/file'
import { fileTypeFromFile } from 'file-type'
import { copyFile, stat, unlink } from 'node:fs/promises'
import { Time } from 'cosmokit'
import { Service, Context } from 'cordis'
@ -56,7 +55,12 @@ export class NTQQFileApi extends Service {
}
async getFileType(filePath: string) {
return fileTypeFromFile(filePath)
return await invoke<{
ext: string
mime: string
}>(NTMethod.FILE_TYPE, [filePath], {
className: NTClass.FS_API
})
}
/** 上传文件到 QQ 的文件夹 */
@ -111,23 +115,20 @@ export class NTQQFileApi extends Service {
}
const data = await invoke<{ notifyInfo: OnRichMediaDownloadCompleteParams }>(
'nodeIKernelMsgService/downloadRichMedia',
[
{
getReq: {
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
},
[{
getReq: {
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
},
null,
],
}],
{
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
cmdCB: payload => payload.notifyInfo.msgId === msgId,
@ -186,14 +187,11 @@ export class NTQQFileApi extends Service {
async downloadFileForModelId(peer: Peer, fileModelId: string, timeout = 2 * Time.minute) {
const data = await invoke<{ notifyInfo: OnRichMediaDownloadCompleteParams }>(
'nodeIKernelRichMediaService/downloadFileForModelId',
[
{
peer,
fileModelIdList: [fileModelId],
save_path: ''
},
null,
],
[{
peer,
fileModelIdList: [fileModelId],
save_path: ''
}],
{
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
cmdCB: payload => payload.notifyInfo.fileModelId === fileModelId,
@ -211,7 +209,7 @@ export class NTQQFileCacheApi extends Service {
}
async setCacheSilentScan(isSilent: boolean = true) {
return await invoke<GeneralCallResult>(NTMethod.CACHE_SET_SILENCE, [{ isSilent }, null])
return await invoke<GeneralCallResult>(NTMethod.CACHE_SET_SILENCE, [{ isSilent }])
}
getCacheSessionPathList() {
@ -225,7 +223,7 @@ export class NTQQFileCacheApi extends Service {
scanCache() {
invoke<GeneralCallResult>(ReceiveCmdS.CACHE_SCAN_FINISH, [], { registerEvent: true })
return invoke<CacheScanResult>(NTMethod.CACHE_SCAN, [null, null], { timeout: 300 * Time.second })
return invoke<CacheScanResult>(NTMethod.CACHE_SCAN, [], { timeout: 300 * Time.second })
}
getHotUpdateCachePath() {
@ -245,13 +243,13 @@ export class NTQQFileCacheApi extends Service {
pageSize: pageSize,
order: 1,
lastRecord: _lastRecord,
}, null])
}])
}
async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {
return await invoke<GeneralCallResult>(NTMethod.CACHE_CHAT_CLEAR, [{
chats,
fileKeys,
}, null])
}])
}
}

View File

@ -24,15 +24,11 @@ export class NTQQFriendApi extends Service {
categroyMbCount: number
buddyList: Friend[]
}[]
}>(
'getBuddyList',
[],
{
className: NTClass.NODE_STORE_API,
cbCmd: ReceiveCmdS.FRIENDS,
afterFirstCmd: false,
}
)
}>('getBuddyList', [], {
className: NTClass.NODE_STORE_API,
cbCmd: ReceiveCmdS.FRIENDS,
afterFirstCmd: false
})
const _friends: Friend[] = []
for (const item of data.data) {
_friends.push(...item.buddyList)
@ -121,13 +117,13 @@ export class NTQQFriendApi extends Service {
}
async getBuddyRecommendContact(uin: string) {
const ret = await invoke('nodeIKernelBuddyService/getBuddyRecommendContactArkJson', [{ uin }, null])
const ret = await invoke('nodeIKernelBuddyService/getBuddyRecommendContactArkJson', [{ uin }])
return ret.arkMsg
}
async setBuddyRemark(uid: string, remark: string) {
return await invoke('nodeIKernelBuddyService/setBuddyRemark', [{
remarkParams: { uid, remark }
}, null])
}])
}
}

View File

@ -15,9 +15,7 @@ import { invoke, NTClass, NTMethod } from '../ntcall'
import { GeneralCallResult } from '../services'
import { NTQQWindows } from './window'
import { getSession } from '../wrapper'
import { NodeIKernelGroupService } from '../services'
import { Service, Context } from 'cordis'
import { isNumeric } from '@/common/utils/misc'
declare module 'cordis' {
interface Context {
@ -28,8 +26,6 @@ declare module 'cordis' {
export class NTQQGroupApi extends Service {
static inject = ['ntWindowApi']
public groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>()
constructor(protected ctx: Context) {
super(ctx, 'ntGroupApi', true)
}
@ -51,49 +47,37 @@ export class NTQQGroupApi extends Service {
}
async getGroupMembers(groupCode: string, num = 3000): Promise<Map<string, GroupMember>> {
const session = getSession()
let result: Awaited<ReturnType<NodeIKernelGroupService['getNextMemberList']>>
if (session) {
const groupService = session.getGroupService()
const sceneId = groupService.createMemberListScene(groupCode, 'groupMemberList_MainWindow')
result = await groupService.getNextMemberList(sceneId, undefined, num)
} else {
const sceneId = await invoke(NTMethod.GROUP_MEMBER_SCENE, [{ groupCode, scene: 'groupMemberList_MainWindow' }])
result = await invoke(NTMethod.GROUP_MEMBERS, [{ sceneId, num }, null])
const sceneId = await invoke(NTMethod.GROUP_MEMBER_SCENE, [{ groupCode, scene: 'groupMemberList_MainWindow' }])
const data = await invoke(NTMethod.GROUP_MEMBERS, [{ sceneId, num }])
if (data.errCode !== 0) {
throw new Error('获取群成员列表出错,' + data.errMsg)
}
if (result.errCode !== 0) {
throw ('获取群成员列表出错,' + result.errMsg)
}
return result.result.infos
return data.result.infos
}
async getGroupMember(groupCode: string, memberUinOrUid: string) {
if (!this.groupMembers.has(groupCode)) {
try {
// 更新群成员列表
this.groupMembers.set(groupCode, await this.getGroupMembers(groupCode))
async getGroupMember(groupCode: string, uid: string, forceUpdate = false) {
invoke('nodeIKernelGroupListener/onMemberInfoChange', [], {
registerEvent: true
})
const data = await invoke<{
groupCode: string
members: Map<string, GroupMember>
}>(
'nodeIKernelGroupService/getMemberInfo',
[{
groupCode,
uids: [uid],
forceUpdate
}],
{
cbCmd: 'nodeIKernelGroupListener/onMemberInfoChange',
afterFirstCmd: false,
cmdCB: payload => payload.members.has(uid),
timeout: 2000
}
catch (e) {
return
}
}
let members = this.groupMembers.get(groupCode)!
const getMember = () => {
let member: GroupMember | undefined = undefined
if (isNumeric(memberUinOrUid)) {
member = Array.from(members.values()).find(member => member.uin === memberUinOrUid)
} else {
member = members.get(memberUinOrUid)
}
return member
}
let member = getMember()
if (!member) {
this.groupMembers.set(groupCode, await this.getGroupMembers(groupCode))
members = this.groupMembers.get(groupCode)!
member = getMember()
}
return member
)
return data.members.get(uid)!
}
async getGroupIgnoreNotifies() {
@ -105,11 +89,12 @@ export class NTQQGroupApi extends Service {
)
}
async getSingleScreenNotifies(num: number) {
async getSingleScreenNotifies(number: number, startSeq = '') {
invoke(ReceiveCmdS.GROUP_NOTIFY, [], { registerEvent: true })
return (await invoke<GroupNotifies>(
'nodeIKernelGroupService/getSingleScreenNotifies',
[{ doubt: false, startSeq: '', number: num }, null],
[{ doubt: false, startSeq, number }],
{
cbCmd: ReceiveCmdS.GROUP_NOTIFY,
afterFirstCmd: false,
@ -122,68 +107,35 @@ export class NTQQGroupApi extends Service {
const groupCode = flagitem[0]
const seq = flagitem[1]
const type = parseInt(flagitem[2])
const session = getSession()
if (session) {
return session.getGroupService().operateSysNotify(false, {
operateType, // 2 拒绝
return await invoke(NTMethod.HANDLE_GROUP_REQUEST, [{
doubt: false,
operateMsg: {
operateType,
targetMsg: {
seq, // 通知序列号
seq,
type,
groupCode,
postscript: reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
}
})
} else {
return await invoke(NTMethod.HANDLE_GROUP_REQUEST, [{
doubt: false,
operateMsg: {
operateType,
targetMsg: {
seq,
type,
groupCode,
postscript: reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
},
},
}, null])
}
},
}])
}
async quitGroup(groupCode: string) {
const session = getSession()
if (session) {
return session.getGroupService().quitGroup(groupCode)
} else {
return await invoke(NTMethod.QUIT_GROUP, [{ groupCode }, null])
}
return await invoke(NTMethod.QUIT_GROUP, [{ groupCode }])
}
async kickMember(groupCode: string, kickUids: string[], refuseForever = false, kickReason = '') {
const session = getSession()
if (session) {
return session.getGroupService().kickMember(groupCode, kickUids, refuseForever, kickReason)
} else {
return await invoke(NTMethod.KICK_MEMBER, [{ groupCode, kickUids, refuseForever, kickReason }])
}
return await invoke(NTMethod.KICK_MEMBER, [{ groupCode, kickUids, refuseForever, kickReason }])
}
/** timeStamp为秒数, 0为解除禁言 */
async banMember(groupCode: string, memList: Array<{ uid: string, timeStamp: number }>) {
// timeStamp为秒数, 0为解除禁言
const session = getSession()
if (session) {
return session.getGroupService().setMemberShutUp(groupCode, memList)
} else {
return await invoke(NTMethod.MUTE_MEMBER, [{ groupCode, memList }])
}
return await invoke(NTMethod.MUTE_MEMBER, [{ groupCode, memList }])
}
async banGroup(groupCode: string, shutUp: boolean) {
const session = getSession()
if (session) {
return session.getGroupService().setGroupShutUp(groupCode, shutUp)
} else {
return await invoke(NTMethod.MUTE_GROUP, [{ groupCode, shutUp }, null])
}
return await invoke(NTMethod.MUTE_GROUP, [{ groupCode, shutUp }])
}
async setMemberCard(groupCode: string, memberUid: string, cardName: string) {
@ -191,7 +143,7 @@ export class NTQQGroupApi extends Service {
if (session) {
return session.getGroupService().modifyMemberCardName(groupCode, memberUid, cardName)
} else {
return await invoke(NTMethod.SET_MEMBER_CARD, [{ groupCode, uid: memberUid, cardName }, null])
return await invoke(NTMethod.SET_MEMBER_CARD, [{ groupCode, uid: memberUid, cardName }])
}
}
@ -200,21 +152,16 @@ export class NTQQGroupApi extends Service {
if (session) {
return session.getGroupService().modifyMemberRole(groupCode, memberUid, role)
} else {
return await invoke(NTMethod.SET_MEMBER_ROLE, [{ groupCode, uid: memberUid, role }, null])
return await invoke(NTMethod.SET_MEMBER_ROLE, [{ groupCode, uid: memberUid, role }])
}
}
async setGroupName(groupCode: string, groupName: string) {
const session = getSession()
if (session) {
return session.getGroupService().modifyGroupName(groupCode, groupName, false)
} else {
return await invoke(NTMethod.SET_GROUP_NAME, [{ groupCode, groupName }, null])
}
return await invoke(NTMethod.SET_GROUP_NAME, [{ groupCode, groupName }])
}
async getGroupRemainAtTimes(groupCode: string) {
return await invoke(NTMethod.GROUP_AT_ALL_REMAIN_COUNT, [{ groupCode }, null])
return await invoke(NTMethod.GROUP_AT_ALL_REMAIN_COUNT, [{ groupCode }])
}
async removeGroupEssence(groupCode: string, msgId: string) {
@ -235,7 +182,7 @@ export class NTQQGroupApi extends Service {
msgRandom: Number(data?.msgList[0].msgRandom),
msgSeq: Number(data?.msgList[0].msgSeq)
}
}, null])
}])
}
}
@ -257,33 +204,30 @@ export class NTQQGroupApi extends Service {
msgRandom: Number(data?.msgList[0].msgRandom),
msgSeq: Number(data?.msgList[0].msgSeq)
}
}, null])
}])
}
}
async createGroupFileFolder(groupId: string, folderName: string) {
return await invoke('nodeIKernelRichMediaService/createGroupFolder', [{ groupId, folderName }, null])
return await invoke('nodeIKernelRichMediaService/createGroupFolder', [{ groupId, folderName }])
}
async deleteGroupFileFolder(groupId: string, folderId: string) {
return await invoke('nodeIKernelRichMediaService/deleteGroupFolder', [{ groupId, folderId }, null])
return await invoke('nodeIKernelRichMediaService/deleteGroupFolder', [{ groupId, folderId }])
}
async deleteGroupFile(groupId: string, fileIdList: string[], busIdList: number[]) {
return await invoke('nodeIKernelRichMediaService/deleteGroupFile', [{ groupId, busIdList, fileIdList }, null])
return await invoke('nodeIKernelRichMediaService/deleteGroupFile', [{ groupId, busIdList, fileIdList }])
}
async getGroupFileList(groupId: string, fileListForm: GetFileListParam) {
invoke('nodeIKernelMsgListener/onGroupFileInfoUpdate', [], { registerEvent: true })
const data = await invoke<{ fileInfo: GroupFileInfo }>(
'nodeIKernelRichMediaService/getGroupFileList',
[
{
groupId,
fileListForm
},
null,
],
[{
groupId,
fileListForm
}],
{
cbCmd: 'nodeIKernelMsgListener/onGroupFileInfoUpdate',
afterFirstCmd: false,
@ -296,17 +240,17 @@ export class NTQQGroupApi extends Service {
async publishGroupBulletin(groupCode: string, req: PublishGroupBulletinReq) {
const ntUserApi = this.ctx.get('ntUserApi')!
const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!
return await invoke('nodeIKernelGroupService/publishGroupBulletin', [{ groupCode, psKey, req }, null])
return await invoke('nodeIKernelGroupService/publishGroupBulletin', [{ groupCode, psKey, req }])
}
async uploadGroupBulletinPic(groupCode: string, path: string) {
const ntUserApi = this.ctx.get('ntUserApi')!
const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!
return await invoke('nodeIKernelGroupService/uploadGroupBulletinPic', [{ groupCode, psKey, path }, null])
return await invoke('nodeIKernelGroupService/uploadGroupBulletinPic', [{ groupCode, psKey, path }])
}
async getGroupRecommendContact(groupCode: string) {
const ret = await invoke('nodeIKernelGroupService/getGroupRecommendContactArkJson', [{ groupCode }, null])
const ret = await invoke('nodeIKernelGroupService/getGroupRecommendContactArkJson', [{ groupCode }])
return ret.arkJson
}
@ -317,7 +261,7 @@ export class NTQQGroupApi extends Service {
msgSeq: +msgSeq,
msgRandom: +msgRandom
}
}, null])
}])
}
async getGroupHonorList(groupCode: string) {
@ -326,31 +270,33 @@ export class NTQQGroupApi extends Service {
req: {
groupCode: [+groupCode]
}
}, null])
}])
}
async getGroupAllInfo(groupCode: string, timeout = 1000) {
invoke('nodeIKernelGroupListener/onGroupAllInfoChange', [], { registerEvent: true })
async getGroupAllInfo(groupCode: string) {
invoke('nodeIKernelGroupListener/onGroupAllInfoChange', [], {
registerEvent: true
})
return await invoke<{ groupAll: GroupAllInfo }>(
'nodeIKernelGroupService/getGroupAllInfo',
[
{
groupCode,
source: 4
},
null
],
[{
groupCode,
source: 4
}],
{
cbCmd: 'nodeIKernelGroupListener/onGroupAllInfoChange',
afterFirstCmd: false,
cmdCB: payload => payload.groupAll.groupCode === groupCode,
timeout
cmdCB: payload => payload.groupAll.groupCode === groupCode
}
)
}
async getGroupBulletinList(groupCode: string) {
invoke('nodeIKernelGroupListener/onGetGroupBulletinListResult', [], { registerEvent: true })
invoke('nodeIKernelGroupListener/onGetGroupBulletinListResult', [], {
registerEvent: true
})
const ntUserApi = this.ctx.get('ntUserApi')!
const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!
return await invoke<{
@ -377,4 +323,8 @@ export class NTQQGroupApi extends Service {
}
)
}
async setGroupAvatar(groupCode: string, path: string) {
return await invoke('nodeIKernelGroupService/setHeader', [{ path, groupCode }])
}
}

View File

@ -1,7 +1,5 @@
import { invoke, NTMethod } from '../ntcall'
import { GeneralCallResult } from '../services'
import { RawMessage, SendMessageElement, Peer, ChatType } from '../types'
import { getSession } from '@/ntqqapi/wrapper'
import { Service, Context } from 'cordis'
import { selfInfo } from '@/common/globalVars'
@ -19,37 +17,27 @@ export class NTQQMsgApi extends Service {
}
async getTempChatInfo(chatType: ChatType, peerUid: string) {
const session = getSession()
if (session) {
return session.getMsgService().getTempChatInfo(chatType, peerUid)
} else {
return await invoke('nodeIKernelMsgService/getTempChatInfo', [{ chatType, peerUid }, null])
}
return await invoke('nodeIKernelMsgService/getTempChatInfo', [{ chatType, peerUid }])
}
async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, setEmoji: boolean) {
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
// nt_qq/global/nt_data/Emoji/emoji-resource/sysface_res/apng/ 下可以看到所有QQ表情预览
// nt_qq/global/nt_data/Emoji/emoji-resource/face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
// 其实以官方文档为准是最好的https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
const emojiType = emojiId.length > 3 ? '2' : '1'
return await invoke(NTMethod.EMOJI_LIKE, [{ peer, msgSeq, emojiId, emojiType, setEmoji }])
}
async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) {
const session = getSession()
if (session) {
return session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId)
} else {
return await invoke(NTMethod.GET_MULTI_MSG, [{ peer, rootMsgId, parentMsgId }, null])
}
return await invoke(NTMethod.GET_MULTI_MSG, [{ peer, rootMsgId, parentMsgId }])
}
async activateChat(peer: Peer) {
return await invoke<GeneralCallResult>(NTMethod.ACTIVE_CHAT_PREVIEW, [{ peer, cnt: 1 }, null])
return await invoke(NTMethod.ACTIVE_CHAT_PREVIEW, [{ peer, cnt: 1 }])
}
async activateChatAndGetHistory(peer: Peer) {
return await invoke<GeneralCallResult>(NTMethod.ACTIVE_CHAT_HISTORY, [{ peer, cnt: 20 }, null])
async activateChatAndGetHistory(peer: Peer, cnt: number) {
return await invoke(NTMethod.ACTIVE_CHAT_HISTORY, [{ peer, cnt, msgId: '0', queryOrder: true }])
}
async getAioFirstViewLatestMsgs(peer: Peer, cnt: number) {
@ -62,9 +50,9 @@ export class NTQQMsgApi extends Service {
return await invoke('nodeIKernelMsgService/getMsgsByMsgId', [{ peer, msgIds }])
}
async getMsgHistory(peer: Peer, msgId: string, cnt: number, isReverseOrder: boolean = false) {
// 消息时间从旧到新
return await invoke(NTMethod.HISTORY_MSG, [{ peer, msgId, cnt, queryOrder: isReverseOrder }])
async getMsgHistory(peer: Peer, msgId: string, cnt: number, queryOrder = false) {
// 默认情况下消息时间从旧到新
return await invoke(NTMethod.HISTORY_MSG, [{ peer, msgId, cnt, queryOrder }])
}
async recallMsg(peer: Peer, msgIds: string[]) {
@ -96,6 +84,7 @@ export class NTQQMsgApi extends Service {
timeout
}
)
delete peer.guildId
return data.msgList.find(msgRecord => msgRecord.guildId === uniqueId)
}
@ -124,6 +113,7 @@ export class NTQQMsgApi extends Service {
}
}
)
delete destPeer.guildId
return data.msgList.filter(msgRecord => msgRecord.guildId === uniqueId)
}
@ -171,27 +161,8 @@ export class NTQQMsgApi extends Service {
throw new Error('转发消息超时')
}
async getMsgsBySeqAndCount(peer: Peer, msgSeq: string, count: number, desc: boolean, z: boolean) {
const session = getSession()
if (session) {
return await session.getMsgService().getMsgsBySeqAndCount(peer, msgSeq, count, desc, z)
} else {
return await invoke('nodeIKernelMsgService/getMsgsBySeqAndCount', [{
peer,
cnt: count,
msgSeq,
queryOrder: desc
}, null])
}
}
async getSingleMsg(peer: Peer, msgSeq: string) {
const session = getSession()
if (session) {
return await session.getMsgService().getSingleMsg(peer, msgSeq)
} else {
return await invoke('nodeIKernelMsgService/getSingleMsg', [{ peer, msgSeq }, null])
}
return await invoke('nodeIKernelMsgService/getSingleMsg', [{ peer, msgSeq }])
}
async queryFirstMsgBySeq(peer: Peer, msgSeq: string) {
@ -209,7 +180,7 @@ export class NTQQMsgApi extends Service {
isIncludeCurrent: true,
pageLimit: 1,
}
}, null])
}])
}
async queryMsgsWithFilterExBySeq(peer: Peer, msgSeq: string, filterMsgTime: string, filterSendersUid: string[] = []) {
@ -231,7 +202,7 @@ export class NTQQMsgApi extends Service {
}
async setMsgRead(peer: Peer) {
return await invoke('nodeIKernelMsgService/setMsgRead', [{ peer }, null])
return await invoke('nodeIKernelMsgService/setMsgRead', [{ peer }])
}
async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number) {
@ -250,7 +221,7 @@ export class NTQQMsgApi extends Service {
count,
backwardFetch: true,
forceRefresh: true
}, null])
}])
}
async generateMsgUniqueId(chatType: number) {
@ -289,6 +260,6 @@ export class NTQQMsgApi extends Service {
}
async getServerTime() {
return await invoke('nodeIKernelMSFService/getServerTime', [null])
return await invoke('nodeIKernelMSFService/getServerTime', [])
}
}

View File

@ -1,7 +1,6 @@
import { User, UserDetailInfoByUin, UserDetailInfoByUinV2, UserDetailInfo, UserDetailSource, ProfileBizType, SimpleInfo } from '../types'
import { invoke } from '../ntcall'
import { getBuildVersion } from '@/common/utils'
import { getSession } from '@/ntqqapi/wrapper'
import { RequestUtil } from '@/common/utils/request'
import { isNullable, Time } from 'cosmokit'
import { Service, Context } from 'cordis'
@ -20,15 +19,12 @@ export class NTQQUserApi extends Service {
super(ctx, 'ntUserApi', true)
}
async setQQAvatar(path: string) {
async setSelfAvatar(path: string) {
return await invoke(
'nodeIKernelProfileService/setHeader',
[
{ path },
null,
],
[{ path }],
{
timeout: 10 * Time.second, // 10秒不一定够
timeout: 10 * Time.second // 10秒不一定够
}
)
}
@ -92,49 +88,25 @@ export class NTQQUserApi extends Service {
}
async getPSkey(domains: string[]) {
return await invoke('nodeIKernelTipOffService/getPskey', [{ domains, isForNewPCQQ: true }, null])
return await invoke('nodeIKernelTipOffService/getPskey', [{ domains, isForNewPCQQ: true }])
}
async like(uid: string, count = 1) {
const session = getSession()
if (session) {
return session.getProfileLikeService().setBuddyProfileLike({
friendUid: uid,
sourceId: 71,
doLikeCount: count,
doLikeTollCount: 0
})
} else {
return await invoke(
'nodeIKernelProfileLikeService/setBuddyProfileLike',
[
{
doLikeUserInfo: {
friendUid: uid,
sourceId: 71,
doLikeCount: count,
doLikeTollCount: 0
}
},
null,
],
)
}
return await invoke(
'nodeIKernelProfileLikeService/setBuddyProfileLike',
[{
doLikeUserInfo: {
friendUid: uid,
sourceId: 71,
doLikeCount: count,
doLikeTollCount: 0
}
}]
)
}
async getUidByUinV1(uin: string) {
async getUidByUinV1(uin: string, groupCode?: string) {
let uid = (await invoke('nodeIKernelUixConvertService/getUid', [{ uins: [uin] }])).uidInfo.get(uin)
if (!uid) {
for (const membersList of this.ctx.ntGroupApi.groupMembers.values()) { //从群友列表转
for (const member of membersList.values()) {
if (member.uin === uin) {
uid = member.uid
break
}
}
if (uid) break
}
}
if (!uid) {
const unveifyUid = (await this.getUserDetailInfoByUin(uin)).info.uid //特殊转换
if (unveifyUid.indexOf('*') === -1) {
@ -145,29 +117,30 @@ export class NTQQUserApi extends Service {
const friends = await this.ctx.ntFriendApi.getFriends() //从好友列表转
uid = friends.find(item => item.uin === uin)?.uid
}
if (!uid && groupCode) {
const members = await this.ctx.ntGroupApi.getGroupMembers(groupCode)
uid = Array.from(members.values()).find(e => e.uin === uin)?.uid
}
return uid
}
async getUidByUinV2(uin: string, groupCode?: string) {
let uid = (await invoke('nodeIKernelGroupService/getUidByUins', [{ uin: [uin] }])).uids.get(uin)
async getUidByUinV2(uin: string) {
let uid = (await invoke('nodeIKernelGroupService/getUidByUins', [{ uinList: [uin] }])).uids.get(uin)
if (uid) return uid
uid = (await invoke('nodeIKernelProfileService/getUidByUin', [{ callFrom: 'FriendsServiceImpl', uin: [uin] }])).get(uin)
if (uid) return uid
uid = (await invoke('nodeIKernelUixConvertService/getUid', [{ uins: [uin] }])).uidInfo.get(uin)
if (uid) return uid
const unveifyUid = (await this.getUserDetailInfoByUinV2(uin)).detail.uid
if (!unveifyUid.includes('*')) return unveifyUid
if (groupCode) {
const member = await this.ctx.ntGroupApi.getGroupMember(groupCode, uin)
return member?.uid
}
//if (!unveifyUid.includes('*')) return unveifyUid
return unveifyUid
}
async getUidByUin(uin: string, groupCode?: string) {
if (getBuildVersion() >= 26702) {
return this.getUidByUinV2(uin, groupCode)
return this.getUidByUinV2(uin)
}
return this.getUidByUinV1(uin)
return this.getUidByUinV1(uin, groupCode)
}
async getUserDetailInfoByUinV2(uin: string) {
@ -194,7 +167,7 @@ export class NTQQUserApi extends Service {
}
async getUinByUidV2(uid: string) {
let uin = (await invoke('nodeIKernelGroupService/getUinByUids', [{ uid: [uid] }])).uins.get(uid)
let uin = (await invoke('nodeIKernelGroupService/getUinByUids', [{ uidList: [uid] }])).uins.get(uid)
if (uin) return uin
uin = (await invoke('nodeIKernelProfileService/getUinByUid', [{ callFrom: 'FriendsServiceImpl', uid: [uid] }])).get(uid)
if (uin) return uin
@ -214,18 +187,13 @@ export class NTQQUserApi extends Service {
}
async forceFetchClientKey() {
const session = getSession()
if (session) {
return await session.getTicketService().forceFetchClientKey('')
} else {
return await invoke('nodeIKernelTicketService/forceFetchClientKey', [{ url: '' }, null])
}
return await invoke('nodeIKernelTicketService/forceFetchClientKey', [{ url: '' }])
}
async getSelfNick(refresh = true) {
if ((refresh || !selfInfo.nick) && selfInfo.uid) {
const { profiles } = await this.getUserSimpleInfo(selfInfo.uid)
selfInfo.nick = profiles[selfInfo.uid].coreInfo.nick
const data = await this.getUserSimpleInfo(selfInfo.uid)
selfInfo.nick = data.coreInfo.nick
}
return selfInfo.nick
}
@ -237,7 +205,7 @@ export class NTQQUserApi extends Service {
extStatus,
batteryStatus,
}
}, null])
}])
}
async getProfileLike(uid: string) {
@ -252,11 +220,11 @@ export class NTQQUserApi extends Service {
start: 0,
limit: 20,
}
}, null])
}])
}
async getUserSimpleInfo(uid: string, force = true) {
return await invoke<{ profiles: Record<string, SimpleInfo> }>(
const data = await invoke<{ profiles: Record<string, SimpleInfo> }>(
'nodeIKernelProfileService/getUserSimpleInfo',
[{
uids: [uid],
@ -268,6 +236,7 @@ export class NTQQUserApi extends Service {
cmdCB: payload => !isNullable(payload.profiles[uid]),
}
)
return data.profiles[uid]
}
async getCoreAndBaseInfo(uids: string[]) {

View File

@ -35,7 +35,7 @@ export class NTQQWindowApi extends Service {
super(ctx, 'ntWindowApi', true)
}
// 打开窗口并获取对应的下发事件
/** 打开窗口并获取对应的下发事件 */
async openWindow<R = GeneralCallResult>(
ntQQWindow: NTQQWindow,
args: unknown[],
@ -53,7 +53,6 @@ export class NTQQWindowApi extends Service {
)
setTimeout(() => {
for (const w of BrowserWindow.getAllWindows()) {
// log("close window", w.webContents.getURL())
if (w.webContents.getURL().indexOf(ntQQWindow.windowUrlHash) != -1) {
w.close()
}

View File

@ -52,15 +52,15 @@ export namespace SendElement {
}
}
export function reply(msgSeq: string, msgId: string, senderUin: string, senderUinStr: string): SendReplyElement {
export function reply(msgSeq: string, msgId: string, senderUin: string): SendReplyElement {
return {
elementType: ElementType.Reply,
elementId: '',
replyElement: {
replayMsgSeq: msgSeq, // raw.msgSeq
replayMsgId: msgId, // raw.msgId
replayMsgSeq: msgSeq,
replayMsgId: msgId,
senderUin: senderUin,
senderUinStr: senderUinStr,
senderUinStr: senderUin,
},
}
}
@ -251,19 +251,19 @@ export namespace SendElement {
}
}
export function face(faceId: number): SendFaceElement {
export function face(faceId: number, faceType?: number): SendFaceElement {
// 从face_config.json中获取表情名称
const sysFaces = faceConfig.sysface
const emojiFaces = faceConfig.emoji
const face = sysFaces.find((face) => face.QSid === faceId.toString())
faceId = parseInt(faceId.toString())
// let faceType = parseInt(faceId.toString().substring(0, 1));
let faceType = 1
if (faceId >= 222) {
faceType = 2
}
if (face?.AniStickerType) {
faceType = 3;
const face = sysFaces.find(face => face.QSid === String(faceId))
if (!faceType) {
if (faceId < 222) {
faceType = 1
} else {
faceType = 2
}
if (face?.AniStickerType) {
faceType = 3
}
}
return {
elementType: ElementType.Face,

View File

@ -1,5 +1,15 @@
{
"sysface": [
{
"QSid": "419",
"QDes": "/火车",
"IQLid": "419",
"AQLid": "419",
"EMCode": "10419",
"AniStickerType": 3,
"AniStickerPackId": "1",
"AniStickerId": "47"
},
{
"QSid": "392",
"QDes": "/龙年快乐",
@ -3662,4 +3672,4 @@
"EMCode": "401016"
}
]
}
}

View File

@ -123,4 +123,6 @@ export interface NodeIKernelGroupService {
addGroupEssence(param: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>
removeGroupEssence(param: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>
setHeader(args: unknown[]): Promise<GeneralCallResult>
}

View File

@ -20,6 +20,10 @@ export interface NodeIKernelMsgService {
getAioFirstViewLatestMsgs(peer: Peer, num: number): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getAioFirstViewLatestMsgsAndAddActiveChat(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>
getMsgsIncludeSelfAndAddActiveChat(...args: unknown[]): 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[] }>

View File

@ -3,7 +3,7 @@ import { GeneralCallResult } from './common'
import { Dict } from 'cosmokit'
export interface NodeIKernelProfileLikeService {
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number }
setBuddyProfileLike(...args: unknown[]): GeneralCallResult & { succCounts: number }
getBuddyProfileLike(req: BuddyProfileLikeReq): Promise<GeneralCallResult & {
info: {

View File

@ -197,6 +197,60 @@ export interface PicElement {
md5HexStr?: string
}
export interface TipAioOpGrayTipElement {
operateType: number
peerUid: string
fromGrpCodeOfTmpChat: string
}
export enum TipGroupElementType {
MemberIncrease = 1,
Kicked = 3, // 被移出群
Ban = 8,
}
export interface TipGroupElement {
type: TipGroupElementType // 1是表示有人加入群, 自己加入群也会收到这个
role: number
groupName: string // 暂时获取不到
memberUid: string
memberNick: string
memberRemark: string
adminUid: string
adminNick: string
adminRemark: string
createGroup: null
memberAdd?: {
showType: number
otherAdd?: {
uid: string
name: string
}
otherAddByOtherQRCode?: unknown
otherAddByYourQRCode?: unknown
youAddByOtherQRCode?: unknown
otherInviteOther?: unknown
otherInviteYou?: unknown
youInviteOther?: unknown
}
shutUp?: {
curTime: string
duration: string // 禁言时间,秒
admin: {
uid: string
card: string
name: string
role: GroupMemberRole
}
member: {
uid: string
card: string
name: string
role: GroupMemberRole
}
}
}
export enum GrayTipElementSubType {
Revoke = 1,
Proclamation = 2,
@ -234,6 +288,8 @@ export interface GrayTipElement {
xmlElement?: {
templId: string
content: string
templParam: Map<string, string>
members: Map<string, string> // uid -> remark
}
jsonGrayTipElement?: {
busiId: string
@ -241,7 +297,6 @@ export interface GrayTipElement {
}
}
export enum FaceIndex {
Dice = 358,
RPS = 359, // 石头剪刀布
@ -268,6 +323,10 @@ export interface MarketFaceElement {
key: string
imageWidth?: number
imageHeight?: number
supportSize?: {
width: number
height: number
}[]
}
export interface VideoElement {
@ -326,58 +385,6 @@ export interface InlineKeyboardElement {
]
}
export interface TipAioOpGrayTipElement {
// 这是什么提示来着?
operateType: number
peerUid: string
fromGrpCodeOfTmpChat: string
}
export enum TipGroupElementType {
MemberIncrease = 1,
Kicked = 3, // 被移出群
Ban = 8,
}
export interface TipGroupElement {
type: TipGroupElementType // 1是表示有人加入群, 自己加入群也会收到这个
role: 0 // 暂时不知
groupName: string // 暂时获取不到
memberUid: string
memberNick: string
memberRemark: string
adminUid: string
adminNick: string
adminRemark: string
createGroup: null
memberAdd?: {
showType: 1
otherAdd: null
otherAddByOtherQRCode: null
otherAddByYourQRCode: null
youAddByOtherQRCode: null
otherInviteOther: null
otherInviteYou: null
youInviteOther: null
}
shutUp?: {
curTime: string
duration: string // 禁言时间,秒
admin: {
uid: string
card: string
name: string
role: GroupMemberRole
}
member: {
uid: string
card: string
name: string
role: GroupMemberRole
}
}
}
export interface StructLongMsgElement {
xmlContent: string
resId: string
@ -409,11 +416,25 @@ export interface RawMessage {
guildId: string
sendNickName: string
sendMemberName?: string // 发送者群名片
sendRemarkName?: string // 发送者好友备注
chatType: ChatType
sendStatus?: number // 消息状态别人发的2是已撤回自己发的2是已发送
recallTime: string // 撤回时间, "0"是没有撤回
records: RawMessage[]
elements: MessageElement[]
peerName: string
multiTransInfo?: {
status: number
msgId: number
friendFlag: number
fromFaceUrl: string
}
emojiLikesList: {
emojiId: string
emojiType: string
likesCnt: string
isClicked: boolean
}[]
}
export interface Peer {

View File

@ -1,19 +1,19 @@
export enum GroupNotifyType {
INVITED_BY_MEMBER = 1,
REFUSE_INVITED,
REFUSED_BY_ADMINI_STRATOR,
AGREED_TOJOIN_DIRECT, // 有人接受了邀请入群
INVITED_NEED_ADMINI_STRATOR_PASS, // 有人邀请了别人入群
AGREED_TO_JOIN_BY_ADMINI_STRATOR,
REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
SET_ADMIN,
KICK_MEMBER_NOTIFY_ADMIN,
KICK_MEMBER_NOTIFY_KICKED,
MEMBER_LEAVE_NOTIFY_ADMIN, // 主动退出
CANCEL_ADMIN_NOTIFY_CANCELED, // 我被取消管理员
CANCEL_ADMIN_NOTIFY_ADMIN, // 其他人取消管理员
TRANSFER_GROUP_NOTIFY_OLDOWNER,
TRANSFER_GROUP_NOTIFY_ADMIN
InvitedByMember = 1,
RefuseInvited,
RefusedByAdminiStrator,
AgreedTojoinDirect, // 有人接受了邀请入群
InvitedNeedAdminiStratorPass, // 有人邀请了别人入群
AgreedToJoinByAdminiStrator,
RequestJoinNeedAdminiStratorPass,
SetAdmin,
KickMemberNotifyAdmin,
KickMemberNotifyKicked,
MemberLeaveNotifyAdmin, // 主动退出
CancelAdminNotifyCanceled, // 我被取消管理员
CancelAdminNotifyAdmin, // 其他人取消管理员
TransferGroupNotifyOldowner,
TransferGroupNotifyAdmin
}
export interface GroupNotifies {
@ -23,11 +23,11 @@ export interface GroupNotifies {
}
export enum GroupNotifyStatus {
KINIT, // 初始化
KUNHANDLE, // 未处理
KAGREED, // 同意
KREFUSED, // 拒绝
KIGNORED // 忽略
Init, // 初始化
Unhandle, // 未处理
Agreed, // 同意
Refused, // 拒绝
Ignored // 忽略
}
export interface GroupNotify {
@ -79,41 +79,3 @@ export interface FriendRequestNotify {
buddyReqs: FriendRequest[]
}
}
export enum MemberExtSourceType {
DEFAULTTYPE = 0,
TITLETYPE = 1,
NEWGROUPTYPE = 2,
}
export interface GroupExtParam {
groupCode: string
seq: string
beginUin: string
dataTime: string
uinList: Array<string>
uinNum: string
groupType: string
richCardNameVer: string
sourceType: MemberExtSourceType
memberExtFilter: {
memberLevelInfoUin: number
memberLevelInfoPoint: number
memberLevelInfoActiveDay: number
memberLevelInfoLevel: number
memberLevelInfoName: number
levelName: number
dataTime: number
userShowFlag: number
sysShowFlag: number
timeToUpdate: number
nickName: number
specialTitle: number
levelNameNew: number
userShowFlagNew: number
msgNeedField: number
cmdUinFlagExt3Grocery: number
memberIcon: number
memberInfoSeq: number
}
}

View File

@ -50,7 +50,7 @@ export class GetGroupFileUrl extends BaseAction<Payload, Response> {
private async search(groupId: string, fileId: string, folderId?: string) {
let modelId: string | undefined
let nextIndex: number | undefined
let folders: GroupFileInfo['item'] = []
const folders: GroupFileInfo['item'] = []
while (nextIndex !== 0) {
const res = await this.ctx.ntGroupApi.getGroupFileList(groupId, {
sortType: 1,

View File

@ -38,7 +38,7 @@ export class GetGroupSystemMsg extends BaseAction<void, Response> {
invitor_nick: notify.user1.nickName,
group_id: +notify.group.groupCode,
group_name: notify.group.groupName,
checked: notify.status !== GroupNotifyStatus.KUNHANDLE,
checked: notify.status !== GroupNotifyStatus.Unhandle,
actor: notify.user2?.uid ? Number(await this.ctx.ntUserApi.getUinByUid(notify.user2.uid)) : 0
})
} else if (notify.type == 7) {
@ -49,7 +49,7 @@ export class GetGroupSystemMsg extends BaseAction<void, Response> {
message: notify.postscript,
group_id: +notify.group.groupCode,
group_name: notify.group.groupName,
checked: notify.status !== GroupNotifyStatus.KUNHANDLE,
checked: notify.status !== GroupNotifyStatus.Unhandle,
actor: notify.user2?.uid ? Number(await this.ctx.ntUserApi.getUinByUid(notify.user2.uid)) : 0
})
}

View File

@ -28,7 +28,7 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
let picInfo: { id: string, width: number, height: number } | undefined
if (payload.image) {
const { path, isLocal, success, errMsg } = await uri2local(payload.image, undefined, true)
const { path, isLocal, success, errMsg } = await uri2local(this.ctx, payload.image, true)
if (!success) {
throw new Error(`设置群公告失败, 错误信息: uri2local: ${errMsg}`)
}

View File

@ -23,7 +23,7 @@ export class UploadGroupFile extends BaseAction<Payload, null> {
})
protected async _handle(payload: Payload): Promise<null> {
const { success, errMsg, path, fileName } = await uri2local(payload.file)
const { success, errMsg, path, fileName } = await uri2local(this.ctx, payload.file)
if (!success) {
throw new Error(errMsg)
}

View File

@ -19,7 +19,7 @@ export class UploadPrivateFile extends BaseAction<UploadPrivateFilePayload, null
})
protected async _handle(payload: UploadPrivateFilePayload): Promise<null> {
const { success, errMsg, path, fileName } = await uri2local(payload.file)
const { success, errMsg, path, fileName } = await uri2local(this.ctx, payload.file)
if (!success) {
throw new Error(errMsg)
}

View File

@ -18,7 +18,9 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
protected async _handle(payload: Payload) {
const groupCode = payload.group_id.toString()
const member = await this.ctx.ntGroupApi.getGroupMember(groupCode, payload.user_id.toString())
const uid = await this.ctx.ntUserApi.getUidByUin(payload.user_id.toString())
if (!uid) throw new Error('无法获取用户信息')
const member = await this.ctx.ntGroupApi.getGroupMember(groupCode, uid)
if (member) {
if (isNullable(member.sex)) {
const info = await this.ctx.ntUserApi.getUserDetailInfo(member.uid)

View File

@ -16,10 +16,8 @@ export default class Debug extends BaseAction<Payload, unknown> {
for (const ntqqApiClass of ntqqApi) {
const method = ntqqApiClass[payload.method as keyof typeof ntqqApiClass]
if (method && method instanceof Function) {
const result = method.apply(ntqqApiClass, payload.args)
if (method.constructor.name === 'AsyncFunction') {
return await result
}
const result = await method.apply(ntqqApiClass, payload.args)
this.ctx.logger.info('debug', result)
return result
}
}

View File

@ -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 = data.notifies.filter(notify => notify.status === GroupNotifyStatus.KUNHANDLE)
const notifies = data.notifies.filter(notify => notify.status === GroupNotifyStatus.Unhandle)
const returnData: OB11GroupRequestNotify[] = []
for (const notify of notifies) {
const uin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid)

View File

@ -11,13 +11,13 @@ export default class SetAvatar extends BaseAction<Payload, null> {
actionName = ActionName.SetQQAvatar
protected async _handle(payload: Payload): Promise<null> {
const { path, isLocal, errMsg } = await uri2local(payload.file)
const { path, isLocal, errMsg } = await uri2local(this.ctx, payload.file)
if (errMsg) {
throw new Error(errMsg)
}
if (path) {
await checkFileReceived(path, 5000) // 文件不存在QQ会崩溃需要提前判断
const ret = await this.ctx.ntUserApi.setQQAvatar(path)
const ret = await this.ctx.ntUserApi.setSelfAvatar(path)
if (!isLocal) {
unlink(path)
}

View File

@ -4,10 +4,7 @@ import {
GroupNotify,
GroupNotifyType,
RawMessage,
BuddyReqType,
FriendRequest,
GroupMember,
GroupMemberRole,
GroupNotifyStatus
} from '../ntqqapi/types'
import { OB11GroupRequestEvent } from './event/request/OB11GroupRequest'
@ -23,7 +20,6 @@ import { OB11BaseMetaEvent } from './event/meta/OB11BaseMetaEvent'
import { postHttpEvent } from './helper/eventForHttp'
import { initActionMap } from './action'
import { llonebotError } from '../common/globalVars'
import { OB11GroupCardEvent } from './event/notice/OB11GroupCardEvent'
import { OB11GroupAdminNoticeEvent } from './event/notice/OB11GroupAdminNoticeEvent'
import { OB11ProfileLikeEvent } from './event/notice/OB11ProfileLikeEvent'
import { SysMsg } from '@/ntqqapi/proto/compiled'
@ -35,8 +31,10 @@ declare module 'cordis' {
}
class OneBot11Adapter extends Service {
static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi', 'ntWebApi', 'store']
static inject = [
'ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi',
'ntGroupApi', 'ntUserApi', 'ntWindowApi', 'ntWebApi', 'store'
]
private ob11WebSocket: OB11WebSocket
private ob11WebSocketReverseManager: OB11WebSocketReverseManager
private ob11Http: OB11Http
@ -91,7 +89,7 @@ class OneBot11Adapter extends Service {
private async handleGroupNotify(notify: GroupNotify) {
try {
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type
if ([GroupNotifyType.MEMBER_LEAVE_NOTIFY_ADMIN, GroupNotifyType.KICK_MEMBER_NOTIFY_ADMIN].includes(notify.type)) {
if ([GroupNotifyType.MemberLeaveNotifyAdmin, GroupNotifyType.KickMemberNotifyAdmin].includes(notify.type)) {
this.ctx.logger.info('有成员退出通知', notify)
const member1Uin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid)
let operatorId = member1Uin
@ -112,7 +110,7 @@ class OneBot11Adapter extends Service {
)
this.dispatch(event)
}
else if (notify.type === GroupNotifyType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS && notify.status === GroupNotifyStatus.KUNHANDLE) {
else if (notify.type === GroupNotifyType.RequestJoinNeedAdminiStratorPass && notify.status === GroupNotifyStatus.Unhandle) {
this.ctx.logger.info('有加群请求')
const requestUin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid)
const event = new OB11GroupRequestEvent(
@ -123,7 +121,7 @@ class OneBot11Adapter extends Service {
)
this.dispatch(event)
}
else if (notify.type === GroupNotifyType.INVITED_BY_MEMBER && notify.status === GroupNotifyStatus.KUNHANDLE) {
else if (notify.type === GroupNotifyType.InvitedByMember && notify.status === GroupNotifyStatus.Unhandle) {
this.ctx.logger.info('收到邀请我加群通知')
const userId = await this.ctx.ntUserApi.getUinByUid(notify.user2.uid)
const event = new OB11GroupRequestEvent(
@ -136,7 +134,7 @@ class OneBot11Adapter extends Service {
)
this.dispatch(event)
}
else if (notify.type === GroupNotifyType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status === GroupNotifyStatus.KUNHANDLE) {
else if (notify.type === GroupNotifyType.InvitedNeedAdminiStratorPass && notify.status === GroupNotifyStatus.Unhandle) {
this.ctx.logger.info('收到群员邀请加群通知')
const userId = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid)
const event = new OB11GroupRequestEvent(
@ -147,6 +145,20 @@ class OneBot11Adapter extends Service {
)
this.dispatch(event)
}
else if ([
GroupNotifyType.SetAdmin,
GroupNotifyType.CancelAdminNotifyCanceled,
GroupNotifyType.CancelAdminNotifyAdmin
].includes(notify.type)) {
this.ctx.logger.info('收到管理员变动通知')
const uin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid)
const event = new OB11GroupAdminNoticeEvent(
notify.type === GroupNotifyType.SetAdmin ? 'set' : 'unset',
parseInt(notify.group.groupCode),
parseInt(uin),
)
this.dispatch(event)
}
} catch (e) {
this.ctx.logger.error('解析群通知失败', (e as Error).stack)
}
@ -306,29 +318,6 @@ class OneBot11Adapter extends Service {
})
}
private async handleGroupMemberInfoUpdated(groupCode: string, members: GroupMember[]) {
for (const member of members) {
const existMember = await this.ctx.ntGroupApi.getGroupMember(groupCode, member.uin)
if (existMember) {
if (member.cardName !== existMember.cardName) {
this.ctx.logger.info('群成员名片变动', `${groupCode}: ${existMember.uin}`, existMember.cardName, '->', member.cardName)
this.dispatch(
new OB11GroupCardEvent(parseInt(groupCode), parseInt(member.uin), member.cardName, existMember.cardName),
)
} else if (member.role !== existMember.role) {
this.ctx.logger.info('有管理员变动通知')
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
member.role == GroupMemberRole.admin ? 'set' : 'unset',
parseInt(groupCode),
parseInt(member.uin)
)
this.dispatch(groupAdminNoticeEvent)
}
Object.assign(existMember, member)
}
}
}
public start() {
if (this.config.enableWs) {
this.ob11WebSocket.start()
@ -360,9 +349,6 @@ class OneBot11Adapter extends Service {
this.ctx.on('nt/friend-request', input => {
this.handleFriendRequest(input)
})
this.ctx.on('nt/group-member-info-updated', input => {
this.handleGroupMemberInfoUpdated(input.groupCode, input.members)
})
this.ctx.on('nt/system-message-created', input => {
const sysMsg = SysMsg.SystemMessage.decode(input)
const { msgType, subType, subSubType } = sysMsg.msgSpec[0] ?? {}
@ -385,7 +371,6 @@ namespace OneBot11Adapter {
token: string
debug: boolean
reportSelfMessage: boolean
msgCacheExpire: number
musicSignUrl?: string
enableLocalFile2Url: boolean
ffmpeg?: string

View File

@ -214,7 +214,7 @@ class OB11WebSocketReverse {
let receive: { action: ActionName | null; params: unknown; echo?: unknown } = { action: null, params: {} }
try {
receive = JSON.parse(msg.toString())
this.ctx.logger.info('收到反向Websocket消息', receive)
this.ctx.logger.info('收到反向 Websocket 消息', receive)
} catch (e) {
return this.reply(this.wsClient!, OB11Response.error('json解析失败请检查数据格式', 1400, receive.echo))
}

View File

@ -30,7 +30,6 @@ import { OB11GroupUploadNoticeEvent } from './event/notice/OB11GroupUploadNotice
import { OB11GroupNoticeEvent } from './event/notice/OB11GroupNoticeEvent'
import { calcQQLevel } from '../common/utils/misc'
import { OB11GroupTitleEvent } from './event/notice/OB11GroupTitleEvent'
import { OB11GroupCardEvent } from './event/notice/OB11GroupCardEvent'
import { OB11GroupDecreaseEvent } from './event/notice/OB11GroupDecreaseEvent'
import { OB11GroupMsgEmojiLikeEvent } from './event/notice/OB11MsgEmojiLikeEvent'
import { OB11FriendAddNoticeEvent } from './event/notice/OB11FriendAddNoticeEvent'
@ -39,7 +38,7 @@ import { OB11GroupRecallNoticeEvent } from './event/notice/OB11GroupRecallNotice
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from './event/notice/OB11PokeEvent'
import { OB11BaseNoticeEvent } from './event/notice/OB11BaseNoticeEvent'
import { OB11GroupEssenceEvent } from './event/notice/OB11GroupEssenceEvent'
import { omit, isNullable, pick, Dict } from 'cosmokit'
import { omit, pick, Dict } from 'cosmokit'
import { Context } from 'cordis'
import { selfInfo } from '@/common/globalVars'
import { pathToFileURL } from 'node:url'
@ -80,7 +79,7 @@ export namespace OB11Entities {
if (msg.chatType === ChatType.Group) {
resMsg.sub_type = 'normal'
resMsg.group_id = parseInt(msg.peerUin)
const member = await ctx.ntGroupApi.getGroupMember(msg.peerUin, msg.senderUin)
const member = await ctx.ntGroupApi.getGroupMember(msg.peerUin, msg.senderUid)
if (member) {
resMsg.sender.role = groupMemberRole(member.role)
resMsg.sender.nickname = member.nick
@ -89,12 +88,12 @@ export namespace OB11Entities {
}
else if (msg.chatType === ChatType.C2C) {
resMsg.sub_type = 'friend'
resMsg.sender.nickname = (await ctx.ntUserApi.getUserDetailInfo(msg.senderUid)).nick
resMsg.sender.nickname = (await ctx.ntUserApi.getUserSimpleInfo(msg.senderUid)).coreInfo.nick
}
else if (msg.chatType === ChatType.TempC2CFromGroup) {
resMsg.sub_type = 'group'
resMsg.temp_source = 0 //群聊
resMsg.sender.nickname = (await ctx.ntUserApi.getUserDetailInfo(msg.senderUid)).nick
resMsg.sender.nickname = (await ctx.ntUserApi.getUserSimpleInfo(msg.senderUid)).coreInfo.nick
const ret = await ctx.ntMsgApi.getTempChatInfo(ChatType.TempC2CFromGroup, msg.senderUid)
if (ret?.result === 0) {
resMsg.sender.group_id = Number(ret.tmpChatInfo?.groupCode)
@ -403,7 +402,7 @@ export namespace OB11Entities {
if (msg.chatType !== ChatType.Group) {
return
}
if (msg.senderUin) {
/**if (msg.senderUin) {
const member = await ctx.ntGroupApi.getGroupMember(msg.peerUid, msg.senderUin)
if (member && member.cardName !== msg.sendMemberName) {
const event = new OB11GroupCardEvent(
@ -415,24 +414,17 @@ export namespace OB11Entities {
member.cardName = msg.sendMemberName!
return event
}
}
}*/
for (const element of msg.elements) {
const grayTipElement = element.grayTipElement
const groupElement = grayTipElement?.groupElement
if (groupElement) {
if (groupElement.type === TipGroupElementType.MemberIncrease) {
ctx.logger.info('收到群成员增加消息', groupElement)
await ctx.sleep(1000)
const member = await ctx.ntGroupApi.getGroupMember(msg.peerUid, groupElement.memberUid)
let memberUin = member?.uin
if (!memberUin) {
memberUin = (await ctx.ntUserApi.getUserDetailInfo(groupElement.memberUid)).uin
}
const adminMember = await ctx.ntGroupApi.getGroupMember(msg.peerUid, groupElement.adminUid)
if (memberUin) {
const operatorUin = adminMember?.uin || memberUin
return new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(operatorUin))
}
const { memberUid, adminUid } = groupElement
const memberUin = await ctx.ntUserApi.getUinByUid(memberUid)
const operatorUin = adminUid ? await ctx.ntUserApi.getUinByUid(adminUid) : memberUin
return new OB11GroupIncreaseEvent(+msg.peerUid, +memberUin, +operatorUin)
}
else if (groupElement.type === TipGroupElementType.Ban) {
ctx.logger.info('收到群成员禁言提示', groupElement)
@ -547,10 +539,9 @@ export namespace OB11Entities {
while ((match = regex.exec(xmlElement.content)) !== null) {
matches.push(match[1])
}
// log("新人进群匹配到的QQ号", matches)
if (matches.length === 2) {
const [inviter, invitee] = matches
return new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(invitee), parseInt(inviter), 'invite')
const [invitor, invitee] = matches
return new OB11GroupIncreaseEvent(+msg.peerUid, +invitee, +invitor, 'invite')
}
}
}
@ -594,11 +585,6 @@ export namespace OB11Entities {
const memberUin = json.items[1].param[0]
const title = json.items[3].txt
ctx.logger.info('收到群成员新头衔消息', json)
ctx.ntGroupApi.getGroupMember(msg.peerUid, memberUin).then(member => {
if (!isNullable(member)) {
member.memberSpecialTitle = title
}
})
return new OB11GroupTitleEvent(parseInt(msg.peerUid), parseInt(memberUin), title)
} else if (grayTipElement.jsonGrayTipElement?.busiId === '19217') {
ctx.logger.info('收到新人被邀请进群消息', grayTipElement)
@ -616,13 +602,7 @@ export namespace OB11Entities {
msg: RawMessage,
shortId: number
): Promise<OB11FriendRecallNoticeEvent | OB11GroupRecallNoticeEvent | undefined> {
const msgElement = msg.elements.find(
(element) => element.grayTipElement?.subElementType === GrayTipElementSubType.Revoke,
)
if (!msgElement) {
return
}
const revokeElement = msgElement.grayTipElement!.revokeElement
const revokeElement = msg.elements[0].grayTipElement?.revokeElement
if (msg.chatType === ChatType.Group) {
const operator = await ctx.ntGroupApi.getGroupMember(msg.peerUid, revokeElement!.operatorUid)
return new OB11GroupRecallNoticeEvent(

View File

@ -56,7 +56,7 @@ export async function createSendElements(
remainAtAllCount = (await ctx.ntGroupApi.getGroupRemainAtTimes(groupCode)).atInfo
.RemainAtAllCountForUin
ctx.logger.info(`${groupCode}剩余at全体次数`, remainAtAllCount)
const self = await ctx.ntGroupApi.getGroupMember(groupCode, selfInfo.uin)
const self = await ctx.ntGroupApi.getGroupMember(groupCode, selfInfo.uid)
isAdmin = self?.role === GroupMemberRole.admin || self?.role === GroupMemberRole.owner
} catch (e) {
}
@ -66,44 +66,24 @@ export async function createSendElements(
}
}
else if (peer.chatType === ChatType.Group) {
const atMember = await ctx.ntGroupApi.getGroupMember(peer.peerUid, atQQ)
if (atMember) {
const display = `@${atMember.cardName || atMember.nick}`
sendElements.push(
SendElement.at(atQQ, atMember.uid, AtType.One, display),
)
} else {
const atNmae = sendMsg.data?.name
const uid = await ctx.ntUserApi.getUidByUin(atQQ) || ''
const display = atNmae ? `@${atNmae}` : ''
sendElements.push(
SendElement.at(atQQ, uid, AtType.One, display),
)
}
const uid = await ctx.ntUserApi.getUidByUin(atQQ) ?? ''
const atNmae = sendMsg.data?.name
const display = atNmae ? `@${atNmae}` : ''
sendElements.push(SendElement.at(atQQ, uid, AtType.One, display))
}
}
}
break
case OB11MessageDataType.reply: {
if (sendMsg.data?.id) {
const replyMsgId = await ctx.store.getMsgInfoByShortId(+sendMsg.data.id)
if (!replyMsgId) {
ctx.logger.warn('回复消息不存在', replyMsgId)
const info = await ctx.store.getMsgInfoByShortId(+sendMsg.data.id)
if (!info) {
ctx.logger.warn('回复消息不存在', info)
continue
}
const replyMsg = (await ctx.ntMsgApi.getMsgsByMsgId(
replyMsgId.peer,
[replyMsgId.msgId!]
)).msgList[0]
if (replyMsg) {
sendElements.push(
SendElement.reply(
replyMsg.msgSeq,
replyMsg.msgId,
replyMsg.senderUin!,
replyMsg.senderUin!,
),
)
const source = (await ctx.ntMsgApi.getMsgsByMsgId(info.peer, [info.msgId])).msgList[0]
if (source) {
sendElements.push(SendElement.reply(source.msgSeq, source.msgId, source.senderUin))
}
}
}
@ -147,7 +127,7 @@ export async function createSendElements(
const { path, fileName } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
let thumb = sendMsg.data.thumb
if (thumb) {
const uri2LocalRes = await uri2local(thumb)
const uri2LocalRes = await uri2local(ctx, thumb)
if (uri2LocalRes.success) thumb = uri2LocalRes.path
}
const res = await SendElement.video(ctx, path, fileName, thumb)
@ -206,7 +186,7 @@ async function handleOb11FileLikeMessage(
fileName,
errMsg,
success,
} = (await uri2local(inputdata?.url || inputdata.file))
} = (await uri2local(ctx, inputdata.url || inputdata.file))
if (!success) {
ctx.logger.error(errMsg)