Overview
Message events are triggered when the bot receives a message. NapCat supports both private (direct) messages and group messages, following the OneBot 11 standard.
Common Fields
All message events share these fields:
Unix timestamp (in seconds) when the message was sent
Always "message" for message events
Type of message: "private" or "group"
Sub-type of the message:
For private: "friend", "group" (temp chat from group), or "normal"
For group: "normal", "anonymous", or "notice"
Unique message ID for this message
QQ number of the message sender
Message content as an array of message segments (or string in CQ code format)
Plain text representation of the message
Information about the message sender Group card name (group messages only)
Role in group: "owner", "admin", or "member" (group messages only)
Gender: "male", "female", or "unknown"
Age (may be 0 if not available)
Private Message Event
Private messages are direct messages sent to the bot.
Additional Fields
"friend" - Message from a friend
"group" - Temporary chat from group member
"normal" - Normal private message
Source of temporary chat (present when sub_type is “group”)
Example: Friend Message
{
"time" : 1709641234 ,
"self_id" : 123456789 ,
"post_type" : "message" ,
"message_type" : "private" ,
"sub_type" : "friend" ,
"message_id" : 123456 ,
"message_seq" : 78901 ,
"real_id" : 123456 ,
"user_id" : 987654321 ,
"message" : [
{
"type" : "text" ,
"data" : {
"text" : "Hello, bot!"
}
}
],
"raw_message" : "Hello, bot!" ,
"font" : 14 ,
"sender" : {
"user_id" : 987654321 ,
"nickname" : "Alice" ,
"sex" : "female" ,
"age" : 20
}
}
Example: Temporary Chat from Group
{
"time" : 1709641234 ,
"self_id" : 123456789 ,
"post_type" : "message" ,
"message_type" : "private" ,
"sub_type" : "group" ,
"temp_source" : 12345678 ,
"message_id" : 234567 ,
"user_id" : 987654321 ,
"message" : [
{
"type" : "text" ,
"data" : {
"text" : "Private message from group"
}
}
],
"raw_message" : "Private message from group" ,
"font" : 14 ,
"sender" : {
"user_id" : 987654321 ,
"nickname" : "Bob" ,
"sex" : "unknown" ,
"age" : 0
}
}
Group Message Event
Group messages are messages sent in group chats where the bot is a member.
Additional Fields
Group number where the message was sent
"normal" - Normal group message
"anonymous" - Anonymous message
"notice" - System notice
Example: Normal Group Message
{
"time" : 1709641234 ,
"self_id" : 123456789 ,
"post_type" : "message" ,
"message_type" : "group" ,
"sub_type" : "normal" ,
"message_id" : 345678 ,
"message_seq" : 90123 ,
"group_id" : 12345678 ,
"group_name" : "Bot Testing Group" ,
"user_id" : 987654321 ,
"message" : [
{
"type" : "text" ,
"data" : {
"text" : "Hello everyone!"
}
}
],
"raw_message" : "Hello everyone!" ,
"font" : 14 ,
"sender" : {
"user_id" : 987654321 ,
"nickname" : "Charlie" ,
"card" : "Group Leader" ,
"role" : "admin" ,
"sex" : "male" ,
"age" : 25 ,
"area" : "" ,
"level" : "10" ,
"title" : "Active Member"
}
}
Example: Group Message with At
{
"time" : 1709641234 ,
"self_id" : 123456789 ,
"post_type" : "message" ,
"message_type" : "group" ,
"sub_type" : "normal" ,
"message_id" : 456789 ,
"group_id" : 12345678 ,
"user_id" : 987654321 ,
"message" : [
{
"type" : "at" ,
"data" : {
"qq" : "123456789"
}
},
{
"type" : "text" ,
"data" : {
"text" : " Are you there?"
}
}
],
"raw_message" : "[CQ:at,qq=123456789] Are you there?" ,
"font" : 14 ,
"sender" : {
"user_id" : 987654321 ,
"nickname" : "Diana" ,
"card" : "" ,
"role" : "member"
}
}
Message Content
The message field contains an array of message segments. Each segment has a type and data field.
Text Segment
{
"type" : "text" ,
"data" : {
"text" : "Hello, world!"
}
}
Image Segment
{
"type" : "image" ,
"data" : {
"file" : "file:///path/to/image.jpg" ,
"url" : "https://example.com/image.jpg" ,
"file_size" : "123456"
}
}
Face Segment (Emoji)
{
"type" : "face" ,
"data" : {
"id" : "1"
}
}
At Segment
{
"type" : "at" ,
"data" : {
"qq" : "123456789" ,
"name" : "Nickname"
}
}
Reply Segment
{
"type" : "reply" ,
"data" : {
"id" : "123456"
}
}
Record Segment (Voice)
{
"type" : "record" ,
"data" : {
"file" : "file:///path/to/audio.silk" ,
"url" : "https://example.com/audio.silk"
}
}
Video Segment
{
"type" : "video" ,
"data" : {
"file" : "file:///path/to/video.mp4" ,
"url" : "https://example.com/video.mp4"
}
}
Forward Segment
{
"type" : "forward" ,
"data" : {
"id" : "forward_message_id"
}
}
Messages can also be represented as CQ code strings in the raw_message field:
Hello [CQ:face,id=1] [CQ:at,qq=123456789] [CQ:image,file=abc.jpg]
CQ codes follow the format:
[CQ:type,param1=value1,param2=value2]
Handling Messages
Basic Message Handler
function handleMessage ( event ) {
if ( event . message_type === 'private' ) {
console . log ( `Private message from ${ event . sender . nickname } : ${ event . raw_message } ` );
} else if ( event . message_type === 'group' ) {
console . log ( `Group message in ${ event . group_name } from ${ event . sender . nickname } : ${ event . raw_message } ` );
}
}
Checking for At Mentions
function isAtBot ( event , botQQ ) {
if ( ! Array . isArray ( event . message )) return false ;
return event . message . some ( seg =>
seg . type === 'at' && seg . data . qq === botQQ . toString ()
);
}
if ( event . message_type === 'group' && isAtBot ( event , event . self_id )) {
console . log ( 'Bot was mentioned!' );
}
Processing Images
function getImages ( event ) {
if ( ! Array . isArray ( event . message )) return [];
return event . message
. filter ( seg => seg . type === 'image' )
. map ( seg => seg . data . url || seg . data . file );
}
const images = getImages ( event );
if ( images . length > 0 ) {
console . log ( `Message contains ${ images . length } image(s)` );
}
function getPlainText ( event ) {
if ( ! Array . isArray ( event . message )) return event . raw_message ;
return event . message
. filter ( seg => seg . type === 'text' )
. map ( seg => seg . data . text )
. join ( '' );
}
const text = getPlainText ( event ). trim ();
if ( text . startsWith ( '/' )) {
// Handle command
const command = text . slice ( 1 ). split ( ' ' )[ 0 ];
console . log ( `Command: ${ command } ` );
}
Quick Reply
When using HTTP POST adapter, you can reply to messages by returning a response:
app . post ( '/event' , ( req , res ) => {
const event = req . body ;
if ( event . post_type === 'message' ) {
// Quick reply
res . json ({
reply: 'Thanks for your message!' ,
at_sender: event . message_type === 'group' , // At sender in groups
delete: false , // Don't delete the original message
kick: false , // Don't kick the sender
ban: false , // Don't ban the sender
ban_duration: 0 // Ban duration in seconds
});
} else {
res . status ( 204 ). send ();
}
});
Next Steps