$$$$$$$$$$$$$$$$$ &&&&&&&&&&&&& $$$$$ $$ &&&&& &&& $$$ $ &&& && $$ && && $ & && $$ $ &&& && $$$$ $$ &&&& &&& $$$$$$$$$$$$$$$ S O T E R I C &&&&&&&&&&&& O T $$$$ $$ &&&& &&& $$ $ &&& && $ && && $$ & && $$$ $ && && $$$$$ $$ &&&& &&& $$$$$$$$$$$$$$$$$ &&&&&&&&&&&&&& -------------------------------------------------------------------------------- READ ME FIRST -------------------------------------------------------------------------------- CONTENTS: * 1. General Info * 2. Usage ( for IRC users ) * 3. Usage ( for admin ) * 4. Configuration * 5. Plugins * 6. Hacking * 7. Credits * 8. License -------------------------------------------------------------------------------- 1. GENERAL INFO -------------------------------------------------------------------------------- Esotericbot is a sophisticated, lightweight IRC bot, written in Haskell. Esotericbot launches sub-processes to respond to commands issued by users over IRC. Why would you use esotericbot? For lambdabot-4.2.2.1: spoon@utensil:~/esotericbot$ ps e v PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 23433 pts/3 Sl+ 0:00 0 8228 84855 33476 1.6 lambdabot 15040 pts/5 S+ 0:00 0 2171 28804 3292 0.1 ./esotericbot I'm not insinuating that lambdabot sucks in any way, ( it has cool stateful features, and is stable and excellent ) however, my hope is that esotericbot could attain equal functionality through plugins, while maintaining its small memory footprint. Esotericbot is small ( for Haskell :), designed to run on a server with limited memory - typically running at under 5mb of private memory. Features: * Advanced memory and time management options, utilizing POSIX resource management. For example, the memory used by all child processes, can be limited - commands be queued until the memory usage has dropped. * Privileged channel operator commands ( Currently, can be disabled and enabled by channel operators, to prevent abuse. ) * Easy to add new plugins. A C library is included to make writing new plugins/adapting programs easy. * Set up through a configuration file. * Multiple channels. * Multi-threaded * Lightweight, typically running at under 5mb of private memory. Cons: * POSIX memory management, process forking, and chroot means that Esotericbot is POSIX only. * Esotericbot is, due to the libraries and extensions used, restricted to being compiled by the Glorious Glasgow Haskell Compiler. See http://www.haskell.org/ghc -------------------------------------------------------------------------------- 2. USAGE ( for IRC users ) -------------------------------------------------------------------------------- Invoke esotericbot through sending him a message, or by sending a message to a channel in which esotericbot is currently lurking. The default prefix for commands is ';' Try 'help' to see which commands are available. Example: \msg esotericbot ;help -------------------------------------------------------------------------------- 3. USAGE ( for admin ) -------------------------------------------------------------------------------- Usage: esotericbot [configuration_file] Esotericbot optionally takes one command line argument, which is either the string "help", which will give you this information, or it will be a file system location, specifying the location of a configuration file. If this argument is not provided, esotericbot will look for a file called esotericbot.conf in the present working directory, and use that as the configuration file. For cabal users: esotericbot comes with several plugins which will be installed into your cabal-bin, and libraries installed into your cabal-lib. Check cabal-bin for 'brainfuck', 'unlambda' , 'malbolge', 'sk' and 'whirl'. These will need libplugin and libplugin++ from your cabal-lib to function correctly. -------------------------------------------------------------------------------- 4. CONFIGURATION -------------------------------------------------------------------------------- The easiest way to configure esotericbot is to read the sample configuration file ( esotericbot.conf ) and modify it for your own purposes. This section describes the syntax and semantics of the esotericbot configuration file. Firstly, I will attempt to describe the syntax of the configuration file ( because it's very simple really ). Here, I will also provide examples. Secondly, I will give a formal grammar, to reduce ambiguity. ( You probably won't need to look at this, if you glance over the examples. ) Thirdly, ( ...and this is the semantics bit ) I will discuss the specific options available, and give examples. ****** Simply ****** The configuration file uses 4 simple constructs. * 1. A simple variable is a key = "value" pair. Example: key = "value" * 2. A list variable is a key = list of "value"s pair. Example: key = [ "value1" , "value2" , "value3" ] * 3. A complex variable is a key { simple variables } structure. Example: key { inner_key1 = "inner_value1" inner_key2 = "inner_value2" inner_key3 = "inner_value3" } * 4. A comment Example: -- I am a comment, and I will last to the end of the line. You might be wondering: Why not XML? Well, in my opinion, for this specific case, XML doesn't read as nicely as simple key=value pairs, which constitute the bulk of the file. Anyway.... ******** Formally ******** The syntax of the configuration file is defined by the following informal grammar ( It's not really BNF, because I'm lazy ). Note: This looks right, but if you want to know exactly what it is, read Esotericbot/Config.hs. The ambiguity police say: Literals are surrounded by single inverted commas, ie, these: ' + means one or more of the previous term. Being enclosed by square brackets [ ] means the term is optional. Curly brackets ( ) are used to group terms. Config -> Conf_var Config | End_of_file conf_var -> -- See note Comment | List_var | Complex_var | Simple_var Simple_var -> Identifier [ Whitespace ] '=' [ Whitespace ] '"' Identifier '"' Complex_var -> Identifier [ Whitespace ] '{' ( [ Whitespace ] Simple_var Whitespace )+ '}' List_var -> Identifier [ Whitespace ] '=' [ Whitespace ] '[' List_elements List_elements -> [ Whitespace ] '"' Identifier '"' [ Whitespace ] Next_element Next_element -> ',' List_elements | ']' Comment -> '-' '-' Till_end_of_line Till_end_of_line -> Anything_really_at_all Till_end_of_line | '\n' -- Note: The Conf_var alternatives are attempted in the order given, otherwise incorrect parsing results. *********** The Options *********** &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&CORE OPTIONS&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& These options control the esotericbot itself. $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ nick $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ The 'nick' option is a prerequisite for connecting to an IRC server, obviously. 'nick' is a simple variable. Example: nick = "Joe_Bloggs_666" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ pass $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ The 'pass' option is not prerequisite for connecting to IRC, but is recommended. The most unfortunate thing is, it's stored as plaintext. 'pass' is a simple variable. Example: pass = "super_secret_lololol" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ server $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Again, unless you're SHODAN, you'll probably need this option to connect to a server. 'server' is a simple variable. Example: server = "irc.freenode.org" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ chans $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Specify the channels the bot initially connects to. `chans` is a list variable. Example: chans = [ "#haskell" , "#esoteric" , "#egotistical_bot_authors" ] $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ user $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Give a numeric value representing a unix user ID. Specify this option to switch to this unix user after connecting. You're going to need this if you use the 'chroot' option. 'user' is a simple variable. Example: user = "1000" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ chroot $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Specify a file system location. Esoteric bot will first cd to this location, then chroot in it. Incase you did not realize, you must be root to chroot. You MUST also specify `user` if you use chroot - any application must change to a non-root user after chroot. "If you have the ability to use chroot() you are root. If you are root you can walk happily out of any chroot by a thousand other means." - Alan Cox Example: chroot = "/home/esotericbot/execution_dir" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ mem_limit $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Given a integer, restrict the memory use of the esotericbot process to this many bytes. Uses posix resource management. Example: mem_limit = "40000000" -- restrict memory usage to 40mb ( remember, this is counting both private memory and the memory used by various shared libraries ). $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ time_limit $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ `time_limit` restricts the time ( in seconds ) for which a child process can run. After this time, the child will recieve SIG_TERM. Example: time_limit = "5" -- After a plugin has been launched, send it SIG_TERM after 5 seconds. $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ hard_time_limit $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ `hard_time_limit` is like time_limit, but sends SIG_KILL, rather than SIG_TIME. Example: hard_time_limit = "10" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ children_mem_limit $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Given a number in bytes `children_mem_limit` restricts the memory use of all child processes together. Example: chilren_mem_limit = "30000000" -- The memory of all child processes together shall not exceed 30mb $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ output_limit $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Given an integer, crop esotericbot's responses to IRC commands to this length. Example: output_limit = "100" &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&PLUGIN OPTIONS&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ default_child_mem_limit $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Given an integer, restrict each child's memory usage to this number of bytes. This default limit can be over-ridden by the mem_limit variable within the lang variable. See the lang option. Example: default_child_mem_limit = "20000000" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ plugin $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ `plugin` is where you define a plugin. `plugin` is a complex variable. The bare minimum for a plugin is a `cmd` and a `prefix`. Here's an example: plugin { cmd = "./super_cool_plugin_executable" prefix = "scp" name = "super cool plugin" input_sep = "///" mem_limit = "20000000" } Here's a discussion of the options available within `lang`. * 1. cmd `cmd` is the command which is passed to exec. See the example a couple of lines above. * 2. prefix `prefix` is what an IRC user types to use a certain command. Also see the example a couple of lines above. * 3. name `name` is listed using esotericbot's `help` command, next to the prefix. `name` can also be used in the place of a prefix. * 4. input_sep `input_sep` is what an IRC user types between say, program code for a plugin, and intended input for the plugin. It's only really useful for language interpreters. * 5. mem_limit `mem_limit` restricts the memory usage for this plugin. This overides `default_child_mem_limit`. * 6. shortcut `short_cut` enables irc users to request a command without the value specified by `command_prefix`. The motivation for this is that lambdabot executes haskell expressions when called in the form > 1 + 1, to use a different plugin, you must use the prefix for that plugin, eg >bf ++++++++++++++++++++++++++++++++++++++++++++++++++. ( Note, these two code segments are equivalent :) ------------------------------------------------------------------------------- 5. PLUGINS -------------------------------------------------------------------------------- Esotericbot ships with a few plugins -- if you want to write more, I'd recommend studying these. However, the rule is: Whatever the user types in _before_ `input_sep`, is sent to the plugin's stdin. ( This is typically used as program code ) Whatever the user types in _after_ `input_sep`, is set as plugin's first command line argument. ( This is typically used as input ) If the plugin runs out of input, it must halt, and send an appropriate message to stdout. I have created a tiny c library which helps deal with char by char input from the command line argument ( which is important for esoteric languages, which typically process input char by char ). It is easy to use, and doesn't leak memory. Plugin.h: struct char_lock { int alive; char * str; } char_lock; char uncons( struct char_lock * ); struct char_lock * get_input( int argc , char ** argv ); Don't use char_lock directly. It is a tombstone, as you can see. Use get_input to get the initial char_lock. ( Saves you having to check if the arguments are present ) uncons returns the first character of str, and sets str to the tail of the string. If there is no more input, it will send an appropriate message to stdout, and halt ( which is the desired behaviour ) -------------------------------------------------------------------------------- 6. HACKING -------------------------------------------------------------------------------- Please hack on esotericbot! There are a few things which need thinking about. Config.hs would be better if it used parsec maybe, instead of adhoc code in to_conf_var. It would be better also maybe if in Execution.hs, when_mem would use an explicite queue ( which would be driven by a worker thread ), rather than just have the threads yeild to eachother if they can't run. -------------------------------------------------------------------------------- 7. CREDITS -------------------------------------------------------------------------------- Esotericbot was written by John Morrice. 2009, under the BSD license. Plugin credits: SK caluclus plugin: John Morrice Thanks are due to a few people, whose code was modified to be used as plugins. Keep in mind, that their code is distributed under the terms of their individual licenses. Oleg Mazonka: bff4.c http://mazonka.com/brainf/index.html Jacob L. Mendelson: unlambda.c http://www.ofb.net/~jlm/ BigZaphod, creator of the whirl programming language: whirl.cpp http://www.bigzaphod.org/whirl/ Ben Olmstead, creator of the malbolge programming language: malbolge.c http://www.lscheffer.com/malbolge_spec.html ( Link to specification, I cannot find his homepage :)