mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8b89fd7a0b | ||
![]() |
1b0c9ad57c | ||
![]() |
2910b8f4e6 | ||
![]() |
2453509734 | ||
![]() |
8239e9a243 | ||
![]() |
50e5f89f4f | ||
![]() |
be2119a1e6 | ||
![]() |
951afea794 | ||
![]() |
0946d9652e | ||
![]() |
a66e48dfb0 | ||
![]() |
029842ca08 | ||
![]() |
39fda24799 |
@@ -4,7 +4,7 @@
|
||||
"name": "LLOneBot",
|
||||
"slug": "LLOneBot",
|
||||
"description": "实现 OneBot 11 和 Satori 协议,用于 QQ 机器人开发",
|
||||
"version": "4.0.9",
|
||||
"version": "4.0.12",
|
||||
"icon": "./icon.webp",
|
||||
"authors": [
|
||||
{
|
||||
|
@@ -12,7 +12,7 @@
|
||||
"deploy-win": "cmd /c \"xcopy /C /S /Y dist\\* %LITELOADERQQNT_PROFILE%\\plugins\\LLOneBot\\\"",
|
||||
"format": "prettier -cw .",
|
||||
"check": "tsc",
|
||||
"compile:proto": "pbjs --no-create --no-convert --no-encode --no-verify -t static-module -w es6 -p src/ntqqapi/proto -o src/ntqqapi/proto/compiled.js systemMessage.proto profileLikeTip.proto && pbts -o src/ntqqapi/proto/compiled.d.ts src/ntqqapi/proto/compiled.js"
|
||||
"compile:proto": "pbjs --no-create --no-convert --no-encode --no-verify -t static-module -w es6 -p src/ntqqapi/proto -o src/ntqqapi/proto/compiled.js systemMessage.proto profileLikeTip.proto groupMemberIncrease.proto && pbts -o src/ntqqapi/proto/compiled.d.ts src/ntqqapi/proto/compiled.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
@@ -43,7 +43,7 @@
|
||||
"electron-vite": "^2.3.0",
|
||||
"protobufjs-cli": "^1.1.3",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.4.8",
|
||||
"vite": "^5.4.9",
|
||||
"vite-plugin-cp": "^4.0.8"
|
||||
},
|
||||
"packageManager": "yarn@4.5.0"
|
||||
|
@@ -1,6 +1,5 @@
|
||||
export const CHANNEL_GET_CONFIG = 'llonebot_get_config'
|
||||
export const CHANNEL_SET_CONFIG = 'llonebot_set_config'
|
||||
export const CHANNEL_SET_CONFIG_CONFIRMED = 'llonebot_set_config_confirmed'
|
||||
export const CHANNEL_LOG = 'llonebot_log'
|
||||
export const CHANNEL_ERROR = 'llonebot_error'
|
||||
export const CHANNEL_UPDATE = 'llonebot_update'
|
||||
|
@@ -50,7 +50,7 @@ export class ConfigUtil {
|
||||
token: '',
|
||||
enableLocalFile2Url: false,
|
||||
debug: false,
|
||||
log: false,
|
||||
log: true,
|
||||
autoDeleteFile: false,
|
||||
autoDeleteFileSecond: 60,
|
||||
musicSignUrl: '',
|
||||
|
126
src/main/main.ts
126
src/main/main.ts
@@ -15,8 +15,7 @@ import {
|
||||
CHANNEL_LOG,
|
||||
CHANNEL_SELECT_FILE,
|
||||
CHANNEL_SET_CONFIG,
|
||||
CHANNEL_UPDATE,
|
||||
CHANNEL_SET_CONFIG_CONFIRMED
|
||||
CHANNEL_UPDATE
|
||||
} from '../common/channels'
|
||||
import { startHook } from '../ntqqapi/hook'
|
||||
import { checkNewVersion, upgradeLLOneBot } from '../common/utils/upgrade'
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
NTQQWebApi,
|
||||
NTQQWindowApi
|
||||
} from '../ntqqapi/api'
|
||||
import { mkdir } from 'node:fs/promises'
|
||||
import { existsSync, mkdirSync } from 'node:fs'
|
||||
|
||||
declare module 'cordis' {
|
||||
@@ -56,6 +54,29 @@ function onLoad() {
|
||||
mkdirSync(LOG_DIR)
|
||||
}
|
||||
|
||||
if (!existsSync(TEMP_DIR)) {
|
||||
mkdirSync(TEMP_DIR)
|
||||
}
|
||||
|
||||
const dbDir = path.join(DATA_DIR, 'database')
|
||||
if (!existsSync(dbDir)) {
|
||||
mkdirSync(dbDir)
|
||||
}
|
||||
|
||||
const ctx = new Context()
|
||||
|
||||
ctx.plugin(NTQQFileApi)
|
||||
ctx.plugin(NTQQFileCacheApi)
|
||||
ctx.plugin(NTQQFriendApi)
|
||||
ctx.plugin(NTQQGroupApi)
|
||||
ctx.plugin(NTQQMsgApi)
|
||||
ctx.plugin(NTQQUserApi)
|
||||
ctx.plugin(NTQQWebApi)
|
||||
ctx.plugin(NTQQWindowApi)
|
||||
ctx.plugin(Database)
|
||||
|
||||
let started = false
|
||||
|
||||
ipcMain.handle(CHANNEL_CHECK_VERSION, async () => {
|
||||
return checkNewVersion()
|
||||
})
|
||||
@@ -114,7 +135,9 @@ function onLoad() {
|
||||
if (!ask) {
|
||||
getConfigUtil().setConfig(config)
|
||||
log('配置已更新', config)
|
||||
checkFfmpeg(config.ffmpeg).then()
|
||||
if (started) {
|
||||
ctx.parallel('llob/config-updated', config)
|
||||
}
|
||||
resolve(true)
|
||||
return
|
||||
}
|
||||
@@ -131,7 +154,9 @@ function onLoad() {
|
||||
if (result.response === 0) {
|
||||
getConfigUtil().setConfig(config)
|
||||
log('配置已更新', config)
|
||||
checkFfmpeg(config.ffmpeg).then()
|
||||
if (started) {
|
||||
ctx.parallel('llob/config-updated', config)
|
||||
}
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
@@ -146,61 +171,6 @@ function onLoad() {
|
||||
log(arg)
|
||||
})
|
||||
|
||||
async function start() {
|
||||
log('process pid', process.pid)
|
||||
const config = getConfigUtil().getConfig()
|
||||
if (!existsSync(TEMP_DIR)) {
|
||||
await mkdir(TEMP_DIR)
|
||||
}
|
||||
const dbDir = path.join(DATA_DIR, 'database')
|
||||
if (!existsSync(dbDir)) {
|
||||
await mkdir(dbDir)
|
||||
}
|
||||
const ctx = new Context()
|
||||
ctx.plugin(Log, {
|
||||
enable: config.log!,
|
||||
filename: logFileName
|
||||
})
|
||||
ctx.plugin(NTQQFileApi)
|
||||
ctx.plugin(NTQQFileCacheApi)
|
||||
ctx.plugin(NTQQFriendApi)
|
||||
ctx.plugin(NTQQGroupApi)
|
||||
ctx.plugin(NTQQMsgApi)
|
||||
ctx.plugin(NTQQUserApi)
|
||||
ctx.plugin(NTQQWebApi)
|
||||
ctx.plugin(NTQQWindowApi)
|
||||
ctx.plugin(Core, config)
|
||||
ctx.plugin(Database)
|
||||
ctx.plugin(SQLiteDriver, {
|
||||
path: path.join(dbDir, `${selfInfo.uin}.db`)
|
||||
})
|
||||
ctx.plugin(Store, {
|
||||
msgCacheExpire: config.msgCacheExpire! * 1000
|
||||
})
|
||||
if (config.ob11.enable) {
|
||||
ctx.plugin(OneBot11Adapter, {
|
||||
...config.ob11,
|
||||
heartInterval: config.heartInterval,
|
||||
token: config.token!,
|
||||
debug: config.debug!,
|
||||
musicSignUrl: config.musicSignUrl,
|
||||
enableLocalFile2Url: config.enableLocalFile2Url!,
|
||||
ffmpeg: config.ffmpeg,
|
||||
})
|
||||
}
|
||||
if (config.satori.enable) {
|
||||
ctx.plugin(SatoriAdapter, {
|
||||
...config.satori,
|
||||
ffmpeg: config.ffmpeg,
|
||||
})
|
||||
}
|
||||
ctx.start()
|
||||
llonebotError.otherError = ''
|
||||
ipcMain.on(CHANNEL_SET_CONFIG_CONFIRMED, (event, config: LLOBConfig) => {
|
||||
ctx.parallel('llob/config-updated', config)
|
||||
})
|
||||
}
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
const self = Object.assign(selfInfo, {
|
||||
uin: globalThis.authData?.uin,
|
||||
@@ -209,7 +179,41 @@ function onLoad() {
|
||||
})
|
||||
if (self.uin) {
|
||||
clearInterval(intervalId)
|
||||
start()
|
||||
log('process pid', process.pid)
|
||||
|
||||
const config = getConfigUtil().getConfig()
|
||||
ctx.plugin(Log, {
|
||||
enable: config.log!,
|
||||
filename: logFileName
|
||||
})
|
||||
ctx.plugin(SQLiteDriver, {
|
||||
path: path.join(dbDir, `${selfInfo.uin}.db`)
|
||||
})
|
||||
ctx.plugin(Store, {
|
||||
msgCacheExpire: config.msgCacheExpire! * 1000
|
||||
})
|
||||
ctx.plugin(Core, config)
|
||||
if (config.ob11.enable) {
|
||||
ctx.plugin(OneBot11Adapter, {
|
||||
...config.ob11,
|
||||
heartInterval: config.heartInterval,
|
||||
token: config.token!,
|
||||
debug: config.debug!,
|
||||
musicSignUrl: config.musicSignUrl,
|
||||
enableLocalFile2Url: config.enableLocalFile2Url!,
|
||||
ffmpeg: config.ffmpeg,
|
||||
})
|
||||
}
|
||||
if (config.satori.enable) {
|
||||
ctx.plugin(SatoriAdapter, {
|
||||
...config.satori,
|
||||
ffmpeg: config.ffmpeg,
|
||||
})
|
||||
}
|
||||
|
||||
ctx.start()
|
||||
started = true
|
||||
llonebotError.otherError = ''
|
||||
}
|
||||
}, 600)
|
||||
}
|
||||
|
@@ -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[]) {
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
src/ntqqapi/proto/compiled.d.ts
vendored
84
src/ntqqapi/proto/compiled.d.ts
vendored
@@ -64,11 +64,8 @@ export namespace SysMsg {
|
||||
/** Properties of a SystemMessageHeader. */
|
||||
interface ISystemMessageHeader {
|
||||
|
||||
/** SystemMessageHeader peerNumber */
|
||||
peerNumber?: (number|null);
|
||||
|
||||
/** SystemMessageHeader peerString */
|
||||
peerString?: (string|null);
|
||||
/** SystemMessageHeader peerUin */
|
||||
peerUin?: (number|null);
|
||||
|
||||
/** SystemMessageHeader uin */
|
||||
uin?: (number|null);
|
||||
@@ -86,11 +83,8 @@ export namespace SysMsg {
|
||||
*/
|
||||
constructor(properties?: SysMsg.ISystemMessageHeader);
|
||||
|
||||
/** SystemMessageHeader peerNumber. */
|
||||
public peerNumber: number;
|
||||
|
||||
/** SystemMessageHeader peerString. */
|
||||
public peerString: string;
|
||||
/** SystemMessageHeader peerUin. */
|
||||
public peerUin: number;
|
||||
|
||||
/** SystemMessageHeader uin. */
|
||||
public uin: number;
|
||||
@@ -160,10 +154,10 @@ export namespace SysMsg {
|
||||
public msgType: number;
|
||||
|
||||
/** SystemMessageMsgSpec subType. */
|
||||
public subType: number;
|
||||
public subType?: (number|null);
|
||||
|
||||
/** SystemMessageMsgSpec subSubType. */
|
||||
public subSubType: number;
|
||||
public subSubType?: (number|null);
|
||||
|
||||
/** SystemMessageMsgSpec msgSeq. */
|
||||
public msgSeq: number;
|
||||
@@ -172,7 +166,7 @@ export namespace SysMsg {
|
||||
public time: number;
|
||||
|
||||
/** SystemMessageMsgSpec other. */
|
||||
public other: number;
|
||||
public other?: (number|null);
|
||||
|
||||
/**
|
||||
* Decodes a SystemMessageMsgSpec message from the specified reader or buffer.
|
||||
@@ -466,4 +460,68 @@ export namespace SysMsg {
|
||||
*/
|
||||
public static getTypeUrl(typeUrlPrefix?: string): string;
|
||||
}
|
||||
|
||||
/** Properties of a GroupMemberIncrease. */
|
||||
interface IGroupMemberIncrease {
|
||||
|
||||
/** GroupMemberIncrease groupCode */
|
||||
groupCode?: (number|null);
|
||||
|
||||
/** GroupMemberIncrease memberUid */
|
||||
memberUid?: (string|null);
|
||||
|
||||
/** GroupMemberIncrease type */
|
||||
type?: (number|null);
|
||||
|
||||
/** GroupMemberIncrease adminUid */
|
||||
adminUid?: (string|null);
|
||||
}
|
||||
|
||||
/** Represents a GroupMemberIncrease. */
|
||||
class GroupMemberIncrease implements IGroupMemberIncrease {
|
||||
|
||||
/**
|
||||
* Constructs a new GroupMemberIncrease.
|
||||
* @param [properties] Properties to set
|
||||
*/
|
||||
constructor(properties?: SysMsg.IGroupMemberIncrease);
|
||||
|
||||
/** GroupMemberIncrease groupCode. */
|
||||
public groupCode: number;
|
||||
|
||||
/** GroupMemberIncrease memberUid. */
|
||||
public memberUid: string;
|
||||
|
||||
/** GroupMemberIncrease type. */
|
||||
public type: number;
|
||||
|
||||
/** GroupMemberIncrease adminUid. */
|
||||
public adminUid: string;
|
||||
|
||||
/**
|
||||
* Decodes a GroupMemberIncrease message from the specified reader or buffer.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @param [length] Message length if known beforehand
|
||||
* @returns GroupMemberIncrease
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): SysMsg.GroupMemberIncrease;
|
||||
|
||||
/**
|
||||
* Decodes a GroupMemberIncrease message from the specified reader or buffer, length delimited.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @returns GroupMemberIncrease
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.GroupMemberIncrease;
|
||||
|
||||
/**
|
||||
* Gets the default type url for GroupMemberIncrease
|
||||
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
|
||||
* @returns The default type url
|
||||
*/
|
||||
public static getTypeUrl(typeUrlPrefix?: string): string;
|
||||
}
|
||||
}
|
||||
|
@@ -150,8 +150,7 @@ export const SysMsg = $root.SysMsg = (() => {
|
||||
* Properties of a SystemMessageHeader.
|
||||
* @memberof SysMsg
|
||||
* @interface ISystemMessageHeader
|
||||
* @property {number|null} [peerNumber] SystemMessageHeader peerNumber
|
||||
* @property {string|null} [peerString] SystemMessageHeader peerString
|
||||
* @property {number|null} [peerUin] SystemMessageHeader peerUin
|
||||
* @property {number|null} [uin] SystemMessageHeader uin
|
||||
* @property {string|null} [uid] SystemMessageHeader uid
|
||||
*/
|
||||
@@ -172,20 +171,12 @@ export const SysMsg = $root.SysMsg = (() => {
|
||||
}
|
||||
|
||||
/**
|
||||
* SystemMessageHeader peerNumber.
|
||||
* @member {number} peerNumber
|
||||
* SystemMessageHeader peerUin.
|
||||
* @member {number} peerUin
|
||||
* @memberof SysMsg.SystemMessageHeader
|
||||
* @instance
|
||||
*/
|
||||
SystemMessageHeader.prototype.peerNumber = 0;
|
||||
|
||||
/**
|
||||
* SystemMessageHeader peerString.
|
||||
* @member {string} peerString
|
||||
* @memberof SysMsg.SystemMessageHeader
|
||||
* @instance
|
||||
*/
|
||||
SystemMessageHeader.prototype.peerString = "";
|
||||
SystemMessageHeader.prototype.peerUin = 0;
|
||||
|
||||
/**
|
||||
* SystemMessageHeader uin.
|
||||
@@ -231,11 +222,7 @@ export const SysMsg = $root.SysMsg = (() => {
|
||||
let tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
message.peerNumber = reader.uint32();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
message.peerString = reader.string();
|
||||
message.peerUin = reader.uint32();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
@@ -327,19 +314,19 @@ export const SysMsg = $root.SysMsg = (() => {
|
||||
|
||||
/**
|
||||
* SystemMessageMsgSpec subType.
|
||||
* @member {number} subType
|
||||
* @member {number|null|undefined} subType
|
||||
* @memberof SysMsg.SystemMessageMsgSpec
|
||||
* @instance
|
||||
*/
|
||||
SystemMessageMsgSpec.prototype.subType = 0;
|
||||
SystemMessageMsgSpec.prototype.subType = null;
|
||||
|
||||
/**
|
||||
* SystemMessageMsgSpec subSubType.
|
||||
* @member {number} subSubType
|
||||
* @member {number|null|undefined} subSubType
|
||||
* @memberof SysMsg.SystemMessageMsgSpec
|
||||
* @instance
|
||||
*/
|
||||
SystemMessageMsgSpec.prototype.subSubType = 0;
|
||||
SystemMessageMsgSpec.prototype.subSubType = null;
|
||||
|
||||
/**
|
||||
* SystemMessageMsgSpec msgSeq.
|
||||
@@ -359,11 +346,32 @@ export const SysMsg = $root.SysMsg = (() => {
|
||||
|
||||
/**
|
||||
* SystemMessageMsgSpec other.
|
||||
* @member {number} other
|
||||
* @member {number|null|undefined} other
|
||||
* @memberof SysMsg.SystemMessageMsgSpec
|
||||
* @instance
|
||||
*/
|
||||
SystemMessageMsgSpec.prototype.other = 0;
|
||||
SystemMessageMsgSpec.prototype.other = null;
|
||||
|
||||
// OneOf field names bound to virtual getters and setters
|
||||
let $oneOfFields;
|
||||
|
||||
// Virtual OneOf for proto3 optional field
|
||||
Object.defineProperty(SystemMessageMsgSpec.prototype, "_subType", {
|
||||
get: $util.oneOfGetter($oneOfFields = ["subType"]),
|
||||
set: $util.oneOfSetter($oneOfFields)
|
||||
});
|
||||
|
||||
// Virtual OneOf for proto3 optional field
|
||||
Object.defineProperty(SystemMessageMsgSpec.prototype, "_subSubType", {
|
||||
get: $util.oneOfGetter($oneOfFields = ["subSubType"]),
|
||||
set: $util.oneOfSetter($oneOfFields)
|
||||
});
|
||||
|
||||
// Virtual OneOf for proto3 optional field
|
||||
Object.defineProperty(SystemMessageMsgSpec.prototype, "_other", {
|
||||
get: $util.oneOfGetter($oneOfFields = ["other"]),
|
||||
set: $util.oneOfSetter($oneOfFields)
|
||||
});
|
||||
|
||||
/**
|
||||
* Decodes a SystemMessageMsgSpec message from the specified reader or buffer.
|
||||
@@ -1007,6 +1015,141 @@ export const SysMsg = $root.SysMsg = (() => {
|
||||
return ProfileLikeTip;
|
||||
})();
|
||||
|
||||
SysMsg.GroupMemberIncrease = (function() {
|
||||
|
||||
/**
|
||||
* Properties of a GroupMemberIncrease.
|
||||
* @memberof SysMsg
|
||||
* @interface IGroupMemberIncrease
|
||||
* @property {number|null} [groupCode] GroupMemberIncrease groupCode
|
||||
* @property {string|null} [memberUid] GroupMemberIncrease memberUid
|
||||
* @property {number|null} [type] GroupMemberIncrease type
|
||||
* @property {string|null} [adminUid] GroupMemberIncrease adminUid
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a new GroupMemberIncrease.
|
||||
* @memberof SysMsg
|
||||
* @classdesc Represents a GroupMemberIncrease.
|
||||
* @implements IGroupMemberIncrease
|
||||
* @constructor
|
||||
* @param {SysMsg.IGroupMemberIncrease=} [properties] Properties to set
|
||||
*/
|
||||
function GroupMemberIncrease(properties) {
|
||||
if (properties)
|
||||
for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
|
||||
if (properties[keys[i]] != null)
|
||||
this[keys[i]] = properties[keys[i]];
|
||||
}
|
||||
|
||||
/**
|
||||
* GroupMemberIncrease groupCode.
|
||||
* @member {number} groupCode
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @instance
|
||||
*/
|
||||
GroupMemberIncrease.prototype.groupCode = 0;
|
||||
|
||||
/**
|
||||
* GroupMemberIncrease memberUid.
|
||||
* @member {string} memberUid
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @instance
|
||||
*/
|
||||
GroupMemberIncrease.prototype.memberUid = "";
|
||||
|
||||
/**
|
||||
* GroupMemberIncrease type.
|
||||
* @member {number} type
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @instance
|
||||
*/
|
||||
GroupMemberIncrease.prototype.type = 0;
|
||||
|
||||
/**
|
||||
* GroupMemberIncrease adminUid.
|
||||
* @member {string} adminUid
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @instance
|
||||
*/
|
||||
GroupMemberIncrease.prototype.adminUid = "";
|
||||
|
||||
/**
|
||||
* Decodes a GroupMemberIncrease message from the specified reader or buffer.
|
||||
* @function decode
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @static
|
||||
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
|
||||
* @param {number} [length] Message length if known beforehand
|
||||
* @returns {SysMsg.GroupMemberIncrease} GroupMemberIncrease
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
GroupMemberIncrease.decode = function decode(reader, length) {
|
||||
if (!(reader instanceof $Reader))
|
||||
reader = $Reader.create(reader);
|
||||
let end = length === undefined ? reader.len : reader.pos + length, message = new $root.SysMsg.GroupMemberIncrease();
|
||||
while (reader.pos < end) {
|
||||
let tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
message.groupCode = reader.uint32();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
message.memberUid = reader.string();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
message.type = reader.uint32();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
message.adminUid = reader.string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a GroupMemberIncrease message from the specified reader or buffer, length delimited.
|
||||
* @function decodeDelimited
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @static
|
||||
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
|
||||
* @returns {SysMsg.GroupMemberIncrease} GroupMemberIncrease
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
GroupMemberIncrease.decodeDelimited = function decodeDelimited(reader) {
|
||||
if (!(reader instanceof $Reader))
|
||||
reader = new $Reader(reader);
|
||||
return this.decode(reader, reader.uint32());
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the default type url for GroupMemberIncrease
|
||||
* @function getTypeUrl
|
||||
* @memberof SysMsg.GroupMemberIncrease
|
||||
* @static
|
||||
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
|
||||
* @returns {string} The default type url
|
||||
*/
|
||||
GroupMemberIncrease.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
|
||||
if (typeUrlPrefix === undefined) {
|
||||
typeUrlPrefix = "type.googleapis.com";
|
||||
}
|
||||
return typeUrlPrefix + "/SysMsg.GroupMemberIncrease";
|
||||
};
|
||||
|
||||
return GroupMemberIncrease;
|
||||
})();
|
||||
|
||||
return SysMsg;
|
||||
})();
|
||||
|
||||
|
10
src/ntqqapi/proto/groupMemberIncrease.proto
Normal file
10
src/ntqqapi/proto/groupMemberIncrease.proto
Normal file
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
package SysMsg;
|
||||
|
||||
// GroupChange?
|
||||
message GroupMemberIncrease {
|
||||
uint32 groupCode = 1;
|
||||
string memberUid = 3;
|
||||
uint32 type = 4; // 130:主动 131:被邀请
|
||||
string adminUid = 5;
|
||||
}
|
@@ -8,23 +8,22 @@ message SystemMessage {
|
||||
}
|
||||
|
||||
message SystemMessageHeader {
|
||||
uint32 peerNumber = 1;
|
||||
string peerString = 2;
|
||||
uint32 peerUin = 1;
|
||||
//string peerUid = 2;
|
||||
uint32 uin = 5;
|
||||
optional string uid = 6;
|
||||
}
|
||||
|
||||
message SystemMessageMsgSpec {
|
||||
uint32 msgType = 1;
|
||||
uint32 subType = 2;
|
||||
uint32 subSubType = 3;
|
||||
optional uint32 subType = 2;
|
||||
optional uint32 subSubType = 3;
|
||||
uint32 msgSeq = 5;
|
||||
uint32 time = 6;
|
||||
//uint64 msgId = 12;
|
||||
uint32 other = 13;
|
||||
optional uint32 other = 13;
|
||||
}
|
||||
|
||||
message SystemMessageBodyWrapper {
|
||||
bytes body = 2;
|
||||
// Find the first [08], or ignore the first 7 bytes?
|
||||
}
|
||||
}
|
||||
|
@@ -438,6 +438,10 @@ export interface RawMessage {
|
||||
likesCnt: string
|
||||
isClicked: boolean
|
||||
}[]
|
||||
msgAttrs: Map<number, {
|
||||
attrType: number
|
||||
attrId: string
|
||||
}>
|
||||
}
|
||||
|
||||
export interface Peer {
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -2,33 +2,35 @@ import { BaseAction, Schema } from '../BaseAction'
|
||||
import { OB11GroupMember } from '../../types'
|
||||
import { OB11Entities } from '../../entities'
|
||||
import { ActionName } from '../types'
|
||||
import { calcQQLevel } from '@/common/utils/misc'
|
||||
import { calcQQLevel, parseBool } from '@/common/utils/misc'
|
||||
|
||||
interface Payload {
|
||||
group_id: number | string
|
||||
user_id: number | string
|
||||
no_cache: boolean
|
||||
}
|
||||
|
||||
class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||
actionName = ActionName.GetGroupMemberInfo
|
||||
payloadSchema = Schema.object({
|
||||
group_id: Schema.union([Number, String]).required(),
|
||||
user_id: Schema.union([Number, String]).required()
|
||||
user_id: Schema.union([Number, String]).required(),
|
||||
no_cache: Schema.union([Boolean, Schema.transform(String, parseBool)]).default(false)
|
||||
})
|
||||
|
||||
protected async _handle(payload: Payload) {
|
||||
const groupCode = payload.group_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)
|
||||
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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import { llonebotError } from '../common/globalVars'
|
||||
import { OB11GroupAdminNoticeEvent } from './event/notice/OB11GroupAdminNoticeEvent'
|
||||
import { OB11ProfileLikeEvent } from './event/notice/OB11ProfileLikeEvent'
|
||||
import { SysMsg } from '@/ntqqapi/proto/compiled'
|
||||
import { OB11GroupIncreaseEvent } from './event/notice/OB11GroupIncreaseEvent'
|
||||
|
||||
declare module 'cordis' {
|
||||
interface Context {
|
||||
@@ -349,7 +350,7 @@ class OneBot11Adapter extends Service {
|
||||
this.ctx.on('nt/friend-request', input => {
|
||||
this.handleFriendRequest(input)
|
||||
})
|
||||
this.ctx.on('nt/system-message-created', input => {
|
||||
this.ctx.on('nt/system-message-created', async input => {
|
||||
const sysMsg = SysMsg.SystemMessage.decode(input)
|
||||
const { msgType, subType, subSubType } = sysMsg.msgSpec[0] ?? {}
|
||||
if (msgType === 528 && subType === 39 && subSubType === 39) {
|
||||
@@ -358,8 +359,16 @@ class OneBot11Adapter extends Service {
|
||||
const detail = tip.content?.msg?.detail
|
||||
if (!detail) return
|
||||
const [times] = detail.txt?.match(/\d+/) ?? ['0']
|
||||
const profileLikeEvent = new OB11ProfileLikeEvent(detail.uin!, detail.nickname!, +times)
|
||||
this.dispatch(profileLikeEvent)
|
||||
const event = new OB11ProfileLikeEvent(detail.uin!, detail.nickname!, +times)
|
||||
this.dispatch(event)
|
||||
} else if (msgType === 33) {
|
||||
const tip = SysMsg.GroupMemberIncrease.decode(sysMsg.bodyWrapper!.body!)
|
||||
if (tip.type !== 130) return
|
||||
this.ctx.logger.info('群成员增加', tip)
|
||||
const memberUin = await this.ctx.ntUserApi.getUinByUid(tip.memberUid)
|
||||
const operatorUin = await this.ctx.ntUserApi.getUinByUid(tip.adminUid)
|
||||
const event = new OB11GroupIncreaseEvent(tip.groupCode, +memberUin, +operatorUin)
|
||||
this.dispatch(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -134,11 +134,13 @@ namespace OB11Http {
|
||||
|
||||
class OB11HttpPost {
|
||||
private disposeInterval?: () => void
|
||||
private activated = false
|
||||
|
||||
constructor(protected ctx: Context, public config: OB11HttpPost.Config) {
|
||||
}
|
||||
|
||||
public start() {
|
||||
this.activated = true
|
||||
if (this.config.enableHttpHeart && !this.disposeInterval) {
|
||||
this.disposeInterval = this.ctx.setInterval(() => {
|
||||
// ws的心跳是ws自己维护的
|
||||
@@ -148,10 +150,14 @@ class OB11HttpPost {
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.activated = false
|
||||
this.disposeInterval?.()
|
||||
}
|
||||
|
||||
public async emitEvent(event: OB11BaseEvent | OB11Message) {
|
||||
if (!this.activated || !this.config.hosts.length) {
|
||||
return
|
||||
}
|
||||
const msgStr = JSON.stringify(event)
|
||||
const headers: Dict = {
|
||||
'Content-Type': 'application/json',
|
||||
|
@@ -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,
|
||||
|
@@ -11,8 +11,6 @@ export interface OB11User {
|
||||
age?: number
|
||||
qid?: string
|
||||
login_days?: number
|
||||
categroyName?: string
|
||||
categoryId?: number
|
||||
}
|
||||
|
||||
export enum OB11UserSex {
|
||||
|
@@ -7,11 +7,8 @@ import {
|
||||
CHANNEL_SELECT_FILE,
|
||||
CHANNEL_SET_CONFIG,
|
||||
CHANNEL_UPDATE,
|
||||
CHANNEL_SET_CONFIG_CONFIRMED,
|
||||
} from './common/channels'
|
||||
|
||||
const { contextBridge } = require('electron')
|
||||
const { ipcRenderer } = require('electron')
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
|
||||
const llonebot = {
|
||||
log: (data: unknown) => {
|
||||
@@ -24,8 +21,7 @@ const llonebot = {
|
||||
return ipcRenderer.invoke(CHANNEL_UPDATE)
|
||||
},
|
||||
setConfig: async (ask: boolean, config: Config) => {
|
||||
const isSuccess = await ipcRenderer.invoke(CHANNEL_SET_CONFIG, ask, config)
|
||||
if (isSuccess) ipcRenderer.send(CHANNEL_SET_CONFIG_CONFIRMED, config)
|
||||
return ipcRenderer.invoke(CHANNEL_SET_CONFIG, ask, config)
|
||||
},
|
||||
getConfig: async (): Promise<Config> => {
|
||||
return ipcRenderer.invoke(CHANNEL_GET_CONFIG)
|
||||
|
@@ -25,9 +25,6 @@ async function onSettingWindowCreated(view: Element) {
|
||||
} else {
|
||||
Object.assign(config, { [key]: value })
|
||||
}
|
||||
if (!['heartInterval', 'token', 'ffmpeg'].includes(key)) {
|
||||
window.llonebot.setConfig(false, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +175,7 @@ async function onSettingWindowCreated(view: Element) {
|
||||
'调用 /get_image、/get_record、/get_file 时,没有 url 时添加 Base64 字段',
|
||||
SettingSwitch('enableLocalFile2Url', config.enableLocalFile2Url),
|
||||
),
|
||||
SettingItem('', null, SettingButton('保存', 'config-ob11-save-2', 'primary')),
|
||||
]),
|
||||
SettingList([
|
||||
SettingItem(
|
||||
@@ -217,6 +215,7 @@ async function onSettingWindowCreated(view: Element) {
|
||||
'单位为秒,可用于获取撤回的消息',
|
||||
`<div class="q-input"><input class="q-input__inner" data-config-key="msgCacheExpire" type="number" min="1" value="${config.msgCacheExpire}" placeholder="${config.msgCacheExpire}" /></div>`,
|
||||
),
|
||||
SettingItem('', null, SettingButton('保存', 'config-ob11-save-3', 'primary')),
|
||||
]),
|
||||
SettingList([
|
||||
SettingItem('GitHub 仓库', `https://github.com/LLOneBot/LLOneBot`, SettingButton('点个星星', 'open-github')),
|
||||
@@ -395,6 +394,22 @@ async function onSettingWindowCreated(view: Element) {
|
||||
alert('保存成功')
|
||||
})
|
||||
|
||||
doc.querySelector('#config-ob11-save-2')?.addEventListener('click', () => {
|
||||
config.ob11 = ob11Config
|
||||
|
||||
window.llonebot.setConfig(false, config)
|
||||
showError().then()
|
||||
alert('保存成功')
|
||||
})
|
||||
|
||||
doc.querySelector('#config-ob11-save-3')?.addEventListener('click', () => {
|
||||
config.ob11 = ob11Config
|
||||
|
||||
window.llonebot.setConfig(false, config)
|
||||
showError().then()
|
||||
alert('保存成功')
|
||||
})
|
||||
|
||||
doc.body.childNodes.forEach((node) => {
|
||||
view.appendChild(node)
|
||||
})
|
||||
|
@@ -99,6 +99,15 @@ export class SatoriServer {
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
if (this.wsClients.length > 0) {
|
||||
for (const socket of this.wsClients) {
|
||||
try {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.close(1000)
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
if (this.wsServer) {
|
||||
const close = promisify(this.wsServer.close)
|
||||
await close.call(this.wsServer)
|
||||
|
@@ -1 +1 @@
|
||||
export const version = '4.0.9'
|
||||
export const version = '4.0.12'
|
||||
|
Reference in New Issue
Block a user