Как бы вы реализовали чтение писем из почтового ящика в Go? Расскажите об основных шагах и библиотеках.

Ответ

Да, приходилось. Для работы с IMAP в Go я бы использовал стандартную де-факто библиотеку github.com/emersion/go-imap и ее экосистему.

Процесс работы с почтой обычно состоит из следующих шагов:

  1. Подключение к серверу: Установка безопасного соединения с IMAP-сервером, обычно через TLS (client.DialTLS).
  2. Аутентификация: Вход в систему с использованием логина и пароля (client.Login).
  3. Выбор почтового ящика (Mailbox): Выбор папки для работы, например, INBOX (client.Select).
  4. Поиск и получение писем (Search & Fetch): Сначала можно найти ID нужных писем (например, все непрочитанные) с помощью client.Search, а затем запросить их содержимое (client.Fetch). При запросе можно указать, какие части письма нужны: ENVELOPE (метаданные), BODY[] (содержимое), UID и т.д.
  5. Парсинг MIME-содержимого: Тело письма — это сложная структура (MIME). Для его разбора на текст, HTML и вложения понадобится дополнительная библиотека, например, net/mail из стандартной библиотеки или github.com/emersion/go-message.

Пример кода (чтение темы последнего письма):

package main

import (
    "log"
    "github.com/emersion/go-imap"
    "github.com/emersion/go-imap/client"
)

func main() {
    c, err := client.DialTLS("imap.example.com:993", nil)
    if err != nil { log.Fatal(err) }
    defer c.Logout()

    if err := c.Login("user@example.com", "password"); err != nil {
        log.Fatal(err)
    }

    mbox, err := c.Select("INBOX", true) // true - read-only
    if err != nil { log.Fatal(err) }

    if mbox.Messages == 0 {
        log.Fatal("No messages in inbox")
    }

    // Берем последнее письмо
    seqset := new(imap.SeqSet)
    seqset.AddNum(mbox.Messages)

    // Запрашиваем его конверт (тема, отправитель и т.д.)
    messages := make(chan *imap.Message, 1)
    go func() {
        if err := c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages); err != nil {
            log.Fatal(err)
        }
    }()

    msg := <-messages
    log.Println("Subject:", msg.Envelope.Subject)
}

Дополнительные возможности:

Библиотека также поддерживает продвинутые функции, такие как команда IDLE, которая позволяет получать уведомления о новых письмах в реальном времени без необходимости постоянно опрашивать сервер (polling).