kopia lustrzana https://github.com/snarfed/bridgy-fed
when someone follows a bot user to opt in, have the bot user follow them back
fixes #999. this makes sure we start receiving posts from AP users immediately, instead of waiting until a Bluesky opted in user follows them.in-reply-to-bridged
rodzic
9765227aa0
commit
4da00192b0
30
protocol.py
30
protocol.py
|
@ -411,7 +411,7 @@ class Protocol:
|
|||
Returns:
|
||||
str:
|
||||
"""
|
||||
return f'{cls.ABBREV}{SUPERDOMAIN}'
|
||||
return f'{cls.ABBREV}{common.SUPERDOMAIN}'
|
||||
|
||||
@classmethod
|
||||
def create_for(cls, user):
|
||||
|
@ -818,6 +818,7 @@ class Protocol:
|
|||
logger.info(f'got DM to {to_cc}: {content}')
|
||||
if content in ('yes', 'ok'):
|
||||
from_user.enable_protocol(proto)
|
||||
proto.bot_follow(from_user)
|
||||
elif content == 'no':
|
||||
from_user.disable_protocol(proto)
|
||||
return 'OK', 200
|
||||
|
@ -850,6 +851,7 @@ class Protocol:
|
|||
# follow of one of our protocol bot users; enable that protocol.
|
||||
# foll through so that we send an accept.
|
||||
from_user.enable_protocol(proto)
|
||||
proto.bot_follow(from_user)
|
||||
|
||||
from_cls.handle_follow(obj)
|
||||
|
||||
|
@ -965,6 +967,32 @@ class Protocol:
|
|||
)
|
||||
accept.put()
|
||||
|
||||
@classmethod
|
||||
def bot_follow(bot_cls, user):
|
||||
"""Follow a user from a protocol bot user.
|
||||
|
||||
...so that the protocol starts sending us their activities, if it needs
|
||||
a follow for that (eg ActivityPub).
|
||||
|
||||
Args:
|
||||
user (User)
|
||||
"""
|
||||
from web import Web
|
||||
bot = Web.get_by_id(bot_cls.bot_user_id())
|
||||
now = util.now().isoformat()
|
||||
logger.info(f'Following {user.key.id()} back from bot user {bot.key.id()}')
|
||||
|
||||
follow_back_id = f'https://{bot.key.id()}/#follow-back-{user.key.id()}-{now}'
|
||||
follow_back = Object(id=follow_back_id, source_protocol='web', our_as1={
|
||||
'objectType': 'activity',
|
||||
'verb': 'follow',
|
||||
'id': follow_back_id,
|
||||
'actor': bot.key.id(),
|
||||
'object': user.key.id(),
|
||||
})
|
||||
|
||||
user.send(follow_back, user.target_for(user.obj), from_user=bot)
|
||||
|
||||
@classmethod
|
||||
def handle_bare_object(cls, obj):
|
||||
"""If obj is a bare object, wraps it in a create or update activity.
|
||||
|
|
|
@ -872,6 +872,9 @@ class ActivityPubTest(TestCase):
|
|||
self.assertIsNone(Object.get_by_id(not_public['object']['id']))
|
||||
|
||||
def test_follow_bot_user_enables_protocol(self, _, mock_get, __):
|
||||
# bot user
|
||||
self.make_user('eefake.brid.gy', cls=Web)
|
||||
|
||||
user = self.make_user('https://mas.to/users/swentel', cls=ActivityPub,
|
||||
obj_as2=ACTOR)
|
||||
self.assertFalse(user.is_enabled(ExplicitEnableFake))
|
||||
|
@ -894,6 +897,9 @@ class ActivityPubTest(TestCase):
|
|||
self.assertTrue(user.is_enabled(ExplicitEnableFake))
|
||||
|
||||
def test_inbox_dm_yes_to_bot_user_enables_protocol(self, *mocks):
|
||||
# bot user
|
||||
self.make_user('eefake.brid.gy', cls=Web)
|
||||
|
||||
user = self.make_user(ACTOR['id'], cls=ActivityPub)
|
||||
self.assertFalse(user.is_enabled(ExplicitEnableFake))
|
||||
|
||||
|
|
|
@ -406,8 +406,19 @@ class IntegrationTests(TestCase):
|
|||
self.assertEqual(['app.bsky.actor.profile'], list(records.keys()))
|
||||
self.assertEqual(['self'], list(records['app.bsky.actor.profile'].keys()))
|
||||
|
||||
# bot user follows back
|
||||
args, kwargs = mock_post.call_args_list[1]
|
||||
self.assert_equals(('http://inst/inbox',), args)
|
||||
self.assert_equals({
|
||||
'type': 'Follow',
|
||||
'id': 'https://bsky.brid.gy/r/https://bsky.brid.gy/#follow-back-https://inst/alice-2022-01-02T03:04:05+00:00',
|
||||
'actor': 'https://bsky.brid.gy/bsky.brid.gy',
|
||||
'object': 'https://inst/alice',
|
||||
}, json_loads(kwargs['data']), ignore=['to', '@context'])
|
||||
|
||||
# accept user's follow
|
||||
args, kwargs = mock_post.call_args_list[2]
|
||||
self.assert_equals(('http://inst/inbox',), args)
|
||||
self.assert_equals({
|
||||
'type': 'Accept',
|
||||
'id': 'http://localhost/r/bsky.brid.gy/followers#accept-http://inst/follow',
|
||||
|
|
|
@ -1855,6 +1855,9 @@ class ProtocolReceiveTest(TestCase):
|
|||
}, obj.key.get().our_as1)
|
||||
|
||||
def test_follow_and_block_protocol_user_sets_enabled_protocols(self):
|
||||
# bot user
|
||||
self.make_user('fa.brid.gy', cls=Web)
|
||||
|
||||
follow = {
|
||||
'objectType': 'activity',
|
||||
'verb': 'follow',
|
||||
|
@ -1886,9 +1889,13 @@ class ProtocolReceiveTest(TestCase):
|
|||
self.assertEqual(['fake'], user.enabled_protocols)
|
||||
self.assertEqual(['eefake:user'], Fake.created_for)
|
||||
self.assertTrue(user.is_enabled(Fake))
|
||||
self.assertEqual([('fa.brid.gy/followers#accept-eefake:follow',
|
||||
'eefake:user:target')],
|
||||
ExplicitEnableFake.sent)
|
||||
|
||||
follow_back_id = 'https://fa.brid.gy/#follow-back-eefake:user-2022-01-02T03:04:05+00:00'
|
||||
self.assertEqual([
|
||||
# fa.brid.gy follows back
|
||||
(follow_back_id, 'eefake:user:target'),
|
||||
('fa.brid.gy/followers#accept-eefake:follow', 'eefake:user:target'),
|
||||
], ExplicitEnableFake.sent)
|
||||
|
||||
# another follow should be a noop
|
||||
follow['id'] += '2'
|
||||
|
@ -1908,6 +1915,9 @@ class ProtocolReceiveTest(TestCase):
|
|||
self.assertFalse(user.is_enabled(Fake))
|
||||
|
||||
def test_follow_bot_user_refreshes_profile(self):
|
||||
# bot user
|
||||
self.make_user('fa.brid.gy', cls=Web)
|
||||
|
||||
# store profile that's opted out
|
||||
user = self.make_user('eefake:user', cls=ExplicitEnableFake, obj_as1={
|
||||
'id': 'eefake:user',
|
||||
|
@ -1936,6 +1946,9 @@ class ProtocolReceiveTest(TestCase):
|
|||
self.assertEqual(['eefake:user'], ExplicitEnableFake.fetched)
|
||||
|
||||
def test_dm_no_yes_sets_enabled_protocols(self):
|
||||
# bot user
|
||||
self.make_user('fa.brid.gy', cls=Web)
|
||||
|
||||
dm = {
|
||||
'objectType': 'note',
|
||||
'id': 'eefake:dm',
|
||||
|
|
|
@ -94,6 +94,7 @@ class Fake(User, protocol.Protocol):
|
|||
def create_for(cls, user):
|
||||
assert not user.get_copy(cls)
|
||||
id = user.key.id()
|
||||
logger.info(f'{cls.__name__}.create_for {id}')
|
||||
cls.created_for.append(id)
|
||||
add(user.copies, Target(uri=ids.translate_user_id(id=id, from_=user, to=cls),
|
||||
protocol=cls.LABEL))
|
||||
|
@ -123,7 +124,7 @@ class Fake(User, protocol.Protocol):
|
|||
|
||||
@classmethod
|
||||
def send(cls, obj, url, from_user=None, orig_obj=None):
|
||||
logger.info(f'{cls.__name__}.send {url}')
|
||||
logger.info(f'{cls.__name__}.send {url} {obj.as1}')
|
||||
cls.sent.append((obj.key.id(), url))
|
||||
return True
|
||||
|
||||
|
@ -146,7 +147,8 @@ class Fake(User, protocol.Protocol):
|
|||
|
||||
@classmethod
|
||||
def target_for(cls, obj, shared=False):
|
||||
assert obj.source_protocol in (cls.LABEL, cls.ABBREV, 'ui', None)
|
||||
assert obj.source_protocol in (cls.LABEL, cls.ABBREV, 'ui', None), \
|
||||
obj.source_protocol
|
||||
return 'shared:target' if shared else f'{obj.key.id()}:target'
|
||||
|
||||
@classmethod
|
||||
|
|
Ładowanie…
Reference in New Issue