Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit d906938

Browse files
David Robertsonclokep
andauthored
Correctly include room avatars in email notifications (#10658)
Judging by the template, this was intended ages ago, but we never actually passed an avatar URL to the template. So let's provide one. Closes #1546. Co-authored-by: Patrick Cloke <[email protected]>
1 parent f8bf83b commit d906938

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

changelog.d/10658.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug where room avatars were not included in email notifications.

synapse/push/mailer.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ async def _fetch_room_state(room_id: str) -> None:
258258
# actually sort our so-called rooms_in_order list, most recent room first
259259
rooms_in_order.sort(key=lambda r: -(notifs_by_room[r][-1]["received_ts"] or 0))
260260

261-
rooms = []
261+
rooms: List[Dict[str, Any]] = []
262262

263263
for r in rooms_in_order:
264264
roomvars = await self._get_room_vars(
@@ -362,6 +362,7 @@ async def _get_room_vars(
362362
"notifs": [],
363363
"invite": is_invite,
364364
"link": self._make_room_link(room_id),
365+
"avatar_url": await self._get_room_avatar(room_state_ids),
365366
}
366367

367368
if not is_invite:
@@ -393,6 +394,27 @@ async def _get_room_vars(
393394

394395
return room_vars
395396

397+
async def _get_room_avatar(
398+
self,
399+
room_state_ids: StateMap[str],
400+
) -> Optional[str]:
401+
"""
402+
Retrieve the avatar url for this room---if it exists.
403+
404+
Args:
405+
room_state_ids: The event IDs of the current room state.
406+
407+
Returns:
408+
room's avatar url if it's present and a string; otherwise None.
409+
"""
410+
event_id = room_state_ids.get((EventTypes.RoomAvatar, ""))
411+
if event_id:
412+
ev = await self.store.get_event(event_id)
413+
url = ev.content.get("url")
414+
if isinstance(url, str):
415+
return url
416+
return None
417+
396418
async def _get_notif_vars(
397419
self,
398420
notif: Dict[str, Any],

tests/push/test_email.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
14+
import email.message
1515
import os
16+
from typing import Dict, List, Sequence, Tuple
1617

1718
import attr
1819
import pkg_resources
@@ -70,9 +71,10 @@ def make_homeserver(self, reactor, clock):
7071
hs = self.setup_test_homeserver(config=config)
7172

7273
# List[Tuple[Deferred, args, kwargs]]
73-
self.email_attempts = []
74+
self.email_attempts: List[Tuple[Deferred, Sequence, Dict]] = []
7475

7576
def sendmail(*args, **kwargs):
77+
# This mocks out synapse.reactor.send_email._sendmail.
7678
d = Deferred()
7779
self.email_attempts.append((d, args, kwargs))
7880
return d
@@ -255,6 +257,39 @@ def test_multiple_rooms(self):
255257
# We should get emailed about those messages
256258
self._check_for_mail()
257259

260+
def test_room_notifications_include_avatar(self):
261+
# Create a room and set its avatar.
262+
room = self.helper.create_room_as(self.user_id, tok=self.access_token)
263+
self.helper.send_state(
264+
room, "m.room.avatar", {"url": "mxc://DUMMY_MEDIA_ID"}, self.access_token
265+
)
266+
267+
# Invite two other uses.
268+
for other in self.others:
269+
self.helper.invite(
270+
room=room, src=self.user_id, tok=self.access_token, targ=other.id
271+
)
272+
self.helper.join(room=room, user=other.id, tok=other.token)
273+
274+
# The other users send some messages.
275+
# TODO It seems that two messages are required to trigger an email?
276+
self.helper.send(room, body="Alpha", tok=self.others[0].token)
277+
self.helper.send(room, body="Beta", tok=self.others[1].token)
278+
279+
# We should get emailed about those messages
280+
args, kwargs = self._check_for_mail()
281+
282+
# That email should contain the room's avatar
283+
msg: bytes = args[5]
284+
# Multipart: plain text, base 64 encoded; html, base 64 encoded
285+
html = (
286+
email.message_from_bytes(msg)
287+
.get_payload()[1]
288+
.get_payload(decode=True)
289+
.decode()
290+
)
291+
self.assertIn("_matrix/media/v1/thumbnail/DUMMY_MEDIA_ID", html)
292+
258293
def test_empty_room(self):
259294
"""All users leaving a room shouldn't cause the pusher to break."""
260295
# Create a simple room with two users
@@ -344,9 +379,14 @@ def test_no_email_sent_after_removed(self):
344379
pushers = list(pushers)
345380
self.assertEqual(len(pushers), 0)
346381

347-
def _check_for_mail(self):
348-
"""Check that the user receives an email notification"""
382+
def _check_for_mail(self) -> Tuple[Sequence, Dict]:
383+
"""
384+
Assert that synapse sent off exactly one email notification.
349385
386+
Returns:
387+
args and kwargs passed to synapse.reactor.send_email._sendmail for
388+
that notification.
389+
"""
350390
# Get the stream ordering before it gets sent
351391
pushers = self.get_success(
352392
self.hs.get_datastore().get_pushers_by({"user_name": self.user_id})
@@ -369,8 +409,9 @@ def _check_for_mail(self):
369409
# One email was attempted to be sent
370410
self.assertEqual(len(self.email_attempts), 1)
371411

412+
deferred, sendmail_args, sendmail_kwargs = self.email_attempts[0]
372413
# Make the email succeed
373-
self.email_attempts[0][0].callback(True)
414+
deferred.callback(True)
374415
self.pump()
375416

376417
# One email was attempted to be sent
@@ -386,3 +427,4 @@ def _check_for_mail(self):
386427

387428
# Reset the attempts.
388429
self.email_attempts = []
430+
return sendmail_args, sendmail_kwargs

0 commit comments

Comments
 (0)