Go-whatsapp: Receive and echo example

Created on 27 Jul 2018  路  8Comments  路  Source: Rhymen/go-whatsapp

I'm not go developer,
can you please create example echo-ing message received.
Receive and send the same message to sender.

something like
```golang
func (*waHandler) HandleTextMessage(message whatsapp.TextMessage) {
if strings.Contains(strings.ToLower(message.Text), "hello") {
msg := whatsapp.TextMessage{
Info: whatsapp.MessageInfo{
RemoteJid: message.Info.RemoteJid,
},
Text: message.Text
}
wac.Send(msg)
}
fmt.Printf("%v %v\n\t%v\n", message.Info.Timestamp, message.Info.RemoteJid, message.Text)
}
````

Most helpful comment

Hi, as @Rhymen already suggested. Here is what I did to call send message inside the handler.

Put this as the first line of code.
var wac, _ = whatsapp.NewConn(5 * time.Second)
Code for sending message in handler. (This is very dangerous code though, please manipulate it a little bit before using)

func (*waHandler) HandleTextMessage(message whatsapp.TextMessage) {
    msg := whatsapp.TextMessage{
        Info: whatsapp.MessageInfo{
            RemoteJid: message.Info.RemoteJid,
        },
        Text: "Message sent by github.com/Rhymen/go-whatsapp",
    }

    err = wac.Send(msg)
    if err != nil {
        fmt.Fprintf(os.Stderr, "error sending message: %v", err)
    }
    fmt.Printf("%v %v\n\t%v\n", message.Info.Timestamp, message.Info.RemoteJid, message.Text)
}

Do not forget to remove wac *whatsapp.Conn parameter from the login function, as the variable is globally declared now.

An echo example that replies only for new queries would be really cool. Look forward to it.
Regards,

All 8 comments

Hey lutpiero,

I think that I can see a two mistakes in your code. Please try to format it correctly next time as it is very hard to read like this.

  1. You transform the message to lower case, but compare with an upper case "Hello". The condition is always false.

  2. I can't really see it in the short snippet here, but is wac really in the correct scope? If you want to use it in the listener, you have to create the connection globally.

I will add an echo example in the next couple of days, so if you can't get it to work you can check mine.

Hi, as @Rhymen already suggested. Here is what I did to call send message inside the handler.

Put this as the first line of code.
var wac, _ = whatsapp.NewConn(5 * time.Second)
Code for sending message in handler. (This is very dangerous code though, please manipulate it a little bit before using)

func (*waHandler) HandleTextMessage(message whatsapp.TextMessage) {
    msg := whatsapp.TextMessage{
        Info: whatsapp.MessageInfo{
            RemoteJid: message.Info.RemoteJid,
        },
        Text: "Message sent by github.com/Rhymen/go-whatsapp",
    }

    err = wac.Send(msg)
    if err != nil {
        fmt.Fprintf(os.Stderr, "error sending message: %v", err)
    }
    fmt.Printf("%v %v\n\t%v\n", message.Info.Timestamp, message.Info.RemoteJid, message.Text)
}

Do not forget to remove wac *whatsapp.Conn parameter from the login function, as the variable is globally declared now.

An echo example that replies only for new queries would be really cool. Look forward to it.
Regards,

I've created an echo example, this example will echo the message if it contain @echo keyword.
But the problem is I often got error

panic: concurrent write to websocket connection

The code is like this:
````golang
package main

import (
"encoding/gob"
"fmt"
"os"
"strings"
"time"

"github.com/Baozisoftware/qrcode-terminal-go"
"github.com/Rhymen/go-whatsapp"

)

type waHandler struct {
wac *whatsapp.Conn
}

//HandleError needs to be implemented to be a valid WhatsApp handler
func (wh *waHandler) HandleError(err error) {
fmt.Fprintf(os.Stderr, "error occoured: %v", err)
}

//Optional to be implemented. Implement HandleXXXMessage for the types you need.
func (wh *waHandler) HandleTextMessage(message whatsapp.TextMessage) {
var err error
if strings.Contains(strings.ToLower(message.Text), "@echo") && !message.Info.FromMe {
msg := whatsapp.TextMessage{
Info: whatsapp.MessageInfo{
RemoteJid: message.Info.RemoteJid,
},
Text: message.Text,
}

    err = wh.wac.Send(msg)
    if err != nil {
        fmt.Fprintf(os.Stderr, "error sending message: %v", err)
    }       
}
fmt.Printf("%v %v\n\t%v\n", message.Info.Timestamp, message.Info.RemoteJid, message.Text)

}

//Example for media handling. Video, Audio, Document are also possible in the same way
func (wh *waHandler) HandleImageMessage(message whatsapp.ImageMessage) {
data, err := message.Download()
if err != nil {
return
}
filename := fmt.Sprintf("%v/%v.%v", os.TempDir(), message.Info.Id, strings.Split(message.Type, "/")[1])
file, err := os.Create(filename)
defer file.Close()
if err != nil {
return
}
_, err = file.Write(data)
if err != nil {
return
}
fmt.Printf("%v %v\n\timage reveived, saved at:%v\n", message.Info.Timestamp, message.Info.RemoteJid, filename)
}

func main() {
//create new WhatsApp connection
wac, err := whatsapp.NewConn(5 * time.Second)
if err != nil {
fmt.Fprintf(os.Stderr, "error creating connection: %v\n", err)
return
}

//Add handler
wac.AddHandler(&waHandler{wac})

err = login(wac)
if err != nil {
    fmt.Fprintf(os.Stderr, "error logging in: %v\n", err)
    return
}

<-time.After(500 * time.Minute)

}

func login(wac *whatsapp.Conn) error {
//load saved session
session, err := readSession()
if err == nil {
//restore session
session, err = wac.RestoreSession(session)
if err != nil {
return fmt.Errorf("restoring failed: %v\n", err)
}
} else {
//no saved session -> regular login
qr := make(chan string)
go func() {
terminal := qrcodeTerminal.New()
terminal.Get(<-qr).Print()
}()
session, err = wac.Login(qr)
if err != nil {
return fmt.Errorf("error during login: %v\n", err)
}
}

//save session
err = writeSession(session)
if err != nil {
    return fmt.Errorf("error saving session: %v\n", err)
}
return nil

}

func readSession() (whatsapp.Session, error) {
session := whatsapp.Session{}
file, err := os.Open(os.TempDir() + "/whatsappSession.gob")
if err != nil {
return session, err
}
defer file.Close()
decoder := gob.NewDecoder(file)
err = decoder.Decode(&session)
if err != nil {
return session, err
}
return session, nil
}

func writeSession(session whatsapp.Session) error {
file, err := os.Create(os.TempDir() + "/whatsappSession.gob")
if err != nil {
return err
}
defer file.Close()
encoder := gob.NewEncoder(file)
err = encoder.Encode(session)
if err != nil {
return err
}
return nil
}
````

This happens propably due to the flood of messages received after connecting to whatsapp. We are currently creating a goroutine for every incoming message to not block the message queue. Maybe we should consider to remove this. For an easy and simple workaround you can try to add the handler a few seconds after logging into whatsapp.

I think we have to implement a queue for message sending so that no two messages get sent at the same time.

I think we have to implement a queue for message sending so that no two messages get sent at the same time.

This. It's not allowed to read / write from mutiple goroutines from / to the socket. We have to implement a "send pump", much like the read pump we already got. Look at the comments above the readPump and writePump in this example.

I implemented the writePump, messages can now be send without worrying about concurrenry.

I added a slightly modified version of the echo example that @lutpiero provided to the examples folder.

Great I've tested the echo no more
panic: concurrent write to websocket connection

I'm closing the issue, thanks @Rhymen

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jhow2892 picture jhow2892  路  3Comments

bamjohn2222 picture bamjohn2222  路  5Comments

Valdenirmezadri picture Valdenirmezadri  路  7Comments

ribeiroferreiralucas picture ribeiroferreiralucas  路  8Comments

xnodcorp picture xnodcorp  路  3Comments