From 2f52210578acd6dc233b4319eebcbfa4d0d33bce Mon Sep 17 00:00:00 2001 From: Joshua Miertschin Date: Sat, 23 Dec 2023 00:13:06 -0600 Subject: [PATCH] add new users timer --- config/config.exs | 3 +- lib/miata_bot/new_users_timer.ex | 37 +++++ .../listeners/looking_for_miata_listener.ex | 12 +- .../listeners/new_users_listener.ex | 147 ++++++++++++++++++ lib/miata_bot_discord/settings.ex | 2 +- .../20231223051435_add__new_timer_table.exs | 14 ++ 6 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 lib/miata_bot/new_users_timer.ex create mode 100644 lib/miata_bot_discord/listeners/new_users_listener.ex create mode 100644 priv/repo/migrations/20231223051435_add__new_timer_table.exs diff --git a/config/config.exs b/config/config.exs index 3b5a3a3..2c5e6b3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -27,7 +27,8 @@ config :quarrel, Quarrel.GuildSupervisor, MiataBotDiscord.LookingForMiataListener, MiataBotDiscord.MemesChannelListener, MiataBotDiscord.SettingsListener, - MiataBotDiscord.TimeInteractionListener + MiataBotDiscord.TimeInteractionListener, + # MiataBotDiscord.NewUsersListener ] config :logger, backends: [:console, RingLogger] diff --git a/lib/miata_bot/new_users_timer.ex b/lib/miata_bot/new_users_timer.ex new file mode 100644 index 0000000..77eb1d9 --- /dev/null +++ b/lib/miata_bot/new_users_timer.ex @@ -0,0 +1,37 @@ +defmodule MiataBot.NewUsersTimer do + use Ecto.Schema + import Ecto.Changeset + + @enactment_date %DateTime{ + calendar: Calendar.ISO, + day: 22, + hour: 12, + microsecond: {0, 0}, + minute: 0, + month: 12, + second: 0, + std_offset: 0, + time_zone: "Etc/UTC", + utc_offset: 0, + year: 2023, + zone_abbr: "UTC" + } + + schema "new_users" do + field(:joined_at, :utc_datetime) + field(:refreshed_at, :utc_datetime, default: @enactment_date) + field(:discord_user_id, Snowflake) + field(:discord_guild_id, Snowflake) + end + + def changeset(new_user, params \\ %{}) do + new_user + |> cast(params, [ + :joined_at, + :refreshed_at, + :discord_user_id, + :discord_guild_id + ]) + |> unique_constraint([:discord_guild_id, :discord_user_id]) + end +end diff --git a/lib/miata_bot_discord/listeners/looking_for_miata_listener.ex b/lib/miata_bot_discord/listeners/looking_for_miata_listener.ex index 35cc878..16a89f3 100644 --- a/lib/miata_bot_discord/listeners/looking_for_miata_listener.ex +++ b/lib/miata_bot_discord/listeners/looking_for_miata_listener.ex @@ -74,12 +74,12 @@ defmodule MiataBotDiscord.LookingForMiataListener do end @impl Quarrel.Listener - def handle_guild_available(guild, state) do - for {_member_id, m} <- guild.members do - if state.config.looking_for_miata_role_id in m.roles do - ensure_looking_for_miata_timer(guild, m) - end - end + def handle_guild_available(_guild, state) do + # for {_member_id, m} <- guild.members do + # if state.config.looking_for_miata_role_id in m.roles do + # ensure_looking_for_miata_timer(guild, m) + # end + # end {:noreply, state} end diff --git a/lib/miata_bot_discord/listeners/new_users_listener.ex b/lib/miata_bot_discord/listeners/new_users_listener.ex new file mode 100644 index 0000000..7824d2f --- /dev/null +++ b/lib/miata_bot_discord/listeners/new_users_listener.ex @@ -0,0 +1,147 @@ +defmodule MiataBotDiscord.NewUsersListener do + require Logger + + use Quarrel.Listener + alias MiataBot.{Repo, NewUsersTimer} + import Ecto.Query + @expiry_days 0 + + def debug_expire_timer(timer, refreshed_at \\ nil) do + refreshed_at = + refreshed_at || + %DateTime{ + calendar: Calendar.ISO, + day: 20, + hour: 19, + microsecond: {0, 0}, + minute: 41, + month: 6, + second: 27, + std_offset: 0, + time_zone: "Etc/UTC", + utc_offset: 0, + year: 2019, + zone_abbr: "UTC" + } + + NewUsersTimer.changeset(timer, %{refreshed_at: refreshed_at}) + |> Repo.update!() + end + + @impl GenServer + def init(state) do + send(self(), :checkup) + {:ok, state} + end + + @impl GenServer + def handle_info(:checkup, state) do + guild_id = state.guild.id + timers = Repo.all(from t in NewUsersTimer, where: t.discord_guild_id == ^guild_id) + {:noreply, state, {:continue, timers}} + end + + @impl GenServer + def handle_continue([timer | rest], state) do + begin = timer.refreshed_at || timer.joined_at + complete = Timex.shift(begin, days: @expiry_days) + now = DateTime.utc_now() + + case Timex.compare(now, complete, :days) do + -1 -> + # now comes before compelte + # timer is not up yet + :ok + + 0 -> + # now is the same day as complete + # timer is expired + do_expire_timer(timer, state) + + 1 -> + # now is after complete + # timer is expired + do_expire_timer(timer, state) + end + + {:noreply, state, {:continue, rest}} + end + + def handle_continue([], state) do + # Logger.info("checkup complete") + Process.send_after(self(), :checkup, 300_000) + {:noreply, state, :hibernate} + end + + @impl Quarrel.Listener + def handle_guild_available(_guild, state) do + # for {_member_id, m} <- guild.members do + # if state.config.accepted_role_id in m.roles do + # ensure_new_user_timer(guild, m) + # end + # end + + {:noreply, state} + end + + @impl Quarrel.Listener + def handle_guild_member_update(old, new, state) do + if state.config.accepted_role_id in (new.roles -- old.roles) do + Logger.info("refreshing timer for #{new.user_id}") + timer = ensure_new_user_timer(state.guild, new) + refresh_looking_for_miata_timer(state.guild, timer) + end + + if state.config.accepted_role_id in (old.roles -- new.roles) do + Logger.info("refreshing timer for #{new.user_id}") + timer = ensure_new_user_timer(state.guild, new) + Repo.delete!(timer) + end + + {:noreply, state} + end + + def ensure_new_user_timer(guild, member) do + case Repo.get_by(NewUsersTimer, + discord_user_id: member.user_id, + discord_guild_id: guild.id + ) do + nil -> + NewUsersTimer.changeset(%NewUsersTimer{}, %{ + joined_at: DateTime.utc_now(), + discord_user_id: member.user_id, + discord_guild_id: guild.id + }) + |> Repo.insert!() + + timer -> + timer + end + end + + def refresh_looking_for_miata_timer(_guild, timer) do + NewUsersTimer.changeset(timer, %{ + refreshed_at: DateTime.utc_now() + }) + |> Repo.update!() + end + + def do_expire_timer(timer, state) do + member = get_guild_member!(state.guild.id, timer.discord_user_id) + + Logger.info("expiring new user timer for member: #{inspect(member)}") + + Nostrum.Api.remove_guild_member(state.guild.id, timer.discord_user_id) + catch + _, error -> + Logger.error("error doing user timer thing idk: #{inspect(error)}") + delete(timer) + end + + defp delete(timer) do + case Repo.get(NewUsersTimer, timer.id) do + nil -> :ok + timer -> Repo.delete!(timer) + end + end +end diff --git a/lib/miata_bot_discord/settings.ex b/lib/miata_bot_discord/settings.ex index 1e3ab64..60d6fa3 100644 --- a/lib/miata_bot_discord/settings.ex +++ b/lib/miata_bot_discord/settings.ex @@ -23,7 +23,7 @@ defmodule MiataBotDiscord.Settings do general_channel_id: 322_080_266_761_797_633, offtopic_channel_id: 322_162_421_156_282_369, looking_for_miata_role_id: 504_088_951_485_890_561, - accepted_role_id: 591_899_819_132_583_936, + accepted_role_id: 322_458_363_700_969_482, memes_channel_id: 555_431_196_884_992_000, verification_channel_id: 322_127_502_212_333_570, tcg_channel_id: nil diff --git a/priv/repo/migrations/20231223051435_add__new_timer_table.exs b/priv/repo/migrations/20231223051435_add__new_timer_table.exs new file mode 100644 index 0000000..cddb290 --- /dev/null +++ b/priv/repo/migrations/20231223051435_add__new_timer_table.exs @@ -0,0 +1,14 @@ +defmodule MiataBot.Repo.Migrations.Add_NewTimerTable do + use Ecto.Migration + + def change do + create table(:new_users) do + add :refreshed_at, :utc_datetime + add :joined_at, :utc_datetime + add :discord_user_id, :string + add :discord_guild_id, :string, default: "322080266761797633", null: false + end + + create unique_index(:looking_for_miatas, [:discord_user_id, :discord_guild_id]) + end +end