×
Community Blog Automating Daily Outlook Email Summarization with HermesAgent on Alibaba Cloud ECS

Automating Daily Outlook Email Summarization with HermesAgent on Alibaba Cloud ECS

This article explains how to automate summarization of daily outlook email by HermesAgent which build on top of Alibaba Cloud ECS.

In the modern professional landscape, managing hundreds of emails daily can lead to significant cognitive overload. Developers and knowledge workers frequently miss critical communications, delay responses to mentions, or struggle to prioritize their next actions.

HermesAgent is an autonomous AI agent framework designed for multi-step workflow orchestration, tool calling, and context-aware decision-making. This article provides a highly detailed, step-by-step guide to installing HermesAgent on Alibaba Cloud Elastic Compute Service (ECS) and configuring it as an automated "Morning Assistant." This assistant will automatically summarize the previous day's Outlook emails at 7:00 AM, highlighting unread messages, specific mentions, and actionable next steps.

Solution Architecture

This solution relies on four primary components working in harmony:

  1. Microsoft Graph API: The secure, standardized source for retrieving Outlook email data.
  2. Alibaba Cloud ECS: A stable, isolated compute environment to run HermesAgent continuously.
  3. HermesAgent: The orchestration engine that bridges data extraction, AI processing, and scheduling.
  4. Alibaba Cloud Model Studio (DashScope): Provides the Large Language Model (e.g., qwen-plus) to analyze email metadata and generate intelligent, structured recommendations.

Step-by-Step Configuration and Execution Guide

Step 1: Register Application in Microsoft Entra ID (Azure AD)

We will use Delegated Permissions. This means the application will act on behalf of the signed-in user, and only you need to grant it permission to read your own mailbox.

  1. Navigate to the Microsoft Entra Admin Center (https://entra.microsoft.com) and go to App registrations > New registration.
    Name: Hermes-Email-Summarizer

Supported account types: Accounts in this organizational directory only (Single tenant)
Redirect URI: Select Public client/native (mobile & desktop) and enter http://localhost

  1. Record the Application (client) ID and Directory (tenant) ID.
    Screen_Shot_2026_06_13_at_12_41_43
  2. Navigate to Certificates & secrets > Client secrets > New client secret. Copy the Value immediately, as it will not be displayed again.
    Screen_Shot_2026_06_13_at_12_44_11
  3. Navigate to API permissions > Add a permission > Microsoft Graph > Delegated permissions.
  4. Search for and select the following permissions:
    Mail.Read (Allows the app to read email in your mailbox)

Mail.Send (Send mail as a user)
offline_access (Allows the app to maintain access to your data even when you are not actively using it, via a Refresh Token)
User.Read (Allows the app to read your basic profile)
Screen_Shot_2026_06_13_at_12_47_51

Step 2: One-Time Manual Authentication (Generate Refresh Token)

To run in the background without a browser, the agent needs a Refresh Token. You must generate this once manually.

  1. Create a temporary Python script named get_token.py on your local machine:
import msal

client_id = "YOUR_CLIENT_ID"
tenant_id = "YOUR_TENANT_ID"
client_secret = "YOUR_CLIENT_SECRET"
authority = f"https://login.microsoftonline.com/{tenant_id}"
scopes = ["https://graph.microsoft.com/Mail.Read", "https://graph.microsoft.com/offline_access", "https://graph.microsoft.com/User.Read"]

app = msal.ConfidentialClientApplication(client_id, authority=authority, client_credential=client_secret)

# 1. Get the authorization URL
auth_url = app.get_authorization_request_url(scopes)
print("1. Visit this URL in your browser and log in with your work email:")
print(auth_url)

# 2. After logging in and consenting, the browser will redirect to http://localhost/?code=...
# Copy the 'code' parameter from the URL and paste it below:
auth_code = input("2. Paste the 'code' from the redirect URL here: ")

# 3. Exchange the code for tokens
result = app.acquire_token_by_authorization_code(auth_code, scopes=scopes)

if "access_token" in result:
    print("\nSuccess! Copy this Refresh Token into your .env file:")
    print(result.get("refresh_token"))
else:
    print("Error:", result.get("error"), result.get("error_description"))

2 .Run the script locally (python get_token.py), follow the prompts, log in with your work email, and grant consent when prompted by Microsoft.

3 .Copy the resulting Refresh Token. You will need this for the ECS configuration.

Step 3: Environment Setup on ECS

Login to your ECS, and establish an SSH connection to your new ECS instance:

ssh root@your_public_ip

Install HermesAgent on ECS:

curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

Screen_Shot_2026_06_13_at_13_21_01

After installation, reload the terminal environment:

source ~/.bashrc    # If using zsh, change to source ~/.zshrc

Screen_Shot_2026_06_13_at_13_21_43

Update the system and install basic dependencies:

apt update && apt upgrade -y
apt install -y python3 python3-pip python3-venv git cron systemd

Verify the installation. A version number confirms success.

hermes --version

Create the project directory and a Python virtual environment:

mkdir -p /opt/hermes-outlook
cd /opt/hermes-outlook
python3 -m venv .venv
source .venv/bin/activate

Install the required Python libraries:

pip install msal requests pyyaml python-dotenv schedule

Step 4: Credential Configuration and Email Fetching Script

Create a .env file to store secrets securely:

nano .env

Populate it with your configuration (replace with your actual values, including the Refresh Token from Step 2):

OUTLOOK_CLIENT_ID=your_client_id_here
OUTLOOK_TENANT_ID=your_tenant_id_here
OUTLOOK_CLIENT_SECRET=your_client_secret_here
OUTLOOK_REFRESH_TOKEN=your_refresh_token_here
OUTLOOK_USER_EMAIL=your.email@domain.com
MENTION_KEYWORD=YourName
DASHSCOPE_API_KEY=sk-xxxx-your-dashscope-key
WEBHOOK_URL=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
TIMEZONE=Asia/Jakarta

Secure the file permissions:

chmod 600 .env

Next, create the Python script to fetch yesterday's emails (fetch_outlook.py). Note the updated token acquisition method:

#!/usr/bin/env python3
import os, json, re, requests, msal
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo

def get_access_token():
    app = msal.ConfidentialClientApplication(
        os.getenv("OUTLOOK_CLIENT_ID"),
        authority=f"https://login.microsoftonline.com/{os.getenv('OUTLOOK_TENANT_ID')}",
        client_credential=os.getenv("OUTLOOK_CLIENT_SECRET")
    )
    
    # Use the refresh token to get a new access token silently
    scopes = ["https://graph.microsoft.com/Mail.Read", "https://graph.microsoft.com/offline_access"]
    result = app.acquire_token_by_refresh_token(
        os.getenv("OUTLOOK_REFRESH_TOKEN"),
        scopes=scos
    )
    
    if "access_token" in result:
        # Optional: Update the .env file with the new refresh token if Microsoft rotates it
        return result["access_token"]
    
    raise Exception("Failed to acquire token. Refresh token may be invalid or revoked. Error: " + result.get("error_description"))

def fetch_yesterday_emails():
    token = get_access_token()
    tz = ZoneInfo(os.getenv("TIMEZONE", "Asia/Jakarta"))
    
    # Calculate yesterday's range (00:00 to 23:59)
    yesterday_start = datetime.now(tz) - timedelta(days=1)
    yesterday_start = yesterday_start.replace(hour=0, minute=0, second=0, microsecond=0)
    yesterday_end = yesterday_start.replace(hour=23, minute=59, second=59)

    # Convert to UTC ISO 8601 format for Graph API
    start_utc = yesterday_start.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    end_utc = yesterday_end.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

    headers = {"Authorization": f"Bearer {token}"}
    user_email = os.getenv("OUTLOOK_USER_EMAIL")
    url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages"
    
    params = {
        "$filter": f"receivedDateTime ge {start_utc} and receivedDateTime le {end_utc}",
        "$select": "subject,from,receivedDateTime,bodyPreview,isRead",
        "$top": 50, # Limit to 50 emails to control token costs
        "$orderby": "receivedDateTime desc"
    }

    resp = requests.get(url, headers=headers, params=params)
    resp.raise_for_status()
    emails = resp.json().get("value", [])

    mention_re = re.compile(rf'(?i)(@{os.getenv("MENTION_KEYWORD")}|{os.getenv("MENTION_KEYWORD")})', re.IGNORECASE)
    results = []

    for mail in emails:
        sender = mail.get("from", {}).get("emailAddress", {}).get("address", "Unknown")
        subject = mail.get("subject", "(No Subject)")
        preview = mail.get("bodyPreview", "")[:250].replace("\n", " ").strip()
        is_unread = not mail.get("isRead", False)
        mentioned = bool(mention_re.search(subject + " " + preview))

        results.append({
            "id": mail.get("id"),
            "subject": subject,
            "sender": sender,
            "received": mail.get("receivedDateTime"),
            "unread": is_unread,
            "mentioned": mentioned,
            "preview": preview
        })

    return results

if __name__ == "__main__":
    print(json.dumps(fetch_yesterday_emails(), ensure_ascii=False, indent=2))

Make the script executable:

chmod +x fetch_outlook.py

Step 5: HermesAgent Workflow Configuration

Create a workflow.yaml file to define the agent's logic, scheduling, and AI prompt:

agent:
  name: outlook-daily-orchestrator
  version: 1.0
  model: qwen3.7-plus
  max_turns: 3

tools:
  - name: fetch_outlook
    type: python_script
    path: "./fetch_outlook.py"
    env_file: "./.env"

  - name: generate_summary
    type: llm_generate
    system_prompt: |
      You are a highly efficient executive productivity assistant.
      Your task is to analyze the provided JSON email data and generate a daily report in Markdown format.
      
      The report MUST include three main sections:
      1. Unread Emails: List emails where "unread" is true. Assign a priority (High, Medium, Low) based on the subject and sender.
      2. Mentioned Emails: List emails where "mentioned" is true.
      3. Recommended Next Actions: For EVERY email listed in sections 1 and 2, provide one concrete, actionable sentence on what the user should do (e.g., "Reply with the attached document", "Schedule a 15-minute sync", "Archive if irrelevant").
      
      Rules:
      - Use professional, concise English.
      - Do not hallucinate facts. If a category is empty, state "None".
      - The output MUST be clean, well-formatted Markdown.
    output_format: text

schedule:
  type: cron
  expression: "0 7 * * *"
  timezone: "Asia/Jakarta"

delivery:
  type: console_file
  path: "/opt/hermes-outlook/daily_summary.md"
  # Alternatively, configure a webhook type to send to Slack, Teams, or Email

safety:
  dry_run_first: false
  max_tokens: 3000
  pii_mask: true
  retry_on_fail: 2

Step 6: Automated Scheduling with Systemd Timer

To ensure HermesAgent runs precisely at 7:00 AM daily without relying on an active SSH session, use a systemd timer, which is more robust than traditional cron.

Create the service file:

sudo tee /etc/systemd/system/hermes-outlook.service <<EOF
[Unit]
Description=HermesAgent Outlook Daily Summary
After=network.target

[Service]
Type=oneshot
User=root
WorkingDirectory=/opt/hermes-outlook
EnvironmentFile=/opt/hermes-outlook/.env
ExecStart=/opt/hermes-outlook/.venv/bin/python3 -m hermes_agent run --config workflow.yaml

[Install]
WantedBy=multi-user.target
EOF

Create the timer file to schedule execution at 7:00 AM:

sudo tee /etc/systemd/system/hermes-outlook.timer <<EOF
[Unit]
Description=Run HermesAgent Outlook daily at 07:00 AM

[Timer]
OnCalendar=*-*-* 07:00:00
Timezone=Asia/Jakarta
Persistent=true

[Install]
WantedBy=timers.target
EOF

Enable and start the timer:

sudo systemctl daemon-reload
sudo systemctl enable --now hermes-outlook.timer

Verify the timer status:

systemctl list-timers | grep hermes

Example Daily Summary Output

Once executed, HermesAgent will generate the daily_summary.md file (or send it via webhook) in the following structured format:

# Daily Email Summary - June 14, 2024

## Unread Emails
| Priority | Sender | Subject | Next Action |
|----------|--------|---------|-------------|
| High | finance@company.com | Q3 Budget Approval Required | Review the attached spreadsheet and reply with approval or requested revisions before 12:00 PM. |
| Medium | vendor@tech.com | API Maintenance Schedule Update | Coordinate with the DevOps team to ensure this does not conflict with our upcoming release. |
| Low | newsletter@industry.com | Weekly Tech Digest | Read during downtime or archive if no longer relevant. |

## Mentioned Emails
- **From**: pm@client.com  
  **Subject**: Re: Project Alpha Timeline  
  **Context**: "@YourName, can we accelerate the testing milestone?"  
  **Next Action**: Reply confirming resource availability or schedule a brief call to negotiate the timeline.

> Generated by HermesAgent at 07:00 AM WIB. Token Usage: ~1,250.

Integrating HermesAgent on Alibaba Cloud ECS transforms email management from a reactive chore into a proactive, automated workflow. By utilizing delegated OAuth 2.0 permissions, this solution bypasses the need for restrictive tenant-wide admin consent, making it highly accessible for individual professionals. With precise configuration, this agent operates silently in the background, filtering noise, highlighting priorities, and delivering clear, actionable recommendations exactly when the workday begins.

This solution not only saves valuable developer time but also establishes a new standard for secure, scalable, and auditable personal workflow automation within an enterprise environment.

0 1 0
Share on

Muhamad Miftah

5 posts | 0 followers

You may also like

Comments

Muhamad Miftah

5 posts | 0 followers

Related Products