Skip to content

Events#

Events are the bread and butter of how bots know what's happening on Discord. They are received over the gateway bot's connection and let us detect anything from someone joining or leaving a guild to moderation action to people creating messages.

bot = hikari.GatewayBot("TOKEN")

@bot.listen()
async def on_event(event: hikari.MessageCreateEvent) -> None:
    if not event.is_human:
        return  # Ignore message creates from non-humans (so bots and webhooks).

    if event.message.content == "Hello Botto":
        await event.message.respond("Good morning human-kyun")

In this basic example we listen for message create events from actual users and send the response message "Good morning human-kyun" if the content matches `"Hello Botto".

Intents#

Remember "intents" which were mentioned briefly earlier, well these are important here as we have to make sure that the correct intents are enabled for the events we want the bot to listen for when creating hikari.GatewayBot. More information on which intents are needed for an event can be found at hikari.Intents and if an event isn't mentioned in this list then it is always enabled.

Privileged intents#

An important point to note is that the message create event that examples listens for is considered "privileged" by Discord meaning that you have to enable them in Discord's application settings before the bot can declare them.

intents location part 1

First go to the "Bot" section of the application you want to enable these settings for.

intents location part 2

Then scroll down until you reach "Privileged Gateway Intents" and enable the intents you need. The presence intent lets the bot monitor the statuses of guild members, the server member intent lets the bot track who is in a server, and the message content intent lets the bot see every message users send (without this the bot will only see the content of messages which mention it).

Client lifetimes#

bot = hikari.GatewayBot("TOKEN")
db: DatabaseProto = DatabaseProto()

@bot.listen(hikari.StartingEvent)
async def on_starting_event(event: hikari.StartingEvent) -> None:
    await db.start()  # Start our database client while Hikari is starting.

async def on_stopping_event(event: hikari.StoppedEvent) -> None:
    await db.stop()  # Stop our database client when Hikari is stopping..

bot.subscribe(hikari.StoppedEvent, on_stopping_event)

Hikari provides 4 event types which can be used for managing state around the client's lifetime:

It should be noted that Tanjun provides its own lifetime management system on-top which isn't tied to the bot type you're using. More information on this can be found in Tanjun's guide here.

Wait for an event#

async def handle_edit(bot: hikari.GatewayBot, message: hikari.Message) -> None:
    try:
        edit_event = await bot.wait_for(
            hikari.MessageUpdateEvent,
            timeout=60,
            predicate=lambda event: event.message_id == message.id,
        )

    except asyncio.TimeoutError:
        # In this example we try to delete the message if it's not edited by
        # author within 60 seconds.
        try:
            await message.delete()

        # ForbiddenError will be raised if the bot can no-longer access the channel.
        # and NotFoundError will be raised if the message doesn't exist anymore.
        # We ignore these expected cases and return early to guard against the
        # edit handling logic being called.
        except (hikari.ForbiddenError, hikari.NotFoundError):
            return

    # ... Handle edit.

GatewayBot.wait_for can be used to wait for the next received event which matches a type and predicate (filter). In the above example we wait 60 seconds for a message update event where event.message_id matches a specific message and process the next edit or delete the message if we don't get a response in time.

Event streams#

async def stream_follow_ups(
    bot: hikari.GatewayBot, channel: hikari.PartialChannel
) -> None:
    iterator = bot.stream(hikari.MessageCreateEvent, timeout=5).filter(
        ("event.channel_id", channel.id)
    )

    with iterator:
        async for event in iterator:
            ...  # ... process message create

GatewayBot.stream provides an asynchronous iterator which you can use to iterate over events as they're received. This comes with several chainable methods such as the filter method (more information on which can be found on LazyIterator) and will cache events received between iterations. The limit keyword argument can be used to limit how many events are cached and, while timeout defaults to None (unlimited) for this, you'll likely want to use timeout to specify how many seconds it should wait between events before ending iteration.

Event handling in frameworks#

Just a final note, frameworks which build on top of Hikari will have their own abstractions and approaches of handling listening to events.

component = tanjun.Component()

@component.with_listener()
async def on_member_event(
    event: hikari.MemberCreateEvent | hikari.MemberUpdateEvent,
): ...

@component.with_listener(hikari.StartedEvent, hikari.StartingEvent)
async def on_event(event: hikari.Event): ...

Tanjun (while minimally invasive) tracks event listeners primarily through its "Components" using the decorator method Component.with_listener, and the method Component.add_listener which add dependency injection support to event dispatch and register the relevant event listeners when the Tanjun client is started; with_listener adds support for unions when parsing event types from the callback's signature but otherwise matches GatewayBot.listen.