kopia lustrzana https://github.com/snarfed/bridgy-fed
move accept_follow from activitypub to protocol
rodzic
9cc8451182
commit
01768fd58e
|
@ -194,61 +194,6 @@ class ActivityPub(Protocol):
|
||||||
else:
|
else:
|
||||||
error('HTTP Signature verification failed', status=401)
|
error('HTTP Signature verification failed', status=401)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def accept_follow(cls, obj, user):
|
|
||||||
"""Replies to an AP Follow request with an Accept request.
|
|
||||||
|
|
||||||
TODO: move to Protocol
|
|
||||||
|
|
||||||
Args:
|
|
||||||
obj: :class:`Object`
|
|
||||||
user: :class:`User`
|
|
||||||
"""
|
|
||||||
logger.info('Replying to Follow with Accept')
|
|
||||||
|
|
||||||
followee = obj.as2.get('object')
|
|
||||||
followee_id = followee.get('id') if isinstance(followee, dict) else followee
|
|
||||||
follower = obj.as2.get('actor')
|
|
||||||
if not followee or not followee_id or not follower:
|
|
||||||
error(f'Follow activity requires object and actor. Got: {follow}')
|
|
||||||
|
|
||||||
inbox = follower.get('inbox')
|
|
||||||
follower_id = follower.get('id')
|
|
||||||
if not inbox or not follower_id:
|
|
||||||
error(f'Follow actor requires id and inbox. Got: {follower}')
|
|
||||||
|
|
||||||
# rendered mf2 HTML proxy pages (in render.py) fall back to redirecting to
|
|
||||||
# the follow's AS2 id field, but Mastodon's ids are URLs that don't load in
|
|
||||||
# browsers, eg https://jawns.club/ac33c547-ca6b-4351-80d5-d11a6879a7b0
|
|
||||||
# so, set a synthetic URL based on the follower's profile.
|
|
||||||
# https://github.com/snarfed/bridgy-fed/issues/336
|
|
||||||
follower_url = util.get_url(follower) or follower_id
|
|
||||||
followee_url = util.get_url(followee) or followee_id
|
|
||||||
obj.as2.setdefault('url', f'{follower_url}#followed-{followee_url}')
|
|
||||||
|
|
||||||
# store Follower
|
|
||||||
follower_obj = Follower.get_or_create(
|
|
||||||
dest=user.key.id(), src=follower_id, last_follow=obj.as2)
|
|
||||||
follower_obj.status = 'active'
|
|
||||||
follower_obj.put()
|
|
||||||
|
|
||||||
# send AP Accept
|
|
||||||
followee_actor_url = common.host_url(user.key.id())
|
|
||||||
accept = {
|
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
||||||
'id': util.tag_uri(common.PRIMARY_DOMAIN,
|
|
||||||
f'accept/{user.key.id()}/{obj.key.id()}'),
|
|
||||||
'type': 'Accept',
|
|
||||||
'actor': followee_actor_url,
|
|
||||||
'object': {
|
|
||||||
'type': 'Follow',
|
|
||||||
'actor': follower_id,
|
|
||||||
'object': followee_actor_url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cls.send(inbox, accept, user=user)
|
|
||||||
|
|
||||||
|
|
||||||
def signed_get(url, *, user=None, **kwargs):
|
def signed_get(url, *, user=None, **kwargs):
|
||||||
return signed_request(util.requests_get, url, user=user, **kwargs)
|
return signed_request(util.requests_get, url, user=user, **kwargs)
|
||||||
|
@ -545,8 +490,9 @@ def inbox(domain=None):
|
||||||
body = request.get_data(as_text=True)
|
body = request.get_data(as_text=True)
|
||||||
error(f"Couldn't parse body as non-empty JSON mapping: {body}", exc_info=True)
|
error(f"Couldn't parse body as non-empty JSON mapping: {body}", exc_info=True)
|
||||||
|
|
||||||
|
type = activity.get('type')
|
||||||
actor_id = as1.get_object(activity, 'actor').get('id')
|
actor_id = as1.get_object(activity, 'actor').get('id')
|
||||||
logger.info(f'Got {activity.get("type")} activity from {actor_id}: {json_dumps(activity, indent=2)}')
|
logger.info(f'Got {type} from {actor_id}: {json_dumps(activity, indent=2)}')
|
||||||
|
|
||||||
# load user
|
# load user
|
||||||
# TODO: store in g instead of passing around
|
# TODO: store in g instead of passing around
|
||||||
|
@ -562,10 +508,22 @@ def inbox(domain=None):
|
||||||
# follows, or other activity types, since Mastodon doesn't currently mark
|
# follows, or other activity types, since Mastodon doesn't currently mark
|
||||||
# those as explicitly public. Use as2's is_public instead of as1's because
|
# those as explicitly public. Use as2's is_public instead of as1's because
|
||||||
# as1's interprets unlisted as true.
|
# as1's interprets unlisted as true.
|
||||||
if activity.get('type') == 'Create' and not as2.is_public(activity):
|
if type == 'Create' and not as2.is_public(activity):
|
||||||
logger.info('Dropping non-public activity')
|
logger.info('Dropping non-public activity')
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
||||||
|
if type == 'Follow':
|
||||||
|
# rendered mf2 HTML proxy pages (in render.py) fall back to redirecting
|
||||||
|
# to the follow's AS2 id field, but Mastodon's Accept ids are URLs that
|
||||||
|
# don't load in browsers, eg:
|
||||||
|
# https://jawns.club/ac33c547-ca6b-4351-80d5-d11a6879a7b0
|
||||||
|
#
|
||||||
|
# so, set a synthetic URL based on the follower's profile.
|
||||||
|
# https://github.com/snarfed/bridgy-fed/issues/336
|
||||||
|
follower_url = redirect_unwrap(util.get_url(activity, 'actor'))
|
||||||
|
followee_url = redirect_unwrap(util.get_url(activity, 'object'))
|
||||||
|
activity.setdefault('url', f'{follower_url}#followed-{followee_url}')
|
||||||
|
|
||||||
return ActivityPub.receive(activity.get('id'), user=user,
|
return ActivityPub.receive(activity.get('id'), user=user,
|
||||||
as2=redirect_unwrap(activity))
|
as2=redirect_unwrap(activity))
|
||||||
|
|
||||||
|
|
51
protocol.py
51
protocol.py
|
@ -12,6 +12,7 @@ from common import error
|
||||||
# import module instead of individual classes to avoid circular import
|
# import module instead of individual classes to avoid circular import
|
||||||
import models
|
import models
|
||||||
from oauth_dropins.webutil import util, webmention
|
from oauth_dropins.webutil import util, webmention
|
||||||
|
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||||
|
|
||||||
SUPPORTED_TYPES = (
|
SUPPORTED_TYPES = (
|
||||||
'accept',
|
'accept',
|
||||||
|
@ -114,6 +115,8 @@ class Protocol:
|
||||||
obj.populate(source_protocol=cls.LABEL, **props)
|
obj.populate(source_protocol=cls.LABEL, **props)
|
||||||
obj.put()
|
obj.put()
|
||||||
|
|
||||||
|
logging.info(f'Got AS1: {json_dumps(obj.as1, indent=2)}')
|
||||||
|
|
||||||
if obj.type not in SUPPORTED_TYPES:
|
if obj.type not in SUPPORTED_TYPES:
|
||||||
error(f'Sorry, {obj.type} activities are not supported yet.', status=501)
|
error(f'Sorry, {obj.type} activities are not supported yet.', status=501)
|
||||||
|
|
||||||
|
@ -193,7 +196,7 @@ class Protocol:
|
||||||
inner_obj = obj.as2['object'] = cls.get_object(inner_obj_id, user=user).as2
|
inner_obj = obj.as2['object'] = cls.get_object(inner_obj_id, user=user).as2
|
||||||
|
|
||||||
if obj.type == 'follow':
|
if obj.type == 'follow':
|
||||||
resp = cls.accept_follow(obj, user)
|
cls.accept_follow(obj, user=user)
|
||||||
|
|
||||||
# send webmentions to each target
|
# send webmentions to each target
|
||||||
send_webmentions(obj, proxy=True)
|
send_webmentions(obj, proxy=True)
|
||||||
|
@ -215,6 +218,52 @@ class Protocol:
|
||||||
obj.put()
|
obj.put()
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def accept_follow(cls, obj, *, user=None):
|
||||||
|
"""Replies to an AP Follow request with an Accept request.
|
||||||
|
|
||||||
|
TODO: move to Protocol
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj: :class:`Object`
|
||||||
|
user: :class:`User`
|
||||||
|
"""
|
||||||
|
logger.info('Replying to Follow with Accept')
|
||||||
|
|
||||||
|
followee = as1.get_object(obj.as1)
|
||||||
|
followee_id = followee.get('id')
|
||||||
|
follower = as1.get_object(obj.as1, 'actor')
|
||||||
|
if not followee or not followee_id or not follower:
|
||||||
|
error(f'Follow activity requires object and actor. Got: {follow}')
|
||||||
|
|
||||||
|
inbox = follower.get('inbox')
|
||||||
|
follower_id = follower.get('id')
|
||||||
|
if not inbox or not follower_id:
|
||||||
|
error(f'Follow actor requires id and inbox. Got: {follower}')
|
||||||
|
|
||||||
|
# store Follower
|
||||||
|
follower_obj = models.Follower.get_or_create(
|
||||||
|
dest=user.key.id(), src=follower_id, last_follow=obj.as2)
|
||||||
|
follower_obj.status = 'active'
|
||||||
|
follower_obj.put()
|
||||||
|
|
||||||
|
# send AP Accept
|
||||||
|
followee_actor_url = common.host_url(user.key.id())
|
||||||
|
accept = {
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
'id': util.tag_uri(common.PRIMARY_DOMAIN,
|
||||||
|
f'accept/{user.key.id()}/{obj.key.id()}'),
|
||||||
|
'type': 'Accept',
|
||||||
|
'actor': followee_actor_url,
|
||||||
|
'object': {
|
||||||
|
'type': 'Follow',
|
||||||
|
'actor': follower_id,
|
||||||
|
'object': followee_actor_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cls.send(inbox, accept, user=user)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@cached(LRUCache(1000), key=lambda cls, id, user=None: util.fragmentless(id),
|
@cached(LRUCache(1000), key=lambda cls, id, user=None: util.fragmentless(id),
|
||||||
lock=threading.Lock())
|
lock=threading.Lock())
|
||||||
|
|
Ładowanie…
Reference in New Issue