func (wac *Conn) connect() (err error) {
	if wac.connected {
		return ErrAlreadyConnected
	}
	wac.connected = true
	defer func() {  
		if err != nil {
			wac.connected = false
		}
	}()
	dialer := &websocket.Dialer{
		ReadBufferSize:   0,
		WriteBufferSize:  0,
		HandshakeTimeout: wac.msgTimeout,
		Proxy:            wac.Proxy,
	}
	headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
	wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
	if err != nil {
		return errors.Wrap(err, "couldn't dial whatsapp web websocket")
	}
	wsConn.SetCloseHandler(func(code int, text string) error {
		message := websocket.FormatCloseMessage(code, "")
		err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
		_, _ = wac.Disconnect()
		wac.handle(&ErrConnectionClosed{Code: code, Text: text})
		return err
	})
	wac.ws = &websocketWrapper{
		conn:  wsConn,
		close: make(chan struct{}),
	}
	wac.listener = &listenerWrapper{
		m: make(map[string]chan string),
	}
	wac.wg = &sync.WaitGroup{}
	wac.wg.Add(2)
	go wac.readPump()
	go wac.keepAlive(20000, 60000)
	wac.loggedIn = false
	return nil
}
connect should be guarded with wsWriteMutex
package whatsapp
import (
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"time"
	"github.com/gorilla/websocket"
	"github.com/pkg/errors"
)
type metric byte
const (
	debugLog metric = iota + 1
	queryResume
	queryReceipt
	queryMedia
	queryChat
	queryContacts
	queryMessages
	presence
	presenceSubscribe
	group
	read
	chat
	received
	pic
	status
	message
	queryActions
	block
	queryGroup
	queryPreview
	queryEmoji
	queryMessageInfo
	spam
	querySearch
	queryIdentity
	queryUrl
	profile
	contact
	queryVcard
	queryStatus
	queryStatusUpdate
	privacyStatus
	queryLiveLocations
	liveLocation
	queryVname
	queryLabels
	call
	queryCall
	queryQuickReplies
)
type flag byte
const (
	ignore flag = 1 << (7 - iota)
	ackRequest
	available
	notAvailable
	expires
	skipOffline
)
type Conn struct {
	ws       *websocketWrapper
	listener *listenerWrapper
	connected bool
	loggedIn  bool
	wg        *sync.WaitGroup
	session        *Session
	sessionLock    uint32
	handler        []Handler
	msgCount       int
	msgTimeout     time.Duration
	Info           *Info
	Store          *Store
	ServerLastSeen time.Time
	timeTag string  
	longClientName  string
	shortClientName string
	clientVersion   string
	loginSessionLock sync.RWMutex
	Proxy            func(*http.Request) (*url.URL, error)
	writerLock sync.RWMutex
}
type websocketWrapper struct {
	sync.Mutex
	conn  *websocket.Conn
	close chan struct{}
}
type listenerWrapper struct {
	sync.RWMutex
	m map[string]chan string
}
func NewConn(timeout time.Duration) (*Conn, error) {
	return NewConnWithOptions(&Options{
		Timeout: timeout,
	})
}
func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL, error)) (*Conn, error) {
	return NewConnWithOptions(&Options{
		Timeout: timeout,
		Proxy: proxy,
	})
}
type Options struct {
	Proxy            func(*http.Request) (*url.URL, error)
	Timeout          time.Duration
	Handler          []Handler
	ShortClientName  string
	LongClientName   string
	ClientVersion    string
	Store            *Store
}
func NewConnWithOptions(opt *Options) (*Conn, error) {
	if opt == nil {
		return nil, ErrOptionsNotProvided
	}
	wac := &Conn{
		handler:    make([]Handler, 0),
		msgCount:   0,
		msgTimeout: opt.Timeout,
		Store:      newStore(),
		longClientName:  "github.com/Rhymen/go-whatsapp",
		shortClientName: "go-whatsapp",
		clientVersion:   "0.1.0",
	}
	if opt.Handler != nil {
		wac.handler = opt.Handler
	}
	if opt.Store != nil {
		wac.Store = opt.Store
	}
	if opt.Proxy != nil {
		wac.Proxy = opt.Proxy
	}
	if len(opt.ShortClientName) != 0 {
		wac.shortClientName = opt.ShortClientName
	}
	if len(opt.LongClientName) != 0 {
		wac.longClientName = opt.LongClientName
	}
	if len(opt.ClientVersion) != 0 {
		wac.clientVersion = opt.ClientVersion
	}
	return wac, wac.connect()
}