โง Email Automation OpenClaw + Gmail + Gemini
Headless Ubuntu ยท SSH ready ยท Smart drafts, never auto-send ยท AI orchestrated by OpenClaw & Gemini
Ubuntu Server โโโ OpenClaw (headless) โโโ Python Worker + venv โโโ Gmail API (OAuth2) โโโ Gemini via OpenClaw โโโ OpenClaw Scheduler (cron native) โโโ Draft automation (review before send)
๐ 1. System dependencies & project setup
# Update & install python3, venv, pip sudo apt update sudo apt install -y python3 python3-pip python3-venv # Create OpenClaw workspace mkdir -p ~/.openclaw/gmail-ai cd ~/.openclaw/gmail-ai # Virtual environment python3 -m venv venv source venv/bin/activate # (venv) should appear # Install required libs pip install google-genai google-api-python-client google-auth-httplib2 google-auth-oauthlib python-dotenv
๐ 2. Gmail API & OAuth setup (RED ZONE)
- Go to Google Cloud Console โ Enable Gmail API
- Configure OAuth consent screen: External โ Testing mode โ Add your Gmail as Test User
- Create OAuth Client ID type โ Desktop app โ Download JSON โ rename to
credentials.json - Place
credentials.jsoninside~/.openclaw/gmail-ai/
# Also generate Gemini API Key from:
# https://aistudio.google.com/app/apikey
nano .env
# inside .env file:
GEMINI_API_KEY=YOUR_API_KEY_HERE
๐ค 3. OpenClaw runtime check
openclaw status # should be running openclaw chat "hello world" # test Gemini integration
๐ 4. Python Worker โ main.py (full automation script)
Create main.py with the core logic: fetch unread email โ filter (blocked senders/promo) โ prompt OpenClaw โ Gemini reply โ create Gmail draft.
import os, time, base64, subprocess
from dotenv import load_dotenv
from email.mime.text import MIMEText
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
load_dotenv()
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
BLOCKED_SENDERS = ["bibit.id","jenius.com","tokopedia.com","shopee","lazada"]
BLOCKED_WORDS = ["unsubscribe","promo","diskon","cashback","newsletter"]
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(host='localhost', port=8080, open_browser=False)
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('gmail', 'v1', credentials=creds)
results = service.users().messages().list(userId='me', q='is:unread category:primary', maxResults=1).execute()
messages = results.get('messages', [])
if not messages:
print("No unread email")
exit()
msg_id = messages[0]['id']
msg = service.users().messages().get(userId='me', id=msg_id, format='full').execute()
payload = msg['payload']
headers = payload['headers']
subject = sender = ""
for h in headers:
if h['name'] == 'Subject': subject = h['value']
if h['name'] == 'From': sender = h['value']
body = ""
parts = payload.get('parts')
if parts:
for part in parts:
if part.get("mimeType") == "text/plain":
body = base64.urlsafe_b64decode(part['body']['data']).decode('utf-8')
break
# Filtering
for blocked in BLOCKED_SENDERS:
if blocked in sender.lower(): print("SKIPPED BLOCKED SENDER"); exit()
for blocked in BLOCKED_WORDS:
if blocked in subject.lower() or blocked in body.lower(): print("SKIPPED PROMO"); exit()
prompt = f"""Balas email berikut secara profesional dan singkat. Gunakan bahasa yang sama dengan email pengirim.
Jangan menulis: SUBJECT, EMAIL, markdown, template tambahan.
Hanya tulis isi balasan email final saja.
Email asli: {body}"""
reply_text = ""
for attempt in range(3):
try:
result = subprocess.run(["openclaw", "chat", prompt], capture_output=True, text=True)
reply_text = result.stdout.strip()
break
except Exception as e:
print(e)
time.sleep(5)
reply_msg = MIMEText(reply_text)
reply_msg['to'] = sender
reply_msg['subject'] = "Re: " + subject
raw = base64.urlsafe_b64encode(reply_msg.as_bytes()).decode()
draft_body = {'message': {'raw': raw}}
draft = service.users().drafts().create(userId='me', body=draft_body).execute()
print("\nโ
DRAFT CREATED in Gmail โ Drafts folder")
print(draft)
๐ฌ 5. OAuth First Run & token generation
# Activate venv & run
source venv/bin/activate
python main.py
Terminal will output a Google authorization URL โ open browser, login, allow access. token.json will be saved automatically.
โฑ๏ธ 6. DNS Fix (Ubuntu critical)
sudo nano /etc/resolv.conf โ add nameserver 8.8.8.8nameserver 1.1.1.1
ping google.com # should resolve
๐ 7. run.sh wrapper & OpenClaw Cron scheduler
nano ~/.openclaw/gmail-ai/run.sh
#!/bin/bash
cd ~/.openclaw/gmail-ai
source venv/bin/activate
python main.py
chmod +x run.sh
./run.sh # test
โฒ๏ธ Native OpenClaw Cron (1 minute interval)
openclaw cron add --name "gmail-ai-worker" --every 60000 --session isolated --message "Execute ~/.openclaw/gmail-ai/run.sh" openclaw cron list # verify
๐งช 8. Testing & verification
- Send a test email to your automation Gmail account (non-promotional, clear content).
- Run
python main.pyor wait for cron job. - Open Gmail โ Drafts folder โ AI generated reply appears, ready for human review.
- No auto-send โ prevents spam & hallucination risks.
๐ญ Production & ideal architecture
โ๏ธ Draft-first strategy: AI never sends automatically. You review, edit, and send manually โ compliant and safe.
๐ Next Recommended Upgrades (Tier roadmap)
Move processed emails to "AI_processed" label.
Structured logs, exponential backoff.
Route emails to specific agents.
Slack/Telegram approval before draft.
Recall past conversations for context.
Analytics and audit trail.
Real-time metrics + alerts.
Multi-channel AI drafts.
๐ง Best Practices for Production
- Draft mode only (no auto-send) โ prevents AI mistakes and reputation risk.
- Set up email filters for spam & automated out-of-office replies.
- Regularly audit
token.jsonpermissions; refresh token stored securely. - Use dedicated Gmail account for automation (not personal primary).
- Add human review stage (check drafts, then send).
chmod 600 credentials.json .env
๐ ๏ธ Troubleshooting & quick reference
# Re-authenticate Gmail if token expired rm token.json && python main.py # fresh OAuth flow # Check OpenClaw logs openclaw logs --tail 30 # Manually test Gemini through OpenClaw openclaw chat "Generate a professional email reply" # Cron Debug (if worker not running) openclaw cron list openclaw cron remove --name "gmail-ai-worker" # then recreate
โ๏ธ Ubuntu headless + OpenClaw + Gmail API connected.
โ๏ธ Gemini generates contextual reply.
โ๏ธ Draft automatically created in Gmail (never auto-sent).
โ๏ธ Cron worker runs every minute, fully autonomous.
๐ด Red alert performance โ fully modular & extensible.
Comments