microblog.pub/app/source.py

94 lines
2.9 KiB
Python
Raw Normal View History

2022-06-22 13:11:22 -05:00
import re
from markdown import markdown
2022-06-29 01:56:39 -05:00
from sqlalchemy import select
2022-06-22 13:11:22 -05:00
from app import models
from app import webfinger
2022-06-26 12:00:29 -05:00
from app.actor import Actor
2022-06-22 13:11:22 -05:00
from app.actor import fetch_actor
from app.config import BASE_URL
2022-06-29 13:43:17 -05:00
from app.database import AsyncSession
2022-06-27 13:55:44 -05:00
from app.utils import emoji
2022-06-22 13:11:22 -05:00
def _set_a_attrs(attrs, new=False):
attrs[(None, "target")] = "_blank"
attrs[(None, "class")] = "external"
attrs[(None, "rel")] = "noopener"
attrs[(None, "title")] = attrs[(None, "href")]
return attrs
_HASHTAG_REGEX = re.compile(r"(#[\d\w]+)")
_MENTION_REGEX = re.compile(r"@[\d\w_.+-]+@[\d\w-]+\.[\d\w\-.]+")
2022-06-29 13:43:17 -05:00
async def _hashtagify(
db_session: AsyncSession, content: str
) -> tuple[str, list[dict[str, str]]]:
2022-06-22 13:11:22 -05:00
tags = []
hashtags = re.findall(_HASHTAG_REGEX, content)
hashtags = sorted(set(hashtags), reverse=True) # unique tags, longest first
for hashtag in hashtags:
tag = hashtag[1:]
link = f'<a href="{BASE_URL}/t/{tag}" class="mention hashtag" rel="tag">#<span>{tag}</span></a>' # noqa: E501
tags.append(dict(href=f"{BASE_URL}/t/{tag}", name=hashtag, type="Hashtag"))
content = content.replace(hashtag, link)
return content, tags
2022-06-29 13:43:17 -05:00
async def _mentionify(
db_session: AsyncSession,
2022-06-26 12:00:29 -05:00
content: str,
) -> tuple[str, list[dict[str, str]], list[Actor]]:
2022-06-22 13:11:22 -05:00
tags = []
2022-06-26 12:00:29 -05:00
mentioned_actors = []
2022-06-22 13:11:22 -05:00
for mention in re.findall(_MENTION_REGEX, content):
_, username, domain = mention.split("@")
2022-06-29 13:43:17 -05:00
actor = (
await db_session.execute(
select(models.Actor).where(models.Actor.handle == mention)
)
2022-06-29 01:56:39 -05:00
).scalar_one_or_none()
2022-06-22 13:11:22 -05:00
if not actor:
actor_url = webfinger.get_actor_url(mention)
if not actor_url:
# FIXME(ts): raise an error?
continue
2022-06-29 13:43:17 -05:00
actor = await fetch_actor(db_session, actor_url)
2022-06-22 13:11:22 -05:00
2022-06-26 12:00:29 -05:00
mentioned_actors.append(actor)
2022-06-22 13:11:22 -05:00
tags.append(dict(type="Mention", href=actor.url, name=mention))
2022-06-26 12:00:29 -05:00
link = f'<span class="h-card"><a href="{actor.url}" class="u-url mention">@{username}</a></span>' # noqa: E501
2022-06-22 13:11:22 -05:00
content = content.replace(mention, link)
2022-06-26 12:00:29 -05:00
return content, tags, mentioned_actors
2022-06-22 13:11:22 -05:00
2022-06-29 13:43:17 -05:00
async def markdownify(
db_session: AsyncSession,
2022-06-22 13:11:22 -05:00
content: str,
mentionify: bool = True,
hashtagify: bool = True,
2022-06-26 12:00:29 -05:00
) -> tuple[str, list[dict[str, str]], list[Actor]]:
2022-06-22 13:11:22 -05:00
"""
>>> content, tags = markdownify("Hello")
"""
tags = []
2022-06-26 12:00:29 -05:00
mentioned_actors: list[Actor] = []
2022-06-22 13:11:22 -05:00
if hashtagify:
2022-06-29 13:43:17 -05:00
content, hashtag_tags = await _hashtagify(db_session, content)
2022-06-22 13:11:22 -05:00
tags.extend(hashtag_tags)
if mentionify:
2022-06-29 13:43:17 -05:00
content, mention_tags, mentioned_actors = await _mentionify(db_session, content)
2022-06-22 13:11:22 -05:00
tags.extend(mention_tags)
2022-06-27 13:55:44 -05:00
# Handle custom emoji
tags.extend(emoji.tags(content))
2022-06-22 13:11:22 -05:00
content = markdown(content, extensions=["mdx_linkify"])
2022-06-27 13:55:44 -05:00
2022-06-26 12:00:29 -05:00
return content, tags, mentioned_actors