Boostrap stream customization (API may change)

This commit is contained in:
Thomas Sileo 2022-11-22 20:30:35 +01:00
parent bd065446bf
commit 2cc4eda143
3 changed files with 73 additions and 4 deletions

View file

@ -32,6 +32,8 @@ from app.config import BLOCKED_SERVERS
from app.config import ID from app.config import ID
from app.config import MANUALLY_APPROVES_FOLLOWERS from app.config import MANUALLY_APPROVES_FOLLOWERS
from app.config import set_moved_to from app.config import set_moved_to
from app.config import stream_visibility_callback
from app.customization import ObjectInfo
from app.database import AsyncSession from app.database import AsyncSession
from app.outgoing_activities import new_outgoing_activity from app.outgoing_activities import new_outgoing_activity
from app.source import dedup_tags from app.source import dedup_tags
@ -1881,16 +1883,30 @@ async def _process_note_object(
is_from_following = ro.actor.ap_id in {f.ap_actor_id for f in following} is_from_following = ro.actor.ap_id in {f.ap_actor_id for f in following}
is_reply = bool(ro.in_reply_to) is_reply = bool(ro.in_reply_to)
is_local_reply = ( is_local_reply = bool(
ro.in_reply_to ro.in_reply_to
and ro.in_reply_to.startswith(BASE_URL) and ro.in_reply_to.startswith(BASE_URL)
and ro.content # Hide votes from Question and ro.content # Hide votes from Question
) )
is_mention = False is_mention = False
hashtags = []
tags = ro.ap_object.get("tag", []) tags = ro.ap_object.get("tag", [])
for tag in ap.as_list(tags): for tag in ap.as_list(tags):
if tag.get("name") == LOCAL_ACTOR.handle or tag.get("href") == LOCAL_ACTOR.url: if tag.get("name") == LOCAL_ACTOR.handle or tag.get("href") == LOCAL_ACTOR.url:
is_mention = True is_mention = True
if tag.get("type") == "Hashtag":
if tag_name := tag.get("name"):
hashtags.append(tag_name)
object_info = ObjectInfo(
is_reply=is_reply,
is_local_reply=is_local_reply,
is_mention=is_mention,
is_from_following=is_from_following,
hashtags=hashtags,
actor_handle=ro.actor.handle,
remote_object=ro,
)
inbox_object = models.InboxObject( inbox_object = models.InboxObject(
server=urlparse(ro.ap_id).hostname, server=urlparse(ro.ap_id).hostname,
@ -1908,9 +1924,7 @@ async def _process_note_object(
activity_object_ap_id=ro.activity_object_ap_id, activity_object_ap_id=ro.activity_object_ap_id,
og_meta=await opengraph.og_meta_from_note(db_session, ro), og_meta=await opengraph.og_meta_from_note(db_session, ro),
# Hide replies from the stream # Hide replies from the stream
is_hidden_from_stream=not ( is_hidden_from_stream=not stream_visibility_callback(object_info),
(not is_reply and is_from_following) or is_mention or is_local_reply
),
# We may already have some replies in DB # We may already have some replies in DB
replies_count=await _get_replies_count(db_session, ro.ap_id), replies_count=await _get_replies_count(db_session, ro.ap_id),
) )

View file

@ -16,6 +16,8 @@ from loguru import logger
from mistletoe import markdown # type: ignore from mistletoe import markdown # type: ignore
from app.customization import _CUSTOM_ROUTES from app.customization import _CUSTOM_ROUTES
from app.customization import _StreamVisibilityCallback
from app.customization import default_stream_visibility_callback
from app.utils.emoji import _load_emojis from app.utils.emoji import _load_emojis
from app.utils.version import get_version_commit from app.utils.version import get_version_commit
@ -262,3 +264,14 @@ def verify_csrf_token(
def hmac_sha256() -> hmac.HMAC: def hmac_sha256() -> hmac.HMAC:
return hmac.new(CONFIG.secret.encode(), digestmod=hashlib.sha256) return hmac.new(CONFIG.secret.encode(), digestmod=hashlib.sha256)
stream_visibility_callback: _StreamVisibilityCallback
try:
from data.stream import ( # type: ignore # noqa: F401, E501
custom_stream_visibility_callback,
)
stream_visibility_callback = custom_stream_visibility_callback
except ImportError:
stream_visibility_callback = default_stream_visibility_callback

View file

@ -1,12 +1,19 @@
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from fastapi import APIRouter from fastapi import APIRouter
from fastapi import Depends from fastapi import Depends
from fastapi import Request from fastapi import Request
from loguru import logger
from starlette.responses import JSONResponse from starlette.responses import JSONResponse
if TYPE_CHECKING:
from app.ap_object import RemoteObject
_DATA_DIR = Path().parent.resolve() / "data" _DATA_DIR = Path().parent.resolve() / "data"
_Handler = Callable[..., Any] _Handler = Callable[..., Any]
@ -110,3 +117,38 @@ def get_custom_router() -> APIRouter | None:
router.add_api_route(path, handler.handler) router.add_api_route(path, handler.handler)
return router return router
@dataclass
class ObjectInfo:
# Is it a reply?
is_reply: bool
# Is it a reply to an outbox object
is_local_reply: bool
# Is the object mentioning the local actor
is_mention: bool
# Is it from someone the local actor is following
is_from_following: bool
# List of hashtags, e.g. #microblogpub
hashtags: list[str]
# @dev@microblog.pub
actor_handle: str
remote_object: "RemoteObject"
_StreamVisibilityCallback = Callable[[ObjectInfo], bool]
def default_stream_visibility_callback(object_info: ObjectInfo) -> bool:
logger.info(f"{object_info=}")
return (
(not object_info.is_reply and object_info.is_from_following)
or object_info.is_mention
or object_info.is_local_reply
)