Introduction

Slack commands are a powerful way to automate tasks and workflows using interactions in Slack. When coupled with Google Cloud Functions to execute the specific tasks, it forms a perfect combo to create interactive end-to-end workflows for simple and complex tasks. In this article we will dive on why we started using Slack commands and the bullet-proof architecture we have set in place to implement it.

The Problem Statement

On a weekly basis we draw an agenda on Google Sheets listing topics we plan to talk about on LinkedIn via our Astrafy account and we assign authors to those topics (i.e. Astrafy employees). We encountered a challenge of efficiently notifying and reminding team members about their tasks. We recognize the importance of not only automating the notification process but also ensuring a mechanism that would alert an admin in case of errors during the process (for instance, if no placeholder document had been created for the author to write his/her post). Furthermore, we needed a solution that would allow the admin to rectify the issues and re-run the notification process via Slack, seamlessly integrating human intervention when necessary. It is on this very point that we leveraged Slack commands; in case of a missing element for the post to be written, an admin would receive a Slack message with actions to correct and a “clear” button. When actions are corrected, pushing on this “clear” button would trigger a specific Cloud Function.

The Solution: Interactive Slack Commands with Cloud Function

Architecture Overview:

The solution comprises two primary components:

  • A private Cloud Function

  • A public Cloud Function


Overall architecture shows how Slack interact with our Cloud Function


Security aspect

Security is always key in every solution we develop. Given that Slack cannot directly trigger a private Cloud Function, we had to come up with another safe mechanism which includes a proxy public Cloud Function depicted above.

This public Cloud Function can be invoked by anyone and the key to ensuring the security of our system lies in making sure that only requests originating from our Slack app are allowed to trigger our private Cloud Function. To ensure this, the public Cloud Function includes a request verification function that examines incoming requests from Slack. If the request is validated as originating from our Slack app, it is then permitted to trigger the private Cloud Function.

The diagram below demonstrates how the public Cloud Function works (inspired by this workflow from the Slack documentation):


The sequence diagram illustrates the verification process


Cloud Function structure and code

We use Google Cloud function second generation and as mentioned above we have two Cloud Functions:

Cloud Function #1 (Private Cloud function):

  • Trigger: Directly from Cloud Scheduler or from the Public Cloud Function

  • Purpose: Dispatch task reminder to team members via Slack

  • Process:
    - Pulls task details from a Google Sheet file.
    - Sends a Slack message with task information, including a hyperlink.
    - If no hyperlink is provided, it alerts the admin a Slack message including a “Done” button.

def send_task_reminder(context):try:sheet_data = get_sheet_data(sheet_id=settings.SHEET_ID, range=settings.RANGE)task = get_task_for_today(sheet_data)if task:date, topic, who = task[0], task[1], task[2]# Check if the hyperlink is missing.if len(task) < 4:# Handle the case where hyperlink is missing.hyperlink = get_guideline_url()who = settings.ADMINmessage = create_slack_message(date, topic, who, hyperlink)send_slack_message(who=get_closest_user(who), block=message)return "Warning sent successfully", 200else:hyperlink = task[3]message = create_slack_message(date, topic, who, hyperlink)send_slack_message(who=get_closest_user(who), block=message)return make_response(jsonify(message="Reminder sent successfully", status=200))else:logger.info("No task for today")return make_response(jsonify(message="No task found for today", status=200))except HttpError as e:return make_response(jsonify(message=f"Error accessing Google Sheets: {e}", status=500))

Cloud Function #2 (Public Cloud function):

  • Trigger: Manually

  • Purpose: Verify incoming requests and trigger the private Cloud Function if requests originate from Slack.

  • Process:
    -
    Checks if the request source is from Slack.
    - If the request is verified as originating from Slack, it triggers the private Cloud Function.

You can see below our code snippet the request verification function:

@app.route('/cf_starter', methods=['POST'])def verify_slack_signature(req):# Get the Slack signing secret from environment variablesslack_signing_secret = get_secret_value(settings.SECRET_PROJECT_ID, settings.SIGN_SECRET_NAME)# Extract necessary headers from the incoming requestrequest_body = req.get_data().decode('utf-8')timestamp = req.headers.get('X-Slack-Request-Timestamp')if abs(time.time() - float(timestamp)) > 60 * 5:# The request timestamp is more than five minutes from local time.return# Create a basestring by concatenating the version, timestamp and request bodysig_basestring = f'v0:{timestamp}:{request_body}'# Hash the basestring using your signing secretmy_signature = 'v0=' + hmac.new(bytes(slack_signing_secret, 'utf-8'), bytes(sig_basestring, 'utf-8'), hashlib.sha256).hexdigest()slack_signature = request.headers['X-Slack-Signature']#Compare the generated hash against the hash from the incoming requestif hmac.compare_digest(my_signature, slack_signature):logger.info('Hooray, the request came from Slack!')res = trigger_private_function()if res.json()['status'] == 200:return make_response(jsonify(message="OK", status=200)) # Signature matchelse:return make_response(jsonify(message="Error accessing Google Sheets", status=401))else:logger.error('Signature mismatch!')return make_response(jsonify(message="Signature is not valid", status=401)) # Signature mismatch

Conclusion

In this article, we’ve journeyed through the innovative integration of interactive Slack commands with Cloud Functions, a fusion that redefines the landscape of internal workflows. The possibilities are boundless, and for us, it’s been a groundbreaking way to inject dynamism into our routine operations.

But it’s more than just about efficiency. Our foray into this integration has been a testament to the power of simplicity and timeliness. Tasks are now streamlined, organized, and executed with precision, thanks to this cutting-edge solution. And let’s not overlook the security aspect — it’s not just reliable, it’s fortress-level secure.

The real kicker? The speed of development with Cloud Functions. We are talking about turbocharging your automation features, getting them up and running in mere hours. It’s not just a change; it’s an evolution in how we handle our key workflows.

We are now starting to automate many of our routine tasks with slack commands and Cloud Function. And you, what are you waiting for ?


If you have any questions or encounter any issues while applying this solution, let us know. We are open to discussion and here to help.

If you enjoyed reading this article, stay tuned as we regularly publish articles on handy solutions to automate your daily processes using Google Cloud ecosystem. Follow Astrafy on LinkedIn to be notified for the next article ;).

If you are looking for support with Google Cloud or Data Stack solutions, feel free to reach out to us at sales@astrafy.io.