summaryrefslogtreecommitdiff
path: root/lib/chessh
diff options
context:
space:
mode:
authorSimponic <loganhunt@simponic.xyz>2023-02-01 14:57:14 -0700
committerGitHub <noreply@github.com>2023-02-01 14:57:14 -0700
commitfe5f5b77fcc3ef24516866561f9b54ac07663ad6 (patch)
treeba63998ec306983d87594195d60ecb0abb6ea5d1 /lib/chessh
parent324d041d5c5cbcdf0083dcd802144a57443789f6 (diff)
downloadchessh-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')
-rw-r--r--lib/chessh/application.ex1
-rw-r--r--lib/chessh/discord/notifier.ex129
-rw-r--r--lib/chessh/ssh/client/game/game.ex14
3 files changed, 143 insertions, 1 deletions
diff --git a/lib/chessh/application.ex b/lib/chessh/application.ex
index 59926cc..f92707f 100644
--- a/lib/chessh/application.ex
+++ b/lib/chessh/application.ex
@@ -18,6 +18,7 @@ defmodule Chessh.Application do
children = [
Chessh.Repo,
Chessh.SSH.Daemon,
+ Chessh.DiscordNotifier,
Plug.Cowboy.child_spec(
scheme: :http,
plug: Chessh.Web.Endpoint,
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
diff --git a/lib/chessh/ssh/client/game/game.ex b/lib/chessh/ssh/client/game/game.ex
index 4a79d05..fc48d6f 100644
--- a/lib/chessh/ssh/client/game/game.ex
+++ b/lib/chessh/ssh/client/game/game.ex
@@ -77,7 +77,7 @@ defmodule Chessh.SSH.Client.Game do
) do
{:allow, _count} ->
# Starting a new game
- {:ok, %Game{} = game} =
+ {:ok, %Game{id: game_id} = game} =
Game.changeset(
%Game{},
Map.merge(
@@ -92,6 +92,12 @@ defmodule Chessh.SSH.Client.Game do
)
|> Repo.insert()
+ GenServer.cast(
+ :discord_notifier,
+ {:schedule_notification, {:game_created, game_id},
+ Application.get_env(:chessh, DiscordNotifications)[:game_created_notif_delay_ms]}
+ )
+
init([
%State{
state
@@ -403,6 +409,12 @@ defmodule Chessh.SSH.Client.Game do
:syn.publish(:games, {:game, game_id}, {:new_move, attempted_move})
+ GenServer.cast(
+ :discord_notifier,
+ {:schedule_notification, {:move_reminder, game_id},
+ Application.get_env(:chessh, DiscordNotifications)[:game_move_notif_delay_ms]}
+ )
+
_ ->
nil
end