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