X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=turbot%2Finteraction.py;h=ab35a395bbdc6fe1020b3d84611865a6bf6db69b;hb=7c526803f06e2af19deb4fb76bc85229ac62496a;hp=78e209aa10dd61803ac63c57e07ff027deac5449;hpb=aa0b6c1b27cc839116597b3900f6eb46347abef2;p=turbot diff --git a/turbot/interaction.py b/turbot/interaction.py index 78e209a..ab35a39 100644 --- a/turbot/interaction.py +++ b/turbot/interaction.py @@ -7,6 +7,7 @@ import json import re import requests from botocore.exceptions import ClientError +from turbot.slack import slack_send_message actions = {} commands = {} @@ -15,6 +16,8 @@ submission_handlers = {} # Hunt/Puzzle IDs are restricted to lowercase letters, numbers, and underscores valid_id_re = r'^[_a-z0-9]+$' +lambda_ok = {'statusCode': 200} + def bot_reply(message): """Construct a return value suitable for a bot reply @@ -70,10 +73,7 @@ def new_hunt(turb, payload): if (result['ok']): submission_handlers[result['view']['id']] = new_hunt_submission - return { - 'statusCode': 200, - 'body': 'OK' - } + return lambda_ok actions['button'] = {"new_hunt": new_hunt} @@ -148,9 +148,7 @@ def new_hunt_submission(turb, payload, metadata): # Invite the initiating user to the channel turb.slack_client.conversations_invite(channel=channel_id, users=user_id) - return { - 'statusCode': 200, - } + return lambda_ok def view_submission(turb, payload): """Handler for Slack interactive view submission @@ -198,10 +196,7 @@ def rot(turb, body, args): else: turb.slack_client.chat_postMessage(channel=channel_id, text=result) - return { - 'statusCode': 200, - 'body': "" - } + return lambda_ok commands["/rot"] = rot @@ -217,7 +212,7 @@ def get_table_item(turb, table_name, key, value): if 'Item' in response: return (response['Item'], table) else: - return None + return (None, None) def channel_is_puzzle(turb, channel_id, channel_name): """Given a channel ID/name return the database item for the puzzle @@ -257,7 +252,7 @@ def find_hunt_for_channel(turb, channel_id, channel_name): Returns a tuple of (hunt_name, hunt_id) or (None, None).""" - (hunt, hunts_table) = channel_is_hunt(turb, channel_id) + (hunt, _) = channel_is_hunt(turb, channel_id) if hunt: return (hunt['hunt_id'], hunt['name']) @@ -266,12 +261,14 @@ def find_hunt_for_channel(turb, channel_id, channel_name): # puzzle channel with a hunt-id prefix. hunt_id = channel_name.split('-')[0] + hunts_table = turb.db.Table("hunts") + response = hunts_table.scan( FilterExpression='hunt_id = :hunt_id', ExpressionAttributeValues={':hunt_id': hunt_id} ) - if 'Items' in response: + if 'Items' in response and len(response['Items']): item = response['Items'][0] return (item['hunt_id'], item['name']) @@ -305,9 +302,6 @@ def puzzle(turb, body, args): "blocks": [ section_block(text_block("*For {}*".format(hunt_name))), input_block("Puzzle name", "name", "Name of the puzzle"), - input_block("Puzzle ID", "puzzle_id", - "Used as part of channel name " - + "(no spaces nor punctuation)"), input_block("Puzzle URL", "url", "External URL of puzzle", optional=True) ] @@ -319,9 +313,7 @@ def puzzle(turb, body, args): if (result['ok']): submission_handlers[result['view']['id']] = puzzle_submission - return { - 'statusCode': 200 - } + return lambda_ok commands["/puzzle"] = puzzle @@ -336,14 +328,10 @@ def puzzle_submission(turb, payload, metadata): state = payload['view']['state']['values'] name = state['name']['name']['value'] - puzzle_id = state['puzzle_id']['puzzle_id']['value'] url = state['url']['url']['value'] - # Validate that the puzzle_id contains no invalid characters - if not re.match(valid_id_re, puzzle_id): - return submission_error("puzzle_id", - "Puzzle ID can only contain lowercase letters, " - + "numbers, and underscores") + # Create a Slack-channel-safe puzzle_id + puzzle_id = re.sub(r'[^a-zA-Z0-9_]', '', name).lower() # Create a channel for the puzzle hunt_dash_channel = "{}-{}".format(hunt_id, puzzle_id) @@ -352,9 +340,10 @@ def puzzle_submission(turb, payload, metadata): response = turb.slack_client.conversations_create( name=hunt_dash_channel) except SlackApiError as e: - return submission_error("puzzle_id", - "Error creating Slack channel: {}" - .format(e.response['error'])) + return submission_error( + "name", + "Error creating Slack channel {}: {}" + .format(hunt_dash_channel, e.response['error'])) puzzle_channel_id = response['channel']['id'] @@ -371,17 +360,23 @@ def puzzle_submission(turb, payload, metadata): } ) - return { - 'statusCode': 200 - } + return lambda_ok # XXX: This duplicates functionality eith events.py:set_channel_description def set_channel_topic(turb, puzzle): channel_id = puzzle['channel_id'] - description = puzzle['name'] + name = puzzle['name'] url = puzzle.get('url', None) sheet_url = puzzle.get('sheet_url', None) state = puzzle.get('state', None) + status = puzzle['status'] + + description = '' + + if status == 'solved': + description += "Solved: `{}` ".format('`, `'.join(puzzle['solution'])) + + description += name links = [] if url: @@ -418,4 +413,37 @@ def state(turb, body, args): set_channel_topic(turb, puzzle) + return lambda_ok + commands["/state"] = state + +def solved(turb, body, args): + """Implementation of the /solved command + + The args string should be a confirmed solution.""" + + channel_id = body['channel_id'][0] + channel_name = body['channel_name'][0] + user_name = body['user_name'][0] + + (puzzle, table) = channel_is_puzzle(turb, channel_id, channel_name) + + if not puzzle: + return bot_reply("Sorry, this is not a puzzle channel.") + + # Set the status and solution fields in the database + puzzle['status'] = 'solved' + puzzle['solution'].append(args) + table.put_item(Item=puzzle) + + # Report the solution to the puzzle's channel + slack_send_message( + turb.slack_client, channel_id, + "Puzzle mark solved by {}: `{}`".format(user_name, args)) + + # And update the puzzle's description + set_channel_topic(turb, puzzle) + + return lambda_ok + +commands["/solved"] = solved