Overview
NapCat provides comprehensive message handling capabilities through the NTQQMsgApi and OneBotMsgApi layers. This guide covers sending messages, receiving messages, and working with different message segment types.
Message Architecture
NapCat uses a two-layer message system:
Raw Message Layer (NTQQMsgApi): Direct NT QQ protocol messages from msg.ts:1-314
OneBot Layer (OneBotMsgApi): OneBot 11 standard compatibility from napcat-onebot/api/msg.ts
Sending Messages
Basic Send Message
From msg.ts:232-264:
const peer : Peer = {
chatType: ChatType . KCHATTYPEGROUP , // or KCHATTYPEC2C for private
peerUid: '123456789' , // Group ID or user UID
guildId: '' ,
};
const msgElements : SendMessageElement [] = [
{
elementType: ElementType . TEXT ,
elementId: '' ,
textElement: {
content: 'Hello, World!' ,
atType: NTMsgAtType . ATTYPEUNKNOWN ,
atUid: '' ,
atTinyId: '' ,
atNtUid: '' ,
},
},
];
const returnMsg = await core . apis . MsgApi . sendMsg ( peer , msgElements , 10000 );
console . log ( 'Message sent:' , returnMsg . msgId );
Generate Unique Message ID
Before sending, generate a unique message ID:
const msgId = await core . apis . MsgApi . generateMsgUniqueId ( peer . chatType );
peer . guildId = msgId ; // Used for message tracking
Send with Timeout
const timeout = 10000 + ( totalSize / 1024 / 256 * 1000 );
const returnMsg = await core . apis . MsgApi . sendMsg ( peer , msgElements , timeout );
Message Segments
NapCat supports various message segment types (CQ codes in OneBot format).
Text Messages
// Raw format
const textElement : SendMessageElement = {
elementType: ElementType . TEXT ,
elementId: '' ,
textElement: {
content: 'Hello, World!' ,
atType: NTMsgAtType . ATTYPEUNKNOWN ,
atUid: '' ,
atTinyId: '' ,
atNtUid: '' ,
},
};
At Mentions
// At specific user
const atElement : SendMessageElement = {
elementType: ElementType . TEXT ,
elementId: '' ,
textElement: {
content: '@User' ,
atType: NTMsgAtType . ATTYPEONE ,
atUid: '123456789' ,
atNtUid: 'user_uid_here' ,
},
};
// At all members
const atAllElement : SendMessageElement = {
elementType: ElementType . TEXT ,
elementId: '' ,
textElement: {
content: '@全体成员' ,
atType: NTMsgAtType . ATTYPEALL ,
atUid: 'all' ,
atNtUid: 'all' ,
},
};
OneBot format:
{
"type" : "at" ,
"data" : {
"qq" : "123456789" // or "all" for @everyone
}
}
Images
Images are processed through createValidSendPicElement which handles local files, URLs, and base64 data.
// Send image
const imageElement = await obContext . apis . FileApi . createValidSendPicElement (
context ,
'/path/to/image.jpg' ,
'Image description' ,
0 // sub_type: 0=normal, 1=表情包
);
OneBot format:
{
"type" : "image" ,
"data" : {
"file" : "file:///path/to/image.jpg" ,
"summary" : "Image description" ,
"sub_type" : 0
}
}
Video
const videoElement = await obContext . apis . FileApi . createValidSendVideoElement (
context ,
'/path/to/video.mp4' ,
'video.mp4' ,
'/path/to/thumb.jpg' // Optional thumbnail
);
OneBot format:
{
"type" : "video" ,
"data" : {
"file" : "file:///path/to/video.mp4" ,
"thumb" : "/path/to/thumb.jpg"
}
}
Voice (PTT)
const voiceElement = await obContext . apis . FileApi . createValidSendPttElement (
context ,
'/path/to/audio.silk'
);
OneBot format:
{
"type" : "voice" ,
"data" : {
"file" : "file:///path/to/audio.silk"
}
}
Reply Messages
From napcat-onebot/api/msg.ts:642-682:
const replyElement : SendMessageElement = {
elementType: ElementType . REPLY ,
elementId: '' ,
replyElement: {
replayMsgSeq: replyMsg . msgSeq ,
replayMsgId: replyMsg . msgId ,
senderUin: replyMsg . senderUin ,
senderUinStr: replyMsg . senderUin ,
replyMsgClientSeq: replyMsg . clientSeq ,
_replyMsgPeer: replyMsgPeer ,
},
};
OneBot format:
{
"type" : "reply" ,
"data" : {
"id" : "message_id_here"
}
}
Face Emojis
const faceElement : SendMessageElement = {
elementType: ElementType . FACE ,
elementId: '' ,
faceElement: {
faceIndex: 1 , // Face ID (0-255)
faceType: 1 ,
faceText: '[微笑]' ,
stickerId: '' ,
stickerType: 0 ,
packId: '' ,
sourceType: 1 ,
},
};
OneBot format:
{
"type" : "face" ,
"data" : {
"id" : "1"
}
}
Market Face (Stickers)
const marketFaceElement : SendMessageElement = {
elementType: ElementType . MFACE ,
elementId: '' ,
marketFaceElement: {
emojiPackageId: '1' ,
emojiId: 'emoji_id' ,
key: 'sticker_key' ,
faceName: '[贴图]' ,
},
};
Files
const fileElement = await obContext . apis . FileApi . createValidSendFileElement (
context ,
'/path/to/file.pdf' ,
'document.pdf'
);
OneBot format:
{
"type" : "file" ,
"data" : {
"file" : "file:///path/to/file.pdf" ,
"name" : "document.pdf"
}
}
JSON Cards
const jsonElement : SendMessageElement = {
elementType: ElementType . ARK ,
elementId: '' ,
arkElement: {
bytesData: JSON . stringify ({
app: 'com.tencent.structmsg' ,
view: 'news' ,
// ... JSON card structure
}),
linkInfo: null ,
subElementType: null ,
},
};
Markdown
const markdownElement : SendMessageElement = {
elementType: ElementType . MARKDOWN ,
elementId: '' ,
markdownElement: {
content: '# Title \n\n Markdown content here' ,
},
};
Receiving Messages
Parse Received Messages
From napcat-onebot/api/msg.ts:1042-1098:
// Parse raw message to OB11 format
const ob11Message = await obContext . apis . MsgApi . parseMessage (
rawMessage ,
'array' , // or 'string' for CQ code format
true , // parseMultMsg: parse forward messages
false , // disableGetUrl: disable URL fetching
false // quick_reply: quick reply mode
);
console . log ( 'Message segments:' , ob11Message . message );
console . log ( 'Raw message:' , ob11Message . raw_message );
Message Event Structure
interface OB11Message {
self_id : number ; // Bot's QQ number
user_id : number ; // Sender's QQ number
time : number ; // Unix timestamp
message_id : number ; // Unique message ID
message_seq : number ; // Message sequence
real_id : number ; // Real message ID
real_seq : string ; // Real sequence
message_type : 'group' | 'private' ;
sender : {
user_id : number ;
nickname : string ;
card ?: string ; // Group card name
role ?: 'owner' | 'admin' | 'member' ;
};
raw_message : string ; // CQ code format
message : OB11MessageData []; // Segment array
group_id ?: number ; // For group messages
message_format : 'array' | 'string' ;
post_type : 'message' | 'message_sent' ;
}
Forward Messages
Forward Single Message
From msg.ts:270-272:
await core . apis . MsgApi . forwardMsg ( srcPeer , destPeer , [ msgId ]);
Multi-Forward Messages
From msg.ts:274-308:
const msgInfos = msgIds . map ( id => ({
msgId: id ,
senderShowName: core . selfInfo . nick ,
}));
const returnMsg = await core . apis . MsgApi . multiForwardMsg (
srcPeer ,
destPeer ,
msgIds
);
console . log ( 'Forward message:' , returnMsg . msgId );
Message History
Get Message History
From msg.ts:192-195:
const history = await core . apis . MsgApi . getMsgHistory (
peer ,
msgId , // Start from this message
20 , // Count
false // isReverseOrder
);
console . log ( 'Messages:' , history . msgList );
Get Specific Message
// By message ID
const msg = await core . apis . MsgApi . getMsgsByMsgId ( peer , [ msgId ]);
// By sequence number
const msg = await core . apis . MsgApi . getSingleMsg ( peer , msgSeq );
Message Operations
Recall Message
From msg.ts:197-207:
await core . apis . MsgApi . recallMsg ( peer , msgId );
Mark as Read
// Mark specific chat as read
await core . apis . MsgApi . setMsgRead ( peer );
// Mark all messages as read
await core . apis . MsgApi . markAllMsgAsRead ();
Emoji Reactions
// Add emoji like
await core . apis . MsgApi . setEmojiLike (
peer ,
msgSeq ,
'128077' , // Emoji ID (thumbs up)
true // true to add, false to remove
);
// Get emoji likes list
const likes = await core . apis . MsgApi . getMsgEmojiLikesList (
peer ,
msgSeq ,
'128077' ,
'1' , // Emoji type
'' , // Cookie for pagination
20 // Count
);
CQ Code Encoding/Decoding
From cqcode.ts:34-86:
Decode CQ Code
import { decodeCQCode } from '@/napcat-onebot/helper/cqcode' ;
const segments = decodeCQCode ( '[CQ:at,qq=123456] Hello [CQ:face,id=1]' );
// [
// { type: 'at', data: { qq: '123456' } },
// { type: 'text', data: { text: ' Hello ' } },
// { type: 'face', data: { id: '1' } }
// ]
Encode to CQ Code
import { encodeCQCode } from '@/napcat-onebot/helper/cqcode' ;
const cqCode = encodeCQCode ({
type: 'image' ,
data: {
file: 'file:///path/to/image.jpg' ,
summary: 'An image'
}
});
// [CQ:image,file=file:///path/to/image.jpg,summary=An image]
Complete Example
Send Text with Image
Handle Incoming Messages
import { ChatType , ElementType , Peer , SendMessageElement } from 'napcat-core' ;
const peer : Peer = {
chatType: ChatType . KCHATTYPEGROUP ,
peerUid: '123456789' ,
guildId: '' ,
};
const msgElements : SendMessageElement [] = [
// Text
{
elementType: ElementType . TEXT ,
elementId: '' ,
textElement: {
content: 'Check out this image:' ,
atType: 0 ,
atUid: '' ,
atTinyId: '' ,
atNtUid: '' ,
},
},
// Image
await obContext . apis . FileApi . createValidSendPicElement (
{ peer , deleteAfterSentFiles: [] },
'/path/to/image.jpg' ,
'Cool image' ,
0
),
];
const result = await core . apis . MsgApi . sendMsg ( peer , msgElements , 10000 );
console . log ( 'Message sent:' , result . msgId );
Large media files (images, videos, voice) may require longer timeouts. Calculate timeout based on file size: 10000 + (fileSize / 1024 / 256 * 1000) milliseconds.
Best Practices
Always set appropriate timeouts for send operations based on message content
Handle file paths correctly - support URLs, local paths, and base64 data
Clean up temporary files after sending to save disk space
Use message IDs for tracking rather than sequence numbers when possible
Parse messages carefully - handle all segment types your bot needs
Implement error handling for network failures and invalid messages
Next Steps
Plugin Development Build plugins to extend functionality
Packet Inspection Monitor and analyze protocol packets