Merge pull request #448 from LLOneBot/dev

release: 3.33.8
This commit is contained in:
idranme 2024-09-26 12:57:16 +08:00 committed by GitHub
commit a58fb31f8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 138 additions and 81 deletions

View File

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

View File

@ -236,7 +236,7 @@ export class NTQQFileCacheApi extends Service {
} }
scanCache() { scanCache() {
invoke<GeneralCallResult>(ReceiveCmdS.CACHE_SCAN_FINISH, [], { classNameIsRegister: true }) 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, [null, null], { timeout: 300 * Time.second })
} }

View File

@ -101,52 +101,20 @@ export class NTQQFriendApi extends Service {
return retMap return retMap
} }
async getBuddyV2ExWithCate(refresh = false) { async getBuddyV2WithCate(refresh = false) {
const session = getSession() const data = await invoke<{
if (session) { buddyCategory: CategoryFriend[]
const uids: string[] = [] userSimpleInfos: Record<string, SimpleInfo>
const categoryMap: Map<string, Dict> = new Map() }>(
const buddyService = session.getBuddyService() 'getBuddyList',
const buddyListV2 = (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data [refresh],
uids.push( {
...buddyListV2.flatMap(item => { className: NTClass.NODE_STORE_API,
item.buddyUids.forEach(uid => { cbCmd: ReceiveCmdS.FRIENDS,
categoryMap.set(uid, { categoryId: item.categoryId, categroyName: item.categroyName }) afterFirstCmd: false,
})
return item.buddyUids
}))
const data = await session.getProfileService().getCoreAndBaseInfo('nodeStore', uids)
return Array.from(data).map(([key, value]) => {
const category = categoryMap.get(key)
return category ? { ...value, categoryId: category.categoryId, categroyName: category.categroyName } : value
})
} else {
const data = await invoke<{
buddyCategory: CategoryFriend[]
userSimpleInfos: Record<string, SimpleInfo>
}>(
'getBuddyList',
[refresh],
{
className: NTClass.NODE_STORE_API,
cbCmd: ReceiveCmdS.FRIENDS,
afterFirstCmd: false,
}
)
const category: Map<number, Pick<CategoryFriend, 'buddyUids' | 'categroyName'>> = new Map()
for (const item of data.buddyCategory) {
category.set(item.categoryId, pick(item, ['buddyUids', 'categroyName']))
} }
return Object.values(data.userSimpleInfos) )
.filter(v => v.baseInfo && category.get(v.baseInfo.categoryId)?.buddyUids.includes(v.uid!)) return data
.map(value => {
return {
...value,
categoryId: value.baseInfo.categoryId,
categroyName: category.get(value.baseInfo.categoryId)?.categroyName
}
})
}
} }
async isBuddy(uid: string): Promise<boolean> { async isBuddy(uid: string): Promise<boolean> {

View File

@ -7,7 +7,8 @@ import {
GroupRequestOperateTypes, GroupRequestOperateTypes,
GetFileListParam, GetFileListParam,
OnGroupFileInfoUpdateParams, OnGroupFileInfoUpdateParams,
PublishGroupBulletinReq PublishGroupBulletinReq,
GroupAllInfo
} from '../types' } from '../types'
import { invoke, NTClass, NTMethod } from '../ntcall' import { invoke, NTClass, NTMethod } from '../ntcall'
import { GeneralCallResult } from '../services' import { GeneralCallResult } from '../services'
@ -104,7 +105,7 @@ export class NTQQGroupApi extends Service {
} }
async getSingleScreenNotifies(num: number) { async getSingleScreenNotifies(num: number) {
invoke(ReceiveCmdS.GROUP_NOTIFY, [], { classNameIsRegister: true }) invoke(ReceiveCmdS.GROUP_NOTIFY, [], { registerEvent: true })
return (await invoke<GroupNotifies>( return (await invoke<GroupNotifies>(
'nodeIKernelGroupService/getSingleScreenNotifies', 'nodeIKernelGroupService/getSingleScreenNotifies',
[{ doubt: false, startSeq: '', number: num }, null], [{ doubt: false, startSeq: '', number: num }, null],
@ -272,7 +273,7 @@ export class NTQQGroupApi extends Service {
} }
async getGroupFileList(groupId: string, fileListForm: GetFileListParam) { async getGroupFileList(groupId: string, fileListForm: GetFileListParam) {
invoke('nodeIKernelMsgListener/onGroupFileInfoUpdate', [], { classNameIsRegister: true }) invoke('nodeIKernelMsgListener/onGroupFileInfoUpdate', [], { registerEvent: true })
const data = await invoke<{ fileInfo: OnGroupFileInfoUpdateParams }>( const data = await invoke<{ fileInfo: OnGroupFileInfoUpdateParams }>(
'nodeIKernelRichMediaService/getGroupFileList', 'nodeIKernelRichMediaService/getGroupFileList',
[ [
@ -326,4 +327,24 @@ export class NTQQGroupApi extends Service {
} }
}, null]) }, null])
} }
async getGroupAllInfo(groupCode: string, timeout = 1000) {
invoke('nodeIKernelGroupListener/onGroupAllInfoChange', [], { registerEvent: true })
return await invoke<{ groupAll: GroupAllInfo }>(
'nodeIKernelGroupService/getGroupAllInfo',
[
{
groupCode,
source: 4
},
null
],
{
cbCmd: 'nodeIKernelGroupListener/onGroupAllInfoChange',
afterFirstCmd: false,
cmdCB: payload => payload.groupAll.groupCode === groupCode,
timeout
}
)
}
} }

View File

@ -215,7 +215,7 @@ class Core extends Service {
this.ctx.parallel('nt/friend-request', payload.data.buddyReqs) this.ctx.parallel('nt/friend-request', payload.data.buddyReqs)
}) })
invoke('nodeIKernelMsgListener/onRecvSysMsg', [], { classNameIsRegister: true }) invoke('nodeIKernelMsgListener/onRecvSysMsg', [], { registerEvent: true })
registerReceiveHook<{ registerReceiveHook<{
msgBuf: number[] msgBuf: number[]

View File

@ -98,7 +98,7 @@ interface NTService {
interface InvokeOptions<ReturnType> { interface InvokeOptions<ReturnType> {
className?: NTClass className?: NTClass
channel?: NTChannel channel?: NTChannel
classNameIsRegister?: boolean registerEvent?: boolean
cbCmd?: string | string[] cbCmd?: string | string[]
cmdCB?: (payload: ReturnType, result: unknown) => boolean cmdCB?: (payload: ReturnType, result: unknown) => boolean
afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd
@ -115,7 +115,7 @@ export function invoke<
const timeout = options.timeout ?? 5000 const timeout = options.timeout ?? 5000
const afterFirstCmd = options.afterFirstCmd ?? true const afterFirstCmd = options.afterFirstCmd ?? true
let eventName = className + '-' + channel[channel.length - 1] let eventName = className + '-' + channel[channel.length - 1]
if (options.classNameIsRegister) { if (options.registerEvent) {
eventName += '-register' eventName += '-register'
} }
return new Promise<R>((resolve, reject) => { return new Promise<R>((resolve, reject) => {

View File

@ -78,3 +78,45 @@ export interface PublishGroupBulletinReq {
pinned: number pinned: number
confirmRequired: number confirmRequired: number
} }
export interface GroupAllInfo {
groupCode: string
ownerUid: string
groupFlag: number
groupFlagExt: number
maxMemberNum: number
memberNum: number
groupOption: number
classExt: number
groupName: string
fingerMemo: string
groupQuestion: string
certType: number
shutUpAllTimestamp: number
shutUpMeTimestamp: number //解除禁言时间
groupTypeFlag: number
privilegeFlag: number
groupSecLevel: number
groupFlagExt3: number
isConfGroup: number
isModifyConfGroupFace: number
isModifyConfGroupName: number
noFigerOpenFlag: number
noCodeFingerOpenFlag: number
groupFlagExt4: number
groupMemo: string
cmdUinMsgSeq: number
cmdUinJoinTime: number
cmdUinUinFlag: number
cmdUinMsgMask: number
groupSecLevelInfo: number
cmdUinPrivilege: number
cmdUinFlagEx2: number
appealDeadline: number
remarkName: number
isTop: boolean
richFingerMemo: string
groupAnswer: string
joinGroupAuth: string
isAllowModifyConfGroupName: number
}

View File

@ -4,14 +4,35 @@ import { OB11Entities } from '../../entities'
import { ActionName } from '../types' import { ActionName } from '../types'
import { getBuildVersion } from '@/common/utils' import { getBuildVersion } from '@/common/utils'
export class GetFriendWithCategory extends BaseAction<void, OB11User[]> { interface Category {
categoryId: number
categorySortId: number
categoryName: string
categoryMbCount: number
onlineCount: number
buddyList: OB11User[]
}
export class GetFriendWithCategory extends BaseAction<void, Category[]> {
actionName = ActionName.GetFriendsWithCategory actionName = ActionName.GetFriendsWithCategory
protected async _handle() { protected async _handle() {
if (getBuildVersion() >= 26702) { 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') throw new Error('this ntqq version not support, must be 26702 or later')
} }
const data = await this.ctx.ntFriendApi.getBuddyV2WithCate(true)
return data.buddyCategory.map(item => {
return {
categoryId: item.categoryId,
categorySortId: item.categorySortId,
categoryName: item.categroyName,
categoryMbCount: item.categroyMbCount,
onlineCount: item.onlineCount,
buddyList: item.buddyUids.map(uid => {
const info = data.userSimpleInfos[uid]
return OB11Entities.friendV2(info)
})
}
})
} }
} }

View File

@ -129,7 +129,7 @@ export namespace OB11Entities {
} }
else if (element.textElement) { else if (element.textElement) {
const text = element.textElement.content const text = element.textElement.content
if (!text.trim()) { if (!text) {
continue continue
} }
messageSegment = { messageSegment = {
@ -359,15 +359,15 @@ export namespace OB11Entities {
} }
if (messageSegment) { if (messageSegment) {
const cqCode = encodeCQCode(messageSegment) const cqCode = encodeCQCode(messageSegment)
if (messagePostFormat === 'string') { if (messagePostFormat === 'array') {
(resMsg.message as string) += cqCode
} else {
(resMsg.message as OB11MessageData[]).push(messageSegment) (resMsg.message as OB11MessageData[]).push(messageSegment)
} }
resMsg.raw_message += cqCode resMsg.raw_message += cqCode
} }
} }
resMsg.raw_message = resMsg.raw_message.trim() if (messagePostFormat === 'string') {
resMsg.message = resMsg.raw_message
}
return resMsg return resMsg
} }
@ -436,7 +436,7 @@ export namespace OB11Entities {
} }
} }
else if (groupElement.type === TipGroupElementType.Ban) { else if (groupElement.type === TipGroupElementType.Ban) {
ctx.logger.info('收到群员禁言提示', groupElement) ctx.logger.info('收到群员禁言提示', groupElement)
const memberUid = groupElement.shutUp?.member.uid const memberUid = groupElement.shutUp?.member.uid
const adminUid = groupElement.shutUp?.admin.uid const adminUid = groupElement.shutUp?.admin.uid
let memberUin: string = '' let memberUin: string = ''
@ -649,23 +649,20 @@ export namespace OB11Entities {
return friends.map(friend) return friends.map(friend)
} }
export function friendsV2(friends: FriendV2[]): OB11User[] { export function friendV2(raw: FriendV2): OB11User {
const data: OB11User[] = [] return {
for (const friend of friends) { ...omit(raw.baseInfo, ['richBuffer', 'phoneNum']),
const sexValue = sex(friend.baseInfo.sex!) ...omit(raw.coreInfo, ['nick']),
data.push({ user_id: parseInt(raw.coreInfo.uin),
...omit(friend.baseInfo, ['richBuffer']), nickname: raw.coreInfo.nick,
...friend.coreInfo, remark: raw.coreInfo.remark || raw.coreInfo.nick,
user_id: parseInt(friend.coreInfo.uin), sex: sex(raw.baseInfo.sex),
nickname: friend.coreInfo.nick, level: 0
remark: friend.coreInfo.nick,
sex: sexValue,
level: 0,
categroyName: friend.categroyName,
categoryId: friend.categoryId
})
} }
return data }
export function friendsV2(raw: FriendV2[]): OB11User[] {
return raw.map(friendV2)
} }
export function groupMemberRole(role: number): OB11GroupMemberRole | undefined { export function groupMemberRole(role: number): OB11GroupMemberRole | undefined {

View File

@ -248,8 +248,16 @@ export async function sendMsg(
sendElements: SendMessageElement[], sendElements: SendMessageElement[],
deleteAfterSentFiles: string[] deleteAfterSentFiles: string[]
) { ) {
if (peer.chatType === ChatType.Group) {
const info = await ctx.ntGroupApi.getGroupAllInfo(peer.peerUid)
.catch(() => undefined)
const shutUpMeTimestamp = info?.groupAll.shutUpMeTimestamp
if (shutUpMeTimestamp && shutUpMeTimestamp * 1000 > Date.now()) {
throw new Error('当前处于被禁言状态')
}
}
if (!sendElements.length) { if (!sendElements.length) {
throw '消息体无法解析,请检查是否发送了不支持的消息类型' throw new Error('消息体无法解析,请检查是否发送了不支持的消息类型')
} }
// 计算发送的文件大小 // 计算发送的文件大小
let totalSize = 0 let totalSize = 0

View File

@ -1 +1 @@
export const version = '3.33.7' export const version = '3.33.8'