Skip to content

A simple bot - example

Let's take the following code and break it down, so that you can better understand how niobot works:

import niobot


bot = niobot.NioBot(
    homeserver="https://matrix.org",
    user_id="@my_username:matrix.org",
    device_id="my_device_name",  # defaults to the easily recognisable 'nio-bot'.
    store_path="./store",  # See the 'What is the store?' section for more info.
    command_prefix="!",
    case_insensitive=True,  # means `!PING` and `!piNG` will both work and run `!ping`.
    owner_id="@owner:matrix.org",  # The user ID who owns this bot. Optional, but required for bot.is_owner(...).#
    # Here, you can pass extra options that you would usually pass to `nio.AsyncClient`. Lets pass a proxy:
    proxy="socks5://username:password@host:port"
)


# Now, we want to register a command. NioBot uses the decorator `@NioBot.command()` for this.
# This decorator takes a few arguments, but the only one you'll use most of the time is `name`.
# There are also other arguments though.
# For now we'll register a simple `ping` command.
@bot.command(name="ping")
# We now need to define the function
async def ping(ctx: niobot.Context):
    """Shows the latency between events"""
    # So a few things have happened here:
    # `async def` makes this command asynchronous. This means that it can `await` other things.
    # `ctx` is the only argument this command takes. By default, all niobot commands must take at least one argument,
    # which is the command's context.
    # We also then typehinted it with `niobot.Context`. This isn't critical to make the bot run, however if you're using
    # an IDE like PyCharm, or just a workspace editor with intellisense like Visual Studio Code, it will help you
    # to see what attributes and functions `niobot.Context` has without needing to check the documentation.
    # Anyway, lets write the command itself.
    # First, we need to measure the latency. NioBot has a handy function for this:
    latency_ms = bot.latency(ctx.message)
    # `bot.latency` measures the latency between when the event was dispatched by the server, and when the bot
    # received it. It then returns the latency in milliseconds.
    # `Context.message` is the event that triggered this command.
    # Now, we need to reply to the user.
    await ctx.respond("Pong! Latency: {:.2f}ms".format(latency_ms))
    # And that's it! We've written our first command.
    # `Context.respond` always sends a reply to the user who sent the command.
    # To send a message without a reply, you can use `NioBot.send_message`.


# And while we're at it, we can add an event listener.
# For this example, we'll add an event listener that tells the user if there's a command error.
@bot.on_event("command_error")
async def on_command_error(ctx: niobot.Context, error: Exception):
    """Called when a command raises an exception"""
    # Take a look at the event reference for more information about events.
    # Now, we can send a message to the user.
    await ctx.respond("Error: {}".format(error))


# And while we're at it, we'll log when a user runs a command.
@bot.on_event("command")
async def on_command(ctx):
    print("User {} ran command {}".format(ctx.message.sender, ctx.command.name))


# Now, we need to start our bot.
# This is done by calling `NioBot.run()`.
# In this example, we'll use an access token, rather than an insecure password.
# You can get an access token through the niobot CLI:
# $ niocli get-access-token
# Copy the resulting access token, and then you can put it here:
bot.run(access_token="my_access_token")
# Bear in mind that no code will run after `bot.run`. This function will block until the bot is stopped.
# And even when the bot is stopped, its usually with an exception, so code after `bot.run` is not guaranteed to run.