NapCatQQ source snapshot
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
Release Tag / validate-version (push) Waiting to run

This commit is contained in:
TanTan committed 2026-11-13 09:01:47 +08:00
commit f954252a34
1111 files changed
+139867

No files matched your search

@@ -0,0 +1,40 @@
import { MessageUnique } from 'napcat-common/src/message-unique';
import { ChatType, Peer } from 'napcat-core';
import { Static, Type } from '@sinclair/typebox';
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
export const GroupTodoPayloadSchema = Type.Object({
group_id: Type.Union([Type.String(), Type.Number()], { description: '群号' }),
message_id: Type.Optional(Type.String({ description: '消息ID' })),
message_seq: Type.Optional(Type.String({ description: '消息Seq (可选)' })),
});
export type GroupTodoPayload = Static<typeof GroupTodoPayloadSchema>;
export abstract class BaseGroupTodoAction extends GetPacketStatusDepends<GroupTodoPayload, void> {
override payloadSchema = GroupTodoPayloadSchema;
override returnSchema = Type.Null();
override actionTags = ['核心接口'];
protected abstract handleGroupTodo (groupId: number, msgSeq: string): Promise<void>;
async _handle (payload: GroupTodoPayload) {
const groupId = +payload.group_id;
if (payload.message_seq) {
return await this.handleGroupTodo(groupId, payload.message_seq.toString());
}
if (!payload.message_id) {
throw new Error('缺少参数 message_id 或 message_seq');
}
const peer: Peer = {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
};
const { MsgId, Peer } = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id) ?? { Peer: peer, MsgId: payload.message_id.toString() };
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(Peer, [MsgId])).msgList[0];
if (!msg) {
throw new Error('消息不存在');
}
await this.handleGroupTodo(groupId, msg.msgSeq);
}
}
@@ -0,0 +1,15 @@
import { ActionName } from '../router';
import { PacketActionsExamples } from '../example/PacketActionsExamples';
import { BaseGroupTodoAction } from './BaseGroupTodoAction';
export class CancelGroupTodo extends BaseGroupTodoAction {
override actionName = ActionName.CancelGroupTodo;
override actionSummary = '取消群待办';
override actionDescription = '将指定消息对应的群待办取消';
override payloadExample = PacketActionsExamples.CancelGroupTodo.payload;
override returnExample = PacketActionsExamples.CancelGroupTodo.response;
protected override async handleGroupTodo (groupId: number, msgSeq: string) {
await this.core.apis.PacketApi.pkt.operation.CancelGroupTodo(groupId, msgSeq);
}
}
@@ -0,0 +1,15 @@
import { ActionName } from '../router';
import { PacketActionsExamples } from '../example/PacketActionsExamples';
import { BaseGroupTodoAction } from './BaseGroupTodoAction';
export class CompleteGroupTodo extends BaseGroupTodoAction {
override actionName = ActionName.CompleteGroupTodo;
override actionSummary = '完成群待办';
override actionDescription = '将指定消息对应的群待办标记为已完成';
override payloadExample = PacketActionsExamples.CompleteGroupTodo.payload;
override returnExample = PacketActionsExamples.CompleteGroupTodo.response;
protected override async handleGroupTodo (groupId: number, msgSeq: string) {
await this.core.apis.PacketApi.pkt.operation.CompleteGroupTodo(groupId, msgSeq);
}
}
@@ -0,0 +1,48 @@
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
import { ActionName, BaseCheckResult } from '@/napcat-onebot/action/router';
import { Type } from '@sinclair/typebox';
export abstract class GetPacketStatusDepends<PT, RT> extends OneBotAction<PT, RT> {
protected override async check (payload: PT): Promise<BaseCheckResult> {
const schemaResult = await super.check(payload);
if (!schemaResult.valid) return schemaResult;
if (!this.core.apis.PacketApi.packetStatus) {
return {
valid: false,
message: 'packetBackend不可用,请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置!' +
'错误堆栈信息:' + this.core.apis.PacketApi.clientLogStack,
};
}
return { valid: true };
}
}
export abstract class PacketSendAvailableDepends<PT, RT> extends OneBotAction<PT, RT> {
protected override async check (payload: PT): Promise<BaseCheckResult> {
const schemaResult = await super.check(payload);
if (!schemaResult.valid) return schemaResult;
if (!this.core.apis.PacketApi.available) {
return {
valid: false,
message: 'packetBackend发包能力不可用,请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置!' +
'错误堆栈信息:' + this.core.apis.PacketApi.clientLogStack,
};
}
return { valid: true };
}
}
export class GetPacketStatus extends GetPacketStatusDepends<void, void> {
override actionName = ActionName.GetPacketStatus;
override payloadSchema = Type.Object({});
override returnSchema = Type.Null();
override actionSummary = '获取Packet状态';
override actionDescription = '获取底层Packet服务的运行状态';
override actionTags = ['系统接口'];
override payloadExample = {};
override returnExample = null;
async _handle () {
}
}
@@ -0,0 +1,41 @@
import { ActionName } from '@/napcat-onebot/action/router';
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
import { Type, Static } from '@sinclair/typebox';
export const GetRkeyExReturnSchema = Type.Array(Type.Object({
type: Type.String({ description: '类型 (private/group)' }),
rkey: Type.String({ description: 'RKey' }),
created_at: Type.Number({ description: '创建时间' }),
ttl: Type.Number({ description: '有效期' }),
}), { description: 'RKey 列表' });
export type GetRkeyExReturn = Static<typeof GetRkeyExReturnSchema>;
export class GetRkeyEx extends GetPacketStatusDepends<void, GetRkeyExReturn> {
override actionName = ActionName.GetRkeyEx;
override payloadSchema = Type.Object({});
override returnSchema = GetRkeyExReturnSchema;
override actionSummary = '获取扩展 RKey';
override actionTags = ['系统扩展'];
override payloadExample = {};
override returnExample = [
{
type: 'private',
rkey: 'rkey_123',
created_at: 1734567890,
ttl: 3600,
},
];
async _handle () {
const rkeys = await this.core.apis.PacketApi.pkt.operation.FetchRkey();
return rkeys.map(rkey => {
return {
type: rkey.type === 10 ? 'private' : 'group',
rkey: rkey.rkey,
created_at: Number(rkey.time),
ttl: Number(rkey.ttl),
};
});
}
}
@@ -0,0 +1,56 @@
import { ActionName } from '@/napcat-onebot/action/router';
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
import { Type, Static } from '@sinclair/typebox';
export const GetRkeyServerReturnSchema = Type.Object({
private_rkey: Type.Optional(Type.String({ description: '私聊 RKey' })),
group_rkey: Type.Optional(Type.String({ description: '群聊 RKey' })),
expired_time: Type.Optional(Type.Number({ description: '过期时间' })),
name: Type.String({ description: '名称' }),
});
export type GetRkeyServerReturn = Static<typeof GetRkeyServerReturnSchema>;
export class GetRkeyServer extends GetPacketStatusDepends<void, GetRkeyServerReturn> {
override actionName = ActionName.GetRkeyServer;
override actionSummary = '获取 RKey 服务器';
override actionTags = ['系统扩展'];
override payloadExample = {};
override returnExample = {
private_rkey: '&rkey=123456789',
group_rkey: '&rkey=123456789',
expired_time: 1694560000,
name: 'NapCat 4',
};
override payloadSchema = Type.Object({});
override returnSchema = GetRkeyServerReturnSchema;
private rkeyCache: GetRkeyServerReturn | null = null;
private expiryTime: number | null = null;
async _handle () {
// 检查缓存是否有效
if (this.expiryTime && this.expiryTime > Math.floor(Date.now() / 1000) && this.rkeyCache) {
return this.rkeyCache;
}
// 获取新的 Rkey
const rkeys = await this.core.apis.PacketApi.pkt.operation.FetchRkey();
const privateRkeyItem = rkeys.filter(rkey => rkey.type === 10)[0];
const groupRkeyItem = rkeys.filter(rkey => rkey.type === 20)[0];
this.expiryTime = Math.floor(Date.now() / 1000) + Math.min(+groupRkeyItem!.ttl.toString(), +privateRkeyItem!.ttl.toString());
// 更新缓存
this.rkeyCache = {
private_rkey: privateRkeyItem ? privateRkeyItem.rkey : undefined,
group_rkey: groupRkeyItem ? groupRkeyItem.rkey : undefined,
expired_time: this.expiryTime,
name: 'NapCat 4',
};
return this.rkeyCache;
}
}
@@ -0,0 +1,45 @@
import { ActionName } from '@/napcat-onebot/action/router';
import { PacketSendAvailableDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox';
import { PacketActionsExamples } from '../example/PacketActionsExamples';
export const SendPokePayloadSchema = Type.Object({
group_id: Type.Optional(Type.String({ description: '群号' })),
user_id: Type.String({ description: '用户QQ' }),
target_id: Type.Optional(Type.String({ description: '目标QQ' })),
});
export type SendPokePayload = Static<typeof SendPokePayloadSchema>;
export class SendPokeBase extends PacketSendAvailableDepends<SendPokePayload, void> {
override payloadSchema = SendPokePayloadSchema;
override returnSchema = Type.Null();
override actionSummary = '发送戳一戳';
override actionDescription = '在群聊或私聊中发送戳一戳动作';
override actionTags = ['核心接口'];
override payloadExample = PacketActionsExamples.SendPoke.payload;
override returnExample = PacketActionsExamples.SendPoke.response;
async _handle (payload: SendPokePayload) {
// 这里的 !! 可以传入空字符串 忽略这些数据有利用接口统一接口
const target_id = payload.target_id?.toString() || payload.user_id?.toString();
const peer_id = payload.group_id?.toString() || payload.user_id?.toString();
const is_group = !!payload.group_id;
if (!target_id || !peer_id) {
throw new Error('请检查参数,缺少 user_id 或 group_id');
}
await this.core.apis.PacketApi.pkt.operation.SendPoke(is_group, +peer_id, +target_id);
}
}
export class SendPoke extends SendPokeBase {
override actionName = ActionName.SendPoke;
}
export class GroupPoke extends SendPokeBase {
override actionName = ActionName.GroupPoke;
}
export class FriendPoke extends SendPokeBase {
override actionName = ActionName.FriendPoke;
}
@@ -0,0 +1,14 @@
import { ActionName } from '../router';
import { PacketActionsExamples } from '../example/PacketActionsExamples';
import { BaseGroupTodoAction } from './BaseGroupTodoAction';
export class SetGroupTodo extends BaseGroupTodoAction {
override actionName = ActionName.SetGroupTodo;
override actionSummary = '设置群待办';
override actionDescription = '将指定消息设置为群待办';
override payloadExample = PacketActionsExamples.SetGroupTodo.payload;
override returnExample = PacketActionsExamples.SetGroupTodo.response;
protected override async handleGroupTodo (groupId: number, msgSeq: string) {
await this.core.apis.PacketApi.pkt.operation.SetGroupTodo(groupId, msgSeq);
}
}