Initial commit: email-openclaw bridge v1
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>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# Checkpoint 002 — IMAP Ingress
|
||||
|
||||
**Ngày:** 2026-04-24
|
||||
**Trạng thái:** ✅ Hoàn thành
|
||||
**Verify:** `go build ./...` thành công
|
||||
|
||||
---
|
||||
|
||||
## Việc đã làm
|
||||
|
||||
### 1. Task Repository (`internal/database/repository.go`)
|
||||
Thêm các helper function cho CRUD operations:
|
||||
- `FindByMessageID` — lookup cho idempotency check
|
||||
- `FindLatestByThreadID` — tìm task COMPLETED gần nhất trong thread
|
||||
- `FindByTaskUUID` — lookup cho callback handler
|
||||
- `ThreadHistory` — lấy toàn bộ history thread (cho OpenClaw context)
|
||||
- `CreateTask`, `UpdateTask` — CRUD cơ bản
|
||||
|
||||
### 2. IMAP Watcher (`internal/mail/imap.go`)
|
||||
Module chính cho Ingress, bao gồm:
|
||||
|
||||
**Kết nối & IDLE:**
|
||||
- `DialTLS` → Login → Select INBOX
|
||||
- IDLE loop với timer 28 phút (RFC recommends < 29 min)
|
||||
- Auto-reconnect sau 10s khi mất kết nối
|
||||
- Graceful shutdown qua `context.Context`
|
||||
- `UnilateralDataHandler` nhận notification khi có email mới
|
||||
|
||||
**Xử lý email (đúng 5 bước theo requirements 4.1):**
|
||||
1. **Anti-loop**: sender == `SYSTEM_EMAIL` → `IGNORED`
|
||||
2. **Whitelist**: sender không trong danh sách → `IGNORED` (empty whitelist = cho phép tất cả)
|
||||
3. **Idempotency**: `message_id` đã tồn tại → skip
|
||||
4. **Threading**: parse `In-Reply-To` header → tìm parent task → kế thừa `thread_id`
|
||||
5. **Save**: tạo task `status = RECEIVED`
|
||||
|
||||
**Extras:**
|
||||
- Fetch UNSEEN messages on startup (xử lý email đến trong lúc offline)
|
||||
- Mark `\Seen` sau khi xử lý xong
|
||||
- Lưu task IGNORED vào DB cho audit
|
||||
- Parse body parts qua `go-message` để lấy plain text
|
||||
- Hook `OnReceived` cho dispatch (chưa wire)
|
||||
|
||||
### 3. Entrypoint (`cmd/bridge/main.go`)
|
||||
- Chạy IMAP watcher + HTTP server concurrently
|
||||
- Graceful shutdown qua SIGINT/SIGTERM
|
||||
- `sync.WaitGroup` đợi cả hai goroutine
|
||||
|
||||
---
|
||||
|
||||
## Dependencies mới
|
||||
|
||||
```
|
||||
github.com/emersion/go-imap/v2 v2.0.0-beta.8
|
||||
github.com/emersion/go-message v0.18.2
|
||||
github.com/google/uuid (for task_uuid generation)
|
||||
```
|
||||
|
||||
## Quyết định thiết kế
|
||||
|
||||
| Quyết định | Lý do |
|
||||
|---|---|
|
||||
| go-imap v2 thay vì v1 | IDLE tích hợp sẵn, API hiện đại hơn, dự án mới nên dùng v2 |
|
||||
| IDLE timer 28 min | RFC recommends < 29 min, close + reopen IDLE để tránh server timeout |
|
||||
| Fetch UNSEEN on startup | Xử lý email đến lúc bridge offline |
|
||||
| Empty whitelist = allow all | UX tốt hơn: không cấu hình whitelist thì mặc định chấp nhận mọi sender |
|
||||
| Lưu IGNORED vào DB | Auditability: biết email nào bị bỏ qua và lý do |
|
||||
| `UnilateralDataHandler` | Nhận notification realtime khi mailbox thay đổi (không cần polling) |
|
||||
|
||||
---
|
||||
|
||||
## Files thay đổi
|
||||
|
||||
- **Mới:** `internal/database/repository.go`
|
||||
- **Mới:** `internal/mail/imap.go`
|
||||
- **Cập nhật:** `cmd/bridge/main.go` (IMAP watcher + graceful shutdown)
|
||||
- **Xóa:** `internal/mail/mail.go` (stub cũ)
|
||||
|
||||
## Tiếp theo
|
||||
|
||||
1. **OpenClaw Dispatch** — wire `OnReceived` → HTTP POST to OpenClaw
|
||||
2. **Callback handler** — `POST /callback` + token auth
|
||||
3. **SMTP Egress** — gửi reply với In-Reply-To/References
|
||||
Reference in New Issue
Block a user