示例#1
0
 def SendLikeActivity(self, req, context):
     response = general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
     # Get article from DB
     article, err = self._get_article(req.article_id)
     if err is not None:
         self._logger.error("Error getting article: " + err)
         response.result_type = general_pb2.ResultType.ERROR
         response.error = err
         return response
     author = self._get_author(article)
     if author is None:
         self._logger.error("Error getting article author from DB")
         response.result_type = general_pb2.ResultType.ERROR
         response.error = "Error getting article author from DB"
         return response
     activity = build_like_activity(
         self._activ_util.build_actor(req.liker_handle, self._hostname),
         self._activ_util.build_article_ap_id(author, article))
     inbox = self._activ_util.build_inbox_url(author.handle, author.host)
     resp, err = self._activ_util.send_activity(activity, inbox)
     if err is not None:
         response.result_type = general_pb2.ResultType.ERROR
         response.error = err
     return response
示例#2
0
    def ReceiveCreate(self, req, context):
        self._logger.debug("Recieved a new create notification.")
        resp = general_pb2.GeneralResponse()

        # get actor ids
        author_id, follower_id = self._get_actor_ids(req.attributedTo,
                                                     req.recipient)
        if author_id is None:
            resp.result_type = general_pb2.ResultType.ERROR_400
            return resp

        # check if local user follows
        follower_flag = self._check_follow(author_id, follower_id)
        if follower_flag is False:
            resp.result_type = general_pb2.ResultType.ERROR
            return resp

        # add to article db
        added_flag = self._add_to_posts_db(author_id, req)
        if added_flag is False:
            resp.result_type = general_pb2.ResultType.ERROR
            return resp

        self._logger.debug("Recieve create Article success")
        resp.result_type = general_pb2.ResultType.OK
        return resp
示例#3
0
 def ReceiveLikeUndoActivity(self, req, ctx):
     self._logger.debug("Got undo for like object")
     user = self.get_user(req.liking_user_ap_id)
     if user is None:
         return self.gen_error("Couldn't get user: "******"Error getting article: %s", err)
         return self.gen_error("Could not get article: "
                               + req.liked_object_ap_id)
     if not self.remove_like_from_db(user.global_id, article.global_id):
         return self.gen_error("Error removing like from DB")
     # TODO(CianLR): If this is the author's local server then federate
     # the unlike
     if self._users_util.user_is_local(article.author_id):
         # Build the activity.
         a = self._activ_util.build_undo(like_util.build_like_activity(
             req.liking_user_ap_id,
             req.liked_object_ap_id))
         # Forward it to the followers
         self._activ_util.forward_activity_to_followers(
             article.author_id, a)
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK,
     )
示例#4
0
    def SendUnfollowActivity(self, req, context):
        resp = general_pb2.GeneralResponse()

        follower_actor = self._activ_util.build_actor(
            req.follower.handle, req.follower.host)
        followed_actor = self._activ_util.build_actor(
            req.followed.handle, req.followed.host)

        # Build a follow activity, then wrap it in an Undo activity
        follow_activity = self._build_activity(follower_actor,
                                               followed_actor,
                                               sendable=False)
        undo_activity = self._build_undo(follower_actor,
                                         followed_actor,
                                         follow_activity)

        inbox_url = self._activ_util.build_inbox_url(
            req.followed.handle, req.followed.host)

        self._logger.debug('Sending unfollow activity to foreign server')
        self._logger.debug(str(undo_activity))
        sender_id = self._get_local_user_id(req.follower.handle)
        _, err = self._activ_util.send_activity(undo_activity,
                                                inbox_url,
                                                sender_id=sender_id)
        if err is None:
            resp.result_type = general_pb2.ResultType.OK
        else:
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = err

        return resp
示例#5
0
    def ReceiveUnfollow(self, request, context):
        resp = general_pb2.GeneralResponse()
        local_user, foreign_user = self._util.validate_and_get_users(
            resp, request)
        if foreign_user is None or local_user is None:
            self._logger.info('Error receiving unfollow: %s', resp.error)
            return resp

        self._logger.info('User ID %d is unfollowing User ID %d',
                          foreign_user.global_id, local_user.global_id)

        follow_resp = self._util.delete_follow_in_db(foreign_user.global_id,
                                                     local_user.global_id)

        if follow_resp.result_type == general_pb2.ResultType.ERROR:
            self._logger.error('Error deleting follow: %s', follow_resp.error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = 'Could not delete requested follow from database'
            return resp

        if self._recommender_stub is not None:
            req = recommend_follows_pb2.UpdateFollowRecommendationsRequest(
                follower=foreign_user.global_id,
                followed=local_user.global_id,
                following=False)
            self._recommender_stub.UpdateFollowRecommendations(req)

        resp.result_type = general_pb2.ResultType.OK
        return resp
 def parse_actor_error(self, actor_name, actor_id):
     self._logger.error("Received error while parsing %s author id: %s",
                        actor_name, actor_id)
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.ERROR_400,
         error="Could not parse {} author id".format(actor_name),
     )
示例#7
0
 def WriteLog(self, request, context):
     py_level = self._log_levels[request.severity]
     log_message = self._format_log(request.timestamp, request.source,
                                    py_level, request.message)
     self._logger.log(py_level, log_message)
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
示例#8
0
    def SendCreate(self, req, context):
        self._logger.debug("Recieved a new create action.")

        author = self._users_util.get_user_from_db(global_id=req.author_id)
        # Insert ActivityPub ID into database.
        # build author entry from scratch to add host into call
        ue = database_pb2.UsersEntry(handle=author.handle,
                                     host=self._host_name)
        pe = database_pb2.PostsEntry(global_id=req.global_id)
        ap_id = self._activ_util.build_article_ap_id(ue, pe)
        article_url = self._activ_util.build_local_article_url(ue, pe)
        err = self._add_ap_id(req.global_id, ap_id)
        if err is not None:
            self._logger.error("Continuing through error: %s", err)

        # list of follow objects
        follow_list = self._users_util.get_follower_list(author.global_id)
        # remove local users
        foreign_follows = self._users_util.remove_local_users(follow_list)

        # go through follow send create activity
        # TODO (sailslick) make async/ parallel in the future
        for follower in foreign_follows:
            self._post_create_req(follower, req, ap_id, author, article_url)

        resp = general_pb2.GeneralResponse()
        resp.result_type = general_pb2.ResultType.OK
        return resp
示例#9
0
 def ReceiveDeleteActivity(self, req, ctx):
     self._logger.info("Received delete for article '%s'", req.ap_id)
     article = get_article(self._logger, self._db, ap_id=req.ap_id)
     if article is None:
         # Don't have the article, our work here is done.
         # This can happen for natural reasons, dublicate deletes,
         # deletes of articles that were created before a follower
         # followed the creator, etc.
         self._logger.info("Don't have article %s, exiting", req.ap_id)
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.OK
         )
     author = self._users_util.get_user_from_db(global_id=article.author_id)
     if author is None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Could not retrieve author",
         )
     # Grab the people who shared the article before we delete everything.
     sharer_ids = get_sharers_of_article(
         self._logger, self._db, article.global_id)
     # Delete the local copy.
     if not delete_article(self._logger, self._db, ap_id=req.ap_id):
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Could not delete article",
         )
     # Forward the delete to the announcers.
     delete_obj = self._activ_util.build_delete(
         author, article, self._hostname)
     for user_id in sharer_ids:
         err = self._activ_util.forward_activity_to_followers(
             user_id, delete_obj)
         if err is not None:
             # Warn but do not quit on error sending to announcer followers.
             self._logger.warning(
                 "Sending activity to %d followers failed", user_id)
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK
     )
示例#10
0
 def ReceiveUpdateActivity(self, req, ctx):
     self._logger.info("Received edit for article '%s'", req.title)
     html_body = md_to_html(self._md, req.body)
     resp = self._db.Posts(
         dbpb.PostsRequest(
             request_type=dbpb.RequestType.UPDATE,
             match=dbpb.PostsEntry(ap_id=req.ap_id),
             entry=dbpb.PostsEntry(
                 title=req.title,
                 body=html_body,
                 md_body=req.body,
                 summary=req.summary,
             ),
         ))
     if resp.result_type != general_pb2.ResultType.OK:
         self._logger.error("Could not update article: %s", resp.error)
         return general_pb2.GeneralResponse(
             result_type=upb.UpdateRespones.ERROR,
             error="Error updating article in DB",
         )
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
示例#11
0
    def ReceiveLikeActivity(self, req, context):
        self._logger.debug("Got like for %s from %s", req.liked_object,
                           req.liker_id)
        # Get article.
        article, err = self._activ_util.get_article_by_ap_id(req.liked_object)
        if err is not None:
            self._logger.error("Error getting article: %s", err)
            return general_pb2.GeneralResponse(
                result_type=general_pb2.ResultType.ERROR,
                error=err,
            )
        # Get user.
        user_id = self._get_liking_user(req.liker_id)
        if user_id is None:
            self._logger.error("Could not get liking user")
            return general_pb2.GeneralResponse(
                result_type=general_pb2.ResultType.ERROR,
                error="Could not get liking user",
            )
        # Add like to local database.
        err = self._add_like_to_db(user_id, article.global_id)
        if err is not None:
            self._logger.error("Could not add like to DB: %s", err)
            return general_pb2.GeneralResponse(
                result_type=general_pb2.ResultType.ERROR,
                error="Could not add like to DB: " + err)
        if self._user_util.user_is_local(article.author_id):
            # This is the author's local server, must distribute like to
            # all the servers who follow this user.
            self._activ_util.forward_activity_to_followers(
                article.author_id,
                build_like_activity(req.liker_id, req.liked_object))

            # If post_recommender is on, send like to post_recommender
            if self._post_recommendation_stub is not None:
                self._add_like_to_user_model(user_id, article.global_id)

        return general_pb2.GeneralResponse(
            result_type=general_pb2.ResultType.OK)
示例#12
0
    def ReceiveUnfollowActivity(self, req, context):
        self._logger.debug('Received unfollow activity.')
        resp = general_pb2.GeneralResponse()

        follow = self._s2s_req_to_follows_req(req)
        self._logger.info('{}@{} requested to unfollow {}.'.format(
            follow.follower_handle, follow.follower_host, follow.followed))

        follows_resp = self._follows_stub.ReceiveUnfollow(follow)

        resp.result_type = follows_resp.result_type
        resp.error = follows_resp.error
        return resp
示例#13
0
    def RssFollowRequest(self, req, context):
        resp = general_pb2.GeneralResponse()
        self._logger.info("Got follow rss request.")

        # check if url has right endings
        valid_url = self._validate_url(req.feed_url)
        if not valid_url:
            self._logger.error("Invalid rss/atom url: %s", req.feed_url)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = "Invalid rss/atom url"
            return resp

        # check if rss user already exists (handle: domain, local so no host)
        rss_handle = self._convert_rss_url_to_handle(req.feed_url)
        rss_entry = self._users_util.get_user_from_db(handle=rss_handle,
                                                      host_is_null=True)
        if rss_entry is None:
            # send to rss service to be created
            rss_user_id, rss_error = self._create_rss_user(req.feed_url)
            if rss_error:
                resp.result_type = general_pb2.ResultType.ERROR
                resp.error = rss_error
                return resp
        else:
            rss_user_id = rss_entry.global_id

        # Get local user id
        follower_entry = self._users_util.get_user_from_db(handle=req.follower,
                                                           host_is_null=True)
        if follower_entry is None:
            error = "Could not find local user {}".format(req.follower)
            self._logger.error(error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = error
            return resp

        self._logger.info("User ID %d has requested to follow RSS user ID %d",
                          follower_entry.global_id, rss_user_id)

        # Add follower
        follow_resp = self._util.create_follow_in_db(follower_entry.global_id,
                                                     rss_user_id)
        if follow_resp.result_type == general_pb2.ResultType.ERROR:
            self._logger.error("Error creating follow: %s", follow_resp.error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = "Could not add requested follow to database"
            return resp

        resp.result_type = general_pb2.ResultType.OK
        return resp
示例#14
0
文件: send.py 项目: SailSlick/rabble
    def SendApproval(self, req, context):
        resp = general_pb2.GeneralResponse()
        url = self._activ_util.build_inbox_url(req.follow.follower.handle,
                                               req.follow.follower.host)
        activity = self._build_activity(req)

        sender_id = self._get_local_user_id(req.follow.followed.handle)
        _, err = self._activ_util.send_activity(activity,
                                                url,
                                                sender_id=sender_id)
        if err is not None:
            resp.error = err
            resp.result_type = general_pb2.ResultType.ERROR

        return resp
示例#15
0
 def SendLikeUndoActivity(self, req, ctx):
     self._logger.info(
         "Got request to undo like for article {} by user {}".format(
             req.article_id, req.liker_handle))
     try:
         article = self._get_article(req.article_id)
         author = self._users_util.get_user_from_db(
             global_id=article.author_id)
         if author is None:
             raise SendUndoException("Error getting author")
         if not author.host:
             author.host = self._hostname
         undo_obj = self._build_like_undo_object(req.liker_handle, author,
                                                 article)
         inbox = self._activ_util.build_inbox_url(author.handle,
                                                  author.host)
         _, err = self._activ_util.send_activity(undo_obj, inbox)
         if err:
             raise SendUndoException(err)
     except SendUndoException as e:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR, error=str(e))
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
示例#16
0
 def SendDeleteActivity(self, req, ctx):
     self._logger.info("Got request to delete article %d from %d",
                       req.article_id, req.user_id)
     user = self._users_util.get_user_from_db(global_id=req.user_id)
     if user is None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Could not retrieve user",
         )
     article = get_article(self._logger, self._db, global_id=req.article_id)
     if article is None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Could not retrieve article",
         )
     if article.author_id != req.user_id:
         self._logger.error("User requesting article deletion isn't author")
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR_401,
             error="User is not the author of this article",
         )
     sharer_ids = get_sharers_of_article(self._logger, self._db,
                                         article.global_id)
     if not delete_article(
             self._logger, self._db, global_id=article.global_id):
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Could not delete article locally",
         )
     delete_obj = self._activ_util.build_delete(user, article,
                                                self._hostname)
     self._logger.info("Activity: %s", str(delete_obj))
     err = self._activ_util.forward_activity_to_followers(
         req.user_id, delete_obj)
     if err is not None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error=err,
         )
     # Send deletes of the article to the followers of people who
     # announced the article. There may be some duplicate Deletes
     # sent but this is acceptable.
     # This roughly the same pattern Mastodon follows:
     # https://github.com/tootsuite/mastodon/issues/5761#issuecomment-345875480
     for user_id in sharer_ids:
         err = self._activ_util.forward_activity_to_followers(
             user_id, delete_obj)
         if err is not None:
             # Warn but do not quit on error sending to announcer followers.
             self._logger.warning(
                 "Sending activity to followers of user %d failed", user_id)
     self._logger.info("Article %d successfully deleted", req.article_id)
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
示例#17
0
    def SendFollowActivity(self, req, context):
        resp = general_pb2.GeneralResponse()
        follower_actor = self._activ_util.build_actor(
            req.follower.handle, req.follower.host)
        followed_actor = self._activ_util.build_actor(
            req.followed.handle, req.followed.host)
        activity = self._build_activity(follower_actor, followed_actor)
        inbox_url = self._activ_util.build_inbox_url(
            req.followed.handle, req.followed.host)
        self._logger.debug('Sending follow activity to foreign server')
        self._logger.debug(str(activity))

        err = self._send(activity, inbox_url, req.follower.handle)
        if err is None:
            resp.result_type = general_pb2.ResultType.OK
        else:
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = err
        return resp
    def add_share_update_count(self, announcer, article, announce_datetime):
        self._logger.debug("Adding share")
        req = db_pb.ShareEntry(user_id=announcer.global_id,
                               article_id=article.global_id,
                               announce_datetime=announce_datetime)
        # check if share already exists
        resp = self._db.FindShare(req)
        if resp.result_type == general_pb2.ResultType.OK and resp.exists:
            # Share exists. return success
            return None

        resp = self._db.AddShare(req)
        if resp.result_type != general_pb2.ResultType.OK:
            self._logger.error("Received error while adding share %s",
                               resp.error)
            return general_pb2.GeneralResponse(
                result_type=general_pb2.ResultType.ERROR,
                error="Could not add share to db {}".format(resp.error),
            )
        return None
示例#19
0
 def AddLike(self, req, context):
     self._logger.debug("Adding like by %d to article %d", req.user_id,
                        req.article_id)
     response = general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
     try:
         self._db.execute(
             'INSERT INTO likes (user_id, article_id) '
             'VALUES (?, ?)',
             req.user_id,
             req.article_id,
             commit=False)
         self._db.execute(
             'UPDATE posts SET likes_count = likes_count + 1 '
             'WHERE global_id=?', req.article_id)
     except sqlite3.Error as e:
         self._db.discard_cursor()
         self._logger.error("AddLike error: %s", str(e))
         response.result_type = general_pb2.ResultType.ERROR
         response.error = str(e)
     return response
示例#20
0
 def RemoveLike(self, req, context):
     self._logger.debug("Removing like by %d to article %d", req.user_id,
                        req.article_id)
     response = general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK)
     try:
         self._db.execute(
             'DELETE FROM likes WHERE user_id=? AND article_id=?',
             req.user_id,
             req.article_id,
             commit=False)
         self._db.execute(
             'UPDATE posts SET likes_count = likes_count - 1 '
             'WHERE global_id=?',
             req.article_id,
             commit=True)
     except sqlite3.Error as e:
         self._db.discard_cursor()
         self._logger.error("RemoveLike error %s", str(e))
         response.result_type = general_pb2.ResultType.ERROR
         response.error = str(e)
     return response
示例#21
0
    def ReceiveFollowRequest(self, request, context):
        resp = general_pb2.GeneralResponse()
        local_user, foreign_user = self._util.validate_and_get_users(resp,
                                                                     request)
        if foreign_user is None or local_user is None:
            return resp

        self._logger.info('User ID %d has requested to follow User ID %d',
                          foreign_user.global_id,
                          local_user.global_id)

        if not local_user.private.value:
            self._logger.info('Accepting follow request')
            self._util.attempt_to_accept(
                local_user, foreign_user, self._host_name, True)

        state = database_pb2.Follow.ACTIVE
        if local_user.private.value:
            self._logger.info('Follow private user: waiting for approval')
            state = database_pb2.Follow.PENDING

        follow_resp = self._util.create_follow_in_db(foreign_user.global_id,
                                                     local_user.global_id,
                                                     state=state)
        if follow_resp.result_type == general_pb2.ResultType.ERROR:
            self._logger.error('Error creating follow: %s', follow_resp.error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = 'Could not add requested follow to database'
            return resp

        if self._recommender_stub is not None:
            req = recommend_follows_pb2.UpdateFollowRecommendationsRequest(
                follower=foreign_user.global_id,
                followed=local_user.global_id,
                following=True)
            self._recommender_stub.UpdateFollowRecommendations(req)

        resp.result_type = general_pb2.ResultType.OK
        return resp
示例#22
0
    def ReceiveApproval(self, req, context):
        self._logger.info("Received an approval request")
        resp = general_pb2.GeneralResponse()

        follower, followed = self._get_users(resp, req)
        if follower is None or followed is None:
            return resp

        db_resp = None
        if req.accept:
            db_resp = self._set_approved(follower, followed)
        else:
            db_resp = self._set_rejected(follower, followed)

        if db_resp.result_type == general_pb2.ResultType.ERROR:
            err = "Could not add follow to database: " + db_resp.error
            self._logger.error(err)
            resp.error = err
            resp.result_type = general_pb2.ResultType.ERROR
            return resp

        resp.result_type = general_pb2.ResultType.OK
        return resp
示例#23
0
    def AcceptFollow(self, request, context):
        resp = general_pb2.GeneralResponse()
        if not request.handle or not request.follower.handle:
            self._logger.error('Error accepting follow: bad arguments')
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = 'Could not accept follow.'
            return resp

        follower, followed = self._get_users(resp, request)
        if follower is None or followed is None:
            return resp

        err = self._get_follow(resp, follower.global_id, followed.global_id)
        if err is not None:
            return resp

        if request.follower.host:
            # Acceptor handles everything from here
            self._util.attempt_to_accept(followed, follower, self._host_name,
                                         request.is_accepted)

        self._modify_follow(resp, follower.global_id, followed.global_id,
                            request.is_accepted)
        return resp
示例#24
0
 def SendUpdateActivity(self, req, ctx):
     self._logger.info("Got request to update article %d from %d",
                       req.article_id, req.user_id)
     user = self._users_util.get_user_from_db(global_id=req.user_id)
     if user is None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Error retrieving user",
         )
     article = get_article(self._logger, self._db, global_id=req.article_id)
     if article is None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Error retrieving article",
         )
     if article.author_id != user.global_id:
         self._logger.warning(
             "User %d requested to edit article belonging to user %d",
             req.user_id, article.author_id)
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR_401
         )
     # Update article locally
     if not self._update_locally(article, req):
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error="Error updating article",
         )
     # Send out update activity
     update_obj = self._build_update(user, article, req)
     self._logger.info("Activity: %s", str(update_obj))
     err = self._activ_util.forward_activity_to_followers(
         req.user_id, update_obj)
     if err is not None:
         return general_pb2.GeneralResponse(
             result_type=general_pb2.ResultType.ERROR,
             error=err,
         )
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.OK
     )
示例#25
0
    def SendFollowRequest(self, request, context):
        resp = general_pb2.GeneralResponse()
        self._logger.info('Sending follow request.')

        from_handle, from_instance = self._users_util.parse_username(
            request.follower)
        to_handle, to_instance = \
            self._users_util.parse_username(request.followed)
        self._logger.info('%s@%s has requested to follow %s@%s.',
                          from_handle,
                          from_instance,
                          to_handle,
                          to_instance)
        if to_instance is None and to_handle is None:
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = 'Could not parse followed username'
            return resp

        # Get user IDs for follow.
        follower_entry = self._users_util.get_or_create_user_from_db(
            handle=from_handle, host=from_instance,
            host_is_null=(from_instance is None))
        if follower_entry is None:
            error = 'Could not find or create user {}@{}'.format(from_handle,
                                                                 from_instance)
            self._logger.error(error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = error
            return resp

        is_local = to_instance is None
        created_user = False
        followed_entry = self._users_util.get_user_from_db(
            handle=to_handle, host=to_instance,
            host_is_null=is_local)
        if not is_local:
            created_user = True
            fu_host, _, fu_bio = self._users_util.get_actor_details(
                to_handle, to_instance)
            if fu_host is None:
                resp.result_type = general_pb2.ResultType.ERROR
                resp.error = "Invalid foreign user to follow"
                return resp
            followed_entry = self._users_util.get_or_create_user_from_db(
                handle=to_handle, host=to_instance, bio=fu_bio)

        if followed_entry is None:
            error = 'Could not find or create user {}@{}'.format(to_handle,
                                                                 to_instance)
            self._logger.error(error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = error
            return resp
        self._logger.info('User ID %d has requested to follow User ID %d',
                          follower_entry.global_id,
                          followed_entry.global_id)

        err = self._add_follow(resp,
                               follower_entry.global_id,
                               followed_entry.global_id,
                               followed_entry.private.value,
                               not is_local)
        if err is not None:
            return resp

        if not is_local:
            err = self._send_s2s(from_handle, to_handle, to_instance)
            if err is not None:
                self._logger.error("Error from s2sFollow: %s", err)
                self._roll_back_follow(follower_entry.global_id,
                                       followed_entry.global_id,
                                       created_user)
                resp.result_type = general_pb2.ResultType.ERROR
                resp.error = err
                return resp

        if self._recommender_stub is not None:
            req = recommend_follows_pb2.UpdateFollowRecommendationsRequest(
                follower=follower_entry.global_id,
                followed=followed_entry.global_id,
                following=True)
            self._recommender_stub.UpdateFollowRecommendations(req)

        resp.result_type = general_pb2.ResultType.OK
        return resp
示例#26
0
    def SendAnnounceActivity(self, req, context):
        self._logger.debug("Sending announce for article_id %s from %s at %s",
                           req.article_id, req.announcer_id,
                           req.announce_time.seconds)
        response = general_pb2.GeneralResponse(
            result_type=general_pb2.ResultType.OK)

        announcer = self._users_util.get_user_from_db(
            global_id=req.announcer_id)
        if announcer is None:
            response.result_type = general_pb2.ResultType.ERROR_400
            response.error = "Announcer does not exist"
            return response

        # Get post author & reblogged article
        article, err = self._get_shared_article(req.article_id)
        if err is not None:
            response.result_type = general_pb2.ResultType.ERROR_400
            response.error = "Shared Article does not exist"
            return response

        author = self._users_util.get_user_from_db(global_id=article.author_id)
        if author is None:
            response.result_type = general_pb2.ResultType.ERROR_400
            response.error = "Author of shared post does not exist"
            return response
        if author.global_id == announcer.global_id:
            response.result_type = general_pb2.ResultType.ERROR_400
            response.error = "Author cannot share their own post"
            return response

        host = author.host
        article_url = None
        # check if author is local, if they are build local article url
        if not author.host:
            host = self._hostname
            article_url = self._activ_util.build_local_article_url(
                author, article)
        author_actor = self._activ_util.build_actor(author.handle, host)

        # Create Announce activity
        article_ap_id = self._activ_util.build_article_ap_id(author, article)
        timestamp = self._activ_util.timestamp_to_rfc(
            article.creation_datetime)
        article_obj = self._activ_util.build_article(
            article_ap_id,
            article.title,
            timestamp,
            author_actor,
            article.md_body,
            article.summary,
            article_url=article_url,
        )
        actor = self._activ_util.build_actor(announcer.handle, self._hostname)
        creation_datetime = self._activ_util.timestamp_to_rfc(
            req.announce_time)

        announce_activity = self._announce_util.build_announce_activity(
            actor, article_obj, creation_datetime)

        # Create a list of foreign followers
        follow_list = self._users_util.get_follower_list(req.announcer_id)
        foreign_follows = self._users_util.remove_local_users(follow_list)

        # Add article author if not in list
        # This is so they can increment their share count & supply to followers
        if author not in foreign_follows:
            foreign_follows.append(author)

        # Add announcer so local instance can add to share db
        if announcer not in foreign_follows:
            foreign_follows.append(announcer)

        # Send activity to all followers
        response = self._announce_util.send_announce_activity(
            foreign_follows, announce_activity, response)

        return response
示例#27
0
 def gen_error(self, err):
     return general_pb2.GeneralResponse(
         result_type=general_pb2.ResultType.ERROR,
         error=err,
     )
示例#28
0
    def SendUnfollow(self, request, context):
        resp = general_pb2.GeneralResponse()
        self._logger.info('Setting unfollow.')

        from_handle, from_instance = self._users_util.parse_username(
            request.follower)
        to_handle, to_instance = \
            self._users_util.parse_username(request.followed)
        self._logger.info('%s@%s has requested to unfollow %s@%s.',
                          from_handle,
                          from_instance,
                          to_handle,
                          to_instance)
        if to_instance is None and to_handle is None:
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = 'Could not parse unfollowed username'
            return resp

        # Get user IDs for unfollow.
        follower_entry = self._users_util.get_or_create_user_from_db(
            handle=from_handle, host=from_instance,
            host_is_null=(from_instance is None))
        if follower_entry is None:
            error = 'Could not find or create user {}@{}'.format(from_handle,
                                                                 from_instance)
            self._logger.error(error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = error
            return resp
        followed_entry = self._users_util.get_or_create_user_from_db(
            handle=to_handle, host=to_instance,
            host_is_null=(to_instance is None))
        if followed_entry is None:
            error = 'Could not find or create user {}@{}'.format(to_handle,
                                                                 to_instance)
            self._logger.error(error)
            resp.result_type = general_pb2.ResultType.ERROR
            resp.error = error
            return resp
        self._logger.info('User ID %d has requested to unfollow User ID %d',
                          follower_entry.global_id,
                          followed_entry.global_id)

        is_foreign = to_instance is not None
        err = self._remove_follow(resp,
                                  follower_entry.global_id,
                                  followed_entry.global_id)
        if err is not None:
            # If there was an error during unfollowing, return it.
            return resp
        if is_foreign:
            # Local user won't have a from_instance, set it.
            from_instance = self._host_name
            s2s_follower = s2s_follow_pb2.FollowActivityUser(handle=from_handle,
                                                             host=from_instance)
            s2s_followed = s2s_follow_pb2.FollowActivityUser(handle=to_handle,
                                                             host=to_instance)
            s2s_req = s2s_follow_pb2.FollowDetails(follower=s2s_follower,
                                                   followed=s2s_followed)
            self._s2s_stub.SendUnfollowActivity(s2s_req)

        if self._recommender_stub is not None:
            req = recommend_follows_pb2.UpdateFollowRecommendationsRequest(
                follower=follower_entry.global_id,
                followed=followed_entry.global_id,
                following=False)
            self._recommender_stub.UpdateFollowRecommendations(req)

        resp.result_type = general_pb2.ResultType.OK
        return resp
    def ReceiveAnnounceActivity(self, req, context):
        self._logger.debug("Received announce for %s of %s from %s at %s",
                           req.target_id, req.announced_object,
                           req.announcer_id, req.announce_time.seconds)
        response = general_pb2.GeneralResponse(
            result_type=general_pb2.ResultType.OK)

        # Parse announcer, author and target ids
        author_tuple = self._users_util.parse_actor_details(req.author_ap_id)
        if author_tuple[0] is None:
            return parse_actor_error("author", req.author_ap_id)
        announcer_tuple = self._users_util.parse_actor_details(
            req.announcer_id)
        if announcer_tuple[0] is None:
            return parse_actor_error("announcer", req.announcer_id)

        author = self.get_user_by_ap_id(author_tuple)
        # TODO(sailslick) check if author is actual author/post exists irl
        # https://github.com/CPSSD/rabble/issues/409
        if author is None:
            self._logger.debug(
                "Author does not exist on this server, creating")
            # Author is foreign and doesn't exist.
            # Check if announcer exists
            announcer = self.get_user_by_ap_id(announcer_tuple)
            if announcer is None:
                # If announcer & author doesn't exist, announce is error
                self._logger.error(
                    "Received announce without knowing author or announcer")
                return general_pb2.GeneralResponse(
                    result_type=general_pb2.ResultType.ERROR_400,
                    error=
                    "Received announce without knowing author or announcer",
                )

            # Follower of announcer
            # Create author.
            author = self._users_util.get_or_create_user_from_db(
                handle=author_tuple[1],
                host=author_tuple[0],
                bio=author_tuple[2])
            # Create post
            article = self.create_post(author, req)
            if article is None:
                self._logger.debug(
                    "Issue creating new article from unknown author")
                return general_pb2.GeneralResponse(
                    result_type=general_pb2.ResultType.ERROR,
                    error="Could not create local version of article id: {}".
                    format(req.announced_object),
                )
        else:
            # Check if announcer exists
            announcer = self.get_user_by_ap_id(announcer_tuple)
            if announcer is None:
                # If announcer doesn't exist, target is follower of author
                # Add announcer
                announcer = self._users_util.get_or_create_user_from_db(
                    handle=announcer_tuple[1],
                    host=announcer_tuple[0],
                    bio=announcer_tuple[2])

            # Check if article exists
            article, err = self._activ_util.get_article_by_ap_id(
                req.announced_object)
            if err is not None and (author.host_is_null or not author.host):
                # Author is local but the post doesn't exist.
                # This is a bad request.
                self._logger.error(
                    "Received announce with local author for post that doesn't exist"
                )
                return general_pb2.GeneralResponse(
                    result_type=general_pb2.ResultType.ERROR_400,
                    error=
                    "Announce with local author for post that doesn't exist",
                )
            elif err is not None:
                message = "Could not find new article ap_id: {} even though author exists".format(
                    req.announced_object)
                self._logger.debug(message)
                # TODO(sailslick) check when author is foreign, maybe we didn't get article?
                # But this requires checking the ap_id of article and matching to author
                # Also check if author actually wrote this
                # https://github.com/CPSSD/rabble/issues/409
                # Create post
                article = self.create_post(author, req)
                if article is None:
                    return general_pb2.GeneralResponse(
                        result_type=general_pb2.ResultType.ERROR,
                        error="Could not create local version of article id: {}"
                        .format(req.announced_object),
                    )
            elif author.host_is_null or not author.host:
                self._logger.debug("Author and article local")
                # author and article local, check if author is target.
                # if not target, send success as author will receive share
                if req.target_id != req.author_ap_id:
                    self._logger.debug(
                        "Target is local but not author, returning success")
                    return response
                # if target, add to shares db, update share count, send to followers
                err_resp = self.add_share_update_count(announcer, article,
                                                       req.announce_time)
                if err_resp is not None:
                    return err_resp
                article_url = self._activ_util.build_local_article_url(
                    author, article)
                timestamp = self._activ_util.timestamp_to_rfc(
                    article.creation_datetime)
                article_obj = self._activ_util.build_article(
                    req.announced_object,
                    article.title,
                    timestamp,
                    req.author_ap_id,
                    article.md_body,
                    article.summary,
                    article_url=article_url,
                )
                return self.send_to_followers(author, req.announce_time,
                                              req.announcer_id, article_obj,
                                              response)

        # At this point, author, announcer and article all exist.
        err_resp = self.add_share_update_count(announcer, article,
                                               req.announce_time)
        if err_resp is not None:
            return err_resp
        return response