]> git.cworth.org Git - turbot/blobdiff - turbot/interaction.py
Wire up a function to handle the edit_puzzle button
[turbot] / turbot / interaction.py
index dfe99c15059cc1060c798186e72eb8372d882fb3..f9fcedee34b174dcc4bc2bd0669e3214c9ea72c9 100644 (file)
@@ -2,7 +2,7 @@ from slack.errors import SlackApiError
 from turbot.blocks import (
     input_block, section_block, text_block, multi_select_block
 )
-from turbot.hunt import find_hunt_for_hunt_id
+from turbot.hunt import find_hunt_for_hunt_id, hunt_blocks
 from turbot.puzzle import find_puzzle_for_url
 import turbot.rot
 import turbot.sheets
@@ -13,12 +13,19 @@ import requests
 from botocore.exceptions import ClientError
 from boto3.dynamodb.conditions import Key
 from turbot.slack import slack_send_message
+import shlex
 
 actions = {}
+actions['button'] = {}
 commands = {}
 submission_handlers = {}
 
 # Hunt/Puzzle IDs are restricted to lowercase letters, numbers, and underscores
+#
+# Note: This restriction not only allows for hunt and puzzle ID values to
+# be used as Slack channel names, but it also allows for '-' as a valid
+# separator between a hunt and a puzzle ID (for example in the puzzle
+# edit dialog where a single attribute must capture both values).
 valid_id_re = r'^[_a-z0-9]+$'
 
 lambda_ok = {'statusCode': 200}
@@ -62,6 +69,15 @@ def multi_static_select(turb, payload):
 
 actions['multi_static_select'] = {"*": multi_static_select}
 
+def edit_puzzle(turb, payload):
+    """Handler for the action of user pressing an edit_puzzle button"""
+
+    print("DEBUG: In edit_puzzle with payload: {}".format(str(payload)))
+
+    return lambda_ok
+
+actions['button']['edit_puzzle'] = edit_puzzle
+
 def new_hunt(turb, payload):
     """Handler for the action of user pressing the new_hunt button"""
 
@@ -87,7 +103,7 @@ def new_hunt(turb, payload):
 
     return lambda_ok
 
-actions['button'] = {"new_hunt": new_hunt}
+actions['button']['new_hunt'] = new_hunt
 
 def new_hunt_submission(turb, payload, metadata):
     """Handler for the user submitting the new hunt modal
@@ -466,7 +482,12 @@ def puzzle_submission(turb, payload, metadata):
     # Add any new rounds to the database
     if new_rounds:
         for round in new_rounds.split(','):
-            rounds += round
+            # Drop any leading/trailing spaces from the round name
+            round = round.strip()
+            # Ignore any empty string
+            if not len(round):
+                continue
+            rounds.append(round)
             turb.table.put_item(
                 Item={
                     'hunt_id': hunt_id,
@@ -571,7 +592,8 @@ def solved(turb, body, args):
     # Set the status and solution fields in the database
     puzzle['status'] = 'solved'
     puzzle['solution'].append(args)
-    del puzzle['state']
+    if 'state' in puzzle:
+        del puzzle['state']
     turb.table.put_item(Item=puzzle)
 
     # Report the solution to the puzzle's channel
@@ -591,9 +613,9 @@ def solved(turb, body, args):
     # And update the puzzle's description
     set_channel_topic(turb, puzzle)
 
-    # And rename the sheet to prefix with "SOLVED: "
+    # And rename the sheet to suffix with "-SOLVED"
     turbot.sheets.renameSheet(turb, puzzle['sheet_url'],
-                              'SOLVED: ' + puzzle['name'])
+                              puzzle['name'] + "-SOLVED")
 
     # Finally, rename the Slack channel to add the suffix '-solved'
     channel_name = "{}-{}-solved".format(
@@ -606,3 +628,62 @@ def solved(turb, body, args):
     return lambda_ok
 
 commands["/solved"] = solved
+
+
+def hunt(turb, body, args):
+    """Implementation of the /hunt command
+
+    The (optional) args string can be used to filter which puzzles to
+    display. The first word can be one of 'all', 'unsolved', or
+    'solved' and can be used to display only puzzles with the given
+    status. Any remaining text in the args string will be interpreted
+    as search terms. These will be split into separate terms on space
+    characters, (though quotation marks can be used to include a space
+    character in a term). All terms must match on a puzzle in order
+    for that puzzle to be included. But a puzzle will be considered to
+    match if any of the puzzle title, round title, puzzle URL, puzzle
+    state, or puzzle solution match. Matching will be performed
+    without regard to case sensitivity and the search terms can
+    include regular expression syntax.
+    """
+
+    channel_id = body['channel_id'][0]
+    response_url = body['response_url'][0]
+
+    terms = None
+    if args:
+        # The first word can be a puzzle status and all remaining word
+        # (if any) are search terms. _But_, if the first word is not a
+        # valid puzzle status ('all', 'unsolved', 'solved'), then all
+        # words are search terms and we default status to 'unsolved'.
+        split_args = args.split(' ', 1)
+        status = split_args[0]
+        if (len(split_args) > 1):
+            terms = split_args[1]
+        if status not in ('unsolved', 'solved', 'all'):
+            terms = args
+            status = 'unsolved'
+    else:
+        status = 'unsolved'
+
+    # Separate search terms on spaces (but allow for quotation marks
+    # to capture spaces in a search term)
+    if terms:
+        terms = shlex.split(terms)
+
+    hunt = hunt_for_channel(turb, channel_id)
+
+    if not hunt:
+        return bot_reply("Sorry, this channel doesn't appear to "
+                         + "be a hunt or puzzle channel")
+
+    blocks = hunt_blocks(turb, hunt, puzzle_status=status, search_terms=terms)
+
+    requests.post(response_url,
+                  json = { 'blocks': blocks },
+                  headers = {'Content-type': 'application/json'}
+                  )
+
+    return lambda_ok
+
+commands["/hunt"] = hunt