<Doolan> .ping <Wheaties> pong!
Welcome to Wheaties, Botfast of Champions!
Wheaties is a chat bot whose behavior is invented by you. Anyone can add "commands" that make Wheaties do things, such as respond with a greeting or display a tweet.
This user guide shows how to invoke Wheaties' commands in a chat room, and how to create new ones.
Wheaties responds to chat messages that start with a period (.
) and the name of a command. For example, here’s how to invoke the ping command:
<Doolan> .ping <Wheaties> pong!
The period is Wheaties' command prefix.
Some commands accept parameters called arguments. (You might recognize the term "argument" from math functions or computer programming.) Commands can use arguments to change their behavior based on your input.
Wheaties' argument syntax is simple: elements separated by spaces. (If you’re familiar with Bash or Tcl, you’ll notice some similarities.) Wheaties understands several types of arguments: strings, numbers, regular expressions, lists, and commands.
Strings come in three flavors: naked, single-quoted, and double-quoted. Both regular and curly ("smart") quotes are supported.
Use naked strings for single words or short phrases. Escape spaces with a backslash (\
).
Use single quotes for strings with lots of whitespace, or brackets that you don’t want to turn into commands.
Use double quotes for strings with commands inside of them.
Here are some examples of using each flavor of string. The echo command makes Wheaties reply with one message per argument.
<Doolan> .echo foo bar baz <Wheaties> foo <Wheaties> bar <Wheaties> baz <Doolan> .echo foo bar\ baz <Wheaties> foo <Wheaties> bar baz
<Doolan> .echo foo 'bar baz' <Wheaties> foo <Wheaties> bar baz <Doolan> .echo foo ‘bar baz’ <Wheaties> foo <Wheaties> bar baz <Doolan> .echo foo 'bar [smile]' <Wheaties> foo <Wheaties> bar [smile]
<Doolan> .echo foo "bar [smile]" <Wheaties> foo <Wheaties> bar :) <Doolan> .echo foo “bar [smile]” <Wheaties> foo <Wheaties> bar :)
Numbers come in two flavors: integer and decimal.
<Doolan> .add 123 456 <Wheaties> 579 <Doolan> .add 12.3 0.7 <Wheaties> 13.0
Regular expressions are an advanced argument type used to define search patterns that can be used by commands. Most users won’t need to use them. You can learn more about regular expressions on Wikipedia.
Regular expressions are contained within forward slashes /…/
with optional modifier characters after the trailing slash. The pattern can contain whitespace.
The parser uses Ruby’s Regexp
class. See its documentation for details on supported features.
Wheaties supports the following optional regular expression modifiers:
Ignore case
Treat a newline as a character matched by .
Ignore whitespace
These behave the same as Ruby’s Regexp
modifiers. Refer to Ruby’s documentation for details.
<Doolan> .delete /[aeiou\s]/ 'the quick brown fox' <Wheaties> thqckbrwnfx <Doolan> .replace /baz/i Qux 'Foo Bar Baz' <Wheaties> Foo Bar Qux
Group arguments into lists using parentheses ( )
. You can use any argument type inside the list, even other lists!
To send a memo to several people:
<Doolan> .memo (CCP Gaf Jep Mav) 'Anyone up for Worms tomorrow night?' <Wheaties> I sent your memo to CCP, Gaf, Jep, and Mav!
Lists can contain any argument type, including other lists. This example uses the inspect command, which outputs a Ruby representation of the arguments.
<Doolan> .inspect foo (bar ('baz qux' [smile])) flonk <Wheaties> ["foo", ["bar", ["baz qux", ":)"]], "flonk"]
Commands are wrapped in brackets [ ]
and contain a command name and any number of arguments of any type. The command name or its arguments may even be other commands! Don’t forget that you can embed commands inside of double-quoted strings, too.
<Doolan> .echo [smile] <Wheaties> :) <Doolan> .echo [repeat 3 [smile]] <Wheaties> :):):) <Doolan> .echo "weird: [delete /[aeiou\s]/ 'the quick brown fox']" <Wheaties> weird: thqckbrwnfx <Doolan> .echo [[random smile frown] 3] <Wheaties> :))) or :(((
Anyone can create and edit Wheaties' commands using this website. Some commands can be created or modified by interacting with Wheaties in a chat room.
There are five types of commands: plain text, random line, Ruby, YAML, and built-in.
When a plain text command is invoked, Wheaties sends each line of text to the chat room. For example, the traps command contains four lines:
When setting a trap you always have to remember three things: 1.) NEVER SET SPIKES! 2.) WATCH OUT FOR THE CLOUDS! 3.) LAY THOSE BRICKS DOWN!
When invoked in a chat room, Wheaties sends each line as a chat message:
<Doolan> .traps <Wheaties> When setting a trap you always have to remember three things: <Wheaties> 1.) NEVER SET SPIKES! <Wheaties> 2.) WATCH OUT FOR THE CLOUDS! <Wheaties> 3.) LAY THOSE BRICKS DOWN!
Random line commands behave similarly to plain text commands, except that instead of sending every line of the command as a message, Wheaties chooses one line at random. For example, the jcdenton command contains:
My vision is augmented. When due process fails us, we really do live in a world of terror. You mechs may have copper wiring to reroute your fear of pain, but I've got nerves of steel. A BOMB!
Each time it is invoked, Wheaties chooses a random line:
<Doolan> .jcdenton <Wheaties> A BOMB! <Doolan> .jcdenton <Wheaties> When due process fails us, we really do live in a world of terror. <Doolan> .alert [jcdenton] <Wheaties> /!\ My vision is augmented. /!\
Ruby commands run in a Ruby interpreter, and have access to the entire Ruby programming language, several Ruby gems, and many Wheaties helper methods.
For more information about writing Ruby commands, see the Ruby Commands chapter.
Note
|
This section has not yet been written. It will explain how comments at the top of Ruby commands are used by the help command.
|
YAML commands are rarely used directly. Instead, they’re meant as a way to store structured data that can be easily used in Ruby commands.
YAML commands are parsed using Ruby’s YAML library. The contents of the YAML command are parsed using Psych.safe_load, and the return value of that method is returned from the command invocation.
If a YAML command is invoked directly in a chat room, Wheaties will interpret it based on the top-level object in the YAML document:
If the YAML document is a single string or a number, it will be sent as a single message.
If the YAML document is an array, each element will be sent as a message. If the array contains other arrays, they will be interpreted recursively according to these rules.
If the YAML document is a dictionary or null
, no message will be sent.
For more details on the YAML language, see its website.
Some of Wheaties' helper methods are available as "built-in" commands that can be invoked directly in a chat room.
help
— Get help for a command.
version
— See Wheaties' version.
builtins
— See a list of these built-in commands.
env
— See information about Wheaties' Ruby process environment.
bold(text)
, b(text)
— Make text bold.
color(text)
, co(text)
— Control the foreground and background color of text.
italic(text)
, i(text)
— Make text italic.
underline(text)
, ul(text)
— Make text underlined.
plain(text)
, pl(text)
, unformat(text)
, uf(text)
— Strip all mIRC-style formatting from text.
increment(key)
, decrement(key)
— Increment or decrement integers in Redis.
get(key)
, set(key, value)
, del(key)
— Get, set, and delete string values in Redis.
hget(key, field)
, hset(key, field, value)
— Get and set Redis hash fields.
jget(key)
, jset(key, value)
— Get and set JSON-encoded values in Redis.
Ruby commands are the heart of Wheaties' interactivity. Within a Ruby command, you have access to information about the message that invoked the command, the chat channel and everyone in it, and a wide variety of third-party libraries and Wheaties helper methods.
Ruby commands can send messages back to the chat room, send private messages to individual users, or they can return standard Ruby objects for use by other Ruby commands.
If you’re new to Ruby, check out its official website and the community style guide to get started.
Wheaties evaluates Ruby commands using MRI Ruby 2.7.1.
These Ruby gems are loaded:
activesupport ~> 7.1, >= 7.1.3.2 (using require 'active_support/all'
)
cinch ~> 2.3.5 (a special fork just for Wheaties)
chronic_duration ~> 0.10.6
http ~> 5.2
httparty ~> 0.21.0
nokogiri ~> 1.16, >= 1.16.4
redis ~> 5.2
Ruby commands have access to a shared, persistent Redis database.
The redis
helper method returns an instance of the Redis
class from the redis Ruby gem, which can be used to interact directly with the Redis database. In addition, Wheaties provides several storage helper methods, like get(key)
and set(key, value)
, which provide some syntactic sugar over the Redis
instance methods.
Like Ruby methods and blocks, the last statement of a Ruby command becomes its return value. Wheaties interprets different types of return values differently.
String
or Numeric
If a command returns a String
or any kind of Numeric
, Wheaties will send it as a message to the channel or private message where the command was invoked.
Array
Each element of the array will be recursively evaluated according to these rules. For example, if a command returns ["foo", "bar", "baz"]
, Wheaties will send three messages to the channel or private message where the command was invoked.
If a command returns any other kind of Ruby object, including nil
, Wheaties will not send any message. If the command has been invoked by another command, its Ruby return value is available to the other command, no matter what type of Ruby object it is.
Ruby commands have access to a number of Wheaties helper methods.
Returns an array of arguments passed to the command.
Halts execution of the current command, returning an optional value. halt
is useful for implementing guard clauses, like when validating user input:
unless args.size >= 2
halt('please supply at least two arguments')
end
"hello #{args[0]}, welcome to #{args[1]}!"
Behaves like halt
, but halts execution of the entire command stack, even from within nested commands. halt!
is useful for implementing guard clauses in commands that might be called from other commands, or for bailing on a command invocation in the event of a fatal error.
begin
do_something_dangerous
rescue SomeError
halt!
end
For commands whose name is a regular expression, match
returns an instance of Ruby’s MatchData
representing the pattern match.
Returns true
if the current command is at the top of the stack, i.e. it was the first command executed by the user or event; or false
otherwise.
Calls #inspect
on each argument, then sends the output to the sender in a private message prefixed with the name of the current command, and prints it to Wheaties' server log. (The server-side logging will only occur if Wheaties' server log level is set to "debug", which is normally not the case, so don’t worry about filling his logs with your debug output.)
debug
is useful for inspecting variables while editing a command. Since it includes the current command name in its message, it’s especially useful when debugging output from nested commands.
If the given command includes documentation, help
returns a multi-line string describing how to use the command.
# Greet someone.
# Usage: greet NAME
"Hello, #{args.first}!"
<Doolan> .help greet <Wheaties> greet: Greet someone. <Wheaties> Usage: greet NAME <Doolan> .greet Gaf <Wheaties> Hello, Gaf!
Returns an array of Wheaties' built-in command names.
Returns the name of Wheaties' current environment: development
, staging
, or production
.
Returns Wheaties' current version.
Wheaties' formatting helpers style text using mIRC-style formatting codes. If you have complex formatting needs, you can use Ruby’s sprintf to construct a string without needing to do lots of #{…}
interpolation:
sprintf('some %s text', b('emphasized'))
sprintf('hello %{name}, welcome to %{place}!',
name: bold(italic(sender.nick)), place: green(underline('#perkele'))
)
All of the formatting helpers are available as built-in commands, so you can use them directly in chat:
<Doolan> .echo "hello [bold [italic Gaf]], welcome to [green [underline #perkele]]!" <Wheaties> hello Gaf, welcome to #perkele!
Colors text
with a foreground color and optional background color. foreground
and background
can be strings or symbols. The available colors are:
aqua
black
blue
brown
green
grey
lime
orange
pink
purple
red
royal
silver
teal
white
yellow
In addition to the color
and co
helper methods, each color has its own helper method that accepts a single text
argument. These color-specific helper methods only set the foreground color, so if you want to control background color, you’ll need to use color
or co
.
co(foreground, background = nil, text)
# Using co(...)
"Have some #{co(:green, 'colorful')} #{b(co(:white, :blue, 'text!'))}"
# Using the color-specific helper methods
"Have some #{green('colorful')} #{b(blue('text!'))}"
Inverts the foreground and background colors of text
. If text
doesn’t have any color formatting applied to it, most chat clients will invert whatever its default text color is.
inv(text)
Returns an instance of Cinch::Channel
representing the channel in which the command was invoked, or nil
if the command was invoked in a private message.
Returns true
if the command stack was invoked directly by a user in a chat room, or false
if the command stack was invoked automatically in response to an event.
Returns true
if the command stack was invoked automatically in response to an event, or false
if the command stack was invoked directly by a user in a chat room.
Returns an instance of Wheaties::TargetHistory
for the channel in which the command was invoked, or for the user in whose private messages the command was invoked.
Wheaties::TargetHistory
implements a selection of standard Ruby Enumerable
methods, and convenience methods such as #for_matching_nick(nick)
and #for_user(Cinch::User)
, which can be used to read the message history of the current channel or private messages.
Returns an instance of Cinch::Message
representing the chat message that invoked the command.
msg.message
channel
helper)msg.channel
sender
helper)msg.user
Returns true
if the command was invoked in a private message, or false
if the command was invoked in a channel.
Important
|
This method is provided for backwards compatibility, and should not be used in new code. Instead of response.text , use the msg helper: msg.message .
|
Returns an instance of Wheaties::ResponseShim
to provide backwards compatibility with old Wheaties commands that access the message text using response.text
.
Returns an instance of Cinch::User
representing the user who invoked the command, whether they invoked it directly, or indirectly via an event.
sender.nick #=> "Doolan"
sender.user #=> "doolan"
sender.realname #=> "Harry Flashman"
Wheaties provides several storage helpers that work with his Redis database.
Decrements key
in Redis by an integer amount. Equivalent to calling Redis#decrby
, which calls Redis’s DECRBY
command. Returns the integer value of key
after the decrement.
get('foo') #=> 3
decrement('foo') #=> 2
decrement('foo', by: 3) #=> -1
Returns true
if field
exists in the Redis hash key
, or false
otherwise. Equivalent to calling Redis#hexists
, which calls Redis’s HEXISTS
command.
Returns field
from the Redis hash key
, or default
if key
or field
doesn’t exist. Equivalent to calling Redis#hget
, which calls Redis’s HGET
command.
Sets the value of field
in the Redis hash key
. Equivalent to calling Redis#hset
, which calls Redis’s HSET
command.
Increments key
in Redis by an integer amount. Equivalent to calling Redis#incrby
, which calls Redis’s INCRBY
command. Returns the integer value of key
after the increment.
increment('foo') #=> 1
increment('foo', by: 4) #=> 5
Returns the JSON-decoded value of key
in Redis. If key
doesn’t exist, returns default
. The value stored in Redis is decoded using Ruby’s JSON.parse
.
Returns an instance of Redis
that’s connected to Wheaties' Redis database. This is the same Redis client used by the other storage helpers.
Sets the string value of key
in Redis. Equivalent to calling Redis#set
, which calls Redis’s SET
command.
Tip
|
To use any of the options available to Redis#set , such as setting expiration time or only setting a value if key doesn’t already exist, use the Redis client directly, e.g. redis.set('foo', ex: 3600, nx: true) .
|