Coverage for src/couchers/notifications/render.py: 84%
181 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-10-04 23:02 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-10-04 23:02 +0000
1import logging
2from dataclasses import dataclass
4from couchers import urls
5from couchers.notifications.unsubscribe import generate_unsub_topic_action
6from couchers.templates.v2 import v2avatar, v2date, v2esc, v2phone, v2timestamp
8logger = logging.getLogger(__name__)
11@dataclass(kw_only=True)
12class RenderedNotification:
13 # whether the notification is critical and cannot be turned off
14 is_critical: bool = False
15 # email subject
16 email_subject: str
17 # shows up when listing emails in many clients
18 email_preview: str
19 # corresponds to .mjml + .txt file in templates/v2
20 email_template_name: str
21 # other template args
22 email_template_args: dict
23 # the link label on the topic_action unsubscribe link
24 email_topic_action_unsubscribe_text: str = None
25 # the link label on the topic_key unsubscribe link
26 email_topic_key_unsubscribe_text: str = None
27 # url to unsubscribe with one click
28 email_list_unsubscribe_url: str = None
29 # push notification title
30 push_title: str
31 # push notification content
32 push_body: str
33 # url to an icon for push notifications
34 push_icon: str
35 # url to where clicking on the notification should take you
36 push_url: str
39def render_notification(user, notification) -> RenderedNotification:
40 data = notification.topic_action.data_type.FromString(notification.data)
41 if notification.topic == "host_request":
42 view_link = urls.host_request(host_request_id=data.host_request.host_request_id)
43 if notification.action == "missed_messages":
44 their_your = "their" if data.am_host else "your"
45 other = data.user
46 # "declined your host request", or similar
47 message = f"{other.name} sent you message(s) in {their_your} host request"
48 return RenderedNotification(
49 email_subject=message,
50 email_preview=message,
51 email_template_name="host_request__plain",
52 email_template_args={
53 "view_link": view_link,
54 "host_request": data.host_request,
55 "message": message,
56 "other": other,
57 },
58 email_topic_action_unsubscribe_text="missed messages in host requests",
59 push_title=message,
60 push_body="Check the app for more info.",
61 push_icon=v2avatar(other),
62 push_url=view_link,
63 )
64 elif notification.action in ["create", "message"]:
65 if notification.action == "create":
66 other = data.surfer
67 message = f"{other.name} sent you a host request"
68 topic_action_unsub_text = "new host requests"
69 elif notification.action == "message":
70 other = data.user
71 if data.am_host:
72 message = f"{other.name} sent you a message in their host request"
73 else:
74 message = f"{other.name} sent you a message in your host request"
75 topic_action_unsub_text = "messages in host request"
76 return RenderedNotification(
77 email_subject=message,
78 email_preview=message,
79 email_template_name="host_request__message",
80 email_template_args={
81 "view_link": view_link,
82 "host_request": data.host_request,
83 "message": message,
84 "other": other,
85 "text": data.text,
86 },
87 email_topic_action_unsubscribe_text=topic_action_unsub_text,
88 push_title=f"{message}",
89 push_body=f"Dates: {v2date(data.host_request.from_date, user)} to {v2date(data.host_request.to_date, user)}.\n\n{data.text}",
90 push_icon=v2avatar(other),
91 push_url=view_link,
92 )
93 elif notification.action in ["accept", "reject", "confirm", "cancel"]:
94 if notification.action in ["accept", "reject"]:
95 other = data.host
96 their_your = "your"
97 else:
98 other = data.surfer
99 their_your = "their"
100 actioned = {
101 "accept": "accepted",
102 "reject": "declined",
103 "confirm": "confirmed",
104 "cancel": "cancelled",
105 }[notification.action]
106 # "declined your host request", or similar
107 message = f"{other.name} {actioned} {their_your} host request"
108 return RenderedNotification(
109 email_subject=message,
110 email_preview=message,
111 email_template_name="host_request__plain",
112 email_template_args={
113 "view_link": view_link,
114 "host_request": data.host_request,
115 "message": message,
116 "other": other,
117 },
118 email_topic_action_unsubscribe_text=f"{actioned} host requests",
119 push_title=message,
120 push_body="Check the app for more info.",
121 push_icon=v2avatar(other),
122 push_url=view_link,
123 )
124 elif notification.topic_action.display == "password:change":
125 title = "Your password was changed"
126 message = "Your login password for Couchers.org was changed."
127 return RenderedNotification(
128 is_critical=True,
129 email_subject=title,
130 email_preview=message,
131 email_template_name="security",
132 email_template_args={
133 "title": title,
134 "message": message,
135 },
136 push_title=title,
137 push_body=message,
138 push_icon=urls.icon_url(),
139 push_url=urls.account_settings_link(),
140 )
141 elif notification.topic_action.display == "password_reset:start":
142 message = "Someone initiated a password change on your account."
143 return RenderedNotification(
144 is_critical=True,
145 email_subject="Reset your Couchers.org password",
146 email_preview=message,
147 email_template_name="password_reset",
148 email_template_args={
149 "password_reset_link": urls.password_reset_link(password_reset_token=data.password_reset_token)
150 },
151 push_title="A password reset was initiated on your account",
152 push_body=message,
153 push_icon=urls.icon_url(),
154 push_url=urls.account_settings_link(),
155 )
156 elif notification.topic_action.display == "password_reset:complete":
157 title = "Your password was successfully reset"
158 message = "Your password on Couchers.org was changed. If that was you, then no further action is needed."
159 return RenderedNotification(
160 is_critical=True,
161 email_subject=title,
162 email_preview=title,
163 email_template_name="security",
164 email_template_args={
165 "title": title,
166 "message": message,
167 },
168 push_title=title,
169 push_body=message,
170 push_icon=urls.icon_url(),
171 push_url=urls.account_settings_link(),
172 )
173 elif notification.topic_action.display == "email_address:change":
174 title = "An email change was initiated on your account"
175 message = f"An email change to the email <b>{data.new_email}</b> was initiated on your account."
176 message_plain = f"An email change to the email {data.new_email} was initiated on your account."
177 return RenderedNotification(
178 is_critical=True,
179 email_subject=title,
180 email_preview=title,
181 email_template_name="security",
182 email_template_args={
183 "title": title,
184 "message": message,
185 },
186 push_title=title,
187 push_body=message_plain,
188 push_icon=urls.icon_url(),
189 push_url=urls.account_settings_link(),
190 )
191 elif notification.topic_action.display == "email_address:verify":
192 title = "Email change completed"
193 message = "Your new email address has been verified."
194 return RenderedNotification(
195 is_critical=True,
196 email_subject=title,
197 email_preview=message,
198 email_template_name="security",
199 email_template_args={
200 "title": title,
201 "message": message,
202 },
203 push_title=title,
204 push_body=message,
205 push_icon=urls.icon_url(),
206 push_url=urls.account_settings_link(),
207 )
208 elif notification.topic_action.display == "phone_number:change":
209 title = "Phone verification started"
210 message = f"You started phone number verification with the number <b>{v2phone(data.phone)}</b>."
211 message_plain = f"You started phone number verification with the number {v2phone(data.phone)}."
212 return RenderedNotification(
213 is_critical=True,
214 email_subject=title,
215 email_preview=message,
216 email_template_name="security",
217 email_template_args={
218 "title": title,
219 "message": message,
220 },
221 push_title=title,
222 push_body=message_plain,
223 push_icon=urls.icon_url(),
224 push_url=urls.feature_preview_link(),
225 )
226 elif notification.topic_action.display == "phone_number:verify":
227 title = "Phone successfully verified"
228 message = f"Your phone was successfully verified as <b>{v2phone(data.phone)}</b> on Couchers.org."
229 message_plain = f"Your phone was successfully verified as {v2phone(data.phone)} on Couchers.org."
230 return RenderedNotification(
231 is_critical=True,
232 email_subject=title,
233 email_preview=message_plain,
234 email_template_name="security",
235 email_template_args={
236 "title": title,
237 "message": message,
238 },
239 push_title=title,
240 push_body=message_plain,
241 push_icon=urls.icon_url(),
242 push_url=urls.feature_preview_link(),
243 )
244 elif notification.topic_action.display == "gender:change":
245 title = "Your gender was changed"
246 message = f"Your gender on Couchers.org was changed to <b>{data.gender}</b> by an admin."
247 message_plain = f"Your gender on Couchers.org was changed to {data.gender} by an admin."
248 return RenderedNotification(
249 is_critical=True,
250 email_subject=title,
251 email_preview=message_plain,
252 email_template_name="security",
253 email_template_args={
254 "title": title,
255 "message": message,
256 },
257 push_title=title,
258 push_body=message_plain,
259 push_icon=urls.icon_url(),
260 push_url=urls.account_settings_link(),
261 )
262 elif notification.topic_action.display == "birthdate:change":
263 title = "Your date of birth was changed"
264 message = (
265 f"Your date of birth on Couchers.org was changed to <b>{v2date(data.birthdate, user)}</b> by an admin."
266 )
267 message_plain = f"Your date of birth on Couchers.org was changed to {v2date(data.birthdate, user)} by an admin."
268 return RenderedNotification(
269 is_critical=True,
270 email_subject=title,
271 email_preview=message_plain,
272 email_template_name="security",
273 email_template_args={
274 "title": title,
275 "message": message,
276 },
277 push_title=title,
278 push_body=message_plain,
279 push_icon=urls.icon_url(),
280 push_url=urls.account_settings_link(),
281 )
282 elif notification.topic_action.display == "api_key:create":
283 return RenderedNotification(
284 is_critical=True,
285 email_subject="Your API key for Couchers.org",
286 email_preview="We have issued you an API key as per your request.",
287 email_template_name="api_key",
288 email_template_args={
289 "api_key": data.api_key,
290 "expiry": data.expiry,
291 },
292 push_title="An API key was created for your account",
293 push_body="Details were sent to you via email.",
294 push_icon=urls.icon_url(),
295 push_url=urls.app_link(),
296 )
297 elif notification.topic_action.display in ["badge:add", "badge:remove"]:
298 actioned = "added to" if notification.action == "add" else "removed from"
299 title = f"The {data.badge_name} badge was {actioned} your profile"
300 return RenderedNotification(
301 email_subject=title,
302 email_preview=title,
303 email_template_name="badge",
304 email_template_args={
305 "badge_name": data.badge_name,
306 "actioned": actioned,
307 "unsub_type": "badge additions" if notification.action == "add" else "badge removals",
308 },
309 email_topic_action_unsubscribe_text="badge additions" if notification.action == "add" else "badge removals",
310 push_title=title,
311 push_body=(
312 "Check out your profile to see the new badge!"
313 if notification.action == "add"
314 else "You can see all your badges on your profile."
315 ),
316 push_icon=urls.icon_url(),
317 push_url=urls.profile_link(),
318 email_list_unsubscribe_url=generate_unsub_topic_action(notification),
319 )
320 elif notification.topic_action.display == "donation:received":
321 title = "Thank you for your donation to Couchers.org!"
322 message = f"Thank you so much for your donation of ${data.amount} to Couchers.org."
323 return RenderedNotification(
324 is_critical=True,
325 email_subject=title,
326 email_preview=message,
327 email_template_name="donation_received",
328 email_template_args={
329 "amount": data.amount,
330 "receipt_url": data.receipt_url,
331 },
332 push_title=title,
333 push_body=message,
334 push_icon=urls.icon_url(),
335 push_url=data.receipt_url,
336 )
337 elif notification.topic_action.display == "friend_request:create":
338 other = data.other_user
339 preview = f"You've received a friend request from {other.name}"
340 return RenderedNotification(
341 email_subject=f"{other.name} wants to be your friend on Couchers.org!",
342 email_preview=preview,
343 email_template_name="friend_request",
344 email_template_args={
345 "friend_requests_link": urls.friend_requests_link(),
346 "other": other,
347 },
348 email_topic_action_unsubscribe_text="new friend requests",
349 push_title=f"{other.name} wants to be your friend",
350 push_body=preview,
351 push_icon=v2avatar(other),
352 push_url=urls.friend_requests_link(),
353 )
354 elif notification.topic_action.display == "friend_request:accept":
355 other = data.other_user
356 title = f"{other.name} accepted your friend request!"
357 preview = f"{v2esc(other.name)} has accepted your friend request"
358 return RenderedNotification(
359 email_subject=title,
360 email_preview=preview,
361 email_template_name="friend_request_accepted",
362 email_template_args={
363 "other_user_link": urls.user_link(username=other.username),
364 "other": other,
365 },
366 email_topic_action_unsubscribe_text="accepted friend requests",
367 push_title=title,
368 push_body=preview,
369 push_icon=v2avatar(other),
370 push_url=urls.user_link(username=other.username),
371 )
372 elif notification.topic_action.display == "account_deletion:start":
373 return RenderedNotification(
374 is_critical=True,
375 email_subject="Confirm your Couchers.org account deletion",
376 email_preview="Please confirm that you want to delete your Couchers.org account.",
377 email_template_name="account_deletion_start",
378 email_template_args={
379 "deletion_link": urls.delete_account_link(account_deletion_token=data.deletion_token),
380 },
381 push_title="Account deletion initiated",
382 push_body="Someone initiated the deletion of your Couchers.org account. To delete your account, please follow the link in the email we sent you.",
383 push_icon=urls.icon_url(),
384 push_url=urls.app_link(),
385 )
386 elif notification.topic_action.display == "account_deletion:complete":
387 title = "Your Couchers.org account has been deleted"
388 return RenderedNotification(
389 is_critical=True,
390 email_subject=title,
391 email_preview="We have deleted your Couchers.org account, to undo, follow the link in this email.",
392 email_template_name="account_deletion_complete",
393 email_template_args={
394 "undelete_link": urls.recover_account_link(account_undelete_token=data.undelete_token),
395 "days": data.undelete_days,
396 },
397 push_title=title,
398 push_body=f"You can still undo this by following the link we emailed to you within {data.undelete_days} days.",
399 push_icon=urls.icon_url(),
400 push_url=urls.app_link(),
401 )
402 elif notification.topic_action.display == "account_deletion:recovered":
403 title = "Your Couchers.org account has been recovered!"
404 subtitle = "We have recovered your Couchers.org account as per your request! Welcome back!"
405 return RenderedNotification(
406 is_critical=True,
407 email_subject=title,
408 email_preview=subtitle,
409 email_template_name="account_deletion_recovered",
410 email_template_args={
411 "app_link": urls.app_link(),
412 },
413 push_title=title,
414 push_body=subtitle,
415 push_icon=urls.icon_url(),
416 push_url=urls.app_link(),
417 )
418 elif notification.topic_action.display == "chat:message":
419 return RenderedNotification(
420 email_subject=data.message,
421 email_preview="You received a message on Couchers.org!",
422 email_template_name="chat_message",
423 email_template_args={
424 "author": data.author,
425 "message": data.message,
426 "text": data.text,
427 "view_link": urls.chat_link(chat_id=data.group_chat_id),
428 },
429 email_topic_action_unsubscribe_text="new chat messages",
430 email_topic_key_unsubscribe_text="this chat (mute)",
431 push_title=data.message,
432 push_body=data.text,
433 push_icon=v2avatar(data.author),
434 push_url=urls.chat_link(chat_id=data.group_chat_id),
435 )
436 elif notification.topic_action.display == "chat:missed_messages":
437 return RenderedNotification(
438 email_subject="You have unseen messages on Couchers.org!",
439 email_preview="You missed some messages on the platform.",
440 email_template_name="chat_unseen_messages",
441 email_template_args={
442 "items": [
443 {
444 "author": item.author,
445 "message": item.message,
446 "text": item.text,
447 "view_link": urls.chat_link(chat_id=item.group_chat_id),
448 }
449 for item in data.messages
450 ]
451 },
452 email_topic_action_unsubscribe_text="unseen chat messages",
453 push_title="You have unseen messages on Couchers.org",
454 push_body="Please check out any messages you missed.",
455 push_icon=urls.icon_url(),
456 push_url=urls.messages_link(),
457 )
458 elif notification.topic == "event":
459 event = data.event
460 time_display = f"{v2timestamp(event.start_time, user)} - {v2timestamp(event.end_time, user)}"
461 event_link = urls.event_link(occurrence_id=event.event_id, slug=event.slug)
462 if notification.action in ["create_approved", "create_any"]:
463 body = f"{time_display}\n"
464 body += f"Invited by {data.inviting_user.name}\n\n"
465 body += event.content
466 community_link = (
467 urls.community_link(node_id=data.in_community.community_id, slug=data.in_community.slug)
468 if data.in_community
469 else None
470 )
471 return RenderedNotification(
472 email_subject=f'{data.inviting_user.name} invited you to "{event.title}"',
473 email_preview="You've been invited to a new event on Couchers.org!",
474 email_template_name="event_create",
475 email_template_args={
476 "inviting_user": data.inviting_user,
477 "time_display": time_display,
478 "nearby": "nearby" if data.nearby else None,
479 "community": data.in_community if data.in_community else None,
480 "community_link": community_link,
481 "nearby_or_community_text_plain": (
482 "nearby" if data.nearby else f"in the {data.in_community.name} community"
483 ),
484 "event": event,
485 "view_link": event_link,
486 },
487 email_topic_action_unsubscribe_text=(
488 "new events by community members"
489 if notification.action == "create_any"
490 else "new events approved by moderators"
491 ),
492 push_title=f'{data.inviting_user.name} invited you to "{event.title}"',
493 push_body=body,
494 push_icon=v2avatar(data.inviting_user),
495 push_url=event_link,
496 )
497 elif notification.action == "update":
498 updated_text = ", ".join(data.updated_items)
499 body = f"{time_display}\n"
500 body += f"{data.updating_user.name} updated: {updated_text}\n\n"
501 body += event.content
502 return RenderedNotification(
503 email_subject=f'{data.updating_user.name} updated "{event.title}"',
504 email_preview="An event you are subscribed to was updated.",
505 email_template_name="event_update",
506 email_template_args={
507 "updating_user": data.updating_user,
508 "time_display": time_display,
509 "event": event,
510 "updated_text": updated_text,
511 "view_link": event_link,
512 },
513 email_topic_action_unsubscribe_text="event updates",
514 push_title=f'{data.updating_user.name} updated "{event.title}"',
515 push_body=body,
516 push_icon=v2avatar(data.updating_user),
517 push_url=event_link,
518 )
519 elif notification.action == "cancel":
520 body = f"{time_display}\n"
521 body += f"The event has been cancelled by {data.cancelling_user.name}.\n\n"
522 body += event.content
523 return RenderedNotification(
524 email_subject=f'{data.cancelling_user.name} cancelled "{event.title}"',
525 email_preview="An event you are subscribed to has been cancelled.",
526 email_template_name="event_cancel",
527 email_template_args={
528 "cancelling_user": data.cancelling_user,
529 "time_display": time_display,
530 "event": event,
531 "view_link": event_link,
532 },
533 email_topic_action_unsubscribe_text="event cancellations",
534 push_title=f'{data.cancelling_user.name} cancelled "{event.title}"',
535 push_body=body,
536 push_icon=v2avatar(data.cancelling_user),
537 push_url=event_link,
538 )
539 elif notification.action == "delete":
540 return RenderedNotification(
541 email_subject=f'A moderator deleted "{event.title}"',
542 email_preview="An event you are subscribed to has been deleted.",
543 email_template_name="event_delete",
544 email_template_args={
545 "time_display": time_display,
546 "event": event,
547 },
548 email_topic_action_unsubscribe_text="event deletions",
549 push_title=f'A moderator deleted "{event.title}"',
550 push_body=f"{time_display}\nThe event has been deleted by the moderators.",
551 push_icon=urls.icon_url(),
552 push_url=urls.app_link(),
553 )
554 elif notification.action == "invite_organizer":
555 body = f"{time_display}\n"
556 body += f"Invited to co-organize by {data.inviting_user.name}\n\n"
557 body += event.content
558 return RenderedNotification(
559 email_subject=f'{data.inviting_user.name} invited you to co-organize "{event.title}"',
560 email_preview="You were invited to co-organize an event on Couchers.org.",
561 email_template_name="event_invite_organizer",
562 email_template_args={
563 "inviting_user": data.inviting_user,
564 "time_display": time_display,
565 "event": event,
566 "view_link": event_link,
567 },
568 email_topic_action_unsubscribe_text="invitations to co-organize events",
569 push_title=f'{data.inviting_user.name} invited you to co-organize "{event.title}"',
570 push_body=body,
571 push_icon=v2avatar(data.inviting_user),
572 push_url=event_link,
573 )
574 elif notification.topic == "reference":
575 if notification.action == "receive_friend":
576 title = f"You've received a friend reference from {data.from_user.name}!"
577 return RenderedNotification(
578 email_subject=title,
579 email_preview=v2esc(data.text),
580 email_template_name="friend_reference",
581 email_template_args={
582 "from_user": data.from_user,
583 "profile_references_link": urls.profile_references_link(),
584 "text": data.text,
585 },
586 email_topic_action_unsubscribe_text="new references from friends",
587 push_title=title,
588 push_body=data.text,
589 push_icon=v2avatar(data.from_user),
590 push_url=urls.profile_references_link(),
591 )
592 elif notification.action in ["receive_hosted", "receive_surfed"]:
593 title = f"You've received a reference from {data.from_user.name}!"
594 # what was my type? i surfed with them if i received a "hosted" request
595 surfed = (notification.action == "receive_hosted",)
596 leave_reference_link = urls.leave_reference_link(
597 reference_type="surfed" if surfed else "hosted",
598 to_user_id=data.from_user.user_id,
599 host_request_id=data.host_request_id,
600 )
601 profile_references_link = urls.profile_references_link()
602 if data.text:
603 body = v2esc(data.text)
604 push_url = profile_references_link
605 else:
606 body = "Please go and write a reference for them too. It's a nice gesture and helps us build a community together!"
607 push_url = leave_reference_link
608 return RenderedNotification(
609 email_subject=title,
610 email_preview=body,
611 email_template_name="host_reference",
612 email_template_args={
613 "from_user": data.from_user,
614 "leave_reference_link": leave_reference_link,
615 "profile_references_link": profile_references_link,
616 "text": data.text,
617 "both_written": True if data.text else False,
618 "surfed": surfed,
619 },
620 email_topic_action_unsubscribe_text="new references from " + ("hosts" if surfed else "surfers"),
621 push_title=title,
622 push_body=body,
623 push_icon=v2avatar(data.from_user),
624 push_url=push_url,
625 )
626 elif notification.action in ["reminder_hosted", "reminder_surfed"]:
627 # what was my type? i surfed with them if i get a surfed reminder
628 surfed = (notification.action == "reminder_surfed",)
629 leave_reference_link = urls.leave_reference_link(
630 reference_type="surfed" if surfed else "hosted",
631 to_user_id=data.other_user.user_id,
632 host_request_id=data.host_request_id,
633 )
634 title = f"You have {data.days_left} days to write a reference for {data.other_user.name}!"
635 preview = "It's a nice gesture to write references and helps us build a community together! References will become visible 2 weeks after the stay, or when you've both written a reference for each other, whichever happens first."
636 return RenderedNotification(
637 email_subject=title,
638 email_preview=preview,
639 email_template_name="reference_reminder",
640 email_template_args={
641 "other_user": data.other_user,
642 "leave_reference_link": leave_reference_link,
643 "days_left": str(data.days_left),
644 "surfed": surfed,
645 },
646 email_topic_action_unsubscribe_text=("surfed" if surfed else "hosted") + " reference reminders",
647 push_title=title,
648 push_body=preview,
649 push_icon=v2avatar(data.other_user),
650 push_url=leave_reference_link,
651 )
652 elif notification.topic_action.display == "onboarding:reminder":
653 if notification.key == "1":
654 return RenderedNotification(
655 email_subject="Welcome to Couchers.org and the future of couch surfing",
656 email_preview="We are so excited to have you join our community!",
657 email_template_name="onboarding1",
658 email_template_args={
659 "app_link": urls.app_link(),
660 "edit_profile_link": urls.edit_profile_link(),
661 },
662 email_topic_action_unsubscribe_text="onboarding emails",
663 push_title="Welcome to Couchers.org and the future of couch surfing",
664 push_body=f"Hi {v2esc(user.name)}! We are excited that you have joined us! Please take a moment to complete your profile with a picture and a bit of text about yourself!",
665 push_icon=urls.icon_url(),
666 push_url=urls.edit_profile_link(),
667 )
668 elif notification.key == "2":
669 return RenderedNotification(
670 email_subject="Complete your profile on Couchers.org",
671 email_preview="We would ask one big favour of you: please fill out your profile by adding a photo and some text.",
672 email_template_name="onboarding2",
673 email_template_args={
674 "edit_profile_link": urls.edit_profile_link(),
675 },
676 email_topic_action_unsubscribe_text="onboarding emails",
677 push_title="Please complete your profile on Couchers.org!",
678 push_body=f"Hi {v2esc(user.name)}! We would ask one big favour of you: please fill out your profile by adding a photo and some text.",
679 push_icon=urls.icon_url(),
680 push_url=urls.edit_profile_link(),
681 )
682 elif notification.topic_action.display == "modnote:create":
683 title = "You have received a mod note"
684 message = "You have received an important note from the moderators. You must read and acknowledge it before continuing to use the platform."
685 return RenderedNotification(
686 is_critical=True,
687 email_subject=title,
688 email_preview=message,
689 email_template_name="mod_note",
690 email_template_args={"title": title},
691 push_title="You received a mod note",
692 push_body="You need to read and acknowledge the note before continuing to use the platform.",
693 push_icon=urls.icon_url(),
694 push_url=urls.app_link(),
695 )
696 else:
697 raise NotImplementedError(f"Unknown topic-action: {notification.topic}:{notification.action}")