mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b40c81c5cb | ||
![]() |
ddf7ffcabe | ||
![]() |
2b0aa6249b | ||
![]() |
6bb4a8fe69 | ||
![]() |
91d78f22f7 | ||
![]() |
457ffc0922 | ||
![]() |
e3a2303e45 | ||
![]() |
8465c47d41 |
@@ -4,7 +4,7 @@
|
||||
"name": "LLOneBot",
|
||||
"slug": "LLOneBot",
|
||||
"description": "实现 OneBot 11 和 Satori 协议,用于 QQ 机器人开发",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.3",
|
||||
"icon": "./icon.webp",
|
||||
"authors": [
|
||||
{
|
||||
|
@@ -18,7 +18,7 @@ import {
|
||||
CHANNEL_UPDATE,
|
||||
CHANNEL_SET_CONFIG_CONFIRMED
|
||||
} from '../common/channels'
|
||||
import { hookNTQQApiCall, hookNTQQApiReceive } from '../ntqqapi/hook'
|
||||
import { startHook } from '../ntqqapi/hook'
|
||||
import { checkNewVersion, upgradeLLOneBot } from '../common/utils/upgrade'
|
||||
import { getConfigUtil } from '../common/config'
|
||||
import { checkFfmpeg } from '../common/utils/video'
|
||||
@@ -217,23 +217,11 @@ function onLoad() {
|
||||
|
||||
// 创建窗口时触发
|
||||
function onBrowserWindowCreated(window: BrowserWindow) {
|
||||
if (![2, 4, 6].includes(window.id)) {
|
||||
return
|
||||
}
|
||||
if (window.id === 2) {
|
||||
mainWindow = window
|
||||
}
|
||||
//log('window create', window.webContents.getURL().toString())
|
||||
try {
|
||||
hookNTQQApiCall(window, window.id !== 2)
|
||||
hookNTQQApiReceive(window, window.id !== 2)
|
||||
} catch (e) {
|
||||
log('LLOneBot hook error: ', String(e))
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
onLoad()
|
||||
startHook()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ export class NTQQGroupApi extends Service {
|
||||
}
|
||||
|
||||
async getGroupMember(groupCode: string, uid: string, forceUpdate = false) {
|
||||
invoke('nodeIKernelGroupListener/onMemberInfoChange', [], {
|
||||
await invoke('nodeIKernelGroupListener/onMemberInfoChange', [], {
|
||||
registerEvent: true
|
||||
})
|
||||
|
||||
|
@@ -16,7 +16,7 @@ import {
|
||||
SendTextElement,
|
||||
SendVideoElement,
|
||||
} from './types'
|
||||
import { stat, writeFile, copyFile, unlink } from 'node:fs/promises'
|
||||
import { stat, writeFile, copyFile, unlink, access } from 'node:fs/promises'
|
||||
import { calculateFileMD5 } from '../common/utils/file'
|
||||
import { defaultVideoThumb, getVideoInfo } from '../common/utils/video'
|
||||
import { encodeSilk } from '../common/utils/audio'
|
||||
@@ -115,25 +115,17 @@ export namespace SendElement {
|
||||
}
|
||||
|
||||
export async function video(ctx: Context, filePath: string, fileName = '', diyThumbPath = ''): Promise<SendVideoElement> {
|
||||
try {
|
||||
await stat(filePath)
|
||||
} catch (e) {
|
||||
throw `文件${filePath}异常,不存在`
|
||||
}
|
||||
ctx.logger.info('复制视频到QQ目录', filePath)
|
||||
await access(filePath)
|
||||
const { fileName: _fileName, path, fileSize, md5 } = await ctx.ntFileApi.uploadFile(filePath, ElementType.Video)
|
||||
|
||||
ctx.logger.info('复制视频到QQ目录完成', path)
|
||||
if (fileSize === 0) {
|
||||
throw '文件异常,大小为0'
|
||||
throw new Error('文件异常,大小为 0')
|
||||
}
|
||||
const maxMB = 100;
|
||||
const maxMB = 100
|
||||
if (fileSize > 1024 * 1024 * maxMB) {
|
||||
throw `视频过大,最大支持${maxMB}MB,当前文件大小${fileSize}B`
|
||||
throw new Error(`视频过大,最大支持${maxMB}MB,当前文件大小${fileSize}B`)
|
||||
}
|
||||
let thumbDir = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`)
|
||||
thumbDir = pathLib.dirname(thumbDir)
|
||||
// log("thumb 目录", thumb)
|
||||
const thumbDir = pathLib.dirname(path.replaceAll('\\', '/').replace(`/Ori/`, `/Thumb/`))
|
||||
let videoInfo = {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
@@ -194,7 +186,6 @@ export namespace SendElement {
|
||||
const _thumbPath = await createThumb
|
||||
ctx.logger.info('生成视频缩略图', _thumbPath)
|
||||
const thumbSize = (await stat(_thumbPath)).size
|
||||
// log("生成缩略图", _thumbPath)
|
||||
thumbPath.set(0, _thumbPath)
|
||||
const thumbMd5 = await calculateFileMD5(_thumbPath)
|
||||
const element: SendVideoElement = {
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import { NTClass, NTMethod } from './ntcall'
|
||||
import { NTMethod } from './ntcall'
|
||||
import { log } from '@/common/utils'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { Dict } from 'cosmokit'
|
||||
import { ipcMain } from 'electron'
|
||||
|
||||
export const hookApiCallbacks: Record<string, (res: any) => void> = {}
|
||||
|
||||
@@ -28,19 +27,6 @@ export enum ReceiveCmdS {
|
||||
MEDIA_UPLOAD_COMPLETE = 'nodeIKernelMsgListener/onRichMediaUploadComplete',
|
||||
}
|
||||
|
||||
type NTReturnData = [
|
||||
{
|
||||
type: 'request'
|
||||
eventName: NTClass
|
||||
callbackId?: string
|
||||
},
|
||||
{
|
||||
cmdName: ReceiveCmdS
|
||||
cmdType: 'event'
|
||||
payload: unknown
|
||||
}[]
|
||||
]
|
||||
|
||||
const logHook = false
|
||||
|
||||
const receiveHooks: Array<{
|
||||
@@ -54,92 +40,64 @@ const callHooks: Array<{
|
||||
hookFunc: (callParams: unknown[]) => void | Promise<void>
|
||||
}> = []
|
||||
|
||||
export function hookNTQQApiReceive(window: BrowserWindow, onlyLog: boolean) {
|
||||
window.webContents.send = new Proxy(window.webContents.send, {
|
||||
apply(target, thisArg, args: [channel: string, ...args: NTReturnData]) {
|
||||
try {
|
||||
if (logHook && !args[1]?.eventName?.startsWith('ns-LoggerApi')) {
|
||||
log('received ntqq api message', args)
|
||||
}
|
||||
} catch { }
|
||||
if (!onlyLog) {
|
||||
if (args[2] instanceof Array) {
|
||||
for (const receiveData of args[2]) {
|
||||
const ntMethodName = receiveData.cmdName
|
||||
for (const hook of receiveHooks) {
|
||||
if (hook.method.includes(ntMethodName)) {
|
||||
Promise.resolve(hook.hookFunc(receiveData.payload))
|
||||
export function startHook() {
|
||||
const senderExclude = Symbol()
|
||||
|
||||
ipcMain.emit = new Proxy(ipcMain.emit, {
|
||||
apply(target, thisArg, args: [eventName: string, ...args: any]) {
|
||||
if (args[2]?.eventName.startsWith('ns-LoggerApi')) {
|
||||
return target.apply(thisArg, args)
|
||||
}
|
||||
if (logHook) {
|
||||
log('request', args)
|
||||
}
|
||||
|
||||
const event = args[1]
|
||||
if (event.sender && !event.sender[senderExclude]) {
|
||||
event.sender[senderExclude] = true
|
||||
event.sender.send = new Proxy(event.sender.send, {
|
||||
apply(target, thisArg, args: any[]) {
|
||||
if (args[1].eventName?.startsWith('ns-LoggerApi')) {
|
||||
return target.apply(thisArg, args)
|
||||
}
|
||||
if (logHook) {
|
||||
log('received', args)
|
||||
}
|
||||
|
||||
const callbackId = args[1].callbackId
|
||||
if (callbackId) {
|
||||
if (hookApiCallbacks[callbackId]) {
|
||||
Promise.resolve(hookApiCallbacks[callbackId](args[2]))
|
||||
delete hookApiCallbacks[callbackId]
|
||||
}
|
||||
} else if (args[2]) {
|
||||
for (const receiveData of args[2]) {
|
||||
for (const hook of receiveHooks) {
|
||||
if (hook.method.includes(receiveData.cmdName)) {
|
||||
Promise.resolve(hook.hookFunc(receiveData.payload))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target.apply(thisArg, args)
|
||||
}
|
||||
}
|
||||
if (args[1]?.callbackId) {
|
||||
const callbackId = args[1].callbackId
|
||||
if (hookApiCallbacks[callbackId]) {
|
||||
Promise.resolve(hookApiCallbacks[callbackId](args[2]))
|
||||
delete hookApiCallbacks[callbackId]
|
||||
})
|
||||
}
|
||||
|
||||
if (args[3]?.length) {
|
||||
const method = args[3][0]
|
||||
const callParams = args[3].slice(1)
|
||||
for (const hook of callHooks) {
|
||||
if (hook.method.includes(method)) {
|
||||
Promise.resolve(hook.hookFunc(callParams))
|
||||
}
|
||||
}
|
||||
}
|
||||
return target.apply(thisArg, args)
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function hookNTQQApiCall(window: BrowserWindow, onlyLog: boolean) {
|
||||
const webContents = window.webContents as Dict
|
||||
const ipc_message_proxy = webContents._events['-ipc-message']?.[0] || webContents._events['-ipc-message']
|
||||
|
||||
const proxyIpcMsg = new Proxy(ipc_message_proxy, {
|
||||
apply(target, thisArg, args) {
|
||||
const isLogger = args[3]?.[0]?.eventName?.startsWith('ns-LoggerApi')
|
||||
if (!isLogger) {
|
||||
try {
|
||||
logHook && log('call NTQQ api', args)
|
||||
} catch (e) { }
|
||||
if (!onlyLog) {
|
||||
try {
|
||||
const _args: unknown[] = args[3][1]
|
||||
const cmdName = _args[0] as NTMethod
|
||||
const callParams = _args.slice(1)
|
||||
callHooks.forEach((hook) => {
|
||||
if (hook.method.includes(cmdName)) {
|
||||
Promise.resolve(hook.hookFunc(callParams))
|
||||
}
|
||||
})
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
return target.apply(thisArg, args)
|
||||
},
|
||||
})
|
||||
if (webContents._events['-ipc-message']?.[0]) {
|
||||
webContents._events['-ipc-message'][0] = proxyIpcMsg
|
||||
} else {
|
||||
webContents._events['-ipc-message'] = proxyIpcMsg
|
||||
}
|
||||
|
||||
/*const ipc_invoke_proxy = webContents._events['-ipc-invoke']?.[0] || webContents._events['-ipc-invoke']
|
||||
const proxyIpcInvoke = new Proxy(ipc_invoke_proxy, {
|
||||
apply(target, thisArg, args) {
|
||||
//HOOK_LOG && log('call NTQQ invoke api', thisArg, args)
|
||||
args[0]['_replyChannel']['sendReply'] = new Proxy(args[0]['_replyChannel']['sendReply'], {
|
||||
apply(sendtarget, sendthisArg, sendargs) {
|
||||
sendtarget.apply(sendthisArg, sendargs)
|
||||
},
|
||||
})
|
||||
const ret = target.apply(thisArg, args)
|
||||
//HOOK_LOG && log('call NTQQ invoke api return', ret)
|
||||
return ret
|
||||
},
|
||||
})
|
||||
if (webContents._events['-ipc-invoke']?.[0]) {
|
||||
webContents._events['-ipc-invoke'][0] = proxyIpcInvoke
|
||||
} else {
|
||||
webContents._events['-ipc-invoke'] = proxyIpcInvoke
|
||||
}*/
|
||||
}
|
||||
|
||||
export function registerReceiveHook<PayloadType>(
|
||||
method: string | string[],
|
||||
hookFunc: (payload: PayloadType) => void,
|
||||
|
@@ -123,8 +123,8 @@ export function invoke<
|
||||
const apiArgs = [method, ...args]
|
||||
const callbackId = randomUUID()
|
||||
const timeoutId = setTimeout(() => {
|
||||
log(`ntqq api timeout ${channel}, ${eventName}, ${method}`, apiArgs)
|
||||
reject(`ntqq api timeout ${channel}, ${eventName}, ${method}, ${apiArgs}`)
|
||||
log(`ntqq api timeout ${channel}, ${eventName}, ${method}`, args)
|
||||
reject(`ntqq api timeout ${channel}, ${eventName}, ${method}, ${JSON.stringify(args)}`)
|
||||
}, timeout)
|
||||
|
||||
if (!options.cbCmd) {
|
||||
|
@@ -95,4 +95,6 @@ export interface NodeIKernelMsgService {
|
||||
getMultiMsg(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>
|
||||
|
||||
getTempChatInfo(chatType: number, uid: string): Promise<TmpChatInfoApi>
|
||||
|
||||
sendSsoCmdReqByContend(ssoCmd: string, content: string): Promise<GeneralCallResult & { rsp: string }>
|
||||
}
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import { QQLevel, Sex } from './user'
|
||||
|
||||
export enum GroupListUpdateType {
|
||||
REFRESHALL,
|
||||
GETALL,
|
||||
@@ -35,36 +33,54 @@ export interface Group {
|
||||
memberUin: string
|
||||
memberUid: string
|
||||
}
|
||||
members: GroupMember[] // 原始数据是没有这个的,为了方便自己加了这个字段
|
||||
createTime: string
|
||||
}
|
||||
|
||||
export enum GroupMemberRole {
|
||||
normal = 2,
|
||||
admin = 3,
|
||||
owner = 4,
|
||||
Normal = 2,
|
||||
Admin = 3,
|
||||
Owner = 4,
|
||||
}
|
||||
|
||||
export interface GroupMember {
|
||||
memberSpecialTitle?: string
|
||||
avatarPath: string
|
||||
cardName: string
|
||||
cardType: number
|
||||
isDelete: boolean
|
||||
nick: string
|
||||
uid: string
|
||||
qid: string
|
||||
uin: string
|
||||
nick: string
|
||||
remark: string
|
||||
role: GroupMemberRole // 群主:4, 管理员:3,群员:2
|
||||
shutUpTime: number // 禁言时间,单位是什么暂时不清楚
|
||||
uid: string // 加密的字符串
|
||||
uin: string // QQ号
|
||||
cardType: number
|
||||
cardName: string
|
||||
role: GroupMemberRole
|
||||
avatarPath: string
|
||||
shutUpTime: number
|
||||
isDelete: boolean
|
||||
isSpecialConcerned: boolean
|
||||
isSpecialShield: boolean
|
||||
isRobot: boolean
|
||||
sex?: Sex
|
||||
qqLevel?: QQLevel
|
||||
isChangeRole: boolean
|
||||
groupHonor: Uint8Array
|
||||
memberRealLevel: number
|
||||
memberLevel: number
|
||||
globalGroupLevel: number
|
||||
globalGroupPoint: number
|
||||
memberTitleId: number
|
||||
memberSpecialTitle: string
|
||||
specialTitleExpireTime: string
|
||||
userShowFlag: number
|
||||
userShowFlagNew: number
|
||||
richFlag: number
|
||||
mssVipType: number
|
||||
bigClubLevel: number
|
||||
bigClubFlag: number
|
||||
autoRemark: string
|
||||
creditLevel: number
|
||||
joinTime: number
|
||||
lastSpeakTime: number
|
||||
memberLevel: number
|
||||
memberFlag: number
|
||||
memberFlagExt: number
|
||||
memberMobileFlag: number
|
||||
memberFlagExt2: number
|
||||
isSpecialShielded: boolean
|
||||
cardNameId: number
|
||||
}
|
||||
|
||||
export interface PublishGroupBulletinReq {
|
||||
|
@@ -67,6 +67,7 @@ export interface User {
|
||||
recommendImgFlag?: number
|
||||
disableEmojiShortCuts?: number
|
||||
pendantId?: string
|
||||
age?: number
|
||||
}
|
||||
|
||||
export interface SelfInfo extends User {
|
||||
|
@@ -26,7 +26,7 @@ export class GetStrangerInfo extends BaseAction<Payload, OB11User> {
|
||||
...extendData,
|
||||
user_id: parseInt(extendData.info.uin) || 0,
|
||||
nickname: extendData.info.nick,
|
||||
sex: OB11UserSex.unknown,
|
||||
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,
|
||||
@@ -46,7 +46,7 @@ export class GetStrangerInfo extends BaseAction<Payload, OB11User> {
|
||||
...extendData,
|
||||
user_id: parseInt(extendData.detail.uin) || 0,
|
||||
nickname: extendData.detail.simpleInfo.coreInfo.nick,
|
||||
sex: OB11UserSex.unknown,
|
||||
sex: OB11UserSex.Unknown,
|
||||
age: 0,
|
||||
level: extendData.detail.commonExt.qqLevel && calcQQLevel(extendData.detail.commonExt.qqLevel) || 0,
|
||||
login_days: 0,
|
||||
|
@@ -2,7 +2,7 @@ import { BaseAction, Schema } from '../BaseAction'
|
||||
import { OB11GroupMember } from '../../types'
|
||||
import { OB11Entities } from '../../entities'
|
||||
import { ActionName } from '../types'
|
||||
import { isNullable } from 'cosmokit'
|
||||
import { calcQQLevel } from '@/common/utils/misc'
|
||||
|
||||
interface Payload {
|
||||
group_id: number | string
|
||||
@@ -22,14 +22,14 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||
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)
|
||||
Object.assign(member, info)
|
||||
}
|
||||
const ret = OB11Entities.groupMember(groupCode, member)
|
||||
const date = Math.round(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.age = info.age ?? 0
|
||||
return ret
|
||||
}
|
||||
throw new Error(`群成员${payload.user_id}不存在`)
|
||||
|
@@ -25,7 +25,7 @@ export default class SetGroupAdmin extends BaseAction<Payload, null> {
|
||||
await this.ctx.ntGroupApi.setMemberRole(
|
||||
groupCode,
|
||||
uid,
|
||||
payload.enable ? GroupMemberRole.admin : GroupMemberRole.normal
|
||||
payload.enable ? GroupMemberRole.Admin : GroupMemberRole.Normal
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
@@ -647,21 +647,21 @@ export namespace OB11Entities {
|
||||
return raw.map(friendV2)
|
||||
}
|
||||
|
||||
export function groupMemberRole(role: number): OB11GroupMemberRole | undefined {
|
||||
export function groupMemberRole(role: number): OB11GroupMemberRole {
|
||||
return {
|
||||
4: OB11GroupMemberRole.owner,
|
||||
3: OB11GroupMemberRole.admin,
|
||||
2: OB11GroupMemberRole.member,
|
||||
}[role]
|
||||
4: OB11GroupMemberRole.Owner,
|
||||
3: OB11GroupMemberRole.Admin,
|
||||
2: OB11GroupMemberRole.Member,
|
||||
}[role] ?? OB11GroupMemberRole.Member
|
||||
}
|
||||
|
||||
export function sex(sex: Sex): OB11UserSex {
|
||||
const sexMap = {
|
||||
[Sex.male]: OB11UserSex.male,
|
||||
[Sex.female]: OB11UserSex.female,
|
||||
[Sex.unknown]: OB11UserSex.unknown,
|
||||
[Sex.male]: OB11UserSex.Male,
|
||||
[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 {
|
||||
@@ -670,20 +670,20 @@ export namespace OB11Entities {
|
||||
user_id: parseInt(member.uin),
|
||||
nickname: member.nick,
|
||||
card: member.cardName,
|
||||
sex: sex(member.sex!),
|
||||
sex: OB11UserSex.Unknown,
|
||||
age: 0,
|
||||
area: '',
|
||||
level: String(member.memberLevel ?? 0),
|
||||
qq_level: (member.qqLevel && calcQQLevel(member.qqLevel)) || 0,
|
||||
qq_level: 0,
|
||||
join_time: member.joinTime,
|
||||
last_sent_time: member.lastSpeakTime,
|
||||
title_expire_time: 0,
|
||||
title_expire_time: +member.specialTitleExpireTime,
|
||||
unfriendly: false,
|
||||
card_changeable: true,
|
||||
is_robot: member.isRobot,
|
||||
shut_up_timestamp: member.shutUpTime,
|
||||
role: groupMemberRole(member.role),
|
||||
title: member.memberSpecialTitle || '',
|
||||
title: member.memberSpecialTitle,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -57,7 +57,7 @@ export async function createSendElements(
|
||||
.RemainAtAllCountForUin
|
||||
ctx.logger.info(`群${groupCode}剩余at全体次数`, remainAtAllCount)
|
||||
const self = await ctx.ntGroupApi.getGroupMember(groupCode, selfInfo.uid)
|
||||
isAdmin = self?.role === GroupMemberRole.admin || self?.role === GroupMemberRole.owner
|
||||
isAdmin = self?.role === GroupMemberRole.Admin || self?.role === GroupMemberRole.Owner
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
@@ -67,8 +67,15 @@ export async function createSendElements(
|
||||
}
|
||||
else if (peer.chatType === ChatType.Group) {
|
||||
const uid = await ctx.ntUserApi.getUidByUin(atQQ) ?? ''
|
||||
const atNmae = sendMsg.data?.name
|
||||
const display = atNmae ? `@${atNmae}` : ''
|
||||
let display = ''
|
||||
if (sendMsg.data.name) {
|
||||
display = `@${sendMsg.data.name}`
|
||||
} else {
|
||||
try {
|
||||
const member = await ctx.ntGroupApi.getGroupMember(peer.peerUid, uid)
|
||||
display = `@${member.cardName || member.nick}`
|
||||
} catch { }
|
||||
}
|
||||
sendElements.push(SendElement.at(atQQ, uid, AtType.One, display))
|
||||
}
|
||||
}
|
||||
|
@@ -16,36 +16,36 @@ export interface OB11User {
|
||||
}
|
||||
|
||||
export enum OB11UserSex {
|
||||
male = 'male',
|
||||
female = 'female',
|
||||
unknown = 'unknown',
|
||||
Male = 'male',
|
||||
Female = 'female',
|
||||
Unknown = 'unknown',
|
||||
}
|
||||
|
||||
export enum OB11GroupMemberRole {
|
||||
owner = 'owner',
|
||||
admin = 'admin',
|
||||
member = 'member',
|
||||
Owner = 'owner',
|
||||
Admin = 'admin',
|
||||
Member = 'member',
|
||||
}
|
||||
|
||||
export interface OB11GroupMember {
|
||||
group_id: number
|
||||
user_id: number
|
||||
nickname: string
|
||||
card?: string
|
||||
sex?: OB11UserSex
|
||||
age?: number
|
||||
join_time?: number
|
||||
last_sent_time?: number
|
||||
level?: string
|
||||
card: string
|
||||
sex: OB11UserSex
|
||||
age: number
|
||||
join_time: number
|
||||
last_sent_time: number
|
||||
level: string
|
||||
qq_level?: number
|
||||
role?: OB11GroupMemberRole
|
||||
title?: string
|
||||
area?: string
|
||||
unfriendly?: boolean
|
||||
title_expire_time?: number
|
||||
card_changeable?: boolean
|
||||
role: OB11GroupMemberRole
|
||||
title: string
|
||||
area: string
|
||||
unfriendly: boolean
|
||||
title_expire_time: number
|
||||
card_changeable: boolean
|
||||
// 以下为gocq字段
|
||||
shut_up_timestamp?: number
|
||||
shut_up_timestamp: number
|
||||
// 以下为扩展字段
|
||||
is_robot?: boolean
|
||||
qage?: number
|
||||
|
@@ -56,7 +56,7 @@ export class SatoriServer {
|
||||
|
||||
const { listen, port } = this.config
|
||||
this.httpServer = this.express.listen(port, listen, () => {
|
||||
this.ctx.logger.info(`HTTP server started ${listen}:${port}`)
|
||||
this.ctx.logger.info(`server started ${listen}:${port}`)
|
||||
})
|
||||
this.wsServer = new WebSocketServer({
|
||||
server: this.httpServer
|
||||
@@ -65,7 +65,7 @@ export class SatoriServer {
|
||||
this.wsServer.on('connection', (socket, req) => {
|
||||
const url = req.url?.split('?').shift()
|
||||
if (!['/v1/events', '/v1/events/'].includes(url!)) {
|
||||
return socket.close()
|
||||
return socket.close(1008, 'invalid address')
|
||||
}
|
||||
|
||||
socket.addEventListener('message', async (event) => {
|
||||
|
@@ -155,20 +155,15 @@ export async function decodeMessage(
|
||||
}
|
||||
message.user = decodeMessageUser(data)
|
||||
message.created_at = +data.msgTime * 1000
|
||||
if (message.channel.type === Universal.Channel.Type.DIRECT) {
|
||||
if (!message.user.name) {
|
||||
const info = await ctx.ntUserApi.getUserSimpleInfo(data.senderUid)
|
||||
message.channel.name = info.nick
|
||||
message.user.name = info.nick
|
||||
message.user.nick = info.remark || info.nick
|
||||
if (message.channel.type === Universal.Channel.Type.DIRECT) {
|
||||
message.channel.name = info.nick
|
||||
}
|
||||
}
|
||||
if (guildId) {
|
||||
let nick = data.sendMemberName || data.sendNickName
|
||||
if (!data.sendNickName) {
|
||||
const info = await ctx.ntGroupApi.getGroupMember(guildId, data.senderUid)
|
||||
message.user.name = info.nick
|
||||
message.user.nick = info.remark || info.nick
|
||||
nick = info.cardName || info.nick
|
||||
}
|
||||
message.guild = {
|
||||
id: guildId,
|
||||
name: data.peerName,
|
||||
@@ -176,7 +171,7 @@ export async function decodeMessage(
|
||||
}
|
||||
message.member = {
|
||||
user: message.user,
|
||||
nick
|
||||
nick: data.sendMemberName || message.user.name
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1 +1 @@
|
||||
export const version = '4.0.1'
|
||||
export const version = '4.0.3'
|
||||
|
Reference in New Issue
Block a user