Skip to content

Commit cccc186

Browse files
committed
Implement IRCv3 draft/message-redaction (Matrix -> IRC only)
1 parent 90f954f commit cccc186

File tree

5 files changed

+140
-29
lines changed

5 files changed

+140
-29
lines changed

lib/irc/command.ex

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##
2-
# Copyright (C) 2021-2022 Valentin Lorentz
2+
# Copyright (C) 2021-2023 Valentin Lorentz
33
#
44
# This program is free software: you can redistribute it and/or modify
55
# it under the terms of the GNU Affero General Public License version 3,
@@ -251,6 +251,8 @@ defmodule M51.Irc.Command do
251251
252252
"""
253253
def downgrade(command, capabilities) do
254+
original_tags = command.tags
255+
254256
# downgrade echo-message
255257
command =
256258
if Enum.member?(capabilities, :echo_message) do
@@ -319,6 +321,46 @@ defmodule M51.Irc.Command do
319321
nil
320322
end
321323

324+
%{command: "REDACT"} ->
325+
if Enum.member?(capabilities, :message_redaction) do
326+
command
327+
else
328+
sender = Map.get(original_tags, "account")
329+
330+
display_name =
331+
case Map.get(original_tags, "+draft/display-name", nil) do
332+
dn when is_binary(dn) -> " (#{dn})"
333+
_ -> ""
334+
end
335+
336+
tags = Map.drop(command.tags, ["+draft/display-name", "account"])
337+
338+
command = case command do
339+
%{params: [channel, msgid, reason]} ->
340+
%M51.Irc.Command{
341+
tags: Map.put(tags, "+draft/reply", msgid),
342+
source: "server.",
343+
command: "NOTICE",
344+
params: [channel, "#{sender}#{display_name} deleted an event: #{reason}"]
345+
}
346+
347+
%{params: [channel, msgid]} ->
348+
%M51.Irc.Command{
349+
tags: Map.put(tags, "+draft/reply", msgid),
350+
source: "server.",
351+
command: "NOTICE",
352+
params: [channel, "#{sender}#{display_name} deleted an event"]
353+
}
354+
355+
_ ->
356+
# shouldn't happen
357+
nil
358+
end
359+
360+
# run downgrade() recursively in order to drop the new tags if necessary
361+
downgrade(command, capabilities)
362+
end
363+
322364
%{command: "TAGMSG"} ->
323365
if Enum.member?(capabilities, :message_tags) do
324366
command

lib/irc/handler.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ defmodule M51.IrcConn.Handler do
7272
# https://ircv3.net/specs/extensions/multiline
7373
"draft/multiline" => {:multiline, "max-bytes=#{@multiline_max_bytes}"},
7474

75+
# https://github.com/progval/ircv3-specifications/blob/redaction/extensions/message-redaction.md
76+
"draft/message-redaction" => {:message_redaction, nil},
77+
7578
# https://ircv3.net/specs/extensions/sasl-3.1
7679
"sasl" => {:sasl, "PLAIN"},
7780

lib/matrix_client/poller.ex

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##
2-
# Copyright (C) 2021-2022 Valentin Lorentz
2+
# Copyright (C) 2021-2023 Valentin Lorentz
33
#
44
# This program is free software: you can redistribute it and/or modify
55
# it under the terms of the GNU Affero General Public License version 3,
@@ -884,38 +884,39 @@ defmodule M51.MatrixClient.Poller do
884884
member = M51.MatrixClient.State.room_member(state, room_id, sender)
885885
send = make_send_function(sup_pid, event, write)
886886

887-
reason =
888-
case event["content"] do
889-
%{"reason" => reason} when is_binary(reason) -> ": #{reason}"
890-
_ -> ""
891-
end
887+
tags = %{"account" => sender}
892888

893889
# TODO: dedup this with m.reaction handler
894-
display_name =
890+
tags =
895891
case member do
896892
%M51.Matrix.RoomMember{display_name: display_name} when display_name != nil ->
897-
" (#{display_name})"
893+
Map.put(tags, "+draft/display-name", display_name)
898894

899895
_ ->
900-
""
896+
tags
901897
end
902898

903-
tags =
904-
case event do
905-
%{"redacts" => redacts_id}
906-
when is_binary(redacts_id) ->
907-
%{"+draft/reply" => redacts_id}
908-
909-
_ ->
910-
%{}
911-
end
899+
case event do
900+
%{"redacts" => redacts_id} when is_binary(redacts_id) ->
901+
case event["content"] do
902+
%{"reason" => reason} when is_binary(reason) ->
903+
send.(%M51.Irc.Command{
904+
tags: tags,
905+
source: nick2nuh(sender),
906+
command: "REDACT",
907+
params: [channel, redacts_id, reason]
908+
})
912909

913-
send.(%M51.Irc.Command{
914-
tags: tags,
915-
source: "server.",
916-
command: "NOTICE",
917-
params: [channel, "#{sender}#{display_name} deleted an event#{reason}"]
918-
})
910+
_ ->
911+
send.(%M51.Irc.Command{
912+
tags: tags,
913+
source: nick2nuh(sender),
914+
command: "REDACT",
915+
params: [channel, redacts_id]
916+
})
917+
end
918+
_ -> nil
919+
end
919920
end
920921

921922
def handle_event(

test/irc/handler_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ defmodule M51.IrcConn.HandlerTest do
1818
use ExUnit.Case, async: false
1919
doctest M51.IrcConn.Handler
2020

21-
@cap_ls_302 ":server. CAP * LS :account-tag batch draft/account-registration=before-connect draft/channel-rename draft/chathistory draft/multiline=max-bytes=8192 draft/sasl-ir echo-message extended-join labeled-response message-tags sasl=PLAIN server-time soju.im/account-required standard-replies userhost-in-names\r\n"
22-
@cap_ls ":server. CAP * LS :account-tag batch draft/account-registration draft/channel-rename draft/chathistory draft/multiline draft/sasl-ir echo-message extended-join labeled-response message-tags sasl server-time soju.im/account-required standard-replies userhost-in-names\r\n"
21+
@cap_ls_302 ":server. CAP * LS :account-tag batch draft/account-registration=before-connect draft/channel-rename draft/chathistory draft/message-redaction draft/multiline=max-bytes=8192 draft/sasl-ir echo-message extended-join labeled-response message-tags sasl=PLAIN server-time soju.im/account-required standard-replies userhost-in-names\r\n"
22+
@cap_ls ":server. CAP * LS :account-tag batch draft/account-registration draft/channel-rename draft/chathistory draft/message-redaction draft/multiline draft/sasl-ir echo-message extended-join labeled-response message-tags sasl server-time soju.im/account-required standard-replies userhost-in-names\r\n"
2323
@isupport "CASEMAPPING=rfc3454 CLIENTTAGDENY=*,-draft/react,-draft/reply CHANLIMIT= CHANTYPES=#! CHATHISTORY=100 MAXTARGETS=1 MSGREFTYPES=msgid PREFIX= TARGMAX=JOIN:1,PART:1 UTF8ONLY :are supported by this server\r\n"
2424

2525
setup do

test/matrix_client/poller_test.exs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##
2-
# Copyright (C) 2021-2022 Valentin Lorentz
2+
# Copyright (C) 2021-2023 Valentin Lorentz
33
#
44
# This program is free software: you can redistribute it and/or modify
55
# it under the terms of the GNU Affero General Public License version 3,
@@ -2288,7 +2288,7 @@ defmodule M51.MatrixClient.PollerTest do
22882288
})
22892289

22902290
assert_line(":nick:[email protected] PRIVMSG #test:example.org :first message\r\n")
2291-
# m.room.redaction not implemented yet, so it's just ignored
2291+
# We'll probably see the m.room.redaction message later, so we can simply ignore this one.
22922292
assert_line(
22932293
":nick:[email protected] PRIVMSG #test:example.org :second message\r\n"
22942294
)
@@ -2297,6 +2297,71 @@ defmodule M51.MatrixClient.PollerTest do
22972297
end
22982298

22992299
test "message redaction" do
2300+
M51.IrcConn.State.add_capabilities(:process_ircconn_state, [
2301+
:multiline,
2302+
:message_tags,
2303+
:message_redaction
2304+
])
2305+
2306+
joined_room()
2307+
2308+
timeline_events = [
2309+
%{
2310+
"content" => %{"body" => "first message", "msgtype" => "m.text"},
2311+
"event_id" => "$event1",
2312+
"origin_server_ts" => 1_632_946_233_579,
2313+
"sender" => "@nick:example.org",
2314+
"type" => "m.room.message",
2315+
"unsigned" => %{}
2316+
},
2317+
%{
2318+
"content" => %{},
2319+
"event_id" => "$event2",
2320+
"redacts" => "$event1",
2321+
"origin_server_ts" => 1_633_808_172_505,
2322+
"sender" => "@admin:example.org",
2323+
"type" => "m.room.redaction",
2324+
"unsigned" => %{}
2325+
},
2326+
%{
2327+
"content" => %{
2328+
"reason" => "Redacting again!"
2329+
},
2330+
"event_id" => "$event3",
2331+
"redacts" => "$event1",
2332+
"origin_server_ts" => 1_633_808_172_505,
2333+
"sender" => "@admin:example.org",
2334+
"type" => "m.room.redaction",
2335+
"unsigned" => %{}
2336+
}
2337+
]
2338+
2339+
M51.MatrixClient.Poller.handle_events(self(), false, %{
2340+
"rooms" => %{
2341+
"join" => %{
2342+
"!testid:example.org" => %{
2343+
"timeline" => %{"events" => timeline_events}
2344+
}
2345+
}
2346+
}
2347+
})
2348+
2349+
assert_line(
2350+
"@msgid=$event1 :nick:[email protected] PRIVMSG #test:example.org :first message\r\n"
2351+
)
2352+
2353+
assert_line(
2354+
"@msgid=$event2 :admin:[email protected] REDACT #test:example.org :$event1\r\n"
2355+
)
2356+
2357+
assert_line(
2358+
"@msgid=$event3 :admin:[email protected] REDACT #test:example.org $event1 :Redacting again!\r\n"
2359+
)
2360+
2361+
assert_last_line()
2362+
end
2363+
2364+
test "message redaction fallback" do
23002365
M51.IrcConn.State.add_capabilities(:process_ircconn_state, [
23012366
:multiline,
23022367
:message_tags

0 commit comments

Comments
 (0)