# Marvin, the paranoid bot (⍺ stage) [![Travis](https://travis-ci.org/JustusAdam/marvin.svg?branch=master)](https://travis-ci.org/JustusAdam/marvin) [![Hackage](https://img.shields.io/hackage/v/marvin.svg)](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. The in-depth documentation can be found on readthedocs https://marvin.readthedocs.org ## 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 "sudo (.+)" $ do match <- getMatch reply $(isL "All right, i'll do #{match !! 1}") respond "open the (\\w+) door" $ do match <- getMatch let door = match !! 1 openDoor door send $(isL "Door #{door} opened") respond "what is in file (\\w+)\\??" $ do match <- getMatch let file = match !! 1 contents <- liftIO $ readFile file send contents ``` ## 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 acts depending on the adapter used, in the case of the slack real time messaging adapter for instance it opens a websocket to the slack server from which it recieves events as they happen in your chat application. ### 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 Channel the tiggering message weas sent to. - `messageChannel` sends a message to a Channel specified by the user. ### Configuration Configuration for marvin is written in the [configurator](https://hackage.haskell.org/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.`. 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.` ``` 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 Marvins Prelude exposes the [marvin-interpolate](https://github.com/JustusAdam/marvin-interpolate) library, which enables the user to write CoffeeScript/Scala like interpolated Strings using Template Haskell. ```haskell str = let x = "Hello" in $(isL "#{x} World!") -- "Hello World" ``` #### 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 `logError`, `logInfo` and `logWarn`. 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.