diff --git a/lib/teiserver/coordinator/coordinator_commands.ex b/lib/teiserver/coordinator/coordinator_commands.ex index 980f7e4bf3..3f68ecdd17 100644 --- a/lib/teiserver/coordinator/coordinator_commands.ex +++ b/lib/teiserver/coordinator/coordinator_commands.ex @@ -20,7 +20,7 @@ defmodule Teiserver.Coordinator.CoordinatorCommands do @splitter "---------------------------" @always_allow ~w(help whoami whois discord coc mute unmute ignore unignore website party) # These commands are handled by coordinator commands, but are not on the always allow list - @mod_allow ~w(modparty unparty) + @mod_allow ~w(modparty unparty modme unmodme) @forward_to_consul ~w(s status players follow joinq leaveq splitlobby y yes n no explain) @admin_commands ~w(broadcast) @@ -524,6 +524,24 @@ defmodule Teiserver.Coordinator.CoordinatorCommands do state end + # Changes the client status to show the person is a moderator + defp do_handle(%{command: "modme", senderid: sender_id} = _cmd, state) do + client = Account.get_client_by_id(sender_id) + + if client.moderator do + Client.merge_update_client(sender_id, %{show_moderator: true}) + end + + state + end + + # Changes the client status to hide the person is a moderator + defp do_handle(%{command: "unmodme", senderid: sender_id} = _cmd, state) do + Client.merge_update_client(sender_id, %{show_moderator: false}) + + state + end + defp do_handle(%{command: "website", senderid: senderid} = _cmd, state) do client = Client.get_client_by_id(senderid) sender = Account.get_user(senderid) diff --git a/lib/teiserver/data/client.ex b/lib/teiserver/data/client.ex index 34ff409661..8881251463 100644 --- a/lib/teiserver/data/client.ex +++ b/lib/teiserver/data/client.ex @@ -23,6 +23,9 @@ defmodule Teiserver.Client do away: false, rank: 1, moderator: false, + + # Used to control showing/hiding the moderator-ness of the client + show_moderator: false, bot: false, # Battle stuff @@ -97,6 +100,7 @@ defmodule Teiserver.Client do tcp_pid: self(), rank: user.rank, moderator: Auth.moderator?(db_user) or Auth.is_event_organizer?(db_user), + show_moderator: false, bot: Auth.is_bot?(db_user), away: false, in_game: false, @@ -198,6 +202,9 @@ defmodule Teiserver.Client do T.client() def update(client, reason), do: ClientLib.replace_update_client(client, reason) + @spec merge_update_client(User.id(), map()) :: nil | :ok + defdelegate merge_update_client(user_id, updates), to: ClientLib + @spec get_client_pid(User.id()) :: pid() | nil defdelegate get_client_pid(userid), to: ClientLib diff --git a/lib/teiserver/protocols/spring/spring.ex b/lib/teiserver/protocols/spring/spring.ex index 129761e8a4..d01ce46cc5 100644 --- a/lib/teiserver/protocols/spring/spring.ex +++ b/lib/teiserver/protocols/spring/spring.ex @@ -19,17 +19,24 @@ defmodule Teiserver.Protocols.Spring do } end - @spec create_client_status(map()) :: Integer.t() - def create_client_status(client) do + @spec create_client_status(map(), boolean()) :: Integer.t() + def create_client_status(client, true_mod_status \\ false) do [r1, r2, r3] = BitParse.parse_bits("#{client.rank || 1}", 3) + moderator = + if true_mod_status do + client.moderator + else + Map.get(client, :show_moderator, client.moderator) + end + [ if(client.in_game, do: 1, else: 0), if(client.away, do: 1, else: 0), r3, r2, r1, - if(client.moderator, do: 1, else: 0), + if(moderator, do: 1, else: 0), if(client.bot, do: 1, else: 0) ] |> Enum.reverse() diff --git a/lib/teiserver/protocols/spring/spring_out.ex b/lib/teiserver/protocols/spring/spring_out.ex index bf9b5880cb..f92bb32634 100644 --- a/lib/teiserver/protocols/spring/spring_out.ex +++ b/lib/teiserver/protocols/spring/spring_out.ex @@ -354,11 +354,15 @@ defmodule Teiserver.Protocols.SpringOut do defp do_reply(:client_status, nil), do: "" - defp do_reply(:client_status, client) do + defp do_reply(:client_status, client) when is_map(client) do status = Spring.create_client_status(client) "CLIENTSTATUS #{client.name} #{status}\n" end + defp do_reply(:client_status, {name, status}) do + "CLIENTSTATUS #{name} #{status}\n" + end + defp do_reply(:client_battlestatus, nil), do: nil defp do_reply(:client_battlestatus, client) do diff --git a/lib/teiserver/tcp/spring/spring_tcp_server.ex b/lib/teiserver/tcp/spring/spring_tcp_server.ex index 6205b3557d..6ee2a3e9ad 100644 --- a/lib/teiserver/tcp/spring/spring_tcp_server.ex +++ b/lib/teiserver/tcp/spring/spring_tcp_server.ex @@ -11,6 +11,7 @@ defmodule Teiserver.SpringTcpServer do alias Teiserver.Coordinator alias Teiserver.Data.Types, as: T alias Teiserver.Helpers.BurstyRateLimiter + alias Teiserver.Protocols.Spring alias Teiserver.Protocols.Spring.PartyIn alias Teiserver.Protocols.SpringIn alias Teiserver.Protocols.SpringOut @@ -772,7 +773,11 @@ defmodule Teiserver.SpringTcpServer do new_knowns = Map.put(state.known_users, userid, new_user) %{state | known_users: new_knowns} else - SpringOut.reply(:client_status, new_client, nil, state) + # For bots we want to show them as a moderator regardless of what + # they are showing to others + true_mod_status = Auth.is_bot?(state.userid) + status = Spring.create_client_status(new_client, true_mod_status) + SpringOut.reply(:client_status, {new_client.name, status}, nil, state) end end diff --git a/test/teiserver/coordinator/coordinator_commands_sync_test.exs b/test/teiserver/coordinator/coordinator_commands_sync_test.exs index 5e4bee1c32..d1ebb2f2ad 100644 --- a/test/teiserver/coordinator/coordinator_commands_sync_test.exs +++ b/test/teiserver/coordinator/coordinator_commands_sync_test.exs @@ -1,5 +1,6 @@ defmodule Teiserver.Coordinator.CoordinatorCommandsSyncTest do alias Teiserver.Account + alias Teiserver.Account.Auth alias Teiserver.Account.UserLib alias Teiserver.CacheUser @@ -8,6 +9,9 @@ defmodule Teiserver.Coordinator.CoordinatorCommandsSyncTest do import TeiserverTestLib, only: [ auth_setup: 1, + auth_setup: 2, + new_user: 1, + _recv_until: 1, _send_raw: 2, _recv_raw: 1, start_spring_server: 1 @@ -50,5 +54,63 @@ defmodule Teiserver.Coordinator.CoordinatorCommandsSyncTest do Application.put_env(:teiserver, Teiserver, config) end + + test "modme - not moderator", %{socket: socket, user: user} do + client = Account.get_client_by_id(user.id) + refute client.moderator + refute client.show_moderator + + _send_raw(socket, "SAYPRIVATE coordinator $modme\n") + :timer.sleep(100) + + client = Account.get_client_by_id(user.id) + refute client.moderator + refute client.show_moderator + end + + test "modme/unmodme - as moderator", context do + user_watcher = new_user("user_watcher") + %{socket: user_watcher_socket} = auth_setup(context, user_watcher) + bot_watcher = new_user("bot_watcher") + %{socket: bot_watcher_socket} = auth_setup(context, bot_watcher) + {:ok, _} = Auth.add_roles(bot_watcher.id, ["Bot"]) + + std_user = new_user("std_watcher") + %{socket: std_socket} = auth_setup(context, std_user) + mod_user = new_user("mod_user") + %{socket: mod_socket} = auth_setup(context, mod_user) + {:ok, _} = Auth.add_roles(mod_user.id, ["Moderator"]) + + lines1 = _recv_until(user_watcher_socket) + lines2 = _recv_until(bot_watcher_socket) + + _recv_until(mod_socket) + + IO.puts "MODME" + _send_raw(std_socket, "SAYPRIVATE coordinator $modme\n") + _send_raw(mod_socket, "SAYPRIVATE coordinator $modme\n") + + lines3 = _recv_until(user_watcher_socket) + lines4 = _recv_until(bot_watcher_socket) + + lines5 = _recv_until(mod_socket) + + IO.puts "lines1" + IO.puts lines1 + IO.puts "" + IO.puts "lines2" + IO.puts lines2 + IO.puts "" + IO.puts "lines3" + IO.puts lines3 + IO.puts "" + IO.puts "lines4" + IO.puts lines4 + IO.puts "" + + IO.puts "" + IO.puts lines5 + IO.puts "" + end end end