ElixirのPlugを使って
以下のようなHTTPステータスコードの説明を表示してくれる
SlackのSlash Commandsを作成してみました
コードは github にアップしています
使い方
$ git clone https://github.com/tamanugi/slash_bot_http_code
$ cd slash_bot_http_code
$ mix deps.get
$ mix run --no-halt
作業メモ
プロジェクトの作成, 依存モジュールのインストール
$ mix new slash_command_http_code --sup
supコマンドをつけてプロジェクトを作成。
次に依存するモジュールの追加
mix.exs
defp deps do
[
{:plug, "~> 1.4.3"},
{:cowboy, "~> 1.1.2"},
{:poison, "~> 3.1.0"},
{:httpoison, "~> 0.13.0"},
{:floki, "~> 0.18.0"}
]
end
$ mix deps.get
※slash commnadsを作成するだけなら plug
, cowboy
だけでOK
Router
lib/slash_bot_http_code/router.ex
defmodule SlashBotHttpCode.Router do
alias SlashBotHttpCode.HttpCode
use Plug.Router
plug Plug.Parsers, parsers: [:urlencoded]
plug :match
plug :dispatch
get "/" do
send_resp(conn, 200, "")
end
post "/" do
%{"text" => text} = conn.params
resp = text |> HttpCode.get |> process_resp |> Poison.encode!
conn
|> put_resp_content_type("application/json")
|> send_resp(200, resp)
end
match _ do
send_resp(conn, 404, "not found")
end
def process_resp(%{summary: summary, description: description}) do
%{
response_type: "in_channel",
text: summary,
attachments: [
%{
text: description
}
]
}
end
def process_resp(_) do
%{text: "Not Found."}
end
end
SlackからはPOSTで叩かれるので post "/" do ~ end
に処理を書く
送信された内容は conn.params
から取得できる
HttpCode
lib/slash_bot_http_code/http_code.ex
defmodule SlashBotHttpCode.HttpCode do
use Agent
@wiki_url "https://ja.wikipedia.org/wiki/HTTP%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89"
def start_link(_init) do
res = Agent.start_link(fn -> [] end, name: __MODULE__)
init()
res
end
def get(key) do
Agent.get(__MODULE__,
fn http_code_list ->
http_code_list
|> Enum.filter(fn item ->
to_string(item[:status_code]) == key end)
|> List.first
end)
end
def init() do
%{body: html} = HTTPoison.get! @wiki_url
http_code_list = html
|> Floki.find("dt, dd")
|> parse
Agent.update(__MODULE__, fn _ -> http_code_list end)
end
def parse(nodes), do: parse(nodes, [], %{})
def parse([], result, _), do: result
def parse([head | tail], result, tmp) do
{tag, _, children_nodes} = head
text = Floki.text(children_nodes)
{result, tmp} = case tag do
"dt" ->
status_code = text |> String.replace(~r/ .*/, "") |> String.to_integer
{[tmp | result], %{summary: text, status_code: status_code}}
"dd" ->
description =
case tmp[:description] do
nil -> text
desc -> "#{desc}\n#{text}"
end
{result, tmp |> Map.put(:description, description)}
end
parse(tail, result, tmp)
end
end
[Elixir] Flokiを使ってスクレイピングを行う で作成したものをベースに
スクレイピングした内容を Agent
で保持しておくように変更
res = Agent.start_link(fn -> [] end, name: __MODULE__)
init()
res
start_link/1
の返り値は Agent.start_link
の返り値にしないと
Supervisorに追加できない
Application
lib/slash_bot_http_code/application.ex
defmodule SlashBotHttpCode.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
children = [
Plug.Adapters.Cowboy.child_spec(:http, SlashBotHttpCode.Router, [], [port: 4000]),
{SlashBotHttpCode.HttpCode, []}
]
opts = [strategy: :one_for_one, name: SlashBotHttpCode.Supervisor]
Supervisor.start_link(children, opts)
end
end
Plug & Cowboyの起動, HttpCodeの起動