Boostrap custom emoji support

main
Thomas Sileo 2022-06-26 10:01:26 +02:00
parent 1f67d4e71c
commit b977b64bfb
4 changed files with 36 additions and 8 deletions

View File

@ -10,12 +10,14 @@ import timeago # type: ignore
from bs4 import BeautifulSoup # type: ignore from bs4 import BeautifulSoup # type: ignore
from fastapi import Request from fastapi import Request
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from loguru import logger
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from starlette.templating import _TemplateResponse as TemplateResponse from starlette.templating import _TemplateResponse as TemplateResponse
from app import models from app import models
from app.actor import LOCAL_ACTOR from app.actor import LOCAL_ACTOR
from app.ap_object import Attachment from app.ap_object import Attachment
from app.ap_object import Object
from app.boxes import public_outbox_objects_count from app.boxes import public_outbox_objects_count
from app.config import BASE_URL from app.config import BASE_URL
from app.config import DEBUG from app.config import DEBUG
@ -23,6 +25,7 @@ from app.config import VERSION
from app.config import generate_csrf_token from app.config import generate_csrf_token
from app.config import session_serializer from app.config import session_serializer
from app.database import now from app.database import now
from app.media import proxied_media_url
from app.utils.highlight import HIGHLIGHT_CSS from app.utils.highlight import HIGHLIGHT_CSS
from app.utils.highlight import highlight from app.utils.highlight import highlight
@ -160,10 +163,10 @@ def _update_inline_imgs(content):
return soup.find("body").decode_contents() return soup.find("body").decode_contents()
def _clean_html(html: str) -> str: def _clean_html(html: str, note: Object) -> str:
try: try:
return bleach.clean( return bleach.clean(
_update_inline_imgs(highlight(html)), _replace_custom_emojis(_update_inline_imgs(highlight(html)), note),
tags=ALLOWED_TAGS, tags=ALLOWED_TAGS,
attributes=ALLOWED_ATTRIBUTES, attributes=ALLOWED_ATTRIBUTES,
strip=True, strip=True,
@ -194,6 +197,25 @@ def _pluralize(count: int, singular: str = "", plural: str = "s") -> str:
return singular return singular
def _replace_custom_emojis(content: str, note: Object) -> str:
idx = {}
for tag in note.ap_object.get("tag", []):
if tag.get("type") == "Emoji":
try:
idx[tag["name"]] = proxied_media_url(tag["icon"]["url"])
except KeyError:
logger.warning(f"Failed to parse custom emoji {tag=}")
continue
for emoji_name, emoji_url in idx.items():
content = content.replace(
emoji_name,
f'<img class="custom-emoji" src="{emoji_url}" title="{emoji_name}" alt="{emoji_name}">', # noqa: E501
)
return content
_templates.env.filters["domain"] = _filter_domain _templates.env.filters["domain"] = _filter_domain
_templates.env.filters["media_proxy_url"] = _media_proxy_url _templates.env.filters["media_proxy_url"] = _media_proxy_url
_templates.env.filters["clean_html"] = _clean_html _templates.env.filters["clean_html"] = _clean_html

View File

@ -9,6 +9,6 @@
{% if ap_object and ap_object.ap_type == "Person" %} {% if ap_object and ap_object.ap_type == "Person" %}
{{ utils.display_actor(ap_object, actors_metadata) }} {{ utils.display_actor(ap_object, actors_metadata) }}
{% elif ap_object %} {% elif ap_object %}
{{ utils.display_object(ap_object) }} {{ utils.display_object_expanded(ap_object) }}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -114,7 +114,7 @@
{{ display_actor(object.actor, {}) }} {{ display_actor(object.actor, {}) }}
<div> <div>
{{ object.content | clean_html | safe }} {{ object.content | clean_html(object) | safe }}
</div> </div>
<a href="{{ object.url }}">{{ object.ap_published_at | format_date }}</a> <a href="{{ object.url }}">{{ object.ap_published_at | format_date }}</a>
@ -145,7 +145,7 @@
<a href="{{ object.url }}">{{ object.ap_published_at | timeago }}</a> <a href="{{ object.url }}">{{ object.ap_published_at | timeago }}</a>
</span> </span>
<div class="activity-main"> <div class="activity-main">
{{ object.content | clean_html | safe }} {{ object.content | clean_html(object) | safe }}
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,7 +4,7 @@ from urllib.parse import urlparse
import httpx import httpx
from loguru import logger from loguru import logger
from app import activitypub as ap from app import config
def webfinger( def webfinger(
@ -31,7 +31,13 @@ def webfinger(
for i, proto in enumerate(protos): for i, proto in enumerate(protos):
try: try:
url = f"{proto}://{host}/.well-known/webfinger" url = f"{proto}://{host}/.well-known/webfinger"
resp = ap.get(url, params={"resource": resource}) resp = httpx.get(
url,
params={"resource": resource},
headers={
"User-Agent": config.USER_AGENT,
},
)
break break
except httpx.HTTPStatusError as http_error: except httpx.HTTPStatusError as http_error:
logger.exception("HTTP error") logger.exception("HTTP error")
@ -48,7 +54,7 @@ def webfinger(
if is_404: if is_404:
return None return None
return resp return resp.json()
def get_remote_follow_template(resource: str) -> str | None: def get_remote_follow_template(resource: str) -> str | None: