Full pipeline: IMAP ingress -> OpenClaw dispatch -> callback -> SMTP reply. SQLite stateful storage with idempotency, threading, and retry logic. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
62 lines
1.8 KiB
Markdown
62 lines
1.8 KiB
Markdown
# Checkpoint 005 — SMTP Egress
|
|
|
|
**Ngày:** 2026-04-24
|
|
**Trạng thái:** ✅ Hoàn thành
|
|
**Verify:** `go build ./...` thành công
|
|
|
|
---
|
|
|
|
## Việc đã làm
|
|
|
|
### SMTP Sender (`internal/mail/smtp.go`)
|
|
|
|
**Gửi reply email (requirements 4.3):**
|
|
- Subject: thêm prefix `Re:` nếu chưa có
|
|
- Body: `task.AIResponse` từ OpenClaw callback
|
|
- Threading headers:
|
|
- `In-Reply-To: <original message_id>`
|
|
- `References: <original message_id>`
|
|
- MIME: `text/plain; charset=UTF-8`
|
|
|
|
**Retry logic (đúng requirements):**
|
|
- Tối đa 3 lần, backoff: 1s → 5s → 15s
|
|
- Mỗi lần retry: tăng `attempt_smtp`, status = `SMTP_RETRYING`
|
|
- Hết retry: `status = FAILED`, ghi `last_error`
|
|
- Thành công: `status = COMPLETED`
|
|
|
|
**SMTP delivery:**
|
|
- `net/smtp.SendMail` với `PlainAuth`
|
|
- Địa chỉ: `SMTP_HOST:SMTP_PORT`
|
|
- From: `SYSTEM_EMAIL`
|
|
|
|
### Wiring (`cmd/bridge/main.go`)
|
|
- `smtpSender.SendReplyFunc()` → `api.NewRouter(..., onDone)`
|
|
- Callback handler gọi `onDone(task)` async sau khi trả response
|
|
|
|
---
|
|
|
|
## Pipeline hoàn chỉnh 🎉
|
|
|
|
```
|
|
IMAP IDLE → RECEIVED → AI_PROCESSING → (OpenClaw callback) → CALLBACK_DONE → SMTP reply → COMPLETED
|
|
↓ ↓ ↓
|
|
IGNORED FAILED FAILED
|
|
```
|
|
|
|
Tất cả transitions đã được implement:
|
|
- IMAP → RECEIVED ✅
|
|
- RECEIVED → AI_PROCESSING ✅
|
|
- AI_PROCESSING → CALLBACK_DONE ✅
|
|
- CALLBACK_DONE → SMTP_RETRYING → COMPLETED/FAILED ✅
|
|
- Anti-loop/Whitelist → IGNORED ✅
|
|
- Retry exhausted → FAILED ✅
|
|
|
|
## Files thay đổi
|
|
|
|
- **Mới:** `internal/mail/smtp.go`
|
|
- **Cập nhật:** `cmd/bridge/main.go` (wire SMTP sender)
|
|
|
|
## Tiếp theo
|
|
|
|
1. **Integration tests** — 6 scenarios từ requirements section 10
|