Core Architecture
NapCat is built on a modular architecture that wraps NTQQ (the native QQ protocol client) with a clean API layer. The architecture consists of several key components working together to provide a stable bot framework.
NapCatCore Class
The NapCatCore class is the heart of NapCat, managing all core functionality and API services.
export class NapCatCore {
readonly context : InstanceContext ;
readonly eventWrapper : NTEventWrapper ;
event = appEvent ;
apis : StableNTApiWrapper ;
selfInfo : SelfInfo ;
util : NodeQQNTWrapperUtil ;
configLoader : NapCatConfigLoader ;
}
Key responsibilities:
Initialize and manage API wrappers
Handle event system lifecycle
Manage configuration and logging
Coordinate packet handling
Maintain self information and online status
Source: packages/napcat-core/index.ts:113
InstanceContext
The InstanceContext interface provides runtime dependencies to all components:
export interface InstanceContext {
readonly workingEnv : NapCatCoreWorkingEnv ;
readonly wrapper : WrapperNodeApi ;
readonly session : NodeIQQNTWrapperSession ;
readonly logger : LogWrapper ;
readonly basicInfoWrapper : QQBasicInfoWrapper ;
readonly pathWrapper : NapCatPathWrapper ;
readonly packetHandler : NativePacketHandler ;
readonly napi2nativeLoader : Napi2NativeLoader ;
}
This design ensures:
Clean dependency injection
Consistent access to shared resources
Environment-aware behavior
Easy testing and mocking
Source: packages/napcat-core/index.ts:332
API Wrappers
NTQQMsgApi Message sending, receiving, recall, multi-message, emoji reactions
NTQQGroupApi Group management, member operations, file handling, notifications
NTQQFriendApi Friend list, requests, profile management
NTQQUserApi User info, UID/UIN conversion, profile details
NTQQFileApi File uploads, downloads, image/video handling
NTQQSystemApi System utilities, status, platform info
NTQQPacketApi Low-level packet sending and protocol access
NTQQDatabaseApi SQLite database access with encryption support
All API wrappers are initialized in the NapCatCore constructor:
this . apis = {
FileApi: new NTQQFileApi ( this . context , this ),
SystemApi: new NTQQSystemApi ( this . context , this ),
PacketApi: new NTQQPacketApi ( this . context , this ),
WebApi: new NTQQWebApi ( this . context , this ),
FriendApi: new NTQQFriendApi ( this . context , this ),
MsgApi: new NTQQMsgApi ( this . context , this ),
UserApi: new NTQQUserApi ( this . context , this ),
GroupApi: new NTQQGroupApi ( this . context , this ),
FlashApi: new NTQQFlashApi ( this . context , this ),
OnlineApi: new NTQQOnlineApi ( this . context , this ),
DatabaseApi: new NTQQDatabaseApi ( this . context , this ),
};
Source: packages/napcat-core/index.ts:144
Example: NTQQMsgApi
The Message API provides comprehensive message handling:
export class NTQQMsgApi {
context : InstanceContext ;
core : NapCatCore ;
// Send messages
async sendMsg ( peer : Peer , elements : SendMessageElement [], timeout ?: number )
// Get messages
async getMsgsByMsgId ( peer : Peer , msgIds : string [])
async getMultiMsg ( peer : Peer , rootMsgId : string , parentMsgId : string )
// Emoji reactions
async setEmojiLike ( peer : Peer , msgSeq : string , emojiId : string , set : boolean )
async getMsgEmojiLikesList ( peer : Peer , msgSeq : string , emojiId : string )
// Forward and recall
async ForwardMsg ( peer : Peer , msgIds : string [])
async recallMsg ( peer : Peer , msgIds : string [])
}
Source: packages/napcat-core/apis/msg.ts:5
Event System
NapCat uses a sophisticated event system built on multiple layers:
NTEventWrapper
Wraps NTQQ’s native event system with a Promise-based listener API:
export class NTEventWrapper {
// Register event listener with timeout and filter
async registerListen < EventName >(
eventName : string ,
filter : ( payload : EventPayload ) => boolean ,
count : number = 1 ,
timeout : number = 5000
) : Promise < EventPayload []>
}
This allows waiting for specific events with conditions:
const [ msgs ] = await core . eventWrapper . registerListen (
'NodeIKernelMsgListener/onMsgInfoListUpdate' ,
( msgList : RawMessage []) => {
return msgList . find ( e => e . msgId === targetMsgId );
},
1 ,
10000 // 10 second timeout
);
TypedEventEmitter
Provides type-safe event emission throughout NapCat:
export class TypedEventEmitter < Events extends Record < string , any >> {
emit < K extends keyof Events >( event : K , data : Events [ K ]) : void
on < K extends keyof Events >( event : K , listener : ( data : Events [ K ]) => void ) : void
}
Used for high-level events like:
KickedOffLine - Bot was kicked offline
PacketReceived - Protocol packet received
Custom packet events from services
Source: packages/napcat-core/packet/handler/typeEvent.ts
Kernel Listeners
NapCat registers listeners with NTQQ’s kernel services:
const msgListener = new NodeIKernelMsgListener ();
msgListener . onRecvMsg = ( msgs : RawMessage []) => {
msgs . forEach ( msg => this . context . logger . logMessage ( msg , this . selfInfo ));
};
msgListener . onKickedOffLine = ( Info : KickedOffLineInfo ) => {
this . selfInfo . online = false ;
this . event . emit ( 'KickedOffLine' , tips );
};
this . context . session . getMsgService (). addKernelMsgListener (
proxiedListenerOf ( msgListener , this . context . logger )
);
Source: packages/napcat-core/index.ts:225
Packet Handler
The NativePacketHandler provides low-level protocol packet interception:
Packet handling requires native modules and may not be available on all platforms.
Features
Bidirectional monitoring : Intercept both sent and received packets
Flexible filtering : Listen by packet type, command, or exact match
One-time listeners : Support for single-use event handlers
Type safety : Full TypeScript support
Listener API
export class NativePacketHandler {
// Listen to all packets
onAll ( callback : PacketCallback ) : () => void
// Listen by type (0=send, 1=recv)
onType ( type : PacketType , callback : PacketCallback ) : () => void
onSend ( callback : PacketCallback ) : () => void
onRecv ( callback : PacketCallback ) : () => void
// Listen by command name
onCmd ( cmd : string , callback : PacketCallback ) : () => void
// Exact match (type + cmd)
onExact ( type : PacketType , cmd : string , callback : PacketCallback ) : () => void
// One-time listeners
onceCmd ( cmd : string , callback : PacketCallback ) : () => void
onceExact ( type : PacketType , cmd : string , callback : PacketCallback ) : () => void
}
Source: packages/napcat-core/packet/handler/client.ts:28
Example Usage
// Listen for database passphrase packet
nativePacketHandler . onCmd ( 'OidbSvcTrpcTcp.0xcde_2' , ({ type , hex_data }) => {
if ( type !== 1 ) return ; // Only handle received packets
const raw = Buffer . from ( hex_data , 'hex' );
const base = new NapProtoMsg ( OidbSvcTrpcTcpBase ). decode ( raw );
if ( base . body && base . body . length > 0 ) {
const body = new NapProtoMsg ( OidbSvcTrpcTcp0XCDE_2RespBody ). decode ( base . body );
if ( body . inner ?. value ) {
dbPassphrase = body . inner . value ;
logger . log ( '[NapCat] Database support enabled' );
}
}
});
Source: packages/napcat-framework/napcat.ts:68
Service Registry
NapCat uses dependency injection for packet service handlers:
import { container , ReceiverServiceRegistry } from './packet/handler/serviceRegister' ;
container . bind ( NapCatCore ). toConstantValue ( this );
container . bind ( TypedEventEmitter ). toConstantValue ( this . event );
ReceiverServiceRegistry . forEach (( ServiceClass , serviceName ) => {
container . bind ( ServiceClass ). toSelf ();
this . context . packetHandler . onCmd ( serviceName , ({ seq , hex_data }) => {
const serviceInstance = container . get ( ServiceClass );
return serviceInstance . handler ( seq , hex_data );
});
});
This pattern enables:
Automatic service registration
Clean separation of concerns
Easy testing and mocking
Protocol version compatibility
Source: packages/napcat-core/index.ts:158
Initialization Flow
The initialization process:
Constructor phase : Create API wrappers, bind services
initCore phase : Setup filesystem, sync time, init APIs, register listeners
Ready : System is ready to handle messages and events
Source: packages/napcat-core/index.ts:170
Best Practices
Use InstanceContext for shared resources
Always access logger, session, and paths through context rather than global variables. // Good
this . context . logger . log ( 'Message' );
// Bad
console . log ( 'Message' );
Handle async operations properly
NTQQ APIs are async and may timeout. Always use try-catch and timeouts. try {
const result = await core . apis . MsgApi . sendMsg ( peer , elements , 5000 );
} catch ( error ) {
logger . logError ( 'Send failed:' , error );
}
Register packet listeners early
Some packets (like database passphrase) are only sent during login. Register handlers before initCore(). // Register before login
packetHandler . onCmd ( 'OidbSvcTrpcTcp.0xcde_2' , handler );
// Then initialize
await core . initCore ();
Always store and clean up listener removal functions to prevent memory leaks. const removeListener = packetHandler . onCmd ( 'command' , callback );
// Later...
removeListener ();
Working Environments Learn about Shell vs Framework modes
OneBot Protocol Understand the OneBot 11 implementation
Adapters Configure network adapters