commit 1d596be5caaac14a7e652ecba1e544150b17a8f6
parent 5e54e6ca2635d889639fb5cf9a2317278fc42b48
Author: Sivaldo <sivaldodavi@disroot.org>
Date: Fri, 3 Apr 2026 18:57:15 -0300
Simplificação: receber mensagem via e-mail e mandar como antes
Diffstat:
| M | .gitignore | | | 5 | +---- |
| D | README.md | | | 38 | -------------------------------------- |
| M | go.mod | | | 37 | ++++++++++--------------------------- |
| M | go.sum | | | 76 | ++++++++++++++++++++-------------------------------------------------------- |
| M | main.go | | | 648 | +++++++++++++++++++++++-------------------------------------------------------- |
5 files changed, 218 insertions(+), 586 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,11 +1,8 @@
# Binários
-zap-tui
-whatsapp_client
+zap
# Dados sensíveis
whatsapp.db
-whatsapp.db-shm
-whatsapp.db-wal
# Logs e temporários
/tmp/wtc_status
diff --git a/README.md b/README.md
@@ -1,38 +0,0 @@
-
-### 1. Instalar
-```bash
-go mod tidy
-go build -o zap-tui main.go
-
-```
-
-### 2. Uso Global (Link Simbólico)
-
-```bash
-ln -sf $(pwd)/zap-tui /usr/local/bin
-
-```
-
-### 3. Comandos
-
-* **Enviar:** `zap-tui send` (abre FZF para escolher contato)
-* **Receber:** `zap-tui receive` (monitora mensagens)
-
-### 4. Contatos
-
-O arquivo deve estar em: `~/.local/share/contatos`
-
-**Formato:**
-
-```csv
-Nome,Telefone,Email
-Alessandro,+5598xxxxxxxx,
-
-```
-
-### 5. Atalhos
-
-* **Sair:** `Ctrl + C`
-* **Enviar no chat:** `Enter`
-
-```
diff --git a/go.mod b/go.mod
@@ -3,48 +3,31 @@ module wtc
go 1.25.5
require (
- github.com/charmbracelet/bubbles v0.21.0
- github.com/charmbracelet/bubbletea v1.3.10
- github.com/charmbracelet/lipgloss v1.1.0
- github.com/mattn/go-sqlite3 v1.14.33
+ github.com/BurntSushi/toml v1.6.0
+ github.com/mattn/go-sqlite3 v1.14.40
github.com/mdp/qrterminal/v3 v3.2.1
- go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141
+ go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4
google.golang.org/protobuf v1.36.11
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
- github.com/atotto/clipboard v0.1.4 // indirect
- github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beeper/argo-go v1.1.2 // indirect
- github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
- github.com/charmbracelet/x/ansi v0.10.1 // indirect
- github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
- github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/coder/websocket v1.8.14 // indirect
github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect
- github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
- github.com/muesli/cancelreader v0.2.2 // indirect
- github.com/muesli/termenv v0.16.0 // indirect
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/vektah/gqlparser/v2 v2.5.27 // indirect
- github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.mau.fi/libsignal v0.2.1 // indirect
- go.mau.fi/util v0.9.5 // indirect
- golang.org/x/crypto v0.47.0 // indirect
- golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
- golang.org/x/net v0.49.0 // indirect
- golang.org/x/sys v0.40.0 // indirect
- golang.org/x/term v0.39.0 // indirect
- golang.org/x/text v0.33.0 // indirect
+ go.mau.fi/util v0.9.6 // indirect
+ golang.org/x/crypto v0.48.0 // indirect
+ golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect
+ golang.org/x/net v0.50.0 // indirect
+ golang.org/x/sys v0.41.0 // indirect
+ golang.org/x/term v0.40.0 // indirect
+ golang.org/x/text v0.34.0 // indirect
rsc.io/qr v0.2.0 // indirect
)
diff --git a/go.sum b/go.sum
@@ -1,31 +1,15 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
+github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
-github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
-github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/beeper/argo-go v1.1.2 h1:UQI2G8F+NLfGTOmTUI0254pGKx/HUU/etbUGTJv91Fs=
github.com/beeper/argo-go v1.1.2/go.mod h1:M+LJAnyowKVQ6Rdj6XYGEn+qcVFkb3R/MUpqkGR0hM4=
-github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
-github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
-github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
-github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
-github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
-github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
-github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
-github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
-github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
-github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -33,15 +17,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@@ -49,28 +29,15 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
-github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
-github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mattn/go-sqlite3 v1.14.40 h1:f7+saIsbq4EF86mUqe0uiecQOJYMOdfi5uATADmUG94=
+github.com/mattn/go-sqlite3 v1.14.40/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4=
github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
-github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
-github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
-github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
-github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
@@ -80,30 +47,27 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s=
github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0=
go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU=
-go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4=
-go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ=
-go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141 h1:pa4WhVPKTubDgPnsza/UOKWP4eC1d8kLxNw69O/Npk8=
-go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA=
-golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
-golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
-golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
-golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
-golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
-golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+go.mau.fi/util v0.9.6 h1:2nsvxm49KhI3wrFltr0+wSUBlnQ4CMtykuELjpIU+ts=
+go.mau.fi/util v0.9.6/go.mod h1:sIJpRH7Iy5Ad1SBuxQoatxtIeErgzxCtjd/2hCMkYMI=
+go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 h1:E4A6eca9vMJQctC9DIfzUIg27TrJ8IrDHgkJwJ8WPUQ=
+go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM=
+golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
+golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
+golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o=
+golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
+golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
+golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
-golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
-golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
-golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
-golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
+golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
+golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
+golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
+golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
+golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/main.go b/main.go
@@ -3,19 +3,18 @@ package main
import (
"bufio"
"context"
- "encoding/json"
+ "crypto/tls"
"fmt"
+ "net/smtp"
"os"
"os/exec"
+ "os/signal"
"path/filepath"
- "sort"
"strings"
+ "syscall"
"time"
- "github.com/charmbracelet/bubbles/textinput"
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/BurntSushi/toml"
"github.com/mdp/qrterminal/v3"
_ "github.com/mattn/go-sqlite3"
"go.mau.fi/whatsmeow"
@@ -27,121 +26,119 @@ import (
"google.golang.org/protobuf/proto"
)
-// ─── Estilos (aerc-like) ──────────────────────────────────────────────────────
+// --- Config ---
-var (
- styleHeader = lipgloss.NewStyle().
- Bold(true).
- Foreground(lipgloss.Color("15")).
- Background(lipgloss.Color("238")).
- Padding(0, 1)
-
- styleStatus = lipgloss.NewStyle().
- Foreground(lipgloss.Color("243")).
- Background(lipgloss.Color("236")).
- Padding(0, 1)
-
- styleDivider = lipgloss.NewStyle().
- Foreground(lipgloss.Color("240"))
-
- styleMyName = lipgloss.NewStyle().
- Foreground(lipgloss.Color("10")).
- Bold(true)
-
- styleTheirName = lipgloss.NewStyle().
- Foreground(lipgloss.Color("12")).
- Bold(true)
-
- styleTimestamp = lipgloss.NewStyle().
- Foreground(lipgloss.Color("240"))
-
- styleMyMsg = lipgloss.NewStyle().
- Foreground(lipgloss.Color("252"))
-
- styleTheirMsg = lipgloss.NewStyle().
- Foreground(lipgloss.Color("255"))
-
- styleInputPrompt = lipgloss.NewStyle().
- Foreground(lipgloss.Color("10")).
- Bold(true)
-
- styleInputBar = lipgloss.NewStyle().
- BorderTop(true).
- BorderStyle(lipgloss.NormalBorder()).
- BorderForeground(lipgloss.Color("240"))
+type SMTPConfig struct {
+ Host string `toml:"host"`
+ Port int `toml:"port"`
+ User string `toml:"user"`
+ Password string `toml:"password"`
+}
- styleUnreadBadge = lipgloss.NewStyle().
- Foreground(lipgloss.Color("0")).
- Background(lipgloss.Color("9")).
- Bold(true).
- Padding(0, 1)
-)
+type Config struct {
+ SMTP SMTPConfig `toml:"smtp"`
+}
-// ─── Histórico persistente ────────────────────────────────────────────────────
+func loadConfig() Config {
+ exePath, _ := os.Executable()
+ realPath, _ := filepath.EvalSymlinks(exePath)
+ cfgPath := filepath.Join(filepath.Dir(realPath), "config.toml")
-type storedMsg struct {
- Sender string `json:"sender"`
- Content string `json:"content"`
- IsMe bool `json:"is_me"`
- Time time.Time `json:"time"`
+ var cfg Config
+ if _, err := toml.DecodeFile(cfgPath, &cfg); err != nil {
+ fmt.Fprintln(os.Stderr, "erro ao ler config.toml:", err)
+ os.Exit(1)
+ }
+ return cfg
}
-type chatMetadata struct {
- LastRead time.Time `json:"last_read"`
-}
+// --- Email ---
-func dataDir() string {
- dir := filepath.Join(os.Getenv("HOME"), ".local", "share", "zap-tui")
- os.MkdirAll(dir, 0700)
- return dir
+func portugueseWeekday(d time.Weekday) string {
+ return [...]string{"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"}[d]
}
-func historyPath(phone string) string {
- return filepath.Join(dataDir(), phone+".json")
-}
+func sendEmailRaw(cfg Config, subject, body string) {
+ addr := fmt.Sprintf("%s:%d", cfg.SMTP.Host, cfg.SMTP.Port)
+ auth := smtp.PlainAuth("", cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.Host)
-func metadataPath(phone string) string {
- return filepath.Join(dataDir(), phone+".meta.json")
-}
+ msg := fmt.Sprintf(
+ "From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n%s",
+ cfg.SMTP.User, cfg.SMTP.User, subject, body,
+ )
+
+ var err error
+ if cfg.SMTP.Port == 465 {
+ conn, e := tls.Dial("tcp", addr, &tls.Config{ServerName: cfg.SMTP.Host})
+ if e != nil {
+ fmt.Fprintln(os.Stderr, "erro tls:", e)
+ return
+ }
+ c, e := smtp.NewClient(conn, cfg.SMTP.Host)
+ if e != nil {
+ fmt.Fprintln(os.Stderr, "erro smtp:", e)
+ return
+ }
+ defer c.Close()
+ c.Auth(auth)
+ c.Mail(cfg.SMTP.User)
+ c.Rcpt(cfg.SMTP.User)
+ w, _ := c.Data()
+ fmt.Fprint(w, msg)
+ err = w.Close()
+ } else {
+ err = smtp.SendMail(addr, auth, cfg.SMTP.User, []string{cfg.SMTP.User}, []byte(msg))
+ }
-func loadHistory(phone string) []storedMsg {
- data, err := os.ReadFile(historyPath(phone))
if err != nil {
- return nil
+ fmt.Fprintln(os.Stderr, "erro ao enviar email:", err)
+ } else {
+ fmt.Printf("[%s] email enviado: %s\n", time.Now().Format("15:04:05"), subject)
}
- var msgs []storedMsg
- json.Unmarshal(data, &msgs)
- return msgs
}
-func saveHistory(phone string, msgs []storedMsg) {
- data, _ := json.Marshal(msgs)
- os.WriteFile(historyPath(phone), data, 0600)
-}
+// --- WhatsApp ---
+
+func newClient() *whatsmeow.Client {
+ exePath, _ := os.Executable()
+ realPath, _ := filepath.EvalSymlinks(exePath)
+ dbPath := filepath.Join(filepath.Dir(realPath), "whatsapp.db")
+ dbURI := fmt.Sprintf("file:%s?_foreign_keys=on", dbPath)
-func loadMetadata(phone string) chatMetadata {
- data, err := os.ReadFile(metadataPath(phone))
+ container, err := sqlstore.New(
+ context.Background(), "sqlite3", dbURI,
+ waLog.Stdout("Database", "ERROR", true),
+ )
if err != nil {
- return chatMetadata{}
+ fmt.Fprintln(os.Stderr, "erro db:", err)
+ os.Exit(1)
}
- var meta chatMetadata
- json.Unmarshal(data, &meta)
- return meta
+ device, _ := container.GetFirstDevice(context.Background())
+ return whatsmeow.NewClient(device, waLog.Stdout("Client", "ERROR", true))
}
-func saveMetadata(phone string, meta chatMetadata) {
- data, _ := json.Marshal(meta)
- os.WriteFile(metadataPath(phone), data, 0600)
+func connectClient(client *whatsmeow.Client) {
+ if client.Store.ID == nil {
+ qrChan, _ := client.GetQRChannel(context.Background())
+ client.Connect()
+ for evt := range qrChan {
+ if evt.Event == "code" {
+ qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
+ }
+ }
+ } else {
+ client.Connect()
+ }
}
-// ─── Contatos (formato abook) ─────────────────────────────────────────────────
+// --- Abook ---
-type contact struct {
- name string
- mobile string
+type Contact struct {
+ Name string
+ Mobile string
}
-func loadAbook() []contact {
+func loadAbook() []Contact {
path := filepath.Join(os.Getenv("HOME"), ".local", "share", "abook", "addressbook")
f, err := os.Open(path)
if err != nil {
@@ -149,28 +146,28 @@ func loadAbook() []contact {
}
defer f.Close()
- var contacts []contact
- var cur contact
+ var contacts []Contact
+ var cur Contact
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "[") && line != "[format]" {
- if cur.name != "" && cur.mobile != "" {
+ if cur.Name != "" && cur.Mobile != "" {
contacts = append(contacts, cur)
}
- cur = contact{}
+ cur = Contact{}
continue
}
if k, v, ok := strings.Cut(line, "="); ok {
switch strings.TrimSpace(k) {
case "name":
- cur.name = strings.TrimSpace(v)
+ cur.Name = strings.TrimSpace(v)
case "mobile":
- cur.mobile = strings.TrimSpace(v)
+ cur.Mobile = strings.TrimSpace(v)
}
}
}
- if cur.name != "" && cur.mobile != "" {
+ if cur.Name != "" && cur.Mobile != "" {
contacts = append(contacts, cur)
}
return contacts
@@ -185,402 +182,131 @@ func cleanNumber(s string) string {
}, s)
}
-// ─── Lista de Conversas ───────────────────────────────────────────────────────
-
-type chatInfo struct {
- contact contact
- phone string
- lastMsg time.Time
- unreadCount int
- preview string
+func findName(jidUser string, contacts []Contact, pushName string) string {
+ clean := cleanNumber(jidUser)
+ if len(clean) >= 8 {
+ suffix := clean[len(clean)-8:]
+ for _, c := range contacts {
+ s := cleanNumber(c.Mobile)
+ if len(s) >= 8 && s[len(s)-8:] == suffix {
+ return c.Name
+ }
+ }
+ }
+ if pushName != "" {
+ return pushName
+ }
+ return jidUser
}
-func getChats(contacts []contact) []chatInfo {
- var chats []chatInfo
+// --- Comandos ---
- for _, c := range contacts {
- phone := cleanNumber(c.mobile)
- history := loadHistory(phone)
- if len(history) == 0 {
- continue
- }
+func runDaemon() {
+ cfg := loadConfig()
+ contacts := loadAbook()
+ client := newClient()
- meta := loadMetadata(phone)
- lastMsg := history[len(history)-1]
-
- unread := 0
- preview := lastMsg.Content
- if len(preview) > 50 {
- preview = preview[:50] + "..."
+ client.AddEventHandler(func(evt interface{}) {
+ v, ok := evt.(*events.Message)
+ if !ok || v.Info.IsFromMe {
+ return
}
- for i := len(history) - 1; i >= 0; i-- {
- if history[i].Time.After(meta.LastRead) && !history[i].IsMe {
- unread++
- }
+ // Tenta pegar o texto (conversa simples ou estendida)
+ txt := ""
+ if v.Message.Conversation != nil {
+ txt = *v.Message.Conversation
+ } else if v.Message.ExtendedTextMessage != nil {
+ txt = *v.Message.ExtendedTextMessage.Text
}
- chats = append(chats, chatInfo{
- contact: c,
- phone: phone,
- lastMsg: lastMsg.Time,
- unreadCount: unread,
- preview: preview,
- })
- }
-
- sort.Slice(chats, func(i, j int) bool {
- return chats[i].lastMsg.After(chats[j].lastMsg)
- })
+ if txt == "" {
+ return
+ }
- return chats
-}
+ name := findName(v.Info.Sender.User, contacts, v.Info.PushName)
+ fmt.Printf("[%s] %s -> Enviando email...\n", time.Now().Format("15:04:05"), name)
-func pickChatFzf(chats []chatInfo) (contact, bool) {
- if len(chats) == 0 {
- return contact{}, false
- }
+ // Formata corpo do email
+ body := fmt.Sprintf("De: %s\nData: [%s %s]\n\nMensagem:\n%s",
+ name,
+ portugueseWeekday(v.Info.Timestamp.Weekday()),
+ v.Info.Timestamp.Format("02/01 15:04"),
+ txt,
+ )
- var lines []string
- for _, c := range chats {
- badge := ""
- if c.unreadCount > 0 {
- badge = fmt.Sprintf("[%d] ", c.unreadCount)
- }
- when := c.lastMsg.Format("02/01 15:04")
- line := fmt.Sprintf("%s%s\t%s\t%s\t%s",
- badge, c.contact.name, c.contact.mobile, when, c.preview)
- lines = append(lines, line)
- }
+ subject := fmt.Sprintf("WhatsApp de %s", name)
+ sendEmailRaw(cfg, subject, body)
+ })
- cmd := exec.Command("fzf",
- "--prompt=Conversa: ",
- "--delimiter=\t",
- "--with-nth=1",
- "--header=Enter para abrir Tab para novo Esc para sair",
- "--bind=tab:abort",
- )
- cmd.Stdin = strings.NewReader(strings.Join(lines, "\n"))
- cmd.Stderr = os.Stderr
- out, err := cmd.Output()
-
- if err != nil {
- if err.Error() == "exit status 130" { // Tab pressed
- return pickContactFzf(loadAbook())
- }
- return contact{}, false
- }
+ connectClient(client)
+ fmt.Println("daemon rodando (modo imediato). Ctrl+C para parar.")
- parts := strings.SplitN(strings.TrimSpace(string(out)), "\t", 4)
- if len(parts) < 2 {
- return contact{}, false
- }
-
- name := strings.TrimLeft(parts[0], "0123456789[] ")
- mobile := parts[1]
-
- return contact{name: name, mobile: mobile}, true
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+ client.Disconnect()
}
-func pickContactFzf(contacts []contact) (contact, bool) {
- if len(contacts) == 0 {
- return contact{}, false
- }
+func runSend() {
+ contacts := loadAbook()
var lines []string
for _, c := range contacts {
- lines = append(lines, fmt.Sprintf("%s\t%s", c.name, c.mobile))
+ lines = append(lines, fmt.Sprintf("%s\t%s", c.Name, c.Mobile))
}
-
- cmd := exec.Command("fzf",
- "--prompt=Novo contato: ",
- "--delimiter=\t",
- "--with-nth=1",
- "--header=Enter para abrir Esc para sair",
- )
+ cmd := exec.Command("fzf", "--prompt=para: ", "--delimiter=\t", "--with-nth=1")
cmd.Stdin = strings.NewReader(strings.Join(lines, "\n"))
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
- return contact{}, false
+ return
}
parts := strings.SplitN(strings.TrimSpace(string(out)), "\t", 2)
if len(parts) < 2 {
- return contact{}, false
- }
- return contact{name: parts[0], mobile: parts[1]}, true
-}
-
-// ─── Modelo Bubble Tea ────────────────────────────────────────────────────────
-
-type incomingMsg struct {
- sender string
- content string
- isMe bool
- ts time.Time
-}
-
-type model struct {
- client *whatsmeow.Client
- target contact
- phone string
- history []storedMsg
- viewport viewport.Model
- textInput textinput.Model
- width int
- height int
- ready bool
-}
-
-func newModel(client *whatsmeow.Client, target contact) model {
- phone := cleanNumber(target.mobile)
-
- ti := textinput.New()
- ti.Placeholder = "mensagem..."
- ti.Focus()
- ti.CharLimit = 1000
-
- // Marcar como lido ao abrir
- meta := loadMetadata(phone)
- meta.LastRead = time.Now()
- saveMetadata(phone, meta)
-
- return model{
- client: client,
- target: target,
- phone: phone,
- history: loadHistory(phone),
- textInput: ti,
- }
-}
-
-func (m model) Init() tea.Cmd {
- return textinput.Blink
-}
-
-func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- var cmds []tea.Cmd
-
- switch msg := msg.(type) {
-
- case tea.WindowSizeMsg:
- m.width = msg.Width
- m.height = msg.Height
- vpH := m.height - 4
- if vpH < 1 {
- vpH = 1
- }
- if !m.ready {
- m.viewport = viewport.New(m.width, vpH)
- m.ready = true
- } else {
- m.viewport.Width = m.width
- m.viewport.Height = vpH
- }
- m.viewport.SetContent(m.renderHistory())
- m.viewport.GotoBottom()
-
- case tea.KeyMsg:
- switch msg.Type {
- case tea.KeyCtrlC:
- saveHistory(m.phone, m.history)
- // Atualizar última leitura ao sair
- meta := loadMetadata(m.phone)
- meta.LastRead = time.Now()
- saveMetadata(m.phone, meta)
- return m, tea.Quit
-
- case tea.KeyEnter:
- text := strings.TrimSpace(m.textInput.Value())
- if text == "" {
- break
- }
- jid, err := types.ParseJID(m.phone + "@s.whatsapp.net")
- if err == nil {
- go m.client.SendMessage(context.Background(), jid, &waE2E.Message{
- Conversation: proto.String(text),
- })
- }
- entry := storedMsg{Sender: "eu", Content: text, IsMe: true, Time: time.Now()}
- m.history = append(m.history, entry)
- saveHistory(m.phone, m.history)
- m.textInput.Reset()
- m.viewport.SetContent(m.renderHistory())
- m.viewport.GotoBottom()
- }
-
- case incomingMsg:
- entry := storedMsg{
- Sender: msg.sender,
- Content: msg.content,
- IsMe: msg.isMe,
- Time: msg.ts,
- }
- m.history = append(m.history, entry)
- saveHistory(m.phone, m.history)
- m.viewport.SetContent(m.renderHistory())
- m.viewport.GotoBottom()
- }
-
- var vpCmd tea.Cmd
- m.viewport, vpCmd = m.viewport.Update(msg)
- cmds = append(cmds, vpCmd)
-
- var tiCmd tea.Cmd
- m.textInput, tiCmd = m.textInput.Update(msg)
- cmds = append(cmds, tiCmd)
-
- return m, tea.Batch(cmds...)
-}
-
-func (m model) renderHistory() string {
- if len(m.history) == 0 {
- return styleTimestamp.Render(" sem mensagens ainda")
- }
-
- var sb strings.Builder
- lastDate := ""
-
- for _, msg := range m.history {
- date := msg.Time.Format("02 Jan 2006")
- if date != lastDate {
- lastDate = date
- pad := (m.width - len(date) - 4) / 2
- if pad < 1 {
- pad = 1
- }
- line := strings.Repeat("─", pad) + " " + date + " " + strings.Repeat("─", pad)
- sb.WriteString("\n" + styleDivider.Render(line) + "\n\n")
- }
-
- ts := styleTimestamp.Render(msg.Time.Format("15:04"))
-
- if msg.IsMe {
- name := styleMyName.Render("eu")
- content := styleMyMsg.Render(msg.Content)
- sb.WriteString(fmt.Sprintf(" %s %s %s\n", name, ts, content))
- } else {
- name := styleTheirName.Render(msg.Sender)
- content := styleTheirMsg.Render(msg.Content)
- sb.WriteString(fmt.Sprintf(" %s %s %s\n", name, ts, content))
- }
- }
- return sb.String()
-}
-
-func (m model) View() string {
- if !m.ready {
- return ""
- }
-
- header := styleHeader.Width(m.width).Render(
- fmt.Sprintf("zap ─ %s (%s)", m.target.name, m.target.mobile),
- )
-
- chat := m.viewport.View()
-
- prompt := styleInputPrompt.Render(" > ")
- inputArea := styleInputBar.Width(m.width).Render(prompt + m.textInput.View())
-
- status := styleStatus.Width(m.width).Render(
- fmt.Sprintf(" %d mensagens │ PgUp/PgDn rolar │ Ctrl+C sair", len(m.history)),
- )
-
- return lipgloss.JoinVertical(lipgloss.Left, header, chat, inputArea, status)
-}
-
-// ─── Main ─────────────────────────────────────────────────────────────────────
-
-func main() {
- contacts := loadAbook()
- chats := getChats(contacts)
-
- var chosen contact
- var ok bool
-
- if len(chats) > 0 {
- chosen, ok = pickChatFzf(chats)
- } else {
- chosen, ok = pickContactFzf(contacts)
- }
-
- if !ok {
return
}
+ chosen := Contact{Name: parts[0], Mobile: parts[1]}
+ phone := cleanNumber(chosen.Mobile)
- exePath, _ := os.Executable()
- realPath, _ := filepath.EvalSymlinks(exePath)
- dbPath := filepath.Join(filepath.Dir(realPath), "whatsapp.db")
- dbURI := fmt.Sprintf("file:%s?_foreign_keys=on", dbPath)
-
- container, err := sqlstore.New(
- context.Background(), "sqlite3", dbURI,
- waLog.Stdout("Database", "ERROR", true),
- )
+ jid, err := types.ParseJID(phone + "@s.whatsapp.net")
if err != nil {
- fmt.Fprintln(os.Stderr, "erro ao abrir db:", err)
+ fmt.Fprintln(os.Stderr, "numero invalido:", phone)
os.Exit(1)
}
- device, _ := container.GetFirstDevice(context.Background())
- client := whatsmeow.NewClient(device, waLog.Stdout("Client", "ERROR", true))
-
- m := newModel(client, chosen)
- p := tea.NewProgram(m, tea.WithAltScreen())
-
- phoneClean := cleanNumber(chosen.mobile)
-
- client.AddEventHandler(func(evt interface{}) {
- v, ok := evt.(*events.Message)
- if !ok {
- return
- }
-
- txt := v.Message.GetConversation()
- if txt == "" {
- txt = v.Message.GetExtendedTextMessage().GetText()
- }
- if txt == "" {
- return
- }
-
- senderClean := cleanNumber(v.Info.Sender.User)
- mine := v.Info.IsFromMe
+ client := newClient()
+ connectClient(client)
+ defer client.Disconnect()
- relevant := mine
- if !mine && len(senderClean) >= 8 && len(phoneClean) >= 8 {
- relevant = senderClean[len(senderClean)-8:] == phoneClean[len(phoneClean)-8:]
+ fmt.Printf("para: %s (%s)\n", chosen.Name, chosen.Mobile)
+ scanner := bufio.NewScanner(os.Stdin)
+ for {
+ fmt.Print("> ")
+ if !scanner.Scan() {
+ break
}
- if !relevant {
- return
- }
-
- senderName := chosen.name
- if mine {
- senderName = "eu"
+ text := strings.TrimSpace(scanner.Text())
+ if text == "" {
+ break
}
-
- p.Send(incomingMsg{
- sender: senderName,
- content: txt,
- isMe: mine,
- ts: v.Info.Timestamp,
+ client.SendMessage(context.Background(), jid, &waE2E.Message{
+ Conversation: proto.String(text),
})
- })
-
- if client.Store.ID == nil {
- qrChan, _ := client.GetQRChannel(context.Background())
- client.Connect()
- for evt := range qrChan {
- if evt.Event == "code" {
- qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
- }
- }
- } else {
- client.Connect()
}
+}
- if _, err := p.Run(); err != nil {
+func main() {
+ if len(os.Args) < 2 {
+ fmt.Println("uso: zap <daemon|send>")
+ os.Exit(1)
+ }
+ switch os.Args[1] {
+ case "daemon":
+ runDaemon()
+ case "send":
+ runSend()
+ default:
+ fmt.Println("uso: zap <daemon|send>")
os.Exit(1)
}
- client.Disconnect()
}