mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-11-15 03:04:28 +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:
|
else:
|
||||||
try:
|
try:
|
||||||
actor = Person(**ACTOR_SERVICE.get(recipient))
|
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:
|
except NotAnActorError as error:
|
||||||
# Is the activity a `Collection`/`OrderedCollection`?
|
# Is the activity a `Collection`/`OrderedCollection`?
|
||||||
if error.activity and error.activity['type'] in [ActivityType.COLLECTION.value,
|
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:
|
if col_actor.inbox and col_actor.inbox not in out:
|
||||||
out.append(col_actor.inbox)
|
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
|
return out
|
||||||
|
|
||||||
def build_undo(self) -> 'BaseActivity':
|
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)
|
return activitypub_utils.parse_collection(payload, url)
|
||||||
|
|
||||||
|
|
||||||
def embed_collection(data):
|
def embed_collection(total_items, first_page_id):
|
||||||
return {
|
return {
|
||||||
"type": "Collection",
|
"type": ActivityType.ORDERED_COLLECTION.value,
|
||||||
"totalItems": len(data),
|
"totalItems": total_items,
|
||||||
"items": data,
|
"first": first_page_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1097,7 +1096,7 @@ def build_ordered_collection(col, q=None, cursor=None, map_func=None, limit=50,
|
||||||
return {
|
return {
|
||||||
'id': BASE_URL + '/' + col_name,
|
'id': BASE_URL + '/' + col_name,
|
||||||
'totalItems': 0,
|
'totalItems': 0,
|
||||||
'type': 'OrderedCollection',
|
'type': ActivityType.ORDERED_COLLECTION.value,
|
||||||
'orederedItems': [],
|
'orederedItems': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,13 +1114,13 @@ def build_ordered_collection(col, q=None, cursor=None, map_func=None, limit=50,
|
||||||
'@context': CTX_AS,
|
'@context': CTX_AS,
|
||||||
'id': f'{BASE_URL}/{col_name}',
|
'id': f'{BASE_URL}/{col_name}',
|
||||||
'totalItems': total_items,
|
'totalItems': total_items,
|
||||||
'type': 'OrderedCollection',
|
'type': ActivityType.ORDERED_COLLECTION.value,
|
||||||
'first': {
|
'first': {
|
||||||
'id': f'{BASE_URL}/{col_name}?cursor={start_cursor}',
|
'id': f'{BASE_URL}/{col_name}?cursor={start_cursor}',
|
||||||
'orderedItems': data,
|
'orderedItems': data,
|
||||||
'partOf': f'{BASE_URL}/{col_name}',
|
'partOf': f'{BASE_URL}/{col_name}',
|
||||||
'totalItems': total_items,
|
'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
|
return resp
|
||||||
|
|
||||||
|
|
||||||
# If there's a cursor, then we return an OrderedCollectionPage
|
# If there's a cursor, then we return an OrderedCollectionPage
|
||||||
resp = {
|
resp = {
|
||||||
'@context': CTX_AS,
|
'@context': CTX_AS,
|
||||||
'type': 'OrderedCollectionPage',
|
'type': ActivityType.ORDERED_COLLECTION_PAGE.value,
|
||||||
'id': BASE_URL + '/' + col_name + '?cursor=' + start_cursor,
|
'id': BASE_URL + '/' + col_name + '?cursor=' + start_cursor,
|
||||||
'totalItems': total_items,
|
'totalItems': total_items,
|
||||||
'partOf': BASE_URL + '/' + col_name,
|
'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:
|
if len(data) == limit:
|
||||||
resp['next'] = BASE_URL + '/' + col_name + '?cursor=' + next_page_cursor
|
resp['next'] = BASE_URL + '/' + col_name + '?cursor=' + next_page_cursor
|
||||||
|
|
||||||
|
# TODO(tsileo): implements prev with prev=<first item cursor>
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
101
app.py
101
app.py
|
@ -471,17 +471,20 @@ def add_extra_collection(raw_doc: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
if raw_doc['activity']['type'] != ActivityType.CREATE.value:
|
if raw_doc['activity']['type'] != ActivityType.CREATE.value:
|
||||||
return raw_doc
|
return raw_doc
|
||||||
|
|
||||||
if 'col_likes' in raw_doc.get('meta', {}):
|
raw_doc['activity']['object']['replies'] = embed_collection(
|
||||||
col_likes = raw_doc['meta']['col_likes']
|
raw_doc.get('meta', {}).get('count_direct_reply', 0),
|
||||||
raw_doc['activity']['object']['likes'] = embed_collection(col_likes)
|
f'{ID}/outbox/{raw_doc["id"]}/replies',
|
||||||
|
)
|
||||||
|
|
||||||
if 'col_shares' in raw_doc.get('meta', {}):
|
raw_doc['activity']['object']['likes'] = embed_collection(
|
||||||
col_shares = raw_doc['meta']['col_shares']
|
raw_doc.get('meta', {}).get('count_like', 0),
|
||||||
raw_doc['activity']['object']['shares'] = embed_collection(col_shares)
|
f'{ID}/outbox/{raw_doc["id"]}/likes',
|
||||||
|
)
|
||||||
|
|
||||||
if 'count_direct_reply' in raw_doc.get('meta', {}):
|
raw_doc['activity']['object']['shares'] = embed_collection(
|
||||||
# FIXME(tsileo): implements the collection handler
|
raw_doc.get('meta', {}).get('count_boost', 0),
|
||||||
raw_doc['activity']['object']['replies'] = {'type': 'Collection', 'totalItems': raw_doc['meta']['count_direct_reply']}
|
f'{ID}/outbox/{raw_doc["id"]}/shares',
|
||||||
|
)
|
||||||
|
|
||||||
return raw_doc
|
return raw_doc
|
||||||
|
|
||||||
|
@ -547,6 +550,86 @@ def outbox_activity(item_id):
|
||||||
return jsonify(**obj['object'])
|
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'])
|
@app.route('/admin', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def admin():
|
def admin():
|
||||||
|
|
|
@ -301,8 +301,8 @@ def test_post_content_and_like():
|
||||||
|
|
||||||
note = instance1.outbox_get(f'{create_id}/activity')
|
note = instance1.outbox_get(f'{create_id}/activity')
|
||||||
assert 'likes' in note
|
assert 'likes' in note
|
||||||
assert len(note['likes']['items']) == 1
|
assert note['likes']['totalItems'] == 1
|
||||||
assert note['likes']['items'][0]['id'] == like_id
|
# assert note['likes']['items'][0]['id'] == like_id
|
||||||
|
|
||||||
|
|
||||||
def test_post_content_and_like_unlike():
|
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')
|
note = instance1.outbox_get(f'{create_id}/activity')
|
||||||
assert 'likes' in note
|
assert 'likes' in note
|
||||||
assert len(note['likes']['items']) == 1
|
assert note['likes']['totalItems'] == 1
|
||||||
assert note['likes']['items'][0]['id'] == like_id
|
# FIXME(tsileo): parse the collection
|
||||||
|
# assert note['likes']['items'][0]['id'] == like_id
|
||||||
|
|
||||||
instance2.undo(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')
|
note = instance1.outbox_get(f'{create_id}/activity')
|
||||||
assert 'likes' in note
|
assert 'likes' in note
|
||||||
assert len(note['likes']['items']) == 0
|
assert note['likes']['totalItems'] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_post_content_and_boost():
|
def test_post_content_and_boost():
|
||||||
|
@ -363,8 +364,9 @@ def test_post_content_and_boost():
|
||||||
|
|
||||||
note = instance1.outbox_get(f'{create_id}/activity')
|
note = instance1.outbox_get(f'{create_id}/activity')
|
||||||
assert 'shares' in note
|
assert 'shares' in note
|
||||||
assert len(note['shares']['items']) == 1
|
assert note['shares']['totalItems'] == 1
|
||||||
assert note['shares']['items'][0]['id'] == boost_id
|
# FIXME(tsileo): parse the collection
|
||||||
|
# assert note['shares']['items'][0]['id'] == boost_id
|
||||||
|
|
||||||
|
|
||||||
def test_post_content_and_boost_unboost():
|
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')
|
note = instance1.outbox_get(f'{create_id}/activity')
|
||||||
assert 'shares' in note
|
assert 'shares' in note
|
||||||
assert len(note['shares']['items']) == 1
|
assert note['shares']['totalItems'] == 1
|
||||||
assert note['shares']['items'][0]['id'] == boost_id
|
# FIXME(tsileo): parse the collection
|
||||||
|
# assert note['shares']['items'][0]['id'] == boost_id
|
||||||
|
|
||||||
instance2.undo(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')
|
note = instance1.outbox_get(f'{create_id}/activity')
|
||||||
assert 'shares' in note
|
assert 'shares' in note
|
||||||
assert len(note['shares']['items']) == 0
|
assert note['shares']['totalItems'] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_post_content_and_post_reply():
|
def test_post_content_and_post_reply():
|
||||||
|
|
Loading…
Reference in a new issue