A framework that allows anyone to create an Urbit Chatbot with only a few lines of code.
The Urbit Chatbot Framework takes care of all the complexities of connecting to a ship, subscribing to a chat, parsing messages, and sending messages automatically for you. All you have to do is simply write the message handling/response logic and everything else will just work.
This crate is available on crates.io.
At its core, the framework revolves around the Chatbot
struct. It is defined as:
/// This struct represents a chatbot that is connected to a given `ship`,
/// is watching/posting to a specific `chat_ship`/`chat_name`
/// and is using the function `respond_to_message` to process any messages
/// which are posted in said chat.
pub struct Chatbot {
/// `respond_to_message` is a function defined by the user of this framework.
/// This function receives any messages that get posted to the connected chat,
/// and if the function returns `Some(message)`, then `message` is posted to the
/// chat as a response. If it returns `None`, then no message is posted.
respond_to_message: fn(AuthoredMessage) -> Option<Message>,
ship: ShipInterface,
chat_ship: String,
chat_name: String,
}
To create a chatbot, one simply must instantiate a Chatbot
struct with:
ShipInterface
to connect to to interact with the Urbit networkchat_ship
that the chat is running onchat_name
AuthoredMessage
as input, and returns an Option<Message>
as outputA Chatbot
is most easily created using the new_with_local_config
method:
/// Create a new `Chatbot` with a `ShipInterface` derived automatically
/// from a local config file. If the config file does not exist, the
/// `Chatbot` will create the config file, exit, and prompt the user to
/// fill it out.
pub fn new_with_local_config(
respond_to_message: fn(AuthoredMessage) -> Option<Message>,
chat_ship: &str,
chat_name: &str,
) -> Self {
This automatically deals with creating a local ship config file for the user to edit if none available, and then on rerun, reading said config to connect to a ship.
In order to use this method, we need to define a respond_to_message
function which we can supply as an argument. Here is an example of one of the simplest possible implementations:
fn respond_to_message(authored_message: AuthoredMessage) -> Option<Message> {
// Any time a message is posted in the chat, respond in chat with a static message.
Some(Message::new().add_text("Calm Computing ~"))
}
Do note, that if this function returns None
, then the Chatbot
will not reply to the message, and simply continue forward processing the next one.
With this function defined, you can now easily call Chatbot::new_with_local_config()
and acquire an instantiated Chatbot
.
Once the Chatbot
struct is defined, all one needs to do is:
chat_bot.run();
This will automatically perform all messaging, parsing, and interfacing with the connected Urbit ship without any further code required.
And just like that you have an Urbit chatbot ready to go.
The projects linked below are example Urbit Chatbot projects. They link directly to the main.rs
so you can immediately see the implementation of said chatbot.
You can also easily run any of these examples by:
main.rs
with the chat_ship
and chat_name
which you wish to run the bot in.cargo run
the first time to create the ship config file.ship_config.yaml
with your ship information (Moons or comets typically work best. Do not use your daily-driver ship because messages from the ship the chatbot is connected to are all ignored.)cargo run
to run the chatbot with the ship config info provided.The Simple Chatbot is the simplest chatbot possible which replies to all messages in a chat with a static message. Approximately 4 lines of real code, showing off how simple it is to create a chatbot.
The point of this project is to display the bare minimum requirements for setting up a chatbot.
The Anti Comet Chatbot is a slightly more advanced chatbot which takes a look at the ship that authored the latest message. If the ship has a name long enough to be classified a comet, then it responds with a message. Otherwise, it returns None
, meaning no message is sent by the chatbot to the chat in reply (if they aren't a comet).
The Crypto Prices Chatbot is a real world example of a useful chatbot that implements a command for everyone in a chat to use.
In effect, if anyone types:
|price {crypto_name_here}
such as
|price bitcoin
Then the bot will fetch the bitcoin price via coingecko API, and return it:
USD $37167
This is the first chatbot implemented via the Urbit Chatbot Framework which has real utility, and is a great example of how to go about building chatbots for use cases which need to reply based off of commands and make calls to external APIs.
The Bible Chatbot is another real world example of a useful chatbot that implements a command for everyone in a chat to use.
This chatbot allows anyone to use the !bible
command and request one or more verses from the bible (KJV):
!bible John 1:1-5
The Poll Chatbot is the most complex chatbot example enabling anyone to run votes/polls inside of their chats. Every poll is saved into a .json
file and ensured that every ship only gets a single vote.
To start a poll with any given amount of options:
!poll [option1] [option2] ...
or
!poll -t [title] [option1] [option2] ...
The title of a poll acts as its ID, so don't use the same one multiple times yet.
Once a poll is running any ship can vote (precisely once):
!vote [pollid] [option]
Results of a poll can be accessed via:
!results [pollid]
or
!results all
to view all active polls.
The creator of a poll can end the poll via:
!endpoll [pollid]
Polls without titles have a numerical ID generated at creation and are stored in a json file in the directory that the bot is run from.
(Credits to ~hodzod-walrus for creating the Poll Chatbot)