MOTH API ======= Data encoding ----- MOTH runs as an HTTP service, accepting standard HTTP `GET` and `POST`. Parameters may be encoded with standard `GET` query parameters (like `GET /endpoint?a=1&b=2`), or with `POST` as `application/x-www-form-encoded` data. Endpoints -------- ### `/state` Returns the current MOTH event state as a JSON object. #### Parameters * `id`: team ID (optional) #### Return ```json { "Config": { "Devel": false # true means this is a development server }, "Messages: "HTML to be rendered as broadcast messages", "TeamNames": { "self": "Requesting team name", # Only if regestered team id is a provided "0": "Team 1 Name", "1": "Team 2 Name", ... }, "PointsLog": { [1602679698, "0", "category", 1], # epochTime, teamID, category, points ... }, "Puzzles": { "category": [1, 2, 3, 6], # list of unlocked puzzles for category ... } } ``` #### Example HTTP transaction ##### Request ``` GET /state HTTP/1.0 ``` ##### Response This response has been reflowed for readability: an actual on-wire response would not have newlines or indentation. ``` HTTP/1.0 200 OK Content-Type: application/json {"Config": {"Devel":false}, "Messages":"

Welcome to the event!

Event ends at 19:00!

", "TeamNames":{ "0":"Mike and Jack", "12":"Team 2", "4":"Team 8" }, "PointsLog":[ [1602702696,"0","nocode",1], [1602702705,"0","sequence",1], [1602702787,"0","nocode",2], [1602702831,"0","sequence",2], [1602702839,"4","nocode",3], [1602702896,"0","sequence",8], [1602702900,"4","nocode",4], [1602702913,"0","sequence",16] ], "Puzzles":{ "indy":[12], "nocode":[1,2,3,4,10], "sequence":[1,2,8,16,19], "steg":[1] } } ``` ### `/register` Registers a name to a team ID. This is only required once per team, but user interfaces may find it less confusing to users to present a "login" page. For this reason "this team is already registered" does not return an error. #### Parameters * `id`: team ID * `name`: team name #### Return An object inspired by [JSend](https://github.com/omniti-labs/jsend): ```json { "status": "success/fail/error", "data": { "short": "short description", "description": "long description" } } ``` #### Example HTTP transaction ##### Request ``` POST /register HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 26 id=b387ca98&name=dirtbags ``` ##### Repsonse ``` HTTP/1.0 200 OK Content-Type: application/json Content-Length=86 {"status":"success","data":{"short":"registered","description":"Team ID registered"}} ``` ### `/answer` Submits an answer for points. If the answer is wrong, no points are awarded 😉 #### Parameters * `id`: team ID * `category`: along with `points`, uniquely identifies a puzzle * `points`: along with `category`, uniquely identifies a puzzle #### Return An object inspired by [JSend](https://github.com/omniti-labs/jsend): ```json { "status": "success/fail/error", "data": { "short": "short description", "description": "long description" } } ``` #### Example HTTP transaction ##### Request ``` POST /answer HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 62 id=b387ca98&category=sequence&points=2&answer=achilles+turnip ``` ##### Repsonse ``` HTTP/1.0 200 OK Content-Type: application/json Content-Length=83 {"status":"fail","data":{"short":"not accepted","description":"Incorrect answer"}} ``` ### `/content/{category}/{points}/{filename}` Retrieves static content associated with a puzzle. Every puzzle provides `puzzle.json`, a JSON object containing information about the puzzle such as the body and list of attached files. Parameters are all in the URL for this endpoint, so `curl` and `wget` can be used. #### Parameters * `{category}` (in URL): along with `{points}`, uniquely identifies a puzzle * `{points}` (in URL): along with `{category}`, uniquely identifies a puzzle * `{filename}` (in URL): filename to retrieve #### Return Raw file octets, with a (hopefully) suitable `Content-type` HTTP header field. #### Example HTTP transaction ##### Request ``` GET /content/sequence/1/puzzle.json HTTP/1.0 ``` ##### Repsonse ``` HTTP/1.0 200 OK Content-Type: application/json Content-Length: 397 {"Pre":{"Authors":["neale"],"Attachments":[],"Scripts":[],"Body":"\u003cp\u003e1 2 3 4 5 ⬜\u003c/p\u003e\n","AnswerPattern":"","AnswerHashes":["e7f6c011776e8db7cd330b54174fd76f7d0216b612387a5ffcfb81e6f0919683"]},"Post":{"Objective":"","Success":{"Acceptable":"","Mastery":""},"KSAs":null},"Debug":{"Log":[],"Errors":[],"Hints":[],"Summary":"Simple introduction to how this works"},"Answers":[]} ``` `puzzle.json` ------- The special file `puzzle.json` describes a puzzle. It is a JSON object with the following fields: ```json { "Pre": { # Things which appear before the puzzle is solved "Authors": ["Neale Pickett"], # List of puzzle authors, usually rendered as a footnote "Attachments": ["tiger.jpg"], # List of files attached to the puzzle "Scripts": [], # List of scripts which should be included in the HTML render of the puzzle "Body": "

Can you find the hidden text?

\"Grr\"

\n", # HTML puzzle body "AnswerPattern": "", # Regular expression to include in HTML input tag for validation "AnswerHashes": [ # List of SHA265 hashes of correct answers, for client-side answer checking "f91b1fe875cdf9e969e5bccd3e259adec5a987dcafcbc9ca8da62e341a7f29c6" ] }, "Post": { # Things reveal after the puzzle is solved "Objective": "Learn to examine images for hidden text", # Learning objective "Success": { # Measures of learning success "Acceptable": "Visually examine image to find hidden text", "Mastery": "Visually examine image to find hidden text" }, "KSAs": null # Knowledge, Skills, and Abilities covered by this puzzle }, "Debug": { # Debugging output used in development: all fields are emptied when making mothballs "Log": [], # Debug message log "Errors": [], # Errors encountered generating this puzzzle "Hints": [ # Hints for instructional assistants to provide to participants "Zoom in to the image and examine all sections carefully" ], "Summary": "text in image" # Summary of this puzzle, to help identify it in an overview of puzzles }, "Answers": ["sandwich"] # List of answers: empty in production } ```