An extensible and modular bot framework for hack.chat
usv3 requires Python ^3.10 and Poetry
- Set up the project with
poetry install
- Configure the bot in config
- Run the bot with
poetry run usv3
When testing, you can override the options in config/core_config.yml with flags:
poetry run usv3 --debug --server ws://127.0.0.1:6060 --channel testing
usv3 can be extended by adding modules that get triggered on various events.
A few useful dev tools (flake8, icecream and line_profiler) can be installed with poetry install -E dev
. See the Managing dependencies section for adding your own.
A basic module for the command event looks like this:
class Module:
"""
# Module specific configuration in yaml.
# All optional, valid options are:
# - desc
# - usage
# - min_args (Sends usage if not met)
# - max_args (Sends usage if exceeded)
# - alias
# - cooldown (in seconds)
# - groups (list of groups that can access the command)
desc: 'Your command's help text'
usage: '<arg1> [arg2] [arg3]'
min_args: 1
max_args: 3
"""
@staticmethod
async def run(bot, namespace, text, args, sender, trip, ulevel):
If your module needs to do stuff on load, use on_load()
:
class Module:
...metadata...
@staticmethod
def on_load(bot, namespace):
namespace.mylist = []
# Whatever else that needs to be done
@staticmethod
async def run(bot, namespace, text, args, sender, trip, ulevel):
Different events take different arguments for run()
:
Events | Arguments |
---|---|
command, whisper | bot namespace text args sender trip ulevel |
message | bot namespace text sender trip ulevel |
join, leave | bot namespace sender hash trip |
Here's a breakdown of all the arguments:
Argument | Description |
---|---|
bot |
Main usv3 instance, see warning below. |
namespace |
Module's namespace. |
text |
Full message text without the command stripped. |
args |
Text trailing the command split into a list of arguments. |
sender |
Sender's nickname. |
hash |
Sender's connection hash. |
trip |
Sender's tripcode. |
ulevel |
Sender's user level. See hack-chat/main/commands/utility/_UAC.js |
Warning
The bot
object is the main usv3 instance, messing with its attributes can result in crashes. Safe methods you can call from bot
are send()
, reply()
and whisper()
. These are documented in the next section.
A namespace is created for each module. This can be used as a safe place to store data that needs to be accessed later or shared between different modules. Within the same module, this is available as namespace
. A different module's namespace can be accessed with bot.namespaces.<event>.<name>
.
A few example modules can found in a separate repository. You can use them as reference when creating your own.
After creating a module, place it in its respective event in usv3/events. The module will be found and loaded automatically. If your module has any dependencies, add them to pyproject.toml under tool.poetry.group.cmd.dependencies
and run poetry update
.
For chat/whisper commands, the name of the module will be the command that calls it. You can add an alias for each one if a shorter alternate command is needed.
The reload
command live reloads all loaded modules and any new modules that have been added. Note that this will re-run on_load()
in all modules that have it. The configuration file config/extra_config.yml will also be reloaded.
bot.send()
can be used to reply to the server. It's defined in the core framework as:
async def send(self, cmd: str = "chat", **kwargs) -> None:
await self.ws.send(json.dumps({"cmd": cmd, **kwargs}))
To send a chat message:
await bot.send(text="Hello World!")
An alternate command:
await bot.send(cmd="changecolor", color="ff0000")
To reply to users in chat with a consistent format, use bot.reply()
:
await bot.reply(sender, "Hello!")
A whisper shortcut is also provided:
await bot.whisper(sender, "Don't tell anyone")
The bot
object manages a few useful attributes that can be read from within modules:
Attribute | Description |
---|---|
online_users |
List of online nicknames. |
online_trips |
Dictionary of online nicknames and their respective trips. |
online_hashes |
Dictionary of online nicknames and their respective connection hashes. |
Warning
These attributes are meant to be read-only. Modifying them from a module can result in crashes.
usv3 supports the building and loading of cython modules. Dependencies required for this are not installed by default, they can be with poetry install -E cython
. If you don't want dev dependencies to be uninstalled, run poetry install --all-extras
instead.
Adding a cython module is pretty much the same as adding a pure python module, just drop the pyx file into its respective event. After that, run poetry run build_cython
to build all cython modules.
To get a list of modules that will be built without actually building them, pass --dry-run
to the above command.
Note
Unlike pure python modules, cython modules will not reflect changes after rebuilding when using the reload
command. You will have to restart the bot to load the changes.
usv3 uses Poetry to manage dependencies.
poetry install
will install dependencies required by modules and the core framework, removing all extra dependencies. This is what you should run before deploying the bot.
To add a dependency required by a module, you can do one of the following:
- Let Poetry handle it by running
poetry add -G cmd <package>
. - Manually add it to pyproject.toml under
tool.poetry.group.cmd.dependencies
and runpoetry update
.
Two groups of extra dependencies are defined:
dev
for optional tools useful for development and testing. (flake8, icecream and line_profiler)cython
for dependencies required to build cython modules. (cython and setuptools)
To install these extra dependencies, run poetry install --all-extras
.
If you have more dev tools to use, add them under tool.poetry.dependencies
in pyproject.toml as optional and inside the dev extras list.
If you want to run usv3 as a systemd service, here's a sample unit file:
# usv3.service
[Unit]
Description=usv3
After=network-online.target
[Service]
ExecStart=/path/to/poetry run -n usv3
ExecStartPre=/path/to/poetry check -n --lock
WorkingDirectory=/home/<user>/path/to/usv3/directory
Restart=always
RestartSec=60
Type=simple
[Install]
WantedBy=default.target
Edit to match your setup and place it in ~/.config/systemd/user/
.
Run systemctl --user daemon-reload
and systemctl --user enable --now usv3
to start and enable the service.
Once the service is up, you can run systemctl --user status usv3
or journalctl --user -e -u usv3
to view resource usage or logs.
Note
If you have issues with usv3 being killed when you log out, enable lingering with sudo loginctl enable-linger $USER
.