main.py (2441B)
1 #!/usr/bin/python3 2 import json 3 from base64 import b64encode 4 from time import time 5 from urllib.error import HTTPError, URLError 6 from urllib.parse import parse_qs, urlencode 7 from urllib.request import Request, urlopen 8 from wsgiref.simple_server import make_server 9 from Crypto.Hash import CMAC 10 from Crypto.Cipher import AES 11 12 # tokens 13 SLACK_TOKEN = 'EXAMPLE_TOKEN' 14 APIKEY = 'SESAME_API_KEY' 15 SECRET = 'SESAME_API_SECRET' 16 UUID = 'SESAME_UUID' 17 18 # variables 19 LISTEN_HOST = '127.0.0.1' 20 LISTEN_PORT = 18080 21 SESAME_HISTORY = 'slack-sesame-unlock' 22 SLACK_OPEN_TRIGGER = 'ドア開けて' 23 SLACK_TEXT_ON_CLOSE = 'ドア閉めた' 24 SLACK_TEXT_ON_OPEN = 'ドア開けた' 25 26 # 88/82/83 = toggle/lock/unlock 27 CMD_OPEN = 83 28 CMD_CLOSE = 82 29 30 def send_command(cmd): 31 cmac = CMAC.new(bytes.fromhex(SECRET), ciphermod=AES) 32 cmac.update(int(time()).to_bytes(4, byteorder='little')[1:4]) 33 data = json.dumps({ 34 'cmd': cmd, 35 'history': b64encode(SESAME_HISTORY.encode('utf-8')).decode('utf-8'), 36 'sign': cmac.hexdigest(), 37 }).encode('utf-8') 38 headers = { 39 'Content-Type': 'application/json', 40 'x-api-key': APIKEY, 41 } 42 return Request( 43 'https://app.candyhouse.co/api/sesame2/{}/cmd'.format(UUID), 44 data, 45 headers 46 ) 47 48 def application(env, cb): 49 try: 50 reqlen = int(env.get('CONTENT_LENGTH', 0)) 51 except ValueError: 52 reqlen = 0 53 d = parse_qs(env.get('wsgi.input').read(reqlen).decode('utf-8')) 54 55 if d.get('token', [''])[0] != SLACK_TOKEN: 56 body = b'Forbidden' 57 cb('401 Unauthorized', [ 58 ('Content-Type', 'text/plain'), 59 ('Content-Length', str(len(body))), 60 ]) 61 return [body] 62 63 reqtext = d.get('text', [''])[0] 64 if reqtext == SLACK_OPEN_TRIGGER: 65 cmd = CMD_OPEN 66 else: 67 cmd = CMD_CLOSE 68 69 try: 70 urlopen(send_command(cmd)) 71 if cmd == CMD_CLOSE: 72 text = SLACK_TEXT_ON_CLOSE 73 else: 74 text = SLACK_TEXT_ON_OPEN 75 except HTTPError as e: 76 text = '{}: {}'.format(e.getcode(), e.read().decode('utf-8')) 77 except URLError as e: 78 text = 'Fatal: {}'.format(e.reason) 79 80 body = json.dumps({'text': text}).encode('utf-8') 81 cb('200 OK', [ 82 ('Content-Type', 'application/json'), 83 ('Content-Length', str(len(body))), 84 ]) 85 return [body] 86 87 with make_server(LISTEN_HOST, LISTEN_PORT, application) as httpd: 88 httpd.serve_forever()