]> git.cworth.org Git - turbot/blobdiff - turbot/slack.py
Put a message into the channel when a user edits a puzzle
[turbot] / turbot / slack.py
index 62181005bc301aafe458cdb6ce560af19b9d877c..6ea9166595e7b236eeeaa009d41fdd0653e1939d 100644 (file)
@@ -1,73 +1,67 @@
-from flask import current_app
-from slack import WebClient
-from slack.errors import SlackApiError
-from slack.signature import SignatureVerifier
+import hashlib
+import hmac
 import os
-import requests
+import boto3
 
-slack_signing_secret = os.environ['SLACK_SIGNING_SECRET']
-slack_bot_token = os.environ['SLACK_BOT_TOKEN']
+if 'SLACK_SIGNING_SECRET' in os.environ:
+    slack_signing_secret = os.environ['SLACK_SIGNING_SECRET']
+else:
+    ssm = boto3.client('ssm')
+    response = ssm.get_parameter(Name='SLACK_SIGNING_SECRET',
+                                 WithDecryption=True)
+    slack_signing_secret = response['Parameter']['Value']
+    os.environ['SLACK_SIGNING_SECRET'] = slack_signing_secret
 
-signature_verifier = SignatureVerifier(slack_signing_secret)
-slack_client = WebClient(slack_bot_token)
+slack_signing_secret = bytes(os.environ['SLACK_SIGNING_SECRET'], 'utf-8')
 
-def slack_is_valid_request(request):
-    """Returns true if request actually came from Slack.
+def slack_is_valid_request(slack_signature, timestamp, body):
+    """Returns True if the timestamp and body correspond to signature.
 
-    By means of checking the requests signature together with the slack
-    signing key.
+    This implements the Slack signature verification using the slack
+    signing secret (obtained via an SSM parameter in code above)."""
 
-    Note: If flask is in debug mode, this function will always return true."""
+    content = "v0:{}:{}".format(timestamp, body).encode('utf-8')
 
-    if current_app.debug:
-        return True
-
-    data = request.get_data()
-    headers = request.headers
-
-    return signature_verifier.is_valid_request(data, headers)
-
-def slack_send_reply(request, text):
-    """Send a Slack message as a reply to a specified request.
-
-    If the request is associated with a direct message, the reply is
-    made by using the "response_url" from the request. Otherwise, the
-    reply will be sent to the channel associated with the request.
-
-    Note: If flask is in debug mode, this function will just print the
-    text to stdout."""
-
-    app = current_app
-    channel_name = request.form.get('channel_name')
-    response_url = request.form.get('response_url')
-    channel = request.form.get('channel_id')
-
-    if (app.debug):
-        print("Sending message to channel '{}': {}".format(channel, text))
-        return
+    signature = 'v0=' + hmac.new(slack_signing_secret,
+                                 content,
+                                 hashlib.sha256).hexdigest()
 
-    if (channel_name == "directmessage"):
-        resp = requests.post(response_url,
-                             json = {"text": text},
-                             headers = {"Content-type": "application/json"})
-        if (resp.status_code != 200):
-            app.logger.error("Error posting request to Slack: " + resp.text)
+    if hmac.compare_digest(signature, slack_signature):
+        return True
     else:
-        slack_send_message(channel, text)
-
-def slack_send_message(channel, text):
-    """Send a Slack message to a specified channel.
-
-    Note: If flask is in debug mode, this function will just print the
-    text to stdout."""
-
-    app = current_app
-
-    if (app.debug):
-        print("Sending message to channel '{}': {}".format(channel, text))
-        return
-
-    try:
-        slack_client.chat_postMessage(channel=channel, text=text)
-    except SlackApiError as e:
-        app.logger.error("Slack API error: " + e.response["error"])
+        print("Bad signature: {} != {}".format(signature, slack_signature))
+        return False
+
+def slack_channel_members(slack_client, channel_id):
+    members = []
+
+    cursor = None
+    while True:
+        if cursor:
+            response = slack_client.conversations_members(channel=channel_id,
+                                                          cursor=cursor)
+        else:
+            response = slack_client.conversations_members(channel=channel_id)
+
+        if response['ok']:
+            members += response['members']
+        else:
+            print("Error querying members of channel {}: {}"
+                  .format(channel_id, response['error']))
+            return members
+
+        cursor = None
+        if 'next_cursor' in response['response_metadata']:
+            cursor = response['response_metadata']['next_cursor']
+
+        if not cursor or cursor == '':
+            break
+
+    return members
+
+def slack_send_message(slack_client, channel_id, text, blocks=None):
+    if blocks:
+        slack_client.chat_postMessage(channel=channel_id,
+                                      text=text, blocks=blocks)
+    else:
+        slack_client.chat_postMessage(channel=channel_id, text=text)