Developer-first workflow automation

From Inbox to Slack: Automating Email Categorization and Notifications with AI

By Pasha Fateev

Introduction

To summarize, if you’re looking for a way to write code quickly without getting bogged down in details that either don’t interest you or you are not equipped to handle, then trying AutoKitteh is worth your time. It’s open-source, durable, and customizable. You’re limited only by your creativity and the integrations we support. You can find the list of supported integrations here.
There are many cool technologies available today, so there’s no need to build something entirely new. Often the question is, “How do I integrate everything I already use and iterate on top of that?” There are many interesting ways to mix and match APIs. If I want to build a new custom workflow, low-code or no-code tools are the first to come to mind. I tried them out of curiosity and to see if they could supplant me as an engineer.
Low-code/no-code platforms are great for building something quick and simple. I tried Zapier, Make, and n8n. It’s easy to get started but frustrating to keep going if you’re used to writing code. Sure, I can quickly connect Gmail, Slack, and ChatGPT into a cohesive workflow. But if I want to get creative and step outside the bounds dictated by the platform, I have to learn a new language and environment, which often doesn’t accomplish what I want. The limitation of such platforms is also their main benefit. I can’t shoot myself in the foot because I don’t have a gun.
While “building it all myself” seems appealing, I must admit I can’t write my own infrastructure to support a long-running, durable workflow. However, I can write a Python script with business logic that accomplishes a specific goal. That’s what this blog post is about. AutoKitteh handles the infrastructure, so I can focus on the business logic. If you’re skeptical (which you should be), I challenge you to write this workflow in a low-code/no-code environment.

Use Case

I’m obsessed with finding new ways to use AI. So I wrote a workflow that categorizes incoming emails, notifies a specific Slack channel based on that categorization, and adds a corresponding label to the message in my inbox. Did I actually need this? No. But that’s not the point. I wanted something very specific and that’s exactly what I ended up with.

Workflow

  1. Trigger: Detects a new email in Gmail.
  2. Categorize: Use ChatGPT to read and categorize the email (e.g., technical work, marketing, sales).
  3. Notify: Send a message to the corresponding Slack channel based on the category.
  4. Label: Add a label to the email in Gmail.

Code Implementation

I’ll provide and explain the code written for this use case, discussing how we integrated Gmail, ChatGPT, and Slack, and highlighting key parts of the code and their functions.

Trigger

For simplicity, this example uses polling, but you could also implement this using webhooks. There are pros and cons to each method – a robust solution could use both.
def on_http_get(event):
    total_messages = None
    while True:
        total_messages = _poll_inbox(total_messages)
        time.sleep(3600)

Categorize, Notify, and Label

AutoKitteh offers flexibility. You can implement your own OAuth flow with the Gmail API or use AutoKitteh’s easy-to-use library. Initialize your connection through the UI or CLI, provide the connection name (in this case “my_gmail”), and you’re good to go. The `gmail` object is a wrapper for the standard Gmail service object, so it includes all the expected functionality.
def _poll_inbox(prev_total: int):
    gmail = google.gmail_client("my_gmail").users()
    curr_total = gmail.getProfile(userId="me").execute()["messagesTotal"]
    # Note: This is not meant to be a robust solution for handling email operations.
    if prev_total and curr_total > prev_total:
       new_email_count = curr_total - prev_total
       message_ids = _get_latest_message_ids(gmail, new_email_count)
       for message_id in message_ids:
           _process_email(gmail, message_id)

   return curr_total
The following code fetches the latest email, parses it, prompts ChatGPT to categorize it, adds a label, and sends a Slack message to the selected channel. This piece is straightforward (12 lines of code), but it’s important to note that you don’t have to worry about state. If the AutoKitteh server crashes, it will continue where it left off, so you don’t have to stress about potential workflow failures.
def _process_email(gmail, message_id: str):
   message = gmail.messages().get(userId="me", id=message_id).execute()
   email_content = _parse_email(message)
   if email_content:
       channel = _categorize_email(email_content)
       if channel:
           client = slack.slack_client("my_slack")
           client.chat_postMessage(channel=channel, text=email_content)

       # Add label to email
       label_id = _get_label_id(gmail, channel) or _create_label(gmail, channel)
       body = {"addLabelIds": [label_id]}
       gmail.messages().modify(userId="me", id=message_id, body=body).execute()
I won’t go over all the code – you can explore it on your own by visiting our repo. However, it is worth emphasizing the following function:
def _categorize_email(email_content: str) -> str:
   """Prompt ChatGPT to categorize an email based on its content.

   Returns:
       The name of the Slack channel to send a message to as a string.
       If the channel is not in the provided list, returns None.
   """
   client = openai.openai_client("my_chatgpt")
   response = client.chat.completions.create(
       model="gpt-3.5-turbo",
       messages=[
           {"role": "system", "content": "You are a helpful assistant."},
           {
               "role": "user",
               "content": f"""Categorize the following email based on its
               topic and suggest a channel to post it in from the
               provided list. The output should be one of the provided
               channels and nothing else.
               Email Content: {email_content} Channels: {SLACK_CHANNELS}
               Output example: {SLACK_CHANNELS[0]}""",
           },
       ],
   )
   channel = response.choices[0].message.content
   return channel if channel in SLACK_CHANNELS else None
This is all you need to prompt ChatGPT. By using examples directly from OpenAI’s official documentation and letting AutoKitteh handle the rest, I created a highly customizable prompt quickly, easily, and in a familiar environment.

Key Features and Benefits

To summarize, if you’re looking for a way to write code quickly without getting bogged down in details that either don’t interest you or you are not equipped to handle, then trying AutoKitteh is worth your time. It’s open-source, durable, and customizable. You’re limited only by your creativity and the integrations we support. You can find the list of supported integrations here.

Integration and Customization

Here are examples of how you can integrate our software with other tools and customize it for your needs. You will find real-life use cases similar to the one in this blog post. Visit our repository for some basic samples of all available integrations and a quick way to get started with any of them here

Future Roadmap

We’re working on adding many more integrations. If you have integrations you’re passionate about, message us on Discord

Join now

Imagine building versatile automation by simply writing a few lines of code, without the drudgery of repetitive, boilerplate infrastructure tasks. Fast, flexible and reliable automation, letiing you focus solely on the business logic.