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

commit cd3981e2d3081dee2492979f330dfbacc596c1fc
Author: Inoue Yosuke <[email protected]>
Date:   Tue, 29 Nov 2022 12:29:38 +0900

Initial commit

Diffstat:
ALICENSE | 24++++++++++++++++++++++++
AREADME.md | 5+++++
Amain.py | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arequirements.txt | 1+
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