Esempio n. 1
0
def admin_thread() -> _Response:
    oid = request.args.get("oid")
    if not oid:
        abort(404)

    data = find_one_activity({
        **by_type(ap.ActivityType.CREATE),
        **by_object_id(oid)
    })
    if not data:
        dat = DB.replies.find_one({**by_remote_id(oid)})
        data = {
            "activity": {
                "object": dat["activity"]
            },
            "meta": dat["meta"],
            "_id": dat["_id"],
        }

    if not data:
        abort(404)
    if data["meta"].get("deleted", False):
        abort(410)
    thread = _build_thread(data)

    tpl = "note.html"
    if request.args.get("debug"):
        tpl = "note_debug.html"
    return htmlify(render_template(tpl, thread=thread, note=data))
Esempio n. 2
0
def admin_profile() -> _Response:
    if not request.args.get("actor_id"):
        abort(404)

    actor_id = request.args.get("actor_id")
    actor = ap.fetch_remote_activity(actor_id)
    q = {
        "meta.actor_id": actor_id,
        "box": "inbox",
        **not_deleted(),
        "type": {
            "$in":
            [ap.ActivityType.CREATE.value, ap.ActivityType.ANNOUNCE.value]
        },
    }
    inbox_data, older_than, newer_than = paginated_query(
        DB.activities, q, limit=int(request.args.get("limit", 25)))
    follower = find_one_activity({
        "box": "inbox",
        "type": ap.ActivityType.FOLLOW.value,
        "meta.actor_id": actor.id,
        "meta.undo": False,
    })
    following = find_one_activity({
        **by_type(ap.ActivityType.FOLLOW),
        **by_object_id(actor.id),
        **not_undo(),
        **in_outbox(),
        **follow_request_accepted(),
    })

    return htmlify(
        render_template(
            "stream.html",
            actor_id=actor_id,
            actor=actor.to_dict(),
            inbox_data=inbox_data,
            older_than=older_than,
            newer_than=newer_than,
            follower=follower,
            following=following,
            lists=list(DB.lists.find()),
        ))
Esempio n. 3
0
def admin_lookup() -> _Response:
    data = None
    meta = None
    follower = None
    following = None
    if request.args.get("url"):
        data = lookup(request.args.get("url"))  # type: ignore
        if data:
            if not data.has_type(ap.ACTOR_TYPES):
                meta = _meta(data)
            else:
                follower = find_one_activity({
                    "box": "inbox",
                    "type": ap.ActivityType.FOLLOW.value,
                    "meta.actor_id": data.id,
                    "meta.undo": False,
                })
                following = find_one_activity({
                    **by_type(ap.ActivityType.FOLLOW),
                    **by_object_id(data.id),
                    **not_undo(),
                    **in_outbox(),
                    **follow_request_accepted(),
                })

            if data.has_type(ap.ActivityType.QUESTION):
                p.push(data.id, "/task/fetch_remote_question")

        print(data)
        app.logger.debug(data.to_dict())
    return htmlify(
        render_template(
            "lookup.html",
            data=data,
            meta=meta,
            follower=follower,
            following=following,
            url=request.args.get("url"),
        ))
Esempio n. 4
0
def outbox_activity(item_id):
    data = find_one_activity({
        **in_outbox(),
        **by_remote_id(activity_url(item_id)),
        **is_public()
    })
    if not data:
        abort(404)

    _log_sig()
    obj = activity_from_doc(data)
    if data["meta"].get("deleted", False):
        abort(404)

    if obj["type"] != ActivityType.CREATE.value:
        abort(404)
    return jsonify(**obj["object"])
Esempio n. 5
0
def task_process_reply() -> _Response:
    """Process `Announce`d posts from Pleroma relays in order to process replies of activities that are in the inbox."""
    task = p.parse(flask.request)
    app.logger.info(f"task={task!r}")
    iri = task.payload
    try:
        activity = ap.fetch_remote_activity(iri)
        app.logger.info(f"checking for reply activity={activity!r}")

        # Some AP server always return Create when requesting an object
        if activity.has_type(ap.ActivityType.CREATE):
            activity = activity.get_object()

        in_reply_to = activity.get_in_reply_to()
        if not in_reply_to:
            # If it's not reply, we can drop it
            app.logger.info(
                f"activity={activity!r} is not a reply, dropping it")
            return ""

        root_reply = in_reply_to

        # Fetch the activity reply
        reply = ap.fetch_remote_activity(in_reply_to)
        if reply.has_type(ap.ActivityType.CREATE):
            reply = reply.get_object()

        new_replies = [activity, reply]

        while 1:
            in_reply_to = reply.get_in_reply_to()
            if not in_reply_to:
                break

            root_reply = in_reply_to
            reply = ap.fetch_remote_activity(root_reply)

            if reply.has_type(ap.ActivityType.CREATE):
                reply = reply.get_object()

            new_replies.append(reply)

        app.logger.info(f"root_reply={reply!r} for activity={activity!r}")

        # In case the activity was from the inbox
        update_one_activity(
            {
                **by_object_id(activity.id),
                **by_type(ap.ActivityType.CREATE)
            },
            upsert({MetaKey.THREAD_ROOT_PARENT: root_reply}),
        )

        for (new_reply_idx, new_reply) in enumerate(new_replies):
            if find_one_activity({
                    **by_object_id(new_reply.id),
                    **by_type(ap.ActivityType.CREATE)
            }) or DB.replies.find_one(by_remote_id(new_reply.id)):
                continue

            actor = new_reply.get_actor()
            is_root_reply = new_reply_idx == len(new_replies) - 1
            if is_root_reply:
                reply_flags: Dict[str, Any] = {}
            else:
                reply_actor = new_replies[new_reply_idx + 1].get_actor()
                is_in_reply_to_self = actor.id == reply_actor.id
                reply_flags = {
                    MetaKey.IN_REPLY_TO_SELF.value: is_in_reply_to_self,
                    MetaKey.IN_REPLY_TO.value: new_reply.get_in_reply_to(),
                }
                if not is_in_reply_to_self:
                    reply_flags[MetaKey.IN_REPLY_TO_ACTOR.
                                value] = reply_actor.to_dict(embed=True)

            # Save the reply with the cached actor and the thread flag/ID
            save_reply(
                new_reply,
                {
                    **reply_flags,
                    MetaKey.THREAD_ROOT_PARENT.value: root_reply,
                    MetaKey.ACTOR.value: actor.to_dict(embed=True),
                    MetaKey.ACTOR_HASH.value: _actor_hash(actor),
                },
            )

            # Update the reply counters
            if new_reply.get_in_reply_to():
                update_one_activity(
                    {
                        **by_object_id(new_reply.get_in_reply_to()),
                        **by_type(ap.ActivityType.CREATE),
                    },
                    inc(MetaKey.COUNT_REPLY, 1),
                )
                DB.replies.update_one(
                    by_remote_id(new_reply.get_in_reply_to()),
                    inc(MetaKey.COUNT_REPLY, 1),
                )

            # Cache the actor icon
            _cache_actor_icon(actor)
            # And cache the attachments
            Tasks.cache_attachments(new_reply.id)
    except (ActivityGoneError, ActivityNotFoundError):
        app.logger.exception(f"dropping activity {iri}, skip processing")
        return ""
    except Exception as err:
        app.logger.exception(f"failed to process new activity {iri}")
        raise TaskError() from err

    return ""