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

+51
View File
@@ -0,0 +1,51 @@
import { NapCatCore } from 'napcat-core';
import { NapCatProtocolResponse } from '@/napcat-protocol/types';
// 前向声明类型,避免循环依赖
import type { NapCatProtocolAdapter } from '@/napcat-protocol/index';
// Action 基类
export abstract class BaseAction<PayloadType = unknown, ReturnType = unknown> {
abstract actionName: string;
protected core: NapCatCore;
protected adapter: NapCatProtocolAdapter;
constructor (adapter: NapCatProtocolAdapter, core: NapCatCore) {
this.adapter = adapter;
this.core = core;
}
protected abstract _handle (payload: PayloadType): Promise<ReturnType>;
async handle (payload: PayloadType): Promise<NapCatProtocolResponse<ReturnType>> {
try {
const result = await this._handle(payload);
return {
status: 'ok',
retcode: 0,
data: result,
};
} catch (e) {
return {
status: 'failed',
retcode: -1,
data: null,
message: e instanceof Error ? e.message : String(e),
};
}
}
}
// Action 映射类型
export type ActionMap = Map<string, BaseAction<unknown, unknown>>;
// 创建 Action 映射
export function createActionMap (_adapter: NapCatProtocolAdapter, _core: NapCatCore): ActionMap {
const actionMap = new Map<string, BaseAction<unknown, unknown>>();
// 这里可以注册各种 Action
// 例如: actionMap.set('send_msg', new SendMsgAction(adapter, core));
return actionMap;
}
+33
View File
@@ -0,0 +1,33 @@
import { NapCatCore } from 'napcat-core';
import { NapCatProtocolAdapter } from '@/napcat-protocol/index';
// NapCat Protocol API 基类
export abstract class NapCatProtocolApiBase {
protected adapter: NapCatProtocolAdapter;
protected core: NapCatCore;
constructor (adapter: NapCatProtocolAdapter, core: NapCatCore) {
this.adapter = adapter;
this.core = core;
}
}
// 消息 API
export class NapCatProtocolMsgApi extends NapCatProtocolApiBase {
// 消息相关 API 方法可以在这里实现
}
// 用户 API
export class NapCatProtocolUserApi extends NapCatProtocolApiBase {
// 用户相关 API 方法可以在这里实现
}
// 群组 API
export class NapCatProtocolGroupApi extends NapCatProtocolApiBase {
// 群组相关 API 方法可以在这里实现
}
// 好友 API
export class NapCatProtocolFriendApi extends NapCatProtocolApiBase {
// 好友相关 API 方法可以在这里实现
}
+69
View File
@@ -0,0 +1,69 @@
import { Type, Static } from '@sinclair/typebox';
import { TypeCompiler } from '@sinclair/typebox/compiler';
import { Value } from '@sinclair/typebox/value';
// WebSocket 服务端配置
const WebsocketServerConfigSchema = Type.Object({
name: Type.String({ default: 'napcat-ws-server' }),
enable: Type.Boolean({ default: false }),
host: Type.String({ default: '127.0.0.1' }),
port: Type.Number({ default: 6700 }),
token: Type.String({ default: '' }),
heartInterval: Type.Number({ default: 30000 }),
debug: Type.Boolean({ default: false }),
});
// WebSocket 客户端配置
const WebsocketClientConfigSchema = Type.Object({
name: Type.String({ default: 'napcat-ws-client' }),
enable: Type.Boolean({ default: false }),
url: Type.String({ default: 'ws://localhost:6701' }),
token: Type.String({ default: '' }),
reconnectInterval: Type.Number({ default: 5000 }),
heartInterval: Type.Number({ default: 30000 }),
debug: Type.Boolean({ default: false }),
});
// HTTP 服务端配置
const HttpServerConfigSchema = Type.Object({
name: Type.String({ default: 'napcat-http-server' }),
enable: Type.Boolean({ default: false }),
host: Type.String({ default: '127.0.0.1' }),
port: Type.Number({ default: 6702 }),
token: Type.String({ default: '' }),
enableCors: Type.Boolean({ default: true }),
debug: Type.Boolean({ default: false }),
});
// 网络配置
const NetworkConfigSchema = Type.Object({
httpServers: Type.Array(HttpServerConfigSchema, { default: [] }),
websocketServers: Type.Array(WebsocketServerConfigSchema, { default: [] }),
websocketClients: Type.Array(WebsocketClientConfigSchema, { default: [] }),
}, { default: {} });
// NapCat Protocol 主配置 - 默认关闭
export const NapCatProtocolConfigSchema = Type.Object({
enable: Type.Boolean({ default: false }), // 默认关闭
network: NetworkConfigSchema,
});
export type NapCatProtocolConfig = Static<typeof NapCatProtocolConfigSchema>;
export type HttpServerConfig = Static<typeof HttpServerConfigSchema>;
export type WebsocketServerConfig = Static<typeof WebsocketServerConfigSchema>;
export type WebsocketClientConfig = Static<typeof WebsocketClientConfigSchema>;
export type NetworkAdapterConfig = HttpServerConfig | WebsocketServerConfig | WebsocketClientConfig;
export type NetworkConfigKey = keyof NapCatProtocolConfig['network'];
export function loadConfig (config: Partial<NapCatProtocolConfig>): NapCatProtocolConfig {
let data = config;
data = Value.Parse(NapCatProtocolConfigSchema, data) as Partial<NapCatProtocolConfig>;
const validate = TypeCompiler.Compile(NapCatProtocolConfigSchema);
const valid = validate.Check(data);
if (!valid) {
const errorMsg = [...validate.Errors(data)].map(e => e.message).join(', ');
throw new Error(errorMsg);
}
return data as NapCatProtocolConfig;
}
+11
View File
@@ -0,0 +1,11 @@
import { ConfigBase, NapCatCore } from 'napcat-core';
import { NapCatProtocolConfig, NapCatProtocolConfigSchema } from './config';
export class NapCatProtocolConfigLoader extends ConfigBase<NapCatProtocolConfig> {
constructor (core: NapCatCore, configPath: string) {
super('napcat_protocol', core, configPath, NapCatProtocolConfigSchema);
}
}
export * from './config';
@@ -0,0 +1,66 @@
import { NapCatCore } from 'napcat-core';
// NapCat Protocol 事件基类
export abstract class NapCatProtocolEvent {
protected core: NapCatCore;
public time: number;
public self_id: number;
public post_type: string;
constructor (core: NapCatCore) {
this.core = core;
this.time = Math.floor(Date.now() / 1000);
this.self_id = parseInt(core.selfInfo.uin);
this.post_type = 'event';
}
abstract toJSON (): Record<string, unknown>;
}
// 消息事件基类
export abstract class NapCatProtocolMessageEvent extends NapCatProtocolEvent {
public message_type: 'private' | 'group';
public message_id: number;
public user_id: number;
constructor (core: NapCatCore, messageType: 'private' | 'group', messageId: number, userId: number) {
super(core);
this.post_type = 'message';
this.message_type = messageType;
this.message_id = messageId;
this.user_id = userId;
}
}
// 通知事件基类
export abstract class NapCatProtocolNoticeEvent extends NapCatProtocolEvent {
public notice_type: string;
constructor (core: NapCatCore, noticeType: string) {
super(core);
this.post_type = 'notice';
this.notice_type = noticeType;
}
}
// 请求事件基类
export abstract class NapCatProtocolRequestEvent extends NapCatProtocolEvent {
public request_type: string;
constructor (core: NapCatCore, requestType: string) {
super(core);
this.post_type = 'request';
this.request_type = requestType;
}
}
// 元事件基类
export abstract class NapCatProtocolMetaEvent extends NapCatProtocolEvent {
public meta_event_type: string;
constructor (core: NapCatCore, metaEventType: string) {
super(core);
this.post_type = 'meta_event';
this.meta_event_type = metaEventType;
}
}
+1
View File
@@ -0,0 +1 @@
export * from './NapCatProtocolEvent';
+104
View File
@@ -0,0 +1,104 @@
import {
InstanceContext,
NapCatCore,
} from 'napcat-core';
import { NapCatProtocolConfigLoader, NapCatProtocolConfig } from '@/napcat-protocol/config';
import { NapCatPathWrapper } from 'napcat-common/src/path';
import {
NapCatProtocolNetworkManager,
} from '@/napcat-protocol/network';
import {
NapCatProtocolMsgApi,
NapCatProtocolUserApi,
NapCatProtocolGroupApi,
NapCatProtocolFriendApi,
} from '@/napcat-protocol/api';
import { ActionMap, createActionMap } from '@/napcat-protocol/action';
interface ApiListType {
MsgApi: NapCatProtocolMsgApi;
UserApi: NapCatProtocolUserApi;
GroupApi: NapCatProtocolGroupApi;
FriendApi: NapCatProtocolFriendApi;
}
// NapCat Protocol 适配器 - NapCat 私有 Bot 协议实现
export class NapCatProtocolAdapter {
readonly core: NapCatCore;
readonly context: InstanceContext;
configLoader: NapCatProtocolConfigLoader;
public apis: ApiListType;
networkManager: NapCatProtocolNetworkManager;
actions: ActionMap;
constructor (core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
this.core = core;
this.context = context;
this.configLoader = new NapCatProtocolConfigLoader(core, pathWrapper.configPath);
this.apis = {
MsgApi: new NapCatProtocolMsgApi(this, core),
UserApi: new NapCatProtocolUserApi(this, core),
GroupApi: new NapCatProtocolGroupApi(this, core),
FriendApi: new NapCatProtocolFriendApi(this, core),
} as const;
this.actions = createActionMap(this, core);
this.networkManager = new NapCatProtocolNetworkManager();
}
// 检查协议是否启用
isEnabled (): boolean {
return this.configLoader.configData.enable;
}
async createProtocolLog (config: NapCatProtocolConfig) {
let log = '[NapCat Protocol] 配置加载\n';
log += `协议状态: ${config.enable ? '已启用' : '已禁用'}\n`;
if (config.enable) {
for (const key of config.network.httpServers) {
log += `HTTP服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
for (const key of config.network.websocketServers) {
log += `WebSocket服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
for (const key of config.network.websocketClients) {
log += `WebSocket客户端: ${key.url}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
}
return log;
}
async initProtocol () {
const config = this.configLoader.configData;
// 如果协议未启用,直接返回
if (!config.enable) {
this.context.logger.log('[NapCat Protocol] 协议未启用,跳过初始化');
return;
}
const selfInfo = this.core.selfInfo;
const serviceInfo = await this.createProtocolLog(config);
this.context.logger.log(`[Notice] ${serviceInfo}`);
// 注册网络适配器
// 这里可以根据配置注册不同的网络适配器
// 例如: WebSocket Server, WebSocket Client, HTTP Server 等
await this.networkManager.openAllAdapters();
this.context.logger.log(`[NapCat Protocol] 初始化完成,Bot: ${selfInfo.uin}`);
}
async close () {
await this.networkManager.closeAllAdapters();
this.context.logger.log('[NapCat Protocol] 已关闭所有网络适配器');
}
}
export * from './types/index';
export * from './api/index';
export * from './event/index';
export * from './config/index';
export * from './network/index';
@@ -0,0 +1,37 @@
import { NetworkAdapterConfig } from '@/napcat-protocol/config/config';
import { LogWrapper } from 'napcat-core/helper/log';
import { NapCatCore } from 'napcat-core';
import { NapCatProtocolAdapter } from '@/napcat-protocol/index';
import { ActionMap } from '@/napcat-protocol/action';
import { NapCatProtocolEmitEventContent, NapCatProtocolNetworkReloadType } from '@/napcat-protocol/network/index';
export abstract class INapCatProtocolNetworkAdapter<CT extends NetworkAdapterConfig> {
name: string;
isEnable: boolean = false;
config: CT;
readonly logger: LogWrapper;
readonly core: NapCatCore;
readonly protocolContext: NapCatProtocolAdapter;
readonly actions: ActionMap;
constructor (name: string, config: CT, core: NapCatCore, protocolContext: NapCatProtocolAdapter, actions: ActionMap) {
this.name = name;
this.config = structuredClone(config);
this.core = core;
this.protocolContext = protocolContext;
this.actions = actions;
this.logger = core.context.logger;
}
abstract onEvent<T extends NapCatProtocolEmitEventContent> (event: T): Promise<void>;
abstract open (): void | Promise<void>;
abstract close (): void | Promise<void>;
abstract reload (config: unknown): NapCatProtocolNetworkReloadType | Promise<NapCatProtocolNetworkReloadType>;
get isActive (): boolean {
return this.isEnable;
}
}
+112
View File
@@ -0,0 +1,112 @@
import { NapCatProtocolEvent } from '@/napcat-protocol/event/NapCatProtocolEvent';
import { NapCatProtocolMessage } from '@/napcat-protocol/types';
import { NetworkAdapterConfig } from '@/napcat-protocol/config/config';
import { INapCatProtocolNetworkAdapter } from '@/napcat-protocol/network/adapter';
export type NapCatProtocolEmitEventContent = NapCatProtocolEvent | NapCatProtocolMessage;
export enum NapCatProtocolNetworkReloadType {
Normal = 0,
ConfigChange = 1,
NetWorkReload = 2,
NetWorkClose = 3,
NetWorkOpen = 4,
}
export class NapCatProtocolNetworkManager {
adapters: Map<string, INapCatProtocolNetworkAdapter<NetworkAdapterConfig>> = new Map();
async openAllAdapters () {
return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.open()));
}
async emitEvent (event: NapCatProtocolEmitEventContent) {
return Promise.all(Array.from(this.adapters.values()).map(async adapter => {
if (adapter.isActive) {
return await adapter.onEvent(event);
}
}));
}
async emitEvents (events: NapCatProtocolEmitEventContent[]) {
return Promise.all(events.map(event => this.emitEvent(event)));
}
async emitEventByName (names: string[], event: NapCatProtocolEmitEventContent) {
return Promise.all(names.map(async name => {
const adapter = this.adapters.get(name);
if (adapter && adapter.isActive) {
return await adapter.onEvent(event);
}
}));
}
async emitEventByNames (map: Map<string, NapCatProtocolEmitEventContent>) {
return Promise.all(Array.from(map.entries()).map(async ([name, event]) => {
const adapter = this.adapters.get(name);
if (adapter && adapter.isActive) {
return await adapter.onEvent(event);
}
}));
}
registerAdapter<CT extends NetworkAdapterConfig> (adapter: INapCatProtocolNetworkAdapter<CT>) {
this.adapters.set(adapter.name, adapter);
}
async registerAdapterAndOpen<CT extends NetworkAdapterConfig> (adapter: INapCatProtocolNetworkAdapter<CT>) {
this.registerAdapter(adapter);
await adapter.open();
}
async closeSomeAdapters<CT extends NetworkAdapterConfig> (adaptersToClose: INapCatProtocolNetworkAdapter<CT>[]) {
for (const adapter of adaptersToClose) {
this.adapters.delete(adapter.name);
await adapter.close();
}
}
async closeSomeAdapterWhenOpen<CT extends NetworkAdapterConfig> (adaptersToClose: INapCatProtocolNetworkAdapter<CT>[]) {
for (const adapter of adaptersToClose) {
this.adapters.delete(adapter.name);
if (adapter.isEnable) {
await adapter.close();
}
}
}
findSomeAdapter (name: string) {
return this.adapters.get(name);
}
async closeAdapterByPredicate (closeFilter: (adapter: INapCatProtocolNetworkAdapter<NetworkAdapterConfig>) => boolean) {
const adaptersToClose = Array.from(this.adapters.values()).filter(closeFilter);
await this.closeSomeAdapters(adaptersToClose);
}
async closeAllAdapters () {
await Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.close()));
this.adapters.clear();
}
async reloadAdapter<T> (name: string, config: T) {
const adapter = this.adapters.get(name);
if (adapter) {
await adapter.reload(config);
}
}
async reloadSomeAdapters<T> (configMap: Map<string, T>) {
await Promise.all(Array.from(configMap.entries()).map(([name, config]) => this.reloadAdapter(name, config)));
}
hasActiveAdapters (): boolean {
return Array.from(this.adapters.values()).some(adapter => adapter.isActive);
}
async getAllConfig () {
return Array.from(this.adapters.values()).map(adapter => adapter.config);
}
}
export * from './adapter';
+35
View File
@@ -0,0 +1,35 @@
{
"name": "napcat-protocol",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "index.ts",
"scripts": {
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {
".": {
"import": "./index.ts"
},
"./*": {
"import": "./*"
}
},
"dependencies": {
"@sinclair/typebox": "^0.34.38",
"cors": "^2.8.5",
"express": "^5.0.0",
"json5": "^2.2.3",
"napcat-common": "workspace:*",
"napcat-core": "workspace:*",
"ws": "^8.18.3"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/node": "^22.0.1"
},
"engines": {
"node": ">=18.0.0"
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
+70
View File
@@ -0,0 +1,70 @@
// NapCat Protocol 消息类型定义
export interface NapCatProtocolMessage {
post_type: 'message' | 'notice' | 'request' | 'meta_event';
time: number;
self_id: number;
message_type?: 'private' | 'group';
sub_type?: string;
message_id?: number;
user_id?: number;
group_id?: number;
message?: NapCatProtocolMessageSegment[] | string;
raw_message?: string;
sender?: NapCatProtocolSender;
}
export interface NapCatProtocolMessageSegment {
type: string;
data: Record<string, unknown>;
}
export interface NapCatProtocolSender {
user_id: number;
nickname: string;
card?: string;
sex?: 'male' | 'female' | 'unknown';
age?: number;
area?: string;
level?: string;
role?: 'owner' | 'admin' | 'member';
title?: string;
}
// API 请求类型
export interface NapCatProtocolRequest {
action: string;
params?: Record<string, unknown>;
echo?: string | number;
}
// API 响应类型
export interface NapCatProtocolResponse<T = unknown> {
status: 'ok' | 'failed';
retcode: number;
data: T | null;
message?: string;
echo?: string | number;
}
// 心跳事件
export interface NapCatProtocolHeartbeat {
post_type: 'meta_event';
meta_event_type: 'heartbeat';
time: number;
self_id: number;
status: {
online: boolean;
good: boolean;
};
interval: number;
}
// 生命周期事件
export interface NapCatProtocolLifecycle {
post_type: 'meta_event';
meta_event_type: 'lifecycle';
time: number;
self_id: number;
sub_type: 'connect' | 'enable' | 'disable';
}