Bugfixes and notifications improvements

This commit is contained in:
Thomas Sileo 2019-07-29 22:20:13 +02:00
parent d70c73cad7
commit bf954adaea
6 changed files with 87 additions and 38 deletions

View file

@ -3,7 +3,6 @@ import json
import logging import logging
import os import os
from datetime import datetime from datetime import datetime
from enum import Enum
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import List from typing import List
@ -133,7 +132,9 @@ class MicroblogPubBackend(Backend):
except Exception: # TODO(tsileo): should be ValueError, but replies trigger a KeyError on object except Exception: # TODO(tsileo): should be ValueError, but replies trigger a KeyError on object
pass pass
object_visibility = None object_visibility = None
if activity.has_type([ap.ActivityType.CREATE, ap.ActivityType.ANNOUNCE]): if activity.has_type(
[ap.ActivityType.CREATE, ap.ActivityType.ANNOUNCE, ap.ActivityType.LIKE]
):
object_visibility = ap.get_visibility(activity.get_object()).name object_visibility = ap.get_visibility(activity.get_object()).name
actor_id = activity.get_actor().id actor_id = activity.get_actor().id

23
app.py
View file

@ -2005,6 +2005,8 @@ def inbox():
# POST/ inbox # POST/ inbox
try: try:
data = request.get_json(force=True) data = request.get_json(force=True)
if not isinstance(data, dict):
raise ValueError("not a dict")
except Exception: except Exception:
return Response( return Response(
status=422, status=422,
@ -2018,9 +2020,15 @@ def inbox():
and is_blacklisted(data["id"]) and is_blacklisted(data["id"])
or ( or (
"object" in data "object" in data
and isinstance(data["object"], dict)
and "id" in data["object"] and "id" in data["object"]
and is_blacklisted(data["object"]["id"]) and is_blacklisted(data["object"]["id"])
) )
or (
"object" in data
and isinstance(data["object"], str)
and is_blacklisted(data["object"])
)
): ):
logger.info(f"dropping activity from blacklisted host: {data['id']}") logger.info(f"dropping activity from blacklisted host: {data['id']}")
return Response(status=201) return Response(status=201)
@ -3067,13 +3075,6 @@ def task_cache_actor() -> str:
if activity.has_type(ap.ActivityType.CREATE): if activity.has_type(ap.ActivityType.CREATE):
Tasks.fetch_og_meta(iri) Tasks.fetch_og_meta(iri)
# Cache the object if it's a `Like` or an `Announce` unrelated to the server outbox (because it will never get
# displayed)
if activity.has_type(
[ap.ActivityType.LIKE, ap.ActivityType.ANNOUNCE]
) and not activity.get_object_id().startswith(BASE_URL):
Tasks.cache_object(iri)
actor = activity.get_actor() actor = activity.get_actor()
if actor.icon: if actor.icon:
if isinstance(actor.icon, dict) and "url" in actor.icon: if isinstance(actor.icon, dict) and "url" in actor.icon:
@ -3212,14 +3213,6 @@ def task_process_new_activity():
# If the activity was originally forwarded, forward the delete too # If the activity was originally forwarded, forward the delete too
should_forward = True should_forward = True
elif activity.has_type(ap.ActivityType.LIKE):
if activity.get_object_id().startswith(BASE_URL):
should_keep = True
else:
# We only want to keep a like if it's a like for a local activity
# (Pleroma relay the likes it received, we don't want to store them)
should_delete = True
if should_forward: if should_forward:
app.logger.info(f"will forward {activity!r} to followers") app.logger.info(f"will forward {activity!r} to followers")
Tasks.forward_activity(activity.id) Tasks.forward_activity(activity.id)

View file

@ -7,7 +7,7 @@
<div id="admin"> <div id="admin">
{% if request.path == url_for('admin_notifications') and unread_notifications_count %} {% if request.path == url_for('admin_notifications') and unread_notifications_count %}
<div style="clear:both;padding-bottom:30px;"> <div style="clear:both;padding-bottom:60px;">
<form action="/api/mark_notifications_as_read" method="POST"> <form action="/api/mark_notifications_as_read" method="POST">
<input type="hidden" name="redirect" value="{{ request.path }}"/> <input type="hidden" name="redirect" value="{{ request.path }}"/>
<input type="hidden" name="nid" value="{{ nid }}"/> <input type="hidden" name="nid" value="{{ nid }}"/>
@ -26,7 +26,14 @@
{% if item | has_type('Announce') %} {% if item | has_type('Announce') %}
{% set boost_actor = item.meta.actor %} {% set boost_actor = item.meta.actor %}
{% if boost_actor %} {% if boost_actor %}
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> boosted</span></p> <div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;">
<span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> boosted</span>
{% if request.path == url_for('admin_notifications') %}
{% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %}
<span class="bar-item-no-bg">{{ (item.activity.published or item.meta.published) | format_timeago }}</span>
{% endif %}
</div>
{% endif %} {% endif %}
{% if item.meta.object %} {% if item.meta.object %}
{{ utils.display_note(item.meta.object, ui=True, meta=item.meta) }} {{ utils.display_note(item.meta.object, ui=True, meta=item.meta) }}
@ -35,7 +42,11 @@
{% if item | has_type('Like') %} {% if item | has_type('Like') %}
{% set boost_actor = item.meta.actor %} {% set boost_actor = item.meta.actor %}
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> liked</span></p> <div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;">
<span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> liked</span>
{% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %}
<span class="bar-item-no-bg">{{ (item.activity.published or item.meta.published) | format_timeago }}</span>
</div>
{% if item.meta.object %} {% if item.meta.object %}
{{ utils.display_note(item.meta.object, ui=False, meta=item.meta) }} {{ utils.display_note(item.meta.object, ui=False, meta=item.meta) }}
{% endif %} {% endif %}
@ -43,8 +54,9 @@
{% if item | has_type('Follow') %} {% if item | has_type('Follow') %}
<div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;"> <div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;">
<span class="bar-item-no-hover">new follower</span>
{% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %} {% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %}
<span class="bar-item-no-bg">new follower</span> <span class="bar-item-no-bg">{{ (item.activity.published or item.meta.published) | format_timeago }}</span>
{% if item.meta.notification_follows_back %}<span class="bar-item-no-hover">already following</span> {% if item.meta.notification_follows_back %}<span class="bar-item-no-hover">already following</span>
{% else %} {% else %}
<form action="/api/follow" class="action-form" method="POST"> <form action="/api/follow" class="action-form" method="POST">
@ -61,8 +73,9 @@
{% elif item | has_type('Accept') %} {% elif item | has_type('Accept') %}
<div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;"> <div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;">
<span class="bar-item-no-hover">you started following</span>
{% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %} {% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %}
<span class="bar-item-no-bg">you started following</span> <span class="bar-item-no-bg">{{ (item.activity.published or item.meta.published) | format_timeago }}</span>
{% if item.meta.notification_follows_back %}<span class="bar-item-no-hover">follows you back</span>{% endif %} {% if item.meta.notification_follows_back %}<span class="bar-item-no-hover">follows you back</span>{% endif %}
</div> </div>
@ -71,7 +84,11 @@
</div> </div>
{% elif item | has_type('Undo') %} {% elif item | has_type('Undo') %}
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover">unfollowed you</span></p> <div style="margin-left:70px;padding-bottom:5px;margin-bottom:15px;display:inline-block;">
<span class="bar-item-no-hover">unfollowed you</span>
{% if item.meta.notification_unread %}<span class="bar-item-no-bg"><span class="pcolor">new</span></span>{% endif %}
<span class="bar-item-no-bg">{{ (item.activity.published or item.meta.published) | format_timeago }}</span>
</div>
<div style="height: 100px;"> <div style="height: 100px;">
{{ utils.display_actor_inline(item.meta.actor, size=50) }} {{ utils.display_actor_inline(item.meta.actor, size=50) }}
</div> </div>

View file

@ -29,4 +29,4 @@ def parse_datetime(s: str) -> datetime:
def now() -> str: def now() -> str:
ap.format_datetime(datetime.now(timezone.utc)) return ap.format_datetime(datetime.now(timezone.utc))

View file

@ -20,6 +20,9 @@ class MetaKey(Enum):
ACTOR_ID = "actor_id" ACTOR_ID = "actor_id"
UNDO = "undo" UNDO = "undo"
PUBLISHED = "published" PUBLISHED = "published"
GC_KEEP = "gc_keep"
OBJECT = "object"
OBJECT_ACTOR = "object_actor"
def _meta(mk: MetaKey) -> str: def _meta(mk: MetaKey) -> str:

View file

@ -5,9 +5,11 @@ from typing import Dict
from little_boxes import activitypub as ap from little_boxes import activitypub as ap
from config import BASE_URL
from config import DB from config import DB
from config import MetaKey from config import MetaKey
from config import _meta from config import _meta
from tasks import Tasks
from utils.meta import by_actor from utils.meta import by_actor
from utils.meta import by_type from utils.meta import by_type
from utils.meta import in_inbox from utils.meta import in_inbox
@ -18,6 +20,17 @@ _logger = logging.getLogger(__name__)
_NewMeta = Dict[str, Any] _NewMeta = Dict[str, Any]
def _is_from_outbox(activity: ap.BaseActivity) -> bool:
return activity.id.startswith(BASE_URL)
def _flag_as_notification(activity: ap.BaseActivity, new_meta: _NewMeta) -> None:
new_meta.update(
**{_meta(MetaKey.NOTIFICATION): True, _meta(MetaKey.NOTIFICATION_UNREAD): True}
)
return None
@singledispatch @singledispatch
def set_inbox_flags(activity: ap.BaseActivity, new_meta: _NewMeta) -> None: def set_inbox_flags(activity: ap.BaseActivity, new_meta: _NewMeta) -> None:
return None return None
@ -44,13 +57,8 @@ def _accept_set_inbox_flags(activity: ap.Accept, new_meta: _NewMeta) -> None:
) )
# This Accept will be a "You started following $actor" notification # This Accept will be a "You started following $actor" notification
new_meta.update( _flag_as_notification(activity, new_meta)
**{ new_meta.update(**{_meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): follows_back})
_meta(MetaKey.NOTIFICATION): True,
_meta(MetaKey.NOTIFICATION_UNREAD): True,
_meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): follows_back,
}
)
return None return None
@ -74,11 +82,38 @@ def _follow_set_inbox_flags(activity: ap.Follow, new_meta: _NewMeta) -> None:
) )
# This Follow will be a "$actor started following you" notification # This Follow will be a "$actor started following you" notification
new_meta.update( _flag_as_notification(activity, new_meta)
**{ new_meta.update(**{_meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): follows_back})
_meta(MetaKey.NOTIFICATION): True, return None
_meta(MetaKey.NOTIFICATION_UNREAD): True,
_meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): follows_back,
} @set_inbox_flags.register
) def _like_set_inbox_flags(activity: ap.Like, new_meta: _NewMeta) -> None:
# Is it a Like of local acitivty/from the outbox
if _is_from_outbox(activity.get_object()):
# Flag it as a notification
_flag_as_notification(activity, new_meta)
# Cache the object (for display on the notifcation page)
Tasks.cache_object(activity.id)
# Also set the "keep mark" for the GC (as we want to keep it forever)
new_meta.update(**{_meta(MetaKey.GC_KEEP): True})
return None
@set_inbox_flags.register
def _announce_set_inbox_flags(activity: ap.Announce, new_meta: _NewMeta) -> None:
# Is it a Like of local acitivty/from the outbox
if _is_from_outbox(activity.get_object()):
# 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)
new_meta.update(**{_meta(MetaKey.GC_KEEP): True})
# Cache the object in all case (for display on the notifcation page)
Tasks.cache_object(activity.id)
return None return None