Esempio n. 1
0
def answer_request(request_id):

    try:
        search_request = g.db.query(Request).filter(Request.id == request_id).options(joinedload(Request.user)).one()
    except NoResultFound:
        abort(404)

    if search_request.status != "posted" or search_request.user == g.user:
        abort(404)

    new_chat = RequestedChat(url=str(uuid4()).replace("-", ""))
    g.db.add(new_chat)
    g.db.flush()

    new_chat_user = ChatUser.from_user(
        user=search_request.user,
        chat=new_chat,
        name=search_request.name,
        alias=search_request.alias,
        color=search_request.color,
        quirk_prefix=search_request.quirk_prefix,
        quirk_suffix=search_request.quirk_suffix,
        case=search_request.case,
        replacements=search_request.replacements,
        regexes=search_request.regexes,
        # XXX USER VARIABLES
    )
    g.db.add(new_chat_user)

    if len(search_request.scenario) > 0:
        g.db.add(Message(chat=new_chat, type="search_info", alias="Scenario", text=search_request.scenario))

    if len(search_request.prompt) > 0:
        g.db.add(
            Message(
                chat=new_chat,
                user=search_request.user,
                type="ic",
                color=new_chat_user.color,
                alias=new_chat_user.alias,
                name=new_chat_user.name,
                text=search_request.prompt,
            )
        )

    return redirect(url_for("rp_chat", url=new_chat.url))
Esempio n. 2
0
def run_matchmaker(
    lock_id, searchers_key, searcher_prefix, get_searcher_info,
    check_compatibility, ChatClass, get_character_info,
):

    # XXX get log level from stdin
    root = logging.getLogger()
    root.setLevel(logging.DEBUG)

    db = sm()

    print "Obtaining lock..."
    db.query(func.pg_advisory_lock(413, lock_id)).scalar()
    print "Lock obtained."

    redis = StrictRedis(connection_pool=redis_pool)

    searcher_ids = redis.smembers(searchers_key)

    while True:

        # Reset the searcher list for the next iteration.
        redis.delete(searchers_key)
        for searcher in searcher_ids:
            logging.debug("Waking unmatched searcher %s." % searcher)
            redis.publish("%s:%s" % (searcher_prefix, searcher), "{ \"status\": \"unmatched\" }")

        time.sleep(10)

        logging.info("Starting match loop.")

        searcher_ids = redis.smembers(searchers_key)

        # We can't do anything with less than 2 people, so don't bother.
        if len(searcher_ids) < 2:
            logging.info("Not enough searchers, skipping.")
            continue

        searchers = get_searcher_info(redis, searcher_ids)
        logging.debug("Searcher list: %s" % searchers)
        shuffle(searchers)

        already_matched = set()
        # Range hack so we don't check opposite pairs or against itself.
        for n in range(len(searchers)):
            s1 = searchers[n]

            for m in range(n + 1, len(searchers)):
                s2 = searchers[m]

                if s1["id"] in already_matched or s2["id"] in already_matched:
                    continue

                logging.debug("Comparing %s and %s." % (s1["id"], s2["id"]))

                match, options = check_compatibility(redis, s1, s2)
                if not match:
                    logging.debug("No match.")
                    continue

                blocked = (
                    db.query(func.count("*")).select_from(Block).filter(and_(
                        Block.blocking_user_id == s1["user_id"],
                        Block.blocked_user_id == s2["user_id"]
                    )).scalar() != 0
                    or db.query(func.count("*")).select_from(Block).filter(and_(
                        Block.blocking_user_id == s2["user_id"],
                        Block.blocked_user_id == s1["user_id"]
                    )).scalar() != 0
                )
                if blocked:
                    logging.debug("Blocked.")
                    continue

                new_url = str(uuid4()).replace("-", "")
                logging.info(
                    "Matched %s and %s, sending to %s."
                    % (s1["id"], s2["id"], new_url)
                )
                new_chat = ChatClass(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, **get_character_info(db, s1)))
                db.add(ChatUser.from_user(s2_user, chat_id=new_chat.id, number=2, **get_character_info(db, s2)))

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

                db.commit()

                already_matched.add(s1["id"])
                already_matched.add(s2["id"])

                match_message = json.dumps({ "status": "matched", "url": new_url })
                redis.publish("%s:%s" % (searcher_prefix, s1["id"]), match_message)
                redis.publish("%s:%s" % (searcher_prefix, s2["id"]), match_message)
                searcher_ids.remove(s1["id"])
                searcher_ids.remove(s2["id"])
Esempio n. 3
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))
Esempio n. 4
0
    def decorated_function(url, fmt=None, *args, **kwargs):

        # Helper for doing some special URL stuff with PM chats.
        # Normally we just query for a Chat object with the url. However, PM chat
        # URLs take the form "pm/<username>", so we have to look up the username,
        # find the User it belongs to, and use our URL and theirs to create a
        # special URL.

        if url == "pm":
            abort(404)

        elif url.startswith("pm/"):

            username = url[3:]
            if username == "":
                abort(404)

            # You can't PM yourself.
            if g.user is None or username.lower() == g.user.username.lower():
                abort(404)

            try:
                pm_user = g.db.query(User).filter(
                    func.lower(User.username) == username.lower()
                ).one()
            except NoResultFound:
                abort(404)

            # Fix case if necessary.
            if pm_user.username != username:
                if request.method != "GET":
                    abort(404)
                return redirect(url_for(request.endpoint, url="pm/" + pm_user.username, fmt=fmt))

            # Generate URL from our user ID and their user ID.
            # Sort so they're always in the same order.
            pm_url = "pm/" + ("/".join(sorted([str(g.user.id), str(pm_user.id)])))
            try:
                chat = g.db.query(PMChat).filter(
                    PMChat.url == pm_url,
                ).one()
            except NoResultFound:
                # Only create a new PMChat on the main chat page.
                if request.endpoint != "rp_chat":
                    abort(404)
                chat = PMChat(url=pm_url)
                g.db.add(chat)
                g.db.flush()
                # Create ChatUser for the other user.
                pm_chat_user = ChatUser.from_user(pm_user, chat_id=chat.id, number=1)
                g.db.add(pm_chat_user)
                g.db.flush()

            return f(chat, pm_user, url, fmt, *args, **kwargs)

        # Force lower case.
        if url != url.lower():
            if request.method != "GET":
                abort(404)
            return redirect(url_for(request.endpoint, url=url.lower(), fmt=fmt))

        try:
            chat = g.db.query(AnyChat).filter(AnyChat.url == url).one()
        except NoResultFound:
            abort(404)

        g.chat = chat
        g.chat_id = chat.id
        try:
            authorize_joining(g.redis, g.db, g)
        except BannedException:
            if request.endpoint != "rp_chat" or chat.url == "theoubliette":
                abort(403)
            if request.method != "GET":
                abort(404)
            return redirect(url_for(request.endpoint, url="theoubliette", fmt=fmt))
        except UnauthorizedException:
            abort(403)
        except TooManyPeopleException:
            if request.endpoint == "rp_chat":
                abort(403)

        return f(chat, None, url, fmt, *args, **kwargs)
Esempio n. 5
0
def chat(chat, pm_user, url, fmt=None):

    chat_dict = chat.to_dict()

    if chat.type == "pm":
        # Override title with the other person's username.
        chat_dict['title'] = "Messaging " + pm_user.username
        chat_dict['url'] = url

    # Get or create ChatUser.
    try:
        chat_user = g.db.query(ChatUser).filter(and_(
            ChatUser.user_id == g.user.id,
            ChatUser.chat_id == chat.id,
        )).one()
    except NoResultFound:
        # Don't allow more than 2 people in roulette chats.
        if chat.type == "roulette":
            return redirect(url_for("rp_log", url=url))
        new_number = (g.db.query(func.max(ChatUser.number)).filter(ChatUser.chat_id == chat.id).scalar() or 0) + 1
        chat_user = ChatUser.from_user(g.user, chat_id=chat.id, number=new_number)
        if chat.type == "group":
            # Disable typing notifications by default in group chats.
            chat_user.typing_notifications = False
            if g.user.id != chat.creator_id:
                chat_user.subscribed = False
            if chat.autosilence and not g.user.is_admin and g.user.id != chat.creator_id:
                chat_user.group = "silent"
        g.db.add(chat_user)
        g.db.flush()

    # Show the last 50 messages.
    messages = g.db.query(Message).filter(
        Message.chat_id == chat.id,
    )
    if not chat_user.show_system_messages:
        messages = messages.filter(Message.type.in_(("ic", "ooc", "me")))
    messages = messages.options(joinedload(Message.chat_user)).order_by(
        Message.posted.desc(),
    ).limit(50).all()
    messages.reverse()

    latest_message_id = messages[-1].id if len(messages) > 0 else 0
    latest_time = time.mktime(messages[-1].posted.timetuple()) if len(messages) > 0 else 0

    if fmt == "json":

        return jsonify({
            "chat": chat_dict,
            "chat_user": chat_user.to_dict(include_options=True),
            "messages": [
                _.to_dict() for _ in messages
            ],
            "latest_message_id": latest_message_id,
        })

    # Character and search character info for settings form.
    characters = g.db.query(Character).filter(Character.user_id == g.user.id).order_by(Character.title).all()
    character_shortcuts = {_.shortcut: _.id for _ in characters if _.shortcut is not None}
    search_character_groups = g.db.query(SearchCharacterGroup).order_by(
        SearchCharacterGroup.order,
    ).options(joinedload(SearchCharacterGroup.characters)).all()

    return render_template(
        "rp/chat/chat.html",
        url=url,
        chat=chat_dict,
        chat_user=chat_user,
        chat_user_dict=chat_user.to_dict(include_options=True),
        messages=messages,
        latest_message_id=latest_message_id,
        latest_time=latest_time,
        case_options=case_options,
        characters=characters,
        character_shortcuts=character_shortcuts,
        search_character_groups=search_character_groups,
        themes=themes,
    )