目录
skills/
├--- send-email/
├------ SKILL.md
├------ references/
└----- scripts/
SKILL.md
---
name: send-email-go
description: Send emails via pre-built `send-email` binary. JSON config + env overrides for SMTP settings.
category: email
---
# Send Email via Go Binary
## When to use
User asks to send an email programmatically. A pre-built binary is available — no `go run` needed.
## Binary location
```
~/.hermes/skills/email/send-email-go/scripts/send-email
```
## Usage
### 1. Create a JSON config file
```json
{
"host": "smtp.gmail.com",
"port": "587",
"from": "your-email@gmail.com",
"to": ["recipient@example.com"],
"subject": "Test Email",
"body": "Hello from Hermes!",
"is_html": false
}
```
### 2. Run
```bash
# Direct call (credentials in JSON)
~/.hermes/skills/email/send-email-go/scripts/send-email config.json
# Env override (recommended for production)
SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=me@gmail.com SMTP_PASS=app-pass \
~/.hermes/skills/email/send-email-go/scripts/send-email config.json
```
Env variables override JSON values. Available: `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`.
### 3. No-arg usage
Running without args prints usage help:
```bash
~/.hermes/skills/email/send-email-go/scripts/send-email
```
## Config fields
| Field | Required | Description |
|-----------|----------|------------------------------------|
| host | yes | SMTP server hostname |
| port | yes | Port (587 for STARTTLS, 465 for SSL)|
| username | yes | SMTP login user |
| password | yes | SMTP password / app password |
| from | yes | Sender email |
| to | yes | Array of recipient emails |
| subject | yes | Email subject line |
| body | yes | Email body content |
| is_html | no | Set true for HTML emails (default: false) |
## Pitfalls
- Gmail and many providers block regular passwords; use App Passwords or OAuth2
- Port 587 uses STARTTLS; port 465 uses implicit TLS (binary handles both automatically)
- Some SMTP servers require `From` to match the authenticated user
- Corporate firewalls may block outbound SMTP ports
- Always pass credentials via env vars, not hardcoded in config files
## Verification
Check the recipient's inbox (and spam folder) to confirm delivery.
## Rebuilding
If source changes, rebuild from the skill directory:
```bash
cd ~/.hermes/skills/email/send-email-go/scripts
go build -o send-email send_email.go
```
scripts 目录
send_email.go
package main
import (
"crypto/tls"
"encoding/json"
"fmt"
"net/smtp"
"os"
"strings"
)
type EmailConfig struct {
Host string `json:"host"`
Port string `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
From string `json:"from"`
To []string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
IsHTML bool `json:"is_html"`
}
func buildMessage(cfg EmailConfig) string {
header := make(map[string]string)
header["From"] = cfg.From
header["To"] = strings.Join(cfg.To, ",")
header["Subject"] = cfg.Subject
contentType := "text/plain; charset=UTF-8"
if cfg.IsHTML {
contentType = "text/html; charset=UTF-8"
}
msg := ""
for k, v := range header {
msg += fmt.Sprintf("%s: %s\r\n", k, v)
}
msg += fmt.Sprintf("MIME-version: 1.0;\r\nContent-Type: %s\r\n\r\n%s", contentType, cfg.Body)
return msg
}
func sendEmail(cfg EmailConfig) error {
addr := cfg.Host + ":" + cfg.Port
auth := smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host)
msg := buildMessage(cfg)
// Try TLS on port 465, STARTTLS on 587
if cfg.Port == "465" {
tlsConfig := &tls.Config{ServerName: cfg.Host}
conn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
return fmt.Errorf("tls dial: %w", err)
}
defer conn.Close()
client, err := smtp.NewClient(conn, cfg.Host)
if err != nil {
return fmt.Errorf("smtp client: %w", err)
}
defer client.Close()
if err = client.Auth(auth); err != nil {
return fmt.Errorf("auth: %w", err)
}
if err = client.Mail(cfg.From); err != nil {
return fmt.Errorf("mail from: %w", err)
}
for _, to := range cfg.To {
if err = client.Rcpt(to); err != nil {
return fmt.Errorf("rcpt to: %w", err)
}
}
w, err := client.Data()
if err != nil {
return fmt.Errorf("data: %w", err)
}
_, err = w.Write([]byte(msg))
if err != nil {
return fmt.Errorf("write: %w", err)
}
return w.Close()
}
return smtp.SendMail(addr, auth, cfg.From, cfg.To, []byte(msg))
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: send-email <config.json>")
fmt.Println(" config.json: {host, port, username, password, from, to[], subject, body, is_html?}")
os.Exit(1)
}
data, err := os.ReadFile(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading config: %v\n", err)
os.Exit(1)
}
var cfg EmailConfig
if err := json.Unmarshal(data, &cfg); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing config: %v\n", err)
os.Exit(1)
}
// Allow env overrides
if h := os.Getenv("SMTP_HOST"); h != "" {
cfg.Host = h
}
if p := os.Getenv("SMTP_PORT"); p != "" {
cfg.Port = p
}
if u := os.Getenv("SMTP_USER"); u != "" {
cfg.Username = u
}
if pw := os.Getenv("SMTP_PASS"); pw != "" {
cfg.Password = pw
}
if f := os.Getenv("SMTP_FROM"); f != "" {
cfg.From = f
}
if err := sendEmail(cfg); err != nil {
fmt.Fprintf(os.Stderr, "Failed to send email: %v\n", err)
os.Exit(1)
}
fmt.Println("Email sent successfully!")
}
go.mod
module send-email
go 1.21
- THE END -
最后修改:2026年5月26日
非特殊说明,本博所有文章均为博主原创。