All examples were made with Elixir 1.3, however any Elixir >= 1.0 should work. Slack Slash Command Overview Slack has a few different ways for you to write awesome bots for your slack teams. One of the easier ways is through a “slash command.” A works by sending a post request to an HTTPS (yes, SSL is required) endpoint whenever someone issues the command in chat. The post data consists of what command was sent, any text that came after the command, and some other data that we’ll get into in a bit. Then your service can respond with plain-text or some JSON and that response will be displayed in chat. slash command So, lets dig into this. A Simple Echo Bot Let’s do the “hello, world” of bots, an echo bot. First make sure you have elixir installed and then lets create a new mix project: [~] $ mix new slashbot[~] $ cd slashbot Now open up mix.exs and lets specify our dependencies: defp deps do[{:cowboy, "~> 1.0.0"},{:plug, "~> 1.0"}]end and don’t forget do add them to the applications list: def application do[applications: [:cowboy, :plug]]end If you’ve heard about Elixir, you’ve probably heard of the for doing web applications. However phoenix is a little over kill for what we need to do, so we are using (which phoenix is in part built on top of). Plug allows us to easily build a small web app. By default it comes with an adapter for , which is an HTTP server written in Erlang. Phoenix Framework Plug Cowboy Anyway, lets get to writing down some code. Create a lib/slashbot directory in the project and a router.ex file within it. [~/slashbot] $ mkdir lib/slashbot[~/slashbot] $ touch lib/slashbot/router.ex Now lets open that up and write down some elixir: defmodule Slashbot.Router douse Plug.Router plug Plug.Parsers, parsers: [:urlencoded]plug :matchplug :dispatch Slack will periodically send get requests to make sure the bot is still alive. get "/" dosend_resp(conn, 200, "")end post "/" do%{"text" => text} = conn.paramssend_resp(conn, 200, text)end match _ dosend_resp(conn, 404, "not found")endend Do we’ve defined out router module, and we’re using Plug.Router so we can get all its delicious macros for Plug’s router DSL. Next we define our pipline of plugs. A plug pipline defines the functions a connection will get passed down through and processed. So first we have the connection get process with Plug.Parsers, which will parse out any urlencoded parameters and stick them into the ‘params’ map that is attached to the plug connection. The :match and :dispatch plugs come next and are required, :match being the plug is responsible for finding the matching route, and :dispatch being the one that, well, handles the dispatching of the connection. After all that, we get to the real meat of what’s happening. We define a few routes. Our first route handles a simple GET request for ‘/’ and sends a blank 200 response. This is because slack will periodically send get requests to make sure the bot is still around The second route handles a POST request for ‘/’ and this is what will be handling our bot logic. We pull out the “text” field from the posted params, which contains the text a user would put after the slash comment. Like for: “/slashbot hello there” we would get “hello there” in the “text” parameter. So we get that text using pattern-matching on the conn.params map, and then send it back with send_resp, echoing the text back to the slack channel. The last one is our catch-all route for anything that doesn’t match the above routes. It just sends back a 404 saying the page they tried to get doesn’t exist. So now that we have a basic implementation going, lets fire it up and test it out with curl. Open up two terminal windows. In the first, we’re gonna make sure we got our dependencies, compile, and fire up iex: [~/slashbot] $ mix do deps.get, compile[~/slashbot] $ iex -S mixErlang/OTP 19 [erts-8.0.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for help)iex(1)> {:ok, _} = Plug.Adapters.Cowboy.http(Slashbot.Router, []){:ok, #PID<0.163.0>}iex(2)> Now leave that running and head to your other terminal window: [~/slashbot] $ curl -XPOST " "hello[~/slashbot] $ http://localhost:4000?text=hello Yay! It works, with curl at least. Now, opening up iex and typing those commands is fine for testing, but would be annoying for any kind of actual deployment, so lets fix that. Getting Our Echo Bot Prod-Ready Alright, we’re going to open up lib/slashbot.ex and make it a prod-ready, supervisor-using, application: defmodule Slashbot douse Application def start(_type, _args) dochildren = [Plug.Adapters.Cowboy.child_spec(:http, Slashbot.Router, [],[port: 4000])] opts = \[strategy: :one\_for\_one, name: Slashbot.Supervisor\] Supervisor.start\_link(children, opts) endend We’re just creating a simple start function for our app that will spin out a supervisor that will start up our cowboy webserver and plug router. If you don’t know what a supervisor does, it’s a process (as in an elixir process, not a system process) within your application that starts and monitors other processes in your application and restarts them if they die. This way, some transient error could crash our plug router, but the application as a whole would stay up and our router would be instantly restarted. Next we need to tell mix to start this module with start function when the application… starts. So key this into the applications second in your mix.exs: def application do[applications: [:cowboy, :plug],mod: {Slashbot, []}]end Now we can test this out with mix run --no-halt and run our curl: [~/slashbot] $ curl -XPOST " "hello[~/slashbot] $ http://localhost:4000?text=hello Still not good enough for prod though, we want to be able to just start this and forget about it. So lets create a release with . distillery Add distillery to your deps: defp deps do[{:cowboy, "~> 1.0.0"},{:plug, "~> 1.0"},{:distillery, "~> 0.9"}]end And now we can create a release: [~/slashbot] $ mix do deps.get[~/slashbot] $ mix release.init[~/slashbot] $ MIX_ENV=prod mix release --env=prod This will generate a slashbot.tar.gz in rel/slashbot/releases/0.1.0/ that you can copy up to your server. Now at this point, we haven’t set up any sort of SSL. , but I prefer to have nginx or some other process do it, that way I can let my application focus on being the application and some other webserver can handle the SSL. If you don’t have a server set up with SSL certs, you can hit-up for free, trusted certificates. Plug with Cowboy can handle SSL Lets Encrypt If you go the nginx route, you’ll need to set up a proxypass to your app: location /slashbot {proxy_pass ;} http://127.0.0.1:4000/ Once you’ve setup your SSL, lets continue on with getting the slashbot started: [~] $ mkdir slashbot[~] $ cp slashbot.tar.gz slashbot[~] $ cd slashbot[~/slashbot] $ bin/slashbot start That will start slashbot in the background for you. Now we can test it with our curl again: [~/slashbot] $ curl -XPOST " "hello[~/slashbot] $ http://localhost:4000/?text=hello Wee! That’s working. Now you’ll want to set up a , putting in the URL of your server + the path that you have the command running on. With any luck, you’ll be able to do this: slash command on your slack team Future Improvements There’s a couple of things improve upon here. First, you’ll notice that the messages only echo back to you! That’s boring. With a few tweaks, we can send back some JSON that will allow us to set an option to show the message to everyone in the channel. Second, we do no authentication what-so-ever, could set this bot up as a slash command in their slack and get a response back. With our simple echo bot, that’s not too terrible, but with anything slightly more complicated, that’s a huge problem. This can be fixed by checking the token that gets posted to the app, against the token that’s generated when you create the slash command integration. anyone These improvements will be left as an exercise to the reader, or maybe if there’s enough interest I’ll create a part 2 that gets into that stuff.