diff --git a/app/admin.py b/app/admin.py index 960bc83..294d61e 100644 --- a/app/admin.py +++ b/app/admin.py @@ -116,6 +116,16 @@ async def get_lookup( db_session, [ap_object] # type: ignore ) else: + # Check if the object is in the inbox + requested_object = await boxes.get_anybox_object_by_ap_id( + db_session, ap_object.ap_id + ) + if requested_object: + return RedirectResponse( + request.url_for("admin_object") + f"?ap_id={ap_object.ap_id}", + status_code=302, + ) + actors_metadata = await get_actors_metadata( db_session, [ap_object.actor] # type: ignore ) @@ -148,6 +158,14 @@ async def admin_new( in_reply_to_object = await boxes.get_anybox_object_by_ap_id( db_session, in_reply_to ) + if not in_reply_to_object: + logger.info(f"Saving unknwown object {in_reply_to}") + raw_object = await ap.fetch(in_reply_to) + await boxes.save_object_to_inbox(db_session, raw_object) + await db_session.commit() + in_reply_to_object = await boxes.get_anybox_object_by_ap_id( + db_session, in_reply_to + ) # Add mentions to the initial note content if not in_reply_to_object: @@ -891,7 +909,9 @@ async def admin_actions_bookmark( ) -> RedirectResponse: inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id) if not inbox_object: - raise ValueError("Should never happen") + logger.info(f"Saving unknwown object {ap_object_id}") + raw_object = await ap.fetch(ap_object_id) + inbox_object = await boxes.save_object_to_inbox(db_session, raw_object) inbox_object.is_bookmarked = True await db_session.commit() return RedirectResponse(redirect_url, status_code=302) diff --git a/app/boxes.py b/app/boxes.py index d38f8c4..85eb0a0 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -128,7 +128,13 @@ async def send_delete(db_session: AsyncSession, ap_object_id: str) -> None: async def send_like(db_session: AsyncSession, ap_object_id: str) -> None: inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id) if not inbox_object: - raise ValueError(f"{ap_object_id} not found in the inbox") + logger.info(f"Saving unknwown object {ap_object_id}") + raw_object = await ap.fetch(ap.get_id(ap_object_id)) + await save_object_to_inbox(db_session, raw_object) + await db_session.commit() + inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id) + if not inbox_object: + raise ValueError("Should never happen") like_id = allocate_outbox_id() like = { @@ -155,7 +161,13 @@ async def send_like(db_session: AsyncSession, ap_object_id: str) -> None: async def send_announce(db_session: AsyncSession, ap_object_id: str) -> None: inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id) if not inbox_object: - raise ValueError(f"{ap_object_id} not found in the inbox") + logger.info(f"Saving unknwown object {ap_object_id}") + raw_object = await ap.fetch(ap.get_id(ap_object_id)) + await save_object_to_inbox(db_session, raw_object) + await db_session.commit() + inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id) + if not inbox_object: + raise ValueError("Should never happen") if inbox_object.visibility not in [ ap.VisibilityEnum.PUBLIC, @@ -183,12 +195,13 @@ async def send_announce(db_session: AsyncSession, ap_object_id: str) -> None: raise ValueError("Should never happen") inbox_object.announced_via_outbox_object_ap_id = outbox_object.ap_id - await db_session.commit() recipients = await _compute_recipients(db_session, announce) for rcp in recipients: await new_outgoing_activity(db_session, rcp, outbox_object.id) + await db_session.commit() + async def send_follow(db_session: AsyncSession, ap_actor_id: str) -> None: actor = await fetch_actor(db_session, ap_actor_id) @@ -300,6 +313,12 @@ async def fetch_conversation_root( obj: AnyboxObject | RemoteObject, is_root: bool = False, ) -> str: + """Some softwares do not set the context/conversation field (like Misskey). + This means we have to track conversation ourselves. To do set, we fetch + the root of the conversation and either: + - use the context field if set + - or build a custom conversation ID + """ if not obj.in_reply_to or is_root: if obj.ap_context: return obj.ap_context @@ -1731,6 +1750,42 @@ async def save_to_inbox( await db_session.commit() +async def save_object_to_inbox( + db_session: AsyncSession, + raw_object: ap.RawObject, +) -> models.InboxObject: + obj_actor = await fetch_actor(db_session, ap.get_actor_id(raw_object)) + + ro = RemoteObject(raw_object, actor=obj_actor) + + ap_published_at = now() + if "published" in ro.ap_object: + ap_published_at = parse_isoformat(ro.ap_object["published"]) + + inbox_object = models.InboxObject( + server=urlparse(ro.ap_id).hostname, + actor_id=obj_actor.id, + ap_actor_id=obj_actor.ap_id, + ap_type=ro.ap_type, + ap_id=ro.ap_id, + ap_context=ro.ap_context, + conversation=await fetch_conversation_root(db_session, ro), + ap_published_at=ap_published_at, + ap_object=ro.ap_object, + visibility=ro.visibility, + relates_to_inbox_object_id=None, + relates_to_outbox_object_id=None, + activity_object_ap_id=ro.activity_object_ap_id, + og_meta=await opengraph.og_meta_from_note(db_session, ro), + is_hidden_from_stream=True, + ) + + db_session.add(inbox_object) + await db_session.flush() + await db_session.refresh(inbox_object) + return inbox_object + + async def public_outbox_objects_count(db_session: AsyncSession) -> int: return await db_session.scalar( select(func.count(models.OutboxObject.id)).where( diff --git a/app/main.py b/app/main.py index 92a8ed3..057c2c0 100644 --- a/app/main.py +++ b/app/main.py @@ -76,12 +76,12 @@ _RESIZED_CACHE: MutableMapping[tuple[str, int], tuple[bytes, str, Any]] = LFUCac # TODO(ts): # # Next: +# - empty recipients for Share on "was not in the inbox" object # - support Move # - support actor delete # - allow to share old notes # - allow to interact with object not in anybox (i.e. like from a lookup) # - only show 10 most recent threads in DMs -# - custom CSS for disabled button (e.g. sharing on a direct post) # - prevent double accept/double follow # - UI support for updating posts # - indieauth tweaks diff --git a/app/templates/utils.html b/app/templates/utils.html index f170fdb..53aed58 100644 --- a/app/templates/utils.html +++ b/app/templates/utils.html @@ -503,7 +503,7 @@ - {% if is_admin and (object.is_from_outbox or object.is_from_inbox) %} + {% if is_admin %} {% endif %}