diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..1ff202e --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,38 @@ +version: 2 +jobs: + build: + working_directory: /opt/app/snapeth-worker + + docker: + - image: avvo/elixir-circleci:1.5.2-1c + + steps: + - checkout + + # all steps after this run in the remote environment + - type: setup-docker-engine + reusable: true + version: 17.05.0-ce + + - run: + name: Install Docker client + command: | + set -x + VER="17.05.0-ce" + curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz + tar -xz -C /tmp -f /tmp/docker-$VER.tgz + mv /tmp/docker/* /usr/bin + + - run: + name: Build docker image and publish container tagged with branch + command: | + docker login -u $DOCKER_USER -p $DOCKER_PASS + docker build -t avvo/$CIRCLE_PROJECT_REPONAME:$CIRCLE_SHA1 . + docker push avvo/$CIRCLE_PROJECT_REPONAME:$CIRCLE_SHA1 + +workflows: + version: 2 + build: + jobs: + - build: + context: org-global diff --git a/.dockerignore b/.dockerignore index 79ef0e5..0405a9e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ .git _build cover +deps doc test tmp diff --git a/Dockerfile b/Dockerfile index 29377ba..121e450 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ -FROM avvo/elixir-circleci:1.4.5-1g +FROM avvo/elixir-circleci:1.5.2-1c +ENV REPLACE_OS_VARS=true ENV MIX_ENV=prod +WORKDIR /opt/app/snapeth-worker RUN \ mkdir -p \ @@ -10,19 +12,9 @@ RUN \ # Cache elixir deps COPY mix.exs mix.lock ./ COPY config ./config -COPY deps ./deps - RUN mix do deps.get, deps.compile +# Compile source files COPY . . - -WORKDIR assets - -RUN npm install - -RUN ./node_modules/brunch/bin/brunch b -p - -WORKDIR /opt/app -RUN mix phx.digest - RUN mix release --env=prod --verbose +CMD /opt/app/snapeth-worker/_build/prod/rel/snapeth/bin/snapeth foreground diff --git a/README.md b/README.md index 818729c..68d4fb3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Have you witnessed a teammate or coworker doing something inclusive? Would you like to be able to snap your fingers in agreement to promote and reinforce those positive behaviors? -Snapeth is currently an MVP Avvo Hackathon project in Slack. +Snapeth originated as an Avvo Hackathon project and is GOOD TO GO. ## Installation @@ -22,3 +22,7 @@ end Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) and published on [HexDocs](https://hexdocs.pm). Once published, the docs can be found at [https://hexdocs.pm/snapeth](https://hexdocs.pm/snapeth). + +## Possible Improvements + +We should be able to use slash commands (e.g. /snapeth [person] [props description]) if we follow steps [here] (https://bendyworks.com/blog/building-a-slackbot-in-elixir-part-1). diff --git a/config/config.exs b/config/config.exs index 2ca7f51..081f007 100644 --- a/config/config.exs +++ b/config/config.exs @@ -21,8 +21,27 @@ use Mix.Config # config :logger, level: :info # +config :logger, + level: :debug + +config :snapeth, Snapeth.Scheduler, + jobs: [ + # Every Monday at 2:00 + {"0 14 * * MON", {Snapeth, :display_leaderboard, []}}, + # Every Friday at 2:00 + {"0 14 * * FRI", {Snapeth, :display_leaderboard, []}}, + ], + timezone: "America/Los_Angeles" + config :snapeth, - slack_bot_token: "${BOT_TOKEN}" + slack_bot_token: System.get_env("BOT_TOKEN") || "${BOT_TOKEN}", + bucket_name: "snapeth", + leaderboard_data_file: "leaderboard.json" + +config :ex_aws, + access_key_id: [{:system, "S3_KEY_ID"}, :instance_role], + secret_access_key: [{:system, "S3_SECRET"}, :instance_role], + region: "us-west-2" # It is also possible to import configuration files, relative to this # directory. For example, you can emulate configuration per environment diff --git a/lib/application.ex b/lib/application.ex index 524e3e7..679343b 100644 --- a/lib/application.ex +++ b/lib/application.ex @@ -1,9 +1,12 @@ defmodule Snapeth.Application do use Application def start(_type, _args) do + import Supervisor.Spec, warn: false + children = [ - {Snapeth, [0]} + {Snapeth, [0]}, + worker(Snapeth.Scheduler, []) ] Supervisor.start_link(children, strategy: :one_for_one) end diff --git a/lib/scheduler.ex b/lib/scheduler.ex new file mode 100644 index 0000000..12e7a9b --- /dev/null +++ b/lib/scheduler.ex @@ -0,0 +1,4 @@ +defmodule Snapeth.Scheduler do + use Quantum.Scheduler, + otp_app: :snapeth +end diff --git a/lib/slack_bot.ex b/lib/slack_bot.ex index 56081b6..4bad580 100644 --- a/lib/slack_bot.ex +++ b/lib/slack_bot.ex @@ -1,5 +1,7 @@ defmodule Snapeth.SlackBot do use Slack + require Logger + alias Snapeth.Storage @message_types [ {~r/help/i, :help}, @@ -7,32 +9,21 @@ defmodule Snapeth.SlackBot do {~r/leaderboard/i, :leaderboard}, ] - def handle_connect(_, _state) do - IO.puts("Slack bot connected to team Avvo") - {:ok, %{}} - end - - def handle_info(:display_leaderboard, slack, state) do - snaps_leaderboard(slack, state, "#general") - {:ok, %{}} - end - - def handle_event(message = %{channel: "D" <> _, type: "message"}, slack, state) do - {_, func} = Enum.find(@message_types, - {nil, :help}, - fn {reg, _} -> String.match?(message.text, reg) end - ) - IO.inspect func - state = Kernel.apply(Snapeth.SlackBot, func, [message, slack, state]) - {:ok, state} - end + ########## + # CLIENT # + ########## + def help(message, slack, state) do + """ + Have you seen someone demonstrate a positive and inclusive behavior at work? + Tag that teammate here to give them an appreciative snap! - def handle_event(_message, _slack, state) do - {:ok, state} - end + For example: - def help(message, slack, state) do - send_message("Hi! To give snaps, start by tagging a team member and we'll instruct you from there. For example: @slackbot", message.channel, slack) + >@slackbot + _or_ + >@slackbot I appreciate you always considering accessibility when developing features. + """ + |> send_message(message.channel, slack) state end @@ -42,25 +33,34 @@ defmodule Snapeth.SlackBot do end def snap(message = %{user: user}, slack, state, user_id) when user_id == user do - send_message("You can't snap yourself, but this is an opportunity to talk with your teammates about inclusive behaviors and being proactive with their snaps!", message.channel, slack) + "You can't snap yourself, but this is an opportunity " <> + "to talk with your teammates about inclusive behaviors " <> + "and being proactive with their snaps!" + |> send_message(message.channel, slack) + state end def snap(message, slack, state, user_id) do - send_message("Oh snapeth, you got a snap from <@#{message.user}>!", user_id, slack) + snap_reason = strip_mention(message.text, user_id) + + "Oh snapeth, you got a snap from <@#{message.user}>!" + |> add_snap_reason(snap_reason) + |> send_message(user_id, slack) + Map.update(state, user_id, 1, &(&1 + 1)) end def leaderboard(message, slack, state) do - snaps_leaderboard(slack, state, message.channel) + display_leaderboard(slack, state, message.channel) state end - def snaps_leaderboard(slack, state, channel) when map_size(state) == 0 do - send_message("There have been no snaps today from <@#{slack.me.id}>.", channel, slack) + def display_leaderboard(slack, state, channel) when map_size(state) == 0 do + send_message("There have been no snaps this week from <@#{slack.me.id}>.", channel, slack) end - def snaps_leaderboard(slack, state, channel) do + def display_leaderboard(slack, state, channel) do leaderboard = state |> Enum.sort_by(&(elem(&1, 1))) |> Enum.reverse() @@ -69,7 +69,69 @@ defmodule Snapeth.SlackBot do end) |> Enum.join("\n") - send_message("Here is the daily leaderboard for <@#{slack.me.id}> recipients!\n#{leaderboard}", channel, slack) + send_message("Here is the weekly leaderboard for <@#{slack.me.id}> recipients!\n#{leaderboard}\nYou can give snaps via the Snapeth app!", channel, slack) + end + + defp add_snap_reason(message, reason) do + message <> "\n_#{reason}_" + end + + defp strip_mention(text, mentioned_user_id) do + at_mention_length = String.length("<@#{mentioned_user_id}> ") + <<_at_mention::binary-size(at_mention_length), snap_reason::binary>> = text + + snap_reason + end + + ########## + # SERVER # + ########## + def handle_connect(_, _state) do + IO.puts("Slack bot connected to team Avvo") + {:ok, %{}} + end + + def handle_info(:barf_state, _slack, state) do + IO.inspect(state) + {:ok, state} + end + + def handle_info({:load_user, user_id, score}, _slack, state) do + {:ok, Map.put(state, user_id, score)} + end + + def handle_info(:weekly_leaderboard, slack, state) do + display_leaderboard(slack, state, "#general") + + # Currently we don't delete the leaderboard weekly. It'll be a running total + {:ok, state} + end + + def handle_info(:persist_leaderboard, _slack, state) do + Storage.persist_leaderboard(state) + {:ok, state} + end + + def handle_event(%{user: user}, %{me: %{id: id}}, state) when user == id do + {:ok, state} + end + + def handle_event(message = %{channel: "D" <> _, type: "message"}, slack, state) do + {_, func} = Enum.find(@message_types, + {nil, :help}, + fn {reg, _} -> String.match?(message.text, reg) end + ) + IO.inspect func + state = Kernel.apply(Snapeth.SlackBot, func, [message, slack, state]) + + send(self(), :persist_leaderboard) + Logger.info("Persisted leaderboard") + + {:ok, state} + end + + def handle_event(_message, _slack, state) do + {:ok, state} end end diff --git a/lib/snapeth.ex b/lib/snapeth.ex index 596924c..d71372c 100644 --- a/lib/snapeth.ex +++ b/lib/snapeth.ex @@ -1,6 +1,20 @@ defmodule Snapeth do use GenServer + require Logger + alias Snapeth.Storage + + ########## + # CLIENT # + ########## + def display_leaderboard() do + send(SnapethMain, :work) + end + + + ########## + # SERVER # + ########## def child_spec(team_id) do %{ id: __MODULE__, @@ -10,40 +24,48 @@ defmodule Snapeth do end def start_link(team_id) do - GenServer.start_link(__MODULE__, team_id) + GenServer.start_link(__MODULE__, team_id, name: SnapethMain) end def init(_team_id) do - # schedule_work() - {:ok, pid} = Slack.Bot.start_link(Snapeth.SlackBot, [], Application.get_env(:snapeth, :slack_bot_token)) + Logger.info("Running with token #{inspect Application.get_env(:snapeth, :slack_bot_token)}") + + {:ok, pid} = Slack.Bot.start_link( + Snapeth.SlackBot, + [], + Application.get_env(:snapeth, :slack_bot_token) + ) + + Logger.info("Loading leaderboard...") + Storage.fetch_leaderboard() + |> load_leaderboard(pid) + |> (fn users_loaded -> + Logger.info("Loaded standings for #{inspect users_loaded} users") + end).() + {:ok, %{slack: pid}} end def handle_info(:work, state) do - send(state.slack, :display_leaderboard) - schedule_work() + send(state.slack, :weekly_leaderboard) {:noreply, state} end - defp schedule_work() do - next_wednesday_at_1400 = Timex.now - |> Map.put(:hours, 21) - |> Timex.shift(hours: 24) - |> shift_until_wednesday() - - delay = DateTime.diff(next_wednesday_at_1400, Timex.now) - # worst case, we'll end up waiting 8 days - - Process.send_after(self(), :work, delay) + def handle_info(:barf_state, state) do + send(state.slack, :barf_state) + {:noreply, state} end - defp shift_until_wednesday(time) do - if Date.day_of_week(time) == 3 do - time - else - shift_until_wednesday(Timex.shift(time, hours: 24)) - end + ########### + # HELPERS # + ########### + defp load_leaderboard(users, slack_bot_pid) do + users + |> Enum.each(fn {user_id, score} -> + send(slack_bot_pid, {:load_user, user_id, score}) + end) + Map.keys(users) |> length end end diff --git a/lib/storage.ex b/lib/storage.ex new file mode 100644 index 0000000..5de3550 --- /dev/null +++ b/lib/storage.ex @@ -0,0 +1,23 @@ +defmodule Snapeth.Storage do + require Logger + + def persist_leaderboard(state) do + Application.get_env(:snapeth, :bucket_name) + |> ExAws.S3.put_object( + Application.get_env(:snapeth, :leaderboard_data_file), + Poison.encode!(state) + ) |> ExAws.request() + end + + def fetch_leaderboard() do + Application.get_env(:snapeth, :bucket_name) + |> ExAws.S3.get_object(Application.get_env(:snapeth, :leaderboard_data_file)) + |> ExAws.request() + |> case do + {:ok, %{body: body}} -> + Poison.decode!(body) + error -> + Logger.error "Error loading leaderboard! #{inspect error}" + end + end +end diff --git a/mix.exs b/mix.exs index 89bb2f7..eadadfb 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Worker.MixProject do [ app: :snapeth, version: "0.1.0", - elixir: "~> 1.6", + elixir: "~> 1.5", start_permanent: Mix.env() == :prod, aliases: aliases(), deps: deps() @@ -28,10 +28,15 @@ defmodule Worker.MixProject do defp deps do [ {:distillery, "~> 1.5"}, - {:mox, "~> 0.3", only: :test}, - {:poison, "~> 3.1"}, - {:slack, "~> 0.13"}, - {:timex, "~> 3.1"}, + {:hackney, "~> 1.9"}, + {:sweet_xml, "~> 0.6"}, + {:ex_aws, "~> 2.1"}, + {:ex_aws_s3, "~> 2.0"}, + {:mox, "~> 0.4.0", only: :test}, + {:poison, "~> 3.0"}, + {:quantum, "~> 2.3"}, + {:slack, "~> 0.15"}, + {:timex, "~> 3.4.1"}, ] end end diff --git a/mix.lock b/mix.lock index 5519cc6..c0dcb81 100644 --- a/mix.lock +++ b/mix.lock @@ -1,19 +1,31 @@ %{ - "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"}, - "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [], [], "hexpm"}, - "distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [], [], "hexpm"}, - "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [], [], "hexpm"}, - "hackney": {:hex, :hackney, "1.11.0", "4951ee019df102492dabba66a09e305f61919a8a183a7860236c0fde586134b6", [], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, - "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"}, - "mox": {:hex, :mox, "0.3.2", "3b9b8364fd4f28628139de701d97c636b27a8f925f57a8d5a1b85fbd620dad3a", [], [], "hexpm"}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"}, - "slack": {:hex, :slack, "0.13.0", "565ace2343cb131dabc9524ea4ec182a338c71ccf1a07035ba209f15017ef658", [], [{:httpoison, "~> 0.11", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:websocket_client, "~> 1.2.4", [hex: :websocket_client, repo: "hexpm", optional: false]}], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"}, - "timex": {:hex, :timex, "3.2.1", "639975eac45c4c08c2dbf7fc53033c313ff1f94fad9282af03619a3826493612", [], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, - "tzdata": {:hex, :tzdata, "0.5.16", "13424d3afc76c68ff607f2df966c0ab4f3258859bbe3c979c9ed1606135e7352", [], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"}, - "websocket_client": {:hex, :websocket_client, "1.2.4", "14ec1ca4b6d247b44ccd9a80af8f6ca98328070f6c1d52a5cb00bc9d939d63b8", [], [], "hexpm"}, + "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, + "crontab": {:hex, :crontab, "1.1.4", "eae8de759d60dec2352ca577890f4cac26bcb907088ffef958f42ebb9c4f4cff", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, + "distillery": {:hex, :distillery, "1.5.5", "c6132d5c0152bdce6850fb6c942d58f1971b169b6965d908dc4e8767cfa51a95", [:mix], [], "hexpm"}, + "ex_aws": {:hex, :ex_aws, "2.1.0", "b92651527d6c09c479f9013caa9c7331f19cba38a650590d82ebf2c6c16a1d8a", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"}, + "ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"}, + "gen_stage": {:hex, :gen_stage, "0.14.0", "65ae78509f85b59d360690ce3378d5096c3130a0694bab95b0c4ae66f3008fad", [:mix], [], "hexpm"}, + "gen_state_machine": {:hex, :gen_state_machine, "2.0.3", "477ea51b466a749ab23a0d6090e9e84073f41f9aa28c7efc40eac18f3d4a9f77", [:mix], [], "hexpm"}, + "gettext": {:hex, :gettext, "0.16.0", "4a7e90408cef5f1bf57c5a39e2db8c372a906031cc9b1466e963101cb927dafc", [:mix], [], "hexpm"}, + "hackney": {:hex, :hackney, "1.14.0", "66e29e78feba52176c3a4213d42b29bdc4baff93a18cfe480f73b04677139dee", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "honeybadger": {:hex, :honeybadger, "0.10.3", "1d1176bb64a82e8987e53f1f48670181ed6f44fde4674c7a0c95e188f974ae5f", [:mix], [{:hackney, "~> 1.1", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.0.0 and < 2.0.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, ">= 1.0.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "1.3.1", "7ac607311f5f706b44e8b3fab736d0737f2f62a31910ccd9afe7227b43edb7f0", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, + "mox": {:hex, :mox, "0.4.0", "7f120840f7d626184a3d65de36189ca6f37d432e5d63acd80045198e4c5f7e6e", [:mix], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "quantum": {:hex, :quantum, "2.3.3", "83f565de81ac43b8fda4dd4266b209eaed29545d1c41e17aa6b75b08736c80f6", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"}, + "slack": {:hex, :slack, "0.15.0", "abe600b19e53af481540c176ae5b6433b225b083659b790d2e14853aaa65ab84", [:mix], [{:httpoison, "~> 1.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:websocket_client, "~> 1.2.4", [hex: :websocket_client, repo: "hexpm", optional: false]}], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "swarm": {:hex, :swarm, "3.3.1", "b4d29c49310b92b4a84bd3be6a51d9616eaeda1899b7619201d0908d8d789bd8", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"}, + "sweet_xml": {:hex, :sweet_xml, "0.6.5", "dd9cde443212b505d1b5f9758feb2000e66a14d3c449f04c572f3048c66e6697", [:mix], [], "hexpm"}, + "timex": {:hex, :timex, "3.4.1", "e63fc1a37453035e534c3febfe9b6b9e18583ec7b37fd9c390efdef97397d70b", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, + "tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, + "websocket_client": {:hex, :websocket_client, "1.2.4", "14ec1ca4b6d247b44ccd9a80af8f6ca98328070f6c1d52a5cb00bc9d939d63b8", [:rebar3], [], "hexpm"}, } diff --git a/requirements.yml b/requirements.yml index 6010333..1a474c9 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,8 +1,7 @@ --- service_name: snapeth -image: avvo/snapeth +image: avvo/snapeth-worker facing: backend -env_on: start health_check: false start_before_stop: false routing: