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
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
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
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))
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))
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))
.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")
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()
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))
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 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
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
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