From 5ca021456e8d23ed13a51da9698b9b66e90d8ec2 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 29 Sep 2020 09:59:42 -0700 Subject: [PATCH] Abstract out two slack-related functions The two new functions are slack_is_valid_request and slack_send_reply. These functions provide some shared code for dealing with slack to avoid code duplication. These functions also provide a debug mode (that doesn't use slack at all). This is a convenience for testing the implementation of slash commands in a local "make run" session without needing to touch Slack endpoints at all. (Instead, all requests are considered valid and slack replies are simply printed to stdout.) --- turbot.py | 69 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/turbot.py b/turbot.py index 5095d2a..2b9462f 100755 --- a/turbot.py +++ b/turbot.py @@ -19,6 +19,52 @@ slack_events = SlackEventAdapter(slack_signing_secret, "/slack/events", app) signature_verifier = SignatureVerifier(slack_signing_secret) slack_client = WebClient(slack_bot_token) +def slack_is_valid_request(request): + """Returns true if request actually came from Slack. + + By means of checking the requests signature together with the slack + signing key. + + Note: If flask is in debug mode, this function will always return true.""" + + if 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.""" + + 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 + + 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) + else: + try: + slack_client.chat_postMessage(channel=channel, text=text) + except SlackApiError as e: + app.logger.error("Slack API error: " + e.response["error"]) + def rot_string(str, n=13): """Return a rotated version of a string @@ -54,16 +100,10 @@ def rot(): in a direct message that it is not a member of. Otherwise, if the slash command was issued in a channel, the bot will reply in that channel.""" - data = request.get_data() - headers = request.headers - response_url = request.form.get('response_url') - channel_name = request.form.get('channel_name') - channel = request.form.get('channel_id') - query = request.form.get('text') - - if not signature_verifier.is_valid_request(data, headers): + if not slack_is_valid_request(request): return make_response("invalid request", 403) + query = request.form.get('text') match = re.match(r'^([0-9]+|\*) (.*)$', query) if (match): try: @@ -87,17 +127,8 @@ def rot(): reply += "```" - if (channel_name == "directmessage"): - resp = requests.post(response_url, - json = {"text": reply}, - headers = {"Content-type": "application/json"}) - if (resp.status_code != 200): - app.logger.error("Error posting request to Slack: " + resp.text) - else: - try: - slack_client.chat_postMessage(channel=channel, text=reply) - except SlackApiError as e: - app.logger.error("Slack API error: " + e.response["error"]) + slack_send_reply(request, reply) + return "" @slack_events.on("error") -- 2.45.2