+ The args string can be a sub-command:
+
+ /puzzle new: Bring up a dialog to create a new puzzle
+
+ /puzzle edit: Edit the puzzle for the current channel
+
+ Or with no argument at all:
+
+ /puzzle: Print details of the current puzzle (if in a puzzle channel)
+ """
+
+ if args == 'new':
+ return new_puzzle(turb, body)
+
+ if args == 'edit':
+ return edit_puzzle_command(turb, body)
+
+ if len(args):
+ return bot_reply("Unknown syntax for `/puzzle` command. " +
+ "Valid commands are: `/puzzle`, `/puzzle edit`, " +
+ "and `/puzzle new` to display, edit, or create " +
+ "a puzzle.")
+
+ # For no arguments we print the current puzzle as a reply
+ channel_id = body['channel_id'][0]
+ response_url = body['response_url'][0]
+
+ puzzle = puzzle_for_channel(turb, channel_id)
+
+ if not puzzle:
+ hunt = hunt_for_channel(turb, channel_id)
+ if hunt:
+ return bot_reply(
+ "This is not a puzzle channel, but is a hunt channel. "
+ + "If you want to create a new puzzle for this hunt, use "
+ + "`/puzzle new`.")
+ else:
+ return bot_reply(
+ "Sorry, this channel doesn't appear to be a hunt or a puzzle "
+ + "channel, so the `/puzzle` command cannot work here.")
+
+ blocks = puzzle_blocks(puzzle, include_rounds=True)
+
+ # For a meta puzzle, also display the titles and solutions for all
+ # puzzles in the same round.
+ if puzzle.get('type', 'plain') == 'meta':
+ puzzles = hunt_puzzles_for_hunt_id(turb, puzzle['hunt_id'])
+
+ # Drop this puzzle itself from the report
+ puzzles = [p for p in puzzles if p['puzzle_id'] != puzzle['puzzle_id']]
+
+ for round in puzzle.get('rounds', [None]):
+ answers = round_quoted_puzzles_titles_answers(round, puzzles)
+ blocks += [
+ section_block(text_block(
+ "*Feeder solutions from round {}*".format(
+ round if round else "<none>"
+ ))),
+ section_block(text_block(answers))
+ ]
+
+ requests.post(response_url,
+ json = {'blocks': blocks},
+ headers = {'Content-type': 'application/json'}
+ )
+
+ return lambda_ok
+
+commands["/puzzle"] = puzzle
+
+def new(turb, body, args):
+ """Implementation of the `/new` command
+
+ This can be used to create a new hunt ("/new hunt") or a new
+ puzzle ("/new puzzle" or simply "/new"). So puzzle creation is the
+ default behavior (as it is much more common).
+
+ This operations are identical to the existing "/hunt new" and
+ "/puzzle new". I don't know that that redundancy is actually
+ helpful in the interface. But at least having both allows us to
+ experiment and decide which is more natural and should be kept
+ around long-term.
+ """
+
+ if args == 'hunt':
+ return new_hunt_command(turb, body)
+
+ return new_puzzle(turb, body)
+
+commands["/new"] = new
+
+def new_puzzle(turb, body):
+ """Implementation of the "/puzzle new" command
+
+ This brings up a dialog box for creating a new puzzle.
+ """