README.md in boty-0.0.17.1 vs README.md in boty-0.1.0

- old
+ new

@@ -1,53 +1,157 @@ # Boty [<img src="https://travis-ci.org/ricardovaleriano/boty.svg?branch=master" />](https://travis-ci.org/ricardovaleriano/boty) `Boty` is a utilitary to create bots (at this time, specificaly Slack bots). -A bot is this context is a ordinary ruby application, that knows how to receive -messages. Boty will give you a nice way to bind your own logic to specific -messages of your own interest. +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. -## Slack Bot Integration<a id="integration" /> +## Usage TL;DR version:<a id="tldr" /> + - 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)<a id="usage" /> + +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<a id="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). -## Installation<a id="installation" /> +- ![](docs/images/readme-01-screen-integration.png) +- ![](docs/images/readme-02-screen-integration.png) +- ![](docs/images/readme-03-screen-integration.png) +### Installation<a id="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). -## Usage<a id="usage" /> - -In this section 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. - ### Creating a new bot -To create a new bot, execute: +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 directory, it will have some ruby files, some directories... it's just an +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<a id="configuration" /> @@ -60,12 +164,365 @@ 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. +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 name="command" /> + +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 name="listenter" /> + +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<a name="regexes" /> + +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: + +### Adding custom scripts<a name="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<a name="bot_dsl" /> + +In this section you will see all the methods available to configure your `bot`. + +#### `bot#say` - Sending public messages on Slack channels<a name="bot_say" /> + +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`<a name="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`<a name="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`<a name="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`<a name="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 name="bot_desc" /> + +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<a id="running" /> After create the new _bot application_ and give the *bot api key*, you can just enter the new dir and run @@ -123,79 +580,127 @@ your-bot-name bundle exec ./bot Turn on this resource. And done, your bot is up and running! +### Using the logger -## Adding custom scripts +`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). - **todo: document** +Note that the default logger implementation will write to the `STDOUT` .Let's +see a usage example: -For now, check the `script/ping.rb` and follow it's leads. +```ruby +session.start do + command(/hello/i) do + logger.debug "saying hello" + say "hello there." + end +end +``` -### Describing script usage +By default the logger will write on the standard output, the previous command, +when triggered, will produce the following log line: -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. + D, [2015-12-11T00:13:28.712380 #40369] DEBUG -- : saying hello -Given that your bot has the following script: +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 -desc "pug me", "Send some nice pug in the channel." -respond(/pug me/i) do - # ... -end +Boty::Logger.adapter = Logger.new("log/output.log") ``` -The follow text will be part of the response for a `@bot: knows` command: +And this is all you need! :tada: - pug me: Send some nice pug in the channel. +#### Logger adapters -You can use just the description if you want. In this case the `regex` itself -will be used as the command name. +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 -desc "Send some nice pug in the channel." -respond(/pug me/i) do - # ... +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` +overrite: + +```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 ``` - valeriano 2:25PM - @bot: knows +The add method is called internaly by _Logger_ and has all the information that +the auxiliary methods like `#debug`, `#info` etc received when called. - bot 2:25PM - knows: List all the commands known by this bot. - /pug me/i: Send some nice pug in the channel. +The _Multi_ adapter implementation just delegate this call to the underlying +adapters passed as parameters for the constructor. -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: +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). - valeriano 2:47PM - @jabberu: knows +_Multi_ also allows you to change the level for all the underlying adapters at +once, the `#level=` overrite implementation is like this: - jabberu 2:47PM - knows: List all the commands known by this bot. - /pug me/i - /jabberu, are you there\?/i +```ruby +def level=(level) + @adapters.each do |adapter| + adapter.level = level + end +end +``` -### Testing your own scripts +### I18n **todo: document** -For now, check the `spec/script/ping_spec.rb` and follow it's leads. - ## 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. +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`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +To install this gem onto your local machine, run `bundle exec rake install`. -## Contributing +### The local ./bin/bot -Bug reports and pull requests are 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. +### Code guidelines +## Contributing<a name="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). +The gem is available as open source under the terms of the +[MIT License](http://opensource.org/licenses/MIT).