mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-11-14 18:54:27 +00:00
Add new shares/likes/replies collection for notes
This commit is contained in:
parent
848b8b23a8
commit
6711722bd0
3 changed files with 127 additions and 39 deletions
|
@ -380,6 +380,16 @@ class BaseActivity(object):
|
|||
else:
|
||||
try:
|
||||
actor = Person(**ACTOR_SERVICE.get(recipient))
|
||||
|
||||
if actor.endpoints:
|
||||
shared_inbox = actor.endpoints.get('sharedInbox')
|
||||
if shared_inbox not in out:
|
||||
out.append(shared_inbox)
|
||||
continue
|
||||
|
||||
if actor.inbox and actor.inbox not in out:
|
||||
out.append(actor.inbox)
|
||||
|
||||
except NotAnActorError as error:
|
||||
# Is the activity a `Collection`/`OrderedCollection`?
|
||||
if error.activity and error.activity['type'] in [ActivityType.COLLECTION.value,
|
||||
|
@ -400,17 +410,6 @@ class BaseActivity(object):
|
|||
if col_actor.inbox and col_actor.inbox not in out:
|
||||
out.append(col_actor.inbox)
|
||||
|
||||
continue
|
||||
|
||||
if actor.endpoints:
|
||||
shared_inbox = actor.endpoints.get('sharedInbox')
|
||||
if shared_inbox not in out:
|
||||
out.append(shared_inbox)
|
||||
continue
|
||||
|
||||
if actor.inbox and actor.inbox not in out:
|
||||
out.append(actor.inbox)
|
||||
|
||||
return out
|
||||
|
||||
def build_undo(self) -> 'BaseActivity':
|
||||
|
@ -1076,11 +1075,11 @@ def parse_collection(payload: Optional[Dict[str, Any]] = None, url: Optional[str
|
|||
return activitypub_utils.parse_collection(payload, url)
|
||||
|
||||
|
||||
def embed_collection(data):
|
||||
def embed_collection(total_items, first_page_id):
|
||||
return {
|
||||
"type": "Collection",
|
||||
"totalItems": len(data),
|
||||
"items": data,
|
||||
"type": ActivityType.ORDERED_COLLECTION.value,
|
||||
"totalItems": total_items,
|
||||
"first": first_page_id,
|
||||
}
|
||||
|
||||
|
||||
|
@ -1097,7 +1096,7 @@ def build_ordered_collection(col, q=None, cursor=None, map_func=None, limit=50,
|
|||
return {
|
||||
'id': BASE_URL + '/' + col_name,
|
||||
'totalItems': 0,
|
||||
'type': 'OrderedCollection',
|
||||
'type': ActivityType.ORDERED_COLLECTION.value,
|
||||
'orederedItems': [],
|
||||
}
|
||||
|
||||
|
@ -1115,13 +1114,13 @@ def build_ordered_collection(col, q=None, cursor=None, map_func=None, limit=50,
|
|||
'@context': CTX_AS,
|
||||
'id': f'{BASE_URL}/{col_name}',
|
||||
'totalItems': total_items,
|
||||
'type': 'OrderedCollection',
|
||||
'type': ActivityType.ORDERED_COLLECTION.value,
|
||||
'first': {
|
||||
'id': f'{BASE_URL}/{col_name}?cursor={start_cursor}',
|
||||
'orderedItems': data,
|
||||
'partOf': f'{BASE_URL}/{col_name}',
|
||||
'totalItems': total_items,
|
||||
'type': 'OrderedCollectionPage'
|
||||
'type': ActivityType.ORDERED_COLLECTION_PAGE.value,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1130,10 +1129,11 @@ def build_ordered_collection(col, q=None, cursor=None, map_func=None, limit=50,
|
|||
|
||||
return resp
|
||||
|
||||
|
||||
# If there's a cursor, then we return an OrderedCollectionPage
|
||||
resp = {
|
||||
'@context': CTX_AS,
|
||||
'type': 'OrderedCollectionPage',
|
||||
'type': ActivityType.ORDERED_COLLECTION_PAGE.value,
|
||||
'id': BASE_URL + '/' + col_name + '?cursor=' + start_cursor,
|
||||
'totalItems': total_items,
|
||||
'partOf': BASE_URL + '/' + col_name,
|
||||
|
@ -1142,4 +1142,6 @@ def build_ordered_collection(col, q=None, cursor=None, map_func=None, limit=50,
|
|||
if len(data) == limit:
|
||||
resp['next'] = BASE_URL + '/' + col_name + '?cursor=' + next_page_cursor
|
||||
|
||||
# TODO(tsileo): implements prev with prev=<first item cursor>
|
||||
|
||||
return resp
|
||||
|
|
103
app.py
103
app.py
|
@ -471,18 +471,21 @@ def add_extra_collection(raw_doc: Dict[str, Any]) -> Dict[str, Any]:
|
|||
if raw_doc['activity']['type'] != ActivityType.CREATE.value:
|
||||
return raw_doc
|
||||
|
||||
if 'col_likes' in raw_doc.get('meta', {}):
|
||||
col_likes = raw_doc['meta']['col_likes']
|
||||
raw_doc['activity']['object']['likes'] = embed_collection(col_likes)
|
||||
raw_doc['activity']['object']['replies'] = embed_collection(
|
||||
raw_doc.get('meta', {}).get('count_direct_reply', 0),
|
||||
f'{ID}/outbox/{raw_doc["id"]}/replies',
|
||||
)
|
||||
|
||||
if 'col_shares' in raw_doc.get('meta', {}):
|
||||
col_shares = raw_doc['meta']['col_shares']
|
||||
raw_doc['activity']['object']['shares'] = embed_collection(col_shares)
|
||||
raw_doc['activity']['object']['likes'] = embed_collection(
|
||||
raw_doc.get('meta', {}).get('count_like', 0),
|
||||
f'{ID}/outbox/{raw_doc["id"]}/likes',
|
||||
)
|
||||
|
||||
raw_doc['activity']['object']['shares'] = embed_collection(
|
||||
raw_doc.get('meta', {}).get('count_boost', 0),
|
||||
f'{ID}/outbox/{raw_doc["id"]}/shares',
|
||||
)
|
||||
|
||||
if 'count_direct_reply' in raw_doc.get('meta', {}):
|
||||
# FIXME(tsileo): implements the collection handler
|
||||
raw_doc['activity']['object']['replies'] = {'type': 'Collection', 'totalItems': raw_doc['meta']['count_direct_reply']}
|
||||
|
||||
return raw_doc
|
||||
|
||||
|
||||
|
@ -547,6 +550,86 @@ def outbox_activity(item_id):
|
|||
return jsonify(**obj['object'])
|
||||
|
||||
|
||||
@app.route('/outbox/<item_id>/replies')
|
||||
def outbox_activity_replies(item_id):
|
||||
if not is_api_request():
|
||||
abort(404)
|
||||
data = DB.outbox.find_one({'id': item_id, 'meta.deleted': False})
|
||||
if not data:
|
||||
abort(404)
|
||||
obj = activitypub.parse_activity(data)
|
||||
if obj.type_enum != ActivityType.CREATE:
|
||||
abort(404)
|
||||
|
||||
q = {
|
||||
'meta.deleted': False,
|
||||
'type': ActivityType.CREATE.value,
|
||||
'activity.object.inReplyTo': obj.get_object().id,
|
||||
}
|
||||
|
||||
return jsonify(**activitypub.build_ordered_collection(
|
||||
DB.inbox,
|
||||
q=q,
|
||||
cursor=request.args.get('cursor'),
|
||||
map_func=lambda doc: doc['activity'],
|
||||
col_name=f'outbox/{item_id}/replies',
|
||||
))
|
||||
|
||||
|
||||
@app.route('/outbox/<item_id>/likes')
|
||||
def outbox_activity_likes(item_id):
|
||||
if not is_api_request():
|
||||
abort(404)
|
||||
data = DB.outbox.find_one({'id': item_id, 'meta.deleted': False})
|
||||
if not data:
|
||||
abort(404)
|
||||
obj = activitypub.parse_activity(data)
|
||||
if obj.type_enum != ActivityType.CREATE:
|
||||
abort(404)
|
||||
|
||||
q = {
|
||||
'meta.undo': False,
|
||||
'type': ActivityType.LIKE.value,
|
||||
'$or': [{'activity.object.id': obj.get_object().id},
|
||||
{'activity.object': obj.get_object().id}],
|
||||
}
|
||||
|
||||
return jsonify(**activitypub.build_ordered_collection(
|
||||
DB.inbox,
|
||||
q=q,
|
||||
cursor=request.args.get('cursor'),
|
||||
map_func=lambda doc: doc['activity'],
|
||||
col_name=f'outbox/{item_id}/likes',
|
||||
))
|
||||
|
||||
|
||||
@app.route('/outbox/<item_id>/shares')
|
||||
def outbox_activity_shares(item_id):
|
||||
if not is_api_request():
|
||||
abort(404)
|
||||
data = DB.outbox.find_one({'id': item_id, 'meta.deleted': False})
|
||||
if not data:
|
||||
abort(404)
|
||||
obj = activitypub.parse_activity(data)
|
||||
if obj.type_enum != ActivityType.CREATE:
|
||||
abort(404)
|
||||
|
||||
q = {
|
||||
'meta.undo': False,
|
||||
'type': ActivityType.ANNOUNCE.value,
|
||||
'$or': [{'activity.object.id': obj.get_object().id},
|
||||
{'activity.object': obj.get_object().id}],
|
||||
}
|
||||
|
||||
return jsonify(**activitypub.build_ordered_collection(
|
||||
DB.inbox,
|
||||
q=q,
|
||||
cursor=request.args.get('cursor'),
|
||||
map_func=lambda doc: doc['activity'],
|
||||
col_name=f'outbox/{item_id}/shares',
|
||||
))
|
||||
|
||||
|
||||
@app.route('/admin', methods=['GET'])
|
||||
@login_required
|
||||
def admin():
|
||||
|
|
|
@ -301,8 +301,8 @@ def test_post_content_and_like():
|
|||
|
||||
note = instance1.outbox_get(f'{create_id}/activity')
|
||||
assert 'likes' in note
|
||||
assert len(note['likes']['items']) == 1
|
||||
assert note['likes']['items'][0]['id'] == like_id
|
||||
assert note['likes']['totalItems'] == 1
|
||||
# assert note['likes']['items'][0]['id'] == like_id
|
||||
|
||||
|
||||
def test_post_content_and_like_unlike():
|
||||
|
@ -327,8 +327,9 @@ def test_post_content_and_like_unlike():
|
|||
|
||||
note = instance1.outbox_get(f'{create_id}/activity')
|
||||
assert 'likes' in note
|
||||
assert len(note['likes']['items']) == 1
|
||||
assert note['likes']['items'][0]['id'] == like_id
|
||||
assert note['likes']['totalItems'] == 1
|
||||
# FIXME(tsileo): parse the collection
|
||||
# assert note['likes']['items'][0]['id'] == like_id
|
||||
|
||||
instance2.undo(like_id)
|
||||
|
||||
|
@ -338,7 +339,7 @@ def test_post_content_and_like_unlike():
|
|||
|
||||
note = instance1.outbox_get(f'{create_id}/activity')
|
||||
assert 'likes' in note
|
||||
assert len(note['likes']['items']) == 0
|
||||
assert note['likes']['totalItems'] == 0
|
||||
|
||||
|
||||
def test_post_content_and_boost():
|
||||
|
@ -363,8 +364,9 @@ def test_post_content_and_boost():
|
|||
|
||||
note = instance1.outbox_get(f'{create_id}/activity')
|
||||
assert 'shares' in note
|
||||
assert len(note['shares']['items']) == 1
|
||||
assert note['shares']['items'][0]['id'] == boost_id
|
||||
assert note['shares']['totalItems'] == 1
|
||||
# FIXME(tsileo): parse the collection
|
||||
# assert note['shares']['items'][0]['id'] == boost_id
|
||||
|
||||
|
||||
def test_post_content_and_boost_unboost():
|
||||
|
@ -389,8 +391,9 @@ def test_post_content_and_boost_unboost():
|
|||
|
||||
note = instance1.outbox_get(f'{create_id}/activity')
|
||||
assert 'shares' in note
|
||||
assert len(note['shares']['items']) == 1
|
||||
assert note['shares']['items'][0]['id'] == boost_id
|
||||
assert note['shares']['totalItems'] == 1
|
||||
# FIXME(tsileo): parse the collection
|
||||
# assert note['shares']['items'][0]['id'] == boost_id
|
||||
|
||||
instance2.undo(boost_id)
|
||||
|
||||
|
@ -400,7 +403,7 @@ def test_post_content_and_boost_unboost():
|
|||
|
||||
note = instance1.outbox_get(f'{create_id}/activity')
|
||||
assert 'shares' in note
|
||||
assert len(note['shares']['items']) == 0
|
||||
assert note['shares']['totalItems'] == 0
|
||||
|
||||
|
||||
def test_post_content_and_post_reply():
|
||||
|
|
Loading…
Reference in a new issue