Beispiel #1
0
def set_info():

    # Validation: We must be allowed to set the topic.
    if not g.chat_user.can("set_info"):
        abort(403)

    description = request.form["description"].strip()[:Message.MAX_LENGTH]
    rules = request.form["rules"].strip()[:Message.MAX_LENGTH]
    # If it hasn't changed, don't bother sending a message about it.
    if (description == g.chat.description and rules == g.chat.rules):
        return "", 204
    g.chat.description = description
    g.chat.rules = rules

    send_message(
        g.db, g.redis,
        Message(
            chat_id=g.chat.id,
            user_id=g.user.id,
            name=g.chat_user.name,
            type="chat_meta",
            text="%s [%s] edited the chat information." % (
                g.chat_user.name,
                g.chat_user.acronym,
            ),
        ), g.user_list)

    return "", 204
Beispiel #2
0
def reap():
    redis = reap.redis
    with session_scope() as db:
        current_time = int(time.time())
        disconnected_users = set()

        # Long poll sessions.
        for dead in redis.zrangebyscore("chats_alive", 0, current_time):
            logger.info("Reaping %s" % dead)
            chat_id, session_id = dead.split('/')
            user_id = redis.hget("chat:%s:online" % chat_id, session_id)
            disconnected = disconnect(redis, chat_id, session_id)
            # Only send a timeout message if they were already online.
            if not disconnected:
                logger.info("Not sending timeout message.")
                continue
            disconnected_users.add((chat_id, user_id, False))

        # Sockets.
        for dead in redis.zrangebyscore("sockets_alive", 0, current_time):
            logger.info("Reaping %s" % dead)
            chat_id, session_id, socket_id = dead.split('/')
            user_id = redis.hget("chat:%s:online" % chat_id, socket_id)
            disconnected = disconnect(redis, chat_id, socket_id)
            redis.srem("chat:%s:sockets:%s" % (chat_id, session_id), socket_id)
            redis.zrem("sockets_alive", "%s/%s/%s" % (chat_id, session_id, socket_id))
            # Only send a timeout message if they were already online.
            if not disconnected:
                logger.info("Not sending timeout message.")
                continue
            disconnected_users.add((chat_id, user_id, True))

        for chat_id, user_id, reaped_socket in disconnected_users:
            try:
                dead_chat_user = db.query(ChatUser).filter(and_(
                    ChatUser.user_id == user_id,
                    ChatUser.chat_id == chat_id,
                )).options(joinedload(ChatUser.chat), joinedload(ChatUser.user)).one()
            except NoResultFound:
                logger.error("Unable to find ChatUser (chat %s, user %s)." % (chat_id, user_id))
                continue

            if reaped_socket:
                typing_key = "chat:%s:typing" % chat_id
                if redis.srem(typing_key, dead_chat_user.number):
                    redis.publish("channel:%s:typing" % chat_id, json.dumps({
                        "typing": list(int(_) for _ in redis.smembers(typing_key)),
                    }))

            if dead_chat_user.computed_group == "silent" or dead_chat_user.chat.type in ("pm", "roulette"):
                send_userlist(db, redis, dead_chat_user.chat)
            else:
                send_message(db, redis, Message(
                    chat_id=chat_id,
                    user_id=dead_chat_user.user_id,
                    type="timeout",
                    name=dead_chat_user.name,
                    text="%s's connection timed out." % dead_chat_user.name,
                ))
            logger.info("Sent timeout message for ChatUser (chat %s, user %s)." % (chat_id, user_id))
Beispiel #3
0
def send_join_message(user_list, db, redis, context):
    """
    Send join message or delete previous disconnect message:
    * If the last message in the chat was a disconnect from this user, it's
      deleted.
    * If not, a join message is sent.
    * Either way, the user list is refreshed.
    """

    # Queue their last_online update.
    redis.hset("queue:usermeta", "chatuser:%s" % context.chat_user.user_id, json.dumps({
        "last_online": str(time.time()),
        "chat_id": context.chat_user.chat_id,
    }))

    if context.chat_user.computed_group == "silent" or context.chat.type in ("pm", "roulette"):
        send_userlist(user_list, db, context.chat)
    else:
        last_message = db.query(Message).filter(Message.chat_id == context.chat.id).order_by(Message.posted.desc()).first()
        # If they just disconnected, delete the disconnect message instead.
        if last_message is not None and last_message.type in ("disconnect", "timeout") and last_message.user_id == context.user.id:
            delete_message(user_list, db, last_message, force_userlist=True)
        else:
            send_message(db, user_list.redis, Message(
                chat_id=context.chat.id,
                user_id=context.user.id,
                type="join",
                name=context.chat_user.name,
                text="%s [%s] joined chat. %s" % (
                    context.chat_user.name,
                    context.chat_user.acronym,
                    "~~MxRP STAFF~~" if context.user.is_admin else ""
                ),
            ), user_list)
Beispiel #4
0
def send():

    if g.chat_user.computed_group == "silent":
        abort(403)

    if "text" not in request.form:
        abort(400)

    text = request.form["text"].strip()[:Message.MAX_LENGTH]

    if text == "":
        abort(400)

    message_type = request.form.get("type", "ic")
    if message_type not in ("ic", "ooc", "me"):
        message_type = "ic"

    # Set color, name and acronym based on a saved character.
    character = None
    if "character_id" in request.form:
        try:
            character = g.db.query(Character).filter(
                and_(
                    Character.id == request.form["character_id"],
                    Character.user_id == g.user.id,
                )).order_by(Character.title).one()
        except NoResultFound:
            pass

    # Clear typing status so the front end doesn't have to.
    if g.user_list.user_stop_typing(g.chat_user.number):
        g.redis.publish(
            "channel:%s:typing" % g.chat.id,
            json.dumps({
                "typing": g.user_list.user_numbers_typing(),
            }))

    g.chat_user.draft = ""

    send_message(
        g.db, g.redis,
        Message(
            chat_id=g.chat.id,
            user_id=g.user.id,
            type=message_type,
            color=character.color
            if character is not None else g.chat_user.color,
            acronym=character.acronym
            if character is not None else g.chat_user.acronym,
            name=character.name if character is not None else g.chat_user.name,
            text=text,
        ), g.user_list)

    return "", 204
Beispiel #5
0
def set_topic():

    # Validation: We must be allowed to set the topic.
    if not g.chat_user.can("set_topic"):
        abort(403)

    topic = request.form["topic"].strip()[:500]
    # If it hasn't changed, don't bother sending a message about it.
    if topic == g.chat.topic:
        return "", 204
    g.chat.topic = topic

    if topic == "":
        send_message(
            g.db, g.redis,
            Message(
                chat_id=g.chat.id,
                user_id=g.user.id,
                name=g.chat_user.name,
                type="chat_meta",
                text="%s [%s] removed the conversation topic." % (
                    g.chat_user.name,
                    g.chat_user.acronym,
                ),
            ), g.user_list)
    else:
        send_message(
            g.db, g.redis,
            Message(
                chat_id=g.chat.id,
                user_id=g.user.id,
                name=g.chat_user.name,
                type="chat_meta",
                text="%s [%s] changed the topic to \"%s\"" % (
                    g.chat_user.name,
                    g.chat_user.acronym,
                    topic,
                ),
            ), g.user_list)

    return "", 204
Beispiel #6
0
def unban(chat, pm_user, url, fmt):

    if chat.type != "group":
        abort(404)

    try:
        own_chat_user = g.db.query(ChatUser).filter(
            and_(
                ChatUser.chat_id == chat.id,
                ChatUser.user_id == g.user.id,
            )).one()
    except NoResultFound:
        abort(404)

    if not own_chat_user.can("ban"):
        abort(403)

    try:
        unban_chat_user = g.db.query(ChatUser).filter(
            and_(
                ChatUser.chat_id == chat.id,
                ChatUser.number == int(request.form["number"]),
            )).one()
    except (NoResultFound, ValueError):
        abort(404)

    try:
        ban = g.db.query(Ban).filter(
            and_(Ban.chat_id == chat.id,
                 Ban.user_id == unban_chat_user.user_id)).one()
    except NoResultFound:
        abort(404)

    g.db.delete(ban)

    send_message(
        g.db, g.redis,
        Message(
            chat_id=chat.id,
            user_id=unban_chat_user.user_id,
            type="user_action",
            name=own_chat_user.name,
            text="%s [%s] unbanned %s [%s] from the chat." % (
                own_chat_user.name,
                own_chat_user.acronym,
                unban_chat_user.name,
                unban_chat_user.acronym,
            ),
        ), g.user_list)

    if "Referer" in request.headers:
        return redirect(request.headers["Referer"])
    return redirect(url_for("rp_chat_users", url=url))
Beispiel #7
0
def save_from_character():

    try:
        character = g.db.query(Character).filter(
            and_(
                Character.id == request.form["character_id"],
                Character.user_id == g.user.id,
            )).order_by(Character.title).one()
    except NoResultFound:
        abort(404)

    # Remember old values so we can check if they've changed later.
    old_name = g.chat_user.name
    old_acronym = g.chat_user.acronym

    # Send a message if name, acronym or color has changed.
    changed = (g.chat_user.name != character.name
               or g.chat_user.acronym != character.acronym
               or g.chat_user.color != character.color)

    g.chat_user.name = character.name
    g.chat_user.acronym = character.acronym
    g.chat_user.color = character.color
    g.chat_user.quirk_prefix = character.quirk_prefix
    g.chat_user.quirk_suffix = character.quirk_suffix
    g.chat_user.case = character.case
    g.chat_user.replacements = character.replacements
    g.chat_user.regexes = character.regexes

    if changed:
        if g.chat_user.computed_group == "silent":
            send_userlist(g.user_list, g.db, g.chat)
        else:
            send_message(
                g.db, g.redis,
                Message(
                    chat_id=g.chat.id,
                    user_id=g.user.id,
                    type="user_info",
                    name=g.chat_user.name,
                    text="%s [%s] is now %s [%s]." % (
                        old_name,
                        old_acronym,
                        g.chat_user.name,
                        g.chat_user.acronym,
                    ),
                ), g.user_list)

    return jsonify(g.chat_user.to_dict(include_options=True))
Beispiel #8
0
def send_quit_message(db, redis, chat_user, user, chat, type="disconnect"):
    if chat_user.computed_group == "silent" or chat.type in ("pm", "roulette"):
        send_userlist(db, redis, chat)
    else:
        if type == "disconnect":
            text = "%s [%s] disconnected." % (chat_user.name, chat_user.acronym)
        elif type == "timeout":
            text = "%s's connection timed out." % chat_user.name
        send_message(db, redis, Message(
            chat_id=chat.id,
            user_id=user.id,
            type=type,
            name=chat_user.name,
            text=text,
        ))
Beispiel #9
0
def join(redis, db, context):

    # Queue their last_online update.
    redis.hset(
        "queue:usermeta", "chatuser:%s" % (context.chat_user.user_id),
        json.dumps({
            "last_online": str(time.time()),
            "chat_id": context.chat_user.chat_id,
        }))
    user_online = str(context.user.id) in redis.hvals("chat:%s:online" %
                                                      context.chat.id)

    # Add them to the online list.
    # Use socket id for websockets or session id for long polling.
    online_id = context.id if hasattr(context, "id") else context.session_id
    redis.hset("chat:%s:online" % context.chat.id, online_id, context.user.id)

    # Send join message if user isn't already online. Or not, if they're silent.
    if not user_online:
        if context.chat_user.computed_group == "silent" or context.chat.type in (
                "pm", "roulette"):
            send_userlist(db, redis, context.chat)
        else:
            last_message = db.query(Message).filter(
                Message.chat_id == context.chat.id).order_by(
                    Message.posted.desc()).first()
            # If they just disconnected, delete the disconnect message instead.
            if last_message is not None and last_message.type in (
                    "disconnect",
                    "timeout") and last_message.user_id == context.user.id:
                delete_message(db, redis, last_message, force_userlist=True)
            else:
                send_message(
                    db, redis,
                    Message(
                        chat_id=context.chat.id,
                        user_id=context.user.id,
                        type="join",
                        name=context.chat_user.name,
                        text="%s [%s] joined chat. %s" %
                        (context.chat_user.name, context.chat_user.acronym,
                         "~~MSPARP STAFF~~" if context.user.is_admin else ""),
                    ))

    return not user_online
Beispiel #10
0
def save():

    # Remember old values so we can check if they've changed later.
    old_name = g.chat_user.name
    old_acronym = g.chat_user.acronym
    old_color = g.chat_user.color

    new_details = validate_character_form(request.form)
    g.chat_user.search_character_id = new_details["search_character_id"]
    g.chat_user.name = new_details["name"]
    g.chat_user.acronym = new_details["acronym"]
    g.chat_user.color = new_details["color"]
    g.chat_user.quirk_prefix = new_details["quirk_prefix"]
    g.chat_user.quirk_suffix = new_details["quirk_suffix"]
    g.chat_user.case = new_details["case"]
    g.chat_user.replacements = new_details["replacements"]
    g.chat_user.regexes = new_details["regexes"]

    # Send a message if name or acronym has changed.
    if g.chat_user.name != old_name or g.chat_user.acronym != old_acronym:
        if g.chat_user.computed_group == "silent":
            send_userlist(g.user_list, g.db, g.chat)
        else:
            send_message(
                g.db, g.redis,
                Message(
                    chat_id=g.chat.id,
                    user_id=g.user.id,
                    type="user_info",
                    name=g.chat_user.name,
                    text=("%s [%s] is now %s [%s].") % (
                        old_name,
                        old_acronym,
                        g.chat_user.name,
                        g.chat_user.acronym,
                    ),
                ), g.user_list)
    # Just refresh the user list if the color has changed.
    elif g.chat_user.color != old_color:
        send_userlist(g.user_list, g.db, g.chat)

    return jsonify(g.chat_user.to_dict(include_options=True))
Beispiel #11
0
def reap_chat(chat_id):
    user_list = UserListStore(NewparpRedis(connection_pool=redis_chat_pool),
                              chat_id)
    old_user_ids = user_list.user_ids_online()
    if not old_user_ids:
        return

    with session_scope() as db:
        chat = db.query(Chat).filter(Chat.id == chat_id).one()
        chat_users = {
            _.user_id: _
            for _ in db.query(ChatUser).filter(
                and_(
                    ChatUser.chat_id == chat_id,
                    ChatUser.user_id.in_(old_user_ids),
                ))
        }

        for socket_id, user_id in user_list.inconsistent_entries():
            chat_user = chat_users[user_id]
            dead = user_list.socket_disconnect(socket_id, chat_user.number)
            if dead:
                logger.debug("dead: %s" % chat_user)
                # TODO optimise this when reaping several people at once?
                if chat_user.computed_group == "silent" or chat.type in (
                        "pm", "roulette"):
                    send_userlist(user_list, db, chat)
                else:
                    send_message(
                        db, reap_chat.redis,
                        Message(
                            chat=chat,
                            user_id=chat_user.user_id,
                            type="timeout",
                            name=chat_user.name,
                            text="%s's connection timed out." % chat_user.name,
                        ), user_list)
    def run(self, chat_id, data):
        if len(data["messages"]) == 0:
            return

        self.load_lists()

        for message in data["messages"]:
            if message["user_number"] is None:
                continue

            message["hash"] = hashlib.md5(
                ":".join([message["color"], message["acronym"], message["text"]])
                .encode("utf-8").lower()
            ).hexdigest()

            try:
                self.check_connection_spam(chat_id, message)
                self.check_banned_names(chat_id, message)
                self.check_message_filter(chat_id, message)
                self.check_warnlist(chat_id, message)

            except Mark as e:
                with session_scope() as db:
                    q = db.query(Message).filter(Message.id == message["id"]).update({"spam_flag": str(e)})
                    message.update({"spam_flag": str(e)})
                    self.redis.publish("spamless:live", json.dumps(message))

            except Silence as e:
                with session_scope() as db:
                    # XXX maybe cache this?
                    try:
                        chat_user, user, chat = db.query(
                            ChatUser, User, AnyChat,
                        ).join(
                            User, ChatUser.user_id == User.id,
                        ).join(
                            AnyChat, ChatUser.chat_id == AnyChat.id,
                        ).filter(and_(
                            ChatUser.chat_id == chat_id,
                            ChatUser.number == message["user_number"],
                        )).one()
                    except NoResultFound:
                        continue

                    if chat.type != "group":
                        flag_suffix = chat.type.upper()
                    elif chat_user.computed_group in ("admin", "creator"):
                        flag_suffix = chat_user.computed_group.upper()
                    else:
                        flag_suffix = "SILENCED"

                    db.query(Message).filter(Message.id == message["id"]).update({
                        "spam_flag": str(e) + " " + flag_suffix,
                    })

                    message.update({"spam_flag": str(e) + " " + flag_suffix})
                    self.redis.publish("spamless:live", json.dumps(message))

                    if flag_suffix == "SILENCED":
                        db.query(ChatUser).filter(and_(
                            ChatUser.chat_id == chat_id,
                            ChatUser.number == message["user_number"]
                        )).update({"group": "silent"})
                        send_message(db, self.redis, Message(
                            chat_id=chat_id,
                            type="spamless",
                            name="The Spamless",
                            acronym="\u264b",
                            text="Spam has been detected and silenced. Please come [url=http://help.msparp.com/]here[/url] or ask a chat moderator to unsilence you if this was an accident.",
                            color="626262"
                        ), True)
Beispiel #13
0
def user_action():

    action = request.form["action"]
    if action not in ("kick", "ban"):
        abort(400)

    # Validation #1: We must be allowed to perform this action.
    if not g.chat_user.can(action):
        abort(403)

    # Fetch the ChatUser we're trying to act upon.
    try:
        set_chat_user, set_user = g.db.query(ChatUser, User).join(
            User,
            ChatUser.user_id == User.id,
        ).filter(
            and_(
                ChatUser.chat_id == g.chat.id,
                ChatUser.number == int(request.form["number"]),
            )).one()
    except (ValueError, NoResultFound):
        abort(404)

    # Validation #2: Set user's group must be lower than ours.
    if set_chat_user.computed_rank >= g.chat_user.computed_rank:
        abort(403)

    if action == "kick":
        # Don't allow them back in for 30 seconds.
        kick_key = "kicked:%s:%s" % (g.chat.id, set_user.id)
        g.redis.set(kick_key, 1)
        g.redis.expire(kick_key, 30)

        g.redis.publish(
            "channel:%s:%s" % (g.chat.id, set_user.id),
            "{\"exit\":\"kick\"}",
        )

        # Only send a kick message if they're currently online.
        if g.user_list.user_disconnect(set_user.id, set_chat_user.number):
            send_message(
                g.db, g.redis,
                Message(chat_id=g.chat.id,
                        user_id=set_user.id,
                        type="user_action",
                        name=g.chat_user.name,
                        text=("%s [%s] kicked %s [%s] from the chat.") % (
                            g.chat_user.name,
                            g.chat_user.acronym,
                            set_chat_user.name,
                            set_chat_user.acronym,
                        )), g.user_list)

        return "", 204

    elif action == "ban":
        # In private chats, this un-invites someone instead of banning them.
        if g.chat.publicity == "private":
            deleted = g.db.query(Invite).filter(
                and_(
                    Invite.chat_id == g.chat.id,
                    Invite.user_id == set_user.id,
                )).delete()
            # Don't send a message if there wasn't an invite.
            if not deleted:
                return "", 204
            ban_message = ("%s [%s] un-invited %s [%s] from the chat.") % (
                g.chat_user.name,
                g.chat_user.acronym,
                set_chat_user.name,
                set_chat_user.acronym,
            )
        else:
            # Skip if they're already banned.
            if g.db.query(func.count('*')).select_from(Ban).filter(
                    and_(
                        Ban.chat_id == g.chat.id,
                        Ban.user_id == set_user.id,
                    )).scalar() != 0:
                return "", 204
            reason = None
            if "reason" in request.form:
                reason = request.form["reason"].strip()[:500] or None
            g.db.add(
                Ban(
                    user_id=set_user.id,
                    chat_id=g.chat.id,
                    creator_id=g.user.id,
                    reason=reason,
                ))
            if request.form.get("reason") is not None:
                ban_message = (
                    "%s [%s] banned %s [%s] from the chat. Reason: %s") % (
                        g.chat_user.name,
                        g.chat_user.acronym,
                        set_chat_user.name,
                        set_chat_user.acronym,
                        reason,
                    )
            else:
                ban_message = ("%s [%s] banned %s [%s] from the chat.") % (
                    g.chat_user.name,
                    g.chat_user.acronym,
                    set_chat_user.name,
                    set_chat_user.acronym,
                )
        g.redis.publish(
            "channel:%s:%s" % (g.chat.id, set_user.id),
            "{\"exit\":\"ban\"}",
        )

        g.user_list.user_disconnect(set_user.id, set_chat_user.number)

        send_message(
            g.db, g.redis,
            Message(
                chat_id=g.chat.id,
                user_id=set_user.id,
                type="user_action",
                name=g.chat_user.name,
                text=ban_message,
            ), g.user_list)
        # Unsubscribe if necessary.
        set_chat_user.subscribed = False
        return "", 204
Beispiel #14
0
def invite(chat, pm_user, url, fmt):

    if request.form["username"] == g.user.username:
        abort(404)

    if chat.type != "group" or chat.publicity != "private":
        abort(404)

    try:
        own_chat_user = g.db.query(ChatUser).filter(
            and_(
                ChatUser.chat_id == chat.id,
                ChatUser.user_id == g.user.id,
            )).one()
    except NoResultFound:
        abort(404)

    if not own_chat_user.can("invite"):
        abort(403)

    try:
        invite_user = g.db.query(User).filter(
            func.lower(
                User.username) == request.form["username"].lower(), ).one()
    except NoResultFound:
        if ("X-Requested-With" in request.headers
                and request.headers["X-Requested-With"] == "XMLHttpRequest"):
            abort(404)
        return redirect((request.headers.get("Referer")
                         or url_for("rp_invites", url=url)).split("?")[0] +
                        "?invite_error=no_user")

    if g.db.query(func.count("*")).select_from(Invite).filter(
            and_(
                Invite.chat_id == chat.id,
                Invite.user_id == invite_user.id,
            )).scalar() == 0:
        g.db.add(
            Invite(chat_id=chat.id,
                   user_id=invite_user.id,
                   creator_id=g.user.id))
        # Subscribe them to the chat and make it unread so they get a notification about it.
        try:
            invite_chat_user = g.db.query(ChatUser).filter(
                and_(
                    ChatUser.chat_id == chat.id,
                    ChatUser.user_id == invite_user.id,
                )).one()
        except NoResultFound:
            new_number = (g.db.query(func.max(ChatUser.number)).filter(
                ChatUser.chat_id == chat.id).scalar() or 0) + 1
            invite_chat_user = ChatUser.from_user(user=invite_user,
                                                  chat_id=chat.id,
                                                  number=new_number)
            # Disable typing notifications by default in group chats.
            invite_chat_user.typing_notifications = False
            g.db.add(invite_chat_user)
        invite_chat_user.subscribed = True
        invite_chat_user.last_online = chat.last_message - timedelta(0, 1)
        send_message(
            g.db, g.redis,
            Message(
                chat_id=chat.id,
                user_id=invite_user.id,
                type="user_action",
                name=own_chat_user.name,
                text="%s [%s] invited %s to the chat." % (
                    own_chat_user.name,
                    own_chat_user.acronym,
                    invite_user.username,
                ),
            ), g.user_list)

    if ("X-Requested-With" in request.headers
            and request.headers["X-Requested-With"] == "XMLHttpRequest"):
        return "", 204

    if "Referer" in request.headers:
        return redirect(request.headers["Referer"].split("?")[0])
    return redirect(url_for("rp_invites", url=url))
Beispiel #15
0
def uninvite(chat, pm_user, url, fmt):

    if request.form["username"] == g.user.username:
        abort(404)

    if chat.type != "group" or chat.publicity != "private":
        abort(404)

    try:
        own_chat_user = g.db.query(ChatUser).filter(
            and_(
                ChatUser.chat_id == chat.id,
                ChatUser.user_id == g.user.id,
            )).one()
    except NoResultFound:
        abort(404)

    if not own_chat_user.can("invite"):
        abort(403)

    try:
        invite_user = g.db.query(User).filter(
            func.lower(
                User.username) == request.form["username"].lower(), ).one()
    except NoResultFound:
        abort(404)

    if g.db.query(func.count("*")).select_from(Invite).filter(
            and_(
                Invite.chat_id == chat.id,
                Invite.user_id == invite_user.id,
            )).scalar() != 0:
        g.db.query(Invite).filter(
            and_(
                Invite.chat_id == chat.id,
                Invite.user_id == invite_user.id,
            )).delete()
        # Unsubscribing is impossible if they don't have access to the chat, so
        # we need to force-unsubscribe them here.
        try:
            invite_chat_user = g.db.query(ChatUser).filter(
                and_(
                    ChatUser.chat_id == chat.id,
                    ChatUser.user_id == invite_user.id,
                )).one()
            invite_chat_user.subscribed = False
        except NoResultFound:
            pass
        send_message(
            g.db, g.redis,
            Message(
                chat_id=chat.id,
                user_id=invite_user.id,
                type="user_action",
                name=own_chat_user.name,
                text="%s [%s] un-invited %s from the chat." % (
                    own_chat_user.name,
                    own_chat_user.acronym,
                    invite_user.username,
                ),
            ), g.user_list)

    if ("X-Requested-With" in request.headers
            and request.headers["X-Requested-With"] == "XMLHttpRequest"):
        return "", 204

    if "Referer" in request.headers:
        return redirect(request.headers["Referer"].split("?")[0])
    return redirect(url_for("rp_invites", url=url))
def comparison_callback(results, searcher_id_1):
    redis = comparison_callback.redis

    if redis.exists("lock:matchmaker"):
        logger.debug("locked. calling again in 1 second.")
        comparison_callback.apply_async((results, searcher_id_1), countdown=1)
        return
    redis.setex("lock:matchmaker", 60, 1)

    # Check if there's a match.
    matched_searchers = [_ for _ in results if _[0] is not None]
    if not matched_searchers:
        logger.debug("no results")
        redis.delete("lock:matchmaker")
        return
    logger.debug("results: %s" % matched_searchers)
    shuffle(matched_searchers)

    # Fetch searcher 1.
    s1 = fetch_searcher(redis, searcher_id_1)
    if not all(s1[:-2]):
        logger.debug("%s has expired" % searcher_id_1)
        redis.delete("lock:matchmaker")
        return

    with session_scope() as db:
        # Pick a second searcher from the matches.
        for searcher_id_2, options in matched_searchers:
            s2 = fetch_searcher(redis, searcher_id_2)
            if all(s2[:-2]) and db.query(func.count("*")).select_from(Block).filter(or_(
                and_(Block.blocking_user_id == s1.user_id, Block.blocked_user_id == s2.user_id),
                and_(Block.blocking_user_id == s2.user_id, Block.blocked_user_id == s1.user_id),
            )).scalar() == 0:
                logger.debug("matched %s" % searcher_id_2)
                break
        else:
            logger.debug("all matches have expired")
            redis.delete("lock:matchmaker")
            return

        new_url = str(uuid4()).replace("-", "")
        logger.info("matched %s and %s, sending to %s." % (s1.id, s2.id, new_url))
        new_chat = SearchedChat(url=new_url)
        db.add(new_chat)
        db.flush()

        s1_user = db.query(User).filter(User.id == s1.user_id).one()
        s2_user = db.query(User).filter(User.id == s2.user_id).one()
        db.add(ChatUser.from_user(s1_user, chat_id=new_chat.id, number=1, search_character_id=s1.search_character_id, **s1.character))
        if s1_user != s2_user:
            db.add(ChatUser.from_user(s2_user, chat_id=new_chat.id, number=2, search_character_id=s2.search_character_id, **s2.character))

        if options:
            db.add(Message(
                chat_id=new_chat.id,
                type="search_info",
                text=" ".join(option_messages[_] for _ in options),
            ))

    pipe = redis.pipeline()
    match_key = "matched:%s:%s" % tuple(sorted([s1.user_id, s2.user_id]))
    pipe.set(match_key, 1)
    pipe.expire(match_key, 1800)
    pipe.srem("searchers", s1.id, s2.id)
    match_message = """{"status":"matched","url":"%s"}""" % new_url
    pipe.publish("searcher:%s" % s1.id, match_message)
    pipe.publish("searcher:%s" % s2.id, match_message)
    pipe.delete("lock:matchmaker")
    pipe.execute()
Beispiel #17
0
def set_group():

    # Admins and creators can do this from the chat users list, so only require
    # a socket if we're not one.
    if (not g.user_list.session_has_open_socket(g.session_id, g.user.id)
            and not (g.user.is_admin or g.user.id == g.chat.creator_id)):
        abort(403)

    # Validation #1: We must be allowed to set groups.
    if not g.chat_user.can("set_group"):
        abort(403)

    # Validation #2: Set group must be lower than ours.
    # Also 400 if the group is invalid.
    set_group = request.form["group"]
    try:
        if ChatUser.group_ranks[set_group] >= g.chat_user.computed_rank:
            abort(403)
    except KeyError:
        abort(400)

    # Fetch the ChatUser we're trying to change.
    try:
        set_chat_user, set_user = g.db.query(ChatUser, User).join(
            User,
            ChatUser.user_id == User.id,
        ).filter(
            and_(
                ChatUser.chat_id == g.chat.id,
                ChatUser.number == int(request.form["number"]),
            )).one()
    except (ValueError, NoResultFound):
        abort(404)

    # Validation #3: Set user's group must be lower than ours.
    if set_chat_user.computed_rank >= g.chat_user.computed_rank:
        abort(403)

    # If we're changing them to their current group, just send a 204 without
    # actually doing anything.
    if set_group == set_chat_user.group:
        return "", 204

    if set_group == "mod3":
        message = (
            "%s [%s] set %s [%s] to Professional Wet Blanket. They can now silence, kick and ban other users."
        )
    elif set_group == "mod2":
        message = (
            "%s [%s] set %s [%s] to Bum's Rusher. They can now silence and kick other users."
        )
    elif set_group == "mod1":
        message = (
            "%s [%s] set %s [%s] to Amateur Gavel-Slinger. They can now silence other users."
        )
    elif set_group == "user":
        if set_chat_user.group == "silent":
            message = ("%s [%s] unsilenced %s [%s].")
        else:
            message = ("%s [%s] removed moderator status from %s [%s].")
    elif set_group == "silent":
        message = ("%s [%s] silenced %s [%s].")

    set_chat_user.group = set_group

    send_message(
        g.db, g.redis,
        Message(
            chat_id=g.chat.id,
            user_id=set_chat_user.user_id,
            name=set_chat_user.name,
            type="user_group",
            text=message % (
                g.chat_user.name,
                g.chat_user.acronym,
                set_chat_user.name,
                set_chat_user.acronym,
            ),
        ), g.user_list)

    return "", 204
Beispiel #18
0
def set_flag():

    flag = request.form["flag"]
    value = request.form["value"]
    if "flag" not in request.form or "value" not in request.form:
        abort(400)

    # Validation: We must be allowed to set flags.
    if not g.chat_user.can("set_flag"):
        abort(403)

    # Boolean flags.
    if (flag in ("autosilence") and value in ("on", "off")):
        new_value = value == "on"
        if new_value == getattr(g.chat, flag):
            return "", 204
        setattr(g.chat, flag, new_value)
        message = ("%%s [%%s] switched %s %s.") % (flag, value)

    elif (flag == "style" and value in ("script", "paragraph", "either")):
        if value == g.chat.style:
            return "", 204
        g.chat.style = value
        if g.chat.style == "script":
            message = "%s [%s] marked the chat as script style."
        elif g.chat.style == "paragraph":
            message = "%s [%s] marked the chat as paragraph style."
        elif g.chat.style == "either":
            message = "%s [%s] marked the chat as either style."

    elif (flag == "level" and value in level_options):
        if value == g.chat.level:
            return "", 204
        g.chat.level = value
        if g.chat.level == "sfw":
            message = "%s [%s] marked the chat as safe for work."
        elif g.chat.level == "nsfws":
            message = "%s [%s] marked the chat as not safe for work due to sexual content."
        elif g.chat.level == "nsfwv":
            message = "%s [%s] marked the chat as not safe for work due to violent content."
        elif g.chat.level == "nsfw-extreme":
            message = "%s [%s] marked the chat as NSFW extreme."

    # Publicity is also an enum because we might add options for password
    # protected or invite only chats in the future.
    elif (flag == "publicity" and value in GroupChat.publicity.type.enums):
        # Only admins can set/unset pinned and admin_only.
        admin_values = ("pinned", "admin_only")
        if (value in admin_values or g.chat.publicity
                in admin_values) and g.chat_user.computed_group != "admin":
            abort(403)
        if value == g.chat.publicity:
            return "", 204
        g.chat.publicity = value
        if g.chat.publicity == "private":
            message = "%s [%s] set the chat to private. Only users who have been invited can enter."
        elif g.chat.publicity == "unlisted":
            message = "%s [%s] unlisted the chat. Anyone can visit this chat, but only if they have the URL."
        elif g.chat.publicity == "listed":
            message = "%s [%s] listed the chat. It's now listed on the public chats page."
        elif g.chat.publicity == "pinned":
            message = "%s [%s] pinned the chat. It's now listed at the top of the public chats page."
        elif g.chat.publicity == "admin_only":
            message = "%s [%s] restricted the chat to admins."

    else:
        abort(400)

    send_message(
        g.db, g.redis,
        Message(
            chat_id=g.chat.id,
            user_id=g.user.id,
            type="chat_meta",
            text=message % (g.chat_user.name, g.chat_user.acronym),
        ), g.user_list)

    return "", 204