Compare commits

...

18 Commits

Author SHA1 Message Date
idranme
64c5eb6c04 Merge pull request #422 from LLOneBot/dev
release: 3.32.7
2024-09-16 20:48:15 +08:00
idranme
e5750786cb chore: v3.32.7 2024-09-16 20:46:14 +08:00
idranme
18cb46ade5 fix 2024-09-16 20:43:18 +08:00
idranme
e39c89a441 fix 2024-09-16 19:01:59 +08:00
idranme
476d498e44 Merge pull request #417 from LLOneBot/dev
release: 3.32.6
2024-09-15 17:48:35 +08:00
idranme
55446538de chore: v3.32.6 2024-09-15 17:43:10 +08:00
idranme
b965f50653 fix: friend_add event 2024-09-15 16:14:36 +08:00
idranme
2d354c5eda optimize 2024-09-15 14:08:02 +08:00
idranme
536999f296 feat: support for sending contact message segment 2024-09-14 20:13:45 +08:00
idranme
cad09b2ed1 fix 2024-09-14 19:56:46 +08:00
idranme
6be0c11ca2 refactor 2024-09-13 22:58:21 +08:00
idranme
b03bcf9a7c Merge pull request #415 from LLOneBot/dev
release: 3.32.5
2024-09-13 18:59:37 +08:00
idranme
d4f9629af2 chore: v3.32.5 2024-09-13 18:54:56 +08:00
idranme
6d47e2ee80 chore 2024-09-13 18:52:45 +08:00
idranme
506bddb21a chore: style 2024-09-13 17:30:56 +08:00
idranme
91c689baf8 fix 2024-09-13 14:56:30 +08:00
idranme
b7938aaab8 refactor 2024-09-12 22:39:14 +08:00
idranme
b1a892cf4e chore 2024-09-12 18:29:18 +08:00
95 changed files with 570 additions and 545 deletions

View File

@@ -7,9 +7,6 @@ body:
attributes: attributes:
value: | value: |
欢迎来到 LLOneBot 的 Issue Tracker请填写以下表格来提交 Bug。 欢迎来到 LLOneBot 的 Issue Tracker请填写以下表格来提交 Bug。
在提交新的 Bug 反馈前,请确保您:
* 已经搜索了现有的 issues并且没有找到可以解决您问题的方法
* 不与现有的某一 issue 重复
- type: input - type: input
id: system-version id: system-version
attributes: attributes:
@@ -40,8 +37,6 @@ body:
label: OneBot 客户端 label: OneBot 客户端
description: 连接至 LLOneBot 的客户端版本信息 description: 连接至 LLOneBot 的客户端版本信息
placeholder: Overflow 2.16.0-2cf7991-SNAPSHOT placeholder: Overflow 2.16.0-2cf7991-SNAPSHOT
validations:
required: true
- type: textarea - type: textarea
id: what-happened id: what-happened
attributes: attributes:

View File

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

View File

@@ -12,7 +12,7 @@
"deploy-win": "cmd /c \"xcopy /C /S /Y dist\\* %LITELOADERQQNT_PROFILE%\\plugins\\LLOneBot\\\"", "deploy-win": "cmd /c \"xcopy /C /S /Y dist\\* %LITELOADERQQNT_PROFILE%\\plugins\\LLOneBot\\\"",
"format": "prettier -cw .", "format": "prettier -cw .",
"check": "tsc", "check": "tsc",
"compile:proto": "pbjs --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 && pbts -o src/ntqqapi/proto/compiled.d.ts src/ntqqapi/proto/compiled.js"
}, },
"author": "", "author": "",
"license": "MIT", "license": "MIT",
@@ -41,7 +41,7 @@
"electron-vite": "^2.3.0", "electron-vite": "^2.3.0",
"protobufjs-cli": "^1.1.3", "protobufjs-cli": "^1.1.3",
"typescript": "^5.6.2", "typescript": "^5.6.2",
"vite": "^5.4.4", "vite": "^5.4.5",
"vite-plugin-cp": "^4.0.8" "vite-plugin-cp": "^4.0.8"
}, },
"packageManager": "yarn@4.4.1" "packageManager": "yarn@4.4.1"

View File

@@ -33,7 +33,6 @@ export class ConfigUtil {
enableWsReverse: false, enableWsReverse: false,
messagePostFormat: 'array', messagePostFormat: 'array',
enableHttpHeart: false, enableHttpHeart: false,
enableQOAutoQuote: false,
listenLocalhost: false listenLocalhost: false
} }
const defaultConfig: Config = { const defaultConfig: Config = {

View File

@@ -10,7 +10,11 @@ export interface OB11Config {
enableWsReverse?: boolean enableWsReverse?: boolean
messagePostFormat?: 'array' | 'string' messagePostFormat?: 'array' | 'string'
enableHttpHeart?: boolean enableHttpHeart?: boolean
enableQOAutoQuote: boolean // 快速操作回复自动引用原消息 /**
* 快速操作回复自动引用原消息
* @deprecated
*/
enableQOAutoQuote?: boolean
listenLocalhost: boolean listenLocalhost: boolean
} }

View File

@@ -101,7 +101,7 @@ export async function encodeSilk(ctx: Context, filePath: string) {
type OutFormat = 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac' type OutFormat = 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac'
export async function decodeSilk(ctx: Context, inputFilePath: string, outFormat: OutFormat = 'mp3') { export async function decodeSilk(ctx: Context, inputFilePath: string, outFormat: OutFormat) {
const silk = await fsPromise.readFile(inputFilePath) const silk = await fsPromise.readFile(inputFilePath)
const { data } = await decode(silk, 24000) const { data } = await decode(silk, 24000)
const tmpPath = path.join(TEMP_DIR, path.basename(inputFilePath)) const tmpPath = path.join(TEMP_DIR, path.basename(inputFilePath))

View File

@@ -1,5 +1,5 @@
import { QQLevel } from '@/ntqqapi/types' import { QQLevel } from '@/ntqqapi/types'
import { Dict } from 'cosmokit' import { Dict, isNullable } from 'cosmokit'
export function isNumeric(str: string) { export function isNumeric(str: string) {
return /^\d+$/.test(str) return /^\d+$/.test(str)
@@ -33,3 +33,7 @@ export function mergeNewProperties(newObj: Dict, oldObj: Dict) {
} }
}) })
} }
export function filterNullable<T>(array: T[]) {
return array.filter(e => !isNullable(e)) as NonNullable<T>[]
}

View File

@@ -23,8 +23,7 @@ export default class Log {
return return
} }
const dateTime = new Date(record.timestamp).toLocaleString() const dateTime = new Date(record.timestamp).toLocaleString()
const userInfo = selfInfo.uin ? `${selfInfo.nick}(${selfInfo.uin})` : '' const content = `${dateTime} [${record.type}] ${selfInfo.nick}(${selfInfo.uin}) | ${record.name} ${record.content}\n\n`
const content = `${dateTime} [${record.type}] ${userInfo} | ${record.name} ${record.content}\n\n`
appendFile(file, content, noop) appendFile(file, content, noop)
}, },
} }

View File

@@ -189,4 +189,9 @@ export class NTQQFriendApi extends Service {
return await invoke('nodeIKernelBuddyService/isBuddy', [{ uid }, null]) return await invoke('nodeIKernelBuddyService/isBuddy', [{ uid }, null])
} }
} }
async getBuddyRecommendContact(uin: string) {
const ret = await invoke('nodeIKernelBuddyService/getBuddyRecommendContactArkJson', [{ uin }, null])
return ret.arkMsg
}
} }

View File

@@ -117,17 +117,15 @@ export class NTQQGroupApi extends Service {
const type = parseInt(flagitem[2]) const type = parseInt(flagitem[2])
const session = getSession() const session = getSession()
if (session) { if (session) {
return session.getGroupService().operateSysNotify( return session.getGroupService().operateSysNotify(false, {
false, operateType, // 2 拒绝
{ targetMsg: {
'operateType': operateType, // 2 拒绝 seq, // 通知序列号
'targetMsg': { type,
'seq': seq, // 通知序列号 groupCode,
'type': type, postscript: reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
'groupCode': groupCode, }
'postscript': reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格 })
}
})
} else { } else {
return await invoke(NTMethod.HANDLE_GROUP_REQUEST, [{ return await invoke(NTMethod.HANDLE_GROUP_REQUEST, [{
doubt: false, doubt: false,
@@ -265,8 +263,8 @@ export class NTQQGroupApi extends Service {
return await invoke('nodeIKernelRichMediaService/deleteGroupFolder', [{ groupId, folderId }, null]) return await invoke('nodeIKernelRichMediaService/deleteGroupFolder', [{ groupId, folderId }, null])
} }
async deleteGroupFile(groupId: string, fileIdList: string[]) { async deleteGroupFile(groupId: string, fileIdList: string[], busIdList: number[]) {
return await invoke('nodeIKernelRichMediaService/deleteGroupFile', [{ groupId, busIdList: [102], fileIdList }, null]) return await invoke('nodeIKernelRichMediaService/deleteGroupFile', [{ groupId, busIdList, fileIdList }, null])
} }
async getGroupFileList(groupId: string, fileListForm: GetFileListParam) { async getGroupFileList(groupId: string, fileListForm: GetFileListParam) {
@@ -300,4 +298,9 @@ export class NTQQGroupApi extends Service {
const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')! const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!
return await invoke('nodeIKernelGroupService/uploadGroupBulletinPic', [{ groupCode, psKey, path }, null]) return await invoke('nodeIKernelGroupService/uploadGroupBulletinPic', [{ groupCode, psKey, path }, null])
} }
async getGroupRecommendContact(groupCode: string) {
const ret = await invoke('nodeIKernelGroupService/getGroupRecommendContactArkJson', [{ groupCode }, null])
return ret.arkJson
}
} }

View File

@@ -183,7 +183,7 @@ export class NTQQMsgApi extends Service {
if (!arkElement) { if (!arkElement) {
continue continue
} }
const forwardData = JSON.parse(arkElement.arkElement.bytesData) const forwardData = JSON.parse(arkElement.arkElement!.bytesData)
if (forwardData.app != 'com.tencent.multimsg') { if (forwardData.app != 'com.tencent.multimsg') {
continue continue
} }

View File

@@ -87,17 +87,6 @@ export class NTQQUserApi extends Service {
return result.info return result.info
} }
async getSkey(): Promise<string> {
const clientKeyData = await this.forceFetchClientKey()
if (clientKeyData?.result !== 0) {
throw new Error('获取clientKey失败')
}
const url = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin
+ '&clientkey=' + clientKeyData.clientKey
+ '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=' + clientKeyData.keyIndex
return (await RequestUtil.HttpsGetCookies(url))?.skey
}
async getCookies(domain: string) { async getCookies(domain: string) {
const clientKeyData = await this.forceFetchClientKey() const clientKeyData = await this.forceFetchClientKey()
if (clientKeyData?.result !== 0) { if (clientKeyData?.result !== 0) {

View File

@@ -27,8 +27,8 @@ declare module 'cordis' {
} }
interface Events { interface Events {
'nt/message-created': (input: RawMessage[]) => void 'nt/message-created': (input: RawMessage[]) => void
'nt/message-deleted': (input: RawMessage[]) => void 'nt/message-deleted': (input: RawMessage) => void
'nt/message-sent': (input: RawMessage[]) => void 'nt/message-sent': (input: RawMessage) => void
'nt/group-notify': (input: GroupNotify[]) => void 'nt/group-notify': (input: GroupNotify[]) => void
'nt/friend-request': (input: FriendRequest[]) => void 'nt/friend-request': (input: FriendRequest[]) => void
'nt/group-member-info-updated': (input: { groupCode: string, members: GroupMember[] }) => void 'nt/group-member-info-updated': (input: { groupCode: string, members: GroupMember[] }) => void
@@ -96,7 +96,7 @@ class Core extends Service {
} }
for (const path of pathList) { for (const path of pathList) {
if (path) { if (path) {
fs.unlink(picPath, () => { fs.unlink(path, () => {
this.ctx.logger.info('删除文件成功', path) this.ctx.logger.info('删除文件成功', path)
}) })
} }
@@ -174,23 +174,26 @@ class Core extends Service {
this.ctx.parallel('nt/message-created', payload.msgList) this.ctx.parallel('nt/message-created', payload.msgList)
}) })
const sentMsgIds = new Map<string, boolean>()
const recallMsgIds: string[] = [] // 避免重复上报 const recallMsgIds: string[] = [] // 避免重复上报
registerReceiveHook<{ msgList: RawMessage[] }>([ReceiveCmdS.UPDATE_MSG], payload => { registerReceiveHook<{ msgList: RawMessage[] }>([ReceiveCmdS.UPDATE_MSG], payload => {
const list = payload.msgList.filter(v => { for (const msg of payload.msgList) {
if (recallMsgIds.includes(v.msgId)) { if (msg.recallTime !== '0' && !recallMsgIds.includes(msg.msgId)) {
return false recallMsgIds.push(msg.msgId)
this.ctx.parallel('nt/message-deleted', msg)
} else if (sentMsgIds.get(msg.msgId)) {
sentMsgIds.delete(msg.msgId)
this.ctx.parallel('nt/message-sent', msg)
} }
recallMsgIds.push(v.msgId) }
return true
})
this.ctx.parallel('nt/message-deleted', list)
}) })
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, payload => { registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, payload => {
if (!this.config.reportSelfMessage) { if (!this.config.reportSelfMessage) {
return return
} }
this.ctx.parallel('nt/message-sent', [payload.msgRecord]) sentMsgIds.set(payload.msgRecord.msgId, true)
}) })
const groupNotifyFlags: string[] = [] const groupNotifyFlags: string[] = []

View File

@@ -34,13 +34,6 @@ export namespace SysMsg {
/** SystemMessage bodyWrapper. */ /** SystemMessage bodyWrapper. */
public bodyWrapper?: (SysMsg.ISystemMessageBodyWrapper|null); public bodyWrapper?: (SysMsg.ISystemMessageBodyWrapper|null);
/**
* Creates a new SystemMessage instance using the specified properties.
* @param [properties] Properties to set
* @returns SystemMessage instance
*/
public static create(properties?: SysMsg.ISystemMessage): SysMsg.SystemMessage;
/** /**
* Decodes a SystemMessage message from the specified reader or buffer. * Decodes a SystemMessage message from the specified reader or buffer.
* @param reader Reader or buffer to decode from * @param reader Reader or buffer to decode from
@@ -105,13 +98,6 @@ export namespace SysMsg {
/** SystemMessageHeader uid. */ /** SystemMessageHeader uid. */
public uid?: (string|null); public uid?: (string|null);
/**
* Creates a new SystemMessageHeader instance using the specified properties.
* @param [properties] Properties to set
* @returns SystemMessageHeader instance
*/
public static create(properties?: SysMsg.ISystemMessageHeader): SysMsg.SystemMessageHeader;
/** /**
* Decodes a SystemMessageHeader message from the specified reader or buffer. * Decodes a SystemMessageHeader message from the specified reader or buffer.
* @param reader Reader or buffer to decode from * @param reader Reader or buffer to decode from
@@ -188,13 +174,6 @@ export namespace SysMsg {
/** SystemMessageMsgSpec other. */ /** SystemMessageMsgSpec other. */
public other: number; public other: number;
/**
* Creates a new SystemMessageMsgSpec instance using the specified properties.
* @param [properties] Properties to set
* @returns SystemMessageMsgSpec instance
*/
public static create(properties?: SysMsg.ISystemMessageMsgSpec): SysMsg.SystemMessageMsgSpec;
/** /**
* Decodes a SystemMessageMsgSpec message from the specified reader or buffer. * Decodes a SystemMessageMsgSpec message from the specified reader or buffer.
* @param reader Reader or buffer to decode from * @param reader Reader or buffer to decode from
@@ -241,13 +220,6 @@ export namespace SysMsg {
/** SystemMessageBodyWrapper body. */ /** SystemMessageBodyWrapper body. */
public body: Uint8Array; public body: Uint8Array;
/**
* Creates a new SystemMessageBodyWrapper instance using the specified properties.
* @param [properties] Properties to set
* @returns SystemMessageBodyWrapper instance
*/
public static create(properties?: SysMsg.ISystemMessageBodyWrapper): SysMsg.SystemMessageBodyWrapper;
/** /**
* Decodes a SystemMessageBodyWrapper message from the specified reader or buffer. * Decodes a SystemMessageBodyWrapper message from the specified reader or buffer.
* @param reader Reader or buffer to decode from * @param reader Reader or buffer to decode from
@@ -306,13 +278,6 @@ export namespace SysMsg {
/** LikeDetail nickname. */ /** LikeDetail nickname. */
public nickname: string; public nickname: string;
/**
* Creates a new LikeDetail instance using the specified properties.
* @param [properties] Properties to set
* @returns LikeDetail instance
*/
public static create(properties?: SysMsg.ILikeDetail): SysMsg.LikeDetail;
/** /**
* Decodes a LikeDetail message from the specified reader or buffer. * Decodes a LikeDetail message from the specified reader or buffer.
* @param reader Reader or buffer to decode from * @param reader Reader or buffer to decode from
@@ -371,13 +336,6 @@ export namespace SysMsg {
/** LikeMsg detail. */ /** LikeMsg detail. */
public detail?: (SysMsg.ILikeDetail|null); public detail?: (SysMsg.ILikeDetail|null);
/**
* Creates a new LikeMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns LikeMsg instance
*/
public static create(properties?: SysMsg.ILikeMsg): SysMsg.LikeMsg;
/** /**
* Decodes a LikeMsg message from the specified reader or buffer. * Decodes a LikeMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from * @param reader Reader or buffer to decode from
@@ -405,11 +363,63 @@ export namespace SysMsg {
public static getTypeUrl(typeUrlPrefix?: string): string; public static getTypeUrl(typeUrlPrefix?: string): string;
} }
/** Properties of a ProfileLikeSubTip. */
interface IProfileLikeSubTip {
/** ProfileLikeSubTip msg */
msg?: (SysMsg.ILikeMsg|null);
}
/** Represents a ProfileLikeSubTip. */
class ProfileLikeSubTip implements IProfileLikeSubTip {
/**
* Constructs a new ProfileLikeSubTip.
* @param [properties] Properties to set
*/
constructor(properties?: SysMsg.IProfileLikeSubTip);
/** ProfileLikeSubTip msg. */
public msg?: (SysMsg.ILikeMsg|null);
/**
* Decodes a ProfileLikeSubTip message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns ProfileLikeSubTip
* @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.ProfileLikeSubTip;
/**
* Decodes a ProfileLikeSubTip message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns ProfileLikeSubTip
* @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.ProfileLikeSubTip;
/**
* Gets the default type url for ProfileLikeSubTip
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns The default type url
*/
public static getTypeUrl(typeUrlPrefix?: string): string;
}
/** Properties of a ProfileLikeTip. */ /** Properties of a ProfileLikeTip. */
interface IProfileLikeTip { interface IProfileLikeTip {
/** ProfileLikeTip msg */ /** ProfileLikeTip msgType */
msg?: (SysMsg.ILikeMsg|null); msgType?: (number|null);
/** ProfileLikeTip subType */
subType?: (number|null);
/** ProfileLikeTip content */
content?: (SysMsg.IProfileLikeSubTip|null);
} }
/** Represents a ProfileLikeTip. */ /** Represents a ProfileLikeTip. */
@@ -421,15 +431,14 @@ export namespace SysMsg {
*/ */
constructor(properties?: SysMsg.IProfileLikeTip); constructor(properties?: SysMsg.IProfileLikeTip);
/** ProfileLikeTip msg. */ /** ProfileLikeTip msgType. */
public msg?: (SysMsg.ILikeMsg|null); public msgType: number;
/** /** ProfileLikeTip subType. */
* Creates a new ProfileLikeTip instance using the specified properties. public subType: number;
* @param [properties] Properties to set
* @returns ProfileLikeTip instance /** ProfileLikeTip content. */
*/ public content?: (SysMsg.IProfileLikeSubTip|null);
public static create(properties?: SysMsg.IProfileLikeTip): SysMsg.ProfileLikeTip;
/** /**
* Decodes a ProfileLikeTip message from the specified reader or buffer. * Decodes a ProfileLikeTip message from the specified reader or buffer.

View File

@@ -68,18 +68,6 @@ export const SysMsg = $root.SysMsg = (() => {
*/ */
SystemMessage.prototype.bodyWrapper = null; SystemMessage.prototype.bodyWrapper = null;
/**
* Creates a new SystemMessage instance using the specified properties.
* @function create
* @memberof SysMsg.SystemMessage
* @static
* @param {SysMsg.ISystemMessage=} [properties] Properties to set
* @returns {SysMsg.SystemMessage} SystemMessage instance
*/
SystemMessage.create = function create(properties) {
return new SystemMessage(properties);
};
/** /**
* Decodes a SystemMessage message from the specified reader or buffer. * Decodes a SystemMessage message from the specified reader or buffer.
* @function decode * @function decode
@@ -224,18 +212,6 @@ export const SysMsg = $root.SysMsg = (() => {
set: $util.oneOfSetter($oneOfFields) set: $util.oneOfSetter($oneOfFields)
}); });
/**
* Creates a new SystemMessageHeader instance using the specified properties.
* @function create
* @memberof SysMsg.SystemMessageHeader
* @static
* @param {SysMsg.ISystemMessageHeader=} [properties] Properties to set
* @returns {SysMsg.SystemMessageHeader} SystemMessageHeader instance
*/
SystemMessageHeader.create = function create(properties) {
return new SystemMessageHeader(properties);
};
/** /**
* Decodes a SystemMessageHeader message from the specified reader or buffer. * Decodes a SystemMessageHeader message from the specified reader or buffer.
* @function decode * @function decode
@@ -389,18 +365,6 @@ export const SysMsg = $root.SysMsg = (() => {
*/ */
SystemMessageMsgSpec.prototype.other = 0; SystemMessageMsgSpec.prototype.other = 0;
/**
* Creates a new SystemMessageMsgSpec instance using the specified properties.
* @function create
* @memberof SysMsg.SystemMessageMsgSpec
* @static
* @param {SysMsg.ISystemMessageMsgSpec=} [properties] Properties to set
* @returns {SysMsg.SystemMessageMsgSpec} SystemMessageMsgSpec instance
*/
SystemMessageMsgSpec.create = function create(properties) {
return new SystemMessageMsgSpec(properties);
};
/** /**
* Decodes a SystemMessageMsgSpec message from the specified reader or buffer. * Decodes a SystemMessageMsgSpec message from the specified reader or buffer.
* @function decode * @function decode
@@ -517,18 +481,6 @@ export const SysMsg = $root.SysMsg = (() => {
*/ */
SystemMessageBodyWrapper.prototype.body = $util.newBuffer([]); SystemMessageBodyWrapper.prototype.body = $util.newBuffer([]);
/**
* Creates a new SystemMessageBodyWrapper instance using the specified properties.
* @function create
* @memberof SysMsg.SystemMessageBodyWrapper
* @static
* @param {SysMsg.ISystemMessageBodyWrapper=} [properties] Properties to set
* @returns {SysMsg.SystemMessageBodyWrapper} SystemMessageBodyWrapper instance
*/
SystemMessageBodyWrapper.create = function create(properties) {
return new SystemMessageBodyWrapper(properties);
};
/** /**
* Decodes a SystemMessageBodyWrapper message from the specified reader or buffer. * Decodes a SystemMessageBodyWrapper message from the specified reader or buffer.
* @function decode * @function decode
@@ -643,18 +595,6 @@ export const SysMsg = $root.SysMsg = (() => {
*/ */
LikeDetail.prototype.nickname = ""; LikeDetail.prototype.nickname = "";
/**
* Creates a new LikeDetail instance using the specified properties.
* @function create
* @memberof SysMsg.LikeDetail
* @static
* @param {SysMsg.ILikeDetail=} [properties] Properties to set
* @returns {SysMsg.LikeDetail} LikeDetail instance
*/
LikeDetail.create = function create(properties) {
return new LikeDetail(properties);
};
/** /**
* Decodes a LikeDetail message from the specified reader or buffer. * Decodes a LikeDetail message from the specified reader or buffer.
* @function decode * @function decode
@@ -777,18 +717,6 @@ export const SysMsg = $root.SysMsg = (() => {
*/ */
LikeMsg.prototype.detail = null; LikeMsg.prototype.detail = null;
/**
* Creates a new LikeMsg instance using the specified properties.
* @function create
* @memberof SysMsg.LikeMsg
* @static
* @param {SysMsg.ILikeMsg=} [properties] Properties to set
* @returns {SysMsg.LikeMsg} LikeMsg instance
*/
LikeMsg.create = function create(properties) {
return new LikeMsg(properties);
};
/** /**
* Decodes a LikeMsg message from the specified reader or buffer. * Decodes a LikeMsg message from the specified reader or buffer.
* @function decode * @function decode
@@ -861,13 +789,111 @@ export const SysMsg = $root.SysMsg = (() => {
return LikeMsg; return LikeMsg;
})(); })();
SysMsg.ProfileLikeSubTip = (function() {
/**
* Properties of a ProfileLikeSubTip.
* @memberof SysMsg
* @interface IProfileLikeSubTip
* @property {SysMsg.ILikeMsg|null} [msg] ProfileLikeSubTip msg
*/
/**
* Constructs a new ProfileLikeSubTip.
* @memberof SysMsg
* @classdesc Represents a ProfileLikeSubTip.
* @implements IProfileLikeSubTip
* @constructor
* @param {SysMsg.IProfileLikeSubTip=} [properties] Properties to set
*/
function ProfileLikeSubTip(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]];
}
/**
* ProfileLikeSubTip msg.
* @member {SysMsg.ILikeMsg|null|undefined} msg
* @memberof SysMsg.ProfileLikeSubTip
* @instance
*/
ProfileLikeSubTip.prototype.msg = null;
/**
* Decodes a ProfileLikeSubTip message from the specified reader or buffer.
* @function decode
* @memberof SysMsg.ProfileLikeSubTip
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {SysMsg.ProfileLikeSubTip} ProfileLikeSubTip
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
ProfileLikeSubTip.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.ProfileLikeSubTip();
while (reader.pos < end) {
let tag = reader.uint32();
switch (tag >>> 3) {
case 14: {
message.msg = $root.SysMsg.LikeMsg.decode(reader, reader.uint32());
break;
}
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a ProfileLikeSubTip message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof SysMsg.ProfileLikeSubTip
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {SysMsg.ProfileLikeSubTip} ProfileLikeSubTip
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
ProfileLikeSubTip.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader))
reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Gets the default type url for ProfileLikeSubTip
* @function getTypeUrl
* @memberof SysMsg.ProfileLikeSubTip
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
ProfileLikeSubTip.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com";
}
return typeUrlPrefix + "/SysMsg.ProfileLikeSubTip";
};
return ProfileLikeSubTip;
})();
SysMsg.ProfileLikeTip = (function() { SysMsg.ProfileLikeTip = (function() {
/** /**
* Properties of a ProfileLikeTip. * Properties of a ProfileLikeTip.
* @memberof SysMsg * @memberof SysMsg
* @interface IProfileLikeTip * @interface IProfileLikeTip
* @property {SysMsg.ILikeMsg|null} [msg] ProfileLikeTip msg * @property {number|null} [msgType] ProfileLikeTip msgType
* @property {number|null} [subType] ProfileLikeTip subType
* @property {SysMsg.IProfileLikeSubTip|null} [content] ProfileLikeTip content
*/ */
/** /**
@@ -886,24 +912,28 @@ export const SysMsg = $root.SysMsg = (() => {
} }
/** /**
* ProfileLikeTip msg. * ProfileLikeTip msgType.
* @member {SysMsg.ILikeMsg|null|undefined} msg * @member {number} msgType
* @memberof SysMsg.ProfileLikeTip * @memberof SysMsg.ProfileLikeTip
* @instance * @instance
*/ */
ProfileLikeTip.prototype.msg = null; ProfileLikeTip.prototype.msgType = 0;
/** /**
* Creates a new ProfileLikeTip instance using the specified properties. * ProfileLikeTip subType.
* @function create * @member {number} subType
* @memberof SysMsg.ProfileLikeTip * @memberof SysMsg.ProfileLikeTip
* @static * @instance
* @param {SysMsg.IProfileLikeTip=} [properties] Properties to set
* @returns {SysMsg.ProfileLikeTip} ProfileLikeTip instance
*/ */
ProfileLikeTip.create = function create(properties) { ProfileLikeTip.prototype.subType = 0;
return new ProfileLikeTip(properties);
}; /**
* ProfileLikeTip content.
* @member {SysMsg.IProfileLikeSubTip|null|undefined} content
* @memberof SysMsg.ProfileLikeTip
* @instance
*/
ProfileLikeTip.prototype.content = null;
/** /**
* Decodes a ProfileLikeTip message from the specified reader or buffer. * Decodes a ProfileLikeTip message from the specified reader or buffer.
@@ -923,8 +953,16 @@ export const SysMsg = $root.SysMsg = (() => {
while (reader.pos < end) { while (reader.pos < end) {
let tag = reader.uint32(); let tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 14: { case 1: {
message.msg = $root.SysMsg.LikeMsg.decode(reader, reader.uint32()); message.msgType = reader.uint32();
break;
}
case 2: {
message.subType = reader.uint32();
break;
}
case 203: {
message.content = $root.SysMsg.ProfileLikeSubTip.decode(reader, reader.uint32());
break; break;
} }
default: default:

View File

@@ -2,17 +2,23 @@ syntax = "proto3";
package SysMsg; package SysMsg;
message LikeDetail { message LikeDetail {
string txt = 1; string txt = 1;
uint32 uin = 3; uint32 uin = 3;
string nickname = 5; string nickname = 5;
} }
message LikeMsg { message LikeMsg {
uint32 count = 1; uint32 count = 1;
uint32 time = 2; uint32 time = 2;
LikeDetail detail = 3; LikeDetail detail = 3;
}
message ProfileLikeSubTip {
LikeMsg msg = 14;
} }
message ProfileLikeTip { message ProfileLikeTip {
LikeMsg msg = 14; uint32 msgType = 1;
uint32 subType = 2;
ProfileLikeSubTip content = 203;
} }

View File

@@ -119,7 +119,7 @@ export interface NodeIKernelBuddyService {
reportDoubtBuddyReqUnread(): void reportDoubtBuddyReqUnread(): void
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<unknown> getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<GeneralCallResult & { arkMsg: string }>
isNull(): boolean isNull(): boolean
} }

View File

@@ -232,7 +232,7 @@ export interface NodeIKernelGroupService {
setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<void> setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<void>
getGroupRecommendContactArkJson(groupCode: string): unknown getGroupRecommendContactArkJson(groupCode: string): Promise<GeneralCallResult & { arkJson: string }>
getJoinGroupLink(groupCode: string): unknown getJoinGroupLink(groupCode: string): unknown
@@ -244,6 +244,7 @@ export interface NodeIKernelGroupService {
msgRandom: number, msgRandom: number,
msgSeq: number msgSeq: number
}): Promise<unknown> }): Promise<unknown>
//需要提前判断是否存在 高版本新增 //需要提前判断是否存在 高版本新增
removeGroupEssence(param: { removeGroupEssence(param: {
groupCode: string groupCode: string

View File

@@ -102,7 +102,7 @@ export interface SendPicElement {
export interface SendReplyElement { export interface SendReplyElement {
elementType: ElementType.REPLY elementType: ElementType.REPLY
elementId: '' elementId: ''
replyElement: ReplyElement replyElement: Partial<ReplyElement>
} }
export interface SendFaceElement { export interface SendFaceElement {
@@ -129,6 +129,12 @@ export interface ReplyElement {
replayMsgId: string replayMsgId: string
senderUin: string senderUin: string
senderUinStr: string senderUinStr: string
sourceMsgIdInRecords: string
senderUid: string
senderUidStr: string
sourceMsgIsIncPic: boolean // 原消息是否有图片
sourceMsgText: string
replyMsgTime: string
} }
export interface FileElement { export interface FileElement {
@@ -282,14 +288,28 @@ export interface PicElement {
} }
export enum GrayTipElementSubType { export enum GrayTipElementSubType {
RECALL = 1, REVOKE = 1,
INVITE_NEW_MEMBER = 12, PROCLAMATION = 2,
MEMBER_NEW_TITLE = 17, EMOJIREPLY = 3,
GROUP = 4,
BUDDY = 5,
FEED = 6,
ESSENCE = 7,
GROUPNOTIFY = 8,
BUDDYNOTIFY = 9,
FILE = 10,
FEEDCHANNELMSG = 11,
XMLMSG = 12,
LOCALMSG = 13,
BLOCK = 14,
AIOOP = 15,
WALLET = 16,
JSON = 17,
} }
export interface GrayTipElement { export interface GrayTipElement {
subElementType: GrayTipElementSubType subElementType: GrayTipElementSubType
revokeElement: { revokeElement?: {
operatorRole: string operatorRole: string
operatorUid: string operatorUid: string
operatorNick: string operatorNick: string
@@ -299,14 +319,14 @@ export interface GrayTipElement {
isSelfOperate?: boolean isSelfOperate?: boolean
wording: string // 自定义的撤回提示语 wording: string // 自定义的撤回提示语
} }
aioOpGrayTipElement: TipAioOpGrayTipElement aioOpGrayTipElement?: TipAioOpGrayTipElement
groupElement: TipGroupElement groupElement?: TipGroupElement
xmlElement: { xmlElement?: {
templId: string templId: string
content: string content: string
} }
jsonGrayTipElement: { jsonGrayTipElement?: {
busiId: number busiId: string
jsonStr: string jsonStr: string
} }
} }
@@ -471,36 +491,7 @@ export interface RawMessage {
sendStatus?: number // 消息状态别人发的2是已撤回自己发的2是已发送 sendStatus?: number // 消息状态别人发的2是已撤回自己发的2是已发送
recallTime: string // 撤回时间, "0"是没有撤回 recallTime: string // 撤回时间, "0"是没有撤回
records: RawMessage[] records: RawMessage[]
elements: { elements: MessageElement[]
elementId: string
elementType: ElementType
replyElement: {
sourceMsgIdInRecords: string
senderUid: string // 原消息发送者QQ号
sourceMsgIsIncPic: boolean // 原消息是否有图片
sourceMsgText: string
replayMsgSeq: string // 源消息的msgSeq可以通过这个找到源消息的msgId
senderUidStr: string
replyMsgTime: string
}
textElement: {
atType: AtType
atUid: string // QQ号
content: string
atNtUid: string // uid号
}
picElement: PicElement
pttElement: PttElement
arkElement: ArkElement
grayTipElement: GrayTipElement
faceElement: FaceElement
videoElement: VideoElement
fileElement: FileElement
marketFaceElement: MarketFaceElement
inlineKeyboardElement: InlineKeyboardElement
markdownElement: MarkdownElement
multiForwardMsgElement: MultiForwardMsgElement
}[]
} }
export interface Peer { export interface Peer {
@@ -515,7 +506,7 @@ export interface MessageElement {
extBufForUI: string //"0x" extBufForUI: string //"0x"
textElement?: TextElement textElement?: TextElement
faceElement?: FaceElement faceElement?: FaceElement
marketFaceElement?: MarkdownElement marketFaceElement?: MarketFaceElement
replyElement?: ReplyElement replyElement?: ReplyElement
picElement?: PicElement picElement?: PicElement
pttElement?: PttElement pttElement?: PttElement

View File

@@ -1,44 +1,43 @@
import { ActionName, BaseCheckResult } from './types' import { ActionName } from './types'
import { OB11Response } from './OB11Response' import { OB11Response } from './OB11Response'
import { OB11Return } from '../types' import { OB11Return } from '../types'
import { Context } from 'cordis' import { Context, Schema } from 'cordis'
import type Adapter from '../adapter' import type Adapter from '../adapter'
abstract class BaseAction<PayloadType, ReturnDataType> { abstract class BaseAction<PayloadType, ReturnDataType> {
abstract actionName: ActionName abstract actionName: ActionName
protected ctx: Context protected ctx: Context
payloadSchema?: Schema<PayloadType>
constructor(protected adapter: Adapter) { constructor(protected adapter: Adapter) {
this.ctx = adapter.ctx this.ctx = adapter.ctx
} }
protected async check(payload: PayloadType): Promise<BaseCheckResult> {
return {
valid: true,
}
}
public async handle(payload: PayloadType): Promise<OB11Return<ReturnDataType | null>> { public async handle(payload: PayloadType): Promise<OB11Return<ReturnDataType | null>> {
const result = await this.check(payload) let params: PayloadType
if (!result.valid) { try {
return OB11Response.error(result.message, 400) params = this.payloadSchema ? new this.payloadSchema(payload) : payload
} catch (e) {
return OB11Response.error((e as Error).message, 400)
} }
try { try {
const resData = await this._handle(payload) const resData = await this._handle(params)
return OB11Response.ok(resData) return OB11Response.ok(resData)
} catch (e) { } catch (e) {
this.ctx.logger.error('发生错误', e) this.ctx.logger.error('发生错误', e)
return OB11Response.error(e?.toString() || (e as Error)?.stack?.toString() || '未知错误,可能操作超时', 200) return OB11Response.error((e as Error)?.toString() || (e as Error)?.stack?.toString() || '未知错误,可能操作超时', 200)
} }
} }
public async websocketHandle(payload: PayloadType, echo: unknown): Promise<OB11Return<ReturnDataType | null>> { public async websocketHandle(payload: PayloadType, echo: unknown): Promise<OB11Return<ReturnDataType | null>> {
const result = await this.check(payload) let params: PayloadType
if (!result.valid) { try {
return OB11Response.error(result.message, 1400) params = this.payloadSchema ? new this.payloadSchema(payload) : payload
} catch (e) {
return OB11Response.error((e as Error).message, 1400)
} }
try { try {
const resData = await this._handle(payload) const resData = await this._handle(params)
return OB11Response.ok(resData, echo) return OB11Response.ok(resData, echo)
} catch (e) { } catch (e) {
this.ctx.logger.error('发生错误', e) this.ctx.logger.error('发生错误', e)
@@ -46,9 +45,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
} }
} }
protected async _handle(payload: PayloadType): Promise<ReturnDataType> { protected abstract _handle(payload: PayloadType): Promise<ReturnDataType>
throw `pleas override ${this.actionName} _handle`
}
} }
export default BaseAction export { BaseAction, Schema }

View File

@@ -1,5 +1,5 @@
import BaseAction from '../BaseAction' import { BaseAction, Schema } from '../BaseAction'
import fsPromise from 'node:fs/promises' import { readFile } from 'node:fs/promises'
import { ActionName } from '../types' import { ActionName } from '../types'
import { Peer, ElementType } from '@/ntqqapi/types' import { Peer, ElementType } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
@@ -17,13 +17,16 @@ export interface GetFileResponse {
} }
export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> { export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/onebot11/action/file/GetFile.ts#L44 payloadSchema = Schema.object({
file: Schema.string().required()
})
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> { protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const { enableLocalFile2Url } = this.adapter.config const { enableLocalFile2Url } = this.adapter.config
let fileCache = await MessageUnique.getFileCacheById(String(payload.file)) let fileCache = await MessageUnique.getFileCacheById(payload.file)
if (!fileCache?.length) { if (!fileCache?.length) {
fileCache = await MessageUnique.getFileCacheByName(String(payload.file)) fileCache = await MessageUnique.getFileCacheByName(payload.file)
} }
if (fileCache?.length) { if (fileCache?.length) {
@@ -56,13 +59,13 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
if (!findEle) { if (!findEle) {
throw new Error('element not found') throw new Error('element not found')
} }
res.url = await this.ctx.ntFileApi.getImageUrl(findEle.picElement) res.url = await this.ctx.ntFileApi.getImageUrl(findEle.picElement!)
} else if (fileCache[0].elementType === ElementType.VIDEO) { } else if (fileCache[0].elementType === ElementType.VIDEO) {
res.url = await this.ctx.ntFileApi.getVideoUrl(peer, fileCache[0].msgId, fileCache[0].elementId) res.url = await this.ctx.ntFileApi.getVideoUrl(peer, fileCache[0].msgId, fileCache[0].elementId)
} }
if (enableLocalFile2Url && downloadPath && (res.file === res.url || res.url === undefined)) { if (enableLocalFile2Url && downloadPath && (res.file === res.url || res.url === undefined)) {
try { try {
res.base64 = await fsPromise.readFile(downloadPath, 'base64') res.base64 = await readFile(downloadPath, 'base64')
} catch (e) { } catch (e) {
throw new Error('文件下载失败. ' + e) throw new Error('文件下载失败. ' + e)
} }
@@ -76,11 +79,12 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
export default class GetFile extends GetFileBase { export default class GetFile extends GetFileBase {
actionName = ActionName.GetFile actionName = ActionName.GetFile
payloadSchema = Schema.object({
file: Schema.string(),
file_id: Schema.string().required()
})
protected async _handle(payload: { file_id: string; file: string }): Promise<GetFileResponse> { protected async _handle(payload: { file_id: string, file: string }): Promise<GetFileResponse> {
if (!payload.file_id) {
throw new Error('file_id 不能为空')
}
payload.file = payload.file_id payload.file = payload.file_id
return super._handle(payload) return super._handle(payload)
} }

View File

@@ -3,11 +3,4 @@ import { ActionName } from '../types'
export default class GetImage extends GetFileBase { export default class GetImage extends GetFileBase {
actionName = ActionName.GetImage actionName = ActionName.GetImage
protected async _handle(payload: { file: string }) {
if (!payload.file) {
throw new Error('参数 file 不能为空')
}
return super._handle(payload)
}
} }

View File

@@ -1,8 +1,9 @@
import path from 'node:path'
import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile' import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile'
import { ActionName } from '../types' import { ActionName } from '../types'
import { decodeSilk } from '@/common/utils/audio' import { decodeSilk } from '@/common/utils/audio'
import path from 'node:path' import { Schema } from '../BaseAction'
import fs from 'node:fs' import { stat, readFile } from 'node:fs/promises'
interface Payload extends GetFilePayload { interface Payload extends GetFilePayload {
out_format: 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac' out_format: 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac'
@@ -10,14 +11,18 @@ interface Payload extends GetFilePayload {
export default class GetRecord extends GetFileBase { export default class GetRecord extends GetFileBase {
actionName = ActionName.GetRecord actionName = ActionName.GetRecord
payloadSchema = Schema.object({
file: Schema.string().required(),
out_format: Schema.string().default('mp3')
})
protected async _handle(payload: Payload): Promise<GetFileResponse> { protected async _handle(payload: Payload): Promise<GetFileResponse> {
const res = await super._handle(payload) const res = await super._handle(payload)
res.file = await decodeSilk(this.ctx, res.file!, payload.out_format) res.file = await decodeSilk(this.ctx, res.file!, payload.out_format)
res.file_name = path.basename(res.file) res.file_name = path.basename(res.file)
res.file_size = fs.statSync(res.file).size.toString() res.file_size = (await stat(res.file)).size.toString()
if (this.adapter.config.enableLocalFile2Url) { if (this.adapter.config.enableLocalFile2Url) {
res.base64 = fs.readFileSync(res.file, 'base64') res.base64 = await readFile(res.file, 'base64')
} }
return res return res
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {
@@ -9,6 +9,10 @@ interface Payload {
export class CreateGroupFileFolder extends BaseAction<Payload, null> { export class CreateGroupFileFolder extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_CreateGroupFileFolder actionName = ActionName.GoCQHTTP_CreateGroupFileFolder
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
name: Schema.string().required(),
})
async _handle(payload: Payload) { async _handle(payload: Payload) {
await this.ctx.ntGroupApi.createGroupFileFolder(payload.group_id.toString(), payload.name) await this.ctx.ntGroupApi.createGroupFileFolder(payload.group_id.toString(), payload.name)

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
@@ -8,11 +8,11 @@ interface Payload {
export class DelEssenceMsg extends BaseAction<Payload, unknown> { export class DelEssenceMsg extends BaseAction<Payload, unknown> {
actionName = ActionName.GoCQHTTP_DelEssenceMsg actionName = ActionName.GoCQHTTP_DelEssenceMsg
payloadSchema = Schema.object({
message_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) { protected async _handle(payload: Payload) {
if (!payload.message_id) {
throw Error('message_id不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id) const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
if (!msg) { if (!msg) {
throw new Error('msg not found') throw new Error('msg not found')

View File

@@ -1,17 +1,22 @@
import BaseAction from '../BaseAction' import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {
group_id: string | number group_id: string | number
file_id: string file_id: string
busid?: 102 busid: number
} }
export class DelGroupFile extends BaseAction<Payload, null> { export class DelGroupFile extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_DelGroupFile actionName = ActionName.GoCQHTTP_DelGroupFile
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
file_id: Schema.string().required(),
busid: Schema.number().default(102)
})
async _handle(payload: Payload) { async _handle(payload: Payload) {
await this.ctx.ntGroupApi.deleteGroupFile(payload.group_id.toString(), [payload.file_id]) await this.ctx.ntGroupApi.deleteGroupFile(payload.group_id.toString(), [payload.file_id], [payload.busid])
return null return null
} }
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {
@@ -8,6 +8,10 @@ interface Payload {
export class DelGroupFolder extends BaseAction<Payload, null> { export class DelGroupFolder extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_DelGroupFolder actionName = ActionName.GoCQHTTP_DelGroupFolder
payloadSchema = Schema.object({
group_id: Schema.union([Number, String]).required(),
folder_id: Schema.string().required()
})
async _handle(payload: Payload) { async _handle(payload: Payload) {
await this.ctx.ntGroupApi.deleteGroupFileFolder(payload.group_id.toString(), payload.folder_id) await this.ctx.ntGroupApi.deleteGroupFileFolder(payload.group_id.toString(), payload.folder_id)

View File

@@ -1,7 +1,7 @@
import BaseAction from '../BaseAction'
import fs from 'fs' import fs from 'fs'
import fsPromise from 'fs/promises' import fsPromise from 'fs/promises'
import path from 'node:path' import path from 'node:path'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { calculateFileMD5, fetchFile } from '@/common/utils' import { calculateFileMD5, fetchFile } from '@/common/utils'
import { TEMP_DIR } from '@/common/globalVars' import { TEMP_DIR } from '@/common/globalVars'
@@ -22,6 +22,11 @@ interface FileResponse {
export class DownloadFile extends BaseAction<Payload, FileResponse> { export class DownloadFile extends BaseAction<Payload, FileResponse> {
actionName = ActionName.GoCQHTTP_DownloadFile actionName = ActionName.GoCQHTTP_DownloadFile
payloadSchema = Schema.object({
url: String,
base64: String,
headers: Schema.union([String, Schema.array(String)])
})
protected async _handle(payload: Payload): Promise<FileResponse> { protected async _handle(payload: Payload): Promise<FileResponse> {
const isRandomName = !payload.name const isRandomName = !payload.name

View File

@@ -1,8 +1,9 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11ForwardMessage } from '../../types' import { OB11ForwardMessage } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import { ActionName } from '../types' import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
import { filterNullable } from '@/common/utils/misc'
interface Payload { interface Payload {
message_id: string // long msg idgocq message_id: string // long msg idgocq
@@ -33,19 +34,21 @@ export class GetForwardMsg extends BaseAction<Payload, Response> {
const messages = await Promise.all( const messages = await Promise.all(
msgList.map(async (msg) => { msgList.map(async (msg) => {
const resMsg = await OB11Entities.message(this.ctx, msg) const resMsg = await OB11Entities.message(this.ctx, msg)
if (!resMsg) return
resMsg.message_id = MessageUnique.createMsg({ resMsg.message_id = MessageUnique.createMsg({
chatType: msg.chatType, chatType: msg.chatType,
peerUid: msg.peerUid, peerUid: msg.peerUid,
}, msg.msgId) }, msg.msgId)
return resMsg return resMsg
}), })
) )
const forwardMessages = messages.map(v => { const forwardMessages = filterNullable(messages)
const msg = v as Partial<OB11ForwardMessage> .map(v => {
msg.content = msg.message const msg = v as Partial<OB11ForwardMessage>
delete msg.message msg.content = msg.message
return msg as OB11ForwardMessage delete msg.message
}) return msg as OB11ForwardMessage
})
return { messages: forwardMessages } return { messages: forwardMessages }
} }
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,10 +1,11 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11Message } from '../../types' import { OB11Message } from '../../types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { ChatType } from '@/ntqqapi/types' import { ChatType } from '@/ntqqapi/types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import { RawMessage } from '@/ntqqapi/types' import { RawMessage } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
import { filterNullable } from '@/common/utils/misc'
interface Payload { interface Payload {
group_id: number | string group_id: number | string
@@ -41,6 +42,6 @@ export class GetGroupMsgHistory extends BaseAction<Payload, Response> {
}) })
) )
const ob11MsgList = await Promise.all(msgList.map((msg) => OB11Entities.message(this.ctx, msg))) const ob11MsgList = await Promise.all(msgList.map((msg) => OB11Entities.message(this.ctx, msg)))
return { messages: ob11MsgList } return { messages: filterNullable(ob11MsgList) }
} }
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { OB11GroupFile, OB11GroupFileFolder } from '../../types' import { OB11GroupFile, OB11GroupFileFolder } from '../../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { GroupNotifyStatus } from '@/ntqqapi/types' import { GroupNotifyStatus } from '@/ntqqapi/types'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11User } from '../../types' import { OB11User } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { handleQuickOperation, QuickOperation, QuickOperationEvent } from '../../helper/quickOperation' import { handleQuickOperation, QuickOperation, QuickOperationEvent } from '../../helper/quickOperation'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,14 +1,13 @@
import SendMsg from '../msg/SendMsg' import SendMsg from '../msg/SendMsg'
import { OB11PostSendMsg } from '../../types' import { OB11PostSendMsg } from '../../types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { convertMessage2List } from '../../helper/createMessage'
export class SendForwardMsg extends SendMsg { export class SendForwardMsg extends SendMsg {
actionName = ActionName.GoCQHTTP_SendForwardMsg actionName = ActionName.GoCQHTTP_SendForwardMsg
protected async check(payload: OB11PostSendMsg) { protected async _handle(payload: OB11PostSendMsg) {
if (payload.messages) payload.message = convertMessage2List(payload.messages) if (payload.messages) payload.message = payload.messages
return super.check(payload) return super._handle(payload)
} }
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { unlink } from 'fs/promises' import { unlink } from 'fs/promises'
import { checkFileReceived, uri2local } from '@/common/utils/file' import { checkFileReceived, uri2local } from '@/common/utils/file'
@@ -15,7 +15,7 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_SendGroupNotice actionName = ActionName.GoCQHTTP_SendGroupNotice
async _handle(payload: Payload) { async _handle(payload: Payload) {
if(!payload.content){ if (!payload.content) {
throw new Error('参数 content 不能为空') throw new Error('参数 content 不能为空')
} }
const groupCode = payload.group_id.toString() const groupCode = payload.group_id.toString()

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { SendElementEntities } from '@/ntqqapi/entities' import { SendElementEntities } from '@/ntqqapi/entities'
import { uri2local } from '@/common/utils' import { uri2local } from '@/common/utils'

View File

@@ -1,5 +1,5 @@
import { GroupEssenceMsgRet } from '@/ntqqapi/api' import { GroupEssenceMsgRet } from '@/ntqqapi/api'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface PayloadType { interface PayloadType {

View File

@@ -1,6 +1,6 @@
import { WebHonorType } from '@/ntqqapi/api' import { WebHonorType } from '@/ntqqapi/api'
import { ActionName } from '../types' import { ActionName } from '../types'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
interface Payload { interface Payload {
group_id: number group_id: number

View File

@@ -1,6 +1,6 @@
import { OB11Group } from '../../types' import { OB11Group } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,6 +1,6 @@
import { OB11Group } from '../../types' import { OB11Group } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11GroupMember } from '../../types' import { OB11GroupMember } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,6 +1,6 @@
import { OB11GroupMember } from '../../types' import { OB11GroupMember } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars' import { selfInfo } from '@/common/globalVars'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
export default class GetGuildList extends BaseAction<null, null> { export default class GetGuildList extends BaseAction<null, null> {

View File

@@ -1,14 +1,14 @@
import SendMsg from '../msg/SendMsg' import SendMsg from '../msg/SendMsg'
import { ActionName, BaseCheckResult } from '../types' import { ActionName } from '../types'
import { OB11PostSendMsg } from '../../types' import { OB11PostSendMsg } from '../../types'
class SendGroupMsg extends SendMsg { class SendGroupMsg extends SendMsg {
actionName = ActionName.SendGroupMsg actionName = ActionName.SendGroupMsg
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> { protected _handle(payload: OB11PostSendMsg) {
delete (payload as Partial<OB11PostSendMsg>).user_id delete (payload as Partial<OB11PostSendMsg>).user_id
payload.message_type = 'group' payload.message_type = 'group'
return super.check(payload) return super._handle(payload)
} }
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { GroupRequestOperateTypes } from '@/ntqqapi/types' import { GroupRequestOperateTypes } from '@/ntqqapi/types'
import { ActionName } from '../types' import { ActionName } from '../types'
@@ -14,9 +14,10 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
protected async _handle(payload: Payload): Promise<null> { protected async _handle(payload: Payload): Promise<null> {
const flag = payload.flag.toString() const flag = payload.flag.toString()
const approve = payload.approve?.toString() !== 'false' const approve = payload.approve?.toString() !== 'false'
await this.ctx.ntGroupApi.handleGroupRequest(flag, await this.ctx.ntGroupApi.handleGroupRequest(
flag,
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject, approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
payload.reason || '' payload.reason
) )
return null return null
} }

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { GroupMemberRole } from '@/ntqqapi/types' import { GroupMemberRole } from '@/ntqqapi/types'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -10,7 +10,7 @@ import SendGroupMsg from './group/SendGroupMsg'
import SendPrivateMsg from './msg/SendPrivateMsg' import SendPrivateMsg from './msg/SendPrivateMsg'
import SendMsg from './msg/SendMsg' import SendMsg from './msg/SendMsg'
import DeleteMsg from './msg/DeleteMsg' import DeleteMsg from './msg/DeleteMsg'
import BaseAction from './BaseAction' import { BaseAction } from './BaseAction'
import GetVersionInfo from './system/GetVersionInfo' import GetVersionInfo from './system/GetVersionInfo'
import CanSendRecord from './system/CanSendRecord' import CanSendRecord from './system/CanSendRecord'
import CanSendImage from './system/CanSendImage' import CanSendImage from './system/CanSendImage'
@@ -134,7 +134,7 @@ export function initActionMap(adapter: Adapter) {
new GetGroupRootFiles(adapter), new GetGroupRootFiles(adapter),
new SendGroupNotice(adapter) new SendGroupNotice(adapter)
] ]
const actionMap = new Map<string, BaseAction<unknown, unknown>>() const actionMap = new Map<string, BaseAction<any, unknown>>()
for (const action of actionHandlers) { for (const action of actionHandlers) {
actionMap.set(action.actionName, action) actionMap.set(action.actionName, action)
actionMap.set(action.actionName + '_async', action) actionMap.set(action.actionName + '_async', action)

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { Config } from '@/common/types' import { Config } from '@/common/types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { getConfigUtil } from '@/common/config' import { getConfigUtil } from '@/common/config'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { getHttpEvent } from '../../helper/eventForHttp' import { getHttpEvent } from '../../helper/eventForHttp'
import { OB11Message } from '../../types' import { OB11Message } from '../../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { GroupNotify, GroupNotifyStatus } from '@/ntqqapi/types' import { GroupNotify, GroupNotifyStatus } from '@/ntqqapi/types'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars' import { selfInfo } from '@/common/globalVars'
import { Dict } from 'cosmokit' import { Dict } from 'cosmokit'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,6 +1,6 @@
import BaseAction from '../BaseAction' import fs from 'node:fs'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import * as fs from 'node:fs'
import { checkFileReceived, uri2local } from '../../../common/utils/file' import { checkFileReceived, uri2local } from '../../../common/utils/file'
interface Payload { interface Payload {

View File

@@ -1,5 +1,5 @@
import { ActionName } from '../types' import { ActionName } from '../types'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ChatType } from '@/ntqqapi/types' import { ChatType } from '@/ntqqapi/types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { Peer } from '@/ntqqapi/types' import { Peer } from '@/ntqqapi/types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11Message } from '../../types' import { OB11Message } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import { ActionName } from '../types' import { ActionName } from '../types'
@@ -15,12 +15,12 @@ class GetMsg extends BaseAction<PayloadType, OB11Message> {
protected async _handle(payload: PayloadType) { protected async _handle(payload: PayloadType) {
if (!payload.message_id) { if (!payload.message_id) {
throw '参数message_id不能为空' throw new Error('参数message_id不能为空')
} }
const msgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString()) const msgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString())
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(msgShortId || +payload.message_id) const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(msgShortId || +payload.message_id)
if (!msgIdWithPeer) { if (!msgIdWithPeer) {
throw ('消息不存在') throw new Error('消息不存在')
} }
const peer = { const peer = {
guildId: '', guildId: '',
@@ -29,6 +29,9 @@ class GetMsg extends BaseAction<PayloadType, OB11Message> {
} }
const msg = this.adapter.getMsgCache(msgIdWithPeer.MsgId) ?? (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgIdWithPeer.MsgId])).msgList[0] const msg = this.adapter.getMsgCache(msgIdWithPeer.MsgId) ?? (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgIdWithPeer.MsgId])).msgList[0]
const retMsg = await OB11Entities.message(this.ctx, msg) const retMsg = await OB11Entities.message(this.ctx, msg)
if (!retMsg) {
throw new Error('消息为空')
}
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgId)! retMsg.message_id = MessageUnique.createMsg(peer, msg.msgId)!
retMsg.message_seq = retMsg.message_id retMsg.message_seq = retMsg.message_id
retMsg.real_id = retMsg.message_id retMsg.real_id = retMsg.message_id

View File

@@ -14,8 +14,8 @@ import {
OB11PostSendMsg, OB11PostSendMsg,
} from '../../types' } from '../../types'
import fs from 'node:fs' import fs from 'node:fs'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName, BaseCheckResult } from '../types' import { ActionName } from '../types'
import { CustomMusicSignPostData, IdMusicSignPostData, MusicSign, MusicSignPostData } from '@/common/utils/sign' import { CustomMusicSignPostData, IdMusicSignPostData, MusicSign, MusicSignPostData } from '@/common/utils/sign'
import { Peer } from '@/ntqqapi/types/msg' import { Peer } from '@/ntqqapi/types/msg'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
@@ -29,27 +29,6 @@ interface ReturnData {
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnData> { export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnData> {
actionName = ActionName.SendMsg actionName = ActionName.SendMsg
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
const messages = convertMessage2List(payload.message)
const fmNum = this.getSpecialMsgNum(messages, OB11MessageDataType.node)
if (fmNum && fmNum != messages.length) {
return {
valid: false,
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
}
}
const musicNum = this.getSpecialMsgNum(messages, OB11MessageDataType.music)
if (musicNum && messages.length > 1) {
return {
valid: false,
message: '音乐消息不可以和其他消息混在一起发送',
}
}
return {
valid: true,
}
}
protected async _handle(payload: OB11PostSendMsg) { protected async _handle(payload: OB11PostSendMsg) {
let contextMode = CreatePeerMode.Normal let contextMode = CreatePeerMode.Normal
if (payload.message_type === 'group') { if (payload.message_type === 'group') {

View File

@@ -1,13 +1,13 @@
import SendMsg from './SendMsg' import SendMsg from './SendMsg'
import { ActionName, BaseCheckResult } from '../types' import { ActionName } from '../types'
import { OB11PostSendMsg } from '../../types' import { OB11PostSendMsg } from '../../types'
class SendPrivateMsg extends SendMsg { class SendPrivateMsg extends SendMsg {
actionName = ActionName.SendPrivateMsg actionName = ActionName.SendPrivateMsg
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> { protected _handle(payload: OB11PostSendMsg) {
payload.message_type = 'private' payload.message_type = 'private'
return super.check(payload) return super._handle(payload)
} }
} }

View File

@@ -1,5 +1,5 @@
import { ActionName } from '../types' import { ActionName } from '../types'
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
interface Payload { interface Payload {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface ReturnType { interface ReturnType {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import fs from 'node:fs' import fs from 'node:fs'
import Path from 'node:path' import Path from 'node:path'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11User } from '../../types' import { OB11User } from '../../types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars' import { selfInfo } from '@/common/globalVars'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11Status } from '../../types' import { OB11Status } from '../../types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars' import { selfInfo } from '@/common/globalVars'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11Version } from '../../types' import { OB11Version } from '../../types'
import { ActionName } from '../types' import { ActionName } from '../types'
import { version } from '../../../version' import { version } from '../../../version'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Response { interface Response {

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { OB11User } from '../../types' import { OB11User } from '../../types'
import { OB11Entities } from '../../entities' import { OB11Entities } from '../../entities'
import { ActionName } from '../types' import { ActionName } from '../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
interface Payload { interface Payload {

View File

@@ -1,6 +1,5 @@
import BaseAction from '../BaseAction' import { BaseAction } from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { NTQQFriendApi } from '../../../ntqqapi/api/friend'
interface Payload { interface Payload {
flag: string flag: string

View File

@@ -195,6 +195,9 @@ class OneBot11Adapter extends Service {
OB11Entities.message(this.ctx, message) OB11Entities.message(this.ctx, message)
.then((msg) => { .then((msg) => {
if (!msg) {
return
}
if (!this.config.debug && msg.message.length === 0) { if (!this.config.debug && msg.message.length === 0) {
return return
} }
@@ -223,20 +226,16 @@ class OneBot11Adapter extends Service {
} }
} }
private handleRecallMsg(msgList: RawMessage[]) { private handleRecallMsg(message: RawMessage) {
for (const message of msgList) { const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId)
if (message.recallTime != '0') { if (!oriMessageId) {
const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) return
if (!oriMessageId) {
continue
}
OB11Entities.recallEvent(this.ctx, message, oriMessageId).then((recallEvent) => {
if (recallEvent) {
this.dispatch(recallEvent)
}
})
}
} }
OB11Entities.recallEvent(this.ctx, message, oriMessageId).then((recallEvent) => {
if (recallEvent) {
this.dispatch(recallEvent)
}
})
} }
private async handleFriendRequest(buddyReqs: FriendRequest[]) { private async handleFriendRequest(buddyReqs: FriendRequest[]) {
@@ -401,7 +400,7 @@ class OneBot11Adapter extends Service {
this.handleRecallMsg(input) this.handleRecallMsg(input)
}) })
this.ctx.on('nt/message-sent', input => { this.ctx.on('nt/message-sent', input => {
this.handleMsg(input) this.handleMsg([input])
}) })
this.ctx.on('nt/group-notify', input => { this.ctx.on('nt/group-notify', input => {
this.handleGroupNotify(input) this.handleGroupNotify(input)
@@ -416,8 +415,9 @@ class OneBot11Adapter extends Service {
const sysMsg = SysMsg.SystemMessage.decode(input) const sysMsg = SysMsg.SystemMessage.decode(input)
const { msgType, subType, subSubType } = sysMsg.msgSpec[0] ?? {} const { msgType, subType, subSubType } = sysMsg.msgSpec[0] ?? {}
if (msgType === 528 && subType === 39 && subSubType === 39) { if (msgType === 528 && subType === 39 && subSubType === 39) {
const tip = SysMsg.ProfileLikeTip.decode(sysMsg.bodyWrapper!.body!.slice(12)) const tip = SysMsg.ProfileLikeTip.decode(sysMsg.bodyWrapper!.body!)
const detail = tip.msg?.detail if (tip.msgType !== 0 || tip.subType !== 203) return
const detail = tip.content?.msg?.detail
if (!detail) return if (!detail) return
const [times] = detail.txt?.match(/\d+/) ?? ['0'] const [times] = detail.txt?.match(/\d+/) ?? ['0']
const profileLikeEvent = new OB11ProfileLikeEvent(detail.uin!, detail.nickname!, +times) const profileLikeEvent = new OB11ProfileLikeEvent(detail.uin!, detail.nickname!, +times)

View File

@@ -1,8 +1,8 @@
import BaseAction from '../action/BaseAction'
import http from 'node:http' import http from 'node:http'
import cors from 'cors' import cors from 'cors'
import crypto from 'node:crypto' import crypto from 'node:crypto'
import express, { Express, Request, Response } from 'express' import express, { Express, Request, Response } from 'express'
import { BaseAction } from '../action/BaseAction'
import { Context } from 'cordis' import { Context } from 'cordis'
import { llonebotError, selfInfo } from '@/common/globalVars' import { llonebotError, selfInfo } from '@/common/globalVars'
import { OB11Response } from '../action/OB11Response' import { OB11Response } from '../action/OB11Response'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../action/BaseAction' import { BaseAction } from '../action/BaseAction'
import { Context } from 'cordis' import { Context } from 'cordis'
import { WebSocket, WebSocketServer } from 'ws' import { WebSocket, WebSocketServer } from 'ws'
import { llonebotError } from '@/common/globalVars' import { llonebotError } from '@/common/globalVars'

View File

@@ -49,7 +49,8 @@ import { pathToFileURL } from 'node:url'
import OneBot11Adapter from './adapter' import OneBot11Adapter from './adapter'
export namespace OB11Entities { export namespace OB11Entities {
export async function message(ctx: Context, msg: RawMessage): Promise<OB11Message> { export async function message(ctx: Context, msg: RawMessage): Promise<OB11Message | undefined> {
if (!msg.senderUin || msg.senderUin === '0') return //跳过空消息
const { const {
debug, debug,
messagePostFormat, messagePostFormat,
@@ -57,14 +58,14 @@ export namespace OB11Entities {
const selfUin = selfInfo.uin const selfUin = selfInfo.uin
const resMsg: OB11Message = { const resMsg: OB11Message = {
self_id: parseInt(selfUin), self_id: parseInt(selfUin),
user_id: parseInt(msg.senderUin!), user_id: parseInt(msg.senderUin),
time: parseInt(msg.msgTime) || Date.now(), time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.msgShortId!, message_id: msg.msgShortId!,
real_id: msg.msgShortId!, real_id: msg.msgShortId!,
message_seq: msg.msgShortId!, message_seq: msg.msgShortId!,
message_type: msg.chatType === ChatType.group ? 'group' : 'private', message_type: msg.chatType === ChatType.group ? 'group' : 'private',
sender: { sender: {
user_id: parseInt(msg.senderUin!), user_id: parseInt(msg.senderUin),
nickname: msg.sendNickName, nickname: msg.sendNickName,
card: msg.sendMemberName ?? '', card: msg.sendMemberName ?? '',
}, },
@@ -81,7 +82,7 @@ export namespace OB11Entities {
if (msg.chatType === ChatType.group) { if (msg.chatType === ChatType.group) {
resMsg.sub_type = 'normal' resMsg.sub_type = 'normal'
resMsg.group_id = parseInt(msg.peerUin) resMsg.group_id = parseInt(msg.peerUin)
const member = await ctx.ntGroupApi.getGroupMember(msg.peerUin, msg.senderUin!) const member = await ctx.ntGroupApi.getGroupMember(msg.peerUin, msg.senderUin)
if (member) { if (member) {
resMsg.sender.role = groupMemberRole(member.role) resMsg.sender.role = groupMemberRole(member.role)
resMsg.sender.nickname = member.nick resMsg.sender.nickname = member.nick
@@ -164,7 +165,7 @@ export namespace OB11Entities {
// 284840486: 合并消息内侧 消息具体定位不到 // 284840486: 合并消息内侧 消息具体定位不到
if (!replyMsg && msg.peerUin !== '284840486') { if (!replyMsg && msg.peerUin !== '284840486') {
ctx.logger.info('queryMsgs', msgList.map(e => pick(e, ['msgSeq', 'msgRandom']))) ctx.logger.info('queryMsgs', msgList.map(e => pick(e, ['msgSeq', 'msgRandom'])), records.msgRandom)
throw new Error('回复消息验证失败') throw new Error('回复消息验证失败')
} }
messageSegment = { messageSegment = {
@@ -373,31 +374,26 @@ export namespace OB11Entities {
} }
for (const element of msg.elements) { for (const element of msg.elements) {
if (element.grayTipElement) { if (element.grayTipElement) {
if (element.grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) { const { grayTipElement } = element
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr) if (grayTipElement.jsonGrayTipElement?.busiId === '1061') {
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) { const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
//判断业务类型 const pokedetail: Dict[] = json.items
//Poke事件 //筛选item带有uid的元素
const pokedetail: Dict[] = json.items const poke_uid = pokedetail.filter(item => item.uid)
//筛选item带有uid的元素 if (poke_uid.length === 2) {
const poke_uid = pokedetail.filter(item => item.uid) return new OB11FriendPokeEvent(
if (poke_uid.length == 2) { Number(await ctx.ntUserApi.getUinByUid(poke_uid[0].uid)),
return new OB11FriendPokeEvent( Number(await ctx.ntUserApi.getUinByUid(poke_uid[1].uid)),
parseInt(await ctx.ntUserApi.getUinByUid(poke_uid[0].uid)), pokedetail
parseInt(await ctx.ntUserApi.getUinByUid(poke_uid[1].uid)), )
pokedetail
)
}
} }
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE }
if (grayTipElement.xmlElement?.templId === '10229') {
const uin = +msg.peerUin || +(await ctx.ntUserApi.getUinByUid(msg.peerUid))
return new OB11FriendAddNoticeEvent(uin)
} }
} }
} }
// 好友增加事件
if (msg.msgType === 5 && msg.subMsgType === 12) {
const event = new OB11FriendAddNoticeEvent(parseInt(msg.peerUin))
return event
}
} }
export async function groupEvent(ctx: Context, msg: RawMessage): Promise<OB11GroupNoticeEvent | void> { export async function groupEvent(ctx: Context, msg: RawMessage): Promise<OB11GroupNoticeEvent | void> {
@@ -543,7 +539,7 @@ export namespace OB11Entities {
} }
if ( if (
grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER && grayTipElement.subElementType == GrayTipElementSubType.XMLMSG &&
xmlElement?.templId == '10179' xmlElement?.templId == '10179'
) { ) {
ctx.logger.info('收到新人被邀请进群消息', grayTipElement) ctx.logger.info('收到新人被邀请进群消息', grayTipElement)
@@ -563,35 +559,9 @@ export namespace OB11Entities {
} }
} }
} }
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) { else if (grayTipElement.subElementType == GrayTipElementSubType.JSON) {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr) const json = JSON.parse(grayTipElement.jsonGrayTipElement!.jsonStr)
/* if (grayTipElement.jsonGrayTipElement?.busiId === '1061') {
{
align: 'center',
items: [
{ txt: '恭喜', type: 'nor' },
{
col: '3',
jp: '5',
param: ["QQ号"],
txt: '林雨辰',
type: 'url'
},
{ txt: '获得群主授予的', type: 'nor' },
{
col: '3',
jp: '',
txt: '好好好',
type: 'url'
},
{ txt: '头衔', type: 'nor' }
]
}
* */
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
const pokedetail: Dict[] = json.items const pokedetail: Dict[] = json.items
//筛选item带有uid的元素 //筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid) const poke_uid = pokedetail.filter(item => item.uid)
@@ -604,7 +574,7 @@ export namespace OB11Entities {
) )
} }
} }
if (grayTipElement.jsonGrayTipElement.busiId == 2401) { if (grayTipElement.jsonGrayTipElement?.busiId === '2401') {
ctx.logger.info('收到群精华消息', json) ctx.logger.info('收到群精华消息', json)
const searchParams = new URL(json.items[0].jp).searchParams const searchParams = new URL(json.items[0].jp).searchParams
const msgSeq = searchParams.get('msgSeq')! const msgSeq = searchParams.get('msgSeq')!
@@ -631,7 +601,7 @@ export namespace OB11Entities {
) )
// 获取MsgSeq+Peer可获取具体消息 // 获取MsgSeq+Peer可获取具体消息
} }
if (grayTipElement.jsonGrayTipElement.busiId == 2407) { if (grayTipElement.jsonGrayTipElement?.busiId === '2407') {
const memberUin = json.items[1].param[0] const memberUin = json.items[1].param[0]
const title = json.items[3].txt const title = json.items[3].txt
ctx.logger.info('收到群成员新头衔消息', json) ctx.logger.info('收到群成员新头衔消息', json)
@@ -653,14 +623,14 @@ export namespace OB11Entities {
shortId: number shortId: number
): Promise<OB11FriendRecallNoticeEvent | OB11GroupRecallNoticeEvent | undefined> { ): Promise<OB11FriendRecallNoticeEvent | OB11GroupRecallNoticeEvent | undefined> {
const msgElement = msg.elements.find( const msgElement = msg.elements.find(
(element) => element.grayTipElement?.subElementType === GrayTipElementSubType.RECALL, (element) => element.grayTipElement?.subElementType === GrayTipElementSubType.REVOKE,
) )
if (!msgElement) { if (!msgElement) {
return return
} }
const revokeElement = msgElement.grayTipElement.revokeElement const revokeElement = msgElement.grayTipElement!.revokeElement
if (msg.chatType === ChatType.group) { if (msg.chatType === ChatType.group) {
const operator = await ctx.ntGroupApi.getGroupMember(msg.peerUid, revokeElement.operatorUid) const operator = await ctx.ntGroupApi.getGroupMember(msg.peerUid, revokeElement!.operatorUid)
return new OB11GroupRecallNoticeEvent( return new OB11GroupRecallNoticeEvent(
parseInt(msg.peerUid), parseInt(msg.peerUid),
parseInt(msg.senderUin!), parseInt(msg.senderUin!),

View File

@@ -174,6 +174,12 @@ export async function createSendElements(
sendElements.push(SendElementEntities.rps(resultId)) sendElements.push(SendElementEntities.rps(resultId))
} }
break break
case OB11MessageDataType.contact: {
const { type, id } = sendMsg.data
const data = type === 'qq' ? ctx.ntFriendApi.getBuddyRecommendContact(id) : ctx.ntGroupApi.getGroupRecommendContact(id)
sendElements.push(SendElementEntities.ark(await data))
}
break
} }
} }

View File

@@ -1,4 +1,4 @@
import { OB11Message, OB11MessageAt, OB11MessageData, OB11MessageDataType } from '../types' import { OB11Message, OB11MessageData, OB11MessageDataType } from '../types'
import { OB11FriendRequestEvent } from '../event/request/OB11FriendRequest' import { OB11FriendRequestEvent } from '../event/request/OB11FriendRequest'
import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest' import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest'
import { GroupRequestOperateTypes } from '@/ntqqapi/types' import { GroupRequestOperateTypes } from '@/ntqqapi/types'
@@ -6,7 +6,6 @@ import { convertMessage2List, createSendElements, sendMsg, createPeer, CreatePee
import { MessageUnique } from '@/common/utils/messageUnique' import { MessageUnique } from '@/common/utils/messageUnique'
import { isNullable } from 'cosmokit' import { isNullable } from 'cosmokit'
import { Context } from 'cordis' import { Context } from 'cordis'
import { OB11Config } from '@/common/types'
interface QuickOperationPrivateMessage { interface QuickOperationPrivateMessage {
reply?: string reply?: string
@@ -57,7 +56,6 @@ export async function handleQuickOperation(ctx: Context, event: QuickOperationEv
async function handleMsg(ctx: Context, msg: OB11Message, quickAction: QuickOperationPrivateMessage | QuickOperationGroupMessage) { async function handleMsg(ctx: Context, msg: OB11Message, quickAction: QuickOperationPrivateMessage | QuickOperationGroupMessage) {
const reply = quickAction.reply const reply = quickAction.reply
const ob11Config: OB11Config = ctx.config
let contextMode = CreatePeerMode.Normal let contextMode = CreatePeerMode.Normal
if (msg.message_type === 'group') { if (msg.message_type === 'group') {
contextMode = CreatePeerMode.Group contextMode = CreatePeerMode.Group
@@ -67,23 +65,21 @@ async function handleMsg(ctx: Context, msg: OB11Message, quickAction: QuickOpera
const peer = await createPeer(ctx, msg, contextMode) const peer = await createPeer(ctx, msg, contextMode)
if (reply) { if (reply) {
let replyMessage: OB11MessageData[] = [] let replyMessage: OB11MessageData[] = []
if (ob11Config.enableQOAutoQuote) { replyMessage.push({
replyMessage.push({ type: OB11MessageDataType.reply,
type: OB11MessageDataType.reply, data: {
data: { id: msg.message_id.toString(),
id: msg.message_id.toString(), },
}, })
})
}
if (msg.message_type == 'group') { if (msg.message_type == 'group') {
if ((quickAction as QuickOperationGroupMessage).at_sender) { if ((quickAction as QuickOperationGroupMessage).at_sender) {
replyMessage.push({ replyMessage.push({
type: 'at', type: OB11MessageDataType.at,
data: { data: {
qq: msg.user_id.toString(), qq: msg.user_id.toString(),
}, },
} as OB11MessageAt) })
} }
} }
replyMessage = replyMessage.concat(convertMessage2List(reply, quickAction.auto_escape)) replyMessage = replyMessage.concat(convertMessage2List(reply, quickAction.auto_escape))

View File

@@ -130,6 +130,7 @@ export enum OB11MessageDataType {
poke = 'poke', poke = 'poke',
dice = 'dice', dice = 'dice',
RPS = 'rps', RPS = 'rps',
contact = 'contact',
} }
export interface OB11MessageMFace { export interface OB11MessageMFace {
@@ -276,6 +277,14 @@ export interface OB11MessageForward {
} }
} }
export interface OB11MessageContact {
type: OB11MessageDataType.contact
data: {
type: 'qq' | 'group'
id: string
}
}
export type OB11MessageData = export type OB11MessageData =
| OB11MessageText | OB11MessageText
| OB11MessageFace | OB11MessageFace
@@ -295,6 +304,7 @@ export type OB11MessageData =
| OB11MessageRPS | OB11MessageRPS
| OB11MessageMarkdown | OB11MessageMarkdown
| OB11MessageForward | OB11MessageForward
| OB11MessageContact
export interface OB11PostSendMsg { export interface OB11PostSendMsg {
message_type?: 'private' | 'group' message_type?: 'private' | 'group'

View File

@@ -160,11 +160,6 @@ async function onSettingWindowCreated(view: Element) {
`<div class="q-input" style="width:210px;"><input class="q-input__inner" data-config-key="musicSignUrl" type="text" value="${config.musicSignUrl}" placeholder="未设置" /></div>`, `<div class="q-input" style="width:210px;"><input class="q-input__inner" data-config-key="musicSignUrl" type="text" value="${config.musicSignUrl}" placeholder="未设置" /></div>`,
'config-musicSignUrl', 'config-musicSignUrl',
), ),
SettingItem(
'快速操作回复自动引用原消息',
null,
SettingSwitch('ob11.enableQOAutoQuote', config.ob11.enableQOAutoQuote),
),
SettingItem( SettingItem(
'HTTP、正向 WebSocket 服务仅监听 127.0.0.1', 'HTTP、正向 WebSocket 服务仅监听 127.0.0.1',
'而不是 0.0.0.0', '而不是 0.0.0.0',

View File

@@ -1 +1 @@
export const version = '3.32.4' export const version = '3.32.7'