]> git.cworth.org Git - turbot/blob - turbot_flask/turbot.py
Add notes on how to update the Google sheets credentials
[turbot] / turbot_flask / turbot.py
1 #!/usr/bin/env python3
2
3 from flask import current_app
4 from slack import WebClient
5 from slack.signature import SignatureVerifier
6 from flask import Flask, request, make_response
7 from slackeventsapi import SlackEventAdapter
8
9 import os
10 import requests
11 import threading
12 from turbot.rot import rot
13 from turbot.sheets import sheets_create
14
15 app = Flask(__name__)
16
17 slack_signing_secret = os.environ['SLACK_SIGNING_SECRET']
18 slack_events = SlackEventAdapter(slack_signing_secret, "/events", app)
19
20 slack_bot_token = os.environ['SLACK_BOT_TOKEN']
21
22 signature_verifier = SignatureVerifier(slack_signing_secret)
23 slack_client = WebClient(slack_bot_token)
24
25 def slack_is_valid_request(request):
26     """Returns true if request actually came from Slack.
27
28     By means of checking the requests signature together with the slack
29     signing key.
30
31     Note: If flask is in debug mode, this function will always return true."""
32
33     if current_app.debug:
34         return True
35
36     data = request.get_data()
37     headers = request.headers
38
39     return signature_verifier.is_valid_request(data, headers)
40
41 def slack_send_reply(request, text):
42     """Send a Slack message as a reply to a specified request.
43
44     If the request is associated with a direct message, the reply is
45     made by using the "response_url" from the request. Otherwise, the
46     reply will be sent to the channel associated with the request.
47
48     Note: If flask is in debug mode, this function will just print the
49     text to stdout."""
50
51     app = current_app
52     channel_name = request.form.get('channel_name')
53     response_url = request.form.get('response_url')
54     channel = request.form.get('channel_id')
55
56     if (app.debug):
57         print("Sending message to channel '{}': {}".format(channel, text))
58         return
59
60     if (channel_name == "directmessage"):
61         resp = requests.post(response_url,
62                              json = {"text": text},
63                              headers = {"Content-type": "application/json"})
64         if (resp.status_code != 200):
65             app.logger.error("Error posting request to Slack: " + resp.text)
66     else:
67         slack_send_message(channel, text)
68
69 def slack_send_message(channel, text):
70     """Send a Slack message to a specified channel."""
71
72     slack_client.chat_postMessage(channel=channel, text=text)
73
74 @app.route('/rot', methods = ['POST'])
75 def rot_route():
76     """Implements the /rot slash command for Slack replying in Slack
77
78     The format of this command is as follows:
79
80         /rot [count|*] String to be rotated
81
82     The optional count indicates an amount to rotate each character in the
83     string. If the count is '*' or is not present, then the string will
84     be rotated through all possible 25 values.
85
86     The result of the rotation is provided as a message in Slack. If the
87     slash command was issued in a direct message, the response is made by
88     using the "response_url" from the request. This allows the bot to reply
89     in a direct message that it is not a member of. Otherwise, if the slash
90     command was issued in a channel, the bot will reply in that channel."""
91
92     if not slack_is_valid_request(request):
93         return make_response("invalid request", 403)
94
95     text = request.form.get('text')
96
97     reply = rot(text)
98
99     slack_send_reply(request, reply)
100
101     return ""
102
103
104 @slack_events.on("channel_created")
105 def handle_channel_created(event_data):
106     def later(channel):
107         sheet_url = sheets_create(channel["name"])
108         slack_send_message(channel["id"],
109                            "Auto-created a sheet for this channel: {}"
110                            .format(sheet_url))
111
112     event = event_data["event"]
113     channel = event["channel"]
114     thread = threading.Thread(target=later, kwargs={'channel': channel})
115     thread.start()
116     return
117
118 @slack_events.on("error")
119 def handle_error(error):
120     app.logger.error("Error from Slack: " + str(error))