diff options
author | Simponic <loganhunt@simponic.xyz> | 2023-02-01 14:57:14 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-01 14:57:14 -0700 |
commit | fe5f5b77fcc3ef24516866561f9b54ac07663ad6 (patch) | |
tree | ba63998ec306983d87594195d60ecb0abb6ea5d1 /lib/chessh/discord | |
parent | 324d041d5c5cbcdf0083dcd802144a57443789f6 (diff) | |
download | chessh-fe5f5b77fcc3ef24516866561f9b54ac07663ad6.tar.gz chessh-fe5f5b77fcc3ef24516866561f9b54ac07663ad6.zip |
Discord notifs (#14)
* Add role id to config
* Add discord notifications for games
* Fix discord discriminant tests
Diffstat (limited to 'lib/chessh/discord')
-rw-r--r-- | lib/chessh/discord/notifier.ex | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/lib/chessh/discord/notifier.ex b/lib/chessh/discord/notifier.ex new file mode 100644 index 0000000..09c4ec0 --- /dev/null +++ b/lib/chessh/discord/notifier.ex @@ -0,0 +1,129 @@ +defmodule Chessh.DiscordNotifier do + use GenServer + + @name :discord_notifier + + alias Chessh.{Game, Player, Repo} + + def start_link(state \\ []) do + GenServer.start_link(__MODULE__, state, name: @name) + end + + @impl true + def init(state) do + {:ok, state} + end + + @impl true + def handle_cast(x, state), do: handle_info(x, state) + + @impl true + def handle_info({:attempt_notification, notification} = body, state) do + [discord_notification_rate, discord_notification_rate_ms] = + Application.get_env(:chessh, RateLimits) + |> Keyword.take([:discord_notification_rate, :discord_notification_rate_ms]) + |> Keyword.values() + + reschedule_delay = Application.get_env(:chessh, RateLimits)[:reschedule_delay] + + case Hammer.check_rate_inc( + :redis, + "discord-webhook-message-rate", + discord_notification_rate_ms, + discord_notification_rate, + 1 + ) do + {:allow, _count} -> + send_notification(notification) + + {:deny, _limit} -> + Process.send_after(self(), body, reschedule_delay) + end + + {:noreply, state} + end + + @impl true + def handle_info({:schedule_notification, notification, delay}, state) do + Process.send_after(self(), {:attempt_notification, notification}, delay) + {:noreply, state} + end + + defp send_notification({:move_reminder, game_id}) do + [min_delta_t, discord_game_move_notif_webhook] = + Application.get_env(:chessh, DiscordNotifications) + |> Keyword.take([:game_move_notif_delay_ms, :discord_game_move_notif_webhook]) + |> Keyword.values() + + case Repo.get(Game, game_id) do + nil -> + nil + + game -> + %Game{ + dark_player: %Player{discord_id: dark_player_discord_id}, + light_player: %Player{discord_id: light_player_discord_id}, + turn: turn, + updated_at: last_updated, + moves: move_count + } = Repo.preload(game, [:dark_player, :light_player]) + + delta_t = NaiveDateTime.diff(NaiveDateTime.utc_now(), last_updated, :millisecond) + + if delta_t >= min_delta_t do + post_discord( + discord_game_move_notif_webhook, + "<@#{if turn == :light, do: light_player_discord_id, else: dark_player_discord_id}> it is your move in Game #{game_id} (move #{move_count})." + ) + end + end + end + + defp send_notification({:game_created, game_id}) do + [pingable_mention, discord_game_created_notif_webhook] = + Application.get_env(:chessh, DiscordNotifications) + |> Keyword.take([:looking_for_games_role_mention, :discord_new_game_notif_webhook]) + |> Keyword.values() + + case Repo.get(Game, game_id) do + nil -> + nil + + game -> + %Game{ + dark_player: dark_player, + light_player: light_player + } = Repo.preload(game, [:dark_player, :light_player]) + + message = + case {is_nil(light_player), is_nil(dark_player)} do + {true, false} -> + "#{pingable_mention}, <@#{dark_player.discord_id}> is looking for an opponent to play as light in Game #{game_id}" + + {false, true} -> + "#{pingable_mention}, <@#{light_player.discord_id}> is looking for an opponent to play as dark in Game #{game_id}" + + _ -> + false + end + + if message do + post_discord(discord_game_created_notif_webhook, message) + end + end + end + + defp post_discord(webhook, message) do + :httpc.request( + :post, + { + String.to_charlist(webhook), + [], + 'application/json', + %{content: message} |> Jason.encode!() |> String.to_charlist() + }, + [], + [] + ) + end +end |