kopia lustrzana https://github.com/snarfed/bridgy-fed
rodzic
08d8c163e5
commit
54881ea707
|
@ -21,7 +21,6 @@ SUPPORTED_TYPES = (
|
|||
'article',
|
||||
'audio',
|
||||
'comment',
|
||||
'create',
|
||||
'delete',
|
||||
'follow',
|
||||
'image',
|
||||
|
@ -360,7 +359,7 @@ class Protocol:
|
|||
inner_obj_as1 = as1.get_object(obj.as1)
|
||||
inner_obj_id = inner_obj_as1.get('id')
|
||||
inner_obj = None
|
||||
if (obj.type in ('post', 'create', 'update')
|
||||
if (obj.type in ('post', 'update')
|
||||
and inner_obj_as1.keys() > set(['id'])):
|
||||
inner_obj = Object.get_or_insert(inner_obj_id)
|
||||
inner_obj.populate(our_as1=inner_obj_as1,
|
||||
|
@ -448,7 +447,7 @@ class Protocol:
|
|||
# deliver original posts and reposts to followers
|
||||
is_reply = (obj.type == 'comment' or
|
||||
(inner_obj_as1 and inner_obj_as1.get('inReplyTo')))
|
||||
if ((obj.type == 'share' or obj.type in ('create', 'post') and not is_reply)
|
||||
if ((obj.type == 'share' or obj.type in ('post', 'update') and not is_reply)
|
||||
and actor_id):
|
||||
logger.info(f'Delivering to followers of {actor_id}')
|
||||
for f in Follower.query(Follower.to == from_cls.key_for(actor_id),
|
||||
|
@ -542,7 +541,7 @@ class Protocol:
|
|||
inner_obj = as1.get_object(obj.as1)
|
||||
obj_url = util.get_url(inner_obj) or inner_obj.get('id')
|
||||
|
||||
if not source or obj.type in ('create', 'post', 'update'):
|
||||
if not source or obj.type in ('post', 'update'):
|
||||
source = obj_url
|
||||
if not source:
|
||||
error("Couldn't find source post URL")
|
||||
|
|
|
@ -42,13 +42,6 @@ class ProtocolTest(TestCase):
|
|||
PROTOCOLS.pop('greedy', None)
|
||||
super().tearDown()
|
||||
|
||||
@staticmethod
|
||||
def store_object(**kwargs):
|
||||
obj = Object(**kwargs)
|
||||
obj.put()
|
||||
del protocol.objects_cache[obj.key.id()]
|
||||
return obj
|
||||
|
||||
def test_protocols_global(self):
|
||||
self.assertEqual(Fake, PROTOCOLS['fake'])
|
||||
self.assertEqual(Web, PROTOCOLS['web'])
|
||||
|
@ -301,195 +294,185 @@ class ProtocolReceiveTest(TestCase):
|
|||
if 'our_as1' in props and field not in props:
|
||||
ignore.append(field)
|
||||
|
||||
return super().assert_object(id, delivered_protocol='fake',
|
||||
return super().assert_object(id, source_protocol='fake',
|
||||
delivered_protocol='fake',
|
||||
ignore=ignore, **props)
|
||||
|
||||
def make_followers(self):
|
||||
from_ = self.make_user(id, cls=ActivityPub, obj_as2=actor)
|
||||
Follower.get_or_create(to=g.user, from_=from_, **kwargs)
|
||||
Follower.get_or_create(to=g.user, from_=self.alice)
|
||||
Follower.get_or_create(to=g.user, from_=self.bob)
|
||||
Follower.get_or_create(to=g.user, from_=Fake(id='fake:eve'),
|
||||
status='inactive')
|
||||
|
||||
for id, kwargs, actor in [
|
||||
('fake:a', {}, None),
|
||||
('fake:b', {}, None),
|
||||
('https://mastodon/bbb', {}, {
|
||||
'publicInbox': 'https://public/inbox',
|
||||
'inbox': 'https://unused',
|
||||
}),
|
||||
('https://mastodon/ccc', {}, {
|
||||
'endpoints': {
|
||||
'sharedInbox': 'https://shared/inbox',
|
||||
},
|
||||
}),
|
||||
('https://mastodon/ddd', {}, {
|
||||
'inbox': 'https://inbox',
|
||||
}),
|
||||
('https://mastodon/ggg', {'status': 'inactive'}, {
|
||||
'inbox': 'https://unused/2',
|
||||
}),
|
||||
('https://mastodon/hhh', {}, {
|
||||
# dupe of ddd; should be de-duped
|
||||
'inbox': 'https://inbox',
|
||||
}),
|
||||
]:
|
||||
from_ = self.make_user(id, cls=ActivityPub, obj_as2=actor)
|
||||
Follower.get_or_create(to=g.user, from_=from_, **kwargs)
|
||||
def test_create_post(self):
|
||||
self.make_followers()
|
||||
|
||||
# def test_create_post(self):
|
||||
# mock_get.side_effect = [NOTE, ACTOR]
|
||||
# mock_post.return_value = requests_response('abc xyz')
|
||||
# self.make_followers()
|
||||
post_as1 = {
|
||||
'id': 'fake:post',
|
||||
'objectType': 'note',
|
||||
}
|
||||
create_as1 = {
|
||||
'id': 'fake:create',
|
||||
'objectType': 'activity',
|
||||
'verb': 'create',
|
||||
'actor': 'fake:user',
|
||||
'object': post_as1,
|
||||
}
|
||||
self.assertEqual('OK', Fake.receive('fake:create', our_as1=create_as1))
|
||||
|
||||
# got = self.client.post('/_ah/queue/webmention', data={
|
||||
# 'source': 'https://user.com/post',
|
||||
# 'target': 'https://fed.brid.gy/',
|
||||
# })
|
||||
# self.assertEqual(200, got.status_code)
|
||||
self.assert_object('fake:post',
|
||||
our_as1=post_as1,
|
||||
type='note',
|
||||
)
|
||||
obj = self.assert_object('fake:create',
|
||||
status='complete',
|
||||
our_as1=create_as1,
|
||||
delivered=['shared:target'],
|
||||
type='create',
|
||||
labels=['user', 'activity', 'feed'],
|
||||
users=[g.user.key, self.alice.key, self.bob.key],
|
||||
)
|
||||
|
||||
# mock_get.assert_has_calls((
|
||||
# self.req('https://user.com/post'),
|
||||
# ))
|
||||
# inboxes = ('https://inbox', 'https://public/inbox', 'https://shared/inbox')
|
||||
# self.assert_deliveries(mock_post, inboxes, CREATE_AS2)
|
||||
self.assertEqual([(obj, 'shared:target')], Fake.sent)
|
||||
|
||||
# self.assert_object('https://user.com/post',
|
||||
# users=[g.user.key],
|
||||
# mf2=NOTE_MF2,
|
||||
# type='note',
|
||||
# source_protocol='web',
|
||||
# )
|
||||
# self.assert_object('https://user.com/post#bridgy-fed-create',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# mf2=NOTE_MF2,
|
||||
# our_as1=CREATE_AS1,
|
||||
# delivered=inboxes,
|
||||
# type='post',
|
||||
# labels=['user', 'activity'],
|
||||
# )
|
||||
def test_create_post_bare_object(self):
|
||||
self.make_followers()
|
||||
|
||||
# def test_update_post(self):
|
||||
# mock_get.side_effect = [NOTE, ACTOR]
|
||||
# mock_post.return_value = requests_response('abc xyz')
|
||||
post_as1 = {
|
||||
'id': 'fake:post',
|
||||
'objectType': 'note',
|
||||
'author': 'fake:user',
|
||||
}
|
||||
self.assertEqual('OK', Fake.receive('fake:post', our_as1=post_as1))
|
||||
|
||||
# mf2 = copy.deepcopy(NOTE_MF2)
|
||||
# mf2['properties']['content'] = 'different'
|
||||
# Object(id='https://user.com/post', users=[g.user.key], mf2=mf2).put()
|
||||
self.assert_object('fake:post',
|
||||
our_as1=post_as1,
|
||||
type='note',
|
||||
users=[g.user.key],
|
||||
)
|
||||
|
||||
# self.make_followers()
|
||||
obj = self.assert_object('fake:post#bridgy-fed-create',
|
||||
status='complete',
|
||||
our_as1={
|
||||
'objectType': 'activity',
|
||||
'verb': 'post',
|
||||
'id': 'fake:post#bridgy-fed-create',
|
||||
'actor': 'http://bf/fake/fake:user/ap',
|
||||
'object': post_as1,
|
||||
'published': '2022-01-02T03:04:05+00:00',
|
||||
},
|
||||
delivered=['shared:target'],
|
||||
type='post',
|
||||
labels=['user', 'activity', 'feed'],
|
||||
users=[g.user.key, self.alice.key, self.bob.key],
|
||||
)
|
||||
|
||||
# got = self.client.post('/_ah/queue/webmention', data={
|
||||
# 'source': 'https://user.com/post',
|
||||
# 'target': 'https://fed.brid.gy/',
|
||||
# })
|
||||
# self.assertEqual(200, got.status_code)
|
||||
self.assertEqual([(obj, 'shared:target')], Fake.sent)
|
||||
|
||||
# mock_get.assert_has_calls((
|
||||
# self.req('https://user.com/post'),
|
||||
# ))
|
||||
# inboxes = ('https://inbox', 'https://public/inbox', 'https://shared/inbox')
|
||||
# self.assert_deliveries(mock_post, inboxes, UPDATE_AS2)
|
||||
def test_update_post(self):
|
||||
self.make_followers()
|
||||
|
||||
# update_as1 = {
|
||||
# 'objectType': 'activity',
|
||||
# 'verb': 'update',
|
||||
# 'id': 'https://user.com/post#bridgy-fed-update-2022-01-02T03:04:05+00:00',
|
||||
# 'actor': 'http://localhost/user.com',
|
||||
# 'object': {
|
||||
# **NOTE_AS1,
|
||||
# 'updated': '2022-01-02T03:04:05+00:00',
|
||||
# },
|
||||
# }
|
||||
# self.assert_object(
|
||||
# f'https://user.com/post#bridgy-fed-update-2022-01-02T03:04:05+00:00',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# mf2=NOTE_MF2,
|
||||
# our_as1=update_as1,
|
||||
# delivered=inboxes,
|
||||
# type='update',
|
||||
# labels=['user', 'activity'],
|
||||
# )
|
||||
post_as1 = {
|
||||
'id': 'fake:post',
|
||||
'objectType': 'note',
|
||||
}
|
||||
self.store_object(id='fake:post', our_as1=post_as1)
|
||||
|
||||
# def test_update_skip_if_content_unchanged(self):
|
||||
# """https://github.com/snarfed/bridgy-fed/issues/78"""
|
||||
# Object(id='https://user.com/reply', mf2=REPLY_MF2).put()
|
||||
update_as1 = {
|
||||
'id': 'fake:update',
|
||||
'objectType': 'activity',
|
||||
'verb': 'update',
|
||||
'actor': 'fake:user',
|
||||
'object': post_as1,
|
||||
}
|
||||
self.assertEqual('OK', Fake.receive('fake:update', our_as1=update_as1))
|
||||
|
||||
# mock_get.side_effect = ACTIVITYPUB_GETS
|
||||
self.assert_object('fake:post',
|
||||
our_as1=post_as1,
|
||||
type='note',
|
||||
)
|
||||
obj = self.assert_object('fake:update',
|
||||
status='complete',
|
||||
our_as1=update_as1,
|
||||
delivered=['shared:target'],
|
||||
type='update',
|
||||
labels=['user', 'activity'],
|
||||
users=[g.user.key],
|
||||
)
|
||||
|
||||
# got = self.client.post('/_ah/queue/webmention', data={
|
||||
# 'source': 'https://user.com/reply',
|
||||
# 'target': 'https://fed.brid.gy/',
|
||||
# })
|
||||
# self.assertEqual(204, got.status_code)
|
||||
# mock_post.assert_not_called()
|
||||
self.assertEqual([(obj, 'shared:target')], Fake.sent)
|
||||
|
||||
# def test_create_with_image(self):
|
||||
# create_html = NOTE_HTML.replace(
|
||||
# '</body>', '<img class="u-photo" src="http://im/age" />\n</body>')
|
||||
# mock_get.side_effect = [
|
||||
# requests_response(create_html, url='https://user.com/post',
|
||||
# content_type=CONTENT_TYPE_HTML),
|
||||
# ACTOR,
|
||||
# ]
|
||||
# mock_post.return_value = requests_response('abc xyz ')
|
||||
def test_update_post_bare_object(self):
|
||||
self.make_followers()
|
||||
|
||||
# Follower.get_or_create(
|
||||
# to=g.user,
|
||||
# from_=self.make_user('http://a', cls=ActivityPub,
|
||||
# obj_as2={'inbox': 'https://inbox'}))
|
||||
# got = self.client.post('/_ah/queue/webmention', data={
|
||||
# 'source': 'https://user.com/post',
|
||||
# 'target': 'https://fed.brid.gy/',
|
||||
# })
|
||||
# self.assertEqual(200, got.status_code)
|
||||
post_as1 = {
|
||||
'id': 'fake:post',
|
||||
'objectType': 'note',
|
||||
'content': 'first',
|
||||
}
|
||||
self.store_object(id='fake:post', our_as1=post_as1)
|
||||
|
||||
# self.assertEqual(('https://inbox',), mock_post.call_args[0])
|
||||
# create = copy.deepcopy(CREATE_AS2)
|
||||
# create['object'].update({
|
||||
# 'image': {'url': 'http://im/age', 'type': 'Image'},
|
||||
# 'attachment': [{'url': 'http://im/age', 'type': 'Image'}],
|
||||
# })
|
||||
# self.assert_equals(create, json_loads(mock_post.call_args[1]['data']))
|
||||
post_as1['content'] = 'second'
|
||||
self.assertEqual('OK', Fake.receive('fake:post', our_as1=post_as1))
|
||||
|
||||
# def test_create_reply(self):
|
||||
# mock_get.side_effect = ACTIVITYPUB_GETS
|
||||
# mock_post.return_value = requests_response('abc xyz')
|
||||
self.assert_object('fake:post',
|
||||
our_as1=post_as1,
|
||||
type='note',
|
||||
users=[g.user.key],
|
||||
labels=['user'],
|
||||
)
|
||||
|
||||
# got = self.client.post('/_ah/queue/webmention', data={
|
||||
# 'source': 'https://user.com/reply',
|
||||
# 'target': 'https://fed.brid.gy/',
|
||||
# })
|
||||
# self.assertEqual(200, got.status_code)
|
||||
update_id = 'fake:post#bridgy-fed-update-2022-01-02T03:04:05+00:00'
|
||||
obj = self.assert_object(update_id,
|
||||
status='complete',
|
||||
our_as1={
|
||||
'objectType': 'activity',
|
||||
'verb': 'post',
|
||||
'id': update_id,
|
||||
'actor': 'http://bf/fake/fake:user/ap',
|
||||
'object': post_as1,
|
||||
'published': '2022-01-02T03:04:05+00:00',
|
||||
},
|
||||
delivered=['shared:target'],
|
||||
type='update',
|
||||
labels=['user', 'activity', 'feed'],
|
||||
users=[g.user.key, self.alice.key, self.bob.key],
|
||||
)
|
||||
|
||||
# mock_get.assert_has_calls((
|
||||
# self.req('https://user.com/reply'),
|
||||
# self.as2_req('http://not/fediverse'),
|
||||
# self.req('http://not/fediverse'),
|
||||
# self.as2_req('https://mas.to/toot'),
|
||||
# self.as2_req('https://mas.to/author'),
|
||||
# ))
|
||||
self.assertEqual([(obj, 'shared:target')], Fake.sent)
|
||||
|
||||
# self.assert_deliveries(mock_post, ['https://mas.to/inbox'], AS2_CREATE)
|
||||
def test_create_reply(self):
|
||||
self.make_followers()
|
||||
|
||||
# self.assert_object('https://user.com/reply',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# mf2=REPLY_MF2,
|
||||
# as1=REPLY_AS1,
|
||||
# type='comment',
|
||||
# )
|
||||
# self.assert_object('https://user.com/reply#bridgy-fed-create',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# mf2=REPLY_MF2,
|
||||
# our_as1=CREATE_REPLY_AS1,
|
||||
# delivered=['https://mas.to/inbox'],
|
||||
# type='post',
|
||||
# labels=['user', 'activity'],
|
||||
# )
|
||||
reply_as1 = {
|
||||
'id': 'fake:reply',
|
||||
'objectType': 'note',
|
||||
'inReplyTo': 'fake:post',
|
||||
'author': 'fake:alice',
|
||||
}
|
||||
create_as1 = {
|
||||
'id': 'fake:create',
|
||||
'objectType': 'activity',
|
||||
'verb': 'create',
|
||||
'actor': 'fake:user',
|
||||
'object': reply_as1,
|
||||
}
|
||||
self.assertEqual('OK', Fake.receive('fake:create', our_as1=create_as1))
|
||||
|
||||
self.assert_object('fake:reply',
|
||||
our_as1=reply_as1,
|
||||
type='note',
|
||||
)
|
||||
obj = self.assert_object('fake:create',
|
||||
status='complete',
|
||||
our_as1=create_as1,
|
||||
delivered=['fake:post:target'],
|
||||
type='create',
|
||||
labels=['user', 'activity', 'notification'],
|
||||
users=[g.user.key, self.alice.key],
|
||||
)
|
||||
|
||||
self.assertEqual([(obj, 'fake:post:target')], Fake.sent)
|
||||
|
||||
# def test_update_reply(self):
|
||||
# self.make_followers()
|
||||
|
@ -511,31 +494,35 @@ class ProtocolReceiveTest(TestCase):
|
|||
# self.assertEqual(200, got.status_code)
|
||||
# self.assertEqual((AS2_UPDATE, 'https://mas.to/inbox'), Fake.sent)
|
||||
|
||||
@patch('requests.get')
|
||||
def test_receive_reply_not_feed_not_notification(self, mock_get):
|
||||
Follower.get_or_create(to=Fake.get_or_create(id=ACTOR['id']),
|
||||
from_=Fake.get_or_create(id='foo.com'))
|
||||
other_user = self.make_user('user.com', cls=Web)
|
||||
def test_receive_reply_not_feed_not_notification(self):
|
||||
Follower.get_or_create(to=g.user, from_=self.alice)
|
||||
|
||||
# user.com webmention discovery
|
||||
mock_get.return_value = requests_response('<html></html>')
|
||||
reply_as1 = {
|
||||
'objectType': 'note',
|
||||
'id': 'fake:reply',
|
||||
'author': 'fake:bob',
|
||||
'content': 'A ☕ reply',
|
||||
'inReplyTo': 'fake:post',
|
||||
}
|
||||
create_as1 = {
|
||||
'objectType': 'create',
|
||||
'id': 'fake:create',
|
||||
'object': reply_as1,
|
||||
}
|
||||
Fake.receive('fake:create', our_as1=create_as1)
|
||||
|
||||
Fake.receive(REPLY['id'], as2=REPLY)
|
||||
|
||||
self.assert_object(REPLY['id'],
|
||||
as2=REPLY,
|
||||
type='post',
|
||||
users=[other_user.key],
|
||||
self.assert_object('fake:create',
|
||||
our_as1=reply_as1,
|
||||
type='create',
|
||||
users=[g.user.key],
|
||||
# not feed since it's a reply
|
||||
# not notification since it doesn't involve the user
|
||||
labels=['activity'],
|
||||
labels=['user'],
|
||||
status='complete',
|
||||
source_protocol='fake',
|
||||
)
|
||||
self.assert_object(REPLY['object']['id'],
|
||||
our_as1=as2.to_as1(REPLY['object']),
|
||||
our_as1=create_as1,
|
||||
type='comment',
|
||||
source_protocol='fake',
|
||||
)
|
||||
|
||||
# def test_follow(self):
|
||||
|
@ -557,7 +544,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
# obj = self.assert_object('https://user.com/follow',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# mf2=FOLLOW_MF2,
|
||||
# as1=FOLLOW_AS1,
|
||||
|
@ -636,7 +622,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
follow_obj = self.assert_object('fake:follow',
|
||||
our_as1=follow_as1,
|
||||
type='follow',
|
||||
source_protocol='fake',
|
||||
labels=['user', 'activity'],
|
||||
status='complete',
|
||||
delivered=['fake:bob:target'],
|
||||
|
@ -671,76 +656,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
Follower.query().get(),
|
||||
ignore=['created', 'updated'])
|
||||
|
||||
# def test_follow_multiple(self):
|
||||
# html = FOLLOW_HTML.replace(
|
||||
# '<a class="u-follow-of" href="https://mas.to/mrs-foo"></a>',
|
||||
# '<a class="u-follow-of" href="https://mas.to/mrs-foo"></a> '
|
||||
# '<a class="u-follow-of" href="https://mas.to/mr-biff"></a>')
|
||||
|
||||
# mock_get.side_effect = [
|
||||
# requests_response(
|
||||
# html, url='https://user.com/follow',
|
||||
# content_type=CONTENT_TYPE_HTML),
|
||||
# self.as2_resp({
|
||||
# 'objectType': 'Person',
|
||||
# 'displayName': 'Mr. ☕ Biff',
|
||||
# 'id': 'https://mas.to/mr-biff',
|
||||
# 'inbox': 'https://mas.to/inbox/biff',
|
||||
# }),
|
||||
# ACTOR,
|
||||
# ]
|
||||
# mock_post.return_value = requests_response('unused')
|
||||
|
||||
# got = self.client.post('/_ah/queue/webmention', data={
|
||||
# 'source': 'https://user.com/follow',
|
||||
# 'target': 'https://fed.brid.gy/',
|
||||
# })
|
||||
# self.assertEqual(200, got.status_code)
|
||||
|
||||
# mock_get.assert_has_calls((
|
||||
# self.req('https://user.com/follow'),
|
||||
# self.as2_req('https://mas.to/mr-biff'),
|
||||
# self.as2_req('https://mas.to/mrs-foo'),
|
||||
# ))
|
||||
|
||||
# calls = mock_post.call_args_list
|
||||
# self.assertEqual('https://mas.to/inbox', calls[0][0][0])
|
||||
# self.assertEqual(FOLLOW_AS2, json_loads(calls[0][1]['data']))
|
||||
# self.assertEqual('https://mas.to/inbox/biff', calls[1][0][0])
|
||||
# self.assertEqual({
|
||||
# **FOLLOW_AS2,
|
||||
# 'object': 'https://mas.to/mr-biff',
|
||||
# }, json_loads(calls[1][1]['data']))
|
||||
|
||||
# mf2 = util.parse_mf2(html)['items'][0]
|
||||
# as1 = microformats2.json_to_object(mf2)
|
||||
# obj = self.assert_object('https://user.com/follow',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# mf2=mf2,
|
||||
# as1=as1,
|
||||
# delivered=['https://mas.to/inbox',
|
||||
# 'https://mas.to/inbox/biff'],
|
||||
# type='follow',
|
||||
# object_ids=['https://mas.to/mrs-foo',
|
||||
# 'https://mas.to/mr-biff'],
|
||||
# labels=['user', 'activity'],
|
||||
# )
|
||||
|
||||
# followers = Follower.query().fetch()
|
||||
# self.assertEqual(2, len(followers))
|
||||
|
||||
# self.assertEqual(g.user.key, followers[0].from_)
|
||||
# self.assertEqual(ActivityPub(id='https://mas.to/mr-biff').key,
|
||||
# followers[0].to)
|
||||
# self.assert_equals(obj.key, followers[0].follow)
|
||||
|
||||
# self.assertEqual(g.user.key, followers[1].from_)
|
||||
# self.assertEqual(ActivityPub(id='https://mas.to/mrs-foo').key,
|
||||
# followers[1].to)
|
||||
# self.assert_equals(obj.key, followers[1].follow)
|
||||
|
||||
# def test_repost(self):
|
||||
# self._test_repost(REPOST_HTML, REPOST_AS2)
|
||||
|
||||
|
@ -782,7 +697,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
# mf2 = util.parse_mf2(html)['items'][0]
|
||||
# self.assert_object('https://user.com/repost',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# mf2=mf2,
|
||||
# as1=microformats2.json_to_object(mf2),
|
||||
|
@ -823,7 +737,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
like_obj = self.assert_object('fake:like',
|
||||
users=[g.user.key],
|
||||
source_protocol='fake',
|
||||
status='complete',
|
||||
our_as1=like_as1,
|
||||
delivered=['fake:post:target'],
|
||||
|
@ -853,7 +766,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
# self.assert_object('https://user.com/like',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# mf2=LIKE_MF2,
|
||||
# as1=microformats2.json_to_object(LIKE_MF2),
|
||||
# type='like',
|
||||
|
@ -908,7 +820,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
# self.assert_object('https://user.com/post#bridgy-fed-delete',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='complete',
|
||||
# our_as1=DELETE_AS1,
|
||||
# delivered=inboxes,
|
||||
|
@ -966,7 +877,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
# self.assert_object('https://user.com/follow',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='web',
|
||||
# status='failed',
|
||||
# mf2=FOLLOW_MF2,
|
||||
# as1=FOLLOW_AS1,
|
||||
|
@ -1019,7 +929,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
# profile object
|
||||
self.assert_object('fake:user',
|
||||
source_protocol='fake',
|
||||
our_as1=update_as1['object'],
|
||||
type='person',
|
||||
)
|
||||
|
@ -1029,7 +938,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
update_obj = self.assert_object(
|
||||
id,
|
||||
users=[g.user.key],
|
||||
source_protocol='fake',
|
||||
status='complete',
|
||||
our_as1=update_as1,
|
||||
delivered=['shared:target'],
|
||||
|
@ -1065,7 +973,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
# expected_as2 = copy.deepcopy(MENTION_OBJECT)
|
||||
# expected_as2['tag'][1]['href'] = 'https://tar.get/'
|
||||
# self.assert_object(MENTION_OBJECT['id'],
|
||||
# source_protocol='activitypub',
|
||||
# as2=expected_as2,
|
||||
# type='note')
|
||||
|
||||
|
@ -1098,7 +1005,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
# expected_as2 = common.redirect_unwrap(mention)
|
||||
# self.assert_object(mention['id'],
|
||||
# users=[Web(id='tar.get').key],
|
||||
# source_protocol='activitypub',
|
||||
# status='complete',
|
||||
# as2=expected_as2,
|
||||
# delivered=['https://tar.get/'],
|
||||
|
@ -1148,7 +1054,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
|
||||
# obj = self.assert_object('fake:follow',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='fake',
|
||||
# status='complete',
|
||||
# our_as1=follow_as1,
|
||||
# delivered=['fake:user'],
|
||||
|
@ -1230,7 +1135,6 @@ class ProtocolReceiveTest(TestCase):
|
|||
# })
|
||||
# self.assert_object('https://mas.to/6d1a',
|
||||
# users=[g.user.key],
|
||||
# source_protocol='activitypub',
|
||||
# status='complete',
|
||||
# as2=follow,
|
||||
# delivered=['https://user.com/'],
|
||||
|
@ -1310,4 +1214,3 @@ class ProtocolReceiveTest(TestCase):
|
|||
self.assertEqual('inactive', follower.key.get().status)
|
||||
self.assertEqual('inactive', followee.key.get().status)
|
||||
self.assertEqual('active', other.key.get().status)
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class Fake(User, protocol.Protocol):
|
|||
@classmethod
|
||||
def fetch(cls, obj, **kwargs):
|
||||
id = obj.key.id()
|
||||
logger.info(f'Fake.load {id}')
|
||||
logger.info(f'Fake.fetch {id}')
|
||||
cls.fetched.append(id)
|
||||
|
||||
if id in cls.fetchable:
|
||||
|
@ -213,22 +213,31 @@ class TestCase(unittest.TestCase, testutil.Asserts):
|
|||
return user
|
||||
|
||||
def add_objects(self):
|
||||
with self.request_context:
|
||||
# post
|
||||
Object(id='a', domains=['user.com'], labels=['feed', 'notification'],
|
||||
as2=as2.from_as1(NOTE)).put()
|
||||
# different domain
|
||||
Object(id='b', domains=['nope.org'], labels=['feed', 'notification'],
|
||||
as2=as2.from_as1(MENTION)).put()
|
||||
# reply
|
||||
Object(id='d', domains=['user.com'], labels=['feed', 'notification'],
|
||||
as2=as2.from_as1(COMMENT)).put()
|
||||
# not feed/notif
|
||||
Object(id='e', domains=['user.com'],
|
||||
as2=as2.from_as1(NOTE)).put()
|
||||
# deleted
|
||||
Object(id='f', domains=['user.com'], labels=['feed', 'notification', 'user'],
|
||||
as2=as2.from_as1(NOTE), deleted=True).put()
|
||||
# post
|
||||
self.store_object(id='a', domains=['user.com'],
|
||||
labels=['feed', 'notification'],
|
||||
as2=as2.from_as1(NOTE))
|
||||
# different domain
|
||||
self.store_object(id='b', domains=['nope.org'],
|
||||
labels=['feed', 'notification'],
|
||||
as2=as2.from_as1(MENTION))
|
||||
# reply
|
||||
self.store_object(id='d', domains=['user.com'],
|
||||
labels=['feed', 'notification'],
|
||||
as2=as2.from_as1(COMMENT))
|
||||
# not feed/notif
|
||||
self.store_object(id='e', domains=['user.com'], as2=as2.from_as1(NOTE))
|
||||
# deleted
|
||||
self.store_object(id='f', domains=['user.com'],
|
||||
labels=['feed', 'notification', 'user'],
|
||||
as2=as2.from_as1(NOTE), deleted=True)
|
||||
|
||||
@staticmethod
|
||||
def store_object(**kwargs):
|
||||
obj = Object(**kwargs)
|
||||
obj.put()
|
||||
del protocol.objects_cache[obj.key.id()]
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def random_keys_and_cids(num):
|
||||
|
|
73
web.py
73
web.py
|
@ -498,7 +498,7 @@ def webmention_task():
|
|||
|
||||
# fetch source page
|
||||
try:
|
||||
obj = Web.load(source, remote=True, check_backlink=True)
|
||||
obj = Web.load(source, local=False, remote=True, check_backlink=True)
|
||||
except BadRequest as e:
|
||||
error(str(e.description), status=304)
|
||||
except HTTPError as e:
|
||||
|
@ -557,25 +557,18 @@ def webmention_task():
|
|||
|
||||
|
||||
def _deliver(obj):
|
||||
|
||||
targets = _targets(obj) # maps Target to Object or None
|
||||
|
||||
if not targets:
|
||||
add(obj.labels, 'user')
|
||||
obj.status = 'ignored'
|
||||
obj.put()
|
||||
return 'No targets', 204
|
||||
|
||||
err = None
|
||||
last_success = None
|
||||
log_data = True
|
||||
|
||||
# if this is a post, wrap it in a create or update activity, if necessary
|
||||
now = util.now().isoformat()
|
||||
if obj.type in ('note', 'article', 'comment'):
|
||||
# have we already seen this object? has it changed? or is it new?
|
||||
if obj.new is None and obj.changed is None:
|
||||
# check if we've seen this object, and if it's changed since then
|
||||
existing = Object.get_by_id(obj.key.id())
|
||||
obj.new = existing is not None
|
||||
obj.changed = existing and as1.activity_changed(existing.as1, obj.as1)
|
||||
|
||||
if obj.changed:
|
||||
logger.info(f'Content has changed from last time at {obj.updated}! Redelivering to all inboxes')
|
||||
updated = util.now().isoformat()
|
||||
id = f'{obj.key.id()}#bridgy-fed-update-{updated}'
|
||||
id = f'{obj.key.id()}#bridgy-fed-update-{now}'
|
||||
logger.info(f'Wrapping in update activity {id}')
|
||||
obj.put()
|
||||
update_as1 = {
|
||||
|
@ -589,12 +582,12 @@ def _deliver(obj):
|
|||
# https://docs.joinmastodon.org/spec/activitypub/#supported-activities-for-statuses
|
||||
# https://socialhub.activitypub.rocks/t/what-could-be-the-reason-that-my-update-activity-does-not-work/2893/4
|
||||
# https://github.com/mastodon/documentation/pull/1150
|
||||
'updated': updated,
|
||||
'updated': now,
|
||||
**obj.as1,
|
||||
},
|
||||
}
|
||||
obj = Object(id=id, mf2=obj.mf2, our_as1=update_as1, labels=['user'],
|
||||
users=[g.user.key], source_protocol='web')
|
||||
obj = Object(id=id, our_as1=update_as1, users=[g.user.key],
|
||||
source_protocol=obj.source_protocol)
|
||||
|
||||
elif obj.new or 'force' in request.form:
|
||||
logger.info(f'New Object {obj.key.id()}')
|
||||
|
@ -607,28 +600,43 @@ def _deliver(obj):
|
|||
'id': id,
|
||||
'actor': g.user.ap_actor(),
|
||||
'object': obj.as1,
|
||||
'published': now,
|
||||
}
|
||||
obj = Object(id=id, mf2=obj.mf2, our_as1=create_as1,
|
||||
users=[g.user.key], labels=['user'],
|
||||
source_protocol='web')
|
||||
source_protocol = obj.source_protocol
|
||||
obj = Object.get_or_insert(id)
|
||||
obj.populate(our_as1=create_as1, users=[g.user.key],
|
||||
source_protocol=source_protocol)
|
||||
|
||||
else:
|
||||
msg = f'{obj.key.id()} is unchanged, nothing to do'
|
||||
logger.info(msg)
|
||||
return msg, 204
|
||||
|
||||
# find delivery targets
|
||||
# sort targets so order is deterministic for tests, debugging, etc
|
||||
targets = _targets(obj) # maps Target to Object or None
|
||||
|
||||
if not targets:
|
||||
add(obj.labels, 'user')
|
||||
obj.status = 'ignored'
|
||||
obj.put()
|
||||
return 'No targets', 204
|
||||
|
||||
sorted_targets = sorted(targets.items(), key=lambda t: t[0].uri)
|
||||
obj.populate(
|
||||
status='in progress',
|
||||
labels=['user'],
|
||||
delivered=[],
|
||||
failed=[],
|
||||
undelivered=[t for t, _ in sorted_targets],
|
||||
)
|
||||
add(obj.labels, 'user')
|
||||
logger.info(f'Delivering to: {obj.undelivered}')
|
||||
|
||||
# make copy of undelivered because we modify it below.
|
||||
# sort targets so order is deterministic for tests.
|
||||
err = None
|
||||
last_success = None
|
||||
log_data = True
|
||||
|
||||
# deliver!
|
||||
for target, orig_obj in sorted_targets:
|
||||
assert target.uri
|
||||
protocol = PROTOCOLS[target.protocol]
|
||||
|
@ -713,6 +721,8 @@ def _targets(obj):
|
|||
logger.info(f"Couldn't load {id}")
|
||||
continue
|
||||
|
||||
# TODO: attach orig_obj's author/actor to obj.users
|
||||
|
||||
target = protocol.target_for(orig_obj)
|
||||
if target:
|
||||
targets[Target(protocol=protocol.LABEL, uri=target)] = orig_obj
|
||||
|
@ -722,14 +732,19 @@ def _targets(obj):
|
|||
# TODO: surface errors like this somehow?
|
||||
logger.error(f"Can't find delivery target for {id}")
|
||||
|
||||
if not targets or verb == 'share':
|
||||
# deliver to followers?
|
||||
inner_obj_as1 = as1.get_object(obj.as1)
|
||||
is_reply = (obj.type == 'comment' or
|
||||
(inner_obj_as1 and inner_obj_as1.get('inReplyTo')))
|
||||
if obj.type == 'share' or (obj.type in ('post', 'update') and not is_reply):
|
||||
logger.info('Delivering to followers')
|
||||
followers = Follower.query(Follower.to == g.user.key,
|
||||
Follower.status == 'active'
|
||||
).fetch()
|
||||
users = ndb.get_multi(f.from_ for f in followers)
|
||||
users = [u for u in users if u]
|
||||
users = [u for u in ndb.get_multi(f.from_ for f in followers) if u]
|
||||
User.load_multi(users)
|
||||
obj.users.extend(u.key for u in users)
|
||||
add(obj.labels, 'feed')
|
||||
|
||||
for user in users:
|
||||
# TODO: should we pass remote=False through here to Protocol.load?
|
||||
|
|
Ładowanie…
Reference in New Issue