Discord threads (#16)
* Add initial support for discord threads * Finish thread creation
This commit is contained in:
parent
a26256700d
commit
47d3f28f16
@ -17,6 +17,7 @@ SERVER_REDIRECT_URI=http://127.0.0.1:3000/api/oauth/redirect
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
DISCORD_USER_AGENT=
|
||||
DISCORD_BOT_TOKEN=
|
||||
|
||||
JWT_SECRET=aVerySecretJwtSigningSecret
|
||||
|
||||
@ -27,6 +28,6 @@ REACT_APP_SSH_PORT=42069
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
|
||||
NEW_GAME_PINGABLE_ROLE_ID=1123232
|
||||
NEW_GAME_CHANNEL_WEBHOOK=https://discordapp.com/api/webhooks/
|
||||
REMIND_MOVE_CHANNEL_WEBHOOK=https://discordapp.com/api/webhooks/
|
||||
NEW_GAME_PINGABLE_ROLE_ID=10
|
||||
REMIND_MOVE_CHANNEL_ID=91
|
||||
NEW_GAME_CHANNEL_ID=91
|
||||
|
@ -15,7 +15,7 @@ config :chessh, RateLimits,
|
||||
player_public_keys: 15,
|
||||
create_game_ms: 60 * 1000,
|
||||
create_game_rate: 3,
|
||||
discord_notification_rate: 3,
|
||||
discord_notification_rate: 30,
|
||||
discord_notification_rate_ms: 1000
|
||||
|
||||
config :chessh, Web,
|
||||
@ -24,8 +24,8 @@ config :chessh, Web,
|
||||
discord_scope: "identify"
|
||||
|
||||
config :chessh, DiscordNotifications,
|
||||
game_move_notif_delay_ms: 3 * 60 * 1000,
|
||||
game_created_notif_delay_ms: 30 * 1000,
|
||||
game_move_notif_delay_ms: 10 * 1000,
|
||||
game_created_notif_delay_ms: 10 * 1000,
|
||||
reschedule_delay: 5 * 1000
|
||||
|
||||
config :joken, default_signer: "secret"
|
||||
|
@ -5,8 +5,9 @@ config :chessh,
|
||||
|
||||
config :chessh, DiscordNotifications,
|
||||
looking_for_games_role_mention: "<@&#{System.get_env("NEW_GAME_PINGABLE_ROLE_ID")}>",
|
||||
discord_game_move_notif_webhook: System.get_env("REMIND_MOVE_CHANNEL_WEBHOOK"),
|
||||
discord_new_game_notif_webhook: System.get_env("NEW_GAME_CHANNEL_WEBHOOK")
|
||||
remind_move_channel_id: System.get_env("REMIND_MOVE_CHANNEL_ID"),
|
||||
discord_bot_token: System.get_env("DISCORD_BOT_TOKEN"),
|
||||
new_game_channel_id: System.get_env("NEW_GAME_CHANNEL_ID")
|
||||
|
||||
config :chessh, Web,
|
||||
discord_client_id: System.get_env("DISCORD_CLIENT_ID"),
|
||||
|
@ -28,7 +28,7 @@ defmodule Chessh.DiscordNotifier do
|
||||
|
||||
case Hammer.check_rate_inc(
|
||||
:redis,
|
||||
"discord-webhook-message-rate",
|
||||
"discord-rate",
|
||||
discord_notification_rate_ms,
|
||||
discord_notification_rate,
|
||||
1
|
||||
@ -50,9 +50,9 @@ defmodule Chessh.DiscordNotifier do
|
||||
end
|
||||
|
||||
defp send_notification({:move_reminder, game_id}) do
|
||||
[min_delta_t, discord_game_move_notif_webhook] =
|
||||
[min_delta_t, remind_move_channel_id] =
|
||||
Application.get_env(:chessh, DiscordNotifications)
|
||||
|> Keyword.take([:game_move_notif_delay_ms, :discord_game_move_notif_webhook])
|
||||
|> Keyword.take([:game_move_notif_delay_ms, :remind_move_channel_id])
|
||||
|> Keyword.values()
|
||||
|
||||
case Repo.get(Game, game_id) |> Repo.preload([:dark_player, :light_player]) do
|
||||
@ -62,13 +62,27 @@ defmodule Chessh.DiscordNotifier do
|
||||
turn: turn,
|
||||
updated_at: last_updated,
|
||||
moves: move_count,
|
||||
status: :continue
|
||||
} ->
|
||||
status: :continue,
|
||||
discord_thread_id: discord_thread_id
|
||||
} = game ->
|
||||
delta_t = NaiveDateTime.diff(NaiveDateTime.utc_now(), last_updated, :millisecond)
|
||||
|
||||
game =
|
||||
if is_nil(discord_thread_id) do
|
||||
{:ok, game} =
|
||||
Game.changeset(game, %{
|
||||
discord_thread_id: make_private_discord_thread_id(remind_move_channel_id, game)
|
||||
})
|
||||
|> Repo.update()
|
||||
|
||||
game
|
||||
else
|
||||
game
|
||||
end
|
||||
|
||||
if delta_t >= min_delta_t do
|
||||
post_discord(
|
||||
discord_game_move_notif_webhook,
|
||||
game.discord_thread_id,
|
||||
"<@#{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
|
||||
@ -78,16 +92,33 @@ defmodule Chessh.DiscordNotifier do
|
||||
end
|
||||
end
|
||||
|
||||
defp send_notification({:cleanup_thread, game_id}) do
|
||||
case Repo.get(Game, game_id) |> Repo.preload([:dark_player, :light_player]) do
|
||||
%Game{
|
||||
discord_thread_id: discord_thread_id,
|
||||
status: status
|
||||
} = game ->
|
||||
if !is_nil(discord_thread_id) && status != :continue do
|
||||
destroy_channel(discord_thread_id)
|
||||
|
||||
Game.changeset(game, %{
|
||||
discord_thread_id: nil
|
||||
})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp send_notification({:game_created, game_id}) do
|
||||
[pingable_mention, discord_game_created_notif_webhook] =
|
||||
[pingable_mention, new_game_channel_id] =
|
||||
Application.get_env(:chessh, DiscordNotifications)
|
||||
|> Keyword.take([:looking_for_games_role_mention, :discord_new_game_notif_webhook])
|
||||
|> Keyword.take([:looking_for_games_role_mention, :new_game_channel_id])
|
||||
|> Keyword.values()
|
||||
|
||||
case Repo.get(Game, game_id) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
game ->
|
||||
%Game{
|
||||
dark_player: dark_player,
|
||||
@ -107,22 +138,85 @@ defmodule Chessh.DiscordNotifier do
|
||||
end
|
||||
|
||||
if message do
|
||||
post_discord(discord_game_created_notif_webhook, message)
|
||||
post_discord(new_game_channel_id, message)
|
||||
end
|
||||
|
||||
nil ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp post_discord(webhook, message) do
|
||||
:httpc.request(
|
||||
defp make_private_discord_thread_id(channel_id, %Game{
|
||||
id: game_id,
|
||||
dark_player: %Player{discord_id: dark_player_discord_id, username: dark_username},
|
||||
light_player: %Player{discord_id: light_player_discord_id, username: light_username}
|
||||
}) do
|
||||
case make_discord_api_call(
|
||||
:post,
|
||||
"channels/#{channel_id}/threads",
|
||||
%{
|
||||
# Private thread
|
||||
type: 12,
|
||||
name: "Game #{game_id} - #{light_username} V #{dark_username}"
|
||||
}
|
||||
) do
|
||||
{:ok, {_, _, body}} ->
|
||||
%{"id" => thread_id} = Jason.decode!(body)
|
||||
|
||||
[light_player_discord_id, dark_player_discord_id]
|
||||
|> Enum.map(fn id ->
|
||||
make_discord_api_call(:put, 'channels/#{thread_id}/thread-members/#{id}')
|
||||
end)
|
||||
|
||||
thread_id
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp post_discord(channel_id, message) do
|
||||
make_discord_api_call(:post, "channels/#{channel_id}/messages", %{content: message})
|
||||
end
|
||||
|
||||
defp destroy_channel(channel_id) do
|
||||
make_discord_api_call(:delete, "channels/#{channel_id}")
|
||||
end
|
||||
|
||||
defp make_discord_api_call(method, route),
|
||||
do:
|
||||
:httpc.request(
|
||||
method,
|
||||
{
|
||||
String.to_charlist(webhook),
|
||||
[],
|
||||
'application/json',
|
||||
%{content: message} |> Jason.encode!() |> String.to_charlist()
|
||||
'https://discord.com/api/#{route}',
|
||||
[
|
||||
make_authorization_header()
|
||||
]
|
||||
},
|
||||
[],
|
||||
[]
|
||||
)
|
||||
|
||||
defp make_discord_api_call(method, route, body),
|
||||
do:
|
||||
:httpc.request(
|
||||
method,
|
||||
{
|
||||
'https://discord.com/api/#{route}',
|
||||
[
|
||||
make_authorization_header()
|
||||
],
|
||||
'application/json',
|
||||
body
|
||||
|> Jason.encode!()
|
||||
|> String.to_charlist()
|
||||
},
|
||||
[],
|
||||
[]
|
||||
)
|
||||
|
||||
defp make_authorization_header() do
|
||||
bot_token = Application.get_env(:chessh, DiscordNotifications)[:discord_bot_token]
|
||||
{'Authorization', 'Bot #{bot_token}'}
|
||||
end
|
||||
end
|
||||
|
@ -15,6 +15,8 @@ defmodule Chessh.Game do
|
||||
belongs_to(:light_player, Player, foreign_key: :light_player_id)
|
||||
belongs_to(:dark_player, Player, foreign_key: :dark_player_id)
|
||||
|
||||
field(:discord_thread_id, :string)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@ -28,7 +30,8 @@ defmodule Chessh.Game do
|
||||
:status,
|
||||
:last_move,
|
||||
:light_player_id,
|
||||
:dark_player_id
|
||||
:dark_player_id,
|
||||
:discord_thread_id
|
||||
])
|
||||
end
|
||||
end
|
||||
|
@ -396,7 +396,7 @@ defmodule Chessh.SSH.Client.Game do
|
||||
{:ok, status} ->
|
||||
{:ok, fen} = :binbo.get_fen(binbo_pid)
|
||||
|
||||
{:ok, _new_game} =
|
||||
{:ok, %Game{status: after_move_status}} =
|
||||
game
|
||||
|> Game.changeset(
|
||||
Map.merge(
|
||||
@ -413,11 +413,18 @@ defmodule Chessh.SSH.Client.Game do
|
||||
|
||||
:syn.publish(:games, {:game, game_id}, {:new_move, attempted_move})
|
||||
|
||||
if after_move_status == :continue do
|
||||
GenServer.cast(
|
||||
:discord_notifier,
|
||||
{:schedule_notification, {:move_reminder, game_id},
|
||||
Application.get_env(:chessh, DiscordNotifications)[:game_move_notif_delay_ms]}
|
||||
)
|
||||
else
|
||||
GenServer.cast(
|
||||
:discord_notifier,
|
||||
{:schedule_notification, {:cleanup_thread, game_id}, 0}
|
||||
)
|
||||
end
|
||||
|
||||
_ ->
|
||||
nil
|
||||
|
@ -0,0 +1,9 @@
|
||||
defmodule Chessh.Repo.Migrations.AddDiscordThreadId do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:games) do
|
||||
add(:discord_thread_id, :string, null: true)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user