slack-sesame-unlock

Sesame-3 unlock bot for Slack outgoing webhook
git clone https://git.kamikakushi.net/slack-sesame-unlock.git
Log | Files | Refs | README | LICENSE

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()