Compare commits

..

25 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
idranme
9284fc7e8a Merge pull request #413 from LLOneBot/dev
3.32.4
2024-09-12 18:14:23 +08:00
idranme
ceb063143a chore: v3.32.4 2024-09-12 18:11:01 +08:00
idranme
ed55a5a54c optimize 2024-09-12 17:52:21 +08:00
idranme
2c4fdbfa6a fix: upload_group_file API 2024-09-12 16:46:38 +08:00
idranme
1132495eb3 fix: check for updates 2024-09-12 01:11:17 +08:00
idranme
2ac2c68435 chore 2024-09-11 22:13:11 +08:00
idranme
6477366ba6 chore
chore
2024-09-11 21:35:50 +08:00
96 changed files with 605 additions and 1754 deletions

View File

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

View File

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

View File

@@ -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 -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": "",
"license": "MIT",
@@ -41,7 +41,7 @@
"electron-vite": "^2.3.0",
"protobufjs-cli": "^1.1.3",
"typescript": "^5.6.2",
"vite": "^5.4.4",
"vite": "^5.4.5",
"vite-plugin-cp": "^4.0.8"
},
"packageManager": "yarn@4.4.1"

View File

@@ -35,4 +35,4 @@ const manifest = {
}
}
writeFileSync('manifest.json', JSON.stringify(manifest, null, 2))
writeFileSync('manifest.json', JSON.stringify(manifest, null, 2))

View File

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

View File

@@ -19,4 +19,4 @@ export const selfInfo: SelfInfo = {
uin: '',
nick: '',
online: true,
}
}

View File

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

View File

@@ -101,7 +101,7 @@ export async function encodeSilk(ctx: Context, filePath: string) {
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 { data } = await decode(silk, 24000)
const tmpPath = path.join(TEMP_DIR, path.basename(inputFilePath))
@@ -115,4 +115,4 @@ export async function decodeSilk(ctx: Context, inputFilePath: string, outFormat:
'-ac 1'
]
}, outFilePath)
}
}

View File

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

View File

@@ -5,20 +5,22 @@ import { version } from '../../version'
import { copyFolder, log, fetchFile } from '.'
import { PLUGIN_DIR, TEMP_DIR } from '../globalVars'
const downloadMirrorHosts = ['https://mirror.ghproxy.com/']
const checkVersionMirrorHosts = ['https://kkgithub.com']
const downloadMirrorHosts = ['https://ghp.ci/']
const releasesMirrorHosts = ['https://kkgithub.com']
export async function checkNewVersion() {
const latestVersionText = await getRemoteVersion()
const latestVersion = latestVersionText.split('.')
//log('llonebot last version', latestVersion)
const currentVersion: string[] = version.split('.')
log('LLOneBot latest version', latestVersion)
const currentVersion = version.split('.')
//log('llonebot current version', currentVersion)
for (const k of [0, 1, 2]) {
if (parseInt(latestVersion[k]) > parseInt(currentVersion[k])) {
const latest = parseInt(latestVersion[k])
const current = parseInt(currentVersion[k])
if (latest > current) {
log('')
return { result: true, version: latestVersionText }
} else if (parseInt(latestVersion[k]) < parseInt(currentVersion[k])) {
} else if (latest < current) {
break
}
}
@@ -28,7 +30,7 @@ export async function checkNewVersion() {
export async function upgradeLLOneBot() {
const latestVersion = await getRemoteVersion()
if (latestVersion && latestVersion != '') {
const downloadUrl = 'https://github.com/LLOneBot/LLOneBot/releases/download/v' + latestVersion + '/LLOneBot.zip'
const downloadUrl = `https://github.com/LLOneBot/LLOneBot/releases/download/v${latestVersion}/LLOneBot.zip`
const filePath = path.join(TEMP_DIR, './update-' + latestVersion + '.zip')
let downloadSuccess = false
// 多镜像下载
@@ -73,26 +75,34 @@ export async function upgradeLLOneBot() {
}
export async function getRemoteVersion() {
let Version = ''
for (let i = 0; i < checkVersionMirrorHosts.length; i++) {
const mirrorGithub = checkVersionMirrorHosts[i]
const tVersion = await getRemoteVersionByMirror(mirrorGithub)
if (tVersion && tVersion != '') {
Version = tVersion
break
for (const mirror of releasesMirrorHosts) {
const version = await getRemoteVersionByReleasesMirror(mirror)
if (version) {
return version
}
}
for (const mirror of downloadMirrorHosts) {
const version = await getRemoteVersionByDownloadMirror(mirror)
if (version) {
return version
}
}
return Version
}
export async function getRemoteVersionByMirror(mirrorGithub: string) {
let releasePage = 'error'
try {
releasePage = (await fetchFile(mirrorGithub + '/LLOneBot/LLOneBot/releases')).data.toString()
// log("releasePage", releasePage);
if (releasePage === 'error') return ''
return releasePage.match(new RegExp('(?<=(tag/v)).*?(?=("))'))?.[0]
} catch { }
return ''
}
export async function getRemoteVersionByDownloadMirror(mirrorGithub: string) {
try {
const source = 'https://raw.githubusercontent.com/LLOneBot/LLOneBot/main/src/version.ts'
const page = (await fetchFile(mirrorGithub + source)).data.toString()
return page.match(/(\d+\.\d+\.\d+)/)?.[0]
} catch (e) {
log(e?.toString())
}
}
export async function getRemoteVersionByReleasesMirror(mirrorGithub: string) {
try {
const page = (await fetchFile(mirrorGithub + '/LLOneBot/LLOneBot/releases')).data.toString()
return page.match(new RegExp('(?<=(tag/v)).*?(?=("))'))?.[0]
} catch { }
}

2
src/global.d.ts vendored
View File

@@ -6,4 +6,4 @@ declare global {
var LiteLoader: Dict
var authData: Dict | undefined
var navigation: Dict | undefined
}
}

View File

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

View File

@@ -189,4 +189,9 @@ export class NTQQFriendApi extends Service {
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 session = getSession()
if (session) {
return session.getGroupService().operateSysNotify(
false,
{
'operateType': operateType, // 2 拒绝
'targetMsg': {
'seq': seq, // 通知序列号
'type': type,
'groupCode': groupCode,
'postscript': reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
}
})
return session.getGroupService().operateSysNotify(false, {
operateType, // 2 拒绝
targetMsg: {
seq, // 通知序列号
type,
groupCode,
postscript: reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
}
})
} else {
return await invoke(NTMethod.HANDLE_GROUP_REQUEST, [{
doubt: false,
@@ -265,8 +263,8 @@ export class NTQQGroupApi extends Service {
return await invoke('nodeIKernelRichMediaService/deleteGroupFolder', [{ groupId, folderId }, null])
}
async deleteGroupFile(groupId: string, fileIdList: string[]) {
return await invoke('nodeIKernelRichMediaService/deleteGroupFile', [{ groupId, busIdList: [102], fileIdList }, null])
async deleteGroupFile(groupId: string, fileIdList: string[], busIdList: number[]) {
return await invoke('nodeIKernelRichMediaService/deleteGroupFile', [{ groupId, busIdList, fileIdList }, null])
}
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')!
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) {
continue
}
const forwardData = JSON.parse(arkElement.arkElement.bytesData)
const forwardData = JSON.parse(arkElement.arkElement!.bytesData)
if (forwardData.app != 'com.tencent.multimsg') {
continue
}

View File

@@ -87,17 +87,6 @@ export class NTQQUserApi extends Service {
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) {
const clientKeyData = await this.forceFetchClientKey()
if (clientKeyData?.result !== 0) {

View File

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

View File

@@ -173,4 +173,4 @@ export function registerCallHook(
export function removeReceiveHook(id: string) {
const index = receiveHooks.findIndex((h) => h.id === id)
receiveHooks.splice(index, 1)
}
}

View File

@@ -181,4 +181,4 @@ export function invoke<
apiArgs,
)
})
}
}

View File

@@ -34,29 +34,6 @@ export namespace SysMsg {
/** SystemMessage bodyWrapper. */
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;
/**
* Encodes the specified SystemMessage message. Does not implicitly {@link SysMsg.SystemMessage.verify|verify} messages.
* @param message SystemMessage message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.ISystemMessage, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified SystemMessage message, length delimited. Does not implicitly {@link SysMsg.SystemMessage.verify|verify} messages.
* @param message SystemMessage message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.ISystemMessage, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a SystemMessage message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
@@ -76,34 +53,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.SystemMessage;
/**
* Verifies a SystemMessage message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a SystemMessage message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns SystemMessage
*/
public static fromObject(object: { [k: string]: any }): SysMsg.SystemMessage;
/**
* Creates a plain object from a SystemMessage message. Also converts values to other types if specified.
* @param message SystemMessage
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.SystemMessage, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this SystemMessage to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for SystemMessage
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
@@ -149,29 +98,6 @@ export namespace SysMsg {
/** SystemMessageHeader uid. */
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;
/**
* Encodes the specified SystemMessageHeader message. Does not implicitly {@link SysMsg.SystemMessageHeader.verify|verify} messages.
* @param message SystemMessageHeader message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.ISystemMessageHeader, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified SystemMessageHeader message, length delimited. Does not implicitly {@link SysMsg.SystemMessageHeader.verify|verify} messages.
* @param message SystemMessageHeader message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.ISystemMessageHeader, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a SystemMessageHeader message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
@@ -191,34 +117,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.SystemMessageHeader;
/**
* Verifies a SystemMessageHeader message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a SystemMessageHeader message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns SystemMessageHeader
*/
public static fromObject(object: { [k: string]: any }): SysMsg.SystemMessageHeader;
/**
* Creates a plain object from a SystemMessageHeader message. Also converts values to other types if specified.
* @param message SystemMessageHeader
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.SystemMessageHeader, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this SystemMessageHeader to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for SystemMessageHeader
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
@@ -276,29 +174,6 @@ export namespace SysMsg {
/** SystemMessageMsgSpec other. */
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;
/**
* Encodes the specified SystemMessageMsgSpec message. Does not implicitly {@link SysMsg.SystemMessageMsgSpec.verify|verify} messages.
* @param message SystemMessageMsgSpec message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.ISystemMessageMsgSpec, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified SystemMessageMsgSpec message, length delimited. Does not implicitly {@link SysMsg.SystemMessageMsgSpec.verify|verify} messages.
* @param message SystemMessageMsgSpec message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.ISystemMessageMsgSpec, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a SystemMessageMsgSpec message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
@@ -318,34 +193,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.SystemMessageMsgSpec;
/**
* Verifies a SystemMessageMsgSpec message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a SystemMessageMsgSpec message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns SystemMessageMsgSpec
*/
public static fromObject(object: { [k: string]: any }): SysMsg.SystemMessageMsgSpec;
/**
* Creates a plain object from a SystemMessageMsgSpec message. Also converts values to other types if specified.
* @param message SystemMessageMsgSpec
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.SystemMessageMsgSpec, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this SystemMessageMsgSpec to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for SystemMessageMsgSpec
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
@@ -373,29 +220,6 @@ export namespace SysMsg {
/** SystemMessageBodyWrapper body. */
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;
/**
* Encodes the specified SystemMessageBodyWrapper message. Does not implicitly {@link SysMsg.SystemMessageBodyWrapper.verify|verify} messages.
* @param message SystemMessageBodyWrapper message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.ISystemMessageBodyWrapper, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified SystemMessageBodyWrapper message, length delimited. Does not implicitly {@link SysMsg.SystemMessageBodyWrapper.verify|verify} messages.
* @param message SystemMessageBodyWrapper message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.ISystemMessageBodyWrapper, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a SystemMessageBodyWrapper message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
@@ -415,34 +239,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.SystemMessageBodyWrapper;
/**
* Verifies a SystemMessageBodyWrapper message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a SystemMessageBodyWrapper message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns SystemMessageBodyWrapper
*/
public static fromObject(object: { [k: string]: any }): SysMsg.SystemMessageBodyWrapper;
/**
* Creates a plain object from a SystemMessageBodyWrapper message. Also converts values to other types if specified.
* @param message SystemMessageBodyWrapper
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.SystemMessageBodyWrapper, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this SystemMessageBodyWrapper to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for SystemMessageBodyWrapper
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
@@ -482,29 +278,6 @@ export namespace SysMsg {
/** LikeDetail nickname. */
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;
/**
* Encodes the specified LikeDetail message. Does not implicitly {@link SysMsg.LikeDetail.verify|verify} messages.
* @param message LikeDetail message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.ILikeDetail, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified LikeDetail message, length delimited. Does not implicitly {@link SysMsg.LikeDetail.verify|verify} messages.
* @param message LikeDetail message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.ILikeDetail, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a LikeDetail message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
@@ -524,34 +297,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.LikeDetail;
/**
* Verifies a LikeDetail message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a LikeDetail message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns LikeDetail
*/
public static fromObject(object: { [k: string]: any }): SysMsg.LikeDetail;
/**
* Creates a plain object from a LikeDetail message. Also converts values to other types if specified.
* @param message LikeDetail
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.LikeDetail, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this LikeDetail to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for LikeDetail
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
@@ -591,29 +336,6 @@ export namespace SysMsg {
/** LikeMsg detail. */
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;
/**
* Encodes the specified LikeMsg message. Does not implicitly {@link SysMsg.LikeMsg.verify|verify} messages.
* @param message LikeMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.ILikeMsg, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified LikeMsg message, length delimited. Does not implicitly {@link SysMsg.LikeMsg.verify|verify} messages.
* @param message LikeMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.ILikeMsg, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a LikeMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
@@ -633,34 +355,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.LikeMsg;
/**
* Verifies a LikeMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a LikeMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns LikeMsg
*/
public static fromObject(object: { [k: string]: any }): SysMsg.LikeMsg;
/**
* Creates a plain object from a LikeMsg message. Also converts values to other types if specified.
* @param message LikeMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.LikeMsg, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this LikeMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for LikeMsg
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
@@ -669,11 +363,63 @@ export namespace SysMsg {
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. */
interface IProfileLikeTip {
/** ProfileLikeTip msg */
msg?: (SysMsg.ILikeMsg|null);
/** ProfileLikeTip msgType */
msgType?: (number|null);
/** ProfileLikeTip subType */
subType?: (number|null);
/** ProfileLikeTip content */
content?: (SysMsg.IProfileLikeSubTip|null);
}
/** Represents a ProfileLikeTip. */
@@ -685,31 +431,14 @@ export namespace SysMsg {
*/
constructor(properties?: SysMsg.IProfileLikeTip);
/** ProfileLikeTip msg. */
public msg?: (SysMsg.ILikeMsg|null);
/** ProfileLikeTip msgType. */
public msgType: number;
/**
* Creates a new ProfileLikeTip instance using the specified properties.
* @param [properties] Properties to set
* @returns ProfileLikeTip instance
*/
public static create(properties?: SysMsg.IProfileLikeTip): SysMsg.ProfileLikeTip;
/** ProfileLikeTip subType. */
public subType: number;
/**
* Encodes the specified ProfileLikeTip message. Does not implicitly {@link SysMsg.ProfileLikeTip.verify|verify} messages.
* @param message ProfileLikeTip message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: SysMsg.IProfileLikeTip, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified ProfileLikeTip message, length delimited. Does not implicitly {@link SysMsg.ProfileLikeTip.verify|verify} messages.
* @param message ProfileLikeTip message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: SysMsg.IProfileLikeTip, writer?: $protobuf.Writer): $protobuf.Writer;
/** ProfileLikeTip content. */
public content?: (SysMsg.IProfileLikeSubTip|null);
/**
* Decodes a ProfileLikeTip message from the specified reader or buffer.
@@ -730,34 +459,6 @@ export namespace SysMsg {
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): SysMsg.ProfileLikeTip;
/**
* Verifies a ProfileLikeTip message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a ProfileLikeTip message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns ProfileLikeTip
*/
public static fromObject(object: { [k: string]: any }): SysMsg.ProfileLikeTip;
/**
* Creates a plain object from a ProfileLikeTip message. Also converts values to other types if specified.
* @param message ProfileLikeTip
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: SysMsg.ProfileLikeTip, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this ProfileLikeTip to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
/**
* Gets the default type url for ProfileLikeTip
* @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")

File diff suppressed because it is too large Load Diff

View File

@@ -2,17 +2,23 @@ syntax = "proto3";
package SysMsg;
message LikeDetail {
string txt = 1;
uint32 uin = 3;
string nickname = 5;
string txt = 1;
uint32 uin = 3;
string nickname = 5;
}
message LikeMsg {
uint32 count = 1;
uint32 time = 2;
LikeDetail detail = 3;
uint32 count = 1;
uint32 time = 2;
LikeDetail detail = 3;
}
message ProfileLikeSubTip {
LikeMsg msg = 14;
}
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
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<unknown>
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<GeneralCallResult & { arkMsg: string }>
isNull(): boolean
}
}

View File

@@ -52,7 +52,7 @@ export interface NodeIKernelGroupService {
//26702(其实更早 但是我不知道)
getGroupLatestEssenceList(groupCode: string): Promise<unknown>
//26702(其实更早 但是我不知道)
//26702(其实更早 但是我不知道)
shareDigest(Req: {
appId: string,
appType: number,
@@ -232,7 +232,7 @@ export interface NodeIKernelGroupService {
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
@@ -244,6 +244,7 @@ export interface NodeIKernelGroupService {
msgRandom: number,
msgSeq: number
}): Promise<unknown>
//需要提前判断是否存在 高版本新增
removeGroupEssence(param: {
groupCode: string
@@ -252,4 +253,4 @@ export interface NodeIKernelGroupService {
}): Promise<unknown>
isNull(): boolean
}
}

View File

@@ -102,7 +102,7 @@ export interface SendPicElement {
export interface SendReplyElement {
elementType: ElementType.REPLY
elementId: ''
replyElement: ReplyElement
replyElement: Partial<ReplyElement>
}
export interface SendFaceElement {
@@ -129,6 +129,12 @@ export interface ReplyElement {
replayMsgId: string
senderUin: string
senderUinStr: string
sourceMsgIdInRecords: string
senderUid: string
senderUidStr: string
sourceMsgIsIncPic: boolean // 原消息是否有图片
sourceMsgText: string
replyMsgTime: string
}
export interface FileElement {
@@ -282,14 +288,28 @@ export interface PicElement {
}
export enum GrayTipElementSubType {
RECALL = 1,
INVITE_NEW_MEMBER = 12,
MEMBER_NEW_TITLE = 17,
REVOKE = 1,
PROCLAMATION = 2,
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 {
subElementType: GrayTipElementSubType
revokeElement: {
revokeElement?: {
operatorRole: string
operatorUid: string
operatorNick: string
@@ -299,14 +319,14 @@ export interface GrayTipElement {
isSelfOperate?: boolean
wording: string // 自定义的撤回提示语
}
aioOpGrayTipElement: TipAioOpGrayTipElement
groupElement: TipGroupElement
xmlElement: {
aioOpGrayTipElement?: TipAioOpGrayTipElement
groupElement?: TipGroupElement
xmlElement?: {
templId: string
content: string
}
jsonGrayTipElement: {
busiId: number
jsonGrayTipElement?: {
busiId: string
jsonStr: string
}
}
@@ -471,36 +491,7 @@ export interface RawMessage {
sendStatus?: number // 消息状态别人发的2是已撤回自己发的2是已发送
recallTime: string // 撤回时间, "0"是没有撤回
records: RawMessage[]
elements: {
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
}[]
elements: MessageElement[]
}
export interface Peer {
@@ -515,7 +506,7 @@ export interface MessageElement {
extBufForUI: string //"0x"
textElement?: TextElement
faceElement?: FaceElement
marketFaceElement?: MarkdownElement
marketFaceElement?: MarketFaceElement
replyElement?: ReplyElement
picElement?: PicElement
pttElement?: PttElement
@@ -541,4 +532,4 @@ export interface MessageElement {
taskTopMsgElement?: unknown
recommendedMsgElement?: unknown
actionBarElement?: unknown
}
}

View File

@@ -78,4 +78,4 @@ Process.dlopen = function (module: Dict, filename: string, flags = constants.dlo
export function getSession() {
return wrapperApi['NodeIQQNTWrapperSession']
}
}

View File

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

View File

@@ -1,5 +1,5 @@
import BaseAction from '../BaseAction'
import fsPromise from 'node:fs/promises'
import { BaseAction, Schema } from '../BaseAction'
import { readFile } from 'node:fs/promises'
import { ActionName } from '../types'
import { Peer, ElementType } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique'
@@ -17,13 +17,16 @@ export interface 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> {
const { enableLocalFile2Url } = this.adapter.config
let fileCache = await MessageUnique.getFileCacheById(String(payload.file))
let fileCache = await MessageUnique.getFileCacheById(payload.file)
if (!fileCache?.length) {
fileCache = await MessageUnique.getFileCacheByName(String(payload.file))
fileCache = await MessageUnique.getFileCacheByName(payload.file)
}
if (fileCache?.length) {
@@ -56,13 +59,13 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
if (!findEle) {
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) {
res.url = await this.ctx.ntFileApi.getVideoUrl(peer, fileCache[0].msgId, fileCache[0].elementId)
}
if (enableLocalFile2Url && downloadPath && (res.file === res.url || res.url === undefined)) {
try {
res.base64 = await fsPromise.readFile(downloadPath, 'base64')
res.base64 = await readFile(downloadPath, 'base64')
} catch (e) {
throw new Error('文件下载失败. ' + e)
}
@@ -76,11 +79,12 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
export default class GetFile extends GetFileBase {
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> {
if (!payload.file_id) {
throw new Error('file_id 不能为空')
}
protected async _handle(payload: { file_id: string, file: string }): Promise<GetFileResponse> {
payload.file = payload.file_id
return super._handle(payload)
}

View File

@@ -3,11 +3,4 @@ import { ActionName } from '../types'
export default class GetImage extends GetFileBase {
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 { ActionName } from '../types'
import { decodeSilk } from '@/common/utils/audio'
import path from 'node:path'
import fs from 'node:fs'
import { Schema } from '../BaseAction'
import { stat, readFile } from 'node:fs/promises'
interface Payload extends GetFilePayload {
out_format: 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac'
@@ -10,14 +11,18 @@ interface Payload extends GetFilePayload {
export default class GetRecord extends GetFileBase {
actionName = ActionName.GetRecord
payloadSchema = Schema.object({
file: Schema.string().required(),
out_format: Schema.string().default('mp3')
})
protected async _handle(payload: Payload): Promise<GetFileResponse> {
const res = await super._handle(payload)
res.file = await decodeSilk(this.ctx, res.file!, payload.out_format)
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) {
res.base64 = fs.readFileSync(res.file, 'base64')
res.base64 = await readFile(res.file, 'base64')
}
return res
}

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { MessageUnique } from '@/common/utils/messageUnique'
@@ -8,11 +8,11 @@ interface Payload {
export class DelEssenceMsg extends BaseAction<Payload, unknown> {
actionName = ActionName.GoCQHTTP_DelEssenceMsg
payloadSchema = Schema.object({
message_id: Schema.union([Number, String]).required()
})
protected async _handle(payload: Payload) {
if (!payload.message_id) {
throw Error('message_id不能为空')
}
const msg = await MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)
if (!msg) {
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'
interface Payload {
group_id: string | number
file_id: string
busid?: 102
busid: number
}
export class DelGroupFile extends BaseAction<Payload, null> {
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) {
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
}
}
}

View File

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

View File

@@ -1,7 +1,7 @@
import BaseAction from '../BaseAction'
import fs from 'fs'
import fsPromise from 'fs/promises'
import path from 'node:path'
import { BaseAction, Schema } from '../BaseAction'
import { ActionName } from '../types'
import { calculateFileMD5, fetchFile } from '@/common/utils'
import { TEMP_DIR } from '@/common/globalVars'
@@ -22,6 +22,11 @@ interface FileResponse {
export class DownloadFile extends BaseAction<Payload, FileResponse> {
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> {
const isRandomName = !payload.name

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
interface Payload {
@@ -22,4 +22,4 @@ export class GetGroupAtAllRemain extends BaseAction<Payload, Response> {
remain_at_all_count_for_uin: data.atInfo.RemainAtAllCountForUin
}
}
}
}

View File

@@ -1,10 +1,11 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { OB11Message } from '../../types'
import { ActionName } from '../types'
import { ChatType } from '@/ntqqapi/types'
import { OB11Entities } from '../../entities'
import { RawMessage } from '@/ntqqapi/types'
import { MessageUnique } from '@/common/utils/messageUnique'
import { filterNullable } from '@/common/utils/misc'
interface Payload {
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)))
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 { OB11GroupFile, OB11GroupFileFolder } from '../../types'
@@ -59,4 +59,4 @@ export class GetGroupRootFiles extends BaseAction<Payload, Response> {
})
}
}
}
}

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { GroupNotifyStatus } from '@/ntqqapi/types'
import { ActionName } from '../types'
@@ -56,4 +56,4 @@ export class GetGroupSystemMsg extends BaseAction<void, Response> {
}
return data
}
}
}

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
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 { ActionName } from '../types'
@@ -13,4 +13,4 @@ export class HandleQuickOperation extends BaseAction<Payload, null> {
handleQuickOperation(this.ctx, payload.context, payload.operation).catch(e => this.ctx.logger.error(e))
return null
}
}
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
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 { SendElementEntities } from '@/ntqqapi/entities'
import { uri2local } from '@/common/utils'
@@ -20,9 +20,9 @@ export class UploadGroupFile extends BaseAction<UploadGroupFilePayload, null> {
if (!success) {
throw new Error(errMsg)
}
const sendFileEle = await SendElementEntities.file(this.ctx, path, payload.name || fileName, payload.folder_id)
const file = await SendElementEntities.file(this.ctx, path, payload.name || fileName, payload.folder ?? payload.folder_id)
const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group)
await sendMsg(this.ctx, peer, [sendFileEle], [])
await sendMsg(this.ctx, peer, [file], [])
return null
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
import SendMsg from '../msg/SendMsg'
import { ActionName, BaseCheckResult } from '../types'
import { ActionName } from '../types'
import { OB11PostSendMsg } from '../../types'
class SendGroupMsg extends SendMsg {
actionName = ActionName.SendGroupMsg
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
protected _handle(payload: OB11PostSendMsg) {
delete (payload as Partial<OB11PostSendMsg>).user_id
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 { ActionName } from '../types'
@@ -14,9 +14,10 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
protected async _handle(payload: Payload): Promise<null> {
const flag = payload.flag.toString()
const approve = payload.approve?.toString() !== 'false'
await this.ctx.ntGroupApi.handleGroupRequest(flag,
await this.ctx.ntGroupApi.handleGroupRequest(
flag,
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
payload.reason || ''
payload.reason
)
return null
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,7 @@ import SendGroupMsg from './group/SendGroupMsg'
import SendPrivateMsg from './msg/SendPrivateMsg'
import SendMsg from './msg/SendMsg'
import DeleteMsg from './msg/DeleteMsg'
import BaseAction from './BaseAction'
import { BaseAction } from './BaseAction'
import GetVersionInfo from './system/GetVersionInfo'
import CanSendRecord from './system/CanSendRecord'
import CanSendImage from './system/CanSendImage'
@@ -134,7 +134,7 @@ export function initActionMap(adapter: Adapter) {
new GetGroupRootFiles(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) {
actionMap.set(action.actionName, 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 { ActionName } from '../types'
import { getConfigUtil } from '@/common/config'
@@ -13,7 +13,7 @@ export class GetConfigAction extends BaseAction<null, Config> {
export class SetConfigAction extends BaseAction<Config, void> {
actionName = ActionName.SetConfig
protected async _handle(payload: Config): Promise<void> {
getConfigUtil().setConfig(payload)
}

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
import { getHttpEvent } from '../../helper/eventForHttp'
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 { ActionName } from '../types'

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
import { selfInfo } from '@/common/globalVars'
import { Dict } from 'cosmokit'
@@ -14,4 +14,4 @@ export class GetProfileLike extends BaseAction<void, Dict[]> {
}
return listdata
}
}
}

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
interface Payload {
@@ -22,4 +22,4 @@ export class SetOnlineStatus extends BaseAction<Payload, null> {
}
return null
}
}
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ChatType } from '@/ntqqapi/types'
import { ActionName } from '../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 { OB11Entities } from '../../entities'
import { ActionName } from '../types'
@@ -15,12 +15,12 @@ class GetMsg extends BaseAction<PayloadType, OB11Message> {
protected async _handle(payload: PayloadType) {
if (!payload.message_id) {
throw '参数message_id不能为空'
throw new Error('参数message_id不能为空')
}
const msgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString())
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(msgShortId || +payload.message_id)
if (!msgIdWithPeer) {
throw ('消息不存在')
throw new Error('消息不存在')
}
const peer = {
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 retMsg = await OB11Entities.message(this.ctx, msg)
if (!retMsg) {
throw new Error('消息为空')
}
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgId)!
retMsg.message_seq = retMsg.message_id
retMsg.real_id = retMsg.message_id

View File

@@ -14,8 +14,8 @@ import {
OB11PostSendMsg,
} from '../../types'
import fs from 'node:fs'
import BaseAction from '../BaseAction'
import { ActionName, BaseCheckResult } from '../types'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
import { CustomMusicSignPostData, IdMusicSignPostData, MusicSign, MusicSignPostData } from '@/common/utils/sign'
import { Peer } from '@/ntqqapi/types/msg'
import { MessageUnique } from '@/common/utils/messageUnique'
@@ -29,27 +29,6 @@ interface ReturnData {
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnData> {
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) {
let contextMode = CreatePeerMode.Normal
if (payload.message_type === 'group') {

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
import fs from 'node:fs'
import Path from 'node:path'
@@ -82,4 +82,4 @@ function deleteCachePath(pathList: string[]) {
for (const path of pathList) {
emptyPath(path)
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,26 +1,26 @@
import BaseAction from '../BaseAction'
import { ActionName } from '../types'
interface Response {
cookies: string
bkn: string
}
interface Payload {
domain: string
}
export class GetCookies extends BaseAction<Payload, Response> {
actionName = ActionName.GetCookies
protected async _handle(payload: Payload) {
if (!payload.domain) {
throw '缺少参数 domain'
}
const cookiesObject = await this.ctx.ntUserApi.getCookies(payload.domain)
//把获取到的cookiesObject转换成 k=v; 格式字符串拼接在一起
const cookies = Object.entries(cookiesObject).map(([key, value]) => `${key}=${value}`).join('; ')
const bkn = cookiesObject.skey ? this.ctx.ntWebApi.genBkn(cookiesObject.skey) : ''
return { cookies, bkn }
}
}
import { BaseAction } from '../BaseAction'
import { ActionName } from '../types'
interface Response {
cookies: string
bkn: string
}
interface Payload {
domain: string
}
export class GetCookies extends BaseAction<Payload, Response> {
actionName = ActionName.GetCookies
protected async _handle(payload: Payload) {
if (!payload.domain) {
throw '缺少参数 domain'
}
const cookiesObject = await this.ctx.ntUserApi.getCookies(payload.domain)
//把获取到的cookiesObject转换成 k=v; 格式字符串拼接在一起
const cookies = Object.entries(cookiesObject).map(([key, value]) => `${key}=${value}`).join('; ')
const bkn = cookiesObject.skey ? this.ctx.ntWebApi.genBkn(cookiesObject.skey) : ''
return { cookies, bkn }
}
}

View File

@@ -1,4 +1,4 @@
import BaseAction from '../BaseAction'
import { BaseAction } from '../BaseAction'
import { OB11User } from '../../types'
import { OB11Entities } from '../../entities'
import { ActionName } from '../types'
@@ -32,4 +32,4 @@ export class GetFriendWithCategory extends BaseAction<void, OB11User[]> {
throw new Error('this ntqq version not support, must be 26702 or later')
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,8 @@ import { pathToFileURL } from 'node:url'
import OneBot11Adapter from './adapter'
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 {
debug,
messagePostFormat,
@@ -57,14 +58,14 @@ export namespace OB11Entities {
const selfUin = selfInfo.uin
const resMsg: OB11Message = {
self_id: parseInt(selfUin),
user_id: parseInt(msg.senderUin!),
user_id: parseInt(msg.senderUin),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.msgShortId!,
real_id: msg.msgShortId!,
message_seq: msg.msgShortId!,
message_type: msg.chatType === ChatType.group ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin!),
user_id: parseInt(msg.senderUin),
nickname: msg.sendNickName,
card: msg.sendMemberName ?? '',
},
@@ -81,7 +82,7 @@ export namespace OB11Entities {
if (msg.chatType === ChatType.group) {
resMsg.sub_type = 'normal'
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) {
resMsg.sender.role = groupMemberRole(member.role)
resMsg.sender.nickname = member.nick
@@ -164,7 +165,7 @@ export namespace OB11Entities {
// 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('回复消息验证失败')
}
messageSegment = {
@@ -373,31 +374,26 @@ export namespace OB11Entities {
}
for (const element of msg.elements) {
if (element.grayTipElement) {
if (element.grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr)
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
const pokedetail: Dict[] = json.items
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid)
if (poke_uid.length == 2) {
return new OB11FriendPokeEvent(
parseInt(await ctx.ntUserApi.getUinByUid(poke_uid[0].uid)),
parseInt(await ctx.ntUserApi.getUinByUid(poke_uid[1].uid)),
pokedetail
)
}
const { grayTipElement } = element
if (grayTipElement.jsonGrayTipElement?.busiId === '1061') {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
const pokedetail: Dict[] = json.items
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid)
if (poke_uid.length === 2) {
return new OB11FriendPokeEvent(
Number(await ctx.ntUserApi.getUinByUid(poke_uid[0].uid)),
Number(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> {
@@ -489,7 +485,7 @@ export namespace OB11Entities {
}
}
else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin!), {
return new OB11GroupUploadNoticeEvent(+msg.peerUid, +msg.senderUin!, {
id: element.fileElement.fileUuid!,
name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize),
@@ -543,7 +539,7 @@ export namespace OB11Entities {
}
if (
grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER &&
grayTipElement.subElementType == GrayTipElementSubType.XMLMSG &&
xmlElement?.templId == '10179'
) {
ctx.logger.info('收到新人被邀请进群消息', grayTipElement)
@@ -563,35 +559,9 @@ export namespace OB11Entities {
}
}
}
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
/*
{
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事件
else if (grayTipElement.subElementType == GrayTipElementSubType.JSON) {
const json = JSON.parse(grayTipElement.jsonGrayTipElement!.jsonStr)
if (grayTipElement.jsonGrayTipElement?.busiId === '1061') {
const pokedetail: Dict[] = json.items
//筛选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)
const searchParams = new URL(json.items[0].jp).searchParams
const msgSeq = searchParams.get('msgSeq')!
@@ -631,7 +601,7 @@ export namespace OB11Entities {
)
// 获取MsgSeq+Peer可获取具体消息
}
if (grayTipElement.jsonGrayTipElement.busiId == 2407) {
if (grayTipElement.jsonGrayTipElement?.busiId === '2407') {
const memberUin = json.items[1].param[0]
const title = json.items[3].txt
ctx.logger.info('收到群成员新头衔消息', json)
@@ -653,14 +623,14 @@ export namespace OB11Entities {
shortId: number
): Promise<OB11FriendRecallNoticeEvent | OB11GroupRecallNoticeEvent | undefined> {
const msgElement = msg.elements.find(
(element) => element.grayTipElement?.subElementType === GrayTipElementSubType.RECALL,
(element) => element.grayTipElement?.subElementType === GrayTipElementSubType.REVOKE,
)
if (!msgElement) {
return
}
const revokeElement = msgElement.grayTipElement.revokeElement
const revokeElement = msgElement.grayTipElement!.revokeElement
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(
parseInt(msg.peerUid),
parseInt(msg.senderUin!),

View File

@@ -174,6 +174,12 @@ export async function createSendElements(
sendElements.push(SendElementEntities.rps(resultId))
}
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
}
}
@@ -301,4 +307,4 @@ export async function createPeer(ctx: Context, payload: CreatePeerPayload, mode:
}
}
throw new Error('请指定 group_id 或 user_id')
}
}

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

View File

@@ -130,6 +130,7 @@ export enum OB11MessageDataType {
poke = 'poke',
dice = 'dice',
RPS = 'rps',
contact = 'contact',
}
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 =
| OB11MessageText
| OB11MessageFace
@@ -295,6 +304,7 @@ export type OB11MessageData =
| OB11MessageRPS
| OB11MessageMarkdown
| OB11MessageForward
| OB11MessageContact
export interface OB11PostSendMsg {
message_type?: 'private' | 'group'

View File

@@ -1,7 +1,7 @@
export const iconSvg = `
<?xml version="2.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="50px" y="10px" width="262px" height="150" viewBox="0 0 212 262" enable-background="new 0 0 212 262" xml:space="preserve">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="50px" y="10px" width="262px" height="150" viewBox="0 0 212 262" enable-background="new 0 0 212 262" xml:space="preserve">
<image id="image0" width="212" height="210" x="0" y="-20" transform="rotate(180, 100, 100)"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANQAAAEGCAYAAAAdX7uIAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo
AAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAADsMAAA7DAcdv

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>`,
'config-musicSignUrl',
),
SettingItem(
'快速操作回复自动引用原消息',
null,
SettingSwitch('ob11.enableQOAutoQuote', config.ob11.enableQOAutoQuote),
),
SettingItem(
'HTTP、正向 WebSocket 服务仅监听 127.0.0.1',
'而不是 0.0.0.0',

View File

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