Boostrap custom emoji support
parent
1f67d4e71c
commit
b977b64bfb
|
@ -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
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue