Alice
A Lita-inspired Slack bot written in Elixir.
The Caterpillar and Alice looked at each other for some time in silence: at last the Caterpillar took the hookah out of its mouth, and addressed her in a languid, sleepy voice.
"Who are YOU?" said the Caterpillar. This was not an encouraging opening for conversation. Alice replied, rather shyly, "IβI hardly know, sir, just at presentβat least I know who I WAS when I got up this morning, but I think I must have been changed several times since then."
For an example bot, see the Active Alice bot. For an example handler, see Google Images Handler.
You'll need a Slack API token which can be retrieved from the Web API page or by creating a new bot integration.
Handler Plugins
Alice has a plug in system that allows you to customize the functionality of your bot instance. See the docs for more information about creating your own handlers.
Known Handlers
- Alice Against Humanity: hex, code
- Alice Google Images: hex, code
- Alice Karma: hex, code
- Alice Reddit: hex, code
- Alice Shizzle: hex, code
- Alice XKCD: hex, code
- Alice Doge hex, code
If you write your own handler, please submit a pull request and update this list!
Creating Your Own Bot With Alice
Create the Project
Create a new mix project.
mix new my_bot
cd my_bot
rm lib/my_bot.ex test/my_bot_test.exs
Configure the Application
In mix.exs
, bring in alice and any other handlers you want. You also need to
include the websocket_client
dependency because it's not a hex package.
defp deps do
[
{:alice, "~> 0.4.2"},
{:alice_against_humanity, "~> 0.1.0"},
{:alice_google_images, "~> 0.1.0"}
]
end
In the same file, also configure the app:
def application do
[ applications: [:alice],
mod: {Alice, %{}}
]
end
In config/config.exs
add any configuration that your bot needs. This includes registering any handlers you want. You can
add handlers through dependencies, or you can write them directly in your bot
instance. (See Writing Route Handlers for information on how to write a
handler. We recommend putting them in lib/alice/handlers/
.)
use Mix.Config
config :alice,
api_key: System.get_env("SLACK_API_TOKEN"),
state_backend: :redis,
redis: System.get_env("REDIS_URL"),
handlers: [
Alice.Handlers.Random,
Alice.Handlers.AgainstHumanity,
Alice.Handlers.GoogleImages
]
config :alice_google_images,
cse_id: System.get_env("GOOGLE_CSE_ID"),
cse_token: System.get_env("GOOGLE_CSE_TOKEN"),
safe_search_level: :medium
With that, you're done! Run your bot with iex -S mix
or mix run --no-halt
.
Deploying to Heroku
Create a new heroku app running Elixir.
heroku create --buildpack "https://github.com/HashNuke/heroku-buildpack-elixir.git"
Create a file called elixir_buildpack.config
at the root of your project.
erlang_version=22.0.3
elixir_version=1.8.2
always_rebuild=false
Create a Procfile
at the root of your project. If you don't create the proc
as a worker
, Heroku will assume it's a web process and will terminate it for
not binding to port 80.
worker: mix run --no-halt
You may also need to reconfigure Heroku to run the worker.
heroku ps:scale web=0 worker=1
Add your slack token and any other environment variables to Heroku
heroku config:set SLACK_API_TOKEN=xoxb-23486423876-HjgF354JHG7k567K4j56Gk3o
Push to Heroku
git add -A
git commit -m "initial commit"
git push heroku master
Your bot should be good to go.
Creating a Route Handler Plugin
First Steps
mix new alice_google_images
cd alice_google_images
rm lib/alice_google_images.ex test/alice_google_images_test.exs
mkdir -p lib/alice/handlers
mkdir -p test/alice/handlers
touch lib/alice/handlers/google_images.ex test/alice/handlers/google_images_test.exs
Configuring the App
In mix.exs
, update application
and deps
to look like the following:
def application do
[applications: []]
end
defp deps do
[
{:alice, "~> 0.4.2"}
]
end
Writing Route Handlers
In lib/alice/handlers/google_images.ex
:
defmodule Alice.Handlers.GoogleImages do
use Alice.Router
command ~r/(image|img)\s+me (?<term>.+)/i, :fetch
route ~r/(image|img)\s+me (?<term>.+)/i, :fetch
@doc "`img me alice in wonderland` - gets a random image from Google Images"
def fetch(conn) do
conn
|> Alice.Conn.last_capture
|> get_images
|> select_image
|> reply(conn)
end
#...
end
The Elixir Formatter and Alice
If you want the Elixir formatter to omit the parens on command/2
and
route/2
, simply import the alice config in your .formatter.exs
:
# my_handler/.formatter.exs
[
# ...
import_deps: [:alice]
]
Testing Handlers
Alice provides several helpers to make it easy to test your handlers. First
you'll need to invoke to add use Alice.HandlerCase, handlers: [YourHandler]
passing it the handler you're trying to test. Then you can use
message_received()
within your test, which will simulate a message coming in
from the chat backend and route it through to the handlers appropriately. If
you're wanting to invoke a command, you'll need to make sure your message
includes <@alice>
within the string. From there you can use either
first_reply()
to get the first reply sent out or all_replies()
which will
return a List of replies that have been received during your test. You can use
either to use normal assertions on to ensure your handler behaves in the manner
you expect.
In test/alice/handlers/google_images_test.exs
:
defmodule Alice.Handlers.GoogleImagesTest do
use Alice.HandlerCase, handlers: Alice.Handlers.GoogleImages
test "it fetches an image when asked" do
send_message("img me example image")
assert first_reply() == "http://example.com/image_from_google.jpg"
end
end
Registering Handlers
In the config.exs
file of your bot, add your handler to the list of handlers to
register on start
use Mix.Config
config :alice,
handlers: [
Alice.Handlers.GoogleImages,
...
]