Skip to content

Pushing QR to client #57

@hajsf

Description

@hajsf

Trying to make login screen as WhatsApp, where the QR is pushed to the client for scanning, so I wrote the below server code:

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	_ "github.com/mattn/go-sqlite3"
	"github.com/skip2/go-qrcode"
	"google.golang.org/protobuf/proto"

	"go.mau.fi/whatsmeow"
	"go.mau.fi/whatsmeow/store"
	"go.mau.fi/whatsmeow/store/sqlstore"
	"go.mau.fi/whatsmeow/types/events"
	waLog "go.mau.fi/whatsmeow/util/log"
)

func eventHandler(evt interface{}) {
	switch v := evt.(type) {
	case *events.Message:
		fmt.Println("Received a message!", v.Message.GetConversation())
	}
}

func main() {
	passer := &DataPasser{logs: make(chan string)}

	http.HandleFunc("/", passer.handleHello)
	go http.ListenAndServe(":9999", nil)

	store.DeviceProps.Os = proto.String("WhatsApp GO")
	dbLog := waLog.Stdout("Database", "DEBUG", true)
	// Make sure you add appropriate DB connector imports, e.g. github.com/mattn/go-sqlite3 for SQLite
	container, err := sqlstore.New("sqlite3", "file:datastore.db?_foreign_keys=on", dbLog)
	if err != nil {
		panic(err)
	}
	// If you want multiple sessions, remember their JIDs and use .GetDevice(jid) or .GetAllDevices() instead.
	deviceStore, err := container.GetFirstDevice()
	if err != nil {
		panic(err)
	}
	clientLog := waLog.Stdout("Client", "DEBUG", true)
	client := whatsmeow.NewClient(deviceStore, clientLog)
	client.AddEventHandler(eventHandler)

	if client.Store.ID == nil {
		// No ID stored, new login
		qrChan, _ := client.GetQRChannel(context.Background())
		err = client.Connect()
		if err != nil {
			panic(err)
		}

		for evt := range qrChan {
			switch evt.Event {
			case "success":
				{
					passer.logs <- "success"
					fmt.Println("Login event: success")
				}
			case "timeout":
				{
					passer.logs <- "timeout"
					fmt.Println("Login event: timeout")
				}
			case "code":
				{
					passer.logs <- "new code"
					fmt.Println("new code recieved")
					img, err := qrcode.Encode(evt.Code, qrcode.Medium, 200) // evt.Code
					if err != nil {
						fmt.Println("error when write qrImage", err.Error())
					}
					passer.logs <- string(img)
				}
			}
		}
	} else {
		// Already logged in, just connect
		passer.logs <- "Already logged"
		fmt.Println("Already logged")
		err = client.Connect()
		if err != nil {
			panic(err)
		}
	}

	// Listen to Ctrl+C (you can also do something else that prevents the program from exiting)
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	<-c

	client.Disconnect()
}

With the below api:

package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"sync"
)

var (
	mux sync.Mutex
)

type Result struct {
	ResultType, Result string
}

type DataPasser struct {
	logs chan string
}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
	setupCORS(&w, r)
	w.Header().Set("Content-Type", "text/event-stream")
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Internal error", 500)
		return
	}
	flusher.Flush()
	done := r.Context().Done()
	defer fmt.Println("EXIT")
	for {
		select {
		case <-done:
			// the client disconnected
			return
		case m := <-p.logs:
			fmt.Println(m)
			if m == "new code" || m == "We are logging in" || m == "Already logged" || m == "timeout" || m == "success" {
				if _, err := fmt.Fprintf(w, "data: %s\n\n", m); err != nil {
					// Write to connection failed. Subsequent writes will probably fail.
					return
				}
				flusher.Flush()
			} else {
				mux.Lock()

				buffer := bytes.NewBuffer([]byte(m))

				/*	img, _, err := image.Decode(buffer)
					if err != nil {
						fmt.Println("err: ", err)
					}

					if err := jpeg.Encode(buffer, img, nil); err != nil {
						log.Println("unable to encode image.")
					} */

				w.Header().Set("Content-Type", "image/jpeg")
				w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes())))
				if _, err := w.Write(buffer.Bytes()); err != nil {
					log.Println("unable to write image.")
				}
				mux.Unlock()
				flusher.Flush()
			}
		}
	}
}

func setupCORS(w *http.ResponseWriter, req *http.Request) {
	(*w).Header().Set("Cache-Control", "no-cache")
	(*w).Header().Set("Connection", "keep-alive")
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
	(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

But stuck to read it in the client, I tried something as below, but got lost:

<html>
<head></head>
<body>
    note: <span id="content"></span><br>
    <img id="photo" style="display: block;-webkit-user-select: none;">
</body>
    <script>
      /*  (function(){
                document.getElementById("content").innerHTML='<object type="text/html" data="http://localhost:9999/" ></object>';
        })(); 
*/
        const myRequest = new Request('http://127.0.0.1:9999/', {
        method: 'GET',
        headers: new Headers(),
        type: "arraybuffer",
        mode: 'cors',
        cache: 'default',
        });  

        var source = new EventSource("http://127.0.0.1:9999/");
        source.onmessage = function (event) {
            console.log(event)
            var counter = event.data; // JSON.parse(event.data);
            document.getElementById("content").innerHTML = counter;
        }

    fetch(myRequest).then(response => {
        console.log(response)
        console.log(response.headers)
    const contentType = response.headers.get("content-type");
        if (contentType && contentType.indexOf("application/json") !== -1) {
            return response.json().then(data => {
            var obj = JSON.parse(str);
            console.log(obj)
            // Process your data as a JavaScript object
            });
        }  if (contentType && contentType.indexOf("image/jpeg") !== -1) {
            console.log("Image received")
            return response.blob().then(data => {
                var reader = new FileReader();
                reader.readAsDataURL(blob); 
                reader.onloadend = function() {
                    var imageUrl = reader.result;                
                    var img = document.querySelector("#photo");
                    img.src = imageUrl;
                }
            });
        } else if (contentType && contentType.indexOf("text/event-stream") !== -1) {
            return response.text().then(text => {
            console.log(text)
            var source = new EventSource("http://localhost:9999/");
            source.onmessage = function (event) {
                var response = event.data // JSON.parse(event.data);
                document.getElementById("content").innerHTML = counter;
            } 
            // Process your text as a String
            });
        } else if (contentType && contentType.indexOf("text/html") !== -1) {
            return response.text().then(text => {
            console.log(text)
            var source = new EventSource("http://localhost:9999/");
            source.onmessage = function (event) {
                var response = event.data // JSON.parse(event.data);
                document.getElementById("content").innerHTML = counter;
            } 
            // Process your text as a String
            });
        } 
    });

    </script>
</html>

If I run and see the displayed page, I see:

enter image description here

Any help?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions