From 4a3cecc5a1ba1cc1d823ab6f2889b4e8efa9f2e4 Mon Sep 17 00:00:00 2001 From: Rebecca Kelly Date: Thu, 15 Feb 2024 15:08:22 -0500 Subject: [PATCH 1/2] Keep up to date with the user's current display name Previous behaviour was to use whatever the display name was on first connect and drop any updates, which resulted in skew in displayed messages between M51 and other clients. --- lib/matrix_client/state.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matrix_client/state.ex b/lib/matrix_client/state.ex index 58d8334..8cb2272 100644 --- a/lib/matrix_client/state.ex +++ b/lib/matrix_client/state.ex @@ -95,7 +95,8 @@ defmodule M51.MatrixClient.State do room = Map.get(state.rooms, room_id, @emptyroom) if Map.has_key?(room.members, userid) do - {true, state} + # User may have changed their display-name, so update the member list + {true, update_in(state.rooms[room_id].members[userid], fn _ -> member end)} else room = %{room | members: Map.put(room.members, userid, member)} {false, %{state | rooms: Map.put(state.rooms, room_id, room)}} From c0c4a3d240d20621bea244c04c2afd3bce7dc614 Mon Sep 17 00:00:00 2001 From: Rebecca Kelly Date: Thu, 15 Feb 2024 15:08:43 -0500 Subject: [PATCH 2/2] Send display name in gecos field for WHO and WHOIS --- lib/irc/handler.ex | 16 +++++++--------- lib/matrix_client/state.ex | 34 ++++++++++++++++++++++++---------- test/irc/handler_test.exs | 6 +++--- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/lib/irc/handler.ex b/lib/irc/handler.ex index d1aadbc..16aac76 100644 --- a/lib/irc/handler.ex +++ b/lib/irc/handler.ex @@ -1072,10 +1072,9 @@ defmodule M51.IrcConn.Handler do fn _room_id, room -> commands = room.members - |> Stream.map(fn {user_id, _member} -> + |> Stream.map(fn {user_id, member} -> [local_name, hostname] = String.split(user_id, ":", parts: 2) - # TODO: pick the most common display name of the user instead - gecos = user_id + gecos = member.display_name || user_id # RPL_WHOREPLY make_numeric.("352", [ target, @@ -1097,9 +1096,9 @@ defmodule M51.IrcConn.Handler do else # target is a nick [local_name, hostname] = String.split(target, ":", parts: 2) + display_name = M51.MatrixClient.State.user_display_name(matrix_state, target) - # TODO: pick the most common display name instead - gecos = target + gecos = display_name send_batch.( [ @@ -1133,10 +1132,10 @@ defmodule M51.IrcConn.Handler do end [local_name, hostname] -> - [member: memberships] = M51.MatrixClient.State.user(matrix_state, target) + memberships = M51.MatrixClient.State.user_memberships(matrix_state, target) + display_name = M51.MatrixClient.State.user_display_name(matrix_state, target) - # TODO: pick the most common display name instead - gecos = target + gecos = display_name overhead = make_numeric.("353", [target, ""]) |> M51.Irc.Command.format() |> byte_size() @@ -1148,7 +1147,6 @@ defmodule M51.IrcConn.Handler do channel_commands = memberships - |> Map.keys() |> Enum.map(fn room_id -> M51.MatrixClient.State.room_irc_channel(matrix_state, room_id) end) diff --git a/lib/matrix_client/state.ex b/lib/matrix_client/state.ex index 8cb2272..4a35cce 100644 --- a/lib/matrix_client/state.ex +++ b/lib/matrix_client/state.ex @@ -121,18 +121,32 @@ defmodule M51.MatrixClient.State do end @doc """ - Returns [member: %{room_id => %M51.Matrix.RoomMember{...}}] + Returns the user's current display name. This is the same across all rooms + they're in, but not guaranteed to be unique vs. other users. + + If the user has no known display name (i.e. membership set is empty), just + returns the userid. + """ + def user_display_name(pid, user_id) do + Agent.get(pid, fn state -> + state.rooms + |> Stream.filter(fn {_room_id, room} -> Map.has_key?(room.members, user_id) end) + |> Stream.map(fn {_room_id, room} -> room.members[user_id].display_name end) + # Matrix display names are per-user, so we just pick an arbitrary channel + # that they're in and fish it out of the userlist. + # TODO: store a user_id to user info map separately from the channels? + |> Enum.at(0, user_id) + end) + end + + @doc """ + Returns a list of room_ids that the user is a member in. """ - def user(pid, user_id) do + def user_memberships(pid, user_id) do Agent.get(pid, fn state -> - [ - member: - state.rooms - |> Map.to_list() - |> Enum.map(fn {room_id, room} -> {room_id, Map.get(room.members, user_id)} end) - |> Enum.filter(fn {_room_id, member} -> member != nil end) - |> Map.new() - ] + state.rooms + |> Enum.filter(fn {_room_id, room} -> Map.has_key?(room.members, user_id) end) + |> Enum.map(fn {room_id, _room} -> room_id end) end) end diff --git a/test/irc/handler_test.exs b/test/irc/handler_test.exs index 4c33d21..9dbd041 100644 --- a/test/irc/handler_test.exs +++ b/test/irc/handler_test.exs @@ -762,7 +762,7 @@ defmodule M51.IrcConn.HandlerTest do assert line == "@label=l2 BATCH +#{batch_id} :labeled-response\r\n" assert_line( - "@batch=#{batch_id} :server. 352 foo:example.org #existing_room:example.org user1 example.org * user1:example.org H :0 user1:example.org\r\n" + "@batch=#{batch_id} :server. 352 foo:example.org #existing_room:example.org user1 example.org * user1:example.org H :0 user one\r\n" ) assert_line( @@ -788,7 +788,7 @@ defmodule M51.IrcConn.HandlerTest do send(handler, cmd("WHO #existing_room:example.org")) assert_line( - ":server. 352 foo:example.org #existing_room:example.org user1 example.org * user1:example.org H :0 user1:example.org\r\n" + ":server. 352 foo:example.org #existing_room:example.org user1 example.org * user1:example.org H :0 user one\r\n" ) assert_line( @@ -826,7 +826,7 @@ defmodule M51.IrcConn.HandlerTest do assert line == "@label=l1 BATCH +#{batch_id} :labeled-response\r\n" assert_line( - "@batch=#{batch_id} :server. 311 foo:example.org user1:example.org user1 example.org * :user1:example.org\r\n" + "@batch=#{batch_id} :server. 311 foo:example.org user1:example.org user1 example.org * :user one\r\n" ) assert_line(