eoka-email 0.1.0

IMAP helpers for email-based automation (OTP codes, verification links)
Documentation

eoka-email

IMAP helpers for email-based automation (OTP codes, verification links).

Install (workspace)

Add to your crate's Cargo.toml:

[dependencies]
eoka-email = { path = "../eoka-tools/crates/eoka-email" }
eoka = { path = "../eoka" }
regex = "1" # for code extraction patterns

Example: verification link + eoka

use chrono::Duration;
use eoka::Browser;
use eoka_email::{
    extract_first_link, ImapClient, ImapConfig, LinkFilter, SearchCriteria, WaitOptions,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Launch browser
    let browser = Browser::launch().await?;
    let page = browser.new_page("https://example.com/signup").await?;

    // ... fill/signup steps here ...

    // Wait for the email
    let config = ImapConfig::new("imap.gmail.com", 993, "user@gmail.com", "app-password")
        .mailbox("INBOX")
        .tls(true);

    let criteria = SearchCriteria::new()
        .from("no-reply@example.com")
        .subject_contains("Confirm your email")
        .unseen_only(true)
        .since_minutes(10);

    let options = WaitOptions::new(Duration::minutes(2), Duration::seconds(2));

    let mut client = ImapClient::connect(&config)?;
    let msg = client.wait_for_message(&criteria, &options)?;

    let link = extract_first_link(&msg, &LinkFilter {
        allow_domains: Some(vec!["example.com".into()]),
    })
    .expect("no link found");

    page.goto(&link).await?;
    Ok(())
}

Example: OTP code + eoka

use chrono::Duration;
use eoka::Browser;
use eoka_email::{extract_code, ImapClient, ImapConfig, SearchCriteria, WaitOptions};
use regex::Regex;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let browser = Browser::launch().await?;
    let page = browser.new_page("https://example.com/login").await?;

    // ... login steps ...

    let config = ImapConfig::new("imap.gmail.com", 993, "user@gmail.com", "app-password");
    let criteria = SearchCriteria::new()
        .subject_contains("Your verification code")
        .unseen_only(true)
        .since_minutes(10);
    let options = WaitOptions::new(Duration::minutes(2), Duration::seconds(2));

    let mut client = ImapClient::connect(&config)?;
    let msg = client.wait_for_message(&criteria, &options)?;

    let code = extract_code(&msg, &Regex::new(r"(\d{6})").unwrap())
        .expect("no code found");

    page.fill("input[name=code]", &code).await?;
    Ok(())
}

Async usage (Tokio)

Enable the async feature:

[dependencies]
eoka-email = { path = "../eoka-tools/crates/eoka-email", features = ["async"] }
use chrono::Duration;
use eoka_email::{AsyncImapClient, ImapConfig, SearchCriteria, WaitOptions};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let config = ImapConfig::new("imap.gmail.com", 993, "user@gmail.com", "app-password");
    let criteria = SearchCriteria::new()
        .subject_contains("Your verification code")
        .unseen_only(true)
        .since_minutes(10);
    let options = WaitOptions::new(Duration::minutes(2), Duration::seconds(2));

    let mut client = AsyncImapClient::connect(&config).await?;
    let msg = client.wait_for_message(&criteria, &options).await?;
    println!("Subject: {:?}", msg.subject);
    Ok(())
}

Notes

  • Use app passwords when IMAP access is required (Gmail/Outlook).
  • SearchCriteria::mark_seen(true) will set \\Seen after fetching.
  • Filtering by since_minutes is recommended to avoid picking older emails.