zap-go

Repositório zap-go
git clone git://git.sivaldodavi.com/zap-go.git
Log | Files | Refs | README

commit 2a5c76fa1a972a35413591c7e0018465c4e99905
parent 7d4931c0de5ab306755d41e740eb1b27f080ccd9
Author: Sivaldo <sivaldodavi@disroot.org>
Date:   Tue,  7 Apr 2026 11:19:07 -0300

Múltiplos arquivos são anexados

Diffstat:
Mmain.go | 111+++++++++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 52 insertions(+), 59 deletions(-)

diff --git a/main.go b/main.go @@ -30,7 +30,7 @@ import ( ) // --------------------------------------------------------------------------- -// Log em arquivo (rotativo simples: renomeia ao passar de 1 MB) +// Log em arquivo // --------------------------------------------------------------------------- var ( @@ -81,7 +81,7 @@ type Config struct { SMTP SMTPConfig `toml:"smtp"` } -var exeDir string // diretório do binário, calculado uma vez no main +var exeDir string func loadConfig() Config { cfgPath := filepath.Join(exeDir, "config.toml") @@ -94,7 +94,7 @@ func loadConfig() Config { } // --------------------------------------------------------------------------- -// Lock file — impede dupla instância +// Lock file // --------------------------------------------------------------------------- func acquireLock() (*os.File, bool) { @@ -103,7 +103,6 @@ func acquireLock() (*os.File, bool) { if err != nil { return nil, false } - // Tenta lock exclusivo não-bloqueante via syscall err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) if err != nil { f.Close() @@ -120,34 +119,6 @@ func releaseLock(f *os.File) { } // --------------------------------------------------------------------------- -// Email -// --------------------------------------------------------------------------- - -func sendEmailWithAttachment(cfg Config, subject, body, fileName string, fileData []byte) { - e := email.NewEmail() - e.From = fmt.Sprintf("Zap <%s>", cfg.SMTP.User) - e.To = []string{cfg.SMTP.User} - e.Subject = subject - e.Text = []byte(body) - if len(fileData) > 0 { - e.Attach(bytes.NewReader(fileData), fileName, "application/octet-stream") - } - addr := fmt.Sprintf("%s:%d", cfg.SMTP.Host, cfg.SMTP.Port) - auth := smtp.PlainAuth("", cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.Host) - var err error - if cfg.SMTP.Port == 465 { - err = e.SendWithTLS(addr, auth, &tls.Config{ServerName: cfg.SMTP.Host}) - } else { - err = e.Send(addr, auth) - } - if err != nil { - logf("erro ao enviar email: %v", err) - } else { - logf("email enviado: %s", subject) - } -} - -// --------------------------------------------------------------------------- // Abook & helpers // --------------------------------------------------------------------------- @@ -198,7 +169,6 @@ func cleanNumber(s string) string { }, s) } -// removeNinthDigit remove o 9 extra (Brasil): 55 + DD + 9 + 8 dígitos → 55 + DD + 8 dígitos func removeNinthDigit(n string) string { if len(n) == 13 && strings.HasPrefix(n, "55") { return n[:4] + n[5:] @@ -239,17 +209,21 @@ func portugueseWeekday(d time.Weekday) string { } // --------------------------------------------------------------------------- -// Batcher +// Batcher (Com suporte a múltiplos arquivos) // --------------------------------------------------------------------------- const flushDelay = 2 * time.Minute +type FileAttachment struct { + Name string + Data []byte +} + type PendingMsg struct { - msgType string - txt string - fileName string - fileData []byte - ts time.Time + msgType string + txt string + Files []FileAttachment + ts time.Time } type Batcher struct { @@ -277,9 +251,7 @@ func (b *Batcher) Add(jid, pushName string, msg PendingMsg) { b.pending[jid] = append(b.pending[jid], msg) - if _, seen := b.names[jid]; !seen { - b.names[jid] = findName(jid, b.contacts, pushName) - } else if b.names[jid] == jid || b.names[jid] == "" { + if _, seen := b.names[jid]; !seen || b.names[jid] == jid || b.names[jid] == "" { b.names[jid] = findName(jid, b.contacts, pushName) } @@ -311,12 +283,13 @@ func (b *Batcher) flush(jid string) { return } + e := email.NewEmail() + e.From = fmt.Sprintf("Zap <%s>", b.cfg.SMTP.User) + e.To = []string{b.cfg.SMTP.User} + var sb strings.Builder fmt.Fprintf(&sb, "De: %s\n%d mensagem(ns)\n\n", name, len(msgs)) - var lastFileData []byte - var lastFileName string - for i, m := range msgs { fmt.Fprintf(&sb, "── %d [%s] %s %s ──\n", i+1, m.msgType, @@ -325,19 +298,36 @@ func (b *Batcher) flush(jid string) { if m.txt != "" { fmt.Fprintf(&sb, "%s\n", m.txt) } - if len(m.fileData) > 0 { - lastFileData = m.fileData - lastFileName = m.fileName + + // Anexa todos os arquivos desta mensagem específica + for _, file := range m.Files { + if len(file.Data) > 0 { + e.Attach(bytes.NewReader(file.Data), file.Name, "application/octet-stream") + } } sb.WriteString("\n") } - subject := fmt.Sprintf("Zap de %s [%d msg]", name, len(msgs)) - sendEmailWithAttachment(b.cfg, subject, sb.String(), lastFileName, lastFileData) + e.Subject = fmt.Sprintf("Zap de %s [%d msg]", name, len(msgs)) + e.Text = []byte(sb.String()) + + addr := fmt.Sprintf("%s:%d", b.cfg.SMTP.Host, b.cfg.SMTP.Port) + auth := smtp.PlainAuth("", b.cfg.SMTP.User, b.cfg.SMTP.Password, b.cfg.SMTP.Host) + + var err error + if b.cfg.SMTP.Port == 465 { + err = e.SendWithTLS(addr, auth, &tls.Config{ServerName: b.cfg.SMTP.Host}) + } else { + err = e.Send(addr, auth) + } + + if err != nil { + logf("erro ao enviar email: %v", err) + } else { + logf("email enviado: %s", e.Subject) + } } -// FlushAll cancela todos os timers e envia imediatamente os lotes pendentes. -// Chamado no shutdown para não perder mensagens acumuladas. func (b *Batcher) FlushAll() { b.mu.Lock() jids := make([]string, 0, len(b.pending)) @@ -405,6 +395,7 @@ func buildHandler(client *whatsmeow.Client, batcher *Batcher) func(interface{}) if !ok || v.Info.IsFromMe { return } + // Ignora grupos e status if v.Info.Chat.Server == "g.us" || v.Info.Chat.User == "status" { return } @@ -459,12 +450,16 @@ func buildHandler(client *whatsmeow.Client, batcher *Batcher) func(interface{}) logf("erro download: %v", err) } + var files []FileAttachment + if len(fileData) > 0 { + files = append(files, FileAttachment{Name: fileName, Data: fileData}) + } + batcher.Add(v.Info.Sender.User, v.Info.PushName, PendingMsg{ - msgType: msgType, - txt: txt, - fileName: fileName, - fileData: fileData, - ts: v.Info.Timestamp, + msgType: msgType, + txt: txt, + Files: files, + ts: v.Info.Timestamp, }) } } @@ -489,7 +484,6 @@ func runDaemon() { client.AddEventHandler(buildHandler(client, batcher)) - // Reconexão automática com backoff exponencial client.AddEventHandler(func(evt interface{}) { if _, ok := evt.(*events.Disconnected); !ok { return @@ -576,7 +570,6 @@ func runSend() { // --------------------------------------------------------------------------- func main() { - // Calcula exeDir uma vez — usado por loadConfig, newClient, initLog, acquireLock exe, _ := os.Executable() real, _ := filepath.EvalSymlinks(exe) exeDir = filepath.Dir(real)