WhatsApp¶
The WhatsApp provider uses whatsmeow to connect to WhatsApp Web.
Installation¶
Configuration¶
import "github.com/plexusone/omnichat/providers/whatsapp"
p, err := whatsapp.New(whatsapp.Config{
DBPath: "whatsapp.db",
Logger: slog.Default(),
QRCallback: func(qr string) {
// Display QR code for scanning
qrterminal.GenerateHalfBlock(qr, qrterminal.L, os.Stdout)
},
})
Config Options¶
| Field | Type | Required | Description |
|---|---|---|---|
DBPath |
string |
Yes | SQLite database path for session |
Logger |
*slog.Logger |
No | Logger instance |
QRCallback |
func(string) |
Yes | Called when QR code is available |
Authentication¶
WhatsApp requires QR code authentication:
- Run your application
- Scan the QR code with WhatsApp mobile app
- Session is stored in the database for future connections
import "github.com/mdp/qrterminal/v3"
p, err := whatsapp.New(whatsapp.Config{
DBPath: "whatsapp.db",
Logger: logger,
QRCallback: func(qr string) {
fmt.Println("Scan this QR code with WhatsApp:")
qrterminal.GenerateHalfBlock(qr, qrterminal.L, os.Stdout)
},
})
Usage¶
Basic Setup¶
package main
import (
"context"
"fmt"
"log/slog"
"os"
"github.com/mdp/qrterminal/v3"
"github.com/plexusone/omnichat/provider"
"github.com/plexusone/omnichat/providers/whatsapp"
)
func main() {
logger := slog.Default()
p, err := whatsapp.New(whatsapp.Config{
DBPath: "whatsapp.db",
Logger: logger,
QRCallback: func(qr string) {
fmt.Println("Scan QR code:")
qrterminal.GenerateHalfBlock(qr, qrterminal.L, os.Stdout)
},
})
if err != nil {
panic(err)
}
router := provider.NewRouter(logger)
router.Register(p)
router.OnMessage(provider.All(), func(ctx context.Context, msg provider.IncomingMessage) error {
return router.Send(ctx, "whatsapp", msg.ChatID, provider.OutgoingMessage{
Content: "Hello from WhatsApp!",
})
})
ctx := context.Background()
router.ConnectAll(ctx)
defer router.DisconnectAll(ctx)
select {}
}
Sending Media¶
// Send an image
router.Send(ctx, "whatsapp", chatID, provider.OutgoingMessage{
Content: "Check this out!",
Media: []provider.Media{{
Type: provider.MediaTypeImage,
Data: imageBytes,
MimeType: "image/jpeg",
}},
})
// Send a document
router.Send(ctx, "whatsapp", chatID, provider.OutgoingMessage{
Media: []provider.Media{{
Type: provider.MediaTypeDocument,
Data: pdfBytes,
Filename: "document.pdf",
MimeType: "application/pdf",
}},
})
Voice Messages¶
WhatsApp supports voice notes (PTT - Push to Talk):
// Receive voice messages
router.OnMessage(provider.All(), func(ctx context.Context, msg provider.IncomingMessage) error {
for _, media := range msg.Media {
if media.Type == provider.MediaTypeVoice {
// media.Data contains audio bytes
// media.MimeType is typically "audio/ogg; codecs=opus"
transcription := transcribe(media.Data)
// Process transcription...
}
}
return nil
})
// Send voice messages
router.Send(ctx, "whatsapp", chatID, provider.OutgoingMessage{
Media: []provider.Media{{
Type: provider.MediaTypeVoice,
Data: audioBytes,
MimeType: "audio/ogg; codecs=opus",
}},
})
Message Mapping¶
| OmniChat | |
|---|---|
| JID | ChatID |
| Message ID | ID |
| Sender JID | SenderID |
| Push Name | SenderName |
| Text/Caption | Content |
| Quoted Message | ReplyTo |
| Image/Document/Audio | Media |
| Chat type | IsDM (true for individual chats) |
Chat ID Format¶
WhatsApp JIDs (Jabber IDs) follow this format:
- Individual:
1234567890@s.whatsapp.net - Group:
123456789-1234567890@g.us
// Send to individual
router.Send(ctx, "whatsapp", "1234567890@s.whatsapp.net", msg)
// Send to group
router.Send(ctx, "whatsapp", "123456789-1234567890@g.us", msg)
Session Management¶
The SQLite database stores:
- Authentication keys
- Message history (optional)
- Contact information
// Different database per account
p1, _ := whatsapp.New(whatsapp.Config{
DBPath: "account1.db",
// ...
})
p2, _ := whatsapp.New(whatsapp.Config{
DBPath: "account2.db",
// ...
})
Group Features¶
router.OnMessage(provider.GroupOnly(), func(ctx context.Context, msg provider.IncomingMessage) error {
// msg.ChatID is the group JID
// msg.SenderID is the member who sent the message
return nil
})
Environment Variables¶
No tokens needed - authentication is via QR code.
Troubleshooting¶
QR code not appearing¶
- Ensure
QRCallbackis set in config - Check terminal supports the QR output format
- Try
qrterminal.Generate()instead ofGenerateHalfBlock()
Session expired¶
- Delete the database file
- Re-scan QR code
- WhatsApp Web sessions expire after ~14 days of inactivity
Messages not sending¶
- Verify chat ID format (must include
@s.whatsapp.netor@g.us) - Check you're connected (
Connect()completed successfully) - Verify the recipient hasn't blocked you
Media download fails¶
- Media URLs expire after some time
- Download media immediately when received
- Store locally if needed for later processing
Limitations¶
- One phone number per connection
- WhatsApp may restrict automated messaging
- Media size limits apply
- No official API - uses reverse-engineered protocol