diff --git a/quest/consumers.py b/quest/consumers.py
index 926b8ad..2e3f313 100644
--- a/quest/consumers.py
+++ b/quest/consumers.py
@@ -8,6 +8,7 @@ from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from .events import events
+from .models import Quest
class QuestConsumer(WebsocketConsumer):
"""
@@ -25,6 +26,11 @@ class QuestConsumer(WebsocketConsumer):
self.quest_id = self.scope['url_route']['kwargs']['quest_id']
self.group_name = 'quest_' + str(self.quest_id)
+ try:
+ Quest.objects.get(id=self.quest_id)
+ except Quest.DoesNotExist:
+ return
+
async_to_sync(self.channel_layer.group_add)(
self.group_name,
self.channel_name
diff --git a/quest/events.py b/quest/events.py
index 2253a45..a8247de 100644
--- a/quest/events.py
+++ b/quest/events.py
@@ -3,8 +3,6 @@
Individual functions for handling WebSocket events. Gets called by the
QuestConsumer object in consumers.py.
"""
-# TODO: quest owner only events
-# TODO: try/except on database calls
import re
import time
import types
@@ -25,7 +23,6 @@ def message(socket, data):
"""
Gets called when the server receives a 'message' event.
"""
- # TODO: validation
message = data.get('message')
# cleaning
@@ -64,6 +61,7 @@ def message(socket, data):
if not reg:
return
try:
+ # TODO: form validation?
groups = [0 if d is None else int(d) for d in reg.groups()]
dice_num, dice_sides, dice_mod = groups
assert 1 <= dice_num <= 256
@@ -150,20 +148,28 @@ def text_post(socket, data):
"""
Called when the QM creates a new text post.
"""
- # TODO: security
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
post_text = data.get('text')
page_num = data.get('page_num')
+ try:
+ page = Page.objects.get(quest=quest, page_num=page_num)
+ except Page.DoesNotExist:
+ return
+
# cleaning
post_text = bleach.clean(post_text.strip())
post_text = post_text.replace("\n", "
")
# handle image
- quest = Quest.objects.get(id=socket.quest_id)
p = Post(
quest=quest,
- page=Page.objects.get(quest=quest, page_num=page_num),
+ page=page,
post_type='text',
post_text=post_text)
p.save()
@@ -195,18 +201,31 @@ def dice_post(socket, data):
"""
Called when the QM makes a new dice post.
"""
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
page_num = data.get('page_num')
+ try:
+ page = Page.objects.get(quest=quest, page_num=page_num)
+ except Page.DoesNotExist:
+ return
+
form = DiceCallForm(data)
if not form.is_valid():
return # error message?
form = form.cleaned_data
posts = Post.objects.filter(
- quest__id=socket.quest_id, post_type='dice', dicecall__open=True)
+ quest=quest,
+ post_type='dice',
+ dicecall__open=True
+ )
for post in posts:
post.dicecall.open = False
post.dicecall.save()
- socket.send('close_all_posts')
+ socket.send('close_all_posts', {'post_type': 'dice'})
dice_roll = str(form['diceNum']) + "d"
dice_roll += str(form['diceSides'])
@@ -219,10 +238,9 @@ def dice_post(socket, data):
if form['diceChal']:
post_text += " vs DC" + str(form['diceChal'])
- quest = Quest.objects.get(id=socket.quest_id)
p = Post(
quest=quest,
- page=Page.objects.get(quest=quest, page_num=page_num),
+ page=page,
post_type='dice',
post_text=post_text
)
@@ -264,16 +282,25 @@ def poll_post(socket, data):
"""
Called when the QM makes a new dice post.
"""
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
+ try:
+ page = Page.objects.get(quest=quest, page_num=page_num)
+ except Page.DoesNotExist:
+ return
+
page_num = data.get('page_num')
form = PollForm(data)
if not form.is_valid():
return # error message?
form = form.cleaned_data
- quest=Quest.objects.get(id=socket.quest_id)
p = Post(
quest=quest,
- page=Page.objects.get(quest=quest, page_num=page_num),
+ page=page,
post_type='poll',
post_text="Poll"
)
@@ -322,16 +349,25 @@ def edit_post(socket, data):
"""
Called when the QM saves an edited post.
"""
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
post_id = data.get('post_id')
post_text = data.get('post_text')
+ try:
+ p = Post.objects.get(id=post_id)
+ except Post.DoesNotExist:
+ return
+
# cleaning
post_text = bleach.clean(post_text.strip())
post_text = post_text.replace("\n", "
")
# handle image
- p = Post.objects.get(id=post_id)
p.post_text = post_text
p.save()
@@ -346,8 +382,17 @@ def close_post(socket, data):
"""
Called when the QM closes an open post.
"""
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
post_id = data.get('post_id')
- p = Post.objects.get(id=post_id)
+ try:
+ p = Post.objects.get(id=post_id)
+ except Post.DoesNotExist:
+ return
+
if data.get('post_type') == 'dice':
p.dicecall.open = False
p.dicecall.save()
@@ -364,12 +409,23 @@ def open_post(socket, data):
"""
Called when the QM opens a closed post.
"""
- # TODO: only posts on last page can be opened
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
post_id = data.get('post_id')
- p = Post.objects.get(id=post_id)
+ try:
+ p = Post.objects.get(id=post_id)
+ except Post.DoesNotExist:
+ return
+
if data.get('post_type') == 'dice':
posts = Post.objects.filter(
- quest__id=socket.quest_id, post_type='dice', dicecall__open=True)
+ quest=quest,
+ post_type='dice',
+ dicecall__open=True
+ )
for post in posts:
post.dicecall.open = False
post.dicecall.save()
@@ -386,89 +442,18 @@ def open_post(socket, data):
socket.send('open_post', data)
-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
-
- data = {}
- data['option_id'] = option_id
- data['polarity'] = polarity
- socket.send('vote', data)
-
-
-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)
-
-
def new_page(socket, data):
"""
Called when the QM creates a new page.
"""
+ quest = Quest.objects.get(id=socket.quest_id)
+ user = socket.scope['user']
+ if quest.owner != user:
+ return # error message?
+
title = data.get('page_title')
appendix = bool(data.get('appendix'))
- quest = Quest.objects.get(id=socket.quest_id)
if appendix:
page = Page.objects.filter(
quest=quest,
@@ -518,6 +503,96 @@ def new_page(socket, data):
socket.send('message', data)
+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:
+ try:
+ v = PollVote.objects.get(
+ ip_address=ip_address,
+ option__id=option_id
+ )
+ except PollVote.DoesNotExist:
+ return
+ v.delete()
+ else:
+ try:
+ p = Poll.objects.get(post_id=post_id)
+ option = PollOption.objects.get(id=option_id)
+ except (Poll.DoesNotExist, PollOption.DoesNotExist):
+ return
+ pvs = PollVote.objects.filter(option=option, ip_address=ip_address)
+ if pvs.count() != 0:
+ return
+
+ 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)
+ pv = PollVote(
+ option=option,
+ ip_address=ip_address
+ )
+ if user.username:
+ pv.user = user
+ pv.save()
+
+ data = {}
+ data['option_id'] = option_id
+ data['polarity'] = polarity
+ socket.send('vote', data)
+
+
+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']
+
+ try:
+ p = Poll.objects.get(post_id=post_id)
+ except Poll.DoesNotExist:
+ return
+
+ 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
+ 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)
+
+
events = {}
for obj in dir():
if type(locals()[obj]) == types.FunctionType:
diff --git a/todo b/todo
index 1274fdc..7b28fb8 100644
--- a/todo
+++ b/todo
@@ -10,7 +10,6 @@ Quote backlinks
Improvements:
More options for text posts (lists and so on)
-More rigorous input checking in events.py
Poll vote highlights entire option
Total voters per poll
Chat archives