Handling Message Events
Event listeners can receive message content delivered by the platform and implement features such as commands, command groups, and event listening.
Event listener decorators are located in astrbot.api.event.filter and must be imported first. Please make sure to import it, otherwise it will conflict with Python's built-in filter higher-order function.
from astrbot.api.event import filter, AstrMessageEventMessages and Events
AstrBot receives messages delivered by messaging platforms and encapsulates them as AstrMessageEvent objects, which are then passed to plugins for processing.
Message Events
AstrMessageEvent is AstrBot's message event object, which stores information about the message sender, message content, etc.
Message Object
AstrBotMessage is AstrBot's message object, which stores the specific content of messages delivered by the messaging platform. The AstrMessageEvent object contains a message_obj attribute to retrieve this message object.
class AstrBotMessage:
'''AstrBot's message object'''
type: MessageType # Message type
self_id: str # Bot's identification ID
session_id: str # Session ID. Depends on the unique_session setting.
message_id: str # Message ID
group_id: str = "" # Group ID, empty if it's a private chat
sender: MessageMember # Sender
message: List[BaseMessageComponent] # Message chain. For example: [Plain("Hello"), At(qq=123456)]
message_str: str # The most straightforward plain text message string, concatenating Plain messages (text messages) from the message chain
raw_message: object
timestamp: int # Message timestampHere, raw_message is the raw message object from the messaging platform adapter.
Message Chain
A message chain describes the structure of a message. It's an ordered list where each element is called a message segment.
Common message segment types include:
Plain: Text message segmentAt: Mention message segmentImage: Image message segmentRecord: Audio message segmentVideo: Video message segmentFile: File message segment
Most messaging platforms support the above message segment types.
Additionally, the OneBot v11 platform (QQ personal accounts, etc.) also supports the following common message segment types:
Face: Emoji message segmentNode: A node in a forward messageNodes: Multiple nodes in a forward messagePoke: Poke message segment
In AstrBot, message chains are represented as lists of type List[BaseMessageComponent].
Commands
from astrbot.api.event import filter, AstrMessageEvent
from astrbot.api.star import Context, Star, register
@register("helloworld", "Soulter", "A simple Hello World plugin", "1.0.0")
class MyPlugin(Star):
def __init__(self, context: Context):
super().__init__(context)
@filter.command("helloworld") # from astrbot.api.event.filter import command
async def helloworld(self, event: AstrMessageEvent):
'''This is a hello world command'''
user_name = event.get_sender_name()
message_str = event.message_str # Get the plain text content of the message
yield event.plain_result(f"Hello, {user_name}!")TIP
Commands cannot contain spaces, otherwise AstrBot will parse them as a second parameter. You can use the command group feature below, or use a listener to parse the message content yourself.
Commands with Parameters
AstrBot will automatically parse command parameters for you.
@filter.command("add")
def add(self, event: AstrMessageEvent, a: int, b: int):
# /add 1 2 -> Result is: 3
yield event.plain_result(f"Wow! The answer is {a + b}!")Command Groups
Command groups help you organize commands.
@filter.command_group("math")
def math(self):
pass
@math.command("add")
async def add(self, event: AstrMessageEvent, a: int, b: int):
# /math add 1 2 -> Result is: 3
yield event.plain_result(f"Result is: {a + b}")
@math.command("sub")
async def sub(self, event: AstrMessageEvent, a: int, b: int):
# /math sub 1 2 -> Result is: -1
yield event.plain_result(f"Result is: {a - b}")The command group function doesn't need to implement any logic; just use pass directly or add comments within the function. Subcommands of the command group are registered using command_group_name.command.
When a user doesn't input a subcommand, an error will be reported and the tree structure of the command group will be rendered.



Theoretically, command groups can be nested infinitely!
'''
math
├── calc
│ ├── add (a(int),b(int),)
│ ├── sub (a(int),b(int),)
│ ├── help (command with no parameters)
'''
@filter.command_group("math")
def math():
pass
@math.group("calc") # Note: this is group, not command_group
def calc():
pass
@calc.command("add")
async def add(self, event: AstrMessageEvent, a: int, b: int):
yield event.plain_result(f"Result is: {a + b}")
@calc.command("sub")
async def sub(self, event: AstrMessageEvent, a: int, b: int):
yield event.plain_result(f"Result is: {a - b}")
@calc.command("help")
def calc_help(self, event: AstrMessageEvent):
# /math calc help
yield event.plain_result("This is a calculator plugin with add and sub commands.")Command Aliases
Available after v3.4.28
You can add different aliases for commands or command groups:
@filter.command("help", alias={'帮助', 'helpme'})
def help(self, event: AstrMessageEvent):
yield event.plain_result("This is a calculator plugin with add and sub commands.")Event Type Filtering
Receive All
This will receive all events.
@filter.event_message_type(filter.EventMessageType.ALL)
async def on_all_message(self, event: AstrMessageEvent):
yield event.plain_result("Received a message.")Group Chat and Private Chat
@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE)
async def on_private_message(self, event: AstrMessageEvent):
message_str = event.message_str # Get the plain text content of the message
yield event.plain_result("Received a private message.")EventMessageType is an Enum type that contains all event types. Current event types are PRIVATE_MESSAGE and GROUP_MESSAGE.
Messaging Platform
@filter.platform_adapter_type(filter.PlatformAdapterType.AIOCQHTTP | filter.PlatformAdapterType.QQOFFICIAL)
async def on_aiocqhttp(self, event: AstrMessageEvent):
'''Only receive messages from AIOCQHTTP and QQOFFICIAL'''
yield event.plain_result("Received a message")In the current version, PlatformAdapterType includes AIOCQHTTP, QQOFFICIAL, GEWECHAT, and ALL.
Admin Commands
@filter.permission_type(filter.PermissionType.ADMIN)
@filter.command("test")
async def test(self, event: AstrMessageEvent):
passOnly admins can use the test command.
Multiple Filters
Multiple filters can be used simultaneously by adding multiple decorators to a function. Filters use AND logic, meaning the function will only execute if all filters pass.
@filter.command("helloworld")
@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE)
async def helloworld(self, event: AstrMessageEvent):
yield event.plain_result("Hello!")Event Hooks
TIP
Event hooks do not support being used together with @filter.command, @filter.command_group, @filter.event_message_type, @filter.platform_adapter_type, or @filter.permission_type.
On Bot Initialization Complete
Available after v3.4.34
from astrbot.api.event import filter, AstrMessageEvent
@filter.on_astrbot_loaded()
async def on_astrbot_loaded(self):
print("AstrBot initialization complete")On LLM Request
In AstrBot's default execution flow, the on_llm_request hook is triggered before calling the LLM.
You can obtain the ProviderRequest object and modify it.
The ProviderRequest object contains all information about the LLM request, including the request text, system prompt, etc.
from astrbot.api.event import filter, AstrMessageEvent
from astrbot.api.provider import ProviderRequest
@filter.on_llm_request()
async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # Note there are three parameters
print(req) # Print the request text
req.system_prompt += "Custom system_prompt"You cannot use yield to send messages here. If you need to send, please use the
event.send()method directly.
On LLM Response Complete
After the LLM request completes, the on_llm_response hook is triggered.
You can obtain the ProviderResponse object and modify it.
from astrbot.api.event import filter, AstrMessageEvent
from astrbot.api.provider import LLMResponse
@filter.on_llm_response()
async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # Note there are three parameters
print(resp)You cannot use yield to send messages here. If you need to send, please use the
event.send()method directly.
Before Sending Message
Before sending a message, the on_decorating_result hook is triggered.
You can implement some message decoration here, such as converting to voice, converting to image, adding prefixes, etc.
from astrbot.api.event import filter, AstrMessageEvent
@filter.on_decorating_result()
async def on_decorating_result(self, event: AstrMessageEvent):
result = event.get_result()
chain = result.chain
print(chain) # Print the message chain
chain.append(Plain("!")) # Add an exclamation mark at the end of the message chainYou cannot use yield to send messages here. This hook is only for decorating event.get_result().chain. If you need to send, please use the
event.send()method directly.
After Message Sent
After a message is sent to the messaging platform, the after_message_sent hook is triggered.
from astrbot.api.event import filter, AstrMessageEvent
@filter.after_message_sent()
async def after_message_sent(self, event: AstrMessageEvent):
passYou cannot use yield to send messages here. If you need to send, please use the
event.send()method directly.
Priority
Commands, event listeners, and event hooks can have priority set to execute before other commands, listeners, or hooks. The default priority is 0.
@filter.command("helloworld", priority=1)
async def helloworld(self, event: AstrMessageEvent):
yield event.plain_result("Hello!")Controlling Event Propagation
@filter.command("check_ok")
async def check_ok(self, event: AstrMessageEvent):
ok = self.check() # Your own logic
if not ok:
yield event.plain_result("Check failed")
event.stop_event() # Stop event propagationWhen event propagation is stopped, all subsequent steps will not be executed.
Assuming there's a plugin A, after A terminates event propagation, all subsequent operations will not be executed, such as executing other plugins' handlers or requesting the LLM.

