commit cd3981e2d3081dee2492979f330dfbacc596c1fc
Author: Inoue Yosuke <[email protected]>
Date: Tue, 29 Nov 2022 12:29:38 +0900
Initial commit
Diffstat:
4 files changed, 118 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
diff --git a/README.md b/README.md
@@ -0,0 +1,5 @@
+# slack-sesame-unlock
+[CANDY HOUSE の Sesame API](https://doc.candyhouse.co/ja/SesameAPI#web-api-%E3%81%AB%E3%82%88%E3%82%8B%E6%96%BD%E8%A7%A3%E9%8C%A0)を利用して、[セサミ3](https://jp.candyhouse.co/products/sesame3)を開錠・施錠する Slack bot です。
+
+[Outgoing Webhooks](https://api.slack.com/legacy/custom-integrations/outgoing-webhooks) での利用を想定しています。
+
diff --git a/main.py b/main.py
@@ -0,0 +1,88 @@
+#!/usr/bin/python3
+import json
+from base64 import b64encode
+from time import time
+from urllib.error import HTTPError, URLError
+from urllib.parse import parse_qs, urlencode
+from urllib.request import Request, urlopen
+from wsgiref.simple_server import make_server
+from Crypto.Hash import CMAC
+from Crypto.Cipher import AES
+
+# tokens
+SLACK_TOKEN = 'EXAMPLE_TOKEN'
+APIKEY = 'SESAME_API_KEY'
+SECRET = 'SESAME_API_SECRET'
+UUID = 'SESAME_UUID'
+
+# variables
+LISTEN_HOST = '127.0.0.1'
+LISTEN_PORT = 18080
+SESAME_HISTORY = 'slack-sesame-unlock'
+SLACK_OPEN_TRIGGER = 'ドア開けて'
+SLACK_TEXT_ON_CLOSE = 'ドア閉めた'
+SLACK_TEXT_ON_OPEN = 'ドア開けた'
+
+# 88/82/83 = toggle/lock/unlock
+CMD_OPEN = 83
+CMD_CLOSE = 82
+
+def send_command(cmd):
+ cmac = CMAC.new(bytes.fromhex(SECRET), ciphermod=AES)
+ cmac.update(int(time()).to_bytes(4, byteorder='little')[1:4])
+ data = json.dumps({
+ 'cmd': cmd,
+ 'history': b64encode(SESAME_HISTORY.encode('utf-8')).decode('utf-8'),
+ 'sign': cmac.hexdigest(),
+ }).encode('utf-8')
+ headers = {
+ 'Content-Type': 'application/json',
+ 'x-api-key': APIKEY,
+ }
+ return Request(
+ 'https://app.candyhouse.co/api/sesame2/{}/cmd'.format(UUID),
+ data,
+ headers
+ )
+
+def application(env, cb):
+ try:
+ reqlen = int(env.get('CONTENT_LENGTH', 0))
+ except ValueError:
+ reqlen = 0
+ d = parse_qs(env.get('wsgi.input').read(reqlen).decode('utf-8'))
+
+ if d.get('token', [''])[0] != SLACK_TOKEN:
+ body = b'Forbidden'
+ cb('401 Unauthorized', [
+ ('Content-Type', 'text/plain'),
+ ('Content-Length', str(len(body))),
+ ])
+ return [body]
+
+ reqtext = d.get('text', [''])[0]
+ if reqtext == SLACK_OPEN_TRIGGER:
+ cmd = CMD_OPEN
+ else:
+ cmd = CMD_CLOSE
+
+ try:
+ urlopen(send_command(cmd))
+ if cmd == CMD_CLOSE:
+ text = SLACK_TEXT_ON_CLOSE
+ else:
+ text = SLACK_TEXT_ON_OPEN
+ except HTTPError as e:
+ text = '{}: {}'.format(e.getcode(), e.read().decode('utf-8'))
+ except URLError as e:
+ text = 'Fatal: {}'.format(e.reason)
+
+ body = json.dumps({'text': text}).encode('utf-8')
+ cb('200 OK', [
+ ('Content-Type', 'application/json'),
+ ('Content-Length', str(len(body))),
+ ])
+ return [body]
+
+with make_server(LISTEN_HOST, LISTEN_PORT, application) as httpd:
+ httpd.serve_forever()
diff --git a/requirements.txt b/requirements.txt
@@ -0,0 +1 @@
+pycryptodome