ExBot

ExBot is like Plug, but for Slack. Your bot module defines a pipeline of functions (slugs) that receive events from Slack. Each slug can take actions in response to the events it receives, and may optionally add metadata to an event for other slugs to use downstream. This is similar to Plug and its conn object.

ExBot connects and communicates over websocket using Slack’s RTM API. A simple ExBot.Slack process also provides access to a few handy Web API calls, and maintains a cache of user and channel information for quick lookups.

Hex docs for ExBot are available here.

Installation and example

Start a new project.

mix new bot_family --sup

Add ExBot to your deps, then run mix deps.get.

def deps do
  [
    {:ex_bot, "~> 0.1.0"}
  ]
end

Create your Bot

Create a new module for your bot, pull in ExBot.Bot, and define a simple pipeline of slugs.

# lib/bot_family/my_bot.ex
defmodule BotFamily.MyBot do
  use ExBot.Bot

  slug(ExBot.Slug.Common.MessagesOnly)
  slug(ExBot.Slug.Common.CheckMentioned)
  slug(:simple_reply)

  def simple_reply(%ExBot.Event{data: event_data, metadata: %{mentioned: true}} = event) do
    %{user: user_id, channel: channel_id} = event_data
    ExBot.Bot.send_text(__MODULE__, channel_id, "Oh hey, <@#{user_id}>!")
    event
  end

  def simple_reply(event), do: event
end

What’s going on here? In our example bot we’re using the included ExBot.Slug.Common.MessagesOnly slug module to filter out events that are not message events.

Next we use the included ExBot.Slug.Common.CheckMentioned slug, which will check to see if our bot was mentioned in the message we just received. It will add a mentioned key to the metadata.

Finally, we supply our own slug called simple_reply. If it’s been mentioned, it will send a reply to the user that mentioned it in the channel using ExBot.Bot.send_text/3. If not, it will simply pass the event along.

Check out the documentation in the ExBot.Slug module for more information on creating your own slugs.

Configure your keys

Before we try out our bot, we need to configure the api keys for our bot in confix/config.exs, and for the Slack Web API. In our case, we’ll use the same token for both.

# config/config.exs
use Mix.Config

config :ex_bot,
  keys: %{
    :web_api => "xoxb-your-key",
    BotFamily.MyBot => "xoxb-your-key"
  }

Hello World.

Now let’s boot our bot in iex. Make sure your bot has been invited to, and joined, the channel you are using.

$ iex -S mix
iex(1)> BotFamily.MyBot.start_link()

10:52:55.558 [info]  Elixir.MyBot: Initializing Websocket
{:ok, #PID<0.253.0>}

iex(1)> ExBot.Bot.send_to_channel(BotFamily.MyBot, "general", "hello world")

Check out the documentation in the ExBot.Bot module for more information on sending messages from your bot.

Test your slug pipeline

You should see a “hello world” from your bot in #general (assuming the bot is in that channel).

Let’s test our slug pipeline. Try mentioning the bot by name in your Slack channel. For example, “Hello @mybot” (use the name of the bot you specified when you created the integration in Slack). You should see a reply.

Add your bot as a worker

As a last step, you probably want to add your bot as a worker to your application.ex instead of starting the bot explicitly.

# lib/application.ex
defmodule BotFamily.Application do
  @moduledoc false

  use Application

  def start(_type, _args) do
    children = [BotFamily.MyBot]
    opts = [strategy: :one_for_one, name: BotFamily.Supervisor]
    Supervisor.start_link(children, opts)
  end
end