anonkun/events.py

387 lines
9.3 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""
SocketIO events.
"""
2018-07-11 18:25:10 -04:00
# TODO: harden the fuck up
2018-07-05 13:27:48 -04:00
import re
import time
import random
2018-07-11 18:25:10 -04:00
import functools
import bleach
2018-07-13 08:17:29 -04:00
from flask import session, request
from flask_socketio import SocketIO, emit, join_room
2018-06-25 15:52:10 -04:00
import tools
import database as db
socketio = SocketIO()
2018-07-11 18:25:10 -04:00
def qm_only(msg=""):
"""
A decorator function to protect certain endpoints so that only the
QM can access them.
"""
# TODO: better docstring, test this more thoroughly
def actual_decorator(func):
@functools.wraps(func)
def _nop(*args, **kwargs):
data = args[0]
room = data.get("room")
2018-07-26 09:15:28 -04:00
res = db.get_quest_meta(room)
2018-07-11 18:25:10 -04:00
if not res:
return msg
2018-07-26 09:15:28 -04:00
if session.get("user_id") != res[2]:
2018-07-11 18:25:10 -04:00
return msg
return func(*args, **kwargs)
return _nop
return actual_decorator
@socketio.on('joined')
def joined(data):
"""
Sent by clients when they enter a room.
"""
room = data["room"]
join_room(room)
@socketio.on('message')
def message(data):
"""
Sent by a client when the user entered a new message.
"""
room = data["room"]
message = data["message"]
name = data["name"]
user_id = data.get("user_id")
message = message.strip()
if not message:
return
tags = ["b", "code", "i", "s"]
message = bleach.clean(message, tags=tags)
lines = []
for line in message.splitlines():
if line.startswith(">") and not line.startswith(">>"):
line = '<span class="greenText">' + line + '</span>'
lines.append(line)
message = "<br />".join(lines)
quotes = re.findall(r"&gt;&gt;\d+", message)
for quote in quotes:
msg_id = quote.replace("&gt;&gt;", "")
msg = '<a class="quotelink" '
msg += 'href="javascript:scrollToMsg(\'' + msg_id + '\')" '
msg += 'onmousemove="showPreview(event, \'' + msg_id + '\')" '
msg += 'onmouseout="clearPreview()">' + quote + '</a>'
message = message.replace(quote, msg)
2018-06-25 15:52:10 -04:00
message = tools.handle_img(message)
roll_msg = ""
if message.startswith("/dice") or message.startswith("/roll"):
roll_msg = handle_dice(data)
if roll_msg:
message += '<hr class="msgSrvHr" /><b>' + roll_msg + "</b>"
2018-07-05 13:27:48 -04:00
date = int(time.time())
message_id = db.log_chat_message(message, date, room, user_id)
data = {}
data["date"] = date
data["name"] = name
data["user_id"] = user_id
data["message"] = message
data["message_id"] = message_id
emit("message", data, room=room)
if roll_msg:
open_post_id = db.get_quest_meta(room)[3]
if open_post_id:
dice_call = db.get_dice_call(open_post_id)
dice_roll = re.search(r"(\d+d\d+(?:[+-]\d+)?)", message).group(1)
if dice_call[2] and dice_roll != dice_call[1]:
return
roll_results = re.search(r"Rolled (.+) =", roll_msg).group(1)
roll_total = int(re.search(r"= (\d+)", roll_msg).group(1))
roll_data = (dice_roll, roll_results, roll_total)
db.insert_quest_roll(message_id, room, open_post_id, roll_data)
if len(db.get_dice_rolls(post_id=open_post_id)) == dice_call[4]:
2018-07-13 08:17:29 -04:00
db.set_post_closed(room)
emit("close_post", {"post_id": open_post_id}, room=room)
data = {}
data["post"] = roll_msg + " (" + dice_roll + ")"
if dice_call[3]:
if roll_total >= dice_call[3]:
data["post"] += " - Pass"
else:
data["post"] += " - Fail"
data["post_type"] = "dice"
data["post_id"] = open_post_id
emit("update_post", data, room=room)
2018-07-05 13:27:48 -04:00
def handle_dice(data):
2018-07-05 13:27:48 -04:00
"""
Handle /dice or /roll messages.
"""
reg = re.search(r"(\d+)d(\d+)([+-]\d+)?", data["message"])
2018-07-05 13:27:48 -04:00
if not reg:
return
try:
groups = [0 if d is None else int(d) for d in reg.groups()]
diceNum, diceSides, diceMod = groups
2018-07-05 13:27:48 -04:00
assert 0 < diceNum < 100
assert 0 < diceSides < 100
assert -1000 < diceMod < 1000
except (ValueError, AssertionError):
return
dice = [random.randint(1, diceSides) for _ in range(diceNum)]
total = sum(dice)
if diceMod:
total += diceMod
2018-07-08 17:50:54 -04:00
msg = f"Rolled {', '.join(map(str, dice))}"
2018-07-05 13:27:48 -04:00
if diceMod:
if diceMod > 0:
msg += " + " + str(diceMod)
else:
msg += " - " + str(diceMod)[1:]
2018-07-08 17:50:54 -04:00
msg += " = " + str(total)
return msg
2018-07-05 13:27:48 -04:00
@socketio.on("new_post")
2018-07-11 18:25:10 -04:00
@qm_only()
def new_post(data, internal=False):
"""
Called when the QM makes a new post.
"""
2018-07-11 18:25:10 -04:00
room = data.get("room")
post = data.get("post")
2018-07-26 09:15:28 -04:00
page_num = data.get("page_num")
post = bleach.clean(post.strip())
post = post.replace("\n", "<br />")
post = tools.handle_img(post)
2018-07-11 18:25:10 -04:00
date = int(time.time())
2018-07-26 09:15:28 -04:00
post_id = db.insert_quest_post(room, page_num, "text", post, date)
2018-07-11 18:25:10 -04:00
db.set_post_closed(room)
data = {}
data["post"] = [post]
data["post_type"] = "text"
2018-07-11 18:25:10 -04:00
data["date"] = date
data["post_id"] = post_id
emit("new_post", data, room=room)
2018-06-21 20:44:43 -04:00
@socketio.on("update_post")
2018-07-11 18:25:10 -04:00
@qm_only()
2018-06-21 20:44:43 -04:00
def update_post(data):
"""
Called when the QM edits and saves a post.
"""
# TODO: enforce only update text posts
2018-07-11 18:25:10 -04:00
room = data["room"]
2018-06-21 20:44:43 -04:00
post = data["post"]
2018-06-21 22:49:25 -04:00
post = post.strip().replace("<br>", "<br />")
post = tools.handle_img(post)
2018-06-21 22:49:25 -04:00
2018-06-21 20:44:43 -04:00
post_id = data["post_id"]
db.update_quest_post(post_id, post)
data = {}
data["post"] = post
data["post_id"] = post_id
data["post_type"] = "text"
2018-06-21 20:44:43 -04:00
emit("update_post", data, room=room)
@socketio.on("dice_post")
2018-07-11 18:25:10 -04:00
@qm_only()
def dice_post(data):
"""
Called when the QM posts a new dice call.
"""
room = data["room"]
data = {k: v for k, v in data.items() if v}
try:
diceNum = int(data.get("diceNum", 0))
diceSides = int(data.get("diceSides", 0))
diceMod = int(data.get("diceMod", 0))
diceChal = int(data.get("diceChal", 0))
2018-07-11 18:25:10 -04:00
diceRollsTaken = int(data.get("diceRollsTaken", 0))
assert 0 < diceNum < 100
assert 0 < diceSides < 100
assert -1000 < diceMod < 1000
assert 0 <= diceChal < 100
2018-07-11 18:25:10 -04:00
assert 0 <= diceRollsTaken < 100
except (ValueError, AssertionError):
return
diceStrict = bool(data.get("diceStrict"))
2018-07-26 09:15:28 -04:00
page_num = data.get("page_num")
dice_roll = f"{data['diceNum']}d{data['diceSides']}"
if diceMod:
if diceMod > 0:
dice_roll += "+"
dice_roll += str(diceMod)
2018-07-08 17:50:54 -04:00
post = "Roll " + dice_roll
if diceChal:
post += " vs DC" + str(diceChal)
2018-07-11 18:25:10 -04:00
date = int(time.time())
2018-07-26 09:15:28 -04:00
post_id = db.insert_quest_post(room, page_num, "dice", post, date)
2018-07-11 18:25:10 -04:00
new_call = (dice_roll, diceStrict, diceChal, diceRollsTaken)
db.insert_dice_call(post_id, room, new_call)
db.set_post_open(post_id, room)
data = {}
data["post"] = post
data["post_type"] = "dice"
2018-07-11 18:25:10 -04:00
data["date"] = date
data["post_id"] = post_id
emit("new_post", data, room=room)
2018-07-08 17:50:54 -04:00
2018-07-11 18:25:10 -04:00
@socketio.on('close_post')
@qm_only()
2018-07-08 17:50:54 -04:00
def close_dice_call(data):
"""
2018-07-11 18:25:10 -04:00
Closes an active post.
2018-07-08 17:50:54 -04:00
"""
room = data["room"]
post_id = data.get("post_id")
2018-07-11 18:25:10 -04:00
db.set_post_closed(room)
2018-07-08 17:50:54 -04:00
data = {}
data["post_id"] = post_id
2018-07-11 18:25:10 -04:00
emit("close_post", data, room=room)
2018-07-08 17:50:54 -04:00
2018-07-11 18:25:10 -04:00
@socketio.on("open_post")
@qm_only()
2018-07-08 17:50:54 -04:00
def open_dice_call(data):
"""
2018-07-11 18:25:10 -04:00
Opens an active post. This is only permitted if the active post is
2018-07-08 17:50:54 -04:00
the last post in the quest.
"""
# TODO: enforce only open if last post
2018-07-11 18:25:10 -04:00
room = data["room"]
2018-07-08 17:50:54 -04:00
post_id = data.get("post_id")
2018-07-11 18:25:10 -04:00
db.set_post_open(post_id, room)
2018-07-08 17:50:54 -04:00
data = {}
data["post_id"] = post_id
2018-07-11 18:25:10 -04:00
emit("open_post", data, room=room)
@socketio.on("poll_post")
@qm_only()
def poll_post(data):
"""
2018-07-13 08:17:29 -04:00
Called when the QM posts a new poll.
2018-07-11 18:25:10 -04:00
"""
room = data.pop("room", None)
multi_choice = bool(data.pop("pollAllowMultipleChoices", None))
allow_writein = bool(data.pop("pollAllowUserOptions", None))
2018-07-26 09:15:28 -04:00
page_num = data.get("page_num")
2018-07-11 18:25:10 -04:00
options = []
for key, value in data.items():
if not value or not key.startswith("pollOption-"):
continue
if len(value) >= 200:
return
2018-07-13 08:17:29 -04:00
value = bleach.clean(value).replace("\n", "")
2018-07-11 18:25:10 -04:00
options.append(value)
post = "Poll"
date = int(time.time())
2018-07-26 09:15:28 -04:00
post_id = db.insert_quest_post(room, page_num, "poll", post, date)
2018-07-13 08:17:29 -04:00
db.insert_poll(post_id, room, multi_choice, allow_writein)
new_options = []
2018-07-11 18:25:10 -04:00
for option in options:
option_id = db.insert_poll_option(post_id, option)
new_options.append((option_id, option))
2018-07-11 18:25:10 -04:00
db.set_post_open(post_id, room)
data = {}
data["post"] = post
data["post_id"] = post_id
data["post_type"] = "poll"
data["date"] = int(time.time())
data["options"] = new_options
data["allow_writein"] = allow_writein
2018-07-11 18:25:10 -04:00
emit("new_post", data, room=room)
2018-07-13 08:17:29 -04:00
@socketio.on("vote")
def vote(data):
"""
Called when a user changes their vote on a poll.
"""
# TODO: ensure poll is open before counting vote
2018-07-13 08:17:29 -04:00
room = data.get("room")
option_id = data.get("option_id")
2018-07-18 22:08:36 -04:00
post_id = data.get("post_id")
2018-07-13 08:17:29 -04:00
polarity = data.get("polarity")
2018-07-18 19:09:54 -04:00
#ip_address = request.remote_addr
ip_address = request.headers.get("X-Real-Ip")
2018-07-18 22:08:36 -04:00
if not polarity:
2018-07-18 19:09:54 -04:00
db.remove_poll_vote(option_id, ip_address)
2018-07-18 22:08:36 -04:00
else:
poll = db.get_poll(post_id)
if poll[2]: # multi-vote allowed
db.insert_poll_vote(option_id, ip_address)
else:
votes = db.get_poll_votes_voted(post_id, ip_address)
for vote in votes:
db.remove_poll_vote(vote[0], ip_address)
data = {}
data["option_id"] = vote[0]
data["polarity"] = False
emit("vote", data, room=room)
emit("toggle_option_box", data, room=request.sid)
2018-07-18 22:08:36 -04:00
db.insert_poll_vote(option_id, ip_address)
2018-07-18 19:09:54 -04:00
data = {}
data["option_id"] = option_id
data["polarity"] = polarity
emit("vote", data, room=room)
2018-07-20 20:20:14 -04:00
@socketio.on("write_in")
def write_in(data):
"""
Called when a user submits a write-in option.
"""
room = data.get("room")
post_id = data.get("post_id")
option_text = data.get("option_text")
if len(option_text) > 200:
return
option_text = bleach.clean(option_text)
option_text = "Write-in: " + option_text
option_id = db.insert_poll_option(post_id, option_text)
data = {}
data["post_id"] = post_id
data["post_type"] = "poll"
data["option_id"] = option_id
data["option_text"] = option_text
emit("update_post", data, room=room)