Titivillus/quest/events.py

458 lines
10 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Individual functions for handling WebSocket events. Gets called by the
QuestConsumer object in consumers.py.
"""
2018-08-24 21:51:03 -04:00
# TODO: quest owner only events
2018-09-03 17:59:41 -04:00
# TODO: try/except on database calls
2018-08-20 07:23:33 -04:00
import re
2018-08-16 15:43:43 -04:00
import time
import types
2018-08-21 07:18:03 -04:00
import random
2018-08-16 15:43:43 -04:00
import bleach
from django.db import IntegrityError
2018-08-21 09:01:16 -04:00
from django.utils.timezone import localtime
2018-09-24 13:46:46 -04:00
from django.urls import reverse
2018-08-16 15:43:43 -04:00
2018-08-29 14:01:31 -04:00
from quest.models import *
2018-09-18 11:45:12 -04:00
from quest.tools import handle_img
2018-08-29 14:01:31 -04:00
from quest.forms import DiceCallForm, PollForm
def message(socket, data):
"""
Gets called when the server receives a 'message' event.
"""
# TODO: validation
message = data.get('message')
2018-08-16 15:43:43 -04:00
2018-08-21 07:18:03 -04:00
# cleaning
2018-09-24 08:02:53 -04:00
message = message[:512]
2018-08-16 15:43:43 -04:00
message = message.strip()
if not message:
return
tags = ["b", "code", "i", "s"]
message = bleach.clean(message, tags=tags)
2018-08-20 07:23:33 -04:00
2018-08-21 07:18:03 -04:00
# greentext
2018-08-20 07:23:33 -04:00
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)
2018-08-21 07:18:03 -04:00
# quote links
2018-08-20 07:23:33 -04:00
quotes = re.findall(r"&gt;&gt;\d+", message)
for quote in quotes:
msg_id = quote.replace("&gt;&gt;", "")
msg = '<a class="quotelink" '
2018-09-03 17:59:41 -04:00
msg += 'href="javascript:scroll_to_msg(\'' + msg_id + '\')" '
msg += 'onmousemove="show_preview(event, \'' + msg_id + '\')" '
msg += 'onmouseout="clear_preview()">' + quote + '</a>'
2018-08-20 07:23:33 -04:00
message = message.replace(quote, msg)
2018-08-21 07:18:03 -04:00
# handle image
2018-09-18 11:45:12 -04:00
message = handle_img(message)
2018-08-21 07:18:03 -04:00
# dice rolling
if any(map(message.startswith, ["/dice", "/roll"])):
reg = re.search(r"(\d+)d(\d+)([+-]\d+)?", data["message"])
if not reg:
return
try:
groups = [0 if d is None else int(d) for d in reg.groups()]
dice_num, dice_sides, dice_mod = groups
2018-09-24 09:59:11 -04:00
assert 1 <= dice_num <= 256
assert 1 <= dice_sides <= 256
assert -65536 <= dice_mod <= 65536
2018-08-21 07:18:03 -04:00
except (ValueError, AssertionError):
return
2018-08-24 21:51:03 -04:00
dice_results = [random.randint(1, dice_sides) for _ in range(dice_num)]
total = sum(dice_results) + dice_mod
roll_msg = f"Rolled {', '.join(map(str, dice_results))}"
2018-08-21 07:18:03 -04:00
if dice_mod:
if dice_mod > 0:
roll_msg += " + " + str(dice_mod)
else:
roll_msg += " - " + str(dice_mod)[1:]
roll_msg += " = " + str(total)
2018-08-24 21:51:03 -04:00
message += '<hr class="msgSrvHr"><b>' + roll_msg + "</b>"
2018-08-21 07:18:03 -04:00
user = socket.scope['user']
m = Message(
2018-09-24 08:02:53 -04:00
quest=Quest.objects.get(id=socket.quest_id),
message=message)
if user.username:
m.user = user
m.save()
data = {}
data['message_id'] = m.id
2018-08-16 15:43:43 -04:00
data['message'] = message
data['date'] = int(time.time())
data['name'] = user.username
socket.send('message', data)
2018-08-24 21:51:03 -04:00
# append rolls to dicecall
if any(map(message.startswith, ["/dice", "/roll"])):
try:
2018-09-24 08:02:53 -04:00
dc = DiceCall.objects.get(
post__quest__id=socket.quest_id,
open=True
)
2018-08-24 21:51:03 -04:00
except DiceCall.DoesNotExist:
return
dice_roll = f"{dice_num}d{dice_sides}"
if dice_mod:
if dice_mod > 0:
dice_roll += "+"
dice_roll += str(dice_mod)
if dc.strict and dc.dice_roll != dice_roll:
return
dr = DiceRoll(
dicecall=dc,
message=m,
roll=dice_roll,
results=re.search(r"Rolled (.+) =", roll_msg).group(1),
total=total,
)
dr.save()
if DiceRoll.objects.filter(dicecall=dc).count() == dc.rolls_taken:
dc.open = False
dc.save()
socket.send('close_post', {'post_id': dc.post_id})
data = {}
data['post_text'] = roll_msg + " (" + dice_roll + ")"
if dc.dice_challenge:
if total >= dc.dice_challenge:
data["post_text"] += " - Pass"
else:
data["post_text"] += " - Fail"
data['post_type'] = 'dice'
data['post_id'] = dc.post_id
socket.send('update_post', data)
2018-08-21 09:01:16 -04:00
def text_post(socket, data):
"""
Called when the QM creates a new text post.
"""
2018-08-23 07:40:28 -04:00
# TODO: security
post_text = data.get('text')
2018-08-21 09:01:16 -04:00
page_num = data.get('page_num')
# cleaning
2018-08-23 07:40:28 -04:00
post_text = bleach.clean(post_text.strip())
post_text = post_text.replace("\n", "<br>")
2018-08-21 09:01:16 -04:00
# handle image
2018-09-25 11:58:07 -04:00
quest = Quest.objects.get(id=socket.quest_id)
2018-08-21 09:01:16 -04:00
p = Post(
2018-09-25 11:58:07 -04:00
quest=quest,
page=Page.objects.get(quest=quest, page_num=page_num),
2018-08-21 09:01:16 -04:00
post_type='text',
2018-08-23 07:40:28 -04:00
post_text=post_text)
2018-08-21 09:01:16 -04:00
p.save()
data = {}
2018-08-23 07:40:28 -04:00
data['post_text'] = post_text
2018-08-21 09:01:16 -04:00
data['post_type'] = 'text'
data['date'] = localtime(p.timestamp).strftime('%Y-%m-%d %H:%M')
data['post_id'] = p.id
socket.send('new_post', data)
2018-08-23 07:40:28 -04:00
def dice_post(socket, data):
"""
Called when the QM makes a new dice post.
"""
page_num = data.get('page_num')
form = DiceCallForm(data)
if not form.is_valid():
return # error message?
2018-08-23 11:57:37 -04:00
form = form.cleaned_data
2018-08-23 07:40:28 -04:00
2018-08-24 21:51:03 -04:00
posts = Post.objects.filter(
2018-09-24 08:02:53 -04:00
quest__id=socket.quest_id, post_type='dice', dicecall__open=True)
2018-08-24 21:51:03 -04:00
for post in posts:
post.dicecall.open = False
post.dicecall.save()
socket.send('close_all_posts')
2018-08-23 11:57:37 -04:00
dice_roll = str(form['diceNum']) + "d"
dice_roll += str(form['diceSides'])
if form['diceMod']:
if form['diceMod'] > 0:
2018-08-23 07:40:28 -04:00
dice_roll += "+"
2018-08-23 11:57:37 -04:00
dice_roll += str(form['diceMod'])
2018-08-23 07:40:28 -04:00
post_text = "Roll " + dice_roll
2018-08-23 11:57:37 -04:00
if form['diceChal']:
post_text += " vs DC" + str(form['diceChal'])
2018-09-25 11:58:07 -04:00
quest = Quest.objects.get(id=socket.quest_id)
2018-08-23 11:57:37 -04:00
p = Post(
2018-09-25 11:58:07 -04:00
quest=quest,
page=Page.objects.get(quest=quest, page_num=page_num),
2018-08-23 11:57:37 -04:00
post_type='dice',
post_text=post_text
)
p.save()
2018-08-24 21:51:03 -04:00
dc = DiceCall(
2018-08-23 11:57:37 -04:00
post=p,
dice_roll=dice_roll,
strict=form['diceStrict'],
dice_challenge=form['diceChal'],
2018-08-24 17:45:18 -04:00
rolls_taken=form['diceRollsTaken'],
open=True,
2018-08-23 11:57:37 -04:00
)
2018-08-24 21:51:03 -04:00
dc.save()
2018-08-24 17:45:18 -04:00
2018-08-23 07:40:28 -04:00
data = {}
2018-08-29 14:01:31 -04:00
data['post_id'] = p.id
2018-08-23 07:40:28 -04:00
data['post_type'] = 'dice'
2018-08-29 14:01:31 -04:00
data['post_text'] = post_text
2018-08-23 11:57:37 -04:00
data['date'] = localtime(p.timestamp).strftime('%Y-%m-%d %H:%M')
2018-08-29 14:01:31 -04:00
socket.send('new_post', data)
def poll_post(socket, data):
"""
Called when the QM makes a new dice post.
"""
page_num = data.get('page_num')
form = PollForm(data)
if not form.is_valid():
return # error message?
form = form.cleaned_data
2018-09-25 11:58:07 -04:00
quest=Quest.objects.get(id=socket.quest_id)
2018-08-29 14:01:31 -04:00
p = Post(
2018-09-25 11:58:07 -04:00
quest=quest,
page=Page.objects.get(quest=quest, page_num=page_num),
2018-08-29 14:01:31 -04:00
post_type='poll',
post_text="Poll"
)
p.save()
pl = Poll(
post=p,
multi_choice=form.pop('multi_choice'),
allow_writein=form.pop('allow_writein'),
open=True
)
pl.save()
options = []
for key, option in form.items():
o = PollOption(
poll=pl,
text=option
)
o.save()
options.append(o)
data = {}
data['post_type'] = 'poll'
2018-08-23 11:57:37 -04:00
data['post_id'] = p.id
2018-08-29 14:01:31 -04:00
data['post_text'] = "Poll"
data['date'] = localtime(p.timestamp).strftime('%Y-%m-%d %H:%M')
2018-09-03 17:59:41 -04:00
data['options'] = [(o.id, o.text) for o in options]
2018-08-23 07:40:28 -04:00
socket.send('new_post', data)
2018-09-07 12:56:13 -04:00
def edit_post(socket, data):
"""
Called when the QM saves an edited post.
"""
post_id = data.get('post_id')
post_text = data.get('post_text')
# cleaning
post_text = bleach.clean(post_text.strip())
post_text = post_text.replace("\n", "<br>")
# handle image
p = Post.objects.get(id=post_id)
p.post_text = post_text
p.save()
data = {}
data['post_text'] = post_text
data['post_type'] = 'text'
data['post_id'] = p.id
socket.send('update_post', data)
2018-08-24 17:45:18 -04:00
def close_post(socket, data):
"""
Called when the QM closes an open post.
"""
post_id = data.get('post_id')
p = Post.objects.get(id=post_id)
if data.get('post_type') == 'dice':
p.dicecall.open = False
p.dicecall.save()
elif data.get('post_type') == 'poll':
p.poll.open = False
p.poll.save()
data = {}
data['post_id'] = post_id
socket.send('close_post', data)
def open_post(socket, data):
"""
Called when the QM opens a closed post.
"""
2018-08-24 18:24:25 -04:00
# TODO: only posts on last page can be opened
2018-08-24 17:45:18 -04:00
post_id = data.get('post_id')
p = Post.objects.get(id=post_id)
if data.get('post_type') == 'dice':
2018-08-24 21:51:03 -04:00
posts = Post.objects.filter(
2018-09-24 08:02:53 -04:00
quest__id=socket.quest_id, post_type='dice', dicecall__open=True)
2018-08-24 18:24:25 -04:00
for post in posts:
post.dicecall.open = False
post.dicecall.save()
socket.send('close_all_posts', {'post_type': 'dice'})
2018-08-24 17:45:18 -04:00
p.dicecall.open = True
p.dicecall.save()
elif data.get('post_type') == 'poll':
p.poll.open = True
p.poll.save()
data = {}
data['post_id'] = post_id
socket.send('open_post', data)
2018-09-03 17:59:41 -04:00
def vote(socket, data):
"""
Called when a player votes in a poll.
"""
post_id = data.get('post_id')
option_id = data.get('option_id')
polarity = data.get('polarity')
ip_address = socket.scope['client'][0]
user = socket.scope['user']
if polarity == False:
v = PollVote.objects.get(ip_address=ip_address, option__id=option_id)
v.delete()
else:
p = Poll.objects.get(post_id=post_id)
if p.multi_choice == False:
votes = PollVote.objects.filter(
ip_address=ip_address,
option__poll=p
)
for vote in votes:
vote.delete()
data = {}
data['option_id'] = vote.option.id
data['polarity'] = False
socket.send('vote', data)
socket.self_send('set_option_box', data)
v = PollVote(
option=PollOption.objects.get(id=option_id),
ip_address=ip_address
)
if user.username:
v.user = user
try:
v.save()
except IntegrityError: # shouldn't we check this first?
return
2018-09-03 17:59:41 -04:00
data = {}
data['option_id'] = option_id
data['polarity'] = polarity
socket.send('vote', data)
2018-09-03 17:59:41 -04:00
2018-09-05 08:41:53 -04:00
def write_in(socket, data):
"""
Called when a player creates a new write-in.
"""
post_id = data.get('post_id')
option_text = data.get('option_text', '')
user = socket.scope['user']
option_text = option_text.strip()
if not option_text:
return
option_text = "Write in: " + bleach.clean(option_text)
if len(option_text) > 200:
# error message?
return
p = Poll.objects.get(post_id=post_id)
o = PollOption(
poll=p,
text=option_text
)
o.save()
data = {}
data['post_id'] = post_id
data['post_type'] = 'poll'
data['option_id'] = o.id
data['option_text'] = option_text
socket.send('update_post', data)
2018-09-24 08:22:01 -04:00
def new_page(socket, data):
"""
Called when the QM creates a new page.
"""
title = data.get('page_title')
2018-09-25 11:58:07 -04:00
appendix = bool(data.get('appendix'))
2018-09-24 08:22:01 -04:00
quest = Quest.objects.get(id=socket.quest_id)
2018-09-25 11:58:07 -04:00
if appendix:
page = Page.objects.filter(
quest=quest,
appendix=True
).order_by('page_num').last()
if page:
page_num = chr(ord(page.page_num)+1)
else:
page_num = 'a'
else:
page_num = Page.objects.filter(
quest=quest,
appendix=False
).order_by('page_num').last().page_num + 1
p = Page(
2018-09-24 08:22:01 -04:00
quest=quest,
2018-09-25 11:58:07 -04:00
page_num=page_num,
2018-09-24 08:22:01 -04:00
title=title,
2018-09-25 11:58:07 -04:00
appendix=appendix,
2018-09-24 08:22:01 -04:00
)
p.save()
2018-09-24 13:46:46 -04:00
data = {}
data['page_num'] = p.page_num
data['title'] = title
2018-09-25 11:58:07 -04:00
if appendix:
data['appendix'] = True
else:
data['appendix'] = False
data['url'] = reverse('quest:quest', args=[socket.quest_id, p.page_num])
2018-09-24 13:46:46 -04:00
socket.send('new_page', data)
2018-09-24 08:22:01 -04:00
events = {}
for obj in dir():
if type(locals()[obj]) == types.FunctionType:
events[locals()[obj].__name__] = locals()[obj]