feat: Use IDLE by default to keep a long-lived connection with the mail server; allow forced polling for servers with poor IDLE/NOOP support.

This commit is contained in:
zhouliang 2026-02-18 22:17:22 +08:00
parent a78c92448e
commit cb19ee997d
4 changed files with 18 additions and 16 deletions

View file

@ -490,7 +490,7 @@ Use an inbox as a channel: PicoClaw connects via IMAP to read new mail and (opti
"password": "YOUR_APP_PASSWORD",
"mailbox": "INBOX",
"check_interval": 30,
"use_idle": true,
"forced_polling": false,
"use_tls": true,
"allow_from": ["allowed-sender@example.com"],
"attachment_dir": "/path/to/save/attachments",
@ -507,8 +507,8 @@ Use an inbox as a channel: PicoClaw connects via IMAP to read new mail and (opti
| `imap_server`, `imap_port` | IMAP server (e.g. 993 with TLS). |
| `username`, `password` | Mailbox login; 163/QQ require app password. |
| `mailbox` | Folder to watch (default `INBOX`). |
| `use_idle` | `true`: use IMAP IDLE when supported (push); `false`: poll every `check_interval` seconds. |
| `check_interval` | Polling interval in seconds when IDLE is off or unsupported (default 30). |
| `forced_polling` | When the server does not implement IDLE/NOOP per spec, set `true` to use application-level polling; leave `false` normally. |
| `check_interval` | Polling interval in seconds when using forced polling or when IDLE is unsupported (default 30). |
| `allow_from` | Allowed sender addresses; only their mails trigger the agent. |
| `attachment_dir` | Directory to save attachments; leave empty to skip saving. |
| `smtp_server`, `smtp_port`, `smtp_use_tls` | Optional; if set, the agent can send replies. |

View file

@ -79,10 +79,10 @@
"password": "your_password",
"mailbox": "INBOX",
"check_interval": 30,
"forced_polling": false,
"use_tls": true,
"allow_from": ["allowed_email@example.com"],
"attachment_dir": "~/.picoclaw/workspace/attachments",
"use_idle": true,
"smtp_server": "smtp.example.com--eg:smtp.qq.com",
"smtp_port": 465,
"smtp_use_tls": true

View file

@ -310,12 +310,13 @@ func (c *EmailChannel) checkLoop(ctx context.Context) {
// Run one check immediately
c.checkNewEmails()
if c.config.UseIdle {
if !c.config.ForcedPolling {
// support IDLE user idle loop, waiting for server push update
c.runIdleLoop(ctx, interval)
return
}
// Polling mode
// not support IDLE user polling mode, check new emails every interval
c.mu.Lock()
c.checkTicker = time.NewTicker(interval)
ticker := c.checkTicker

View file

@ -156,8 +156,9 @@ type EmailConfig struct {
Password string `json:"password" env:"PICOCLAW_CHANNELS_EMAIL_PASSWORD"`
Mailbox string `json:"mailbox" env:"PICOCLAW_CHANNELS_EMAIL_MAILBOX"` // 默认 "INBOX"
CheckInterval int `json:"check_interval" env:"PICOCLAW_CHANNELS_EMAIL_CHECK_INTERVAL"` // seconds, default 30; used for polling when IDLE disabled or unsupported
UseIdle bool `json:"use_idle" env:"PICOCLAW_CHANNELS_EMAIL_USE_IDLE"` // use IMAP IDLE (push) when server supports it; otherwise poll
UseTLS bool `json:"use_tls" env:"PICOCLAW_CHANNELS_EMAIL_USE_TLS"`
// ForcedPolling: when the mail server does not implement IDLE/NOOP per spec (e.g. NOOP does not return * EXISTS), set true to use application-level polling (check new mail at CheckInterval). Leave false under normal conditions.
ForcedPolling bool `json:"forced_polling" env:"PICOCLAW_CHANNELS_EMAIL_FORCED_POLLING"`
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_EMAIL_ALLOW_FROM"`
AttachmentDir string `json:"attachment_dir" env:"PICOCLAW_CHANNELS_EMAIL_ATTACHMENT_DIR"`
// SMTP send (optional, if not configured, Send is not available)
@ -332,7 +333,7 @@ func DefaultConfig() *Config {
Password: "",
Mailbox: "INBOX",
CheckInterval: 30,
UseIdle: true,
ForcedPolling: false,
UseTLS: true,
AllowFrom: FlexibleStringSlice{},
SMTPServer: "",