accounts.ex 9.51 KB
Newer Older
1 2 3 4 5
defmodule SnugglesApp.Accounts do
  @moduledoc """
  The Accounts context.
  """

6
  require Logger
7
  import Ecto.Query, warn: false
8

9
  alias SnugglesApp.Repo
10
  alias SnugglesApp.Discord
11 12

  alias SnugglesApp.Accounts.User
13
  alias SnugglesApp.Accounts.EveUser
Peter van der meulen's avatar
Peter van der meulen committed
14
  alias SnugglesApp.Accounts.Corporation
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

  @doc """
  Returns the list of users.

  ## Examples

      iex> list_users()
      [%User{}, ...]

  """
  def list_users do
    Repo.all(User)
  end

  @doc """
30
  Creates a base nuser trough the ESI interface
31 32 33

  ## Examples

34
      iex> create_user(one_time_token)
35 36
      {:ok, %User{}}

37
      iex> create_user(wrong_stuff})
38 39 40
      {:error, %Ecto.Changeset{}}

  """
41
  def create_user(token) do
42
    user_response = EveUser.fetch_character(%{auth_token: token})
43

44
    case user_response do
45 46
      {:ok, user_attrs} -> find_user_from_eve_user(user_attrs)
      {:error, reason} -> {:error, reason}
47 48 49
    end
  end

50 51
  defp find_user_from_eve_user(user_attrs) do
    case Repo.get_by(User, character_id: user_attrs.character_id) do
52
      nil ->
53
        create_user_from_eve_user(user_attrs)
54

55 56 57
      user ->
        attrs = Map.merge(%{auth_token: generate_auth_token()}, user_attrs)
        update_user(user, attrs)
58 59 60 61 62 63 64
    end
  end

  defp generate_auth_token do
    Base.encode64(Ecto.UUID.generate())
  end

65
  defp create_user_from_eve_user(user_attrs) do
66
    attrs = Map.merge(user_attrs, %{auth_token: generate_auth_token()})
67

68 69 70
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
71 72
  end

73 74 75 76 77 78 79
  def handle_user_auth_request(%{
        "level" => _,
        "author_id" => author_id,
        "channel_id" => _,
        "content" => content,
        "mentions" => _
      }) do
80 81 82
    case Regex.run(~r/!auth-(.+?)$/, content) do
      nil ->
        Logger.info("Account unmatched auth req: #{content}")
83

Peter van der meulen's avatar
Peter van der meulen committed
84
      extracted_content ->
85 86 87 88 89
        link_discord_to_user(List.last(extracted_content), author_id)
    end
  end

  defp link_discord_to_user(auth_token, author_id) do
90 91 92 93
    case Repo.one(from(u in User, where: u.auth_token == ^auth_token and is_nil(u.discord_id))) do
      nil ->
        Logger.info("No account found for token")

Peter van der meulen's avatar
Peter van der meulen committed
94
      user ->
95 96 97 98 99 100
        {:ok, _} =
          update_user(user, %{
            auth_token: nil,
            discord_id: Integer.to_string(author_id)
          })

101
        SnugglesApp.RabbitService.publish_json("account.events", %{"user_id" => user.id, "discord_id" => author_id})
102 103 104 105 106 107 108 109
    end
  end

  @doc """
  Patches the user from Eve data and sets permissions.

  ## Examples

110
      iex> handle_user_update_request(%{"user_id" => user_id})
111 112 113 114 115 116
      {:ok, %User{}}

      iex> updatee_user(user, attrs)
      {:error, %Ecto.Changeset{}}

  """
117
  def handle_user_update_request(%{"user_id" => user_id} = _) do
118 119
    user = Repo.get(User, user_id)
    {:ok, eve_user} = EveUser.fetch_full(user.character_id)
120
    {:ok, updated_user} = update_user(user, eve_user)
121
    Discord.manage_alliance_permissions(updated_user, user.access, updated_user.access)
122 123
    manage_corp_discord_permissions(updated_user, user.corp_id, updated_user.corp_id)
    {:ok, updated_user}
124 125
  end

Peter van der meulen's avatar
Peter van der meulen committed
126 127
  def manage_corp_discord_permissions(_, unchanged, unchanged), do: {:ok, "unchanged"}
  def manage_corp_discord_permissions(_, nil, _), do: {:ok, "new member"}
128

129 130 131 132
  def manage_corp_discord_persmissions(user, old_corp_id, _) do
    case Repo.get_by(Corporation, eve_corp_id: old_corp_id) do
      nil ->
        {:ok, "skipping"}
133

134 135 136 137
      corp ->
        Discord.remove_corp_role_from_user(
          user.discord_id,
          corp.discord_guild_id,
138 139
          corp.discord_member_role_id
        )
140
    end
141 142
  end

143 144 145 146
  @doc """
  Queues all users in the user table to be checked for membership

  ## Examples
147

148 149 150 151
      iex> queue_all_users()
      {:ok}

  """
152 153
  def queue_all_users do
    Enum.each(
154
      Repo.all(from(u in User, where: not is_nil(u.discord_id), select: u.id)),
155 156
      &SnugglesApp.RabbitService.publish_json("account.events", %{"user_id" => &1})
    )
157

158 159 160
    {:ok}
  end

Peter van der meulen's avatar
Peter van der meulen committed
161
  @doc """
162
  Sends help message to admin on how to create corps
Peter van der meulen's avatar
Peter van der meulen committed
163 164 165 166

  ## Examples

      iex> handle_admin_request(user, attrs)
167
      {:ok}
Peter van der meulen's avatar
Peter van der meulen committed
168 169

  """
170 171 172
  def handle_admin_request(
        %{"author_id" => author_id, "content" => content, "mentions" => mentions} = _payload
      ) do
Peter van der meulen's avatar
Peter van der meulen committed
173
    case String.downcase(content) do
174
      "help" -> Discord.send_admin_help_message(author_id)
Peter van der meulen's avatar
Peter van der meulen committed
175
      "add corp discord" -> send_admin_corp_creation_help_message(author_id)
176 177
      "rerun " <> _ -> force_user_update(mentions)
      _ -> Discord.send_admin_fallback_message(author_id)
Peter van der meulen's avatar
Peter van der meulen committed
178 179 180 181 182 183 184 185
    end
  end

  defp send_admin_corp_creation_help_message(author_id) do
    user = Repo.get_by(User, discord_id: Integer.to_string(author_id))
    Discord.send_admin_corp_help_message(author_id, user)
  end

Peter van der meulen's avatar
Peter van der meulen committed
186
  defp force_user_update([nil | _]), do: Discord.send_message_to_admin("No user mentioned")
187

Peter van der meulen's avatar
Peter van der meulen committed
188 189
  defp force_user_update([discord_user | _]) do
    case Repo.get_by(User, discord_id: Integer.to_string(discord_user["id"])) do
190
      nil ->
Peter van der meulen's avatar
Peter van der meulen committed
191
        Discord.send_message_to_admin("User is fully unauthed already")
192

Peter van der meulen's avatar
Peter van der meulen committed
193
      user ->
194
        update_user(user, %{access: 0})
195
        SnugglesApp.RabbitService.publish_json("account.events", %{"user_id" => user.id, "discord_id" => user.discord_id})
196
        Discord.send_message_to_admin("User <@#{user.discord_id}>: scheduled to update")
Peter van der meulen's avatar
Peter van der meulen committed
197 198 199
    end
  end

200 201 202 203 204 205 206 207 208 209 210
  @doc """
  Creates corporation

  ## Examples

      iex> handle_admin_request(user, attrs)
      {:ok}

  """
  def handle_create_corp(object) do
    user = Repo.get_by(User, discord_id: Integer.to_string(object["author_id"]))
211 212 213 214 215 216

    corp =
      case user do
        nil -> Repo.get_by(Corporation, discord_guild_id: Integer.to_string(object["guild_id"]))
        _ -> Repo.get_by(Corporation, eve_corp_id: user.corp_id)
      end
217

218
    cond do
Peter van der meulen's avatar
Peter van der meulen committed
219
      user == nil ->
220 221 222 223 224
        Discord.send_message(
          "Hey <@#{object["author_id"]}>, You need to auth yourself in the alliance first",
          object["channel_id"]
        )

225
      corp != nil ->
226 227 228 229 230
        Discord.send_message(
          "Hey <@#{object["author_id"]}>, It looks like your corp is already registered with a discord server",
          object["channel_id"]
        )

Peter van der meulen's avatar
Peter van der meulen committed
231
      object["role_id"] && true ->
232
        {:ok, _} = create_corp(user, object)
233 234 235 236 237 238

        Discord.send_message(
          "Hey #{user.corp_ticker}, \nYour corporation is now registered with its own auth. \nYour members can write `!auth-corp` in the alliance landing pad to receive rights on this server",
          object["channel_id"]
        )

239
      true ->
240 241 242 243
        Discord.send_message(
          "Hey #{user.corp_ticker}, \nIt looks like i can't read the member role you mentioned, make sure the setting \"All members can mention this role\" is enabled on the member role. If you still experience issues try clicking the autocomplete when mentioning the role. you can also only manage one member role",
          object["channel_id"]
        )
244 245 246
    end
  end

247
  defp create_corp(user, msg) do
248 249
    attrs = %{
      eve_corp_id: user.corp_id,
250 251
      discord_guild_id: Integer.to_string(msg["guild_id"]),
      discord_member_role_id: Integer.to_string(msg["role_id"])
252
    }
253

254 255 256 257
    %Corporation{}
    |> Corporation.changeset(attrs)
    |> Repo.insert()
  end
258

259 260
  def handle_user_corp_auth_request(msg) do
    user = Repo.get_by(User, discord_id: Integer.to_string(msg["author_id"]))
261 262 263 264
    corp = user && Repo.get_by(Corporation, eve_corp_id: user.corp_id)

    cond do
      user == nil ->
265 266 267 268 269 270 271 272
        {:ok, reply} =
          Discord.send_message(
            "Hey <@#{msg["author_id"]}>, it looks like you havent yet authorized yourself with the alliance. Click the link in the top of this chat to get started",
            msg["channel_id"]
          )

        spawn(fn -> remove_message(reply) end)

273
      corp == nil ->
274 275 276 277 278 279 280 281
        {:ok, reply} =
          Discord.send_message(
            "Hey <@#{msg["author_id"]}>, Your corp has no private discord or hasn't activated snuggles-auth on it. Talk to your corp leadersip if you have questions",
            msg["channel_id"]
          )

        spawn(fn -> remove_message(reply) end)

282
      true ->
283 284 285 286 287
        add_user_to_corp_discord(user, corp, msg)
    end
  end

  defp add_user_to_corp_discord(user, corp, msg) do
288 289 290 291 292 293 294
    {:ok, reply} =
      Discord.send_message(
        "Hey <@#{msg["author_id"]}>, Success! You should receive member rights on your corp discord soon!",
        msg["channel_id"]
      )

    spawn(fn -> remove_message(reply) end)
295
    {:ok, updated_user} = handle_user_update_request(%{"user_id" => user.id})
296

297
    if updated_user.access do
298 299 300 301 302 303
      Discord.add_corp_role_to_user(
        user.discord_id,
        corp.discord_guild_id,
        corp.discord_member_role_id
      )

Peter van der meulen's avatar
Peter van der meulen committed
304
      Discord.update_corp_user_name(updated_user, corp.discord_guild_id)
305 306 307 308 309 310 311 312
    end
  end

  defp remove_message(msg) do
    Process.sleep(4000)
    Discord.remove_message(msg)
    {:ok}
  end
313

314 315 316 317 318 319 320 321 322 323 324 325 326
  @doc """
  Updates a user.

  ## Examples

      iex> update_user(user, attrs)
      {:ok, %User{}}

      iex> updatee_user(user, attrs)
      {:error, %Ecto.Changeset{}}

  """
  def update_user(user, attrs) do
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a User.

  ## Examples

      iex> delete_user(user)
      {:ok, %User{}}

      iex> delete_user(user)
      {:error, %Ecto.Changeset{}}

  """
  def delete_user(%User{} = user) do
    Repo.delete(user)
  end

348 349 350 351 352
  def delete_user(id) when is_integer(id) do
    user = Repo.get(User, id)
    delete_user(user)
  end

353 354 355 356 357 358 359 360 361 362 363 364 365
  @doc """
  Returns an `%Ecto.Changeset{}` for tracking user changes.

  ## Examples

      iex> change_user(user)
      %Ecto.Changeset{source: %User{}}

  """
  def change_user(%User{} = user) do
    User.changeset(user, %{})
  end
end