Skip to content
/ rustix Public

Matrix chat bot written in Rust

License

Notifications You must be signed in to change notification settings

jpypi/rustix

Repository files navigation

About

pipeline status latest release

Rustix is a matrix bot/library/framework written in rust. This project does not depend on any 3rd party matrix client libraries, but rather, contains one with only the necessary API calls within. HTTP requests are made directly to a matrix server via the reqwest library.

Note: The primary home for this project is on gitlab anywhere else is just a mirror.

Running

The recommended way to run rustix is to use docker. Instructions for running via docker can be found near the bottom of this document.

To run rustix there must be a matrix user account with a password set up. The username and password should be put in config.toml. Rustix uses a database to keep quotes, track "karma" (e.g. rust++ or cabbage--) to record likes and dislikes in a channel, among other things. To set up the database, first, create a file called .env which contains a database url to a PostgreSQL database. It should look something like this:

DATABASE_URL=postgres://user:password@localhost/rustix

(Note: This assumes a database user has been set up and has proper permissions on the proper database.)

Next, run the database migrations and compile + run rustix!

$ diesel migration run
$ cargo run

Architecture

The command/filter/service architecture of rustix can be thought of as a directed graph. Service nodes are added to the graph and matrix events get propogated (or blocked) through to child nodes. This makes rustix very flexible. For example: "self" filter and prefix filter nodes are prebuilt and it is recommended that new services be added under the prefix filter which is under the "self" filter. These filters only propagate events to child processing nodes if the message wasn't sent by the bot itself and the message starts with a prefix, which gets stripped off before being sent along.

Prebuilt commands

The framework should be fairly flexible and not too difficult to use for your own project or to just extend. The following are prebuilt commands, and should be prefixed with the default prefix: !. (The prefix can be changed in config.toml)

  • addquote <quote here>
  • getquote <comma separated list of up to 5 ids>
  • *delquote <quote number>
  • randquote <optional string to search>
  • searchquote <string to search>
  • quoteby <user nickname>
  • roulette
  • rroulette
  • duel
  • dduel
  • choose <item1>, <item2>, ... <itemN>
  • echo <string>
  • structure
  • karma <entity>
  • karmastats <optional entity>
  • badkarmastats <optional entity>
  • nickstats <optional matrix user id>
  • badnickstats <optional matrix user id>
  • p <crypto currency ticker>
  • votekick <username>
  • voteban <username>
  • roll <integer>
  • bf <code>
  • *join <public channel display name>
  • *leave <public channel display name>
  • *emptycleanup
  • *joined
  • *node config <service/node name> <command>
  • *node help <service/node name>
  • help <optional service name>

*Command is under the admin node and requires message sender to be in the admin list specified in config.toml

Quote related commands such as addquote also have aliases e.g. aq.

The node command has two sub commands config and help, which can be used to configure nodes in the processing graph. The help sub command will be useful to understand what commands can be passed to the node when using the config sub command. Note that "service/node name" is the name of the service/node internal to the bot message processing graph, which may not be the the text string used to trigger the command the node is associated with. The names of the services/nodes are visible via the structure command.

Optional Commands (if configured)

Enabled via services.csv_quote:

These commands will look up quotes stored in a csv file. New quotes must be manually added to this file.

The expected column layout is: id,text,user,timestamp,channel

  • oldgetquote <comma separated list of up to 5 ids>
  • oldrandquote <optional string to search>
  • oldsearchquote <string to search>

Enabled via services.try_file:

The TryFile service attempts to interpret !<command name> as referring to a file named <command name>.txt in the var folder in the current working directory and then echo a random line from it. This allows for invocation like !timecube which will echo a random line from var/timecube.txt. N.B. If there is a command name collision both matched commands will trigger, that is if one were to place a file named randquote.txt in var, both the randquote function will be executed and a random line from randquote.txt will be echoed.

Enabled via services.web_search:

The s command, which performs search queries using the Google custom web search API.

  • s <search string>

Enabled via services.factoid:

This service provides functionality for simple pattern matching to simple facts. Though enabled by default, it should be noted that this service can become very irritating, and it may be wise to put it behind some additional filters restricting it to certain rooms or potentially only allowing certain users to use some of the functionality. As a reminder, to disable the service, simply remove the configuration in the config file. Mappings can be added by sending:

"rustix, rust is <reply> awesome"

or

"rustix, waves is <action> waves back"

Now, whenever someone sends a message which starts with the word "rust", rustix will reply "awesome". Similarly, whenever a message starts with the word "waves", rustix will send an event which creates a message as though the action was performed in third person i.e. "* rustix waves back". This is akin to using "/me waves at rustix" in many matrix or irc clients.

Multiple factoids can be assigned to the same "key", and rustix will randomly chose one when a match for the key is found. To list all the responses mapped to a key send: "literal <key>" e.g. "literal waves", and you will get a list of factoids and factoid metadata (e.g. creator, id, etc.). To remove a factoid simply send "delfactoid <id>" and rustix will remove that factoid.

The allfactoids command is behind a whitelist channel filter, to prevent spam. This command enables users to view all factoids set, but is only allowed in the channels with ids listed in list_all_channels. If the list_all_channels config is empty or missing, then the command will not be available.

Enabled via services.openai:

The chat command, which enables interaction with openai's gpt models. Very similar to having your own ChatGPT that everyone can interact with in a shared channel. This command pulls in recent chat history in the channel to provide context to your message.

  • chat <whatever you want to say to rustix>

Enabled via services.bonequest:

This command is disabled in the default config as it has the potential to return some ofensive language. You must specify a list for the profanity config variable, though it may be empty if your group isn't offended by any words. The profanity filter normalizes the comic text and blacklist words when performing the check. This command is also behind a channel filter node so you can control and limit the channels may be used in.

  • bq

Config

Rustix expects a file named config.toml to be in the current working directory. Below is an example config file with all possible config options specified for documentation sake (with dummy values), however the default file in the repository has some of these left out by default as they have additional concerns or requirements.

[connection]
server = "https://matrix.my.domain.com/"
username = "rustix"
password = "mySecr3tPassword"

[bot]
display_name = "rustix"
prefix = "!"
rooms = ["general", "rust", "memes"]
admins = ["@myself:matrix.my.domain.com"]
ignore = ["@bot1:matrix.my.domain.com", "@bot2:matrix.my.domain.com"]

[services]
[services.karma]
max_per_message = 10

[services.try_file]
directory = "/usr/share/rustix"

[services.csv_quote]
file = "csv_quotes.csv"

[services.web_search]
key = "<google api key>"
seid = "<google custom search id>"

[services.factoid]
factoid_leader = "rustix,"
list_all_channels = ["!XYmmdisZsVrOTGLmoIO:matrix.my.domain.com"]

[services.bonequest]
profanity = ["badword"]

[services.openai]
secret = "<openai api key>"
backstory_file = "backstory.txt"
monthly_budget = 5.0
# starting_tokens is optional
starting_tokens = 10000

Rustix will ignore all events by users in the ignore list, not just commands.

Reminder: The configuration for the following services is optional. That is, removing the configuration will disable the service in rustix and not cause an error.

  • try_file
  • csv_quote
  • web_search
  • factoid
  • bonequest
  • openai

State

All nodes may have on_load and on_exit methods, which gets called once the node has been registered with the bot, and when the bot is cleanly shut down, respectively. These methods may be used to save state when stopping/starting the bot. All the state gets saved in various files under the .rustix folder which gets created the first time a node which saves state actually saves state.

Docker - Pre-built (recommended/easiest)

There are pre-built rustix docker images in this gitlab project which the docker-compose.yml file utilizes. By default, docker compose will use a bind mount for the config.toml file, but you could alternately copy the config in to the rustix container via: docker cp config.toml rustix-rustix-1:config.toml or utilize a volume.

You will also need to make sure to have the following in place:

  1. Make is installed
  2. Docker and docker-compose are both installed and setup
  3. An appropriately configured config.toml file
  4. A folder named var, containing all the files that the tryfile service can use, in the project root folder
  5. If you intend to use the old*quote commands, a file named csv_quotes.csv lives in the project root folder

Run Step 3 from the "Docker - DIY" section of this README (below).

Docker - DIY

There is a Makefile which makes building and running a dockerized version of rustix a breeze. Before running this way, note that the instructions make the following assumptions:

  1. Make is installed
  2. Docker and docker-compose are both installed and setup
  3. You have updated docker-compose.yml to use the appropriate local images: perplexinglabs/rustix:0.1 and perplexinglabs/rustix-diesel:0.1 (as defined in the Makefile)
  4. config.toml has been appropriately configured
  5. A folder named var, containing all the files that the tryfile service can use, exists in the project root folder
  6. If you intend to use the old*quote commands, a file named csv_quotes.csv lives in the project root folder

Step 1

Run make rustix which builds the main rustix image

Step 2

Run make migration which builds the db maintenence (for db migrations) image

Step 3

Run make setup which generates a database password, launches rustix and the db maintenence container (which then runs the db migrations) and then removes the db maintenence container after running migrations.

Rustix should now be running. From here you can easily run make up, make down, make stop and make start which are simple helpful wrappers around the respective docker-compose commands.

NOTE: Upon running this command a file named .pw_lock will be created which contains the password to the postgres database which rustix uses. This file could alternately be created before running make setup and set to whatever you like, or removed and thus ephemeral. Keeping it around allows easy stopping/starting of the rustix container independent of any other helper containers (such as the postgres container).

Note

If you see anything that could be improved (I'm sure there are many things), please open an issue and/or PR! I'm open to feedback, and would love to improve this project!

About

Matrix chat bot written in Rust

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Contributors 3

  •  
  •  
  •  

Languages