Files
platform/seed/skills/google-mail-api/example-nodejs.ts
T
2026-02-24 21:47:36 +00:00

408 lines
10 KiB
TypeScript

/**
* Gmail API - Node.js/TypeScript Example
*
* Complete working example showing all major operations.
* Save this file and run: npx ts-node example-nodejs.ts
*/
import fs from "fs/promises";
import readline from "readline";
import { google, gmail_v1 } from "googleapis";
import { OAuth2Client } from "google-auth-library";
// Configuration
const SCOPES = ["https://www.googleapis.com/auth/gmail.modify"];
const CREDENTIALS_PATH = "credentials.json";
const TOKEN_PATH = "token.json";
/**
* Authenticate user and return Gmail service
*/
async function authenticate(): Promise<gmail_v1.Gmail> {
try {
const credentialsContent = await fs.readFile(CREDENTIALS_PATH, "utf-8");
const credentials = JSON.parse(credentialsContent);
const { client_id, client_secret, redirect_uris } = credentials.installed;
const oauth2Client = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
// Check for cached token
try {
const tokenContent = await fs.readFile(TOKEN_PATH, "utf-8");
const token = JSON.parse(tokenContent);
oauth2Client.setCredentials(token);
// Refresh if expired
if (token.expiry_date && token.expiry_date < Date.now()) {
const newToken = await oauth2Client.refreshAccessToken();
oauth2Client.setCredentials(newToken.credentials);
await fs.writeFile(TOKEN_PATH, JSON.stringify(newToken.credentials));
}
console.log("✅ Using cached credentials");
return google.gmail({ version: "v1", auth: oauth2Client });
} catch {
// No token, need to authenticate
console.log("⚠️ No cached token, initiating authentication...");
return authenticateUser(oauth2Client);
}
} catch (error) {
console.error("❌ Authentication failed:", error);
throw error;
}
}
/**
* Get user authorization via browser
*/
async function authenticateUser(
oauth2Client: OAuth2Client
): Promise<gmail_v1.Gmail> {
const authUrl = oauth2Client.generateAuthUrl({
access_type: "offline",
scope: SCOPES,
});
console.log(`\n🔗 Please visit this URL to authorize:\n${authUrl}\n`);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve, reject) => {
rl.question("Enter the authorization code: ", async (code) => {
rl.close();
try {
const { tokens } = await oauth2Client.getToken(code);
oauth2Client.setCredentials(tokens);
await fs.writeFile(TOKEN_PATH, JSON.stringify(tokens));
console.log("✅ Token saved");
resolve(google.gmail({ version: "v1", auth: oauth2Client }));
} catch (error) {
reject(error);
}
});
});
}
/**
* Get user profile
*/
async function getProfile(gmail: gmail_v1.Gmail) {
console.log("\n📋 Getting user profile...");
const response = await gmail.users.getProfile({ userId: "me" });
const data = response.data;
console.log(` Email: ${data.emailAddress}`);
console.log(` Total Messages: ${data.messagesTotal}`);
console.log(` Total Threads: ${data.threadsTotal}`);
return data;
}
/**
* List all labels
*/
async function listLabels(gmail: gmail_v1.Gmail) {
console.log("\n🏷️ Listing labels...");
const response = await gmail.users.labels.list({ userId: "me" });
const labels = response.data.labels || [];
labels.slice(0, 10).forEach((label) => {
console.log(` - ${label.name} (ID: ${label.id})`);
});
console.log(` ... and ${Math.max(0, labels.length - 10)} more`);
return labels;
}
/**
* List inbox messages
*/
async function listInboxMessages(gmail: gmail_v1.Gmail) {
console.log("\n📧 Listing inbox messages (last 5)...");
const response = await gmail.users.messages.list({
userId: "me",
q: "in:inbox",
maxResults: 5,
});
const messages = response.data.messages || [];
for (const message of messages) {
const msgDetails = await gmail.users.messages.get({
userId: "me",
id: message.id!,
format: "metadata",
metadataHeaders: ["Subject", "From", "Date"],
});
const headers = msgDetails.data.payload?.headers || [];
const subject =
headers.find((h) => h.name === "Subject")?.value || "No Subject";
const from = headers.find((h) => h.name === "From")?.value || "Unknown";
const date = headers.find((h) => h.name === "Date")?.value || "Unknown";
console.log(`\n From: ${from}`);
console.log(` Subject: ${subject}`);
console.log(` Date: ${date}`);
}
return messages;
}
/**
* Search messages
*/
async function searchMessages(gmail: gmail_v1.Gmail, query: string) {
console.log(`\n🔍 Searching for: ${query}`);
const response = await gmail.users.messages.list({
userId: "me",
q: query,
maxResults: 5,
});
const messages = response.data.messages || [];
console.log(` Found ${messages.length} messages`);
return messages;
}
/**
* Get full message details
*/
async function getFullMessage(gmail: gmail_v1.Gmail, messageId: string) {
console.log(`\n📄 Getting full message: ${messageId}`);
const response = await gmail.users.messages.get({
userId: "me",
id: messageId,
format: "full",
});
const message = response.data;
const headers = message.payload?.headers || [];
const bodyData = message.payload?.body?.data || "";
const subject =
headers.find((h) => h.name === "Subject")?.value || "No Subject";
const from = headers.find((h) => h.name === "From")?.value || "Unknown";
const to = headers.find((h) => h.name === "To")?.value || "Unknown";
let body = "";
if (bodyData) {
body = Buffer.from(bodyData, "base64").toString();
}
console.log(` Subject: ${subject}`);
console.log(` From: ${from}`);
console.log(` To: ${to}`);
console.log(` Body length: ${body.length} chars`);
return {
subject,
from,
to,
body: body.substring(0, 500), // First 500 chars
};
}
/**
* Send a message
*/
async function sendMessage(
gmail: gmail_v1.Gmail,
to: string,
subject: string,
body: string
) {
console.log(`\n✉️ Sending message to ${to}...`);
const message =
`From: me\r\n` +
`To: ${to}\r\n` +
`Subject: ${subject}\r\n` +
`\r\n` +
`${body}`;
const encodedMessage = Buffer.from(message)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const response = await gmail.users.messages.send({
userId: "me",
requestBody: {
raw: encodedMessage,
},
});
console.log(` ✅ Message sent with ID: ${response.data.id}`);
return response.data;
}
/**
* Create a draft
*/
async function createDraft(
gmail: gmail_v1.Gmail,
to: string,
subject: string,
body: string
) {
console.log(`\n📝 Creating draft for ${to}...`);
const message =
`From: me\r\n` +
`To: ${to}\r\n` +
`Subject: ${subject}\r\n` +
`\r\n` +
`${body}`;
const encodedMessage = Buffer.from(message)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const response = await gmail.users.drafts.create({
userId: "me",
requestBody: {
message: {
raw: encodedMessage,
},
},
});
console.log(` ✅ Draft created with ID: ${response.data.id}`);
return response.data;
}
/**
* Apply label to message
*/
async function applyLabelToMessage(
gmail: gmail_v1.Gmail,
messageId: string,
labelName: string
) {
console.log(`\n🏷️ Applying label "${labelName}" to message...`);
const labelsResponse = await gmail.users.labels.list({ userId: "me" });
const label = labelsResponse.data.labels?.find((l) => l.name === labelName);
if (!label) {
console.log(` ❌ Label "${labelName}" not found`);
return null;
}
const response = await gmail.users.messages.modify({
userId: "me",
id: messageId,
requestBody: {
addLabelIds: [label.id!],
},
});
console.log(` ✅ Label applied`);
return response.data;
}
/**
* List threads
*/
async function listThreads(gmail: gmail_v1.Gmail) {
console.log("\n💬 Listing threads (last 3)...");
const response = await gmail.users.threads.list({
userId: "me",
q: "in:inbox",
maxResults: 3,
});
const threads = response.data.threads || [];
for (const thread of threads) {
const threadData = await gmail.users.threads.get({
userId: "me",
id: thread.id!,
format: "metadata",
});
const numMessages = threadData.data.messages?.length || 0;
const firstMsg = threadData.data.messages?.[0];
const headers = firstMsg?.payload?.headers || [];
const subject =
headers.find((h) => h.name === "Subject")?.value || "No Subject";
console.log(
`\n Thread ${thread.id}: ${numMessages} messages - "${subject}"`
);
}
return threads;
}
/**
* Move message to trash
*/
async function moveToTrash(gmail: gmail_v1.Gmail, messageId: string) {
console.log(`\n🗑️ Moving message to trash...`);
const response = await gmail.users.messages.trash({
userId: "me",
id: messageId,
});
console.log(` ✅ Message moved to trash`);
return response.data;
}
/**
* Main demo function
*/
async function main() {
console.log("🚀 Gmail API - Node.js/TypeScript Example\n");
try {
// Authenticate
const gmail = await authenticate();
// Run examples
await getProfile(gmail);
await listLabels(gmail);
await listInboxMessages(gmail);
// Search example
await searchMessages(gmail, "is:unread");
// Get first message details
const messages = await listInboxMessages(gmail);
if (messages.length > 0) {
await getFullMessage(gmail, messages[0].id!);
}
// Draft example (uncomment to use)
// await createDraft(
// gmail,
// "recipient@example.com",
// "Test Draft",
// "This is a test draft created by the example script."
// );
// List threads
await listThreads(gmail);
console.log("\n✅ All examples completed!");
} catch (error) {
console.error("\n❌ Error:", error);
process.exit(1);
}
}
// Run if executed directly
main();