X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=turbot_lambda%2Fturbot_lambda.py;h=7622d82e46168cde94a539d42850d220d3866616;hb=4bca55ecc12eb56357e5a5279fece0adced30430;hp=e22c4ebb0177625f844081262c5288423a53a2a2;hpb=7bb350e410b3a2c98c3fb7daadb8fb83583cbdea;p=turbot diff --git a/turbot_lambda/turbot_lambda.py b/turbot_lambda/turbot_lambda.py index e22c4eb..7622d82 100644 --- a/turbot_lambda/turbot_lambda.py +++ b/turbot_lambda/turbot_lambda.py @@ -1,28 +1,75 @@ from urllib.parse import parse_qs from slack import WebClient +import base64 import boto3 import requests import json +import pickle import os +from types import SimpleNamespace +from google.auth.transport.requests import Request +from googleapiclient.discovery import build -import turbot.actions -import turbot.commands +import turbot.interaction import turbot.events -import turbot.views 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 - # Note: Late import here to have the environment variable above available from turbot.slack import slack_is_valid_request # noqa -response = ssm.get_parameter(Name='SLACK_BOT_TOKEN', WithDecryption=True) -slack_bot_token = response['Parameter']['Value'] +if 'SLACK_BOT_TOKEN' in os.environ: + slack_bot_token = os.environ['SLACK_BOT_TOKEN'] +else: + response = ssm.get_parameter(Name='SLACK_BOT_TOKEN', WithDecryption=True) + slack_bot_token = response['Parameter']['Value'] + os.environ['SLACK_BOT_TOKEN'] = slack_bot_token slack_client = WebClient(slack_bot_token) +if 'GSHEETS_PICKLE_BASE64' in os.environ: + gsheets_pick_base64 = os.environ['GSHEETS_PICKLE_BASE64'] +else: + response = ssm.get_parameter(Name='GSHEETS_PICKLE_BASE64', + WithDecryption=True) + gsheets_pickle_base64 = response['Parameter']['Value'] + os.environ['GSHEETS_PICKLE_BASE64'] = gsheets_pickle_base64 +gsheets_pickle = base64.b64decode(gsheets_pickle_base64) +gsheets_creds = pickle.loads(gsheets_pickle) + +if gsheets_creds: + if gsheets_creds.valid: + print("Loaded valid GSheets credentials from SSM") + else: + gsheets_creds.refresh(Request()) + gsheets_pickle = pickle.dumps(gsheets_creds) + gsheets_pickle_base64_bytes = base64.b64encode(gsheets_pickle) + gsheets_pickle_base64 = gsheets_pickle_base64_bytes.decode('us-ascii') + print("Storing refreshed GSheets credentials into SSM") + os.environ['GSHEETS_PICKLE_BASE64'] = gsheets_pickle_base64 + ssm.put_parameter(Name='GSHEETS_PICKLE_BASE64', + Type='SecureString', + Value=gsheets_pickle_base64, + Overwrite=True) +service = build('sheets', + 'v4', + credentials=gsheets_creds, + cache_discovery=False) +sheets = service.spreadsheets() +service = build('drive', + 'v3', + credentials=gsheets_creds, + cache_discovery=False) +permissions = service.permissions() + +db = boto3.resource('dynamodb') + +turb = SimpleNamespace() +turb.slack_client = slack_client +turb.db = db +turb.table = db.Table("turbot") +turb.sheets = sheets +turb.permissions = permissions + def error(message): """Generate an error response for a Slack request @@ -65,12 +112,12 @@ def turbot_lambda(event, context): content_type = headers['content-type'] if (content_type == "application/json"): - return turbot_event_handler(event, context) + return turbot_event_handler(turb, event, context) if (content_type == "application/x-www-form-urlencoded"): - return turbot_interactive_or_slash_command(event, context) + return turbot_interactive_or_slash_command(turb, event, context) return error("Unknown content-type: {}".format(content_type)) -def turbot_event_handler(event, context): +def turbot_event_handler(turb, event, context): """Handler for all subscribed Slack events""" body = json.loads(event['body']) @@ -78,12 +125,12 @@ def turbot_event_handler(event, context): type = body['type'] if type == 'url_verification': - return url_verification_handler(body) + return url_verification_handler(turb, body) if type == 'event_callback': - return event_callback_handler(body) + return event_callback_handler(turb, body) return error("Unknown event type: {}".format(type)) -def url_verification_handler(body): +def url_verification_handler(turb, body): # First, we have to properly respond to url_verification # challenges or else Slack won't let us configure our URL as an @@ -95,18 +142,19 @@ def url_verification_handler(body): 'body': challenge } -def event_callback_handler(body): - type = body['event']['type'] +def event_callback_handler(turb, body): + event = body['event'] + type = event['type'] - if type == 'app_home_opened': - return turbot.events.app_home_opened(slack_client, body) + if type in turbot.events.events: + return turbot.events.events[type](turb, event) return error("Unknown event type: {}".format(type)) -def turbot_interactive_or_slash_command(event, context): +def turbot_interactive_or_slash_command(turb, event, context): """Handler for Slack interactive things (buttons, shortcuts, etc.) as well as slash commands. - This function simply makes a quiuck determination of what we're looking + This function simply makes a quick determination of what we're looking at and then defers to either turbot_interactive or turbot_slash_command.""" # Both interactives and slash commands have a urlencoded body @@ -115,12 +163,12 @@ def turbot_interactive_or_slash_command(event, context): # The difference is that an interactive thingy has a 'payload' # while a slash command has a 'command' if 'payload' in body: - return turbot_interactive(json.loads(body['payload'][0])) + return turbot_interactive(turb, json.loads(body['payload'][0])) if 'command' in body: - return turbot_slash_command(body) + return turbot_slash_command(turb, body) return error("Unrecognized event (neither interactive nor slash command)") -def turbot_interactive(payload): +def turbot_interactive(turb, payload): """Handler for Slack interactive requests These are the things that come from a user interacting with a button @@ -130,10 +178,14 @@ def turbot_interactive(payload): type = payload['type'] if type == 'block_actions': - return turbot_block_action(payload) + return turbot_block_action(turb, payload) + if type == 'view_submission': + return turbot.interaction.view_submission(turb, payload) + if type == 'shortcut': + return turbot_shortcut(turb, payload) return error("Unrecognized interactive type: {}".format(type)) -def turbot_block_action(payload): +def turbot_block_action(turb, payload): """Handler for Slack interactive block actions Specifically, those that have a payload type of 'block_actions'""" @@ -147,13 +199,28 @@ def turbot_block_action(payload): action = actions[0] atype = action['type'] - avalue = action['value'] - - if atype == 'button' and avalue == 'new_hunt': - return turbot.actions.new_hunt(payload) + if 'value' in action: + avalue = action['value'] + else: + avalue = '*' + + if ( + atype in turbot.interaction.actions + and avalue in turbot.interaction.actions[atype] + ): + return turbot.interaction.actions[atype][avalue](turb, payload) return error("Unknown action of type/value: {}/{}".format(atype, avalue)) -def turbot_slash_command(body): +def turbot_shortcut(turb, payload): + """Handler for Slack shortcuts + + These are invoked as either global or message shortcuts by a user.""" + + print("In turbot_shortcut, payload is: {}".format(str(payload))) + + return error("Shortcut interactions not yet implemented") + +def turbot_slash_command(turb, body): """Implementation for Slack slash commands. This parses the request and arguments and farms out to @@ -161,9 +228,12 @@ def turbot_slash_command(body): """ command = body['command'][0] - args = body['text'][0] + if 'text' in body: + args = body['text'][0] + else: + args = '' - if command in turbot.commands.commands: - return turbot.commands.commands[command](slack_client, body, args) + if command in turbot.interaction.commands: + return turbot.interaction.commands[command](turb, body, args) return error("Command {} not implemented".format(command))