def _meta(activity: ap.BaseActivity) -> _NewMeta: visibility = ap.get_visibility(activity) is_public = False if visibility in [ap.Visibility.PUBLIC, ap.Visibility.UNLISTED]: is_public = True object_id = None try: object_id = activity.get_object_id() except Exception: # TODO(tsileo): should be ValueError, but replies trigger a KeyError on object pass object_visibility = None if activity.has_type( [ap.ActivityType.CREATE, ap.ActivityType.ANNOUNCE, ap.ActivityType.LIKE] ): object_visibility = ap.get_visibility(activity.get_object()).name actor_id = activity.get_actor().id return { MetaKey.UNDO.value: False, MetaKey.DELETED.value: False, MetaKey.PUBLIC.value: is_public, MetaKey.SERVER.value: urlparse(activity.id).netloc, MetaKey.VISIBILITY.value: visibility.name, MetaKey.ACTOR_ID.value: actor_id, MetaKey.OBJECT_ID.value: object_id, MetaKey.OBJECT_VISIBILITY.value: object_visibility, MetaKey.POLL_ANSWER.value: False, MetaKey.PUBLISHED.value: activity.published if activity.published else now(), }
def api_like() -> _Response: note = _user_api_get_note() to: List[str] = [] cc: List[str] = [] note_visibility = ap.get_visibility(note) if note_visibility == ap.Visibility.PUBLIC: to = [ap.AS_PUBLIC] cc = [ID + "/followers", note.get_actor().id] elif note_visibility == ap.Visibility.UNLISTED: to = [ID + "/followers", note.get_actor().id] cc = [ap.AS_PUBLIC] else: to = [note.get_actor().id] like = ap.Like( object=note.id, actor=MY_PERSON.id, to=to, cc=cc, published=now(), context=new_context(note), ) like_id = post_to_outbox(like) return _user_api_response(activity=like_id)
def task_cache_actor() -> _Response: task = p.parse(flask.request) app.logger.info(f"task={task!r}") iri = task.payload["iri"] try: activity = ap.fetch_remote_activity(iri) app.logger.info(f"activity={activity!r}") # Reload the actor without caching (in case it got upated) actor = ap.fetch_remote_activity(activity.get_actor().id, no_cache=True) # Fetch the Open Grah metadata if it's a `Create` if activity.has_type(ap.ActivityType.CREATE): obj = activity.get_object() links = opengraph.links_from_note(obj.to_dict()) if links: Tasks.fetch_og_meta(iri) # Send Webmentions only if it's from the outbox, and public if (is_from_outbox(obj) and ap.get_visibility(obj) == ap.Visibility.PUBLIC): Tasks.send_webmentions(activity, links) if activity.has_type(ap.ActivityType.FOLLOW): if actor.id == config.ID: # It's a new following, cache the "object" (which is the actor we follow) DB.activities.update_one( by_remote_id(iri), upsert({ MetaKey.OBJECT: activity.get_object().to_dict(embed=True) }), ) # Cache the actor info update_cached_actor(actor) app.logger.info(f"actor cached for {iri}") if not activity.has_type( [ap.ActivityType.CREATE, ap.ActivityType.ANNOUNCE]): return "" if activity.get_object()._data.get( "attachment", []) or activity.get_object().has_type( ap.ActivityType.VIDEO): Tasks.cache_attachments(iri) except (ActivityGoneError, ActivityNotFoundError): DB.activities.update_one({"remote_id": iri}, {"$set": { "meta.deleted": True }}) app.logger.exception( f"flagging activity {iri} as deleted, no actor caching") except Exception as err: app.logger.exception(f"failed to cache actor for {iri}") raise TaskError() from err return ""
def admin_new() -> _Response: reply_id = None content = "" thread: List[Any] = [] print(request.args) default_visibility = None # ap.Visibility.PUBLIC if request.args.get("reply"): data = DB.activities.find_one( {"activity.object.id": request.args.get("reply")}) if data: reply = ap.parse_activity(data["activity"]) else: obj = ap.get_backend().fetch_iri(request.args.get("reply")) data = dict(meta=_meta(ap.parse_activity(obj)), activity=dict(object=obj)) data["_id"] = obj["id"] data["remote_id"] = obj["id"] reply = ap.parse_activity(data["activity"]["object"]) # Fetch the post visibility, in case it's follower only default_visibility = ap.get_visibility(reply) # If it's public, we default the reply to unlisted if default_visibility == ap.Visibility.PUBLIC: default_visibility = ap.Visibility.UNLISTED reply_id = reply.id if reply.ACTIVITY_TYPE == ap.ActivityType.CREATE: reply_id = reply.get_object().id actor = reply.get_actor() domain = urlparse(actor.id).netloc # FIXME(tsileo): if reply of reply, fetch all participants content = f"@{actor.preferredUsername}@{domain} " if reply.has_type(ap.ActivityType.CREATE): reply = reply.get_object() for mention in reply.get_mentions(): if mention.href in [actor.id, ID]: continue m = ap.fetch_remote_activity(mention.href) if m.has_type(ap.ACTOR_TYPES): d = urlparse(m.id).netloc content += f"@{m.preferredUsername}@{d} " thread = _build_thread(data) return htmlify( render_template( "new.html", reply=reply_id, content=content, thread=thread, default_visibility=default_visibility, visibility=ap.Visibility, emojis=config.EMOJIS.split(" "), custom_emojis=sorted( [ap.Emoji(**dat) for name, dat in EMOJIS_BY_NAME.items()], key=lambda e: e.name, ), ))
def save(box: Box, activity: ap.BaseActivity) -> None: """Custom helper for saving an activity to the DB.""" visibility = ap.get_visibility(activity) is_public = False if visibility in [ap.Visibility.PUBLIC, ap.Visibility.UNLISTED]: is_public = True object_id = None try: object_id = activity.get_object_id() except Exception: # TODO(tsileo): should be ValueError, but replies trigger a KeyError on object pass object_visibility = None if activity.has_type([ ap.ActivityType.CREATE, ap.ActivityType.ANNOUNCE, ap.ActivityType.LIKE ]): object_visibility = ap.get_visibility(activity.get_object()).name actor_id = activity.get_actor().id DB.activities.insert_one({ "box": box.value, "activity": activity.to_dict(), "type": _to_list(activity.type), "remote_id": activity.id, "meta": { "undo": False, "deleted": False, "public": is_public, "server": urlparse(activity.id).netloc, "visibility": visibility.name, "actor_id": actor_id, "object_id": object_id, "object_visibility": object_visibility, "poll_answer": False, }, })
def api_boost() -> _Response: note = _user_api_get_note() # Ensures the note visibility allow us to build an Announce (in respect to the post visibility) if ap.get_visibility(note) not in [ap.Visibility.PUBLIC, ap.Visibility.UNLISTED]: abort(400) announce = ap.Announce( actor=MY_PERSON.id, object=note.id, to=[MY_PERSON.followers, note.attributedTo], cc=[ap.AS_PUBLIC], published=now(), context=new_context(note), ) announce_id = post_to_outbox(announce) return _user_api_response(activity=announce_id)
def save_reply(activity: ap.BaseActivity, meta: Dict[str, Any] = {}) -> None: visibility = ap.get_visibility(activity) is_public = False if visibility in [ap.Visibility.PUBLIC, ap.Visibility.UNLISTED]: is_public = True published = activity.published if activity.published else now() DB.replies.insert_one({ "activity": activity.to_dict(), "type": _to_list(activity.type), "remote_id": activity.id, "meta": { "undo": False, "deleted": False, "public": is_public, "server": urlparse(activity.id).netloc, "visibility": visibility.name, "actor_id": activity.get_actor().id, MetaKey.PUBLISHED.value: published, **meta, }, })