123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- import celery
- import json
- import rdflib
- import requests
- import requests_http_signature
- from .. import activitypub
- from .. import feeds
- from .. import model
- from .. import settings
- from . import broker_url
- from . import broker
- from . import database_session
- log = celery.utils.log.get_task_logger(__name__)
- log.setLevel(settings.LOG_LEVEL)
- # The following is a decorator that accepts a dictionary as input and adds it to
- # _PATTERNS_ together with the decorated function. The dictionary is used as a
- # pattern for matching incoming Activities. If an incoming Activity matches one
- # of the patters in _PATTERNS_, then the corresponding function is executed.
- _PATTERNS_ = []
- def pattern(activity_pattern):
- def closure(func):
-
- def decorator(*args, **kwargs):
- func(*args, **kwargs)
-
- global _PATTERNS_
- _PATTERNS_.append((activity_pattern, decorator))
-
- return decorator
- return closure
- @broker.task
- def perform(actor_uri, activity):
- """
- This task is responsible for accepting an incoming Activity after it's been
- validated (see tasks.delivery) and decide what to do with it.
-
- :param actor_uri: URI of the Actor that has received the Activity
- :param activity: the Activity that was sent to the Actor
- """
-
- activity = activitypub.Activity(activity)
- activity.node('actor')
- activity.node('object')
-
- with database_session() as database:
-
- # Recreate the actor class from its URI
- actor = model.from_uri(database, actor_uri)
-
- if not actor:
- log.debug('Actor {} doesn\'t exist. Ignoring incoming Activity.'.format(actor_uri))
- return
-
- for activity_pattern, function in _PATTERNS_:
- if activity.match(activity_pattern):
- return function(database, actor, activity)
-
- log.debug('Activity {} did not match any pattern. Ignoring incoming Activity.'.format(activity['id']))
- @pattern({
- 'type': 'Follow',
- 'id': {},
- 'actor': {
- 'id': {}
- },
- 'object': {
- # 'type': 'Person',
- 'id': {}
- },
- })
- def follow(database, actor, activity):
-
- log.debug('Handling incoming Follow Activity.')
-
- # A Follow request should be sent to the right Actor
- if activity['object']['id'] != actor.local_uri:
- log.debug('Ignoring Activity {}. Actor {} has received a Follow request addressed to Actor {}' \
- .format(activity['id'], actor.local_uri, activity['object']['id']))
- return
-
- # Check if the local actor is already following the remote actor
- if database.query(
- database.query(model.Collection) \
- .filter(model.Collection.uri == actor.following_uri) \
- .filter(model.Collection.item == activity['actor']['id']) \
- .exists()
- ).scalar():
-
- log.info('Actor {} is already following {}. Ignoring Follow request.'.format(actor.local_uri, activity['actor']['id']))
- return
-
- # Add the remote actor to our followers collection
- database.add(model.Collection(uri = actor.followers_uri,
- item = activity['actor']['id']))
-
- # Add a feed
- database.add(model.Feed(
- actor_uri = actor.local_uri,
- content = json.dumps(feeds.follow(activity['actor'], actor.jsonld))
- ))
-
- # Automatically accept Follow requests and add the remote actor to the
- # following collection
- database.add(model.Collection(uri = actor.following_uri,
- item = activity['actor']['id']))
-
- # Add a feed
- database.add(model.Feed(
- actor_uri = actor.local_uri,
- content = json.dumps(feeds.follow(actor.jsonld, activity['actor']))
- ))
-
- # Cache a copy of the remote actor for quick lookups
- database.merge(model.Resource(
- uri = activity['actor']['id'],
- document = json.dumps(activity['actor'])))
-
- # Commit before sending the Accept Activity
- database.commit()
-
- # Create and send Accept Activity
- activitypub.Accept(actor = actor.uri,
- object = activity['id']) \
- .distribute()
- @pattern({
- 'type': 'Accept',
- 'id': {},
- 'actor': {
- 'id': {}
- },
- 'object': {
- 'type': 'Follow',
- 'id': {},
- 'actor': {},
- 'object': {}
- },
- })
- def accept_follow(database, actor, activity):
- """
- Accept of a Follow request.
- """
-
- log.debug('Handling incoming Accept(Follow) Activity.')
-
- follow_activity = activity['object']
- follow_activity.node('actor')
- follow_activity.node('object')
-
- if actor.local_uri == follow_activity['actor']['id'] \
- and follow_activity['object']['id'] == activity['actor']['id']:
-
- log.debug('{} has accepted follow request from {}'.format(
- activity['actor']['id'], actor.local_uri))
-
- # Now that the request was accepted, we add the actor to the "followers" list
- database.add(model.Collection(uri = actor.followers_uri,
- item = activity['actor']['id']))
-
- # Add a feed
- database.add(model.Feed(
- actor_uri = actor.local_uri,
- content = json.dumps(feeds.follow(activity['actor'], actor.jsonld))
- ))
-
- database.commit()
|