marvin-0.0.2: README.md
# Marvin, the paranoid bot (⍺ stage)
[](https://travis-ci.org/JustusAdam/marvin)
[](http://hackage.haskell.org/package/marvin)
Marvin is an attempt to combine the ease of use of [hubot](https://hubot.github.com) with the typesafety and easy syntax of Haskell and the performance gains from compiled languages.
A more in-depth version of the contents of this readme can be found on the [wiki](https://github.com/JustusAdam/marvin/wiki).
## Installation
You can get a release version of marvin on [Hackage](https://hackage.haskell.org/package/marvin).
However this library is still a very early stage so you might want to get updates quicker.
You can do so by using [stack](https://docs.haskellstack.org) and adding a recent commit of this repository to your `stack.yaml` file.
Stack will take care of downloading and building it for you.
## TLDR
```Haskell
module MyScript where
import Marvin.Prelude
script :: IsAdapter a => ScriptInit a
script = defineScript "my-script" $ do
hear "I|i can't stand this (\w+)" $ do
match <- getMatch
let thing = match !! 1
reply $ "I'm sorry to tell you but you'll have to do " ++ thing
respond "open the (\w+) door" $ do
match <- getMatch
let door = match !! 1
openDoor door
send $ printf "Door %v opened" door
respond "what is in file (\w+)" $ do
match <- getMatch
let file = match !! 1
liftIO $ readFile file
send file
```
## How to Marvin
The best way to use Marvin is very much taken from hubot.
A Marvin instance composes of a collection of scripts which are reactions or actions on certain messages posted in slack.
Each script is a Haskell source file.
They get compiled into one single static binary, which is a HTTP server that listens for slack's event calls.
### Defining scripts
Defining scripts is very easy.
Create a new Haskell source file like "MyScript.hs" and import marvins prelude `Marvin.Prelude`.
This provides you with all the tools you need to interact with marvin.
Now you can start to define your script with `defineScript` which produces a script initializer.
If you wish to use marvins automatic script discovery your script initializer should be named `script`
```Haskell
module MyScript where
import Marvin.Prelude
script :: IsAdapter a => ScriptInit a
script = defineScript "my-script" $ do
...
```
The script id, "my-script" in this case, is the name used for this script when repoting loggin messages as well as the key for this scripts configuration, see [configuration](#configuration).
In the define script block you can have marvin react to certain events with `hear` and `respond`.
More information on those in the section [reacting](#reacting)
Finally after you have defined your scripts you have to tie them together.
You can do this [manually](#wiring-manually) or you can have marvin create the boilerplate code for you.
To do this simply place a main file (this is the file you'll be compiling later) in the same directory the scripts are placed in.
Leave the file empty except for this line at the top `{-# OPTIONS_GHC -F -pgmF marvin-pp #-}`.
When you compile the file marvin will look for any other ".hs" and ".lhs" files in the same directory, import them and define a server which runs with the `script` from each.
If you wish to hide a file from the auto discovery either place it in a different directory or prefix it with "." or "_".
### Reacting
There are two main ways (currently) of reacting to events, `hear` and `respond`.
`hear` is for matching any incoming message. The provided regex is tried against all incomming messages, if one matches the handler is called.
`repond` only triggers on message which have the bot name, or a case variation thereof as the first word.
Once a handler has triggered it may perform arbitrary IO actions (using `liftIO`) and send messages using `reply` and `send`.
- `reply` addresses the message to the original sender of the message that triggered the handler.
- `send` sends it to the same room the tiggering message weas sent to.
- `messageRoom` sends a message to a room specified by the user.
### Configuration
Configuration for marvin is written in the [configurator](https://hackage.haskell.com/package/configurator) syntax.
Configuration pertaining to the bot is stored under the "bot" key.
```
bot {
name = "my-bot"
logging = "INFO"
}
```
By default each script has access to a configuration stored under `script.<script-id>`.
And of course these scripts can have nested config groups.
```
bot {
name = "my-bot"
}
script {
script-1 {
some-string = "foo"
some-int = 1337
bome-bool = true
}
script 2 {
nested-group {
val = false
}
name = "Trump"
capable = false
}
}
```
Configuration pertaining to the adapter is stored under `adapter.<adapter-name>`
```
bot {
name = "my-bot"
logging = "INFO"
}
adapter {
slack-rtm {
token = "eofk"
}
}
```
### Wiring manually
How Marvin interacts with your chat program depends on the used Adapter.
For instance the currently default `slack-rtm` adapter creates a (client) websocket connection with the slack API and listens to the events there.
Other adapters may require to set up a server.
### Utilities
All these utilities are already available to you if you import `Marvin.Prelude`.
#### Regex
Implemented in `Marvin.Util.Regex`, documentation coming soon.
#### Mutable variables
Implementation started in `Marvin.Util.Mutable`, documentation coming soon.
#### Format strings
For String formatting Marvin re-exposes the `Text.Printf` module.
Format strings use placeholders with `%`, the default formatter (works for all `Show` datatypes) is `%v`.
Substitution is done with the varargs function `printf`.
You can find the full documentation in the documentation for the [`Text.Printf`](https://www.stackage.org/haddock/lts-7.12/base-4.9.0.0/Text-Printf.html#v:printf) module.
#### JSON
Exposed in `Marvin.Util.JSON` documentation coming soon. Until then refer to [aeson](https://hackage.haskell.org/package/aeson).
#### Logging
Marvin comes with a logging facility built in.
`Marvin.Util.Logging` expose the logging facility.
Several functions are available, depending on the urgency of your message, like `errorM`, `infoM` and `criticalM`.
Logging messages made this way are automatically formatted and tagged with the scripts that reported them.
By default all logging messages with higher priority `NOTICE` or higher are shown.
Using the command line parameter `verbose` also adds `INFO` messages and `debug` adds `DEBUG` messages.
You can select the exact logging level in your config file (see also [configuration](#configuration)).
#### Random
Implemented in `Marvin.Util.Random`, documentation coming soon.
#### HTTP
Coarsely implemented in `Marvin.Util.HTTP`, documentation coming soon.