This file documents Bobot++ by Clinton Ebadi and Etienne Bernard (original author, no longer works on program).

Copyright 2002,2004,2005 Clinton Ebadi

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts.

This document describes Bobot++ by Clinton Ebadi and Etienne Bernard (original author, no longer works on program).

This document applies to version 2.1.5 of the program named Bobot++

Copyright 2002,2004 Clinton Ebadi

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts.

Introduction
************

This manual feels abused and neglected because it has almost no content.

Configuration
*************

Bobot++ is easy to configure. See the examples directory for an example configuration.

Configuration Files
===================

bot.conf
--------

bot.conf contains key value pairs separated by =.

<key> = <value>

Comments are started with a # and cause the entire line to be ignored. Note that this only works when the # is the first character of the line.

bot.conf is the main configuration file for a Bobot++. The available configuration variables are listed below in the format ``VARIABLE <default-value>: description''

  * NICKNAME <Bobot>: The nickname of the bot (NICK is an alias for NICKNAME)

  * USERNAME <bobot>: The IRC username of the bot

  * CMDCHAR <!>: The character that prefixes commands given to the bot (COMMAND is an alias for CMDCHAR)

  * IRCNAME <I'm a bobot++!>: The IRC name (or 'real name') of the bot (REALNAME is an alias for IRCNAME)

  * USERLIST <bot.users>: Name of the file where the userlist is stored

  * SHITLIST <bot.shit>: Name of the file where the shitlist is stored

  * LOGFILE <$LOGDIR/bot.log>: Location of the bot logfile (set this to /dev/null to disable logging).

  * SERVER <None>: This specifies the server to connect to. Note that this has a special syntax.

  * CHANNEL <None>: This specifies a channel the bot will join when it starts up. This also has a special syntax.

server syntax
.............

SERVER = SERVER_NAME [PORT [PASSWORD]]

This will make Bobot++ attempt to connect to SERVER_NAME on port PORT with the password PASSWORD. SERVER_NAME should be the address of the server. PORT and PASSWORD are optional. You may use more than one server line; Bobot++ will attempt to connect to the first one and, if it fails, will connect to the next one in the list. There is also a command to cause the bot to cycle servers. At the present time Bobot++ cannot connect to more than one server at a time. This is a planned feature of 3.0 (which is a very long way away; the current structure of the program would make it very difficult to add support for connecting to multiple servers at a time in a usable manner).

channel syntax
..............

CHANNEL = NAME:INITIAL_MODES:MODES_TO_KEEP:CHANNEL_KEY

You may have any number of channel lines. When Bobot++ starts it will attempt to join and gain ops in every channel listed. It will join NAME and set the channel modes to INITIAL_MODES (e.g. ``nt'') if it is able to gain operator status. It will then maintain MODES_TO_KEEP. If the channel requires a key to enter simply set CHANNEL_KEY. Every argument except for NAME is optional.

A few example lines:

CHANNEL = #foo:nt:nt:bar

The bot will join #foo with the channel key bar and will then maintain the modes nt.

CHANNEL = #bar:::

The bot will join #bar and will not set any modes nor will it attempt to maintain any modes.

bot.users
---------

bot.users is the default file name of the userlist. It may be changed in bot.conf. The file contains lines with the format:

MASK:CHANNEL:LEVEL:PROTECTION:AUTO-OP

  * MASK is the host mask (e.g. *!*username
     .domain.com) of the user

  * CHANNEL is a channel mask of the channels that the user has priviliges to use the bot in (e.g. * for all channels, #* for all non-local channel, #foo* for all channels starting with ``foo,'' #bar for channel ``#bar'' only, etc.)

  * LEVEL is the user level of the user (User Levels).

  * PROTECTION is the protection level of the user (Protection).

  * AUTO-OP is set to control whether a user is automatically given operator priviliges on channel entry (Automatic Op).

Configuration File Placement
============================

     [ I kinda think this should go before the detailed description of the config file.  I was didn't see it at first and was very frustrated trying to find out where to edit all this wonderful stuff. ]

Bobot++ will look in /etc/bobotpp/default/ for its default config if none is specified on the command line. Put the configuration files you want to be loaded by default in this directory. If you are not root or you want to have your own personal configuration, put it in ~/.bobotpp/config/default/.

Using Bobot++
*************

FIXME: stuff here...

Starting the Bot
================

The bot is usually installed with the binary name bobotpp. It accepts the following command line arguments:

  * [--help][-h] - Shows detailed help and exits

  * [--version][-v] - Shows version information and exits

  * [--no-background][-b] - Run bobot++ in the foreground

  * [--config-file file][-f] - Use file instead of bot.conf

  * [--config-dir dir][-d] - Use dir as dir to load config file from

  * [--config dir][-c] - Search your config path (defaults to $HOME/.bobotpp/config/ and then /etc/bobotpp/) for dir and then loads your config data using dir

  * [--sys-config dir][-s] - Looks for config in /etc/bobotpp/dir. Note that the user dir is still searched first

  * [--user-config dir][-u] - Looks for config in $HOME/.bobotpp/config/dir/. Note that the system dir is still searched after this if dir is not found.

  * [--debug][-D] Makes Bobot++ print debugging info and run in the foreground

  * [--debug-scripts][-S] Enabled the Guile debugging evaluator for verbose script errors and backtraces while still running the bot in the background.

The default configuration is read from $HOME/.bobotpp/config/default/ and then /etc/bobotpp/default/ if the user config is not found.

The bot defaults to running in the background as a daemon.

User Levels
===========

There are several user levels available in Bobot++ to provide gradated access to commands. !adduser and bot.users use the numeric code; Scheme uses the textual name for the level. By default (if no catch-all setting is found in see bot.users.) a user is not even a bot:user-none and cannot execute any commands, even commands available to bot:user-none.

 0. bot:user-none - No built-in commands may be executed by default (commands may be added from Scheme that can be executed by users of level none and the level required to execute a command may be changed from Scheme).

 1. bot:user-user - Will be able to execute most commands but not all and cannot use masks on kicks and bans.

 2. bot:user-trusted - For built-ins with a default configuration this user has access to the same set of commands as an user but may use masks on kicks and bans. Scheme commands may be added which require a user to be of this level.

 3. bot:user-friend - In the default configuration a user who is a friend will be able to do everything short of stopping the bot. Again, there may be user added commands that require a higher user level.

 4. bot:user-master - This is the highest user level and has access to every feature of the bot.

Protection
==========

A user added via Scheme, the bot.users file, or !adduser may be protected from being deoped, kicked, or banned. There are currently no symbolic levels in Scheme; just use the numeric code.

 0. No protection

 1. No ban. If a user is banned the bot will unban him..

 2. No kick. The user may still be kicked but the bot will kickban the user who kicked the protected user.

 3. No deop. The bot will ensure that the user always maintains operator status.

Automatic Op
============

A user may be automatically given operator status upon entering a channel. Set the AOP field to ``0'' to disable auto-op or ``1'' to enable auto-op.

Built-In Commands
=================

Bobot++ has many built-in commands that make it useful without scripting support. The reference leaves off the command char; remember to use whatever you defined the command char to be in bot.conf. If a command needs the channel name then you must specify the channel as the first argument to the command when private messaging the bot a command.

COMMAND   NEEDS CHANNEL   MIN LEVEL TO USE   DESCRIPTION

action do   Yes   USER
Causes the bot to perform the action do in the current
channel.

adduser

addserver       Adds the server specified by
HOST NAME or IP ADDRESS to the server list.

addshit

alias       Makes an alias, and adds the
function NEW NAME, that will do exactly the same command as
OLD NAME.

ban       Bans MASK or NICK from
CHANNEL. You need to be a trusted user to ban with a
MASK.

banlist

channels       Prints the channel(s) where
the bot is currently.

cycle   Yes     Makes the bot leave and join
CHANNEL.

dcclist       Gives the list of all DCC Chat
connections.

deban   Yes     Debans MASK or
NICK from CHANNEL. You need to be a trusted user to deban
with a MASK.

delserver       Deletes server from server
list whose number in the server list is SERVER NUMBER.

deluser       Removes NICK or
MASK from the userlist.

delshit       Removes NICK or
MASK from the shitlist.

deop   Yes     Deops MASK or NICK
on CHANNEL.

die       Makes the bot stop immediately.

do

execute       Only available if
scripting support is enabled

help

ident       Identifies you on the bot. Note
that you should not use this command in public ...

invite   Yes     Invites NICK on
CHANNEL.

join       Makes the bot join CHANNEL.

keep   Yes     Sets the MODES that the
bot will keep for CHANNEL.

kick   Yes     Kicks MASK or NICK
out of CHANNEL, because of REASON. You need to be a
trusted user to use a MASK.

kickban   Yes     Bans then kicks MASK
or NICK out of CHANNEL, because of REASON. You need
to be a trusted user to use a MASK.

load       Reloads the userlist from disk.

loadscript       Only available if
scripting support is enabled

lock       Locks topic on CHANNEL.

mode   Yes     Sends MODE STRING as
mode for CHANNEL.

msg

names   Yes     Shows the nicknames and
status of users on CHANNEL.

nextserver       Makes the bot connect to the
next server in its server list.

nick       Makes the bot use nickname NICK.

nslookup       Does a nameserver query about
NICK host, HOST or IP ADDRESS.

op   Yes     Ops NICK on CHANNEL.

part   Yes     Makes the bot leave CHANNEL.

password       Changes your password on the
bot. Use NONE as password if you want to clear it. Do not use this
command in public!

reconnect       Makes the bot reconnect to
its current server.

rspymessage       Removes you from the spy
list.

save       Saves the userlist.

say   Yes     Makes the bot say MESSAGE
on CHANNEL.

server       Select the server to connect
to. SERVER NUMBER is the number of the server in the serverlist.

serverlist       Shows the bot's serverlist.

setfloodrate

setversion

shitlist       Shows the bot's shitlist.

spylist       Shows the bot's spylist.

spymessage       Adds you to the spylist

stats   Yes     Gives CHANNEL's statistics.

tban   Yes     Bans NICK or MASK
from CHANNEL for TIME seconds.

tkban   Yes     Bans NICK or MASK
from CHANNEL for TIME seconds, then kicks him/them because
of REASON.

topic   Yes     If no TOPICis given,
prints CHANNEL's topic. Otherwise, the bot will change
CHANNEL's topic to TOPIC.

unlock   Yes     Makes the bot unlock topic
on CHANNEL

userlist       Shows the bot's userlist

who   Yes     Show your level on CHANNEL

whois   Yes     Shows information about
NICK on CHANNEL

Scripting
*********

Bobot++'s most powerful feature is its scripting system. You write scripts using Guile Scheme. This manual does not cover how to use Guile or how to learn Scheme. See Guile Reference Manual (The Guile Reference Manual), for the Guile reference manual and http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html for a good tutorial on Scheme.

Note that in previous versions the scripting commands where in the form bot-FUNCTION. They are now in the form bot:FUNCTION. The old names are still available, but are deprecated and will be removed in Bobot++ 3.0. New commands are only available with the bot: prefix. The command perl -pi -e ``s/bot-/bot:/g'' YOUR-FILES should be enough to convert your code to use the new functions.

Adding New Commands
===================

Adding a new command is simple. To register a new command use bot:addcommand.

 -- Function: bot:addcommand name func needs-channel? num-of-args min-level

     The NAME is a string representing the name of the command being added.  FUNC is a function accepting NUM-OF-ARGS arguments. NEEDS-CHANNEL? is a bool that is true if the function needs the channel name as its first arg, and false otherwise. NUM-OF-ARGS is the number of args FUNC will take and must be within zero (0) and twenty (20).  MIN-LEVEL is one of the Scheme User Levels.  A user must be at least a min-level user to use the new command.  None of the arguments are guaranteed to be passed; if they aren't they are set to the empty string "". An example of a new command would be:

          (define (hello channel name)
            (if (string=? name "")
              (bot:say channel "Hello world!")
              (bot:say channel (string-append "Hello " name "!")))

          (bot:addcommand "hello" hello #t 2 0)

     This will display ``Hello World!'' if called as !hello and ``Hello World USER'' if called as !hello USER.

Hooks
=====

Hooks are a powerful feature of Bobot++. Hooks are a hybrid of ircII and tiny fugue (a MUD bot) hooks with a little bit of extra stuff added in. A hook is called when a regular expression is matched against a message sent to or by the bot.

Bobot++ uses different hook types for each IRC message type, and also includes a hook for accessing raw irc messages. Hooks are tagged with a priority and a flag that specifies whether to call the next hook that matches after calling the current one or to stop processing.

Hooks are processed from the highest to lowest priority, with fallthrough hooks of equal priority to non-fallthrough hooks being executed first.

Creating a Hook
---------------

To add a new hook you use the function bot:addhook.

 -- Function: bot:addhook type regex function [pri fall name]
     TYPE specifies the type of hook (the types of hooks are listed in Hook Types). REGEX is a standard regular expression. If REGEX is matched, FUNCTION will be called. FUNCTION will take a different number of args depending on the hook type. PRI specifies the priority of the hook---higher priority hooks are executed first. This argument is optional and defaults to 0. FALL is #t if the hook is a fallthrough hook and #f is the hook is not a fallthrough hook. This arg is also optional and default to #t.  NAME is the optional name of the hook that defaults to ``DEFAULT''. If you set the name then you can have more than one hook that matches the same regexp, as long as they have the same name. E.g. in a log script you could have the regexps for the log function all be ".*" and set their names to "log" to avoid a conflict with other hooks.

Hook Types
----------

Here is a list of the various hooks funtions and notes on each one. The general format of each hook description is as if it was was function to be defined, but these describe the function to be passwd to bot:add-hook.  Do not name your functions these names.

     [ Boy, that's clumsy.  I want to say that the hook/xx functions that are documented below are not funtions that you call.  They are the functions that needs to be passed to bot:addhook for that kind of hook.

     Still clumsy.  Oh well. ]

That said, here is the list of available hooks functions. FIXME: write docs

 -- Function: hooks/action from to action
     This hook is triggered when someone performs an action.  FROM is the address of the person that performed the action in the form NICK ! USER @ HOST (without the spaces). TO is the target of the action, which is either a channel or the Bot's nick.  ACTION is the text of the action. E.g. if someone did * foobar does baz, then ACTION would be the string "does baz".

 -- Function: hooks/nickname old-nick new-nick
     This hook gets called when someone changes thir nickname from OLD-NICK to NEW-NICK.

 -- Function: hooks/signoff nick rest

 -- Function: hooks/ctcp nick to command rest

 -- Function: hooks/ctcp-reply nick command rest

 -- Function: hooks/disconnect server
     This is called when the bot is disconnected from a server unintentionally. hooks/signoff is called when the bot purposefully disconnected. The hook function is passed the hostname of the server it was disconnected from.

 -- Function: hooks/flood nick

 -- Function: hooks/invite nick channel

 -- Function: hooks/join nick channel

 -- Function: hooks/kick target from channel reason

 -- Function: hooks/leave nick channel
 -- Function: hooks/part nick channel

 -- Function: hooks/mode nick channel modes

 -- Function: hooks/message from message

 -- Function: hooks/notice nick message

 -- Function: hooks/public from to message

 -- Function: hooks/public-notice nick to message

 -- Function: hooks/raw raw-message

 -- Function: hooks/timer time
     This hook seems to be called once a minute.  TIME is in hh:mm format.

 -- Function: hooks/topic nick channel new-topic

 -- Function: hooks/send/public mynick dest message

 -- Function: hooks/send/message botnick message

 -- Function: hooks/send/action mynick to message

 -- Function: hooks/send/ctcp mynick to command message

 -- Function: hooks/dcc/chat-begin from
     This hook is triggered when a user begins a DCC CHAT with the bot. FROM is the user's address in the form nick!user@host.

 -- Function: hooks/dcc/chat-message from message
     This hook is triggered when a user sends a message to the bot through a DCC CHAT FROM is the user's address in the form nick!user@host.  MESSAGE is the message the user sent to the bot.

Scheme User Levels
==================

There are five levels that a user may be when interfacing with a bot: NONE, USER, TRUSTED_USER, FRIEND, MASTER. The Scheme variables for the user levels are bot:user-none, bot:user-user, bot:user-trusted, bot:user-friend, and bot:user-master. See User Levels for more information on User Levels.

When adding a new command, think about who should be able to use it. Is your command a general purpose command that helps the channel (e.g. !seen) that everyone should be able to use? Or is it something that should be restricted? See User Levels for information on what level users can do what with the built in bot commands and think about what level a user your command is targetted towards. You must be very careful when giving new commands to lower level users because you can do basically everything the bot can do with a script. As the scripting interface becomes more powerful, you must think more about what users can use new commands you add.

Sending Messages
================

There are several types of messages you can send with Bobot++ from scripts. There is the simple, but rather limited, bot:say, bot:action and bot:msg, and the more powerful, but lower level, bot:send-MESSAGE functions. Most bots will probably only need the higher level functions, but for the sake of why-not Bobot++ lets you use the lower level functions (in progress).

``High Level'' Message Functions
--------------------------------

 -- Function: bot:say channel message
     Send a public or private MESSAGE to CHANNEL.

     Sends a normal text message, as if a user had typed it in.  The DEST can be a nickname or a channel.

 -- Function: bot:action channel message
     Send an ``action'' type  MESSAGE to CHANNEL

 -- Function: bot:msg nick message
     The same as if a user typed /msg nick message to their IRC client.

 -- Function: bot:notice target message
     Sends MESSAGE as a NOTICE to TARGET. TARGET may be a user (nick) or a channel. This returns 0 on success.

``Low Level'' Message Functions
-------------------------------

The ``Low Level'' messaging functions allow you to do things like send CTCP messages. You probably want to read rfc 2812 and the CTCP spec before using these. If you have no idea what these do, read rfc 2812 (IRC Client Protocol) and CTCP spec. These functions all return *unspecified* always, so don't use the return value for anything.

 -- Function: bot:send-ctcp to command message
     to is the target of your CTCP message, command is the CTCP command, and message is the message (or arguments) of the command. Make sure to bot:ctcp-quote the message!

 -- Function: bot:send-ctcp-reply to command message
     to is the target of your CTCP reply, command is the CTCP command, and message is the message (or arguments) of the command. Make sure to bot:ctcp-quote the message!

     This is used to reply to a ctcp that the bot has received.

Misc. Scripting Stuff
=====================

These are a few useful things that I thought people writing scripts might want to know.

If you want to execute code when the bot exits, just do add-hook! bot:exit-hook THUNK where THUNK is an argumentless procedure (a thunk). When the bot exits your thunk will be called.

     [ I didn't know where to put any of these, so I jsut stuck them in here.

     There probably needs to be several sections added, like dealing with users (kicking, added, etc), dealing with the bot (channels, nickname of the bot, etc), server issues (serverlist), useful tools (nslookup, whois), and do on. ]

 -- Function: bot:adduser nick-or-mask cbannel-mask level prot auto-op
     Adds an user to the userlist, for a nick!user@host matching the one given, on a channel matching the CHANNELMASK given.

     The LEVEL can be:   The PROT can be:    The AUTO-OP can be:
     0 - No level          0 - No protection    0 - No auto-op
     1 - User              1 - No ban           1 - Op on join
     2 - Trusted User      2 - No kick
     3 - Friend            3 - No deop
     4 - Master

 -- Function: bot:addserver hostname ip-address [portnumber]
     Adds the server specified by HOSTNAME or IP-ADDRESS to the server list.

 -- Function: bot:addshit nick-or-mask channel-mask level [time reason]
     Adds an user to the shitlist, for a nick!user@host matching the one given, on a channel matching the CHANNELMASK given.

          The LEVEL can be:
            0 - No shit
            1 - No op
            2 - Kick and Ban on join
            3 - Kick and Ban on join, no deban

 -- Function: bot:ban channel mask-or-nick
     Bans MASK or NICK from CHANNEL. You need to be a trusted user to ban with a MASK.

 -- Function: bot:change-command-level nick-or-mask channel-mask new-level
     Gives NICK or MASK level NEW-LEVEL on channel(s) CHANNEL-MASK. Note that you can not change level for someone whose level is greater than yours, and that you can not give a level greater than yours.

 -- Function: bot:cycle channel
     Makes the bot leave and join CHANNEL.

 -- Function: bot:deban channel mask-or-nick
     Debans MASK or NICK from CHANNEL. You need to be a trusted user to deban with a MASK.

 -- Function: bot:delserver server-number
     Deletes server from server list whose number in the server list is SERVER-NUMBER

 -- Function: bot:deluser nick-or-mask channel-mask
     Removes NICK or MASK from the userlist.

 -- Function: bot:delshit nick-or-mask channel-mask
     Removes NICK or MASK from the shitlist.

 -- Function: bot:deop channel mask-or-nick
     Deops MASK or NICK on CHANNEL.

 -- Function: bot:die reason
     Makes the bot stop immediately.

 -- Function: bot:do ?

 -- Function: bot:invite channel nick
     Invites NICK on CHANNEL.

 -- Function: bot:join channel
     Makes the bot join CHANNEL.

 -- Function: bot:keep channel modes
     Sets the MODES that the bot will keep for CHANNEL. See also STATS.

 -- Function: bot:kick channel mask-or-nick [reason]
     Kicks MASK or NICK out of CHANNEL, because of REASON. You need to be a trusted user to use a MASK.

 -- Function: bot:kickban channel mask-or-nick [reason]
     Bans then kicks MASK or NICK out of CHANNEL, because of REASON. You need to be a trusted user to use a MASK.

 -- Function: bot:lock channel
     Locks topic on CHANNEL.

 -- Function: bot:logport
     [ Probably returns the log port? ]

 -- Function: bot:mode channel mode-string
     Sends MODE-STRING as mode for CHANNEL.

 -- Function: bot:nextserver
     Makes the bot connect to the next server in its server list.

 -- Function: bot:nick nick
     Makes the bot use nickname NICK.

 -- Function: bot:op channel nick
     Ops NICK on CHANNEL.

 -- Function: bot:part channel
     Makes the bot leave CHANNEL.

 -- Function: bot:reconnect
     Makes the bot reconnect to its current server.

 -- Function: bot:server server-number
     Select the server to connect to. SERVER-NUMBER is the number of the server in the serverlist.

 -- Function: bot:setfloodrate ?

 -- Function: bot:setversion ?

 -- Function: bot:tban channel nick-or-mask time
     Bans NICK or MASK from CHANNEL for TIME seconds.

 -- Function: bot:tkban channel nick-or-mask time [reason]
     Bans NICK or MASK from CHANNEL for TIME seconds, then kicks him/them because of REASON.

 -- Function: bot:topic channel topic
     If no TOPIC is given, prints CHANNEL's topic. Otherwise, the bot will change CHANNEL's topic to TOPIC.

 -- Function: bot:unlock channel
     Makes the bot unlock topic on CHANNEL.

 -- Function: bot:getnickname
     [ Gets the bot's nickname? ]

 -- Function: bot:getserver

 -- Function: bot:getserverlist

 -- Function: bot:flush
     [ Flushes the socket to the server? ]

 -- Function: bot:flushport
     [ Flushes the log port? ]

 -- Function: bot:random ?
     [ Returns a random number?  What range?  Why? ]

 -- Function: bot:delcommand
     [ Probably deletes a command added with bot:addcommand ? ]

 -- Function: bot:addtimer ? ?

 -- Function: bot:deltimer ?

 -- Function: bot:dcc-chat-send ? ?

     [ And what about the stuff defined in bobot-utils.scm ? I just added it here so it could be somewhere.  There should also be a section dealing with modules.  How to use them.  What module scripts are in.  What module bobot++ provided primites are in.  And so on. ]

 -- Function: bot:log . messages
     Write as many MESSAGES as you want to the log.  If the arg is a thunk it will be executed and it's output will be written to the log.

 -- Function: bot:load file

 -- Function: bot:load-module module-spec

 -- Function: bot:use-module module-spec

 -- Function: bot:match-not-channel regex
     match-not-channel adds a prefix regex to your REGEX so it doesn't match the sender or channel in a PUBLIC message

 -- Function: bot:match-to-me regex
     match-to-me matches text that was addressed to the bot with a ':',  ',',  or nothing after the bot name.

 -- Function: bot:sent-to-me? message

 -- Function: bot:ctcp-quote message
     Returns the CTCP quoted message Input MUST NOT contain the trailing \r\n (it is added by the message sending code).

 -- Variable: %bot:loadpath

 -- Function: %bot:load-extensions