Exemplo n.º 1
0
def note_by_id(note_id):
    if is_api_request():
        return redirect(url_for("outbox_activity", item_id=note_id))

    query = {}
    # Prevent displaying direct messages on the public frontend
    if not session.get("logged_in", False):
        query = is_public()

    data = DB.activities.find_one({
        **in_outbox(),
        **by_remote_id(activity_url(note_id)),
        **query
    })
    if not data:
        abort(404)
    if data["meta"].get("deleted", False):
        abort(410)

    thread = _build_thread(data, query=query)
    app.logger.info(f"thread={thread!r}")

    raw_likes = list(
        DB.activities.find({
            **not_undo(),
            **not_deleted(),
            **by_type(ActivityType.LIKE),
            **by_object_id(data["activity"]["object"]["id"]),
        }))
    likes = []
    for doc in raw_likes:
        try:
            likes.append(doc["meta"]["actor"])
        except Exception:
            app.logger.exception(f"invalid doc: {doc!r}")
    app.logger.info(f"likes={likes!r}")

    raw_shares = list(
        DB.activities.find({
            **not_undo(),
            **not_deleted(),
            **by_type(ActivityType.ANNOUNCE),
            **by_object_id(data["activity"]["object"]["id"]),
        }))
    shares = []
    for doc in raw_shares:
        try:
            shares.append(doc["meta"]["actor"])
        except Exception:
            app.logger.exception(f"invalid doc: {doc!r}")
    app.logger.info(f"shares={shares!r}")

    return htmlify(
        render_template("note.html",
                        likes=likes,
                        shares=shares,
                        thread=thread,
                        note=data))
Exemplo n.º 2
0
def _follow_set_inbox_flags(activity: ap.Follow, new_meta: _NewMeta) -> None:
    """Handle notification for new followers."""
    _logger.info(f"set_inbox_flags activity={activity!r}")
    # Check if we're already following this actor
    follows_back = False
    accept_query = {
        **in_inbox(),
        **by_type(ap.ActivityType.ACCEPT),
        **by_actor(activity.get_actor()),
        **not_undo(),
    }
    raw_accept = DB.activities.find_one(accept_query)
    if raw_accept:
        follows_back = True

        DB.activities.update_many(
            accept_query,
            {"$set": {
                _meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): True
            }})

    # This Follow will be a "$actor started following you" notification
    _flag_as_notification(activity, new_meta)
    _set_flag(new_meta, MetaKey.GC_KEEP)
    _set_flag(new_meta, MetaKey.NOTIFICATION_FOLLOWS_BACK, follows_back)
    return None
Exemplo n.º 3
0
def _accept_set_inbox_flags(activity: ap.Accept, new_meta: _NewMeta) -> None:
    """Handle notifications for "accepted" following requests."""
    _logger.info(f"set_inbox_flags activity={activity!r}")
    # Check if this actor already follow us back
    follows_back = False
    follow_query = {
        **in_inbox(),
        **by_type(ap.ActivityType.FOLLOW),
        **by_actor(activity.get_actor()),
        **not_undo(),
    }
    raw_follow = DB.activities.find_one(follow_query)
    if raw_follow:
        follows_back = True

        DB.activities.update_many(
            follow_query,
            {"$set": {
                _meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): True
            }})

    # This Accept will be a "You started following $actor" notification
    _flag_as_notification(activity, new_meta)
    _set_flag(new_meta, MetaKey.GC_KEEP)
    _set_flag(new_meta, MetaKey.NOTIFICATION_FOLLOWS_BACK, follows_back)
    return None
Exemplo n.º 4
0
def following():
    q = {**in_outbox(), **by_type(ActivityType.FOLLOW), **not_undo()}

    if is_api_request():
        _log_sig()
        return jsonify(**activitypub.build_ordered_collection(
            DB.activities,
            q=q,
            cursor=request.args.get("cursor"),
            map_func=lambda doc: doc["activity"]["object"],
            col_name="following",
        ))

    if config.HIDE_FOLLOWING and not session.get("logged_in", False):
        abort(404)

    following, older_than, newer_than = paginated_query(DB.activities, q)
    following = [(doc["remote_id"], doc["meta"]["object"]) for doc in following
                 if "remote_id" in doc and "object" in doc.get("meta", {})]
    lists = list(DB.lists.find())
    return render_template(
        "following.html",
        following_data=following,
        older_than=older_than,
        newer_than=newer_than,
        lists=lists,
    )
Exemplo n.º 5
0
    def migrate(self) -> None:
        for data in find_activities({**by_type(ap.ActivityType.ACCEPT), **in_inbox()}):
            try:
                update_one_activity(
                    {
                        **by_type(ap.ActivityType.FOLLOW),
                        **by_remote_id(data["meta"]["object_id"]),
                    },
                    upsert({MetaKey.FOLLOW_STATUS: FollowStatus.ACCEPTED.value}),
                )
                # Check if we are following this actor
                follow_query = {
                    **in_inbox(),
                    **by_type(ap.ActivityType.FOLLOW),
                    **by_object_id(data["meta"]["actor_id"]),
                    **not_undo(),
                }
                raw_follow = DB.activities.find_one(follow_query)
                if raw_follow:
                    DB.activities.update_many(
                        follow_query,
                        {"$set": {_meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): True}},
                    )

            except Exception:
                logger.exception(f"failed to process activity {data!r}")

        for data in find_activities({**by_type(ap.ActivityType.FOLLOW), **in_outbox()}):
            try:
                print(data)
                follow_query = {
                    **in_inbox(),
                    **by_type(ap.ActivityType.FOLLOW),
                    **by_actor_id(data["meta"]["object_id"]),
                    **not_undo(),
                }
                raw_accept = DB.activities.find_one(follow_query)
                print(raw_accept)
                if raw_accept:
                    DB.activities.update_many(
                        by_remote_id(data["remote_id"]),
                        {"$set": {_meta(MetaKey.NOTIFICATION_FOLLOWS_BACK): True}},
                    )

            except Exception:
                logger.exception(f"failed to process activity {data!r}")
Exemplo n.º 6
0
def index():
    if is_api_request():
        _log_sig()
        return activitypubify(**ME)

    q = {
        **in_outbox(),
        "$or": [
            {
                **by_type(ActivityType.CREATE),
                **not_deleted(),
                **by_visibility(ap.Visibility.PUBLIC),
                "$or": [{
                    "meta.pinned": False
                }, {
                    "meta.pinned": {
                        "$exists": False
                    }
                }],
            },
            {
                **by_type(ActivityType.ANNOUNCE),
                **not_undo()
            },
        ],
    }

    apinned = []
    # Only fetch the pinned notes if we're on the first page
    if not request.args.get("older_than") and not request.args.get(
            "newer_than"):
        q_pinned = {
            **in_outbox(),
            **by_type(ActivityType.CREATE),
            **not_deleted(),
            **pinned(),
            **by_visibility(ap.Visibility.PUBLIC),
        }
        apinned = list(DB.activities.find(q_pinned))

    outbox_data, older_than, newer_than = paginated_query(DB.activities,
                                                          q,
                                                          limit=25 -
                                                          len(apinned))

    return htmlify(
        render_template(
            "index.html",
            outbox_data=outbox_data,
            older_than=older_than,
            newer_than=newer_than,
            pinned=apinned,
        ))
Exemplo n.º 7
0
def all():
    q = {
        **in_outbox(),
        **by_type([ActivityType.CREATE, ActivityType.ANNOUNCE]),
        **not_deleted(),
        **not_undo(),
        **not_poll_answer(),
    }
    outbox_data, older_than, newer_than = paginated_query(DB.activities, q)

    return htmlify(
        render_template(
            "index.html",
            outbox_data=outbox_data,
            older_than=older_than,
            newer_than=newer_than,
        ))
Exemplo n.º 8
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()),
        ))
Exemplo n.º 9
0
def outbox():
    if request.method == "GET":
        if not is_api_request():
            abort(404)
        _log_sig()
        # TODO(tsileo): returns the whole outbox if authenticated and look at OCAP support
        q = {
            **in_outbox(),
            "$or": [
                {
                    **by_type(ActivityType.CREATE),
                    **not_deleted(),
                    **by_visibility(ap.Visibility.PUBLIC),
                },
                {
                    **by_type(ActivityType.ANNOUNCE),
                    **not_undo()
                },
            ],
        }
        return activitypubify(**activitypub.build_ordered_collection(
            DB.activities,
            q=q,
            cursor=request.args.get("cursor"),
            map_func=lambda doc: activity_from_doc(doc, embed=True),
            col_name="outbox",
        ))

    # Handle POST request aka C2S API
    try:
        _api_required()
    except BadSignature:
        abort(401)

    data = request.get_json(force=True)
    activity = ap.parse_activity(data)
    activity_id = post_to_outbox(activity)

    return Response(status=201, headers={"Location": activity_id})
Exemplo n.º 10
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"),
        ))
Exemplo n.º 11
0
def inject_config():
    q = {
        **in_outbox(),
        "$or": [
            {
                **by_type(ActivityType.CREATE),
                **not_deleted(),
                **by_visibility(ap.Visibility.PUBLIC),
            },
            {
                **by_type(ActivityType.ANNOUNCE),
                **not_undo()
            },
        ],
    }
    notes_count = DB.activities.count(q)
    # FIXME(tsileo): rename to all_count, and remove poll answers from it
    all_q = {
        **in_outbox(),
        **by_type([ActivityType.CREATE, ActivityType.ANNOUNCE]),
        **not_deleted(),
        **not_undo(),
        **not_poll_answer(),
    }
    liked_q = {
        **in_outbox(),
        **by_type(ActivityType.LIKE),
        **not_undo(),
        **not_deleted(),
    }
    followers_q = {
        **in_inbox(),
        **by_type(ActivityType.FOLLOW),
        **not_undo(),
        **not_deleted(),
    }
    following_q = {
        **in_outbox(),
        **by_type(ActivityType.FOLLOW),
        **follow_request_accepted(),
        **not_undo(),
        **not_deleted(),
    }
    unread_notifications_q = {_meta(MetaKey.NOTIFICATION_UNREAD): True}

    logged_in = session.get("logged_in", False)

    return dict(
        microblogpub_version=VERSION,
        config=config,
        logged_in=logged_in,
        followers_count=DB.activities.count(followers_q),
        following_count=DB.activities.count(following_q)
        if logged_in or not config.HIDE_FOLLOWING else 0,
        notes_count=notes_count,
        liked_count=DB.activities.count(liked_q) if logged_in else 0,
        with_replies_count=DB.activities.count(all_q) if logged_in else 0,
        unread_notifications_count=DB.activities.count(unread_notifications_q)
        if logged_in else 0,
        me=ME,
        base_url=config.BASE_URL,
        highlight_css=HIGHLIGHT_CSS,
    )