Python
Your first script
Python · Updated 09 November 2026
Set up, write a script class, and run it from the bwu_host GUI or a console.
Python scripts run in bwu_host, the BotWithUs native host. The launcher installs it.
Setup
- Nothing to install to run scripts.
bwu_hostbrings its own CPython 3.12. Write for Python 3.12. - Editor completion. The
botwithuspackage ships type information (py.typed), so an editor that can see the package gives you completion and type checking. If you have the framework source, install it into your editor's environment from its folder withpython -m pip install -e .[dev].python -m pytestthere runs the framework's own unit tests on a plain Python 3.11 or newer, with no game client. - Scripts only run inside
bwu_host. The framework reaches the game through a module that exists only there, so there is nopython -mway to run a script.
Where scripts live
Put your scripts in ~/.botwithus/scripts/python (%USERPROFILE%\.botwithus\scripts\python). That is the folder the bwu_host GUI lists.
The shape of a script
A script is one BotScript subclass with a @script_manifest, in a file of its own. Everything you normally need imports from botwithus.api:
from botwithus.api import BotScript, ScriptContext, script_manifest
@script_manifest(name="Hello", version="1.0", author="you")
class Hello(BotScript):
def on_start(self, ctx: ScriptContext) -> None:
self.ctx = ctx
self.loops = 0
def on_loop(self) -> int:
me = self.ctx.self_player
if me.has_tile:
self.ctx.log.info("standing on %s", me.tile)
self.loops += 1
return -1 if self.loops >= 10 else 600
if __name__ == "__main__":
import time
from botwithus.core.connection import Connection
from botwithus.core.native import default_client_pid
pid = default_client_pid()
if pid is None:
raise SystemExit("no game client found")
conn = Connection(pid)
conn.start_script(Hello())
while conn.running_scripts():
time.sleep(0.5)
conn.close()
on_loop returns the delay in milliseconds before the next call. A negative value stops the script.
One script per file, and nothing at import time
- To list a file, the GUI imports it once and looks for exactly one
BotScriptclass. A file with two fails to load. - Do no work at import time, such as attaching, walking or long setup. Put it in
on_start. - The
if __name__ == "__main__":block runs only when you start the file from a console (below). The GUI never runs it for a script class.
Running it
In the GUI. Start bwu_host with no arguments. It lists every .py file in ~/.botwithus/scripts/python by its manifest. Pick a script and a client, and start it. Each run gets a worker process of its own.
From a console.
bwu_host --py $HOME\.botwithus\scripts\python\hello.py
This runs the file directly, which is why the example ends with the __main__ block. It attaches to the first game client it finds and runs the script until it stops. With no client running, it exits with no game client found. Extra arguments end up in sys.argv: bwu_host --py hello.py a b gives ['hello.py', 'a', 'b'].
A script's INFO lines go to its run log and to the GUI, not to the bwu_host console, so a console run of Hello prints nothing there. See Logs and crash files for where to find them.