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