104 lines
2.7 KiB
Python
104 lines
2.7 KiB
Python
import re
|
|
from html import unescape
|
|
from typing import cast
|
|
|
|
import mistune
|
|
from django.conf import settings
|
|
from django.utils.html import escape
|
|
from django.utils.translation import gettext as _
|
|
|
|
from catalog.models import Item, ItemCategory
|
|
|
|
_mistune_plugins = [
|
|
"url",
|
|
"strikethrough",
|
|
"footnotes",
|
|
"table",
|
|
"mark",
|
|
"superscript",
|
|
"subscript",
|
|
"math",
|
|
"spoiler",
|
|
"ruby",
|
|
]
|
|
_markdown = mistune.create_markdown(plugins=_mistune_plugins)
|
|
|
|
|
|
def convert_leading_space_in_md(body: str) -> str:
|
|
body = re.sub(r"^\s+$", "", body, flags=re.MULTILINE)
|
|
body = re.sub(
|
|
r"^(\u2003*)( +)",
|
|
lambda s: "\u2003" * ((len(s[2]) + 1) // 2 + len(s[1])),
|
|
body,
|
|
flags=re.MULTILINE,
|
|
)
|
|
return body
|
|
|
|
|
|
def render_md(s: str) -> str:
|
|
return cast(str, _markdown(s))
|
|
|
|
|
|
_RE_HTML_TAG = re.compile(r"<[^>]*>")
|
|
|
|
|
|
def html_to_text(h: str) -> str:
|
|
return unescape(_RE_HTML_TAG.sub(" ", h.replace("\r", "")))
|
|
|
|
|
|
def _spolier(s: str) -> str:
|
|
sl = s.split(">!", 1)
|
|
if len(sl) == 1:
|
|
return escape(s)
|
|
r = sl[1].split("!<", 1)
|
|
return (
|
|
escape(sl[0])
|
|
+ '<span class="spoiler" _="on click toggle .revealed on me">'
|
|
+ escape(r[0])
|
|
+ "</span>"
|
|
+ (_spolier(r[1]) if len(r) == 2 else "")
|
|
)
|
|
|
|
|
|
def render_text(s: str) -> str:
|
|
return _spolier(s).strip().replace("\n", "<br>")
|
|
|
|
|
|
def render_post_with_macro(txt: str, item: Item) -> str:
|
|
if not txt:
|
|
return ""
|
|
return (
|
|
txt.replace("[category]", str(ItemCategory(item.category).label))
|
|
.replace("[title]", item.display_title)
|
|
.replace("[url]", item.absolute_url)
|
|
)
|
|
|
|
|
|
def render_rating(score: int | None, star_mode=0) -> str:
|
|
"""convert score(0~10) to mastodon star emoji code"""
|
|
if score is None or score == "" or score == 0:
|
|
return ""
|
|
solid_stars = score // 2
|
|
half_star = int(bool(score % 2))
|
|
empty_stars = 5 - solid_stars if not half_star else 5 - solid_stars - 1
|
|
if star_mode == 0:
|
|
emoji_code = "🌕" * solid_stars + "🌗" * half_star + "🌑" * empty_stars
|
|
else:
|
|
emoji_code = (
|
|
settings.STAR_SOLID * solid_stars
|
|
+ settings.STAR_HALF * half_star
|
|
+ settings.STAR_EMPTY * empty_stars
|
|
)
|
|
emoji_code = emoji_code.replace("::", ": :")
|
|
emoji_code = " " + emoji_code + " "
|
|
return emoji_code
|
|
|
|
|
|
def render_spoiler_text(text, item):
|
|
if text and text.find(">!") != -1:
|
|
spoiler_text = _(
|
|
"regarding {item_title}, may contain spoiler or triggering content"
|
|
).format(item_title=item.display_title)
|
|
return spoiler_text, text.replace(">!", "").replace("!<", "")
|
|
else:
|
|
return None, text or ""
|