Merge pull request #479 from LLOneBot/dev

release: 4.0.12
This commit is contained in:
idranme 2024-10-18 21:16:46 +08:00 committed by GitHub
commit 8b89fd7a0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 130 additions and 117 deletions

View File

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

View File

@ -62,21 +62,35 @@ export class NTQQMsgApi extends Service {
async sendMsg(peer: Peer, msgElements: SendMessageElement[], timeout = 10000) {
const uniqueId = await this.generateMsgUniqueId(peer.chatType)
peer.guildId = uniqueId
const msgAttributeInfos = new Map()
msgAttributeInfos.set(0, {
attrType: 0,
attrId: uniqueId,
vasMsgInfo: {
msgNamePlateInfo: {},
bubbleInfo: {},
avatarPendantInfo: {},
vasFont: {},
iceBreakInfo: {}
}
})
let sentMsgId: string
const data = await invoke<{ msgList: RawMessage[] }>(
'nodeIKernelMsgService/sendMsg',
[{
msgId: '0',
peer,
msgElements,
msgAttributeInfos: new Map()
msgAttributeInfos
}],
{
cbCmd: 'nodeIKernelMsgListener/onMsgInfoListUpdate',
afterFirstCmd: false,
cmdCB: payload => {
for (const msgRecord of payload.msgList) {
if (msgRecord.guildId === uniqueId && msgRecord.sendStatus === 2) {
if (msgRecord.msgAttrs.get(0)?.attrId === uniqueId && msgRecord.sendStatus === 2) {
sentMsgId = msgRecord.msgId
return true
}
}
@ -85,8 +99,8 @@ export class NTQQMsgApi extends Service {
timeout
}
)
delete peer.guildId
return data.msgList.find(msgRecord => msgRecord.guildId === uniqueId)
return data.msgList.find(msgRecord => msgRecord.msgId === sentMsgId)
}
async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {

View File

@ -173,9 +173,10 @@ class Core extends Service {
})
registerReceiveHook<{ msgList: RawMessage[] }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], payload => {
const startTime = this.startTime / 1000
for (const message of payload.msgList) {
// 过滤启动之前的消息
if (parseInt(message.msgTime) < this.startTime / 1000) {
if (parseInt(message.msgTime) < startTime) {
continue
}
if (message.senderUin && message.senderUin !== '0') {
@ -202,7 +203,9 @@ class Core extends Service {
this.ctx.parallel('nt/message-deleted', msg)
} else if (sentMsgIds.get(msg.msgId)) {
sentMsgIds.delete(msg.msgId)
this.ctx.parallel('nt/message-sent', msg)
if (msg.sendStatus === 2) {
this.ctx.parallel('nt/message-sent', msg)
}
}
}
})
@ -211,7 +214,7 @@ class Core extends Service {
sentMsgIds.set(payload.msgRecord.msgId, true)
})
const groupNotifyFlags: string[] = []
const groupNotifyIgnore: string[] = []
registerReceiveHook<{
doubt: boolean
oldestUnreadSeq: string
@ -225,13 +228,11 @@ class Core extends Service {
return
}
for (const notify of notifies) {
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type
const notifyTime = parseInt(notify.seq) / 1000
if (groupNotifyFlags.includes(flag) || notifyTime < this.startTime) {
const notifyTime = Math.trunc(+notify.seq / 1000)
if (groupNotifyIgnore.includes(notify.seq) || notifyTime < this.startTime) {
continue
}
groupNotifyFlags.shift()
groupNotifyFlags.push(flag)
groupNotifyIgnore.push(notify.seq)
this.ctx.parallel('nt/group-notify', notify)
}
}

View File

@ -29,11 +29,10 @@ export enum ReceiveCmdS {
const logHook = false
const receiveHooks: Array<{
const receiveHooks: Map<string, {
method: ReceiveCmdS[]
hookFunc: (payload: any) => void | Promise<void>
id: string
}> = []
}> = new Map()
const callHooks: Array<{
method: NTMethod[]
@ -72,7 +71,7 @@ export function startHook() {
}
} else if (args[2]) {
for (const receiveData of args[2]) {
for (const hook of receiveHooks) {
for (const hook of receiveHooks.values()) {
if (hook.method.includes(receiveData.cmdName)) {
Promise.resolve(hook.hookFunc(receiveData.payload))
}
@ -106,10 +105,9 @@ export function registerReceiveHook<PayloadType>(
if (!Array.isArray(method)) {
method = [method]
}
receiveHooks.push({
receiveHooks.set(id, {
method: method as ReceiveCmdS[],
hookFunc,
id,
})
return id
}
@ -128,6 +126,5 @@ export function registerCallHook(
}
export function removeReceiveHook(id: string) {
const index = receiveHooks.findIndex((h) => h.id === id)
receiveHooks.splice(index, 1)
receiveHooks.delete(id)
}

View File

@ -147,17 +147,13 @@ export function invoke<
const secondCallback = () => {
eventId = registerReceiveHook<R>(options.cbCmd!, (payload) => {
if (options.cmdCB) {
if (options.cmdCB(payload, result)) {
removeReceiveHook(eventId)
clearTimeout(timeoutId)
resolve(payload)
if (!options.cmdCB(payload, result)) {
return
}
}
else {
removeReceiveHook(eventId)
clearTimeout(timeoutId)
resolve(payload)
}
removeReceiveHook(eventId)
clearTimeout(timeoutId)
resolve(payload)
})
}
!afterFirstCmd && secondCallback()
@ -167,9 +163,12 @@ export function invoke<
afterFirstCmd && secondCallback()
}
else {
log('ntqq api call failed,', method, args, res)
clearTimeout(timeoutId)
reject(`ntqq api call failed, ${method}, ${res?.errMsg}`)
if (eventId) {
removeReceiveHook(eventId)
}
log('ntqq api call failed,', method, args, res)
reject(`ntqq api call failed, ${method}, ${JSON.stringify(res)}`)
}
}
}

View File

@ -438,6 +438,10 @@ export interface RawMessage {
likesCnt: string
isClicked: boolean
}[]
msgAttrs: Map<number, {
attrType: number
attrId: string
}>
}
export interface Peer {

View File

@ -296,7 +296,7 @@ export interface UserDetailInfoByUin {
birthday_year: number
birthday_month: number
birthday_day: number
sex: number //0
sex: number
topTime: string
constellation: number
shengXiao: number

View File

@ -3,59 +3,51 @@ import { OB11User } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
import { getBuildVersion } from '@/common/utils'
import { OB11UserSex } from '../../types'
import { calcQQLevel } from '@/common/utils/misc'
interface Payload {
user_id: number | string
}
export class GetStrangerInfo extends BaseAction<Payload, OB11User> {
interface Response extends OB11User {
reg_time: number
long_nick: string
}
export class GetStrangerInfo extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetStrangerInfo
payloadSchema = Schema.object({
user_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload): Promise<OB11User> {
if (!(getBuildVersion() >= 26702)) {
const user_id = payload.user_id.toString()
const extendData = await this.ctx.ntUserApi.getUserDetailInfoByUin(user_id)
const uid = (await this.ctx.ntUserApi.getUidByUin(user_id))!
if (!uid || uid.indexOf('*') != -1) {
const ret = {
...extendData,
user_id: parseInt(extendData.info.uin) || 0,
nickname: extendData.info.nick,
sex: OB11UserSex.Unknown,
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
qid: extendData.info.qid,
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
login_days: 0,
uid: ''
}
return ret
protected async _handle(payload: Payload) {
const uin = payload.user_id.toString()
if (getBuildVersion() >= 26702) {
const data = await this.ctx.ntUserApi.getUserDetailInfoByUinV2(uin)
return {
user_id: parseInt(data.detail.uin) || 0,
nickname: data.detail.simpleInfo.coreInfo.nick,
sex: OB11Entities.sex(data.detail.simpleInfo.baseInfo.sex),
age: data.detail.simpleInfo.baseInfo.age,
qid: data.detail.simpleInfo.baseInfo.qid,
level: data.detail.commonExt.qqLevel && calcQQLevel(data.detail.commonExt.qqLevel) || 0,
login_days: 0,
reg_time: data.detail.commonExt.regTime,
long_nick: data.detail.simpleInfo.baseInfo.longNick
}
const data = { ...extendData, ...(await this.ctx.ntUserApi.getUserDetailInfo(uid)) }
return OB11Entities.stranger(data)
} else {
const user_id = payload.user_id.toString()
const extendData = await this.ctx.ntUserApi.getUserDetailInfoByUinV2(user_id)
const uid = (await this.ctx.ntUserApi.getUidByUin(user_id))!
if (!uid || uid.indexOf('*') != -1) {
const ret = {
...extendData,
user_id: parseInt(extendData.detail.uin) || 0,
nickname: extendData.detail.simpleInfo.coreInfo.nick,
sex: OB11UserSex.Unknown,
age: 0,
level: extendData.detail.commonExt.qqLevel && calcQQLevel(extendData.detail.commonExt.qqLevel) || 0,
login_days: 0,
uid: ''
}
return ret
const data = await this.ctx.ntUserApi.getUserDetailInfoByUin(uin)
return {
user_id: parseInt(data.info.uin) || 0,
nickname: data.info.nick,
sex: OB11Entities.sex(data.info.sex),
age: data.info.birthday_year === 0 ? 0 : new Date().getFullYear() - data.info.birthday_year,
qid: data.info.qid,
level: data.info.qqLevel && calcQQLevel(data.info.qqLevel) || 0,
login_days: 0,
reg_time: data.info.regTime,
long_nick: data.info.longNick
}
const data = { ...extendData, ...(await this.ctx.ntUserApi.getUserDetailInfo(uid)) }
return OB11Entities.stranger(data)
}
}
}

View File

@ -27,7 +27,11 @@ export class UploadGroupFile extends BaseAction<Payload, null> {
if (!success) {
throw new Error(errMsg)
}
const file = await SendElement.file(this.ctx, path, payload.name || fileName, payload.folder ?? payload.folder_id)
const name = payload.name || fileName
if (name.includes('/') || name.includes('\\')) {
throw new Error(`文件名 ${name} 不合法`)
}
const file = await SendElement.file(this.ctx, path, name, payload.folder ?? payload.folder_id)
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group)
await sendMsg(this.ctx, peer, [file], [])
return null

View File

@ -23,7 +23,11 @@ export class UploadPrivateFile extends BaseAction<UploadPrivateFilePayload, null
if (!success) {
throw new Error(errMsg)
}
const sendFileEle = await SendElement.file(this.ctx, path, payload.name || fileName)
const name = payload.name || fileName
if (name.includes('/') || name.includes('\\')) {
throw new Error(`文件名 ${name} 不合法`)
}
const sendFileEle = await SendElement.file(this.ctx, path, name)
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Private)
await sendMsg(this.ctx, peer, [sendFileEle], [])
return null

View File

@ -24,13 +24,13 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
if (!uid) throw new Error('无法获取用户信息')
const member = await this.ctx.ntGroupApi.getGroupMember(groupCode, uid, payload.no_cache)
if (member) {
const ret = OB11Entities.groupMember(groupCode, member)
const date = Math.round(Date.now() / 1000)
const ret = OB11Entities.groupMember(+groupCode, member)
const date = Math.trunc(Date.now() / 1000)
ret.last_sent_time ??= date
ret.join_time ??= date
const info = await this.ctx.ntUserApi.getUserDetailInfo(member.uid)
ret.sex = OB11Entities.sex(info.sex!)
ret.qq_level = (info.qqLevel && calcQQLevel(info.qqLevel)) || 0
ret.qq_level = info.qqLevel && calcQQLevel(info.qqLevel) || 0
ret.age = info.age ?? 0
return ret
}

View File

@ -21,18 +21,22 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
if (groupMembers.size > 0) {
break
}
await this.ctx.sleep(100)
await this.ctx.sleep(60)
groupMembers = await this.ctx.ntGroupApi.getGroupMembers(groupCode)
}
const groupMembersArr = Array.from(groupMembers.values())
const date = Math.round(Date.now() / 1000)
return groupMembersArr.map(item => {
const member = OB11Entities.groupMember(groupCode, item)
const date = Math.trunc(Date.now() / 1000)
const groupId = Number(payload.group_id)
const ret: OB11GroupMember[] = []
for (const item of groupMembers.values()) {
const member = OB11Entities.groupMember(groupId, item)
member.join_time ??= date
member.last_sent_time ??= date
return member
})
ret.push(member)
}
return ret
}
}

View File

@ -23,11 +23,11 @@ export default class SetFriendAddRequest extends BaseAction<Payload, null> {
if (payload.remark) {
await this.ctx.ntFriendApi.setBuddyRemark(uid, payload.remark)
}
await this.ctx.ntMsgApi.activateChat({
/*await this.ctx.ntMsgApi.activateChat({
peerUid: uid,
chatType: ChatType.C2C,
guildId: ''
})
})*/
return null
}
}

View File

@ -155,7 +155,7 @@ class OB11HttpPost {
}
public async emitEvent(event: OB11BaseEvent | OB11Message) {
if (!this.activated) {
if (!this.activated || !this.config.hosts.length) {
return
}
const msgStr = JSON.stringify(event)

View File

@ -71,7 +71,7 @@ export namespace OB11Entities {
sub_type: 'friend',
message: messagePostFormat === 'string' ? '' : [],
message_format: messagePostFormat === 'string' ? 'string' : 'array',
post_type: selfUin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
post_type: selfUin === msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
}
if (debug) {
resMsg.raw = msg
@ -376,11 +376,23 @@ export namespace OB11Entities {
if (msg.chatType !== ChatType.C2C) {
return
}
if (msg.msgType !== 5) {
return
}
for (const element of msg.elements) {
if (element.grayTipElement) {
const { grayTipElement } = element
if (grayTipElement.jsonGrayTipElement?.busiId === '1061') {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
const param = grayTipElement.jsonGrayTipElement.xmlToJsonParam
if (param) {
return new OB11FriendPokeEvent(
Number(param.templParam.get('uin_str1')),
Number(param.templParam.get('uin_str2')),
json.items
)
}
const pokedetail: Dict[] = json.items
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid)
@ -405,31 +417,15 @@ export namespace OB11Entities {
if (msg.chatType !== ChatType.Group) {
return
}
/**if (msg.senderUin) {
const member = await ctx.ntGroupApi.getGroupMember(msg.peerUid, msg.senderUin)
if (member && member.cardName !== msg.sendMemberName) {
const event = new OB11GroupCardEvent(
parseInt(msg.peerUid),
parseInt(msg.senderUin),
msg.sendMemberName!,
member.cardName,
)
member.cardName = msg.sendMemberName!
return event
}
}*/
if (msg.msgType !== 5 && msg.msgType !== 3) {
return
}
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)
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) {
if (groupElement.type === TipGroupElementType.Ban) {
ctx.logger.info('收到群成员禁言提示', groupElement)
const memberUid = groupElement.shutUp?.member.uid
const adminUid = groupElement.shutUp?.admin.uid
@ -673,14 +669,14 @@ export namespace OB11Entities {
[Sex.female]: OB11UserSex.Female,
[Sex.unknown]: OB11UserSex.Unknown,
}
return sexMap[sex] || OB11UserSex.Unknown
return sexMap[sex] ?? OB11UserSex.Unknown
}
export function groupMember(group_id: string, member: GroupMember): OB11GroupMember {
export function groupMember(groupId: number, member: GroupMember): OB11GroupMember {
const titleExpireTime = +member.specialTitleExpireTime
const int32Max = Math.pow(2, 31) - 1
const int32Max = 2147483647
return {
group_id: parseInt(group_id),
group_id: groupId,
user_id: parseInt(member.uin),
nickname: member.nick,
card: member.cardName || member.nick,

View File

@ -11,8 +11,6 @@ export interface OB11User {
age?: number
qid?: string
login_days?: number
categroyName?: string
categoryId?: number
}
export enum OB11UserSex {

View File

@ -1 +1 @@
export const version = '4.0.11'
export const version = '4.0.12'