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).