2024-05-16 10:37:37 -04:00
|
|
|
import mistune
|
|
|
|
|
from notifications_utils import MAGIC_SEQUENCE, magic_sequence_regex
|
|
|
|
|
from notifications_utils.formatters import create_sanitised_html_for_url
|
2025-03-26 09:16:07 -07:00
|
|
|
import re
|
|
|
|
|
from itertools import count
|
2024-05-16 10:37:37 -04:00
|
|
|
|
|
|
|
|
LINK_STYLE = "word-wrap: break-word; color: #1D70B8;"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
|
|
|
|
|
class EmailRenderer(mistune.HTMLRenderer):
|
|
|
|
|
def heading(self, text, level):
|
2024-05-16 10:37:37 -04:00
|
|
|
if level == 1:
|
2025-03-26 09:16:07 -07:00
|
|
|
return (
|
|
|
|
|
'<h2 style="Margin: 0 0 20px 0; padding: 0; '
|
|
|
|
|
'font-size: 27px; line-heigh: 35px; font-weight: bold; color: #0B0C0C;">'
|
|
|
|
|
f"{text}</h2>"
|
|
|
|
|
)
|
2024-05-16 10:37:37 -04:00
|
|
|
return self.paragraph(text)
|
|
|
|
|
|
|
|
|
|
def paragraph(self, text):
|
|
|
|
|
if text.strip():
|
2025-03-26 09:16:07 -07:00
|
|
|
return (
|
|
|
|
|
'<p style="Margin: 0 0 20px 0; font-size: 19px; '
|
|
|
|
|
'line-height: 25px; color: #0B0C0C;">' + text + '</p>'
|
|
|
|
|
)
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def emphasis(self, text):
|
|
|
|
|
return f"*{text}*"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def block_quote(self, text):
|
|
|
|
|
return (
|
|
|
|
|
'<blockquote style="Margin: 0 0 20px 0; border-left: 10px solid #B1B4B6; '
|
|
|
|
|
'padding: 15 px 0 0.1px 15 px; font-size: 19px; line-height: 25px;">'
|
|
|
|
|
f"{text}</blockquote>"
|
2024-05-16 10:37:37 -04:00
|
|
|
)
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def thematic_break(self):
|
|
|
|
|
return '<hr style="border: 0; height: 1px; background: #B1B4B6; Margin: 30px 0 30px 0;">'
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
|
|
|
|
|
def codespan(self, text):
|
|
|
|
|
return (
|
|
|
|
|
f"`{text}`"
|
|
|
|
|
)
|
2024-05-16 10:37:37 -04:00
|
|
|
def linebreak(self):
|
2025-03-26 09:16:07 -07:00
|
|
|
return "<br />"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def list(self, text, ordered, level=None, start=None, **kwargs):
|
|
|
|
|
tag = "ol" if ordered else "ul"
|
|
|
|
|
style = (
|
|
|
|
|
'list-style-type: decimal;' if ordered else 'list-style-type: disc;'
|
|
|
|
|
)
|
|
|
|
|
return (
|
|
|
|
|
'<table role="presentation" style="padding 0 0 20px 0;"><tr<td style="font-family: Helvetica, Arial, sans-serif;">'
|
|
|
|
|
f'<{tag} style="Margin: 0 0 0 20px; padding: 0; {style}">{text}</{tag}>'
|
|
|
|
|
'</td></tr></table'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def list_item(self, text, level=None):
|
|
|
|
|
return (
|
|
|
|
|
'<li style="Margin: 5px 0 5px; padding: 0 0 0 5px; font-size: 19px;'
|
|
|
|
|
'line-height: 25px; color: #0B0C0C;">' + text.strip() + '</li>'
|
|
|
|
|
)
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def link(self, link=None, text=None, title=None, url=None, **kwargs):
|
|
|
|
|
href = url or (link if link and link.startswith("http://", "https://") else "")
|
|
|
|
|
display_text = text or link or href or ""
|
|
|
|
|
title_attr = f' title="{title}"' if title else ""
|
|
|
|
|
return f'<a style="{LINK_STYLE}" href="{href}"{title_attr}>{display_text}</a>'
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def autolink(self, link, is_email=False):
|
|
|
|
|
return create_sanitised_html_for_url(link, style=LINK_STYLE)
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def image(self, src, alt="", title=None, url=None):
|
2024-05-16 10:37:37 -04:00
|
|
|
return ""
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def strikethrough(self, text):
|
|
|
|
|
return (
|
|
|
|
|
'<p style="Margin: 0 0 20px 0; font-size: 19px; line-height: 25px; color: #0B0C0C;">'
|
|
|
|
|
f"~~{text}~~"
|
|
|
|
|
'</p>'
|
|
|
|
|
)
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
class PlainTextRenderer(mistune.HTMLRenderer):
|
|
|
|
|
COLUMN_WIDTH = 65
|
|
|
|
|
|
|
|
|
|
def heading(self, text, level):
|
|
|
|
|
if level == 1:
|
|
|
|
|
return f"\n\n\n{text}\n{'-' * self.COLUMN_WIDTH}"
|
|
|
|
|
return self.paragraph(text)
|
|
|
|
|
|
|
|
|
|
def paragraph(self, text):
|
|
|
|
|
if text.strip():
|
|
|
|
|
return f"\n\n{text}"
|
|
|
|
|
return ""
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def thematic_break(self):
|
|
|
|
|
return f"\n\n{'=' * self.COLUMN_WIDTH}\n"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def heading(self, text, level):
|
|
|
|
|
print(f"TEXT {text} LEVEL {level}")
|
2024-05-16 10:37:37 -04:00
|
|
|
if level == 1:
|
2025-03-26 09:16:07 -07:00
|
|
|
return f"\n\n\n{text}\n{'-' * self.COLUMN_WIDTH}"
|
2024-05-16 10:37:37 -04:00
|
|
|
return self.paragraph(text)
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def block_quote(self, text):
|
|
|
|
|
return text
|
2024-05-16 10:37:37 -04:00
|
|
|
|
|
|
|
|
def linebreak(self):
|
2025-03-26 09:16:07 -07:00
|
|
|
return "\n"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def list(self, text, ordered, level=None, **kwargs):
|
|
|
|
|
return f"\n{text}"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def list_item(self, text, level=None):
|
|
|
|
|
return f"\n• {text.strip()}"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def link(self, link=None, text=None, title=None, url=None, **kwargs):
|
|
|
|
|
display_text = text or link or url or ""
|
|
|
|
|
href = url or link or ""
|
|
|
|
|
output = display_text
|
|
|
|
|
if title:
|
|
|
|
|
output += f" ({title})"
|
|
|
|
|
if href:
|
|
|
|
|
output += f": {href}"
|
|
|
|
|
return output
|
|
|
|
|
|
|
|
|
|
def autolink(self, link, is_email=False):
|
|
|
|
|
return link
|
|
|
|
|
|
|
|
|
|
def image(self, src, alt="", title=None, url=None):
|
2024-05-16 10:37:37 -04:00
|
|
|
return ""
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def emphasis(self, text):
|
|
|
|
|
return f"*{text}*"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def strong(self, text):
|
|
|
|
|
return f"**{text}**"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def codespan(self, text):
|
|
|
|
|
return f"`{text}`"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def strikethrough(self, text):
|
|
|
|
|
return f"~~{text}~~"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
class PreheaderRenderer(PlainTextRenderer):
|
|
|
|
|
def heading(self, text, level):
|
2024-05-16 10:37:37 -04:00
|
|
|
return self.paragraph(text)
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def thematic_break(self):
|
|
|
|
|
return ""
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def link(self, link, text=None, title=None):
|
|
|
|
|
return text or link
|
2024-05-16 10:37:37 -04:00
|
|
|
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def image(self, src, alt="", title=None, url=None):
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LetterPreviewRenderer(mistune.HTMLRenderer):
|
|
|
|
|
def heading(self, text, level):
|
|
|
|
|
if level == 1:
|
|
|
|
|
return super().heading(text, 2)
|
|
|
|
|
return self.paragraph(text)
|
2024-05-16 10:37:37 -04:00
|
|
|
|
|
|
|
|
def paragraph(self, text):
|
|
|
|
|
if text.strip():
|
2025-03-26 09:16:07 -07:00
|
|
|
return f"<p>{text}</p>"
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def link(self, link, text=None, title=None, url=None):
|
|
|
|
|
href = url
|
|
|
|
|
display_text = text or link
|
|
|
|
|
print(f"LINKE {link} URL {url} HREF {href}")
|
|
|
|
|
return f"{display_text}: <strong>{href.replace('http://', '').replace('https://', '')}</strong>"
|
|
|
|
|
#return f"{text}: {link}"
|
|
|
|
|
|
|
|
|
|
def autolink(self, link, is_email=False):
|
|
|
|
|
return f"<strong>{link.replace('http://', '')}.replace(https://', '')</strong>"
|
|
|
|
|
|
|
|
|
|
def thematic_break(self):
|
|
|
|
|
return '<div class="page-break"> </div>'
|
|
|
|
|
|
|
|
|
|
def image(self, src, alt="", title=None, **kwargs):
|
2024-05-16 10:37:37 -04:00
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
def block_quote(self, text):
|
|
|
|
|
return text
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def list_item(self, text, level=None):
|
|
|
|
|
return f"<li>{text.strip()}</li>\n"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def emphasis(self, text):
|
|
|
|
|
return f"*{text}*"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def strong(self, text):
|
|
|
|
|
return f"**{text}**"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def codespan(self, text):
|
|
|
|
|
return f"`{text}`"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
def linebreak(self):
|
|
|
|
|
return "<br>"
|
|
|
|
|
|
|
|
|
|
def newline(self):
|
|
|
|
|
return "<br>"
|
2024-05-16 10:37:37 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-26 09:16:07 -07:00
|
|
|
notify_email_markdown = mistune.create_markdown(renderer=EmailRenderer())
|
|
|
|
|
notify_letter_preview_markdown = mistune.create_markdown(renderer=LetterPreviewRenderer())
|
|
|
|
|
notify_email_preheader_markdown = mistune.create_markdown(renderer=PreheaderRenderer())
|
|
|
|
|
notify_plain_text_email_markdown=mistune.create_markdown(renderer=PlainTextRenderer())
|