From 303124e83e8b66855ac74a23ebc27ffd0215c716 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 11 Dec 2024 17:19:54 +0200 Subject: [PATCH] Add disconnectTelegramBot mutation --- .../accounts/accounts_event_emitter.ex | 16 +++++++ lib/sanbase/accounts/user_settings.ex | 27 ++++++++++- lib/sanbase/event_bus/event_validation.ex | 7 +++ .../event_bus/user_events_subscriber.ex | 20 ++++++++ .../graphql/resolvers/user/user_resolver.ex | 15 ++++++ .../graphql/schema/queries/user_queries.ex | 6 +++ test/sanbase/telegram/telegram_test.exs | 48 +++++++++++++++++++ 7 files changed, 138 insertions(+), 1 deletion(-) diff --git a/lib/sanbase/accounts/accounts_event_emitter.ex b/lib/sanbase/accounts/accounts_event_emitter.ex index 190cbc305c..4717d94b0b 100644 --- a/lib/sanbase/accounts/accounts_event_emitter.ex +++ b/lib/sanbase/accounts/accounts_event_emitter.ex @@ -45,6 +45,22 @@ defmodule Sanbase.Accounts.EventEmitter do |> notify() end + def handle_event( + {:ok, user}, + :disconnect_telegram_bot, + %{telegram_chat_id: telegram_chat_id} = args + ) do + Map.merge( + %{ + event_type: :disconnect_telegram_bot, + user_id: user.id, + telegram_chat_id: telegram_chat_id + }, + args + ) + |> notify() + end + def handle_event({:ok, user}, :update_email_candidate, %{email_candidate: _} = args) do Map.merge(%{event_type: :update_email_candidate, user_id: user.id}, args) |> notify() diff --git a/lib/sanbase/accounts/user_settings.ex b/lib/sanbase/accounts/user_settings.ex index 393fbe50b8..88eb986fc6 100644 --- a/lib/sanbase/accounts/user_settings.ex +++ b/lib/sanbase/accounts/user_settings.ex @@ -39,7 +39,6 @@ defmodule Sanbase.Accounts.UserSettings do higher plan. Both things might take some to fix/upgrade, so having the option to unblock yourself until the issues are resolved is required.k """ - def can_self_reset_api_rate_limits?(user) do %{self_api_rate_limits_reset_at: last_self_reset_at} = settings_for(user) @@ -56,6 +55,32 @@ defmodule Sanbase.Accounts.UserSettings do end end + def disconnect_telegram_bot(user) do + # First get the existing chat id so we can send it with the event + # so the user events subscriber can send a message to that chat id + # informing that the bot is disconnected. + user_settings = + %{settings: %{telegram_chat_id: telegram_chat_id}} = + user_settings_for(user, force: true) + + user_settings + |> changeset(%{settings: %{telegram_chat_id: nil}}) + |> Sanbase.Repo.update() + |> case do + {:ok, user_settings} -> + Sanbase.Accounts.EventEmitter.emit_event( + {:ok, user}, + :disconnect_telegram_bot, + %{telegram_chat_id: telegram_chat_id} + ) + + {:ok, user_settings} + + {:error, error} -> + {:error, error} + end + end + def update_self_reset_api_rate_limits_datetime(user, dt) do user_settings_for(user, force: true) |> changeset(%{user_id: user.id, settings: %{self_api_rate_limits_reset_at: dt}}) diff --git a/lib/sanbase/event_bus/event_validation.ex b/lib/sanbase/event_bus/event_validation.ex index 649ef38d19..ad6433c111 100644 --- a/lib/sanbase/event_bus/event_validation.ex +++ b/lib/sanbase/event_bus/event_validation.ex @@ -20,6 +20,13 @@ defmodule Sanbase.EventBus.EventValidation do def valid?(%{event_type: :update_email_candidate, user_id: id, email_candidate: email}), do: valid_integer_id?(id) and is_binary(email) + def valid?(%{ + event_type: :disconnect_telegram_bot, + user_id: id, + telegram_chat_id: telegram_chat_id + }), + do: valid_integer_id?(id) and is_integer(telegram_chat_id) + def valid?(%{event_type: :register_user, user_id: id, login_origin: login_origin}), do: valid_integer_id?(id) and (is_atom(login_origin) or is_binary(login_origin)) diff --git a/lib/sanbase/event_bus/user_events_subscriber.ex b/lib/sanbase/event_bus/user_events_subscriber.ex index af193b8181..169f0a51f3 100644 --- a/lib/sanbase/event_bus/user_events_subscriber.ex +++ b/lib/sanbase/event_bus/user_events_subscriber.ex @@ -114,6 +114,26 @@ defmodule Sanbase.EventBus.UserEventsSubscriber do state end + defp handle_event( + %{ + data: %{ + event_type: :disconnect_telegram_bot, + user_id: _, + telegram_chat_id: chat_id + } + }, + event_shadow, + state + ) do + Sanbase.Telegram.send_message_to_chat_id( + chat_id, + "You have successfully disconnected your Telegram bot from your Sanbase profile." + ) + + EventBus.mark_as_completed({__MODULE__, event_shadow}) + state + end + defp handle_event(_event, event_shadow, state) do # The unhandled events are marked as completed EventBus.mark_as_completed({__MODULE__, event_shadow}) diff --git a/lib/sanbase_web/graphql/resolvers/user/user_resolver.ex b/lib/sanbase_web/graphql/resolvers/user/user_resolver.ex index 6a001de47b..43356ed84a 100644 --- a/lib/sanbase_web/graphql/resolvers/user/user_resolver.ex +++ b/lib/sanbase_web/graphql/resolvers/user/user_resolver.ex @@ -174,6 +174,21 @@ defmodule SanbaseWeb.Graphql.Resolvers.UserResolver do end end + def disconnect_telegram_bot(_root, _args, %{context: %{auth: %{current_user: user}}}) do + case UserSettings.disconnect_telegram_bot(user) do + {:ok, _user_settings} -> + # Refresh the data in the user + Sanbase.Accounts.get_user(user.id) + + {:error, changeset} -> + { + :error, + message: "Cannot disconnect current user's telegram bot", + details: changeset_errors(changeset) + } + end + end + def change_name(_root, %{name: new_name}, %{context: %{auth: %{current_user: user}}}) do case User.change_name(user, new_name) do {:ok, user} -> diff --git a/lib/sanbase_web/graphql/schema/queries/user_queries.ex b/lib/sanbase_web/graphql/schema/queries/user_queries.ex index 32bbddc94b..4f2d9766b7 100644 --- a/lib/sanbase_web/graphql/schema/queries/user_queries.ex +++ b/lib/sanbase_web/graphql/schema/queries/user_queries.ex @@ -66,6 +66,12 @@ defmodule SanbaseWeb.Graphql.Schema.UserQueries do end object :user_mutations do + field :disconnect_telegram_bot, :user do + middleware(JWTAuth) + + resolve(&UserResolver.disconnect_telegram_bot/3) + end + field :change_username, :user do arg(:username, non_null(:string)) diff --git a/test/sanbase/telegram/telegram_test.exs b/test/sanbase/telegram/telegram_test.exs index df2a44ca6c..039681bc13 100644 --- a/test/sanbase/telegram/telegram_test.exs +++ b/test/sanbase/telegram/telegram_test.exs @@ -50,6 +50,36 @@ defmodule Sanbase.TelegramTest do assert chat_id == @telegram_chat_id end + test "disconnect telegram bot after it is connected", context do + get_telegram_deep_link(context) + + %Telegram.UserToken{token: user_token} = Telegram.UserToken.by_user_id(context.user.id) + + simulate_telegram_deep_link_follow(context, user_token) + %Settings{telegram_chat_id: chat_id} = UserSettings.settings_for(context.user, force: true) + assert chat_id == @telegram_chat_id + + self_pid = self() + + Sanbase.Mock.prepare_mock(Sanbase.Telegram, :send_message_to_chat_id, fn _chat_id, text -> + send(self_pid, {:telegram_to_self, text}) + {:ok, text} + end) + |> Sanbase.Mock.run_with_mocks(fn -> + assert %{"settings" => %{"hasTelegramConnected" => false}} = + disconnect_telegram_bot(context) + + %Settings{telegram_chat_id: chat_id} = UserSettings.settings_for(context.user, force: true) + assert chat_id == nil + + assert_receive( + {:telegram_to_self, + "You have successfully disconnected your Telegram bot from your Sanbase profile."}, + 200 + ) + end) + end + test "revoke telegram deep link removes the token", context do get_telegram_deep_link(context) @@ -113,6 +143,24 @@ defmodule Sanbase.TelegramTest do json_response(result, 200)["data"]["revokeTelegramDeepLink"] end + defp disconnect_telegram_bot(context) do + mutation = """ + mutation{ + disconnectTelegramBot{ + settings{ + hasTelegramConnected + } + } + } + """ + + result = + context.conn + |> post("/graphql", mutation_skeleton(mutation)) + + json_response(result, 200)["data"]["disconnectTelegramBot"] + end + defp simulate_telegram_deep_link_follow(context, user_token) do response = %{ "message" => %{