# Boty [](https://travis-ci.org/ricardovaleriano/boty) [](http://www.rubydoc.info/github/ricardovaleriano/boty) `Boty` is a utilitary to create bots (at this time, specificaly Slack bots). A bot in the context of this gem is an ordinary ruby application that knows how to connect and receive messages in a Slack company room. `Boty` will give you a nice api to bind your own logic to specific messages of your interest. ## Usage TL;DR version: - create [new slack bot integration](#integration) (http://[company].slack.com/services) - `$ gem install boty` - `$ boty new jeeba` (where `jeeba` it's a name that you will choose for the bot in the integration step) - type your company name (the one used in the slack url: http://[company].slack.com) - type the api key created on the [bot integration step](#integration) - `$ cd jeeba` (the dir created after the name passed as parameter to the `new` command - ./bot Your bot should be up and running, which means: connected to your Slack room under the name configured in the Slack Bot integration. ### Custom scripts On the `script` dir of your bot there is a example `ping` script. But you will want to create your own scripts: - stop the bot (if it's already running) - add a new file named `hello.rb` into the `script` dir - add a `command` to made it available via bot mention (`@jeeba` in my case) - add a `hear` instruction so the `bot` listen to any message with the pattern configured - use the method `#say` to instruct the `bot` to send a message in the channel - use the method `#im` to instruct the `bot` to send a private message ```ruby # script/hello.rb command "hi" do say "hi #{user.name}" end command "im me" do im "want something?" end hear "say something" do say "something" end ``` - run the bot `./bot` - on the slack room, issue the commands and send messages: ``` valeriano 12:14 AM @jeeba: hi jeeba BOT 12:14 AM hi valeriano valeriano 12:14 AM @jeeba: im me # a private message will arrive: jeeba BOT 12:14 AM want something? # back to the general channel: valeriano 12:16 AM say something jeeba BOT 12:16 AM something ``` #### Regexes There is an api to capture matches when regexes are used on `hear` and `command` configurations: - stop the bot (if it's already running) - add a new file named `math.rb` into the `script` dir - add the `command` `/sum (\d+) (\d+)/`: ```ruby # script/math.rb command(/sum (\d+) (\d+)/) do |augend, addend| say "(#{augend} plus #{addend}) = #{augend.to_i + addend.to_i}" end ``` - run the bot: `./bot` - on the slack room, issue the command: ``` valeriano 12:30 AM @jabberu: sum 100 200 jabberu BOT 12:30 AM (100 plus 200) = 300 ``` And this is pretty much it! :tada: If you need more details about any of the features in `Boty`, you probably will find it in this README, on the sections that follow. If you don't find what you need, please contact us or even create an issue. Our goals with this project are to make it usefull and easy to use, check the [contributing](#contributing) info for more. ## Usage (long version) Now you will learn how to create a custom bot application for your needs, how to configure it to allow the bot say stuff in your slack channels and finally, will see how to run the bot properly. ### Slack Bot Integration The first thing to do is to create a Slack Bot integration. Go to `http://[your-company-name].slack.com/services` to add a new one. In the process an `API Token` will be generated. That will be used in the [next step](#installation). - ![](docs/images/readme-01-screen-integration.png) - ![](docs/images/readme-02-screen-integration.png) - ![](docs/images/readme-03-screen-integration.png) ### Installation Now you can install `Boty`: ```sh $ gem install boty ``` This will give you an executable `boty`, which ships with a command `help` so you can know all the stuff that it can do. But the main one is to [create your new shiny bot](#usage). ### Creating a new bot To create a new bot, execute this on your terminal: ```sh $ boty new jeeba ``` Where `jeeba` will be the nickname you give your bot in the [Slack integration](#integration). The command will create a new directory after your bot name (`jeeba` in my example). Your _bot application_ will live in this directory. Feel free to check the contents, it will have some ruby files, some directories... it's just an ordinary ruby application. But first, let's see something about the [configurations](#configuration). ### Configuration When executing the `boty new` command, you will be prompted for the *company name* and the *bot api key*. The information that you enter will be used only locally, so you can experiment and test (we will see more about the automated ones in this *README*) while developing your bot. This will be stored in the `.env.local` file on the recently created dir. If you want to understand how this configuration is managed locally, you can check de [`Dotenv` gem documentation](https://github.com/bkeepers/dotenv), but you don't need to worry about it, if you don't want. Now, let's create some commands and message listeners on your bot. ### Creating a Session (and the `bot` file). Since you already have a `Boty` project configured with your company name and api key, you should be able to start a new session for your bot by calling `Session#start`. This is a "blocking" call. It will hang to allow the bot to listen for messages and send them. You can pass a block to it in order to configure your bot preferences, or knowledge if you prefer =). The block passed to `#start` will be _yielded_ in the scope of a `Bot`. In other words, you can call any bot method inside this context. Note that at this time, you can consider your `Bot` already connected to the Slack room. ```ruby session = Boty::Session.new session.start do # configure your bot here end ``` In your project there is a `bot` executable file. This file already creates a `Session` with a minimum bot configuration: a listener with the bot name. Which means that any message with the bot name will trigger the behavior in this configuration. This is just to save you time. You could start a Session by hand, if you wanted, so feel free to check the code on this file and change it at will =). Now it's time to better understand what is a listener, a command and how to use regexes to configure the patterns that trigger the bot. #### Command - a bot mention followed by a instruction pattern A `command` can be understood as a direct instruction to the bot. To be issued the bot should be mentioned by the message using the default Slack mention "operator" *`@`*. To create a command, call the method `#command` on the bot passed as parameter in the `Session#start` method. The `command` method receive the pattern for the command, and the _block_ that should be executed when the command is issued. See how to teach your bot to flip tables like a pro. ```ruby session = Boty::Session.new session.start do # ... command("flip") do say "HEY! (╯°□°)╯︵ ┻━┻" end end ``` Now, to see the result, with the bot running, send in any channel where the bot is (the #general is the default): @jeeba: flip (remember: `jeeba` is the name that I choose for my bot, you should use the correct name here). The previous command will result in the following response: jeeba BOT 2:25 AM HEY! (╯°□°)╯︵ ┻━┻ There is an alternative method to create a `command`: `bot#response`. Use the one more appealing to you, they are the same. The `bot#say` method used in this example is [better explained here](#bot_say). Wrapping up: - a command is a pattern triggered by a bot mention - you can configure your bot patterns in the block passed to `Session#start` Now, let's see how a listener works. #### Listener - a message pattern that the bot should respond to A listener is used when the bot is interested in ANY messages containing some pattern. Note that a listener will not be triggered only when there is a bot mention, but anytime a message containing the pattern is sent on a channel where the bot is. Simple enough, let's get to the code: ```ruby session = Boty::Session.new session.start do # ... hear("what's going on here?") do say "I don't know. But something is always wrong. :scream:" end end ``` This configuration says that anytime a message containing the string "what's going on here?" is sent, the bot will respond. So a valid test could be send the following message in the _#general_ channel: valeriano 3:57 AM OMG guys, what's going on here? And the response will be, as expected: jeeba BOT 3:57 AM I don't know. But something is always wrong. :scream: Wrapping up: - A listener is triggered for any message containing the pattern - A listener don't need a bot mention, so careful: the range of this config is wide Before we start to study the "script" way of configuring `command` and `listener` binds, let's see how we can use ruby regexps to capture message parameters. #### Regexes - a pattern that allow capture parameters within a message What is a bot if it can't annoy people when we want it to? So let's teach our bot to send private messages to people in the Slack room. Let's create an `im` _command_ that will be capable of extract the person to whom we want to send a message, and the message that we want the bot send. Our bot will be capable of understand the following instruction: valeriano 2:44 PM @jeeba: im julian with omg! lol! bbq! The _command_ configuration can be like this: ```ruby command /im (\w+) with (.*)/ do |person, message| im message, to: person end ``` The regex matched portions will be passed as parameter to the block given to _command_. And this is it! :tada: ##### Commands, Listenners and aliases Both *commands* and *listeners* can be aliased, which means that the same `Action` can be triggered by different regexes: ```ruby command /hi/i, /hello/i do say "Ohay #{user.name}! Hello there." end ``` valeriano 12:04 PM @jeeba: hi jeeba BOT 12:04 PM Ohay valeriano! Hello there. valeriano 12:05 PM @jeeba: hello jeeba BOT 12:05 PM Ohay valeriano! Hello there. ### Adding custom scripts Now you already know what are [listeners](#listeners) and [commands](#command). It's time to know another way to create those "binders", it's what we call `scripts`. Any ruby file available on `scripts` will be loaded when a `Session` starts. Your bot ships with an example script named `script/ping.rb`, with a very simple example of what you can do. But let's create a new script from scratch. Create a new file `script/flip.rb` on your bot directory. Any method available in a bot will be available in this file, which means you can use `command`, `hear`, `im`, `say`, `message`, etc. Let's code: ```ruby # script/flip.rb command "flip" do say "(╯°□°)╯︵ ┻━┻" end ``` And this is pretty much it. When your bot runs, it will be able to perform the command `flip`: valeriano 3:36 PM @jeeba: flip jeeba BOT 3:36 PM (╯°□°)╯︵ ┻━┻ So feel free to customize the `bot` file or create your own scripts. What please you more is what you should use. ### DSL for bot scripting In this section you will see all the methods available to configure your `bot`. #### `bot#say` - Sending public messages on Slack channels Use `#say` when the bot should send a message in the channel that triggered the configuration block: ```ruby command "flip" do say "(╯°□°)╯︵ ┻━┻" end ``` If the bot wants to start a conversation, or even just say something in a channel without have received a message (via `#hear` or `#command`), just call `#say` inside a script. ```ruby say "ready to turn tables", channel: "#general" command "flip" do say "(╯°□°)╯︵ ┻━┻" end ``` If a channel isn't passed to `#say` it will defaults to `"#general"`. You can use any [Slack RTM postMessage](https://api.slack.com/methods/chat.postMessage) parameter when calling `#say`. #### `bot#im` Bots can send private messages. Use the `#im` method: ```ruby command "who am I?" do im "you are #{user.name}!" end ``` In the previous example, the private message will be sent to the user that issued the command, this is a default. If you want the bot to send a message to a specific user, just indicate the user name via the `to` option: ```ruby command "annoys julian" do im "Loren Ipsum, Julian boy!", to: "julian" end ``` #### `bot#message` The message that triggered a block is made available in the block's scope in the method `#message`: ```ruby command "what did you say?" do say "Well: #{message.text}" end ``` Execute the previous command will be as follows: valeriano 12:07 AM @jeeba: what did you say? jeeba BOT 12:07 AM Well: @jeeba: what did you say? Note that the `#text` returns the raw message that triggered the handler, including the bot mention itself. #### `bot#user` If the bot need to access the data about the user that send the message that triggered the handler, use the `#user` method. Let me borrow an already used example: ```ruby command "who am I?" do im "you are #{user.name}!" end ``` #### `bot#http` There is an utilitary method `#http` available on the handler block. This guy is useful to make, well... http requests. It supports all http verbs and return a `Hash` if the http response contains a `"application/json"` _Content-Type_ header, or else the raw body. Let's create a `command` that fetches a [xkcd](http://xkcd.com/) strip for us: ```ruby command /xkcd(\s\d+)?/ do |number| number = number ? number.strip : "1" xkcd = http.get "http://xkcd.com/#{number}/info.0.json" say "#{xkcd["alt"]}\n<#{xkcd["img"]}>" end ``` To execute this command: valeriano 12:40 AM @jabberu: xkcd 2 The strip number 2 of [xkcd](http://xkcd.com/) will be brought to the room. If we don't pass a number parameter, the strip number 1 will be assumed as default. If something goes wrong with the request, you can use the `http#response` method to access the inner response object. It'll be a [faraday](https://github.com/lostisland/faraday) http response object. #### `bot#desc` - Describing your bindings A bot list all the commands and message handlers that it knows in the moment. If you want to give a nice description and/or a usage tip on command you can use the `desc` method. Given that your bot has the following script: ```ruby desc "pug me", "Send some nice pug in the channel." respond(/pug me/i) do # ... end ``` The follow text will be part of the response for a `@bot: knows` command: pug me: Send some nice pug in the channel. You can use just the description if you want. In this case the `regex` itself will be used as the command name. ```ruby desc "Send some nice pug in the channel." respond(/pug me/i) do # ... end ``` valeriano 2:25PM @bot: knows bot 2:25PM knows: List all the commands known by this bot. /pug me/i: Send some nice pug in the channel. We strongly recommend that you describe all of your scripts. But if you don't, the bot will be capable of tell you what `regexes` are binded to it: valeriano 2:47PM @jabberu: knows jabberu 2:47PM knows: List all the commands known by this bot. /pug me/i /jabberu, are you there\?/i #### Testing your own scripts **todo: document** For now, check the `spec/script/ping_spec.rb` and follow it's leads. ### Running locally After create the new _bot application_ and give the *bot api key*, you can just enter the new dir and run ```sh $ ./bot ``` Done! Your bot will be connected to your company Slack through the nickname that you provided in the [integration](#integration) step. To see if it's working properly, just go the the slack, in the general channel (or any other where the bot was invited) and type: ```sh @jeeba: ping ``` It will respond to you: `pong`. IT'S ALIVE. ### Deploy on heroku But probably what you want is to have your bot running in a server, not in your machine, right? Your bot is created with an example `Procfile` to make it easy to run it on [Heroku](http://heroku.com) (to give an example). Create a new project on Heroku, following their instructions, until they ask you to do the "deploy" (the `git push heroku master`). #### configure the api key Now you need to add the configurations that are localy stored on `.env.local`. The two environment variables on that file are: SLACK_COMPANY SLACK_BOT_API_TOKEN The *Heroku* command line tool offers a way to create environment vars in the server, check [their documentation](https://devcenter.heroku.com/articles/config-vars). Today, when as I'm writing the readme, you can use the following commands to set those two environment variables: $ heroku config:set SLACK_COMPANY=your-company-name $ heroku config:set SLACK_BOT_API_TOKEN=your-bot-integration-api-token #### allow process to run Heroku will not detect a web application in your bot, so you need to tell them to run your application as a, well... as a "normal" application. Go to your heroku dashboard (https://dashboard.heroku.com/apps), find the application that you have created to your bot. On the tab _Resources_, find a line with the information: your-bot-name bundle exec ./bot Turn on this resource. And done, your bot is up and running! ### Using the logger `Boty` ships with a builtin logger that can be used in your own scripts. It is made available via `#logger`. This will return a instance of a [Ruby Logger](http://ruby-doc.org/stdlib-2.2.3/libdoc/logger/rdoc/Logger.html). Note that the default logger implementation will write to the `STDOUT` .Let's see a usage example: ```ruby session.start do command(/hello/i) do logger.debug "saying hello" say "hello there." end end ``` By default the logger will write on the standard output, the previous command, when triggered, will produce the following log line: D, [2015-12-11T00:13:28.712380 #40369] DEBUG -- : saying hello Of course, if you need to write to a file, instead of the STDOUT, just change the adapter for your logger. #### Log into files (instead of STDOUT) Before start your bot session, configure the file log: ```ruby Boty::Logger.adapter = Logger.new("log/output.log") ``` And this is all you need! :tada: #### Logger adapters There is an adapter included on `Boty` that can be used by you and is also a good example of how to create a custom logger adapter for `Boty`. Supose that you want to write to a file and still send the logs to the `STDOUT`. You can use the `Multi` adapter like this: ```ruby Boty::Logger.adapter = Boty::Logger::Multi.new([ Logger.new(STDOUT), Logger.new("log/output.log") ]) ``` And this is it. Now when you call `logger.debug "some message"` from your bot, this log line will be writen to the `"log/output.log"` file and also in the `STDOUT`. You can write you own log adapter if you want. The `Multi` adapter implementation is fairly simple. Let's use it as an example of how to write your own adapter. You can extend the ruby _Logger_ class and worry yourself on write the `#add` override: ```ruby class Multi < ::Logger def initialize(adapters) @adapters = adapters end def add(*args, &block) @adapters.each do |adapter| adapter.add(*args, &block) end end end ``` The add method is called internaly by _Logger_ and has all the information that the auxiliary methods like `#debug`, `#info` etc received when called. The _Multi_ adapter implementation just delegate this call to the underlying adapters passed as parameters for the constructor. For more information on the `#add` parameters, [check the ruby doc](http://ruby-doc.org/stdlib-2.1.0/libdoc/logger/rdoc/Logger.html#method-i-add). _Multi_ also allows you to change the level for all the underlying adapters at once, the `#level=` overriten implementation is like this: ```ruby def level=(level) @adapters.each do |adapter| adapter.level = level end end ``` ### I18n The descriptions for the commands and listeners that ship with `Boty` can be customized via the [I18n gem mechanics](https://github.com/svenfuchs/i18n). `Boty` ships with translations for `:en` and `:pt-br`. If you want to override those messages, you can take a look at the files on [`locale`](https://github.com/ricardovaleriano/boty/tree/master/locale). I'll recomend that you just copy and paste the file that you want to customize on the `locale` dir of your project, and them make the editions that you want. Of course, you can add translations for any language that you want on your `locale` dir. And even add any translation that you want for your own command descriptions. To instruct your bot about which language to use, set the `Boty.locale` configuration and you're done. The `bot` executable created on your project generated by `boty new` have the following line before the session start: ```ruby Boty.locale = ARGV.pop || :en ``` This means that you can start your bot with a command line argument to tell which is the idiom that this session should use. This allows you to have the same bot running in different sessions with different idioms. The following usage is totally fine: ./bot pt-br ## Development After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. ### The local ./bin/bot The project have a `./bin/bot` executable that should be the product of compiling the `template/project/bot.tt` erb file. This is an easy way to just run the bot using the same logic that a project generated by `bot new` will use. It's highly recomendable that you test your changes using this approach before submit a `PR` or generate a new release. =) #### Compiling the ./bin/bot There is a `bot:compile` rake task that compiles the template right into the `bin` direcotry and make it executable (with `chmod +x`). ### Code guidelines Just make sure to run `bundle exec rubocop` before submit any `PR`, good coding! :D ## Contributing Bug reports and pull requests are very welcome on GitHub at https://github.com/ricardovaleriano/boty. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct. ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).