Example #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()[:5000]
    rules = request.form["rules"].strip()[:5000]
    # 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,
        ),
    ))

    return "", 204
Example #2
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,
            ),
        ))
    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,
            ),
        ))

    return "", 204
Example #3
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()[:5000]

    if text == "":
        abort(400)

    message_type = request.form.get("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

    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,
    ))

    # Clear typing status so the front end doesn't have to.
    if g.redis.scard("chat:%s:sockets:%s" % (g.chat.id, g.session_id)):
        typing_key = "chat:%s:typing" % g.chat.id
        if g.redis.srem(typing_key, g.chat_user.number):
            g.redis.publish("channel:%s:typing" % g.chat.id, json.dumps({
                "typing": list(int(_) for _ in g.redis.smembers(typing_key)),
            }))

    g.chat_user.draft = ""

    return "", 204
Example #4
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,
        ),
    ))

    if "Referer" in request.headers:
        return redirect(request.headers["Referer"])
    return redirect(url_for("rp_chat_users", url=url))
Example #5
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)

    old_color = g.chat_user.color

    # 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.db, g.redis, 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,
                ),
            ))

    return jsonify(g.chat_user.to_dict(include_options=True))
Example #6
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.db, g.redis, 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,
                ),
            ))
    # Just refresh the user list if the color has changed.
    elif g.chat_user.color != old_color:
        send_userlist(g.db, g.redis, g.chat)

    return jsonify(g.chat_user.to_dict(include_options=True))
Example #7
0
                    .filter(and_(ChatUser.user_id == user_id, ChatUser.chat_id == chat_id))
                    .options(joinedload(ChatUser.chat), joinedload(ChatUser.user))
                    .one()
                )
            except NoResultFound:
                print "Unable to find ChatUser (chat %s, user %s)." % (chat_id, user_id)
                continue
            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,
                    ),
                )
            print "Sent timeout message."
        db.commit()

        # Generate connected/searching counters every 10 seconds.
        if int(current_time) % 10 == 0:
            print "Generating user counters."
            connected_users = set()
            next_index = 0
            while True:
                next_index, keys = redis.scan(next_index, "chat:*:online")
Example #8
0
def on_ps(ps_message):

    try:
        chat_id = int(ps_message["channel"].split(":")[1])
        data = json.loads(ps_message["data"])
    except (IndexError, KeyError, ValueError):
        return

    if "messages" not in data or len(data["messages"]) == 0:
        return

    for message in data["messages"]:

        if message["user_number"] is None:
            continue

        message["hash"] = hashlib.md5(message["text"].encode("utf-8").lower()).hexdigest()

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

        except Mark, e:
            time.sleep(0.1)
            q = db.query(Message).filter(Message.id == message["id"]).update({"spam_flag": e.message})
            db.commit()

        except Silence, e:
            time.sleep(0.1)

            # 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": e.message + " " + flag_suffix,
            })

            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, redis, Message(
                    chat_id=chat_id,
                    type="spamless",
                    name="The Spamless",
                    acronym=u"\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)

            db.commit()
Example #9
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,
            ),
        ))

    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_chat_invites", url=url))
Example #10
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,
            ),
        ))

    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_chat_invites", url=url))
Example #11
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 ("sfw", "nsfw", "nsfw-extreme")):
        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 == "nsfw":
            message = "%s [%s] marked the chat as not safe for work."
        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),
    ))

    return "", 204
Example #12
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":
        g.redis.publish(
            "channel:%s:%s" % (g.chat.id, set_user.id),
            "{\"exit\":\"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)
        # Only send a kick message if they're currently online.
        if disconnect_user(g.redis, g.chat.id, set_user.id):
            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,
                )
            ))
        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
            # Unsubscribe if necessary.
            set_chat_user.subscribed = False
            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\"}",
        )
        disconnect_user(g.redis, g.chat.id, set_user.id)
        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,
        ))
        set_chat_user.subscribed = False
        return "", 204
Example #13
0
def set_group():

    # 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,
        ),
    ))

    return "", 204