Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4a37062
Add Honeybadger
Apr 3, 2018
023b496
Add circleci build
Apr 6, 2018
742d420
Update docker for circleci
Apr 6, 2018
98b4614
change workdir
Apr 6, 2018
76c0b16
Add a CMD
Apr 6, 2018
1cafdba
Match up Docker names
Apr 6, 2018
23a2c50
Merge branch 'master' into deploy-test
Apr 6, 2018
0beaa4c
Merge pull request #1 from avvo/deploy-test
ellevargas Apr 6, 2018
318e424
I did need to replace os vars whoops
Apr 6, 2018
ea324fa
Delete the line that assumes I am using avvoenv when I am not
Apr 7, 2018
dfb7df0
Merge branch 'master' of github.com:avvo/snapeth-worker
Apr 7, 2018
d7dfdf2
Add scheduling for leaderboard update
Apr 7, 2018
8b5e8d1
Change copy for help message
Apr 7, 2018
6401cd0
Add newline
Apr 7, 2018
ec2cc1b
Change copy to reflect leaderboard surfacing times
Apr 20, 2018
64a9192
Persist state so showing leaderboard does not delete leaderboard
Apr 20, 2018
6ebbee7
Add logging for debugging new feature
Apr 20, 2018
f7ff9dc
Remove hilarious stack overflow who am I talking to oh it me case
Apr 20, 2018
2712379
Rename for semantic clarity
Apr 20, 2018
96c90fc
Pass snap reason to receiving user
Sep 26, 2018
8ab1a8b
Get leaderboard release into weekly cron job
Sep 26, 2018
6320908
Run the leaderboard every Monday and Friday at 2
Sep 26, 2018
3d15fc1
add debug print
Sep 26, 2018
826c63f
better debug print
Sep 26, 2018
cd68d1c
fix config for bot token
Sep 26, 2018
82a045a
Formatting and future plans
Sep 26, 2018
492ca27
Snapeth now persists the leaderboard to s3
Sep 28, 2018
f29ee6f
Merge branch 'master' of github.com:avvo/snapeth-worker
Sep 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.git
_build
cover
deps
doc
test
tmp
Expand Down
18 changes: 5 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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 \
Expand All @@ -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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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).
21 changes: 20 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion lib/application.ex
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 4 additions & 0 deletions lib/scheduler.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule Snapeth.Scheduler do
use Quantum.Scheduler,
otp_app: :snapeth
end
124 changes: 93 additions & 31 deletions lib/slack_bot.ex
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
defmodule Snapeth.SlackBot do
use Slack
require Logger
alias Snapeth.Storage

@message_types [
{~r/help/i, :help},
{~r/^<@\w+>/, :snap},
{~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

Expand All @@ -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()
Expand All @@ -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
Loading