-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Bluesky changes needed for backfeed #571
Changes from 11 commits
2b9f2d8
a47a26e
d73051a
465c6c2
54df15a
48b264c
1fb9285
222e4d9
30c7294
c3f58a7
4d4ba6d
cbab12e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ | |
* https://atproto.com/lexicons/app-bsky-actor | ||
* https://github.com/bluesky-social/atproto/tree/main/lexicons/app/bsky | ||
""" | ||
import copy | ||
import json | ||
import logging | ||
import re | ||
|
@@ -771,6 +770,7 @@ def to_as1(obj, type=None, repo_did=None, repo_handle=None, pds=DEFAULT_PDS): | |
reason = obj.get('reason') | ||
if reason and reason.get('$type') == 'app.bsky.feed.defs#reasonRepost': | ||
ret = { | ||
'id': obj.get('post', {}).get('viewer', {}).get('repost'), | ||
'objectType': 'activity', | ||
'verb': 'share', | ||
'object': ret, | ||
|
@@ -992,15 +992,155 @@ def get_activities_response(self, user_id=None, group_id=None, app_id=None, | |
resp = self.client.app.bsky.feed.getAuthorFeed({}, actor=handle, **params) | ||
posts = resp.get('feed', []) | ||
|
||
# TODO: inReplyTo | ||
ret = self.make_activities_base_response( | ||
trim_nulls(to_as1(post, type='app.bsky.feed.defs#feedViewPost', | ||
repo_did=self.did, repo_handle=handle)) | ||
for post in posts | ||
) | ||
ret['actor'] = { | ||
'id': self.did, | ||
'displayName': self.handle, | ||
'url': self.user_url(self.handle), | ||
if cache is None: | ||
# for convenience, throwaway object just for this method | ||
cache = {} | ||
|
||
activities = [] | ||
|
||
for post in posts: | ||
reason = post.get('reason') | ||
is_repost = reason and reason.get('$type') == 'app.bsky.feed.defs#reasonRepost' | ||
if is_repost and not include_shares: | ||
continue | ||
|
||
activity = self.postprocess_activity(self._post_to_activity(post)) | ||
activities.append(activity) | ||
obj = activity['object'] | ||
id = obj.get('id') | ||
tags = obj.setdefault('tags', []) | ||
|
||
if is_repost: | ||
# If it's a repost we're not interested in responses to it. | ||
continue | ||
bs_post = post.get('post') | ||
if bs_post and id: | ||
# Likes | ||
like_count = bs_post.get('likeCount') | ||
if fetch_likes and like_count and like_count != cache.get('ABL ' + id): | ||
likers = self.client.app.bsky.feed.getLikes({}, uri=bs_post.get('uri')) | ||
tags.extend(self._make_like(bs_post, l.get('actor')) for l in likers.get('likes')) | ||
cache['ABL ' + id] = count | ||
|
||
# Reposts | ||
repost_count = bs_post.get('repostCount') | ||
if fetch_shares and repost_count and repost_count != cache.get('ABRP ' + id): | ||
reposters = self.client.app.bsky.feed.getRepostedBy({}, uri=bs_post.get('uri')) | ||
tags.extend(self._make_share(bs_post, r) for r in reposters.get('repostedBy')) | ||
cache['ABRP ' + id] = count | ||
|
||
# Replies | ||
reply_count = bs_post.get('replyCount') | ||
if fetch_replies and reply_count and reply_count != cache.get('ABR ' + id): | ||
replies = self._get_replies(bs_post.get('uri')) | ||
replies = [to_as1(reply, 'app.bsky.feed.defs#threadViewPost') for reply in replies] | ||
for r in replies: | ||
r['id'] = self.tag_uri(r['id']) | ||
obj['replies'] = { | ||
'items': replies, | ||
} | ||
cache['ABR ' + id] = count | ||
|
||
resp = self.make_activities_base_response(util.trim_nulls(activities)) | ||
return resp | ||
|
||
def get_comment(self, comment_id, **kwargs): | ||
"""Fetches and returns a comment. | ||
|
||
Args: | ||
comment_id: string status id | ||
**kwargs: unused | ||
|
||
Returns: dict, ActivityStreams object | ||
|
||
Raises: | ||
:class:`ValueError`: if comment_id is invalid | ||
""" | ||
post_thread = self.client.app.bsky.feed.getPostThread({}, uri=comment_id, depth=1) | ||
obj = to_as1(post_thread.get('thread'), 'app.bsky.feed.defs#threadViewPost') | ||
return obj | ||
|
||
def _post_to_activity(self, post): | ||
"""Converts a post to an activity. | ||
|
||
Args: | ||
post: Bluesky post app.bluesky.feed.defs#feedViewPost | ||
|
||
Returns: AS1 activity | ||
""" | ||
obj = to_as1(post, type='app.bsky.feed.defs#feedViewPost') | ||
if obj.get('objectType') == 'activity': | ||
return obj | ||
return { | ||
'id': obj.get('id'), | ||
'verb': 'post', | ||
'actor': obj.get('author'), | ||
'object': obj, | ||
'objectType': 'activity', | ||
'context': {'inReplyTo': obj.get('inReplyTo')}, | ||
} | ||
|
||
def _make_like(self, post, actor): | ||
return self._make_like_or_share(post, actor, 'like') | ||
|
||
def _make_share(self, post, actor): | ||
return self._make_like_or_share(post, actor, 'share') | ||
|
||
def _make_like_or_share(self, post, actor, verb): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now have like/share support in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm yeah I might leave this for now just until I untangle what we need to do with actors/IDs/tags and whether that stuff ought to live in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely! Yeah I think |
||
"""Generates and returns a ActivityStreams like object. | ||
|
||
Args: | ||
post: dict, Bluesky app.bsky.feed.defs#feedViewPost | ||
actor: dict, Bluesky app.bsky.actor.defs#profileView | ||
verb: string, 'like' or 'share' | ||
|
||
Returns: dict, AS1 like activity | ||
""" | ||
assert verb in ('like', 'share') | ||
label = 'liked' if verb == 'like' else 'reposted' | ||
url = at_uri_to_web_url(post.get('uri'), post.get('author').get('handle')) | ||
actor_id = actor.get('did') | ||
author = to_as1(actor, 'app.bsky.actor.defs#profileView') | ||
author['id'] = self.tag_uri(author['id']) | ||
return { | ||
'id': self.tag_uri(f"{post.get('uri')}_{label}_by_{actor_id}"), | ||
'url': url, | ||
'objectType': 'activity', | ||
'verb': verb, | ||
'object': {'url': url}, | ||
'author': author, | ||
} | ||
|
||
def _get_replies(self, uri): | ||
""" | ||
Gets the replies to a specific post and returns them | ||
in ascending order of creation. Does not include the original post. | ||
|
||
Args: | ||
uri: string, post uri | ||
|
||
Returns: list, Bluesky app.bsky.feed.defs#threadViewPost | ||
""" | ||
ret = [] | ||
resp = self.client.app.bsky.feed.getPostThread({}, uri=uri) | ||
thread = resp.get('thread') | ||
if thread: | ||
ret = self._recurse_replies(thread) | ||
return sorted(ret, key = lambda thread: thread.get('post', {}).get('record', {}).get('createdAt')) | ||
|
||
# TODO this ought to take a depth limit. | ||
def _recurse_replies(self, thread): | ||
JoelOtter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
Recurses through a Bluesky app.bsky.feed.defs#threadViewPost | ||
and returns its replies as a list. | ||
|
||
Args: | ||
thread: dict, Bluesky app.bsky.feed.defs#threadViewPost | ||
|
||
Returns: list, Bluesky app.bsky.feed.defs#threadViewPost | ||
""" | ||
ret = [] | ||
for r in thread.get('replies', []): | ||
ret += [r] | ||
ret += self._recurse_replies(r) | ||
return ret |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to drop this, it's mostly vestigial, we don't really depend on it in Bridgy or anywhere else any more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which bit, sorry? The non-activity case? What should I do instead, just replace the whole method with
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no, just the
context
field!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, gotcha - done :)