2016-10-16 20:32:00 -06:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import argparse
|
2016-10-17 13:24:54 -06:00
|
|
|
import base64
|
2016-10-16 20:32:00 -06:00
|
|
|
import glob
|
2016-10-17 13:24:54 -06:00
|
|
|
import hmac
|
2016-10-16 20:32:00 -06:00
|
|
|
import json
|
|
|
|
import mistune
|
2016-10-17 13:24:54 -06:00
|
|
|
import multidict
|
|
|
|
import os
|
2016-10-16 20:32:00 -06:00
|
|
|
import random
|
|
|
|
|
|
|
|
messageChars = b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
|
|
|
|
|
|
def djb2hash(buf):
|
|
|
|
h = 5381
|
|
|
|
for c in buf:
|
|
|
|
h = ((h * 33) + c) & 0xffffffff
|
|
|
|
return h
|
|
|
|
|
2016-10-17 13:24:54 -06:00
|
|
|
class Puzzle(multidict.MultiDict):
|
|
|
|
|
|
|
|
def __init__(self, seed):
|
|
|
|
super().__init__()
|
|
|
|
|
2016-10-16 20:32:00 -06:00
|
|
|
self.message = bytes(random.choice(messageChars) for i in range(20))
|
2016-10-17 13:24:54 -06:00
|
|
|
self.body = ''
|
|
|
|
|
|
|
|
self.rand = random.Random(seed)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_stream(cls, stream):
|
|
|
|
pzl = cls(None)
|
2016-10-16 20:32:00 -06:00
|
|
|
|
|
|
|
body = []
|
|
|
|
header = True
|
|
|
|
for line in stream:
|
|
|
|
if header:
|
|
|
|
line = line.strip()
|
|
|
|
if not line.strip():
|
|
|
|
header = False
|
|
|
|
continue
|
|
|
|
key, val = line.split(':', 1)
|
|
|
|
key = key.lower()
|
|
|
|
val = val.strip()
|
2016-10-17 13:24:54 -06:00
|
|
|
pzl.add(key, val)
|
2016-10-16 20:32:00 -06:00
|
|
|
else:
|
|
|
|
body.append(line)
|
2016-10-17 13:24:54 -06:00
|
|
|
pzl.body = ''.join(body)
|
|
|
|
return pzl
|
2016-10-16 20:32:00 -06:00
|
|
|
|
2016-10-17 13:24:54 -06:00
|
|
|
def add(self, key, value):
|
|
|
|
super().add(key, value)
|
2016-10-16 20:32:00 -06:00
|
|
|
if key == 'answer':
|
2016-10-17 13:24:54 -06:00
|
|
|
super().add(hash, djb2hash(value.encode('utf8')))
|
2016-10-16 20:32:00 -06:00
|
|
|
|
|
|
|
def htmlify(self):
|
|
|
|
return mistune.markdown(self.body)
|
|
|
|
|
|
|
|
def publish(self):
|
|
|
|
obj = {
|
2016-10-17 13:24:54 -06:00
|
|
|
'author': self['author'],
|
|
|
|
'hashes': self.getall('hash'),
|
2016-10-16 20:32:00 -06:00
|
|
|
'body': self.htmlify(),
|
|
|
|
}
|
|
|
|
return obj
|
|
|
|
|
|
|
|
def secrets(self):
|
|
|
|
obj = {
|
2016-10-17 13:24:54 -06:00
|
|
|
'answers': self.getall('answer'),
|
|
|
|
'summary': self['summary'],
|
2016-10-16 20:32:00 -06:00
|
|
|
}
|
|
|
|
return obj
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
parser = argparse.ArgumentParser(description='Build a puzzle category')
|
|
|
|
parser.add_argument('puzzledir', nargs='+', help='Directory of puzzle source')
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
for puzzledir in args.puzzledir:
|
|
|
|
puzzles = {}
|
|
|
|
secrets = {}
|
|
|
|
for puzzlePath in glob.glob(os.path.join(puzzledir, "*.moth")):
|
|
|
|
filename = os.path.basename(puzzlePath)
|
|
|
|
points, ext = os.path.splitext(filename)
|
|
|
|
points = int(points)
|
2016-10-17 13:24:54 -06:00
|
|
|
puzzle = Puzzle.from_stream(open(puzzlePath))
|
2016-10-16 20:32:00 -06:00
|
|
|
puzzles[points] = puzzle
|
|
|
|
|
|
|
|
for points in sorted(puzzles):
|
|
|
|
puzzle = puzzles[points]
|
|
|
|
print(puzzle.secrets())
|
|
|
|
|