ATProto: switch polling posts from getTimeline to getAuthorFeed

for #992
pull/1020/head
Ryan Barrett 2024-05-02 20:11:21 -07:00
rodzic 1bcd675ecd
commit 7b3fa6e82b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 36 dodań i 46 usunięć

Wyświetl plik

@ -653,42 +653,27 @@ def poll_notifications():
# URL route is registered in hub.py
def poll_posts():
"""Fetches and enqueueus new posts from the AppView for our users.
"""Fetches and enqueueus bridged Bluesky users' new posts from the AppView.
Uses the ``getTimeline`` endpoint, which is intended for end users. 🤷
Uses the ``getAuthorFeed`` endpoint, which is intended for clients. 🤷
TODO: unify with poll_notifications
"""
repos = {r.key.id(): r for r in AtpRepo.query()}
logger.info(f'Got {len(repos)} repos')
if not repos:
return 'Nothing to do ¯\_(ツ)_/¯', 204
users = itertools.chain(*(cls.query(cls.copies.uri.IN(list(repos)))
for cls in set(PROTOCOLS.values())
if cls and cls != ATProto))
# this client needs to be request-local because we set its service token
# below per user that we're polling
client = Client(f'https://{os.environ["APPVIEW_HOST"]}',
headers={'User-Agent': USER_AGENT})
for user in users:
if not user.is_enabled(ATProto):
logger.info(f'Skipping {user.key.id()}')
continue
logger.debug(f'Fetching posts for {user.key.id()}')
for user in ATProto.query(ATProto.enabled_protocols != None):
did = user.key.id()
logger.debug(f'Fetching posts for {did} {user.handle}')
# TODO: store and use cursor
# seenAt would be easier, but they don't support it yet
# https://github.com/bluesky-social/atproto/issues/1636
did = user.get_copy(ATProto)
repo = repos[did]
client.session['accessJwt'] = service_jwt(os.environ['APPVIEW_HOST'],
repo_did=did,
privkey=repo.signing_key)
resp = client.app.bsky.feed.getTimeline(limit=10)
resp = appview.app.bsky.feed.getAuthorFeed(
actor=did, filter='posts_no_replies', limit=10)
for item in resp['feed']:
# TODO: handle reposts once we have a URI for them
# https://github.com/bluesky-social/atproto/issues/1811
@ -700,10 +685,9 @@ def poll_posts():
# TODO: verify sig. skipping this for now because we're getting
# these from the AppView, which is trusted, specifically we expect
# the BGS and/or the AppView already checked sigs.
author_did = post['author']['did']
assert did == post['author']['did']
obj = Object.get_or_create(id=post['uri'], bsky=post['record'],
source_protocol=ATProto.ABBREV,
actor=author_did)
source_protocol=ATProto.ABBREV, actor=did)
if obj.status in ('complete', 'ignored'):
continue
@ -713,7 +697,6 @@ def poll_posts():
obj.add('feed', user.key)
obj.put()
common.create_task(queue='receive', obj=obj.key.urlsafe(),
authed_as=author_did)
common.create_task(queue='receive', obj=obj.key.urlsafe(), authed_as=did)
return 'OK'

Wyświetl plik

@ -1266,15 +1266,23 @@ class ATProtoTest(TestCase):
@patch.object(tasks_client, 'create_task', return_value=Task(name='my task'))
@patch('requests.get')
def test_poll_posts(self, mock_get, mock_create_task):
user_a = self.make_user(id='fake:user-a', cls=Fake,
copies=[Target(uri='did:plc:a', protocol='atproto')])
user_b = self.make_user(id='fake:user-b', cls=Fake,
copies=[Target(uri='did:plc:b', protocol='atproto')])
user_c = self.make_user(id='fake:user-c', cls=Fake,
copies=[Target(uri='did:plc:c', protocol='atproto')])
Repo.create(self.storage, 'did:plc:a', signing_key=ATPROTO_KEY)
Repo.create(self.storage, 'did:plc:b', signing_key=ATPROTO_KEY)
Repo.create(self.storage, 'did:plc:c', signing_key=ATPROTO_KEY)
for i in ['a', 'b', 'c', 'd']:
did = f'did:plc:{i}'
self.store_object(id=did, raw={
**DID_DOC,
'id': did,
})
user_a = self.make_user(
id='did:plc:a', cls=ATProto, enabled_protocols=['fake'],
copies=[Target(uri='fake:user-a', protocol='fake')])
user_b = self.make_user(id='did:plc:b', cls=ATProto) # no enabled protocols
user_c = self.make_user(
id='did:plc:c', cls=ATProto, enabled_protocols=['fake'],
copies=[Target(uri='fake:user-c', protocol='fake')])
user_d = self.make_user(
id='did:plc:d', cls=ATProto, enabled_protocols=['fake'],
copies=[Target(uri='fake:user-d', protocol='fake')])
post = {
'$type': 'app.bsky.feed.post',
@ -1288,7 +1296,7 @@ class ATProtoTest(TestCase):
'record': post,
'author': {
'$type': 'app.bsky.actor.defs#profileViewBasic',
'did': 'did:web:alice.com',
'did': 'did:plc:a',
'handle': 'alice.com',
},
}
@ -1330,25 +1338,24 @@ class ATProtoTest(TestCase):
resp = self.post('/queue/atproto-poll-posts', client=hub.app.test_client())
self.assertEqual(200, resp.status_code)
get_timeline = call(
'https://appview.local/xrpc/app.bsky.feed.getTimeline?limit=10',
get = [call(
f'https://api.bsky-sandbox.dev/xrpc/app.bsky.feed.getAuthorFeed?actor=did%3Aplc%3A{i}&filter=posts_no_replies&limit=10',
json=None, data=None,
headers={
'Content-Type': 'application/json',
'User-Agent': common.USER_AGENT,
'Authorization': ANY,
})
}) for i in ('a', 'c', 'd')]
self.assertEqual([
get_timeline,
get[0],
self.req('https://alice.com/.well-known/did.json'),
get_timeline,
get_timeline,
get[1],
get[2],
], mock_get.call_args_list)
post_obj = Object.get_by_id('at://did:web:alice.com/app.bsky.feed.post/123')
self.assertEqual(post, post_obj.bsky)
self.assert_task(mock_create_task, 'receive', '/queue/receive',
obj=post_obj.key.urlsafe(), authed_as='did:web:alice.com')
obj=post_obj.key.urlsafe(), authed_as='did:plc:a')
# TODO: https://github.com/snarfed/bridgy-fed/issues/728
# repost_obj = Object.get_by_id('at://did:plc:d/app.bsky.feed.post/456')