diff --git a/README.md b/README.md index 65ac4ce..fadfc39 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,13 @@ - Compatible with [Mastodon](https://joinmastodon.org/) and others ([Pleroma](https://pleroma.social/), Misskey, Plume, PixelFed, Hubzilla...) - Exposes your outbox as a basic microblog - Support all content types from the Fediverse (`Note`, `Article`, `Page`, `Video`, `Image`, `Question`...) + - Markdown support + - Server-side code syntax highlighting - Comes with an admin UI with notifications and the stream of people you follow - Private "bookmark" support - List support - Allows you to attach files to your notes + - Custom emojus - Cares about your privacy - The image upload endpoint strips EXIF meta data before storing the file - Every attachment/media is cached (or proxied) by the server diff --git a/blueprints/tasks.py b/blueprints/tasks.py index 0e0e8d6..863307e 100644 --- a/blueprints/tasks.py +++ b/blueprints/tasks.py @@ -25,6 +25,7 @@ from core.activitypub import _actor_hash from core.activitypub import _add_answers_to_question from core.activitypub import _cache_actor_icon from core.activitypub import is_from_outbox +from core.activitypub import new_context from core.activitypub import post_to_outbox from core.activitypub import save_reply from core.activitypub import update_cached_actor @@ -47,6 +48,7 @@ from core.shared import _Response from core.shared import back from core.shared import p from core.tasks import Tasks +from utils import now from utils import opengraph from utils.media import is_video from utils.webmentions import discover_webmention_endpoint @@ -102,6 +104,28 @@ def task_update_question() -> _Response: return "" +@blueprint.route("/task/send_actor_update", methods=["POST"]) +def task_send_actor_update() -> _Response: + task = p.parse(flask.request) + app.logger.info(f"task={task!r}") + try: + update = ap.Update( + actor=MY_PERSON.id, + object=MY_PERSON.to_dict(), + to=[MY_PERSON.followers], + cc=[ap.AS_PUBLIC], + published=now(), + context=new_context(), + ) + + post_to_outbox(update) + except Exception as err: + app.logger.exception(f"failed to send actor update") + raise TaskError() from err + + return "" + + @blueprint.route("/task/fetch_og_meta", methods=["POST"]) def task_fetch_og_meta() -> _Response: task = p.parse(flask.request) diff --git a/config.py b/config.py index 2e8af94..cc51783 100644 --- a/config.py +++ b/config.py @@ -69,6 +69,7 @@ with open(os.path.join(KEY_DIR, "me.yml")) as f: ICON_URL = conf["icon_url"] PASS = conf["pass"] + PROFILE_METADATA = conf.get("profile_metadata", {}) HIDE_FOLLOWING = conf.get("hide_following", True) # Theme-related config @@ -130,8 +131,8 @@ def _admin_jwt_token() -> str: ADMIN_API_KEY = get_secret_key("admin_api_key", _admin_jwt_token) attachments = [] -if conf.get("profile_metadata"): - for key, value in conf["profile_metadata"].items(): +if PROFILE_METADATA: + for key, value in PROFILE_METADATA.items(): attachments.append( {"type": "PropertyValue", "name": key, "value": linkify(value)} ) @@ -176,7 +177,7 @@ BLACKLIST = conf.get("blacklist", []) DISABLE_WEBMENTIONS = conf.get("disable_webmentions", False) # By default, we keep 14 of inbox data ; outbox is kept forever (along with bookmarked stuff, outbox replies, liked...) -DAYS_TO_KEEP = 14 +DAYS_TO_KEEP = int(conf.get("days_to_keep", 14)) # Load custom emojis (stored in static/emojis) _load_emojis(ROOT_DIR, BASE_URL) diff --git a/core/activitypub.py b/core/activitypub.py index db154ae..aa90817 100644 --- a/core/activitypub.py +++ b/core/activitypub.py @@ -78,7 +78,7 @@ def _answer_key(choice: str) -> str: return h.hexdigest() -def _actor_hash(actor: ap.ActivityType) -> str: +def _actor_hash(actor: ap.ActivityType, local: bool = False) -> str: """Used to know when to update the meta actor cache, like an "actor version".""" h = hashlib.new("sha1") h.update(actor.id.encode()) @@ -91,6 +91,12 @@ def _actor_hash(actor: ap.ActivityType) -> str: h.update(key.key_id().encode()) if isinstance(actor.icon, dict) and "url" in actor.icon: h.update(actor.icon["url"].encode()) + if local: + # The local hash helps us detect when to send an Update + for item in actor.attachment: + h.update(item["name"].encode()) + h.update(item["value"].encode()) + h.update(("1" if actor.manuallyApprovesFollowers else "0").encode()) return h.hexdigest() diff --git a/core/tasks.py b/core/tasks.py index 41479ae..4278fb7 100644 --- a/core/tasks.py +++ b/core/tasks.py @@ -8,8 +8,8 @@ from typing import Set from little_boxes import activitypub as ap from poussetaches import PousseTaches -from config import MEDIA_CACHE from config import DISABLE_WEBMENTIONS +from config import MEDIA_CACHE from utils import parse_datetime p = PousseTaches( @@ -102,6 +102,10 @@ class Tasks: def finish_post_to_outbox(iri: str) -> None: p.push(iri, "/task/finish_post_to_outbox") + @staticmethod + def send_actor_update() -> None: + p.push({}, "/task/send_actor_update", delay=2) + @staticmethod def update_question_outbox(iri: str, open_for: int) -> None: p.push( diff --git a/templates/header.html b/templates/header.html index cb1a926..708171c 100644 --- a/templates/header.html +++ b/templates/header.html @@ -11,6 +11,16 @@
{{ config.SUMMARY | safe }} + +{% if config.PROFILE_METADATA %} +
+{% for item in config.ME.attachment %} +{% if item.type == "PropertyValue" %} +
{{item.name | safe }}
{{ item.value | safe }}
+{% endif %} +{% endfor %} +
+{% endif %}
diff --git a/templates/layout.html b/templates/layout.html index 500fce9..2c7099c 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -21,6 +21,8 @@ width: 25px; height: 25px; } +dt:after {content: ": ";} +dt, dd { font-size: 0.9em; } {{ highlight_css }} {% block header %}{% endblock %}