def _post_to_outbox(self, obj_id: str, activity: ObjectType, recipients: List[str]) -> None: if isinstance(self._data['object'], str): # Put the object in the cache OBJECT_SERVICE.get( self._data['object'], reload_cache=True, part_of_stream=True, announce_published=self._data['published'], ) obj = self.get_object() DB.inbox.update_one({'activity.object.id': obj.id}, {'$set': {'meta.boosted': obj_id}})
def _user_api_get_note(from_outbox: bool = False): oid = _user_api_arg('id') note = activitypub.parse_activity(OBJECT_SERVICE.get(oid), expected=ActivityType.NOTE) if from_outbox and not note.id.startswith(ID): raise NotFromOutboxError( f'cannot delete {note.id}, id must be owned by the server') return note
def new(): reply_id = None content = '' if request.args.get('reply'): reply = activitypub.parse_activity( OBJECT_SERVICE.get(request.args.get('reply'))) reply_id = reply.id actor = reply.get_actor() domain = urlparse(actor.id).netloc content = f'@{actor.preferredUsername}@{domain} ' return render_template('new.html', reply=reply_id, content=content)
def index(): print(request.headers.get('Accept')) if is_api_request(): return jsonify(**ME) # FIXME(tsileo): implements pagination, also for the followers/following page limit = 50 q = { 'type': 'Create', 'activity.object.type': 'Note', 'meta.deleted': False, } c = request.args.get('cursor') if c: q['_id'] = {'$lt': ObjectId(c)} outbox_data = list( DB.outbox.find({ '$or': [q, { 'type': 'Announce', 'meta.undo': False }] }, limit=limit).sort('_id', -1)) cursor = None if outbox_data and len(outbox_data) == limit: cursor = str(outbox_data[-1]['_id']) for data in outbox_data: if data['type'] == 'Announce': print(data) if data['activity']['object'].startswith('http'): data['ref'] = { 'activity': { 'object': OBJECT_SERVICE.get(data['activity']['object']) }, 'meta': {} } return render_template( 'index.html', me=ME, notes=DB.inbox.find({ 'type': 'Create', 'activity.object.type': 'Note', 'meta.deleted': False }).count(), followers=DB.followers.count(), following=DB.following.count(), outbox_data=outbox_data, cursor=cursor, )
def get_object(self) -> 'BaseActivity': if self.__obj: return self.__obj if isinstance(self._data['object'], dict): p = parse_activity(self._data['object']) else: if self.ACTIVITY_TYPE == ActivityType.FOLLOW: p = Person(**ACTOR_SERVICE.get(self._data['object'])) else: obj = OBJECT_SERVICE.get(self._data['object']) if ActivityType(obj.get('type')) not in self.ALLOWED_OBJECT_TYPES: raise UnexpectedActivityTypeError(f'invalid object type {obj.get("type")}') p = parse_activity(obj) self.__obj: BaseActivity = p return p
def inbox(): if request.method == 'GET': if not is_api_request(): abort(404) try: _api_required() except BadSignature: abort(404) return jsonify(**activitypub.build_ordered_collection( DB.inbox, q={'meta.deleted': False}, cursor=request.args.get('cursor'), map_func=lambda doc: doc['activity'], )) data = request.get_json(force=True) logger.debug(f'req_headers={request.headers}') logger.debug(f'raw_data={data}') try: if not verify_request(ACTOR_SERVICE): raise Exception('failed to verify request') except Exception: logger.exception( 'failed to verify request, trying to verify the payload by fetching the remote' ) try: data = OBJECT_SERVICE.get(data['id']) except Exception: logger.exception(f'failed to fetch remote id at {data["id"]}') return Response( status=422, headers={'Content-Type': 'application/json'}, response=json.dumps({ 'error': 'failed to verify request (using HTTP signatures or fetching the IRI)' }), ) activity = activitypub.parse_activity(data) logger.debug(f'inbox activity={activity}/{data}') activity.process_from_inbox() return Response(status=201, )
def _process_from_inbox(self) -> None: if isinstance(self._data['object'], str) and not self._data['object'].startswith('http'): # TODO(tsileo): actually drop it without storing it and better logging, also move the check somewhere else logger.warn( f'received an Annouce referencing an OStatus notice ({self._data["object"]}), dropping the message' ) return # Save/cache the object, and make it part of the stream so we can fetch it if isinstance(self._data['object'], str): raw_obj = OBJECT_SERVICE.get( self._data['object'], reload_cache=True, part_of_stream=True, announce_published=self._data['published'], ) obj = parse_activity(raw_obj) else: obj = self.get_object() DB.outbox.update_one({'activity.object.id': obj.id}, { '$inc': {'meta.count_boost': 1}, })
def api_new_note(): source = _user_api_arg('content') if not source: raise ValueError('missing content') _reply, reply = None, None try: _reply = _user_api_arg('reply') except ValueError: pass content, tags = parse_markdown(source) to = request.args.get('to') cc = [ID + '/followers'] if _reply: reply = activitypub.parse_activity(OBJECT_SERVICE.get(_reply)) cc.append(reply.attributedTo) for tag in tags: if tag['type'] == 'Mention': cc.append(tag['href']) note = activitypub.Note(cc=cc, to=[to if to else config.AS_PUBLIC], content=content, tag=tags, source={ 'mediaType': 'text/markdown', 'content': source }, inReplyTo=reply.id if reply else None) create = note.build_create() create.post_to_outbox() return _user_api_response(activity=create.id)
def _get_actual_object(self) -> BaseActivity: obj = self.get_object() if obj.type_enum == ActivityType.TOMBSTONE: obj = parse_activity(OBJECT_SERVICE.get(obj.id)) return obj