name: email-writer description: Email writer and sender for OpenClaw. Use when user needs to draft, reply, or send emails via browser. Supports Gmail, Outlook, QQ Mail, 163 Mail. Requires OpenClaw v2026.3.22+ with browser access. version: 1.0.4 license: MIT-0 metadata: {"openclaw": {"emoji": "📧", "requires": {"bins": [], "env": []}, "minVersion": "2026.3.22", "needsBrowser": true}}
Email composition and sending tool using OpenClaw browser automation.
This skill accesses your email account through browser session.
Ask user for email details:
// Detect which email provider based on URL
function detectProvider(url) {
if (url.includes('mail.google.com') || url.includes('gmail.com')) return 'gmail';
if (url.includes('outlook.live.com') || url.includes('outlook.com')) return 'outlook';
if (url.includes('mail.qq.com')) return 'qq';
if (url.includes('mail.163.com')) return '163';
if (url.includes('mail.126.com')) return '126';
return 'unknown';
}
// Open email client based on user preference
await browser.open({ url: "https://mail.google.com" })
await browser.wait({ timeout: 5000 })
Supported platforms: - Gmail: mail.google.com - Outlook: outlook.live.com - QQ Mail: mail.qq.com - 163 Mail: mail.163.com - 126 Mail: mail.126.com
// Click compose button (Gmail) await browser.click({ selector: '[gh="cm"]' }) await browser.wait({ timeout: 2000 }) // Fill recipient await browser.evaluate((email) => { const toField = document.querySelector('[name="to"]'); if (toField) { toField.focus(); toField.value = email; toField.dispatchEvent(new Event('input', { bubbles: true })); } }, recipientEmail) // Fill subject await browser.evaluate((subject) => { const subjectField = document.querySelector('[name="subjectbox"]'); if (subjectField) { subjectField.focus(); subjectField.value = subject; } }, emailSubject) // Fill body await browser.evaluate((body) => { const bodyField = document.querySelector('[role="textbox"][contenteditable="true"]'); if (bodyField) { bodyField.focus(); bodyField.innerHTML = body.replace(/\n/g, '<br>'); } }, emailBody) // Send (Gmail) await browser.click({ selector: '[gh="s"]' })推薦訪問7w4.net獲取更多AI技能。
// Click new message button (Outlook)
await browser.click({ selector: '[aria-label="New message"], .ms-Button--primary' })
await browser.wait({ timeout: 2000 })
// Fill recipient (Outlook)
await browser.evaluate((email) => {
const toField = document.querySelector('[aria-label="To"], input[placeholder*="To"]');
if (toField) {
toField.focus();
toField.value = email;
toField.dispatchEvent(new Event('input', { bubbles: true }));
}
}, recipientEmail)
// Fill subject (Outlook)
await browser.evaluate((subject) => {
const subjectField = document.querySelector('[aria-label="Subject"], input[placeholder*="Subject"]');
if (subjectField) {
subjectField.focus();
subjectField.value = subject;
}
}, emailSubject)
// Fill body (Outlook)
await browser.evaluate((body) => {
const bodyField = document.querySelector('[aria-label="Message body"], [role="textbox"]');
if (bodyField) {
bodyField.focus();
bodyField.innerHTML = body.replace(/\n/g, '<br>');
}
}, emailBody)
// Send (Outlook)
await browser.click({ selector: '[aria-label="Send"], button[title="Send"]' })
// Click compose button (QQ Mail)
await browser.click({ selector: '.compose-button, .btn-compose, [title="寫信"]' })
await browser.wait({ timeout: 2000 })
// Fill recipient (QQ Mail)
await browser.evaluate((email) => {
const toField = document.querySelector('#to_input, input[name="to"]') ||
document.querySelector('[aria-label="收件人"]');
if (toField) {
toField.focus();
toField.value = email;
toField.dispatchEvent(new Event('input', { bubbles: true }));
}
}, recipientEmail)
// Fill subject (QQ Mail)
await browser.evaluate((subject) => {
const subjectField = document.querySelector('#subject, input[name="subject"]');
if (subjectField) {
subjectField.focus();
subjectField.value = subject;
}
}, emailSubject)
// Fill body (QQ Mail)
await browser.evaluate((body) => {
const bodyField = document.querySelector('#QMEditorArea iframe') ||
document.querySelector('.qmEditorArea');
if (bodyField) {
if (bodyField.tagName === 'IFRAME') {
bodyField.contentDocument.body.innerHTML = body.replace(/\n/g, '<br>');
} else {
bodyField.innerHTML = body.replace(/\n/g, '<br>');
}
}
}, emailBody)
// Send (QQ Mail)
await browser.click({ selector: '.btn-send, [title="傳送"], .qmBtn' })
// Click compose button (163 Mail)
await browser.click({ selector: '.nui-mainToolbar-btn, .js-component-composebtn' })
await browser.wait({ timeout: 2000 })
// Fill recipient (163 Mail)
await browser.evaluate((email) => {
const toField = document.querySelector('#draft_to_input, input[name="to"]') ||
document.querySelector('.nui-editableAddr-input');
if (toField) {
toField.focus();
toField.value = email;
toField.dispatchEvent(new Event('input', { bubbles: true }));
}
}, recipientEmail)
// Fill subject (163 Mail)
await browser.evaluate((subject) => {
const subjectField = document.querySelector('#draft_subject, input[name="subject"]');
if (subjectField) {
subjectField.focus();
subjectField.value = subject;
}
}, emailSubject)
// Fill body (163 Mail)
await browser.evaluate((body) => {
const bodyField = document.querySelector('.APP-editor-iframe iframe') ||
document.querySelector('.nuiEditor');
if (bodyField) {
if (bodyField.tagName === 'IFRAME') {
bodyField.contentDocument.body.innerHTML = body.replace(/\n/g, '<br>');
} else {
bodyField.innerHTML = body.replace(/\n/g, '<br>');
}
}
}, emailBody)
// Send (163 Mail)
await browser.click({ selector: '.nui-mainToolbar-sendBtn, .js-component-sendbtn' })
await browser.click({ selector: '.zA' })
await browser.click({ selector: '[aria-label="Reply"]' })
await browser.wait({ timeout: 2000 })
await browser.evaluate((body) => {
const bodyField = document.querySelector('[role="textbox"][contenteditable="true"]');
if (bodyField) bodyField.innerHTML = body.replace(/\n/g, '<br>');
}, replyBody)
await browser.click({ selector: '[gh="s"]' })
await browser.click({ selector: '[aria-label="Reply"]' })
await browser.wait({ timeout: 2000 })
await browser.evaluate((body) => {
const bodyField = document.querySelector('[aria-label="Message body"]');
if (bodyField) bodyField.innerHTML = body.replace(/\n/g, '<br>');
}, replyBody)
await browser.click({ selector: '[aria-label="Send"]' })
await browser.click({ selector: '.btn-reply, [title="回覆"]' })
await browser.wait({ timeout: 2000 })
await browser.evaluate((body) => {
const iframe = document.querySelector('#QMEditorArea iframe');
if (iframe) iframe.contentDocument.body.innerHTML = body.replace(/\n/g, '<br>');
}, replyBody)
await browser.click({ selector: '.btn-send, [title="傳送"]' })
await browser.click({ selector: '.js-component-replybtn, [title="回覆"]' })
await browser.wait({ timeout: 2000 })
await browser.evaluate((body) => {
const iframe = document.querySelector('.APP-editor-iframe iframe');
if (iframe) iframe.contentDocument.body.innerHTML = body.replace(/\n/g, '<br>');
}, replyBody)
await browser.click({ selector: '.js-component-sendbtn' })
// Wait for send
await browser.wait({ timeout: 3000 })
// Check for success message
const sent = await browser.evaluate(() => {
const text = document.body.innerText;
return text.includes('Message sent') ||
text.includes('Sent') ||
text.includes('已傳送') ||
text.includes('傳送成功') ||
text.includes('郵件已傳送');
})
if (sent) {
console.log("Email sent successfully!")
}
Subject: Thank you for your support
Dear [Name],
Thank you for [specific reason].
[Details]
Best regards, [Your name]
Subject: Work Report - [Date]
Hi Manager,
Weekly report:
Best, [Your name]
Subject: Meeting: [Topic]
Hi [Name],
Please join our meeting:
Time: [Time] Location: [Location/Link] Agenda: 1. Topic 1 2. Topic 2
Please confirm your attendance.
Thanks, [Your name]
這個郵件工具質量中規中矩,優點是支援多種常用郵箱、撰寫流程完整,還帶實用模板。但它的程式碼比較死板,一旦郵箱介面改版就可能用不了。缺少附件支援和錯誤恢復機制,偶爾會遇到操作失敗的情況。建議先用不重要的賬號測試再用。總的來說適合日常簡單郵件需求,但複雜場景可能需要手動補充。