Skip to content
Documentation index

Scripting

Scripts tell a bot what to do. They use a small, safe, Python-style language with 225 bot functions and 73 events. Every one of them is also a block in the block builder.

View as Markdown · give it to an AI assistant to write scripts for you

Your first script

PASSWORD = "my-password"

async def on_join(bot):
    bot.sleep(3)
    bot.send_chat(f"/login {PASSWORD}")

async def on_chat(bot, message):
    if "hello" in message.lower():
        message.reply("hi " + message.sender)
  • A script is a set of handlers: functions named after an event (on_join, on_chat, on_kick, …). The bot runs a handler each time its event happens.
  • The first parameter is always the bot. The others are what the event gives you, like message for on_chat or reason for on_kick.
  • bot.send_chat(...) and send_chat(...) do the same thing. await is optional.
  • Arguments go by position or by name: bot.find_block("oak_log", radius=16).
  • Mistakes come back with the line number and a “did you mean” suggestion.

To run a script, pick it when you start an account, or assign it under Details → Behavior on the Accounts page. The assigned script runs every time the account starts. Its log.info(...) lines and errors appear in the editor's Output panel.

What functions give back

Functions return numbers, text, True/False, lists, None, or records you read with a dot. Records that stand for text act like text: "hi" in message, player == "Steve".

RecordFields
message.text, .sender (None for server messages), .kind, .is_whisper, .matches(regex), .reply(text)
player.name, .uuid, .display_name, .prefix, .suffix, .team, .ping, .gamemode, .position, .distance
entity.type, .name, .uuid, .position, .velocity, .health, .distance, .is_player, .is_hostile, .is_item, .item
block.id, .state, .position, .x, .y, .z, .is_air, .is_solid
item.id, .name, .count, .slot, .hotbar
position.x, .y, .z, .distance_to(other), .offset(dx, dy, dz), .floor(), .center()

Inventory slots use Minecraft's numbers: 5–8 armor, 9–35 main inventory, 36–44 hotbar (hotbar slot 0 is 36), 45 off hand.

Settings and variables

Put settings at the top of the script (DELAY = 30, HOME = pos(0, 64, 0)). A variable set in a handler is shared with every other handler. New names inside a helper function stay inside that function.

kills = 0                       # shared by every handler

async def on_attack(bot, entity):
    kills += 1

def count_logs(bot):
    total = 0                   # only exists inside count_logs
    for item in bot.find_items("oak_log"):
        total += item.count
    return total

Helpers, timers, and background tasks

Any def that isn't named after an event is a helper. Call it yourself, or hand it to the bot without quotes or brackets. If its first parameter is bot, the bot is passed in for you.

def patrol(bot):
    bot.walk_to(10, 64, 10)
    bot.walk_to(-10, 64, -10)

async def on_join(bot):
    bot.every(60, patrol)            # every minute
    bot.after(5, patrol)             # once, in 5 seconds
    task = bot.run_task(patrol)      # in the background: task.pause(), .resume(), .cancel(), .wait()

Waiting

Every wait takes timeout= and returns None or False if time runs out.

msg = bot.wait_for_chat("Please log in", timeout=10)
if msg is None:
    log.warn("no login prompt")

bot.wait_for_health(15, timeout=30)
bot.wait_until(is_safe, timeout=60)   # is_safe is one of your functions

Errors

try:
    bot.break_block(10, 64, 10)
except Exception as e:
    log.warn("couldn't mine: " + e.message)

A failing handler never stops the others. The error is logged with its line number and on_error(bot, error) runs. bot.try_action(fn, retries=3) and bot.retry(fn) retry flaky steps; bot.restart_on_error(True) restarts the script after a crash.

Staying online 24/7

The account's Keep online setting (on by default) already rejoins after kicks and restarts. A script lives as long as its session: handlers keep listening, and a while True loop with a wait inside runs for as long as the bot does. During a reconnect, anything that needs the world (moving, looking, attacking) waits until the bot is back instead of failing, so loops carry on by themselves.

def guard(bot):
    while True:
        mob = bot.find_entity("hostile", radius=6)
        if mob:
            bot.attack(mob)
        bot.sleep(0.5)

async def on_start(bot):
    bot.run_task(guard)

async def on_chat(bot, message):     # keeps listening across every rejoin
    if message == "!pos":
        message.reply(str(bot.get_position()))

Variables, timers, and tasks survive a reconnect. on_reconnect runs after the rejoin and on_join runs again. To control reconnects from the script instead, use bot.auto_reconnect(True, delay=10, max_attempts=0) (0 = keep trying). A ban or mute takes the bot off the server and keeps it off.

Anti-AFK

bot.anti_afk(interval=30, jump=True, rotate=True, move=False, swing=False, sneak=False, command=None, no_fly=True) does a small random action every interval (±25%). With no_fly the bot also lands whenever the server has it flying, like ranked players in Hypixel lobbies. It is the same as the account's Anti-AFK setting: calling it replaces that setting for this session, bot.anti_afk(False) turns it off, and bot.afk_settings() shows what is running.

Webhooks

async def on_kick(bot, reason):
    bot.webhook(reason, title="Kicked", level="error")

async def on_whisper(bot, message):
    bot.webhook(f"{message.sender}: {message.text}", webhook="dms")

bot.webhook(message, title=None, level="info", webhook=None) posts to a webhook you added under Webhooks (Discord, Slack, or any URL that accepts JSON). webhook= picks one by name; otherwise your default is used. Scripts never see the URL. Each bot can send 20 per minute.

Raw packets

bot.send_raw_packet("swing", ["varint", 0])                  # main hand
bot.send_raw_packet("chat_command", ["string", "spawn"])     # unsigned command
bot.send_raw_packet(0x3F, "00")                               # id + hex body

For experts: sends a serverbound play packet by name (or numeric id) with a body given as hex text or type/value pairs (bool, byte, ubyte, short, ushort, int, long, float, double, varint, varlong, angle, string, uuid, position, bytes). The body isn't checked, so a wrong one gets the bot kicked — use the normal functions when one exists. At most 50 per second.

The language

Supported: numbers, text and f-strings (f"hp={hp:.1f}"), True/False/None, lists with indexing, slicing and .append(), if/elif/else, while, for, break, continue, return, try/except/finally, and/or/not, in, is None, += -= *= /=, and helper functions.

Not supported, on purpose: imports, files, network, classes, lambdas, decorators, dictionaries, comprehensions, and a def inside another def. Scripts are sandboxed: they can only affect their own bot.

Limit: each handler gets about 200,000 steps of work between waits. Any wait (like bot.sleep) refills it, so a loop that never waits is stopped, while a loop that waits can run for days.

Block builder

  • Every function and event below is also a block. Drag an event (like When joined server) onto the canvas, then drag blocks under it until they snap.
  • If, Repeat, Forever, and the other loop blocks hold blocks inside them.
  • Dragging a block takes the blocks under it along. Drop it back on the block list to delete it; Ctrl+Z undoes.
  • A stack that isn't under an event is kept on the canvas but doesn't run.
  • Type values right on the block. Click a block to see all its options. The { } switch next to a field lets you type code instead of a plain value — a variable like message or an expression like bot.get_health() < 10.
  • Script variables holds the settings at the top of a script (COUNT = 3); they're set once when the script starts and every flow can read them.
  • The Code block holds lines of Python for anything the other blocks can't show, like items.append(x). Tab indents inside it.
  • Saving turns the blocks into the script's code (press Code to read it). Code written in the code editor opens here as blocks, with any line that has no matching block kept in a Code block; it runs exactly the same. Saving from the builder drops comments.

API reference

Functions are called as bot.name(...). Events are handled with async def on_event(bot, ...).

Connection

async def on_connect(bot)
The connection to the server is established.
async def on_login(bot)
Login finished; the world is loading.
async def on_spawn(bot)also on_join
Spawned into the world (after every join and rejoin).
async def on_ready(bot)
Spawned and the surrounding chunks are loaded.
async def on_disconnect(bot, reason)
The connection ended, for any reason.
async def on_kick(bot, reason)
The server sent a disconnect message (reason text as received). If the server closes the socket before the message arrives, only `on_disconnect` fires.
async def on_ban(bot, reason)
Disconnected with a ban/restriction message.
async def on_server_transfer(bot, info)
Moved between servers behind a proxy (BungeeCord/Velocity) or transferred; `info.type` is "switch" or "transfer".
bot.reconnect(delay=1.0)
Drop the connection and rejoin the same server after `delay` seconds. The script keeps running and `on_reconnect` fires after the rejoin.
bot.disconnect()
Leave the server. Auto-reconnect is turned off first, so the session ends.
bot.is_connected() → bool
True while connected to the server.
bot.is_spawned() → bool
True once the bot has spawned into the world.
bot.last_kick_reason() → str | None
The raw reason text of the most recent kick, or None.
bot.last_disconnect_reason() → str | None
The raw reason of the most recent disconnect of any kind, or None.
bot.last_kick_classification() → str | None
How the last disconnect was classified: kick, temporary_ban, permanent_ban, server_restart, network_failure, ...
bot.wait_for_connect(timeout=None) → bool
Wait until connected. Returns False on timeout.
bot.wait_for_spawn(timeout=None) → bool
Wait until spawned in the world. Returns False on timeout.

Chat

async def on_chat(bot, message)
Any chat or system message. `message.text`, `message.sender` (None for system), `message.kind`, `message.matches(regex)`.
async def on_system_message(bot, message)also on_server_message
A message from the server itself (not a player).
async def on_whisper(bot, message)
A private message to the bot.
bot.send_chat(text)also chat
Send a chat message.
bot.send_command(command)
Run a server command. The leading `/` is optional.
bot.whisper(player, text)
Send a private message (`/msg player text`).
bot.wait_for_chat(text=None, timeout=None, regex=False) → message | None
Wait for a chat or system message containing `text` (any message if omitted; a regular expression if `regex=True`). Returns the message, or None on timeout.

Players & tab list

async def on_player_join(bot, player)also on_player_seen
A player appeared in the tab list.
async def on_player_leave(bot, player)also on_player_left
A player left the tab list.
async def on_player_list_update(bot, player)
A tab-list entry changed (display name, game mode, ping).
bot.get_players() → list[player]
Everyone in the tab list.
bot.find_player(name) → player | None
Look up an online player by name (case-insensitive).
bot.find_players(prefix=None, suffix=None, team=None, tag=None, regex=None, distance_lt=None) → list[player]
Filter online players by team prefix/suffix, team name, a tag in their display name, a name regex, or distance (only players in render range have a distance).
bot.is_player_online(name) → bool
True if the player is in the tab list.

Movement

bot.move_forward(seconds=None)
Walk forward. With `seconds`, walk that long and stop; without, keep walking until `stop_moving()`.
bot.move_backward(seconds=None)
Walk backward (see `move_forward`).
bot.move_left(seconds=None)
Strafe left (see `move_forward`).
bot.move_right(seconds=None)
Strafe right (see `move_forward`).
bot.move(direction, duration=None)
Walk in a direction: forward, backward, left, right, forward_left, forward_right, backward_left, backward_right.
bot.stop_moving()
Stop walking, sprinting, and following.
bot.jump()
Jump once.
bot.sprint(on=True)
Start sprinting forward (`sprint(False)` stops).
bot.crouch(on=True)also sneak
Hold or release sneak.
bot.walk_to(x, y=None, z=None, timeout=60.0) → bool
Walk to coordinates or an entity/player/block without breaking blocks. Returns True when it arrives.
bot.follow(target, distance=2.0)
Keep following a player (name or player record) until `stop_following()`.
bot.stop_following()
Stop following.
bot.is_moving() → bool
True while walking, following, or pathfinding.
bot.is_sprinting() → bool
True while sprinting.
bot.is_crouching() → bool
True while sneaking.
bot.is_swimming() → bool
True while swimming.
bot.is_flying() → bool
True while flying (creative/spectator or allowed flight).
bot.is_on_ground() → bool
True while standing on a block.
bot.stop_flying()
Stop flying and drop to the ground.

Camera

bot.look(yaw, pitch)
Face an absolute direction. Yaw: 0 south, 90 west, 180 north, -90 east. Pitch: -90 up, 90 down.
bot.look_at(x, y=None, z=None)
Face a point, block, entity, or player.
bot.turn_left(degrees=90.0)
Turn left by `degrees`.
bot.turn_right(degrees=90.0)
Turn right by `degrees`.
bot.look_up(degrees=30.0)
Tilt the camera up.
bot.look_down(degrees=30.0)
Tilt the camera down.
bot.rotate(dyaw, dpitch)
Turn relative to the current direction.
bot.get_yaw() → float
Current yaw in degrees.
bot.get_pitch() → float
Current pitch in degrees.
bot.get_facing_direction() → str
north, south, east, or west.

Interaction

bot.attack(target=None) → bool
Hit an entity: the one given, or whatever is under the crosshair. Returns False if there was nothing to hit.
bot.hold_attack()
Keep the attack button held (mines the targeted block / hits repeatedly).
bot.release_attack()
Release the attack button.
bot.use()also interact
Right-click with the held item.
bot.hold_use()
Keep right-click held (eat, draw a bow, block with a shield).
bot.release_use()
Release right-click.
bot.interact_block(x=None, y=None, z=None)
Right-click a block (door, lever, button, chest...). Defaults to the block under the crosshair. Walks into reach first if needed.
bot.interact_entity(target=None) → bool
Right-click an entity (villager, mount...). Defaults to the crosshair target.
bot.break_block(x=None, y=None, z=None, timeout=15.0) → bool
Mine a block (crosshair block by default) and wait until it is gone. Walks into reach first if needed. Returns True if it broke.
bot.place_block(x, y=None, z=None, item_id=None) → bool
Place the held block (or `item_id`, equipped first) at a position next to an existing block. Walks into reach first if needed. Returns True if the block appeared.
bot.open_container(x=None, y=None, z=None, timeout=5.0) → screen | None
Open a chest, barrel, furnace, crafting table, ... (walking into reach first if needed) and wait for it. Returns the open screen or None.
bot.close_container()also close_screen
Close the open container.
bot.swing_hand()
Play the arm swing animation.
bot.swap_hands()
Swap main hand and off hand items (F).
bot.pick_block(x=None, y=None, z=None) → bool
Select the hotbar slot holding the targeted block's item, like middle-click.
bot.respawn()
Click respawn after dying.

Inventory

async def on_inventory_change(bot)
Something in the inventory changed.
async def on_slot_change(bot, slot, item)
One slot changed (`item` is None when emptied).
bot.get_inventory() → inventory
The player inventory: `.items` (list of items), `.free_slots`, `.is_full`, `.selected_slot`. `inventory.is_full()` also works.
bot.get_slot(slot) → item | None
The item in an inventory slot (5–8 armor, 9–35 main, 36–44 hotbar, 45 off hand).
bot.set_hotbar_slot(slot)
Select hotbar slot 0–8.
bot.get_selected_slot() → int
The selected hotbar slot, 0–8.
bot.get_held_item() → item | None
The item in the main hand.
bot.find_item(item_id) → item | None
First inventory slot holding `item_id` (e.g. "bread").
bot.find_items(item_id) → list[item]
Every inventory slot holding `item_id`.
bot.has_item(item_id, count=1) → bool
True if the inventory holds at least `count` of the item.
bot.count_item(item_id) → int
Total count of an item across the inventory.
bot.inventory_count() → int
Number of non-empty inventory slots.
bot.use_item(item_id=None) → bool
Equip `item_id` if given, then right-click with it (eat, drink, throw...).
bot.equip_item(item_id, hand="main") → bool
Put an item in the hand (hotbar is used and selected), the off hand (`hand="off"`), or wear armor (`hand="armor"`).
bot.drop_item(slot=None)
Drop one item from the held stack (or a slot).
bot.drop_stack(slot=None)
Drop the whole held stack (or a slot).
bot.drop_all(item_id) → int
Drop every stack of an item. Returns how many stacks were dropped.
bot.move_item(from_slot, to_slot)
Move a stack between inventory slots.
bot.swap_slots(a, b)
Swap two inventory slots.
bot.click_slot(slot, button="left")
Left- or right-click an inventory slot.
bot.shift_click(slot)
Shift-click an inventory slot.
bot.get_armor() → list[item | None]
Worn armor: [helmet, chestplate, leggings, boots].
bot.get_offhand() → item | None
The off hand item.

Containers & menus

async def on_screen_open(bot, screen)
Any container/menu opened.
async def on_screen_close(bot)
The open container/menu closed.
async def on_container_open(bot, screen)
A storage container (chest, barrel, shulker box, ...) opened.
async def on_container_update(bot, screen)
The open container's contents changed.
bot.get_open_screen() → screen | None
The open container/menu: `.title`, `.type` (e.g. "generic_9x3"), `.id`.
bot.get_container_slots() → list[item | None]
The open container's own slots (not your inventory part).
bot.click_menu_slot(slot, button="left")
Click a slot of the open menu (works for GUIs like server selectors and shops).
bot.shift_click_menu_slot(slot)
Shift-click a slot of the open menu.
bot.take_item(item_id, count=None) → intalso withdraw_item
Move an item from the open container into your inventory. Returns the number moved.
bot.deposit_item(item_id, count=None) → int
Move an item from your inventory into the open container. Returns the number moved.
bot.deposit_all(keep_hotbar=True) → int
Move all inventory items (hotbar kept by default) into the open container.
bot.select_trade(index)
Select a trade in an open merchant screen (0 = first).
bot.rename_item(name)
Set the item name in an open anvil.

Player state

async def on_respawn(bot)
Respawned after death.
async def on_death(bot, message)
The bot died (`message` is the death message).
async def on_damage(bot, amount, attacker)
Health went down. `attacker` is the entity responsible, if known.
async def on_heal(bot, amount)
Health went up.
async def on_health_change(bot, health)
Health changed.
async def on_hunger_change(bot, food)
Food level changed.
async def on_effect_added(bot, effect)
A potion effect started.
async def on_effect_removed(bot, effect)
A potion effect ended.
async def on_fire(bot)
The bot caught fire.
async def on_drown(bot)
The bot took drowning damage.
async def on_fall(bot, amount)
The bot took fall damage.
async def on_xp_change(bot, xp)
Total experience changed.
async def on_level_change(bot, level)
Experience level changed.
async def on_position_change(bot, position)
Moved at least one block (checked once per second).
async def on_teleport(bot, position)
The server teleported the bot (lobby/spawn sends, /tp, ...).
bot.get_health() → floatalso health
Health, 0–20.
bot.get_max_health() → float
Maximum health.
bot.get_hunger() → int
Food level, 0–20.
bot.get_saturation() → float
Food saturation.
bot.get_air() → int
Remaining air, 0–300 ticks.
bot.get_armor_value() → float
Armor points, 0–20.
bot.get_experience() → experience
Experience: `.level`, `.progress` (0–1), `.total`.
bot.get_level() → int
Experience level.
bot.get_gamemode() → str
survival, creative, adventure, or spectator.
bot.get_position() → positionalso position
Current position (`.x`, `.y`, `.z`).
bot.get_dimension() → str
Current dimension, e.g. "overworld", "the_nether".
bot.get_effects() → list[effect]
Active potion effects: `.id`, `.amplifier`, `.duration` (ticks).
bot.has_effect(effect_id) → bool
True if the effect (e.g. "speed") is active.
bot.is_dead() → bool
True while dead.
bot.is_burning() → bool
True while on fire.
bot.is_underwater() → bool
True when the head is under water.
bot.is_in_lava() → bool
True while touching lava.
bot.is_in_water() → bool
True while touching water.
bot.is_riding() → bool
True while mounted.
bot.get_vehicle() → entity | None
The entity being ridden.

World & blocks

async def on_world_join(bot, dimension)
Entered a dimension/world.
async def on_world_leave(bot, dimension)
Left a dimension/world.
async def on_block_change(bot, block)
A block within 32 blocks changed.
async def on_block_break(bot, block)
A nearby block became air.
async def on_block_place(bot, block)
A nearby air block became solid.
async def on_weather_change(bot, weather)
Weather changed: clear, rain, thunder.
async def on_time_change(bot, phase)
The time of day crossed into day, sunset, night, or sunrise.
bot.get_block(x, y=None, z=None) → block | None
The block at a position: `.id` (e.g. "oak_log"), `.position`, `.state`, `.is_air`, `.is_solid`. None if the chunk is not loaded.
bot.get_block_state(x, y=None, z=None) → str | None
Full state text, e.g. "oak_log[axis=y]".
bot.get_block_under_player() → block | None
The block the bot stands on.
bot.get_block_in_front() → block | None
The block directly in front at foot level (by facing direction).
bot.find_block(block_id, radius=32.0) → block | None
Nearest loaded block of a type within `radius`. Accepts a list of ids too.
bot.find_blocks(block_id, radius=32.0, limit=64) → list[block]
All loaded blocks of a type within `radius`, nearest first.
bot.find_nearest_block(block_id) → block | None
Nearest block of a type anywhere in loaded chunks.
bot.raycast(distance=4.5) → hit
What the crosshair hits: `.type` ("block", "entity", "miss"), `.block`, `.entity`, `.position`, `.face`.
bot.can_see(x, y=None, z=None) → bool
True if nothing solid is between the bot's eyes and the point/entity.
bot.is_air(x, y=None, z=None) → bool
True if the block is air.
bot.is_solid(x, y=None, z=None) → bool
True if the block has a collision shape.
bot.is_walkable(x, y=None, z=None) → bool
True if a player can stand at this block position (solid below, two free blocks).
bot.get_world_time() → int
Time of day in ticks, 0–23999 (0 = sunrise, 6000 = noon, 13000 = night).
bot.get_weather() → str
clear, rain, or thunder.

Entities

async def on_entity_spawn(bot, entity)
An entity came into range.
async def on_entity_despawn(bot, entity)
An entity left range or was removed.
async def on_entity_move(bot, entity)
An entity within 16 blocks moved (at most twice a second per entity).
async def on_entity_damage(bot, entity)
A nearby entity took damage.
async def on_entity_death(bot, entity)
A nearby entity died.
bot.get_entities() → list[entity]
Every loaded entity, nearest first (max 256).
bot.get_nearby_entities(radius=16.0) → list[entity]
Entities within `radius`, nearest first.
bot.find_entity(type=None, name=None, radius=64.0) → entity | None
Nearest entity matching a type ("zombie") and/or name.
bot.find_entities(type=None, name=None, radius=64.0) → list[entity]
All entities matching a type and/or name, nearest first.
bot.get_nearest_entity(type=None) → entity | None
The nearest entity (optionally of a type).
bot.get_entity(uuid) → entity | None
Look up an entity by UUID.

Dropped items

async def on_item_drop(bot, entity)
An item entity appeared.
async def on_item_pickup(bot, item)
The bot picked up an item.
bot.get_dropped_items(radius=32.0) → list[entity]
Item entities on the ground; each has `.item` (`.id`, `.count`).
bot.find_dropped_item(item_id=None, radius=32.0) → entity | None
Nearest dropped item, optionally of a type.
bot.walk_to_item(item=None, timeout=30.0) → bool
Walk onto a dropped item (nearest one by default, or an item id / item entity) to pick it up.

Combat

async def on_attack(bot, entity)
The bot attacked an entity.
async def on_attacked(bot, attacker)
Something hurt the bot.
bot.get_attacker() → entity | None
The entity that last hurt the bot (within 10 seconds).
bot.get_target() → entity | None
The target set with `set_target`.
bot.set_target(entity)
Remember an entity as the combat target (used by `attack()` when nothing is in the crosshair).
bot.nearest_hostile(radius=16.0) → entity | None
The nearest hostile mob.
bot.is_under_attack(seconds=5.0) → bool
True if the bot took damage in the last `seconds`.
bot.distance_to(x, y=None, z=None) → float
Distance from the bot to a point, block, entity, or player.

Navigation

async def on_path_complete(bot, position)
Arrived at a pathfinding target.
async def on_path_failed(bot, position)
Pathfinding gave up.
bot.pathfind_to(x, y=None, z=None, avoid_lava=True, avoid_fall=True, allow_jump=True, allow_swim=True, allow_mining=False, max_distance=512.0, timeout=120.0) → bool
Find and follow a path to a point or entity. Returns True on arrival; fires `on_path_complete` / `on_path_failed`.
bot.stop_pathfinding()
Cancel the current path.
bot.can_reach(x, y=None, z=None, max_distance=128.0) → bool
True if a walking path to the point exists (computed without moving).
bot.get_path(x, y=None, z=None, max_distance=128.0) → list[position]
The positions a walking path would visit (empty if unreachable).
bot.is_pathfinding() → bool
True while a path is being computed or followed.

Coordinates

bot.distance_to_player(name) → float | None
Distance to a player in render range, else None.
bot.save_position(name, position=None)
Remember the current (or given) position under a name.
bot.get_saved_position(name) → position | None
A position saved with `save_position`.

Scoreboard & server UI

async def on_bossbar(bot, bar)also on_bossbar_update
A boss bar was added or changed.
async def on_scoreboard_update(bot, scoreboard)
The sidebar changed (throttled to once per second).
bot.get_scoreboard() → scoreboard | None
The sidebar: `.title` and `.lines` (top to bottom).
bot.get_scoreboard_title() → str | None
The sidebar title.
bot.get_scoreboard_lines() → list[str]
Sidebar lines top to bottom, colors stripped.
bot.get_tab_header() → str
The tab list header text.
bot.get_bossbars() → list[bossbar]
Visible boss bars: `.title`, `.progress` (0–1), `.id`.

Titles & action bar

async def on_actionbar(bot, text)
Action bar text above the hotbar.
async def on_title(bot, text)
A big title on screen.
async def on_subtitle(bot, text)
A subtitle on screen.
bot.get_title() → str | None
The most recent title text.
bot.get_actionbar() → str | None
The most recent action bar text.
bot.wait_for_title(text=None, timeout=None) → str | None
Wait for a title (containing `text`). Returns its text or None.
bot.wait_for_actionbar(text=None, timeout=None) → str | None
Wait for an action bar message (containing `text`).

Sounds & particles

async def on_sound(bot, sound)
A sound played: `.id`, `.position`, `.volume`, `.pitch`, `.category`.
async def on_particle(bot, particle)
Particles spawned: `.id`, `.position`, `.count`.
bot.wait_for_sound(sound_id, timeout=None, radius=None) → sound | None
Wait for a sound whose id contains `sound_id` (e.g. "fishing_bobber.splash"), optionally within `radius` blocks.

Packets

async def on_packet_receive(bot, packet)
A raw packet arrived: `.type`, `.data` (see `packet_filter`).
bot.log_packets(enabled=True, types=None)
Write received packet types to the script log (optionally only `types`, e.g. ["SetHealth"]).
bot.packet_filter(types)
Choose which packet types fire `on_packet_receive` (default: everything except chunk and movement spam).
bot.send_raw_packet(packet, data=None) → intalso send_packet
Send a serverbound game packet you build yourself. `packet` is a name ("swing", "chat_command", "use_item") or protocol id; `data` is the body as hex text ("0a ff") or type/value pairs (["varint", 0, "string", "hi"]; types: bool, byte, ubyte, short, ushort, int, long, float, double, varint, varlong, angle, string, uuid, position, bytes). Returns the body size. Bodies are not checked: a wrong one gets the bot kicked or banned. At most 50 per second.

Waiting

bot.sleep(seconds)also wait
Pause for a number of seconds.
bot.wait_until(condition, timeout=None, interval=0.25) → bool
Call `condition` until it returns something true. Returns False on timeout.
bot.wait_while(condition, timeout=None, interval=0.25) → bool
Wait while `condition` keeps returning true. Returns False on timeout.
bot.wait_for_event(event, timeout=None) → anyalso wait_for
Wait for an event ("chat", "on_death", or a custom one). Returns its first value (or True), None on timeout.
bot.wait_for_player(name, timeout=None) → bool
Wait until a player is online.
bot.wait_for_player_leave(name, timeout=None) → bool
Wait until a player is no longer online.
bot.wait_for_health(value, timeout=None, below=False) → bool
Wait until health is at least `value` (or at most, with `below=True`).
bot.wait_for_position(x, y=None, z=None, radius=1.5, timeout=None) → bool
Wait until the bot is within `radius` of a point.
bot.wait_for_inventory_change(timeout=None) → bool
Wait until the inventory changes.
bot.wait_for_block(x, y=None, z=None, block_id=None, timeout=None) → bool
Wait until the block at a position becomes `block_id` (or changes at all).

Timers

bot.after(seconds, function, *args) → int
Run a function once after a delay. Returns a timer id.
bot.every(seconds, function, *args) → int
Run a function repeatedly. Returns a timer id.
bot.set_timeout(function, seconds, *args) → int
Same as `after`, with the function first.
bot.set_interval(function, seconds, *args) → int
Same as `every`, with the function first.
bot.cancel_timer(timer_id) → bool
Stop a timer. Returns False if it was already gone.
bot.schedule_at(time, function, *args) → int
Run a function at a clock time "HH:MM" (UTC, next occurrence) or a unix timestamp.
bot.schedule_when(condition, function, interval=1.0) → int
Run `function` once, as soon as `condition` returns true.

Tasks

bot.run_task(function, *args) → task
Run a function in the background. Returns a task: `.cancel()`, `.pause()`, `.resume()`, `.wait()`, `.is_running`.
bot.cancel_all_tasks()
Cancel every background task and timer.
bot.get_tasks() → list[task]
Background tasks that are still running.
bot.acquire(name, timeout=None) → bool
Take a named lock (e.g. "movement") so two tasks don't fight over the same controls. Waits if another task holds it. Released by `release` or when the holder finishes.
bot.release(name)
Release a named lock.

Custom events

bot.on(event, function, priority=0)
Also run `function` for an event. Higher priority runs first; a listener that returns False stops the rest.
bot.once(event, function, priority=0)
Like `on`, but only the next time.
bot.off(event, function=None)
Remove a listener added with `on`/`once` (all of them for the event if no function).
bot.emit(event, data=None)
Fire a custom event; `on_<event>` handlers and listeners receive `data`.

Reconnect & recovery

async def on_reconnect(bot, attempts)
Back in the world after an automatic reconnect.
async def on_reconnect_attempt(bot, attempt)
A reconnect attempt is starting.
async def on_reconnect_failed(bot, attempts)
Auto-reconnect used up `max_attempts`.
async def on_timeout(bot)
No packets for `detect_stuck(packet_timeout=...)` seconds.
async def on_stuck(bot, reason)
Logged in but never spawned within `detect_stuck(loading_timeout=...)`.
bot.auto_reconnect(enabled=True, delay=5.0, max_attempts=10)
Rejoin automatically after a disconnect. The script keeps its variables, timers, and tasks; `on_reconnect` runs after each rejoin. `max_attempts=0` keeps trying forever. Failed attempts in a row wait longer each time (×1.5, up to 5 minutes).
bot.reconnect_attempts() → int
How many reconnect attempts happened since the last successful join.
bot.detect_stuck(loading_timeout=60.0, packet_timeout=30.0, reconnect=True)
Fire `on_stuck` when spawning takes too long or `on_timeout` when the server goes silent; optionally reconnect.
bot.restart_on_error(enabled=True, max_restarts=5)
Restart the script when a handler crashes (at most `max_restarts` per 10 minutes).

Error handling

async def on_error(bot, error)
A handler or task failed: `.message`, `.line`, `.event`, `.trace`.
async def on_script_crash(bot, error)
A failure is restarting the script (`restart_on_error`).
bot.try_action(function, *args, retries=0, delay=1.0, fallback=None) → any
Call a function; if it fails, retry up to `retries` times, then return `fallback`.
bot.retry(function, *args, attempts=3, delay=1.0) → any
Call a function until it returns something true (or stops failing). Returns the last result.
bot.timeout(function, seconds, *args) → any
Run a function but give up after `seconds` (returns None).

Logging

bot.log(message, level="info")
Write to the script log (shown live in the editor and saved with the session).
bot.log_info(message)
`log.info(msg)`.
bot.log_warn(message)
`log.warn(msg)`.
bot.log_error(message)
`log.error(msg)`.
bot.log_debug(message)
`log.debug(msg)` — only written when debug logging is on.
bot.log_events(chat=True, events=False, commands=True, debug=False)
Automatically log incoming chat, every event, and the bot's own chat/commands. `debug=True` enables `log.debug`.

Notifications

bot.notify(message, level="info")
Send a notification to the dashboard bell (and a desktop notification if the browser allows it).
bot.webhook(message, title=None, level="info", webhook=None)also send_webhook
Post a message to one of your webhooks (Settings → Webhooks): Discord, Slack, or any URL that takes JSON. `webhook` picks one by name (default: your default webhook); `level` is info, success, warn, or error (the embed color). The URL never reaches the script. At most 20 per minute per bot.

Script lifecycle

async def on_start(bot)also on_script_start
The script starts (once per session, before joining).
async def on_stop(bot)also on_script_stop
The script is being stopped (session end or `stop_script()`).
async def on_reload(bot)also on_script_reload
After `reload_script()`.
bot.stop_script()
Stop this script (the bot stays connected). `on_stop` runs first.
bot.restart_script()
Stop everything, clear variables, and start again from `on_start`/`on_join`.
bot.reload_script()
Like restart, but fires `on_reload` instead of `on_start`.
bot.uptime() → float
Seconds since the script started.

Session info

bot.server_info() → info
`.host`, `.port`, `.online_mode`.
bot.account_info() → info
`.name`, `.uuid`.
bot.proxy_info() → info
`.protocol`, `.host`, `.port` (never credentials).
bot.get_ping() → int | None
The bot's latency in ms, from the tab list.

Utilities

bot.random_delay(min, max) → float
Wait a random time between `min` and `max` seconds. Returns the time waited.
bot.repeat(function, times, delay=0.0, *args)
Call a function `times` times with an optional delay between calls.
bot.debounce(function, seconds, *args)
Run a function once things go quiet: every call restarts a `seconds` countdown.
bot.throttle(function, seconds, *args) → bool
Run a function now unless it already ran in the last `seconds`. Returns True if it ran.

Debugging

bot.event_history(limit=20) → list[record]
The most recent events: `.event`, `.time`, `.summary`.
bot.stats() → record
Profiler counters: `.uptime`, `.events`, `.errors`, `.handler_runs`, `.avg_handler_ms`, `.max_handler_ms`, `.fuel_used`, `.tasks`, `.timers`.
bot.get_movement_state() → record
What the bot is doing: `.walking`, `.sprinting`, `.crouching`, `.following`, `.pathfinding`, `.on_ground`.
bot.assert_true(condition, message="check failed") → bool
Log PASS/FAIL for a condition (useful while testing a script). Returns the condition.

AFK helpers

async def on_server_restart(bot, reason)
Disconnected because the server is restarting/closing.
async def on_sent_to_spawn(bot, position)
Teleported to within 8 blocks of the world spawn.
bot.anti_afk(enabled=True, interval=30.0, jump=True, rotate=True, move=False, swing=False, command=None, command_interval=300.0, sneak=False, no_fly=True)also set_afk_mode
Built-in anti-AFK: every `interval` seconds (±25%) jump, look around, step, swing, or sneak; optionally run a command every `command_interval` seconds. With `no_fly` (on by default) the bot lands whenever the server has it flying, like ranked players in Hypixel lobbies. The same engine as the account's Anti-AFK setting: calling this replaces it for this session; `anti_afk(False)` turns it off.
bot.afk_settings() → record
Current anti-AFK: `.enabled`, `.source` ("account" or "script"), `.interval`, `.jump`, `.rotate`, `.move`, `.swing`, `.sneak`, `.command`, `.no_fly`, `.actions` (performed this session).
bot.is_afk() → bool
True while anti-AFK mode is running.
bot.get_keepalive() → record
`.last_packet` (seconds since any packet), `.last_keepalive`, `.connected_for`.
bot.wait_for_server(timeout=None) → bool
After a restart/kick with auto-reconnect on, wait until the bot is back in the world.

Built-in functions

len(x)
Length of a string or list.
str(x)
Convert to text.
int(x)
Convert to a whole number.
float(x)
Convert to a decimal number.
bool(x)
Convert to True/False.
list(x)
Copy a list, or split a string into characters.
range(stop) / range(start, stop, step)
A list of numbers.
abs(x)
Absolute value.
min(a, b, ...) / min(list)
Smallest value.
max(a, b, ...) / max(list)
Largest value.
sum(list)
Add up numbers.
round(x, digits=0)
Round a number.
floor(x)
Round down.
ceil(x)
Round up.
sqrt(x)
Square root.
sorted(list)
Sorted copy of a list.
reversed(list)
Reversed copy of a list.
random()
Random decimal between 0 and 1.
randint(a, b)
Random whole number between a and b (inclusive).
choice(list)
Random element of a list.
time()
Current unix time in seconds.
distance(a, b)
Distance between two positions/entities/blocks.
pos(x, y, z)
Make a position.
strip_colors(text)
Remove Minecraft color codes (§a, &b, ...).
format_chat_text(text)
Strip color codes and collapse whitespace.
parse_chat(text)
Split "[Rank] Name: message" or "<Name> message" into `.prefix`, `.name`, `.message`.
regex_match(pattern, text)
True if the regular expression matches.
regex_find(pattern, text)
First match, or None.
regex_find_all(pattern, text)
Every match.
regex_groups(pattern, text)
Capture groups of the first match, or None.
regex_replace(pattern, replacement, text)
Replace matches.

Not available

  • on_packet_send — Azalea does not surface outgoing packets as events at the pinned version; `log_packets()` logs packets sent with `send_raw_packet`, and `log_events(commands=True)` logs chat and commands.
  • Event cancellation of server events — Incoming server events cannot be cancelled. Listener chains can stop early by returning False.
  • Environment variables — Scripts are sandboxed and cannot read the worker's environment. Use top-level constants instead.
  • Secrets store — None. Keep secrets out of scripts you share with others.
  • Sound notifications — None. `notify()` reaches the dashboard bell and browser notifications; `webhook()` reaches Discord, Slack, or your own URL.
  • Breakpoints and step debugging — None. Use `event_history()`, `stats()`, `log_packets()`, and `log.debug()`.
  • Hot reload on save — None. Saving a script applies on the next session start; `reload_script()` restarts the running copy.

Known limitations

  • Pathfinding never digs or places blocks unless allow_mining=True, and never crosses lava or long drops.
  • on_block_break reports what a block became, not what it was.