Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

I Built a Gmail AI Responder in Node.js (and it Actually Works)

I Built a Gmail AI Responder in Node.js (and It Actually Works) You know the situation. You have 30 unread emails. You’ve written a few replies. They technically work… but they feel clunky, too long, slightly awkward, or just off in ton…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!




I Built a Gmail AI Responder in Node.js (and It Actually Works)



You know the situation.



You have 30 unread emails.

You’ve written a few replies.

They technically work… but they feel clunky, too long, slightly awkward, or just off in tone.



I wanted a way to fix that:




  • Without switching tools

  • Without copying emails into ChatGPT

  • Without paying for another SaaS

  • Without building a heavy UI



So I built Gmail Responder, a tiny Node.js CLI that:




  1. Watches for a Gmail label

  2. Finds your draft reply

  3. Polishes it with GPT-4.1

  4. Puts the improved version back into Gmail



No UI. No dashboard. No browser extensions.

Just a script you run when you're ready.



Here’s how it works and how you can build your own.







The Idea



The workflow is intentionally dead simple:




  1. Write a rough draft reply in Gmail

  2. Add a label such as znote to the thread

  3. Run the script

  4. Reopen your draft and see the improved version



The AI gets the full thread context, so it:




  • Matches the tone of the conversation

  • Keeps your intent intact

  • Fixes grammar

  • Trims fluff

  • Makes the message clearer and more professional



It feels like Gmail has a “Make this better” button, except you control everything ✨.







Tech Stack



Minimal and boring in a good way:




  • Node.js 18+


  • googleapis for the Gmail API


  • @google-cloud/local-auth for OAuth2 desktop authentication


  • openai for GPT-4.1


  • dotenv for environment configuration



That’s it.



Under 200 lines of actual logic 🚀.







Project Structure





gmail-responder/
├── src/
│ ├── index.js # orchestration
│ ├── auth.js # Google OAuth2
│ ├── gmail.js # Gmail API wrapper
│ └── openai.js # prompt + OpenAI call
├── .env
└── google-credentials.json





Each file has a single responsibility. No abstractions for the sake of abstractions.



Let’s walk through the interesting parts.







Step 1: Authenticate with Gmail



Google’s OAuth2 flow for desktop apps is surprisingly smooth.

@google-cloud/local-auth handles most of the complexity.




// src/auth.js
import { authenticate } from '@google-cloud/local-auth';
import { google } from 'googleapis';
import fs from 'fs';
import path from 'path';

const SCOPES = ['https://www.googleapis.com/auth/gmail.modify'];
const TOKEN_PATH = path.resolve('./google-credentials-token.json');
const CREDENTIALS_PATH = path.resolve(process.env.GOOGLE_CREDENTIALS_PATH);

export async function getGmailClient() {
let client = await loadSavedToken();
if (!client) {
client = await authenticate({ scopes: SCOPES, keyfilePath: CREDENTIALS_PATH });
await saveToken(client);
}
return google.gmail({ version: 'v1', auth: client });
}






On first run, a browser window opens for authorization.

The token is cached locally. After that, authentication is instant.







Step 2: Find Threads with the Label



We use labels as triggers. If a thread has the configured label, we process it.




// src/gmail.js
export async function getLabelId(gmail, labelName) {
const res = await gmail.users.labels.list({ userId: 'me' });
const label = res.data.labels.find(l => l.name === labelName);
return label?.id ?? null;
}

export async function getThreadsWithLabel(gmail, labelId) {
const res = await gmail.users.threads.list({
userId: 'me',
labelIds: [labelId],
maxResults: 10,
});
return res.data.threads ?? [];
}






Small, focused functions.

Each does exactly one thing.







Step 3: Extract the Draft and Thread History



This is where Gmail gets slightly annoying.



Messages are base64 encoded MIME structures. You have to:




  • Decode them

  • Navigate nested parts

  • Extract the text/plain content



Here’s a minimal parser:




export function parseMessage(msg) {
const headers = msg.payload.headers.reduce((acc, h) => {
acc[h.name] = h.value;
return acc;
}, {});

const getBody = (payload) => {
if (payload.body?.data) {
return Buffer.from(payload.body.data, 'base64url').toString('utf-8');
}
if (payload.parts) {
const textPart = payload.parts.find(p => p.mimeType === 'text/plain');
if (textPart?.body?.data) {
return Buffer.from(textPart.body.data, 'base64url').toString('utf-8');
}
}
return '';
};

return { headers, body: getBody(msg.payload) };
}






Then we match drafts to threads:




export async function getDraftForThread(gmail, threadId) {
const res = await gmail.users.drafts.list({ userId: 'me' });
const drafts = res.data.drafts ?? [];

for (const draft of drafts) {
const full = await gmail.users.drafts.get({ userId: 'me', id: draft.id });
if (full.data.message.threadId === threadId) {
return { id: draft.id, message: full.data.message };
}
}
return null;
}






Not glamorous, but reliable.









Step 4: Improve the Draft with OpenAI



Now the fun part.



We build a prompt that includes the full thread context, so GPT-4.1 understands tone and intent.




// src/openai.js
export async function improveDraft(client, threadMessages, draft) {
const threadContext = threadMessages
.map(m => `From: ${m.headers.From}\n\n${m.body}`)
.join('\n\n---\n\n');

const prompt = `
You are a professional email assistant.
Here is the email thread for context:

${threadContext}

Here is the draft reply to improve:

${draft}

Rewrite the draft to be clearer, more concise, and professional.
Keep the original intent. Return only the improved email body.
`
;

const res = await client.chat.completions.create({
model: process.env.OPENAI_MODEL ?? 'gpt-4.1',
messages: [{ role: 'user', content: prompt }],
});

return res.choices[0].message.content.trim();
}






The important part isn’t the API call.



It’s the prompt discipline:




  • Provide full context

  • Give a clear role

  • Be explicit about constraints

  • Return only what you need









Step 5: Update the Draft and Clean Up



After receiving the improved text, we rebuild the MIME message and update the draft:




export async function updateDraft(gmail, draftId, originalMessage, newBody) {
const headers = [
`From: ${originalMessage.headers.From}`,
`To: ${originalMessage.headers.To}`,
`Subject: ${originalMessage.headers.Subject}`,
`References: ${originalMessage.headers.References ?? ''}`,
`In-Reply-To: ${originalMessage.headers['In-Reply-To'] ?? ''}`,
'Content-Type: text/plain; charset=utf-8',
'',
newBody,
].join('\r\n');

const encoded = Buffer.from(headers).toString('base64url');

await gmail.users.drafts.update({
userId: 'me',
id: draftId,
requestBody: {
message: {
raw: encoded,
threadId: originalMessage.threadId,
},
},
});
}






Then we remove the label so the thread isn’t processed twice.



Simple state management via Gmail labels.









Putting It All Together



The main loop in src/index.js:




const threads = await getThreadsWithLabel(gmail, labelId);

for (const thread of threads) {
const draft = await getDraftForThread(gmail, thread.id);
if (!draft) continue;

const messages = await getThreadMessages(gmail, thread.id);
const draftMessage = parseMessage(draft.message);
const improved = await improveDraft(openai, messages, draftMessage.body);

await updateDraft(gmail, draft.id, draftMessage, improved);
await removeLabelFromThread(gmail, thread.id, labelId);

console.log(`Updated draft for thread ${thread.id}`);
}






Readable. Predictable. Easy to extend.









Setup in 5 Minutes






1️⃣ Clone and install






git clone https://github.com/alagrede/gmail-responder
cd gmail-responder
npm install









2️⃣ Configure .env






GMAIL_LABEL=znote
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4.1
GOOGLE_CREDENTIALS_PATH=./google-credentials.json









3️⃣ Enable the Gmail API




  • Create a project in Google Cloud Console

  • Enable the Gmail API

  • Create an OAuth2 Desktop App credential

  • Download it as google-credentials.json






4️⃣ Create the Gmail label



In Gmail, create a label named znote or whatever you configured.






5️⃣ Run it






npm start






First run opens the browser for authorization.

After that it runs instantly.



Label a thread.

Run the script.

Your draft is ready.









What’s Next?



Some ideas to extend it:




  • Run it via cron every hour

  • Multiple labels for multiple AI personas such as formal, casual, or technical

  • Add a --dry-run flag

  • Swap OpenAI for a local model via Ollama

  • Add logging and rate limiting

  • Convert it into a GitHub Action for shared inboxes









Why This Is Powerful



The Gmail API is massively underused.



Combine it with an LLM and suddenly:




  • Labels become automation triggers

  • Drafts become editable AI objects

  • Your inbox becomes programmable



And you don’t need:




  • A Chrome extension

  • A heavy SaaS

  • A no code automation platform



Just Node.js.









Final Thoughts



This project is small.

Under 200 lines.

Zero runtime infrastructure.



But it saves me time every single day.



That’s my favorite kind of software.



If you build something on top of this, I’d love to see it. Drop your ideas in the comments 👇

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Vulnerability Remediation & Verification
Syntax validiert (0 Fehler)
title: Detect Exploitation - I Built a Gmail AI Responder in Node.js (and it Actually Works)
id: 595f4b67-fc76-4c72-91cd-eb5643eae2c3
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-27
logsource:
  category: network_connection
  product: any
detection:
  selection:
      DestinationHostname:
        - 'originalmessage.headers.to'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-27"
        description = "YARA Signature for "
    strings:
        $str = "I Built a Gmail AI Responder i" ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
(dest_host="originalmessage.headers.to")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
destination.domain: ("originalmessage.headers.to") and event.category: "network"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where DestinationHostName in ("originalmessage.headers.to")
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

IoC Intelligence (1 Indikatoren)
originalmessage[.]headers[.]to
CTI Threat Relationship Graph4 Knoten / 3 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Analyse für identifizierte Bedrohung auf Basis von Live-CTI (ENISA EUVD): CVSS 0.0 · EPSS 0.0% · CISA KEV: nein. Handlungsableitung aus den verlinkten Hersteller-Quellen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten I Built a Gmail AI Responder in Node.js (and it Actually Works)

Thematisch verwandte Begriffe: Built, Gmail, Responder, Nodejs · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-100620 | Capgo CLI (npm package @capgo/cli) through 7.98.2 is affected by an ove…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag