diff --git a/core/meta.py b/core/meta.py index fcab821..683265e 100644 --- a/core/meta.py +++ b/core/meta.py @@ -1,3 +1,4 @@ +from datetime import datetime from enum import Enum from enum import unique from typing import Any @@ -47,6 +48,10 @@ def _meta(mk: MetaKey) -> str: return f"meta.{mk.value}" +def flag(mk: MetaKey, val: Any) -> _SubQuery: + return {_meta(mk): val} + + def by_remote_id(remote_id: str) -> _SubQuery: return {"remote_id": remote_id} @@ -64,23 +69,23 @@ def by_type(type_: ap.ActivityType) -> _SubQuery: def not_undo() -> _SubQuery: - return {_meta(MetaKey.UNDO): False} + return flag(MetaKey.UNDO, False) def by_actor(actor: ap.BaseActivity) -> _SubQuery: - return {_meta(MetaKey.ACTOR_ID): actor.id} + return flag(MetaKey.ACTOR_ID, actor.id) def by_object_id(object_id: str) -> _SubQuery: - return {_meta(MetaKey.OBJECT_ID): object_id} + return flag(MetaKey.OBJECT_ID, object_id) def is_public() -> _SubQuery: - return {_meta(MetaKey.PUBLIC): True} + return flag(MetaKey.PUBLIC, True) def inc(mk: MetaKey, val: int) -> _SubQuery: - return {"$inc": {_meta(mk): val}} + return {"$inc": flag(mk, val)} def upsert(data: Dict[MetaKey, Any]) -> _SubQuery: @@ -92,5 +97,5 @@ def upsert(data: Dict[MetaKey, Any]) -> _SubQuery: return {"$set": sq} -def flag(mk: MetaKey, val: Any) -> _SubQuery: - return {_meta(mk): val} +def published_after(dt: datetime) -> _SubQuery: + return flag(MetaKey.PUBLISHED, {"gt": ap.format(dt)}) diff --git a/core/notifications.py b/core/notifications.py index 977acb2..614b090 100644 --- a/core/notifications.py +++ b/core/notifications.py @@ -1,4 +1,7 @@ import logging +from datetime import datetime +from datetime import timedelta +from datetime import timezone from functools import singledispatch from typing import Any from typing import Dict @@ -8,12 +11,16 @@ from little_boxes import activitypub as ap from config import BASE_URL from config import DB +from core.db import find_one_activity from core.meta import MetaKey from core.meta import _meta from core.meta import by_actor +from core.meta import by_object_id from core.meta import by_type +from core.meta import flag from core.meta import in_inbox from core.meta import not_undo +from core.meta import published_after from core.tasks import Tasks _logger = logging.getLogger(__name__) @@ -123,16 +130,26 @@ def _like_set_inbox_flags(activity: ap.Like, new_meta: _NewMeta) -> None: @set_inbox_flags.register def _announce_set_inbox_flags(activity: ap.Announce, new_meta: _NewMeta) -> None: _logger.info(f"set_inbox_flags activity={activity!r}") - # Is it a Like of local acitivty/from the outbox - if _is_from_outbox(activity.get_object()): + obj = activity.get_object() + # Is it a Annnounce/boost of local acitivty/from the outbox + if _is_from_outbox(obj): # Flag it as a notification _flag_as_notification(activity, new_meta) # Also set the "keep mark" for the GC (as we want to keep it forever) _set_flag(new_meta, MetaKey.GC_KEEP) - # Display it in the stream - _set_flag(new_meta, MetaKey.STREAM) + # Dedup boosts (it's annoying to see the same note multipe times on the same page) + if not find_one_activity( + { + **in_inbox(), + **by_object_id(obj.id), + **flag(MetaKey.STREAM, True), + **published_after(datetime.now(timezone.utc) - timedelta(hours=12)), + } + ): + # Display it in the stream only it not there already (only looking at the last 12 hours) + _set_flag(new_meta, MetaKey.STREAM) return None