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