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}
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"""
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
# 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(
def hunt(turb, body, args):
"""Implementation of the /hunt command
- The (optional) args string should be one of 'all', 'solved', or
- 'unsolved' to indicate which set of puzzles should be
- displayed. If omitted, this command will default to showing only
- unsolved puzzles.
+ 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]
- status = args
- if len(status):
+ 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'):
- return bot_reply("Usage: /hunt [all|solved|unsolved]")
+ 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)
+ blocks = hunt_blocks(turb, hunt, puzzle_status=status, search_terms=terms)
requests.post(response_url,
json = { 'blocks': blocks },