kopia lustrzana https://github.com/snarfed/bridgy-fed
Protocol.targets: if it's a reply to native post that's bridged, bridge the reply too
rodzic
79e5d2be07
commit
099a636ed5
2
ids.py
2
ids.py
|
@ -252,8 +252,10 @@ def translate_object_id(*, id, from_, to):
|
|||
if from_.LABEL in COPIES_PROTOCOLS or to.LABEL in COPIES_PROTOCOLS:
|
||||
if obj := from_.load(id, remote=False):
|
||||
if copy := obj.get_copy(to):
|
||||
util.d(f'copy {to.LABEL}:o:{from_.ABBREV}:{id}')
|
||||
return copy
|
||||
if orig := models.get_original(id):
|
||||
util.d(f'orig {to.LABEL}:o:{from_.ABBREV}:{id}')
|
||||
return orig.key.id()
|
||||
|
||||
match from_.LABEL, to.LABEL:
|
||||
|
|
114
protocol.py
114
protocol.py
|
@ -451,6 +451,9 @@ class Protocol:
|
|||
|
||||
To be implemented by subclasses.
|
||||
|
||||
NOTE: if this protocol's ``HAS_COPIES`` is True, and this method creates
|
||||
a copy and sends it, it *must* add that copy to :attr:`copies`!
|
||||
|
||||
Args:
|
||||
obj (models.Object): with activity to send
|
||||
url (str): destination URL to send to
|
||||
|
@ -1141,13 +1144,13 @@ class Protocol:
|
|||
for in_reply_to in in_reply_tos:
|
||||
if proto := Protocol.for_id(in_reply_to):
|
||||
if in_reply_to_obj := proto.load(in_reply_to):
|
||||
if proto.LABEL != obj.source_protocol:
|
||||
in_reply_to_protocols.add(proto._get_kind())
|
||||
if obj.source_protocol in (proto.LABEL, proto.ABBREV):
|
||||
for copy in in_reply_to_obj.copies:
|
||||
add(target_uris, copy.uri)
|
||||
in_reply_to_protocols.add(
|
||||
PROTOCOLS[copy.protocol]._get_kind())
|
||||
else:
|
||||
proto_labels = proto.DEFAULT_ENABLED_PROTOCOLS + tuple(
|
||||
c.protocol for c in in_reply_to_obj.copies)
|
||||
in_reply_to_protocols.update(PROTOCOLS[c.protocol]._get_kind()
|
||||
for c in in_reply_to_obj.copies)
|
||||
in_reply_to_protocols.add(proto._get_kind())
|
||||
|
||||
if reply_owner := as1.get_owner(in_reply_to_obj.as1):
|
||||
in_reply_to_owners.append(reply_owner)
|
||||
|
@ -1211,54 +1214,7 @@ class Protocol:
|
|||
return targets
|
||||
|
||||
if (obj.type in ('post', 'update', 'delete', 'share')
|
||||
and (not is_reply or (is_self_reply and in_reply_to_protocols))):
|
||||
logger.info(f'Delivering to followers of {user_key}')
|
||||
followers = Follower.query(Follower.to == user_key,
|
||||
Follower.status == 'active'
|
||||
).fetch()
|
||||
user_keys = [f.from_ for f in followers]
|
||||
if is_reply:
|
||||
user_keys = [k for k in user_keys if k.kind() in in_reply_to_protocols]
|
||||
users = [u for u in ndb.get_multi(user_keys) if u]
|
||||
User.load_multi(users)
|
||||
|
||||
# which object should we add to followers' feeds, if any
|
||||
feed_obj = None
|
||||
if obj.type == 'share':
|
||||
feed_obj = obj
|
||||
else:
|
||||
inner = as1.get_object(obj.as1)
|
||||
# don't add profile updates to feeds
|
||||
if not (obj.type == 'update'
|
||||
and inner.get('objectType') in as1.ACTOR_TYPES):
|
||||
inner_id = inner.get('id')
|
||||
if inner_id:
|
||||
feed_obj = cls.load(inner_id)
|
||||
|
||||
for user in users:
|
||||
if feed_obj:
|
||||
feed_obj.add('feed', user.key)
|
||||
|
||||
# TODO: should we pass remote=False through here to Protocol.load?
|
||||
target = user.target_for(user.obj, shared=True) if user.obj else None
|
||||
if not target:
|
||||
# TODO: surface errors like this somehow?
|
||||
logger.error(f'Follower {user.key} has no delivery target')
|
||||
continue
|
||||
|
||||
# normalize URL (lower case hostname, etc)
|
||||
# ...but preserve our PDS URL without trailing slash in path
|
||||
# https://atproto.com/specs/did#did-documents
|
||||
target = util.dedupe_urls([target], trailing_slash=False)[0]
|
||||
|
||||
# HACK: use last target object from above for reposts, which
|
||||
# has its resolved id
|
||||
targets[Target(protocol=user.LABEL, uri=target)] = \
|
||||
orig_obj if obj.as1.get('verb') == 'share' else None
|
||||
|
||||
if feed_obj:
|
||||
feed_obj.put()
|
||||
|
||||
and (not is_reply or in_reply_to_protocols)):
|
||||
# include ATProto if this user is enabled there.
|
||||
# TODO: abstract across protocols. maybe with this, below
|
||||
# targets.update({
|
||||
|
@ -1281,6 +1237,56 @@ class Protocol:
|
|||
Target(protocol=ATProto.LABEL, uri=ATProto.PDS_URL), None)
|
||||
logger.info(f'user has ATProto enabled, adding {ATProto.PDS_URL}')
|
||||
|
||||
if not is_reply or (is_self_reply and in_reply_to_protocols):
|
||||
logger.info(f'Delivering to followers of {user_key}')
|
||||
followers = Follower.query(Follower.to == user_key,
|
||||
Follower.status == 'active'
|
||||
).fetch()
|
||||
user_keys = [f.from_ for f in followers]
|
||||
if is_reply:
|
||||
user_keys = [k for k in user_keys
|
||||
if k.kind() in in_reply_to_protocols]
|
||||
users = [u for u in ndb.get_multi(user_keys) if u]
|
||||
User.load_multi(users)
|
||||
|
||||
# which object should we add to followers' feeds, if any
|
||||
feed_obj = None
|
||||
if obj.type == 'share':
|
||||
feed_obj = obj
|
||||
else:
|
||||
inner = as1.get_object(obj.as1)
|
||||
# don't add profile updates to feeds
|
||||
if not (obj.type == 'update'
|
||||
and inner.get('objectType') in as1.ACTOR_TYPES):
|
||||
inner_id = inner.get('id')
|
||||
if inner_id:
|
||||
feed_obj = cls.load(inner_id)
|
||||
|
||||
for user in users:
|
||||
if feed_obj:
|
||||
feed_obj.add('feed', user.key)
|
||||
|
||||
# TODO: should we pass remote=False through here to Protocol.load?
|
||||
target = (user.target_for(user.obj, shared=True)
|
||||
if user.obj else None)
|
||||
if not target:
|
||||
# TODO: surface errors like this somehow?
|
||||
logger.error(f'Follower {user.key} has no delivery target')
|
||||
continue
|
||||
|
||||
# normalize URL (lower case hostname, etc)
|
||||
# ...but preserve our PDS URL without trailing slash in path
|
||||
# https://atproto.com/specs/did#did-documents
|
||||
target = util.dedupe_urls([target], trailing_slash=False)[0]
|
||||
|
||||
# HACK: use last target object from above for reposts, which
|
||||
# has its resolved id
|
||||
targets[Target(protocol=user.LABEL, uri=target)] = \
|
||||
orig_obj if obj.as1.get('verb') == 'share' else None
|
||||
|
||||
if feed_obj:
|
||||
feed_obj.put()
|
||||
|
||||
# de-dupe targets, discard same-domain
|
||||
# maps string target URL to (Target, Object) tuple
|
||||
candidates = {t.uri: (t, obj) for t, obj in targets.items()}
|
||||
|
|
|
@ -956,6 +956,39 @@ class ProtocolReceiveTest(TestCase):
|
|||
self.assertEqual([(obj.key.id(), 'fake:post:target')], Fake.sent)
|
||||
self.assertEqual([(obj.key.id(), 'other:eve:target')], OtherFake.sent)
|
||||
|
||||
def test_create_reply_is_bridged_if_original_post_is_bridged(self):
|
||||
eve = self.make_user('fake:eve', cls=Fake, obj_id='fake:eve')
|
||||
self.store_object(id='fake:post', source_protocol='fake',
|
||||
copies=[Target(protocol='other', uri='other:post')],
|
||||
our_as1={
|
||||
'objectType': 'note',
|
||||
'id': 'fake:post',
|
||||
'author': 'fake:eve',
|
||||
})
|
||||
self.store_object(id='other:post', source_protocol='other',
|
||||
our_as1={
|
||||
'objectType': 'note',
|
||||
'id': 'other:post',
|
||||
'author': 'fake:eve',
|
||||
})
|
||||
|
||||
reply_as1 = {
|
||||
'id': 'fake:reply',
|
||||
'objectType': 'note',
|
||||
'inReplyTo': 'fake:post',
|
||||
'author': 'fake:user',
|
||||
}
|
||||
self.assertEqual(('OK', 202), Fake.receive_as1(reply_as1))
|
||||
|
||||
reply = self.assert_object('fake:reply', our_as1=reply_as1, type='note')
|
||||
self.assertEqual([('fake:reply#bridgy-fed-create', 'other:post:target')],
|
||||
OtherFake.sent)
|
||||
|
||||
create = Object.get_by_id('fake:reply#bridgy-fed-create')
|
||||
STATE https://github.com/snarfed/bridgy-fed/issues/1038\\\
|
||||
big problem is we're setting copies on the #create, not on the original object
|
||||
self.assertEqual([Target(protocol='other', uri='other:post')], create.copies)
|
||||
|
||||
def test_create_reply_isnt_bridged_if_original_isnt_bridged(self):
|
||||
eve = self.make_user('other:eve', cls=OtherFake, obj_id='other:eve')
|
||||
Follower.get_or_create(to=self.user, from_=eve)
|
||||
|
|
|
@ -126,6 +126,12 @@ class Fake(User, protocol.Protocol):
|
|||
def send(cls, obj, url, from_user=None, orig_obj=None):
|
||||
logger.info(f'{cls.__name__}.send {url} {obj.as1}')
|
||||
cls.sent.append((obj.key.id(), url))
|
||||
|
||||
copy_id = ids.translate_object_id(id=obj.key.id(),
|
||||
from_=PROTOCOLS[obj.source_protocol],
|
||||
to=cls)
|
||||
add(obj.copies, Target(uri=copy_id, protocol=cls.LABEL))
|
||||
obj.put()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
|
|
Ładowanie…
Reference in New Issue