SnowCat 雪猫底层全源码:NapCat 4.18.28 底座 + SnowLuma 能力移植 + V8 通道适配 9.9.37
26 包 monorepo(TS 全源码): - snowcat-core: NT 内核 API 封装 + listener(含 9.9.37 六回调适配)+ Packet 发包 - snowcat-onebot: OneBot v11 全动作 + MCP Streamable HTTP 适配器 + 稳定线 - snowcat-webui-frontend/backend: 雪猫控制台(React+HeroUI) - snowcat-shell: 单文件 bundle 出口(vite inlineDynamicImports) - snowcat-native: napi2native / MoeHoo / ffmpeg / pty 运行时 - 56 项 SnowLuma 独门动作移植(qzone 全家桶/群相册/装扮/闪照/收藏) - 工具件: 解包器/启动器/便携制作器/doctor GitHub 关联已全量指向 DandanLLab/SnowCatQQ
This commit is contained in:
commit
b9655a5ab1
1048 files changed
+130851
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
// 兼容gocq 与name二选一
|
||||
folder_name: Type.Optional(Type.String({ description: '文件夹名称' })),
|
||||
// 兼容gocq 与folder_name二选一
|
||||
name: Type.Optional(Type.String({ description: '文件夹名称' })),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
result: Type.Any({ description: '操作结果' }),
|
||||
groupItem: Type.Any({ description: '群项信息' }),
|
||||
}, { description: '创建文件夹结果' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class CreateGroupFileFolder extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_CreateGroupFileFolder;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '创建群文件目录';
|
||||
override actionDescription = '在群文件系统中创建新的文件夹';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = {
|
||||
group_id: '123456789',
|
||||
folder_name: '新建文件夹',
|
||||
};
|
||||
|
||||
override returnExample = {
|
||||
result: {},
|
||||
groupItem: {},
|
||||
};
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
const folderName = payload.folder_name || payload.name;
|
||||
return (await this.core.apis.GroupApi.creatGroupFileFolder(payload.group_id.toString(), folderName!)).resultWithGroupItem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { FileNapCatOneBotUUID } from 'snowcat-common/src/file-uuid';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
file_id: Type.String({ description: '文件ID' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Any({ description: '删除结果' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class DeleteGroupFile extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GOCQHTTP_DeleteGroupFile;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '删除群文件';
|
||||
override actionDescription = '在群文件系统中删除指定的文件';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.DeleteGroupFile.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.DeleteGroupFile.response;
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
const data = FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
||||
if (!data || !data.fileId) throw new Error('Invalid file_id');
|
||||
return await this.core.apis.GroupApi.delGroupFile(payload.group_id.toString(), [data.fileId]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
folder_id: Type.Optional(Type.String({ description: '文件夹ID' })),
|
||||
folder: Type.Optional(Type.String({ description: '文件夹ID' })),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Any({ description: '删除结果' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class DeleteGroupFileFolder extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '删除群文件目录';
|
||||
override actionDescription = '在群文件系统中删除指定的文件夹';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.DeleteGroupFileFolder.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.DeleteGroupFileFolder.response;
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
return (await this.core.apis.GroupApi.delGroupFileFolder(
|
||||
payload.group_id.toString(), payload.folder ?? payload.folder_id ?? '')).groupFileCommonResult;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import fs from 'fs';
|
||||
import { join as joinPath } from 'node:path';
|
||||
import { calculateFileMD5, uriToLocalFile } from 'snowcat-common/src/file';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
url: Type.Optional(Type.String({ description: '下载链接' })),
|
||||
base64: Type.Optional(Type.String({ description: 'base64数据' })),
|
||||
name: Type.Optional(Type.String({ description: '文件名' })),
|
||||
headers: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())], { description: '请求头' })),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
file: Type.String({ description: '文件路径' }),
|
||||
}, { description: '下载结果' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export default class GoCQHTTPDownloadFile extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_DownloadFile;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '下载文件';
|
||||
override actionDescription = '下载网络文件到本地临时目录';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.DownloadFile.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.DownloadFile.response;
|
||||
|
||||
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||
const isRandomName = !payload.name;
|
||||
const name = payload.name || randomUUID();
|
||||
let result: Awaited<globalThis.ReturnType<typeof uriToLocalFile>>;
|
||||
|
||||
if (payload.base64) {
|
||||
result = await uriToLocalFile(this.core.SnowCatTempPath, `base64://${payload.base64}`, name);
|
||||
} else if (payload.url) {
|
||||
const headers = this.getHeaders(payload.headers);
|
||||
result = await uriToLocalFile(this.core.SnowCatTempPath, payload.url, name, headers);
|
||||
} else {
|
||||
throw new Error('不存在任何文件, 无法下载');
|
||||
}
|
||||
if (!result.success) {
|
||||
throw new Error(result.errMsg);
|
||||
}
|
||||
const filePath = result.path;
|
||||
if (fs.existsSync(filePath)) {
|
||||
if (isRandomName) {
|
||||
// 默认实现要名称未填写时文件名为文件 md5
|
||||
const md5 = await calculateFileMD5(filePath);
|
||||
const newPath = joinPath(this.core.SnowCatTempPath, md5);
|
||||
fs.renameSync(filePath, newPath);
|
||||
return { file: newPath };
|
||||
}
|
||||
return { file: filePath };
|
||||
} else {
|
||||
throw new Error('文件写入失败, 检查权限');
|
||||
}
|
||||
}
|
||||
|
||||
getHeaders (headersIn?: string | string[]): Record<string, string> {
|
||||
const headers: Record<string, string> = {};
|
||||
if (typeof headersIn === 'string') {
|
||||
headersIn = headersIn.split('[\\r\\n]');
|
||||
}
|
||||
if (Array.isArray(headersIn)) {
|
||||
for (const headerItem of headersIn) {
|
||||
const spilt = headerItem.indexOf('=');
|
||||
if (spilt < 0) {
|
||||
headers[headerItem] = '';
|
||||
} else {
|
||||
const key = headerItem.substring(0, spilt);
|
||||
headers[key] = headerItem.substring(spilt + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!headers['Content-Type']) {
|
||||
headers['Content-Type'] = 'application/octet-stream';
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward, OB11MessageNodePlain as OB11MessageNode } from '@/snowcat-onebot/index';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { MessageUnique } from 'snowcat-common/src/message-unique';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { ChatType, ElementType, MsgSourceType, NTMsgType, RawMessage } from 'snowcat-core';
|
||||
import { isNumeric } from 'snowcat-common/src/helper';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
message_id: Type.Optional(Type.String({ description: '消息ID' })),
|
||||
id: Type.Optional(Type.String({ description: '消息ID' })),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
messages: Type.Optional(Type.Array(Type.Unknown(), { description: '消息列表' })),
|
||||
}, { description: '合并转发消息' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
function isForward (msg: OB11MessageData | string): msg is OB11MessageForward {
|
||||
return typeof msg !== 'string' && msg.type === OB11MessageDataType.forward;
|
||||
}
|
||||
|
||||
export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetForwardMsg;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取合并转发消息';
|
||||
override actionDescription = '获取合并转发消息的具体内容';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetForwardMsg.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetForwardMsg.response;
|
||||
|
||||
private createTemplateNode (message: OB11Message): OB11MessageNode {
|
||||
return {
|
||||
type: OB11MessageDataType.node,
|
||||
data: {
|
||||
user_id: message.user_id,
|
||||
nickname: message.sender.nickname,
|
||||
message: [],
|
||||
content: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async parseForward (messages: OB11Message[]): Promise<OB11MessageNode[]> {
|
||||
const retMsg: OB11MessageNode[] = [];
|
||||
|
||||
for (const message of messages) {
|
||||
const templateNode = this.createTemplateNode(message);
|
||||
|
||||
for (const msgdata of message.message) {
|
||||
if (isForward(msgdata)) {
|
||||
const newNode = this.createTemplateNode(message);
|
||||
newNode.data.message = await this.parseForward(msgdata.data.content ?? []);
|
||||
|
||||
templateNode.data.message.push(newNode);
|
||||
} else {
|
||||
if (typeof msgdata === 'string') {
|
||||
const textMsg: OB11MessageData = { type: OB11MessageDataType.text, data: { text: msgdata } };
|
||||
templateNode.data.message.push(textMsg);
|
||||
} else {
|
||||
templateNode.data.message.push(msgdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
retMsg.push(templateNode);
|
||||
}
|
||||
|
||||
return retMsg;
|
||||
}
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
// 1. 检查消息ID是否存在
|
||||
const msgId = payload.message_id || payload.id;
|
||||
if (!msgId) {
|
||||
throw new Error('message_id is required');
|
||||
}
|
||||
|
||||
// 2. 定义辅助函数 - 创建伪转发消息对象
|
||||
const createFakeForwardMsg = (resId: string): RawMessage => {
|
||||
const fakeMsg: RawMessage = {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
elements: [{
|
||||
elementType: ElementType.MULTIFORWARD,
|
||||
elementId: '',
|
||||
multiForwardMsgElement: {
|
||||
resId,
|
||||
fileName: '',
|
||||
xmlContent: '',
|
||||
},
|
||||
}],
|
||||
guildId: '',
|
||||
isOnlineMsg: false,
|
||||
msgId: '', // TODO: no necessary
|
||||
msgRandom: '0',
|
||||
msgSeq: '',
|
||||
msgTime: '',
|
||||
msgType: NTMsgType.KMSGTYPEMIX,
|
||||
parentMsgIdList: [],
|
||||
parentMsgPeer: {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: '',
|
||||
},
|
||||
peerName: '',
|
||||
peerUid: '284840486',
|
||||
peerUin: '284840486',
|
||||
recallTime: '0',
|
||||
records: [],
|
||||
sendNickName: '',
|
||||
sendRemarkName: '',
|
||||
senderUid: '',
|
||||
senderUin: '1094950020',
|
||||
sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN,
|
||||
subMsgType: 1,
|
||||
};
|
||||
return fakeMsg;
|
||||
};
|
||||
|
||||
// 3. 定义协议回退逻辑函数
|
||||
const protocolFallbackLogic = async (resId: string) => {
|
||||
const ob = (await this.obContext.apis.MsgApi.parseMessageV2(createFakeForwardMsg(resId), true))?.arrayMsg;
|
||||
const firstMsg = ob?.message?.[0];
|
||||
if (firstMsg && isForward(firstMsg)) {
|
||||
return {
|
||||
messages: firstMsg.data.content,
|
||||
};
|
||||
}
|
||||
throw new Error('protocolFallbackLogic: 找不到相关的聊天记录');
|
||||
};
|
||||
// 4. 尝试通过正常渠道获取消息
|
||||
// 如果是数字ID,优先使用getMsgsByMsgId获取
|
||||
if (!isNumeric(msgId)) {
|
||||
const ret = await protocolFallbackLogic(msgId);
|
||||
if (ret.messages) {
|
||||
return ret;
|
||||
}
|
||||
throw new Error('ResId无效: 找不到相关的聊天记录');
|
||||
}
|
||||
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId.toString());
|
||||
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId);
|
||||
|
||||
if (rootMsg) {
|
||||
// 5. 获取消息内容
|
||||
const data = await this.core.apis.MsgApi.getMsgHistory(rootMsg.Peer, rootMsg.MsgId, 1);// getMsgsIncludeSelf
|
||||
|
||||
if (data && data.result === 0 && data.msgList.length > 0) {
|
||||
const singleMsg = data.msgList[0];
|
||||
if (!singleMsg) {
|
||||
throw new Error('消息不存在或已过期');
|
||||
}
|
||||
// 6. 解析消息内容
|
||||
const resMsg = (await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array', true));
|
||||
|
||||
const firstMsg = resMsg?.message?.[0];
|
||||
if (firstMsg && isForward(firstMsg)) {
|
||||
const forwardContent = firstMsg.data.content;
|
||||
if (forwardContent) {
|
||||
return { messages: forwardContent };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 说明消息已过期或者为内层消息 NapCat 一次返回不处理内层消息
|
||||
throw new Error('消息已过期或者为内层消息,无法获取转发消息');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { MessageFormat } from '@/snowcat-onebot/config/config';
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { OB11Message } from '@/snowcat-onebot/index';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { ChatType } from 'snowcat-core/types';
|
||||
import { MessageUnique } from 'snowcat-common/src/message-unique';
|
||||
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { NetworkAdapterConfig } from '@/snowcat-onebot/config/config';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
user_id: Type.String({ description: '用户QQ' }),
|
||||
message_seq: Type.Optional(Type.String({ description: '起始消息序号' })),
|
||||
count: Type.Number({ default: 20, description: '获取消息数量' }),
|
||||
reverse_order: Type.Boolean({ default: false, description: '是否反向排序' }),
|
||||
disable_get_url: Type.Boolean({ default: false, description: '是否禁用获取URL' }),
|
||||
parse_mult_msg: Type.Boolean({ default: true, description: '是否解析合并消息' }),
|
||||
quick_reply: Type.Boolean({ default: false, description: '是否快速回复' }),
|
||||
reverseOrder: Type.Boolean({ default: false, description: '是否反向排序(旧版本兼容)' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
messages: Type.Array(Type.Any(), { description: '消息列表' }),
|
||||
}, { description: '好友历史消息' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export default class GetFriendMsgHistory extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GetFriendMsgHistory;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取好友历史消息';
|
||||
override actionDescription = '获取指定好友的历史聊天记录';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetFriendMsgHistory.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetFriendMsgHistory.response;
|
||||
|
||||
async _handle (payload: PayloadType, _adapter: string, config: NetworkAdapterConfig): Promise<ReturnType> {
|
||||
// 处理参数
|
||||
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||
if (!uid) throw new Error(`记录${payload.user_id}不存在`);
|
||||
const friend = await this.core.apis.FriendApi.isBuddy(uid);
|
||||
const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid };
|
||||
const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0');
|
||||
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
|
||||
const msgList = hasMessageSeq
|
||||
? (await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, +payload.count, payload.reverse_order || payload.reverseOrder)).msgList
|
||||
: (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList;
|
||||
if (msgList.length === 0) throw new Error(`消息${payload.message_seq}不存在`);
|
||||
// 转换序号
|
||||
await Promise.all(msgList.map(async msg => {
|
||||
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||
}));
|
||||
// 烘焙消息
|
||||
const ob11MsgList = (await Promise.all(
|
||||
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config['messagePostFormat'] as MessageFormat | undefined ?? 'array', payload.parse_mult_msg, payload.disable_get_url)))
|
||||
).filter((msg): msg is OB11Message => msg !== undefined);
|
||||
return { messages: ob11MsgList };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
can_at_all: Type.Boolean({ description: '是否可以艾特全体' }),
|
||||
remain_at_all_count_for_group: Type.Number({ description: '群艾特全体剩余次数' }),
|
||||
remain_at_all_count_for_uin: Type.Number({ description: '个人艾特全体剩余次数' }),
|
||||
}, { description: '群艾特全体剩余次数' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class GoCQHTTPGetGroupAtAllRemain extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetGroupAtAllRemain;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取群艾特全体剩余次数';
|
||||
override actionDescription = '获取指定群聊中艾特全体成员的剩余次数';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetGroupAtAllRemain.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetGroupAtAllRemain.response;
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
const ret = await this.core.apis.GroupApi.getGroupRemainAtTimes(payload.group_id.toString());
|
||||
const data = {
|
||||
can_at_all: ret.atInfo.canAtAll,
|
||||
remain_at_all_count_for_group: ret.atInfo.RemainAtAllCountForGroup,
|
||||
remain_at_all_count_for_uin: ret.atInfo.RemainAtAllCountForUin,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
file_count: Type.Number({ description: '文件总数' }),
|
||||
limit_count: Type.Number({ description: '文件上限' }),
|
||||
used_space: Type.Number({ description: '已使用空间' }),
|
||||
total_space: Type.Number({ description: '总空间' }),
|
||||
}, { description: '群文件系统信息' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class GetGroupFileSystemInfo extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetGroupFileSystemInfo;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取群文件系统信息';
|
||||
override actionDescription = '获取群聊文件系统的空间及状态信息';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetGroupFileSystemInfo.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetGroupFileSystemInfo.response;
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
const groupCode = payload.group_id.toString();
|
||||
const [countResult, space] = await Promise.all([
|
||||
this.core.apis.GroupApi.getGroupFileCount([groupCode]),
|
||||
this.core.apis.GroupApi.getGroupFileSpace(groupCode),
|
||||
]);
|
||||
const groupFileCount = countResult.groupFileCounts[0];
|
||||
if (groupFileCount === undefined) {
|
||||
throw new Error('Group not found');
|
||||
}
|
||||
return {
|
||||
file_count: groupFileCount,
|
||||
limit_count: 10000,
|
||||
// Real values from the kernel's groupSpaceResult; fall back to the old
|
||||
// placeholders if the field is unexpectedly absent.
|
||||
used_space: space?.usedSpace ?? 0,
|
||||
total_space: space?.totalSpace ?? 10 * 1024 * 1024 * 1024,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { OB11Construct } from '@/snowcat-onebot/helper/data';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
folder_id: Type.Optional(Type.String({ description: '文件夹ID' })),
|
||||
folder: Type.Optional(Type.String({ description: '文件夹ID' })),
|
||||
file_count: Type.Union([Type.Number(), Type.String()], { default: 50, description: '文件数量' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
files: Type.Array(Type.Unknown(), { description: '文件列表' }),
|
||||
folders: Type.Array(Type.Unknown(), { description: '文件夹列表' }),
|
||||
}, { description: '群文件夹文件列表' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class GetGroupFilesByFolder extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetGroupFilesByFolder;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取群文件夹文件列表';
|
||||
override actionDescription = '获取指定群文件夹下的文件及子文件夹列表';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetGroupFilesByFolder.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetGroupFilesByFolder.response;
|
||||
|
||||
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||
const retRaw = await this.core.apis.MsgApi.getGroupFileList(payload.group_id.toString(), {
|
||||
sortType: 1,
|
||||
fileCount: +payload.file_count,
|
||||
startIndex: 0,
|
||||
sortOrder: 2,
|
||||
showOnlinedocFolder: 0,
|
||||
folderId: payload.folder ?? payload.folder_id ?? '',
|
||||
});
|
||||
const ret = Array.isArray(retRaw) ? retRaw : [];
|
||||
return {
|
||||
files: ret.filter(item => item.fileInfo)
|
||||
.map(item => OB11Construct.file(item.peerId, item.fileInfo!)),
|
||||
folders: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { WebHonorType } from 'snowcat-core/types';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
type: Type.Optional(Type.Enum(WebHonorType, { description: '荣誉类型' })),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
group_id: Type.Number({ description: '群号' }),
|
||||
current_talkative: Type.Record(Type.String(), Type.Unknown(), { description: '当前龙王' }),
|
||||
talkative_list: Type.Array(Type.Unknown(), { description: '龙王列表' }),
|
||||
performer_list: Type.Array(Type.Unknown(), { description: '群聊之火列表' }),
|
||||
legend_list: Type.Array(Type.Unknown(), { description: '群聊炽热列表' }),
|
||||
emotion_list: Type.Array(Type.Unknown(), { description: '快乐源泉列表' }),
|
||||
strong_newbie_list: Type.Array(Type.Unknown(), { description: '冒尖小春笋列表' }),
|
||||
}, { description: '群荣誉信息' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class GetGroupHonorInfo extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GetGroupHonorInfo;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取群荣誉信息';
|
||||
override actionDescription = '获取指定群聊的荣誉信息,如龙王等';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetGroupHonorInfo.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetGroupHonorInfo.response;
|
||||
|
||||
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||
if (!payload.type) {
|
||||
payload.type = WebHonorType.ALL;
|
||||
}
|
||||
return await this.core.apis.WebApi.getGroupHonorInfo(payload.group_id.toString(), payload.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { MessageFormat } from '@/snowcat-onebot/config/config';
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { OB11Message } from '@/snowcat-onebot/index';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { ChatType, Peer } from 'snowcat-core/types';
|
||||
import { MessageUnique } from 'snowcat-common/src/message-unique';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { NetworkAdapterConfig } from '@/snowcat-onebot/config/config';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
message_seq: Type.Optional(Type.String({ description: '起始消息序号' })),
|
||||
count: Type.Number({ default: 20, description: '获取消息数量' }),
|
||||
reverse_order: Type.Boolean({ default: false, description: '是否反向排序' }),
|
||||
disable_get_url: Type.Boolean({ default: false, description: '是否禁用获取URL' }),
|
||||
parse_mult_msg: Type.Boolean({ default: true, description: '是否解析合并消息' }),
|
||||
quick_reply: Type.Boolean({ default: false, description: '是否快速回复' }),
|
||||
reverseOrder: Type.Boolean({ default: false, description: '是否反向排序(旧版本兼容)' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
messages: Type.Array(Type.Any(), { description: '消息列表' }),
|
||||
}, { description: '群历史消息' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export default class GoCQHTTPGetGroupMsgHistory extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取群历史消息';
|
||||
override actionDescription = '获取指定群聊的历史聊天记录';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetGroupMsgHistory.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetGroupMsgHistory.response;
|
||||
|
||||
async _handle (payload: PayloadType, _adapter: string, config: NetworkAdapterConfig): Promise<ReturnType> {
|
||||
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
|
||||
const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0');
|
||||
// 拉取消息
|
||||
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
|
||||
const msgList = hasMessageSeq
|
||||
? (await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, +payload.count, payload.reverse_order || payload.reverseOrder)).msgList
|
||||
: (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList;
|
||||
if (msgList.length === 0) throw new Error(`消息${payload.message_seq}不存在`);
|
||||
// 转换序号
|
||||
await Promise.all(msgList.map(async msg => {
|
||||
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||
}));
|
||||
// 烘焙消息
|
||||
const ob11MsgList = (await Promise.all(
|
||||
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config['messagePostFormat'] as MessageFormat | undefined ?? 'array', payload.parse_mult_msg, payload.disable_get_url, payload.quick_reply)))
|
||||
).filter((msg): msg is OB11Message => msg !== undefined);
|
||||
return { messages: ob11MsgList };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { OB11Construct } from '@/snowcat-onebot/helper/data';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
file_count: Type.Union([Type.Number(), Type.String()], { default: 50, description: '文件数量' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
files: Type.Array(Type.Any(), { description: '文件列表' }),
|
||||
folders: Type.Array(Type.Any(), { description: '文件夹列表' }),
|
||||
}, { description: '群根目录文件列表' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class GetGroupRootFiles extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetGroupRootFiles;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取群根目录文件列表';
|
||||
override actionDescription = '获取群文件根目录下的所有文件和文件夹';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetGroupRootFiles.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetGroupRootFiles.response;
|
||||
|
||||
async _handle (payload: PayloadType) {
|
||||
const ret = await this.core.apis.MsgApi.getGroupFileList(payload.group_id.toString(), {
|
||||
sortType: 1,
|
||||
fileCount: +payload.file_count,
|
||||
startIndex: 0,
|
||||
sortOrder: 2,
|
||||
showOnlinedocFolder: 0,
|
||||
});
|
||||
|
||||
return {
|
||||
files: ret.filter(item => item.fileInfo)
|
||||
.map(item => OB11Construct.file(item.peerId, item.fileInfo!)),
|
||||
folders: ret.filter(item => item.folderInfo)
|
||||
.map(item => OB11Construct.folder(item.peerId, item.folderInfo!)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { sleep } from 'snowcat-common/src/helper';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({}, { description: '在线客户端负载' });
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Array(Type.Any(), { description: '在线客户端列表' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export class GetOnlineClient extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GetOnlineClient;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取在线客户端';
|
||||
override actionDescription = '获取当前登录账号的在线客户端列表';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetOnlineClient.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetOnlineClient.response;
|
||||
|
||||
async _handle () {
|
||||
// 注册监听
|
||||
this.core.apis.SystemApi.getOnlineDev();
|
||||
await sleep(500);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { OB11UserSex } from '@/snowcat-onebot/index';
|
||||
import { OB11Construct } from '@/snowcat-onebot/helper/data';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { calcQQLevel } from 'snowcat-common/src/helper';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
const PayloadSchema = Type.Object({
|
||||
user_id: Type.String({ description: '用户QQ' }),
|
||||
no_cache: Type.Union([Type.Boolean(), Type.String()], { default: false, description: '是否不使用缓存' }),
|
||||
});
|
||||
|
||||
type PayloadType = Static<typeof PayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
user_id: Type.Number({ description: '用户QQ' }),
|
||||
uid: Type.String({ description: 'UID' }),
|
||||
nickname: Type.String({ description: '昵称' }),
|
||||
age: Type.Number({ description: '年龄' }),
|
||||
qid: Type.String({ description: 'QID' }),
|
||||
qqLevel: Type.Number({ description: 'QQ等级' }),
|
||||
sex: Type.String({ description: '性别' }),
|
||||
long_nick: Type.String({ description: '个性签名' }),
|
||||
reg_time: Type.Number({ description: '注册时间' }),
|
||||
is_vip: Type.Boolean({ description: '是否VIP' }),
|
||||
is_years_vip: Type.Boolean({ description: '是否年费VIP' }),
|
||||
vip_level: Type.Number({ description: 'VIP等级' }),
|
||||
remark: Type.String({ description: '备注' }),
|
||||
status: Type.Number({ description: '状态' }),
|
||||
login_days: Type.Number({ description: '登录天数' }),
|
||||
}, { description: '陌生人信息' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export default class GoCQHTTPGetStrangerInfo extends OneBotAction<PayloadType, ReturnType> {
|
||||
override actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
||||
override payloadSchema = PayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '获取陌生人信息';
|
||||
override actionDescription = '获取指定非好友用户的信息';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GetStrangerInfo.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GetStrangerInfo.response;
|
||||
|
||||
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||
const user_id = payload.user_id.toString();
|
||||
const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache;
|
||||
const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id);
|
||||
let uid = (await this.core.apis.UserApi.getUidByUinV2(user_id));
|
||||
if (!uid) uid = extendData.detail.uid;
|
||||
const info = (await this.core.apis.UserApi.getUserDetailInfo(uid, isNocache));
|
||||
return {
|
||||
...extendData.detail.simpleInfo.coreInfo,
|
||||
...extendData.detail.commonExt ?? {},
|
||||
...extendData.detail.simpleInfo.baseInfo,
|
||||
...extendData.detail.simpleInfo.relationFlags ?? {},
|
||||
...extendData.detail.simpleInfo.status ?? {},
|
||||
user_id: parseInt(extendData.detail.uin) ?? 0,
|
||||
uid: info.uid ?? uid,
|
||||
nickname: extendData.detail.simpleInfo.coreInfo.nick ?? '',
|
||||
age: extendData.detail.simpleInfo.baseInfo.age ?? info.age,
|
||||
qid: extendData.detail.simpleInfo.baseInfo.qid,
|
||||
qqLevel: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? info.qqLevel),
|
||||
sex: OB11Construct.sex(extendData.detail.simpleInfo.baseInfo.sex) ?? OB11UserSex.unknown,
|
||||
long_nick: extendData.detail.simpleInfo.baseInfo.longNick ?? info.longNick,
|
||||
reg_time: extendData.detail.commonExt?.regTime ?? info.regTime,
|
||||
is_vip: extendData.detail.simpleInfo.vasInfo?.svipFlag ?? false,
|
||||
is_years_vip: extendData.detail.simpleInfo.vasInfo?.yearVipFlag ?? false,
|
||||
vip_level: extendData.detail.simpleInfo.vasInfo?.vipLevel ?? 0,
|
||||
remark: extendData.detail.simpleInfo.coreInfo.remark ?? info.remark ?? '',
|
||||
status: extendData.detail.simpleInfo.status?.status ?? info.status ?? 0,
|
||||
login_days: 0, // 失效
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const GoCQHTTPCheckUrlSafelyPayloadSchema = Type.Object({
|
||||
url: Type.String({ description: '要检查的 URL' }),
|
||||
});
|
||||
|
||||
export type GoCQHTTPCheckUrlSafelyPayload = Static<typeof GoCQHTTPCheckUrlSafelyPayloadSchema>;
|
||||
|
||||
export const GoCQHTTPCheckUrlSafelyReturnSchema = Type.Object({
|
||||
level: Type.Number({ description: '安全等级 (1: 安全, 2: 未知, 3: 危险)' }),
|
||||
});
|
||||
|
||||
export type GoCQHTTPCheckUrlSafelyReturn = Static<typeof GoCQHTTPCheckUrlSafelyReturnSchema>;
|
||||
|
||||
export class GoCQHTTPCheckUrlSafely extends OneBotAction<GoCQHTTPCheckUrlSafelyPayload, GoCQHTTPCheckUrlSafelyReturn> {
|
||||
override actionName = ActionName.GoCQHTTP_CheckUrlSafely;
|
||||
override payloadSchema = GoCQHTTPCheckUrlSafelyPayloadSchema;
|
||||
override returnSchema = GoCQHTTPCheckUrlSafelyReturnSchema;
|
||||
override actionSummary = '检查URL安全性';
|
||||
override actionDescription = '检查指定URL的安全等级';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GoCQHTTPCheckUrlSafely.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GoCQHTTPCheckUrlSafely.response;
|
||||
|
||||
async _handle () {
|
||||
return { level: 1 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const GoCQHTTPDeleteFriendPayloadSchema = Type.Object({
|
||||
friend_id: Type.Optional(Type.Union([Type.String(), Type.Number()], { description: '好友 QQ 号' })),
|
||||
user_id: Type.Optional(Type.Union([Type.String(), Type.Number()], { description: '用户 QQ 号' })),
|
||||
temp_block: Type.Optional(Type.Boolean({ description: '是否加入黑名单' })),
|
||||
temp_both_del: Type.Optional(Type.Boolean({ description: '是否双向删除' })),
|
||||
});
|
||||
|
||||
export type GoCQHTTPDeleteFriendPayload = Static<typeof GoCQHTTPDeleteFriendPayloadSchema>;
|
||||
|
||||
export class GoCQHTTPDeleteFriend extends OneBotAction<GoCQHTTPDeleteFriendPayload, any> {
|
||||
override actionName = ActionName.GoCQHTTP_DeleteFriend;
|
||||
override payloadSchema = GoCQHTTPDeleteFriendPayloadSchema;
|
||||
override returnSchema = Type.Any();
|
||||
override actionSummary = '删除好友';
|
||||
override actionDescription = '从好友列表中删除指定用户';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GoCQHTTPDeleteFriend.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GoCQHTTPDeleteFriend.response;
|
||||
|
||||
async _handle (payload: GoCQHTTPDeleteFriendPayload) {
|
||||
const uin = payload.friend_id ?? payload.user_id ?? '';
|
||||
const uid = await this.core.apis.UserApi.getUidByUinV2(uin.toString());
|
||||
|
||||
if (!uid) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '好友不存在',
|
||||
};
|
||||
}
|
||||
const isBuddy = await this.core.apis.FriendApi.isBuddy(uid);
|
||||
if (!isBuddy) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '不是好友',
|
||||
};
|
||||
}
|
||||
return await this.core.apis.FriendApi.delBuddy(uid, payload.temp_block, payload.temp_both_del);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const GoCQHTTPGetModelShowPayloadSchema = Type.Object({
|
||||
model: Type.Optional(Type.String({ description: '模型名称' })),
|
||||
});
|
||||
|
||||
export type GoCQHTTPGetModelShowPayload = Static<typeof GoCQHTTPGetModelShowPayloadSchema>;
|
||||
|
||||
export const GoCQHTTPGetModelShowReturnSchema = Type.Array(Type.Object({
|
||||
variants: Type.Object({
|
||||
model_show: Type.String({ description: '显示名称' }),
|
||||
need_pay: Type.Boolean({ description: '是否需要付费' }),
|
||||
}),
|
||||
}), { description: '机型显示列表' });
|
||||
|
||||
export type GoCQHTTPGetModelShowReturn = Static<typeof GoCQHTTPGetModelShowReturnSchema>;
|
||||
|
||||
export class GoCQHTTPGetModelShow extends OneBotAction<GoCQHTTPGetModelShowPayload, GoCQHTTPGetModelShowReturn> {
|
||||
override actionName = ActionName.GoCQHTTP_GetModelShow;
|
||||
override payloadSchema = GoCQHTTPGetModelShowPayloadSchema;
|
||||
override returnSchema = GoCQHTTPGetModelShowReturnSchema;
|
||||
override actionSummary = '获取机型显示';
|
||||
override actionDescription = '获取当前账号可用的设备机型显示名称列表';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GoCQHTTPGetModelShow.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GoCQHTTPGetModelShow.response;
|
||||
|
||||
async _handle (payload: GoCQHTTPGetModelShowPayload) {
|
||||
if (!payload.model) {
|
||||
payload.model = 'napcat';
|
||||
}
|
||||
return [{
|
||||
variants: {
|
||||
model_show: 'napcat',
|
||||
need_pay: false,
|
||||
},
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
// 兼容性代码
|
||||
export class GoCQHTTPSetModelShow extends OneBotAction<void, void> {
|
||||
override actionName = ActionName.GoCQHTTP_SetModelShow;
|
||||
override payloadSchema = Type.Object({});
|
||||
override returnSchema = Type.Null();
|
||||
override actionSummary = '设置机型';
|
||||
override actionDescription = '设置当前账号的设备机型名称';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.GoCQHTTPSetModelShow.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.GoCQHTTPSetModelShow.response;
|
||||
|
||||
async _handle () {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { QuickAction, QuickActionEvent } from '@/snowcat-onebot/types';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
import { OB11MessageMixTypeSchema } from '@/snowcat-onebot/types/message';
|
||||
|
||||
const SenderSchema = Type.Object({
|
||||
user_id: Type.String({ description: '用户ID' }),
|
||||
nickname: Type.String({ description: '昵称' }),
|
||||
sex: Type.Optional(Type.String({ description: '性别' })),
|
||||
age: Type.Optional(Type.Number({ description: '年龄' })),
|
||||
card: Type.Optional(Type.String({ description: '群名片' })),
|
||||
level: Type.Optional(Type.String({ description: '群等级' })),
|
||||
role: Type.Optional(Type.String({ description: '群角色' })),
|
||||
});
|
||||
|
||||
// 定义 QuickAction 的详细 Schema
|
||||
const QuickActionSchema = Type.Object({
|
||||
reply: Type.Optional(OB11MessageMixTypeSchema),
|
||||
auto_escape: Type.Optional(Type.Boolean({ description: '是否作为纯文本发送' })),
|
||||
at_sender: Type.Optional(Type.Boolean({ description: '是否 @ 发送者' })),
|
||||
delete: Type.Optional(Type.Boolean({ description: '是否撤回该消息' })),
|
||||
kick: Type.Optional(Type.Boolean({ description: '是否踢出发送者' })),
|
||||
ban: Type.Optional(Type.Boolean({ description: '是否禁言发送者' })),
|
||||
ban_duration: Type.Optional(Type.Number({ description: '禁言时长' })),
|
||||
approve: Type.Optional(Type.Boolean({ description: '是否同意请求/加群' })),
|
||||
remark: Type.Optional(Type.String({ description: '好友备注' })),
|
||||
reason: Type.Optional(Type.String({ description: '拒绝理由' })),
|
||||
}, { description: '快速操作内容' });
|
||||
|
||||
// 定义 QuickActionEvent 的详细 Schema
|
||||
const QuickActionEventSchema = Type.Object({
|
||||
time: Type.Number({ description: '事件发生时间' }),
|
||||
self_id: Type.Number({ description: '收到事件的机器人 QQ 号' }),
|
||||
post_type: Type.String({ description: '上报类型' }),
|
||||
message_type: Type.Optional(Type.String({ description: '消息类型' })),
|
||||
sub_type: Type.Optional(Type.String({ description: '消息子类型' })),
|
||||
user_id: Type.String({ description: '发送者 QQ 号' }),
|
||||
group_id: Type.Optional(Type.String({ description: '群号' })),
|
||||
message_id: Type.Optional(Type.Number({ description: '消息 ID' })),
|
||||
message_seq: Type.Optional(Type.Number({ description: '消息序列号' })),
|
||||
real_id: Type.Optional(Type.Number({ description: '真实消息 ID' })),
|
||||
sender: Type.Optional(SenderSchema),
|
||||
message: Type.Optional(Type.Any({ description: '消息内容' })),
|
||||
message_format: Type.Optional(Type.String({ description: '消息格式' })),
|
||||
raw_message: Type.Optional(Type.String({ description: '原始消息内容' })),
|
||||
font: Type.Optional(Type.Number({ description: '字体' })),
|
||||
notice_type: Type.Optional(Type.String({ description: '通知类型' })),
|
||||
meta_event_type: Type.Optional(Type.String({ description: '元事件类型' })),
|
||||
}, { description: '事件上下文' });
|
||||
|
||||
export const GoCQHTTPHandleQuickActionPayloadSchema = Type.Object({
|
||||
context: QuickActionEventSchema,
|
||||
operation: QuickActionSchema,
|
||||
});
|
||||
|
||||
export type GoCQHTTPHandleQuickActionPayload = {
|
||||
context: QuickActionEvent;
|
||||
operation: QuickAction;
|
||||
} & Static<typeof GoCQHTTPHandleQuickActionPayloadSchema>;
|
||||
|
||||
export class GoCQHTTPHandleQuickAction extends OneBotAction<GoCQHTTPHandleQuickActionPayload, void> {
|
||||
override actionName = ActionName.GoCQHTTP_HandleQuickAction;
|
||||
override payloadSchema = GoCQHTTPHandleQuickActionPayloadSchema;
|
||||
override returnSchema = Type.Null();
|
||||
override actionSummary = '处理快速操作';
|
||||
override actionDescription = '处理来自事件上报的快速操作请求';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.QuickAction.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.QuickAction.response;
|
||||
|
||||
async _handle (payload: GoCQHTTPHandleQuickActionPayload): Promise<void> {
|
||||
this.obContext.apis.QuickActionApi
|
||||
.handleQuickOperation(payload.context, payload.operation)
|
||||
.catch(e => this.core.context.logger.logError(e));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { OB11MessageMixType } from '@/snowcat-onebot/types';
|
||||
import { ContextMode, normalize, ReturnDataType, SendMsgBase, SendMsgPayload } from '@/snowcat-onebot/action/msg/SendMsg';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
|
||||
// 未验证
|
||||
type GoCQHTTPSendForwardMsgPayload = SendMsgPayload & { messages?: OB11MessageMixType; };
|
||||
|
||||
export class GoCQHTTPSendForwardMsgBase extends SendMsgBase {
|
||||
protected override async check (payload: GoCQHTTPSendForwardMsgPayload) {
|
||||
if (payload.messages) payload.message = normalize(payload.messages);
|
||||
return super.check(payload);
|
||||
}
|
||||
}
|
||||
export class GoCQHTTPSendForwardMsg extends GoCQHTTPSendForwardMsgBase {
|
||||
override actionName = ActionName.GoCQHTTP_SendForwardMsg;
|
||||
override actionSummary = '发送合并转发消息';
|
||||
override actionDescription = '发送合并转发消息';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = {
|
||||
group_id: '123456789',
|
||||
messages: [],
|
||||
};
|
||||
|
||||
override returnExample = {
|
||||
message_id: 123456,
|
||||
};
|
||||
|
||||
protected override async check (payload: GoCQHTTPSendForwardMsgPayload) {
|
||||
if (payload.messages) payload.message = normalize(payload.messages);
|
||||
return super.check(payload);
|
||||
}
|
||||
}
|
||||
export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendForwardMsgBase {
|
||||
override actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg;
|
||||
override actionSummary = '发送私聊合并转发消息';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = {
|
||||
user_id: '123456789',
|
||||
messages: [],
|
||||
};
|
||||
|
||||
override returnExample = {
|
||||
message_id: 123456,
|
||||
};
|
||||
|
||||
override async _handle (payload: GoCQHTTPSendForwardMsgPayload): Promise<ReturnDataType> {
|
||||
return this.base_handle(payload, ContextMode.Private);
|
||||
}
|
||||
}
|
||||
|
||||
export class GoCQHTTPSendGroupForwardMsg extends GoCQHTTPSendForwardMsgBase {
|
||||
override actionName = ActionName.GoCQHTTP_SendGroupForwardMsg;
|
||||
override actionSummary = '发送群合并转发消息';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = {
|
||||
group_id: '123456789',
|
||||
messages: [],
|
||||
};
|
||||
|
||||
override returnExample = {
|
||||
message_id: 123456,
|
||||
};
|
||||
|
||||
override async _handle (payload: GoCQHTTPSendForwardMsgPayload): Promise<ReturnDataType> {
|
||||
return this.base_handle(payload, ContextMode.Group);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { checkFileExist, uriToLocalFile } from 'snowcat-common/src/file';
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { unlink } from 'node:fs/promises';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const SendGroupNoticePayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
content: Type.String({ description: '公告内容' }),
|
||||
image: Type.Optional(Type.String({ description: '公告图片路径或 URL' })),
|
||||
pinned: Type.Union([Type.Number(), Type.String()], { default: 0, description: '是否置顶 (0/1)' }),
|
||||
type: Type.Union([Type.Number(), Type.String()], { default: 1, description: '类型 (默认为 1)' }),
|
||||
confirm_required: Type.Union([Type.Number(), Type.String()], { default: 1, description: '是否需要确认 (0/1)' }),
|
||||
is_show_edit_card: Type.Union([Type.Number(), Type.String()], { default: 0, description: '是否显示修改群名片引导 (0/1)' }),
|
||||
tip_window_type: Type.Union([Type.Number(), Type.String()], { default: 0, description: '弹窗类型 (默认为 0)' }),
|
||||
});
|
||||
|
||||
export type SendGroupNoticePayload = Static<typeof SendGroupNoticePayloadSchema>;
|
||||
|
||||
export class SendGroupNotice extends OneBotAction<SendGroupNoticePayload, void> {
|
||||
override actionName = ActionName.GoCQHTTP_SendGroupNotice;
|
||||
override payloadSchema = SendGroupNoticePayloadSchema;
|
||||
override returnSchema = Type.Null();
|
||||
override actionSummary = '发送群公告';
|
||||
override actionDescription = '在指定群聊中发布新的公告';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.SendGroupNotice.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.SendGroupNotice.response;
|
||||
|
||||
async _handle (payload: SendGroupNoticePayload) {
|
||||
let UploadImage: { id: string, width: number, height: number; } | undefined;
|
||||
if (payload.image) {
|
||||
// 公告图逻辑
|
||||
const {
|
||||
path,
|
||||
success,
|
||||
isLocal,
|
||||
} = (await uriToLocalFile(this.core.SnowCatTempPath, payload.image));
|
||||
if (!success) {
|
||||
throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`);
|
||||
}
|
||||
if (!path) {
|
||||
throw new Error(`群公告${payload.image}设置失败,获取资源失败`);
|
||||
}
|
||||
await checkFileExist(path, 5000);
|
||||
const ImageUploadResult = await this.core.apis.GroupApi.uploadGroupBulletinPic(payload.group_id.toString(), path);
|
||||
if (ImageUploadResult.errCode !== 0) {
|
||||
throw new Error(`群公告${payload.image}设置失败,图片上传失败 , 错误信息:${ImageUploadResult.errMsg}`);
|
||||
}
|
||||
|
||||
if (!isLocal) unlink(path).catch(() => { });
|
||||
|
||||
UploadImage = ImageUploadResult.picInfo;
|
||||
}
|
||||
const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice(
|
||||
payload.group_id.toString(),
|
||||
payload.content,
|
||||
+payload.pinned,
|
||||
+payload.type,
|
||||
+payload.is_show_edit_card,
|
||||
+payload.tip_window_type,
|
||||
+payload.confirm_required,
|
||||
UploadImage?.id,
|
||||
UploadImage?.width,
|
||||
UploadImage?.height
|
||||
);
|
||||
if (!publishGroupBulletinResult || publishGroupBulletinResult.ec !== 0) {
|
||||
throw new Error(`设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { checkFileExistV2, uriToLocalFile } from 'snowcat-common/src/file';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import fs from 'node:fs/promises';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const SetGroupPortraitPayloadSchema = Type.Object({
|
||||
file: Type.String({ description: '头像文件路径或 URL' }),
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
});
|
||||
|
||||
export type SetGroupPortraitPayload = Static<typeof SetGroupPortraitPayloadSchema>;
|
||||
|
||||
const ReturnSchema = Type.Object({
|
||||
result: Type.Number(),
|
||||
errMsg: Type.String(),
|
||||
}, { description: '设置结果' });
|
||||
|
||||
export type ReturnType = Static<typeof ReturnSchema>;
|
||||
|
||||
export default class SetGroupPortrait extends OneBotAction<SetGroupPortraitPayload, ReturnType> {
|
||||
override actionName = ActionName.SetGroupPortrait;
|
||||
override payloadSchema = SetGroupPortraitPayloadSchema;
|
||||
override returnSchema = ReturnSchema;
|
||||
override actionSummary = '设置群头像';
|
||||
override actionDescription = '修改指定群聊的头像';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.SetGroupPortrait.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.SetGroupPortrait.response;
|
||||
|
||||
async _handle (payload: SetGroupPortraitPayload): Promise<ReturnType> {
|
||||
const { path, success, isLocal } = (await uriToLocalFile(this.core.SnowCatTempPath, payload.file));
|
||||
if (!success) {
|
||||
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
|
||||
}
|
||||
if (path) {
|
||||
await checkFileExistV2(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||
const ret = await this.core.apis.GroupApi.setGroupAvatar(payload.group_id.toString(), path);
|
||||
if (!isLocal) fs.unlink(path).catch(() => { });
|
||||
if (!ret) {
|
||||
throw new Error(`头像${payload.file}设置失败,api无返回`);
|
||||
}
|
||||
if (Number(ret.result) === 1004022) {
|
||||
throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式或权限不足`);
|
||||
} else if (Number(ret.result) !== 0) {
|
||||
throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);
|
||||
}
|
||||
return {
|
||||
result: Number(ret.result),
|
||||
errMsg: ret.errMsg,
|
||||
};
|
||||
} else {
|
||||
throw new Error(`头像${payload.file}设置失败,无法获取头像,文件可能不存在`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const SetQQProfilePayloadSchema = Type.Object({
|
||||
nickname: Type.String({ description: '昵称' }),
|
||||
personal_note: Type.Optional(Type.String({ description: '个性签名' })),
|
||||
sex: Type.Optional(Type.Union([Type.Number(), Type.String()], { description: '性别 (0: 未知, 1: 男, 2: 女)' })), // 传Sex值?建议传0
|
||||
});
|
||||
|
||||
export type SetQQProfilePayload = Static<typeof SetQQProfilePayloadSchema>;
|
||||
export class SetQQProfile extends OneBotAction<SetQQProfilePayload, any> {
|
||||
override actionName = ActionName.SetQQProfile;
|
||||
override payloadSchema = SetQQProfilePayloadSchema;
|
||||
override returnSchema = Type.Any({ description: '设置结果' });
|
||||
override actionSummary = '设置QQ资料';
|
||||
override actionDescription = '修改当前账号的昵称、个性签名等资料';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.SetQQProfile.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.SetQQProfile.response;
|
||||
|
||||
async _handle (payload: SetQQProfilePayload) {
|
||||
const self = this.core.selfInfo;
|
||||
const OldProfile = await this.core.apis.UserApi.getUserDetailInfo(self.uid);
|
||||
return await this.core.apis.UserApi.modifySelfProfile({
|
||||
nick: payload.nickname,
|
||||
longNick: (payload?.personal_note ?? OldProfile?.longNick) || '',
|
||||
sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()),
|
||||
birthday: {
|
||||
birthday_year: OldProfile?.birthday_year!.toString(),
|
||||
birthday_month: OldProfile?.birthday_month!.toString(),
|
||||
birthday_day: OldProfile?.birthday_day!.toString(),
|
||||
},
|
||||
location: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { ChatType, Peer, ElementType } from 'snowcat-core/types';
|
||||
import fs from 'fs';
|
||||
import { uriToLocalFile } from 'snowcat-common/src/file';
|
||||
import { SendMessageContext } from '@/snowcat-onebot/api';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const GoCQHTTPUploadGroupFilePayloadSchema = Type.Object({
|
||||
group_id: Type.String({ description: '群号' }),
|
||||
file: Type.String({ description: '资源路径或URL' }),
|
||||
name: Type.String({ description: '文件名' }),
|
||||
folder: Type.Optional(Type.String({ description: '父目录 ID' })),
|
||||
folder_id: Type.Optional(Type.String({ description: '父目录 ID (兼容性字段)' })), // 临时扩展
|
||||
upload_file: Type.Boolean({ default: true, description: '是否执行上传' }),
|
||||
});
|
||||
|
||||
export type GoCQHTTPUploadGroupFilePayload = Static<typeof GoCQHTTPUploadGroupFilePayloadSchema>;
|
||||
|
||||
export const GoCQHTTPUploadGroupFileReturnSchema = Type.Object({
|
||||
file_id: Type.Union([Type.String(), Type.Null()], { description: '文件 ID' }),
|
||||
});
|
||||
|
||||
export type GoCQHTTPUploadGroupFileResponse = Static<typeof GoCQHTTPUploadGroupFileReturnSchema>;
|
||||
|
||||
export default class GoCQHTTPUploadGroupFile extends OneBotAction<GoCQHTTPUploadGroupFilePayload, GoCQHTTPUploadGroupFileResponse> {
|
||||
override actionName = ActionName.GoCQHTTP_UploadGroupFile;
|
||||
override payloadSchema = GoCQHTTPUploadGroupFilePayloadSchema;
|
||||
override returnSchema = GoCQHTTPUploadGroupFileReturnSchema;
|
||||
override actionSummary = '上传群文件';
|
||||
override actionDescription = '上传资源路径或URL指定的文件到指定群聊的文件系统中';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.UploadGroupFile.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.UploadGroupFile.response;
|
||||
|
||||
async _handle (payload: GoCQHTTPUploadGroupFilePayload): Promise<GoCQHTTPUploadGroupFileResponse> {
|
||||
let file = payload.file;
|
||||
if (fs.existsSync(file)) {
|
||||
file = `file://${file}`;
|
||||
}
|
||||
const downloadResult = await uriToLocalFile(this.core.SnowCatTempPath, file);
|
||||
const peer: Peer = {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_id.toString(),
|
||||
};
|
||||
if (!downloadResult.success) {
|
||||
throw new Error(downloadResult.errMsg);
|
||||
}
|
||||
const msgContext: SendMessageContext = {
|
||||
peer,
|
||||
deleteAfterSentFiles: [],
|
||||
};
|
||||
const sendFileEle = await this.obContext.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id, payload.upload_file, downloadResult.isLocal);
|
||||
if (!downloadResult.isLocal) {
|
||||
msgContext.deleteAfterSentFiles.push(downloadResult.path);
|
||||
}
|
||||
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], msgContext.deleteAfterSentFiles);
|
||||
|
||||
const fileElement = returnMsg.elements.find(ele => ele.elementType === ElementType.FILE);
|
||||
return {
|
||||
file_id: fileElement?.fileElement?.fileUuid || null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { OneBotAction } from '@/snowcat-onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/snowcat-onebot/action/router';
|
||||
import { ChatType, Peer, SendFileElement, ElementType } from 'snowcat-core/types';
|
||||
import fs from 'fs';
|
||||
import { uriToLocalFile } from 'snowcat-common/src/file';
|
||||
import { SendMessageContext } from '@/snowcat-onebot/api';
|
||||
import { ContextMode, createContext } from '@/snowcat-onebot/action/msg/SendMsg';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples';
|
||||
|
||||
export const GoCQHTTPUploadPrivateFilePayloadSchema = Type.Object({
|
||||
user_id: Type.String({ description: '用户 QQ' }),
|
||||
file: Type.String({ description: '资源路径或URL' }),
|
||||
name: Type.String({ description: '文件名' }),
|
||||
upload_file: Type.Boolean({ default: true, description: '是否执行上传' }),
|
||||
});
|
||||
|
||||
export type GoCQHTTPUploadPrivateFilePayload = Static<typeof GoCQHTTPUploadPrivateFilePayloadSchema>;
|
||||
|
||||
export const GoCQHTTPUploadPrivateFileReturnSchema = Type.Object({
|
||||
file_id: Type.Union([Type.String(), Type.Null()], { description: '文件 ID' }),
|
||||
});
|
||||
|
||||
export type GoCQHTTPUploadPrivateFileResponse = Static<typeof GoCQHTTPUploadPrivateFileReturnSchema>;
|
||||
|
||||
export default class GoCQHTTPUploadPrivateFile extends OneBotAction<GoCQHTTPUploadPrivateFilePayload, GoCQHTTPUploadPrivateFileResponse> {
|
||||
override actionName = ActionName.GOCQHTTP_UploadPrivateFile;
|
||||
override payloadSchema = GoCQHTTPUploadPrivateFilePayloadSchema;
|
||||
override returnSchema = GoCQHTTPUploadPrivateFileReturnSchema;
|
||||
override actionSummary = '上传私聊文件';
|
||||
override actionDescription = '上传本地文件到指定私聊会话中';
|
||||
override actionTags = ['Go-CQHTTP'];
|
||||
override payloadExample = GoCQHTTPActionsExamples.UploadPrivateFile.payload;
|
||||
override returnExample = GoCQHTTPActionsExamples.UploadPrivateFile.response;
|
||||
|
||||
async getPeer (payload: GoCQHTTPUploadPrivateFilePayload): Promise<Peer> {
|
||||
if (payload.user_id) {
|
||||
const peerUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||
if (!peerUid) {
|
||||
throw new Error(`私聊${payload.user_id}不存在`);
|
||||
}
|
||||
const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid);
|
||||
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
|
||||
}
|
||||
throw new Error('缺少参数 user_id');
|
||||
}
|
||||
|
||||
async _handle (payload: GoCQHTTPUploadPrivateFilePayload): Promise<GoCQHTTPUploadPrivateFileResponse> {
|
||||
let file = payload.file;
|
||||
if (fs.existsSync(file)) {
|
||||
file = `file://${file}`;
|
||||
}
|
||||
const downloadResult = await uriToLocalFile(this.core.SnowCatTempPath, file);
|
||||
if (!downloadResult.success) {
|
||||
throw new Error(downloadResult.errMsg);
|
||||
}
|
||||
|
||||
const msgContext: SendMessageContext = {
|
||||
peer: await createContext(this.core, {
|
||||
user_id: payload.user_id.toString(),
|
||||
}, ContextMode.Private),
|
||||
deleteAfterSentFiles: [],
|
||||
};
|
||||
const sendFileEle: SendFileElement = await this.obContext.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, '', payload.upload_file, downloadResult.isLocal);
|
||||
if (!downloadResult.isLocal) {
|
||||
msgContext.deleteAfterSentFiles.push(downloadResult.path);
|
||||
}
|
||||
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], msgContext.deleteAfterSentFiles);
|
||||
|
||||
const fileElement = returnMsg.elements.find(ele => ele.elementType === ElementType.FILE);
|
||||
return {
|
||||
file_id: fileElement?.fileElement?.fileUuid || null,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user