+
+def round(turb, body, args):
+ """Implementation of the /round command
+
+ Displays puzzles in the same round(s) as the puzzle for the
+ current channel.
+
+ 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. If this first word is missing, this command will display
+ all puzzles in the round by default.
+
+ 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]
+
+ puzzle = puzzle_for_channel(turb, channel_id)
+ hunt = hunt_for_channel(turb, channel_id)
+
+ if not puzzle:
+ if hunt:
+ return bot_reply(
+ "This is not a puzzle channel, but is a hunt channel. "
+ + "Use /hunt if you want to see all rounds for this hunt.")
+ else:
+ return bot_reply(
+ "Sorry, this channel doesn't appear to be a puzzle channel "
+ + "so the `/round` command cannot work here.")
+
+ 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 = 'all'
+ else:
+ status = 'all'
+
+ # Separate search terms on spaces (but allow for quotation marks
+ # to capture spaces in a search term)
+ if terms:
+ terms = shlex.split(terms)
+
+ blocks = hunt_blocks(turb, hunt,
+ puzzle_status=status, search_terms=terms,
+ limit_to_rounds=puzzle.get('rounds', [])
+ )
+
+ requests.post(response_url,
+ json = { 'blocks': blocks },
+ headers = {'Content-type': 'application/json'}
+ )
+
+ return lambda_ok
+
+commands["/round"] = round
+
+def help_command(turb, body, args):
+ """Implementation of the /help command
+
+ Displays help on how to use Turbot.
+ """
+
+ channel_name = body['channel_name'][0]
+ channel_id = body['channel_id'][0]
+ response_url = body['response_url'][0]
+ user_id = body['user_id'][0]
+
+ # Process "/help me" first. It calls out to have_you_tried rather
+ # than going through our help system.
+ #
+ # Also, it reports in the current channel, (where all other help
+ # output is reported privately to the invoking user).
+ if args == "me":
+ to_try = "In response to <@{}> asking `/help me`:\n\n{}\n".format(
+ user_id, have_you_tried())
+
+ # If this is a direct message then there's not a usable channel_id
+ # and we have to use the response_url instead
+ if channel_name == "directmessage":
+ requests.post(response_url,
+ json = {"text": to_try},
+ headers = {"Content-type": "application/json"})
+ else:
+ turb.slack_client.chat_postMessage(
+ channel=channel_id, text=to_try)
+ return lambda_ok
+
+ help_string = turbot_help(args)
+
+ requests.post(response_url,
+ json = {"text": help_string},
+ headers = {"Content-type": "application/json"})
+
+ return lambda_ok
+
+commands["/help"] = help_command