1 from urllib.parse import parse_qs
2 from turbot.rot import rot
3 from slack import WebClient
9 ssm = boto3.client('ssm')
11 response = ssm.get_parameter(Name='SLACK_SIGNING_SECRET', WithDecryption=True)
12 slack_signing_secret = bytes(response['Parameter']['Value'], 'utf-8')
14 response = ssm.get_parameter(Name='SLACK_BOT_TOKEN', WithDecryption=True)
15 slack_bot_token = response['Parameter']['Value']
16 slack_client = WebClient(slack_bot_token)
19 """Generate an error response for a Slack request
21 This will print the error message (so that it appears in CloudWatch
22 logs) and will then return a dictionary suitable for returning
23 as an error response."""
25 print("Error: {}.".format(message))
32 def slack_is_valid_request(slack_signature, timestamp, body):
33 """Returns True if the timestamp and body correspond to signature.
35 This implements the Slack signature verification using the slack
36 signing secret (obtained via an SSM parameter in code above)."""
38 content = "v0:{}:{}".format(timestamp,body).encode('utf-8')
40 signature = 'v0=' + hmac.new(slack_signing_secret,
42 hashlib.sha256).hexdigest()
44 if hmac.compare_digest(signature, slack_signature):
47 print("Bad signature: {} != {}".format(signature, slack_signature))
50 def turbot_lambda(event, context):
51 """Top-level entry point for our lambda function.
53 This function first verifies that the request actually came from
54 Slack, (by means of the SLACK_SIGNING_SECRET SSM parameter), and
55 refuses to do anything if not.
57 Then this parses the request and arguments and farms out to
58 supporting functions to implement all supported slash commands.
62 signature = event['headers']['X-Slack-Signature']
63 timestamp = event['headers']['X-Slack-Request-Timestamp']
65 if not slack_is_valid_request(signature, timestamp, event['body']):
66 return error("Invalid Slack signature")
68 body = parse_qs(event['body'])
69 command = body['command'][0]
70 args = body['text'][0]
72 if (command == "/rotlambda" or command == "/rot"):
73 return rot_slash_command(body, args)
75 return error("Command {} not implemented".format(command))
77 def rot_slash_command(body, args):
78 """Implementation of the /rot command
80 The args string should be as follows:
82 [count|*] String to be rotated
84 That is, the first word of the string is an optional number (or
85 the character '*'). If this is a number it indicates an amount to
86 rotate each character in the string. If the count is '*' or is not
87 present, then the string will be rotated through all possible 25
90 The result of the rotation is returned (with Slack formatting) in
91 the body of the response so that Slack will provide it as a reply
92 to the user who submitted the slash command."""
94 channel_name = body['channel_name'][0]
95 response_url = body['response_url'][0]
96 channel_id = body['channel_id'][0]
100 if (channel_name == "directmessage"):
101 requests.post(response_url,
102 json = {"text": result},
103 headers = {"Content-type": "application/json"})
105 slack_client.chat_postMessage(channel=channel_id, text=result)