A framework that allows anyone to create an Urbit Chatbot with only a few lines of code.

Overview

Urbit Chatbot Framework

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.

Basic Design

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:

  • A ShipInterface to connect to to interact with the Urbit network
  • The chat_ship that the chat is running on
  • The chat_name
  • A function (optionally named respond_to_message) which takes an AuthoredMessage as input, and returns an Option<Message> as output

Creating A Chatbot

A 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.

Running The 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.

Example Projects

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:

  1. Cloning this repo.
  2. Changing directory into the example project folder in your terminal.
  3. Editing main.rs with the chat_ship and chat_name which you wish to run the bot in.
  4. Running cargo run the first time to create the ship config file.
  5. Editing the 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.)
  6. Running cargo run to run the chatbot with the ship config info provided.

Simple Chatbot

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.

Anti Comet 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).

Crypto Prices Chatbot

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.

Bible Chatbot

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

Poll Chatbot

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)

You might also like...
A lightweight web framework built on hyper, implemented in Rust language.

Sapper Sapper, a lightweight web framework, written in Rust. Sapper focuses on ergonomic usage and rapid development. It can work with stable Rust. Sa

Web framework in Rust

Rouille, a Rust web micro-framework Rouille is a micro-web-framework library. It creates a listening socket and parses incoming HTTP requests from cli

A fast, boilerplate free, web framework for Rust

Tower Web A web framework for Rust with a focus on removing boilerplate. API Documentation Tower Web is: Fast: Fully asynchronous, built on Tokio and

Sincere is a micro web framework for Rust(stable) based on hyper and multithreading

The project is no longer maintained! Sincere Sincere is a micro web framework for Rust(stable) based on hyper and multithreading. Style like koa. The

:zap: fast http framework for rust

zap ⚡ The mission of zap is, to deliver a basic, but fast rust web server library. Documentation About This code is based on tokio's minihttp project,

RiteRaft - A raft framework, for regular people

RiteRaft - A raft framework, for regular people This is an attempt to create a layer on top of tikv/raft-rs, that is easier to use and implement. This

Node.js http server framework powered by Hyper native binding.

hnsjs POC project. Install this test package yarn add @hnsjs/core Support matrix node10 node12 node14 node15 Windows x64 ✓ ✓ ✓ ✓ Windows x32 ✓ ✓ ✓ ✓

simple static file server written in Rust based on axum framework

static-server simple static file server written in Rust based on axum framework I'm learning Rust and axum. My thought is simple. axum has a static-fi

Handlebars middleware for Iron web framework

handlebars-iron Handlebars middleware for the Iron web framework. This library, together with handlebars, iron and hyper, works on both stable and nig

Comments
  • Bot

    Bot "Replied to message" but no message appears in chat.

    Context Our bot stopped sending messages. The bot logs "Replied to message" though. I started experiencing this after Grid rolled out (~2 weeks ago). To doublecheck I cloned the repo again and built the example simple-chatbot, but that didn't work either.

    Grid brought some breaking changes to graph-store: https://urbit.org/using/os/dist-faq#troubleshooting.

    EDIT:

    It looks like scries have new paths: https://gall-doc-generator.vercel.app/scries but that might not be the cause after all.

    The message appears to make it all the way to send_put_request() in src/interface.rs. The request returns a 204 but graph-store isn't updated with the bot's message.

    The following output in dojo at the time of each request:

    http: %cancel not handled yet
    clay: read-at-aeon fail [desk=%landscape care=%c case=[%da p=~2021.10.24..01.07.41..ddc5] path=/graph-update-2/graph-update-3]
    [%no-cast-from %graph-update-2 %graph-update-3]
    [%error-building-cast %graph-update-2 %graph-update-3]
    [%error-building-tube %graph-update-2 %graph-update-3]
    /sys/vane/clay/hoon:<[4.258 13].[4.258 34]>
    /sys/vane/clay/hoon:<[4.245 7].[4.271 9]>
    

    Reproduce

    1. Have the latest OTA update
    2. Build example chatbot from scratch and Run
    3. Send message in from main ship in chat, verify no message is sent by bot

    Environment Running a Moon in Terminal and Planet from Port. Both are up to date. Specs: Macbook Pro Retina 2015, macOS Mojave 10.14.6

    opened by ridoy 6
  • Adding chatbot to multiple rooms?

    Adding chatbot to multiple rooms?

    Context:

    • We're building a financial data chatbot described by this grant: https://urbit.org/grants/cypherscope-bot
    • You can add it to your chat and query it for financial charts, like BTCUSD prices for the last week.
    • We expect this to be added to multiple chats and expect it to run on a single Moon
    • The bot is currently stateless BUT we expect to have a paid tier so we may need to store a mapping of chats to payment tier.

    Issue: Our bot's fn main() consists of the constructor: Chatbot::new_with_local_config(respond_to_message, "~mitten-dapper", "chat-3973").run();

    I guess I'm looking for the best way to implement this stuff:

    1. Listen for when we're added to a new chat (and spawn a new chatbot)
    2. Persist which chats contain our chatbot as a member, so nothing is lost if the Moon shuts down

    I couldn't find anything resembling this in the examples.

    Thank you!

    opened by ridoy 2
  • add poll-bot to examples

    add poll-bot to examples

    Bot for taking a poll in a chat. !poll [option1] [option2] ... Start a poll with given options -- no limit to number of options !vote [pollid] [option] Each ship can only vote once !endpoll [pollid] Only the creator of a poll can end it Polls get a numerical ID generated at creation and are stored in a json file in the directory that the bot is running in.

    opened by hdzdwlrs 1
Owner
Robert Kornacki
Explorations into the realms of Haskell, Idris, Lean, Coq, Agda, Plutus, mCRL2 and wherever else fate leads me next.
Robert Kornacki
Create, share, fetch and model Atomic Data! This project consists of a graph database + server, a CLI and a rust library.

Create, share, fetch and model Atomic Data! This repo consists of three components: A library, a server and a CLI. atomic-server Status: Beta. Breakin

Joep Meindertsma 195 Dec 28, 2022
Server with Rust, Rocket, Diesel, Docker to create your own to-do-list

Installation Install Docker & Docker-Compose Then download the repository go to the root where the Dockerfile is and do: sudo docker-compose up sudo i

Lucas Aries 3 Aug 19, 2022
Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.

Actix Web Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust Features Supports HTTP/1.x and HTTP/2 Streaming and pipelining

Actix 16.3k Jan 8, 2023
A flexible web framework that promotes stability, safety, security and speed.

A flexible web framework that promotes stability, safety, security and speed. Features Stability focused. All releases target stable Rust. This will n

Gotham 2.1k Jan 3, 2023
An Extensible, Concurrent Web Framework for Rust

Iron Extensible, Concurrency Focused Web Development in Rust. Response Timer Example Note: This example works with the current iron code in this repos

null 6.1k Dec 27, 2022
An expressjs inspired web framework for Rust

nickel.rs nickel.rs is a simple and lightweight foundation for web applications written in Rust. Its API is inspired by the popular express framework

null 3k Jan 3, 2023
[OUTDATED] A light HTTP framework for Rust

Rustful A light HTTP framework for Rust, with REST-like features. The main purpose of Rustful is to create a simple, modular and non-intrusive foundat

Erik Hedvall 873 Nov 12, 2022
A web framework for Rust.

Rocket Rocket is an async web framework for Rust with a focus on usability, security, extensibility, and speed. #[macro_use] extern crate rocket; #[g

Sergio Benitez 19.5k Jan 8, 2023
REST-like API micro-framework for Rust. Works with Iron.

Table of Contents What is Rustless? Usage warning Basic Usage Complex example Mounting Parameters validation and coercion Use JSON Schema Query string

Rustless 610 Jan 4, 2023
Fully async-await http server framework

Saphir is a fully async-await http server framework for rust The goal is to give low-level control to your web stack (as hyper does) without the time

Richer Archambault 83 Dec 19, 2022