R16 — Community-Driven Interactive Code Evaluation
R16 is a "trick bot". It saves snippets of code, called tricks, which can then be recalled and executed on user-provided input.
This manual is structured by frontend. A frontend is a way through which users can interact with the bot.
1 Configuration
The bot is run by invoking its main.rkt file, and stores its configuration in a JSON file that is read on startup. See the -h output for precise details about command-line flags.
The configuration file must contain a top level object with the following keys and associated values:
storage: A string defining the folder in which to store the bot’s save data.
frontends: A list of objects. Within each object, the module key must have a string value that is a module path as in dynamic-require which identifies a frontend to run. Frontends may require more specific configuration within this object, see the documentation of your chosen frontend for more details.
{ "storage": "/home/r16bot/r16_save_data", "frontends": [ { "module": "r16/frontends/discord", "bot_token": "<your bot token here>" } ] }
2 The Trick Environment
(require r16/current-trick-environment) | package: r16 |
Tricks are stored as plain text, and invoked in a sandbox using racket/sandbox.
The following values are available in the sandbox’s namespace:
All symbols exported by threading, for convenience.
All items listed below.
Any items made available by the specific frontend, see the documentation of your chosen frontend for details.
All bindings are automatically available in the top-level phase 0 trick environment, but they can also be required from the special r16/current-trick-environment module path, if you need them in a different phase or a submodule.
value
value
value
parent-context : (or/c (hash/c symbol? any/c) #f)
3 Discord Frontend
This frontend provides integration with the Discord chat service.
Commands to the bot are issued by prefixing Discord messages with a configurable prefix value. The rest of the message is then treated as a command. For more details, send the message <bot_prefix> help in your Discord server.
The frontend also has a configurable trick shorthand prefix. Messages of the form <trick_prefix>foo bar ... are equivalent to <bot_prefix> call foo bar ....
3.1 Discord Configuration
module must be the string "r16/frontends/discord".
bot_token must be a string containing your Discord bot token.
bot_prefix is a string specifying the bot’s trigger prefix. If not present, defaults to "!rkt ".
trick_prefix is a string specifying the bot’s shorthand prefix. If not present, defaults to "!!".
delete_time is a nonnegative integer specifying a time in seconds. If any invocation of R16 is deleted within this time, then R16 will also delete its response. This helps with spam due to incorrect trick invocations. If not present, defaults to 5 minutes (300 seconds). Set to 0 to disable this feature.
irc_bridge_bots is an array of snowflake strings representing a whitelist of bots which r16 should allow parsing IRC-style messages (of the format <nickname> words...) from. Defaults empty.
3.2 Discord Trick Environment Extensions
In additional to the bindings described above, the following items are available in the trick environment.
procedure
(delete-caller) → void?
procedure
(emote-lookup name) → (or/c string? #f)
name : string?
procedure
(emote-image id) → (or/c bytes? #f)
id : string?
procedure
(make-attachment payload name mime) → any/c
payload : bytes? name : (or/c string? bytes?) mime : (or/c symbol? string? bytes?)
value
procedure
(read-storage type) → any/c
type : (or/c 'guild 'channel 'user)
A trick’s "trick-local storage" can be per-guild, per-channel, or per-user.
This will always return #f for the eval command.
procedure
(write-storage type data) → boolean?
type : (or/c 'guild 'channel 'user) data : any/c
Type |
| Size Limit |
guild |
| 64 KiB |
channel |
| 8 KiB |
user |
| 2 KiB |
This will always be a no-op when invoked from the eval command.
procedure
(attachment-data attachment) → bytes?
attachment : any/c
procedure
(open-attachment [index]) → (or/c input-port? #f)
index : natural? = 0
Returns #f if the message doesn’t have an indexth attachment, or if the attachment couldn’t be opened for any other reason.
procedure
(open-reply-attachment [index]) → (or/c input-port? #f)
index : natural? = 0
value
value
value
reply-contents : (or/c string? #f)
4 HTTP Frontend
The HTTP frontend turns R16 into a web application. On startup, R16 starts an HTTP server on the local machine at the given port, which allows tricks to be registered and called from HTML forms.
Caveat: R16 will only bind to localhost, and will not use TLS. Please set up a reverse proxy with TLS termination in front of the bot if you plan to expose it to the internet.
4.1 HTTP Frontend Configuration
module must be the string "r16/frontends/http".
port is an integer specifying which port to bind the web server to. If not present, defaults to 8080.
4.2 HTTP Trick Environment Extensions
In additional to the bindings described above, the following items are available in the trick environment.
procedure
(make-image png-bytes) → any
png-bytes : bytes?
5 Evaluator Details
R16’s evaluator runs in a sandbox (see racket/sandbox) and supports two types of evaluation: repl-style and module-style. In both cases, trick’s source code is evaluated for IO side effects and output values, which are then presented in a frontend-dependent manner.
Repl-style evaluation is used when the source code consists of multiple forms or one non-module form, and always uses racket as the source language. Currently, all forms are read up front, and then each form is evaluated in turn. The final form’s results are the output of the evaluation.
(define greeting (format "Hello, ~a!" string-args)) greeting ;; <- returned and printed to the client
Module-style evaluation is triggered when the entire input reads into a single module form, such as when a #lang line is used. In this case, the module is instantiated for side effects, and doesn’t output any values by default.
#lang racket (define greeting (format "Hello, ~a!" string-args)) greeting ;; <- printed by #%module-begin, NOT returned to the client
Of course, you don’t have to use #lang racket:
#lang scribble/text Hello, @|string-args|!
Bindings from r16/current-trick-environment and the chosen #lang are both introduced into the top-level module, with bindings from the latter able to shadow those from r16/current-trick-environment.