resource-scraper/internal/mail/mail.go

173 lines
3.8 KiB
Go

package mail
import (
"fmt"
"log"
"strings"
"github.com/emersion/go-imap/v2"
client "github.com/emersion/go-imap/v2/imapclient"
"github.com/logrusorgru/aurora/v4"
"github.com/spf13/viper"
"git.amok.space/yevhen/resource-scraper/helper/sugar"
)
type EmailService struct {
User string
pass string
Err error
client *client.Client
Mailboxes []*imap.SelectData
Messages []*client.FetchMessageBuffer
}
func (s *EmailService) CheckErr(msg string, err error) bool {
if err != nil {
if sugar.IsDev() {
sugar.LogError(err.Error())
}
s.Err = err
return true
}
return false
}
func (s *EmailService) success(msg string) {
if sugar.IsDev() {
sugar.LogSuccess(msg)
}
}
func (s *EmailService) Warn(msg string) {
if sugar.IsDev() {
sugar.LogWarning(msg)
}
}
func (s *EmailService) Connect() {
box := strings.Split(s.User, "@")
mail := viper.GetStringMapString("mail." + box[1])
conn, err := client.DialTLS(mail["dial-tls"], nil)
if s.CheckErr("DialTLS", err) {
return
}
s.client = conn
s.pass = mail[s.User]
s.success("connected to " + mail["dial-tls"])
//defer s.Logout()
}
func (s *EmailService) Login() {
s.Connect()
err := s.client.Login(s.User, s.pass).Wait()
if s.CheckErr("Login", err) {
return
}
s.success(s.User + " logged")
}
func (s *EmailService) ListMailboxes(mailboxes []string) {
selectOptions := &imap.SelectOptions{ReadOnly: true}
for _, mailbox := range mailboxes {
mbox, err := s.client.Select(mailbox, selectOptions).Wait()
if s.CheckErr("Listing mailbox", err) {
return
}
s.Mailboxes = append(s.Mailboxes, mbox)
}
if len(s.Mailboxes) == 0 {
s.Warn(sugar.SqueezeLine("mailboxes " + strings.Join(mailboxes, ", ") + " not found"))
}
}
func (s *EmailService) ListMessages(mailboxes []string, criteria *imap.SearchCriteria) {
for _, mailbox := range mailboxes {
mbox, err := s.client.Select(mailbox, nil).Wait()
if s.CheckErr("Listing mailbox", err) {
continue
}
seqs, err := s.client.UIDSearch(criteria, nil).Wait()
if s.CheckErr("UIDSearch", err) {
return
}
seqSet := imap.SeqSet{}
seqSet.AddRange(1, mbox.NumMessages)
if len(seqs.AllUIDs()) == 0 {
s.Warn("no messages found in mailbox: " + mailbox)
continue
}
s.success(fmt.Sprintf("Search complete, found %d messages: %+v", len(seqs.AllUIDs()), mbox.UIDNext))
fetchOptions := &imap.FetchOptions{
Envelope: true,
Flags: true,
BodyStructure: &imap.FetchItemBodyStructure{Extended: true},
BodySection: []*imap.FetchItemBodySection{{Peek: true, Part: []int{2}}},
}
messages, err := s.client.Fetch(seqSet, fetchOptions).Collect()
if s.CheckErr("Fetch", err) {
continue
}
s.Messages = messages
}
}
func (s *EmailService) LogOut() {
err := s.client.Logout().Wait()
if !s.CheckErr("failed to logout", err) {
s.success("logged out successfully")
}
}
func (s *EmailService) CreateMailbox(mailboxName string) {
s.client.Create(mailboxName, nil)
}
func (s *EmailService) MailboxesList() {
listCmd := s.client.List("", "%", &imap.ListOptions{
ReturnStatus: &imap.StatusOptions{
NumMessages: true,
NumUnseen: true,
},
})
for {
mbox := listCmd.Next()
if mbox == nil {
break
}
fmt.Printf("%+v\n", mbox)
}
if err := listCmd.Close(); err != nil {
log.Fatalf("LIST command failed: %v", err)
}
}
func (s *EmailService) MoveMessageToMailbox(msg *client.FetchMessageBuffer, status string) bool {
movable := imap.SeqSet{}
movable.AddNum(msg.SeqNum)
mailbox := viper.GetStringMapString("stb.move-processed-to-mailbox")
wait, err := s.client.Move(movable, mailbox[status]).Wait()
if s.CheckErr("Moving to archive", err) {
return false
}
fmt.Printf("Message %s moved, srcUIDs: %s, dstUIDs: %s\n", aurora.White(msg.Envelope.MessageID), aurora.Yellow(wait.SourceUIDs), aurora.Yellow(wait.DestUIDs))
return true
}