gowhatsapp-multi-devicewhatsapp-apiwhatsapprestgolangwhatsapp-web-multi-devicewhatsapp-api-gorest-apigolang-whatsapp-apigolang-whatsappbot
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.7 KiB
210 lines
5.7 KiB
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/config"
|
|
"go.mau.fi/whatsmeow"
|
|
"go.mau.fi/whatsmeow/appstate"
|
|
waProto "go.mau.fi/whatsmeow/binary/proto"
|
|
"go.mau.fi/whatsmeow/store"
|
|
"go.mau.fi/whatsmeow/store/sqlstore"
|
|
"go.mau.fi/whatsmeow/types"
|
|
"go.mau.fi/whatsmeow/types/events"
|
|
waLog "go.mau.fi/whatsmeow/util/log"
|
|
"google.golang.org/protobuf/proto"
|
|
"mime"
|
|
"os"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
cli *whatsmeow.Client
|
|
log waLog.Logger
|
|
historySyncID int32
|
|
startupTime = time.Now().Unix()
|
|
)
|
|
|
|
const logLevel = "DEBUG"
|
|
|
|
func GetPlatformName(deviceID int) string {
|
|
switch deviceID {
|
|
case 2:
|
|
return "UNKNOWN"
|
|
case 3:
|
|
return "CHROME"
|
|
case 4:
|
|
return "FIREFOX"
|
|
case 5:
|
|
return "IE"
|
|
case 6:
|
|
return "OPERA"
|
|
case 7:
|
|
return "SAFARI"
|
|
case 8:
|
|
return "EDGE"
|
|
case 9:
|
|
return "DESKTOP"
|
|
case 10:
|
|
return "IPAD"
|
|
case 11:
|
|
return "ANDROID_TABLET"
|
|
case 12:
|
|
return "OHANA"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
func ParseJID(arg string) (types.JID, bool) {
|
|
if arg[0] == '+' {
|
|
arg = arg[1:]
|
|
}
|
|
if !strings.ContainsRune(arg, '@') {
|
|
return types.NewJID(arg, types.DefaultUserServer), true
|
|
} else {
|
|
recipient, err := types.ParseJID(arg)
|
|
if err != nil {
|
|
_ = fmt.Errorf("Invalid JID %s: %v", arg, err)
|
|
return recipient, false
|
|
} else if recipient.User == "" {
|
|
_ = fmt.Errorf("Invalid JID %s: no server specified", arg)
|
|
return recipient, false
|
|
}
|
|
return recipient, true
|
|
}
|
|
}
|
|
|
|
func InitWaDB() *sqlstore.Container {
|
|
// Running Whatsapp
|
|
log = waLog.Stdout("Main", logLevel, true)
|
|
dbLog := waLog.Stdout("Database", logLevel, true)
|
|
storeContainer, err := sqlstore.New("sqlite3", fmt.Sprintf("file:%s?_foreign_keys=off", config.DBName), dbLog)
|
|
if err != nil {
|
|
log.Errorf("Failed to connect to database: %v", err)
|
|
panic(err)
|
|
|
|
}
|
|
return storeContainer
|
|
}
|
|
|
|
func InitWaCLI(storeContainer *sqlstore.Container) *whatsmeow.Client {
|
|
device, err := storeContainer.GetFirstDevice()
|
|
if err != nil {
|
|
log.Errorf("Failed to get device: %v", err)
|
|
panic(err)
|
|
}
|
|
|
|
store.CompanionProps.PlatformType = waProto.CompanionProps_UNKNOWN.Enum()
|
|
store.CompanionProps.Os = proto.String("AldinoKemal")
|
|
cli = whatsmeow.NewClient(device, waLog.Stdout("Client", logLevel, true))
|
|
cli.AddEventHandler(handler)
|
|
|
|
return cli
|
|
}
|
|
|
|
func MustLogin(waCli *whatsmeow.Client) {
|
|
if !waCli.IsConnected() {
|
|
panic(AuthError{Message: "you are not connect to whatsapp server, please reconnect"})
|
|
} else if !waCli.IsLoggedIn() {
|
|
panic(AuthError{Message: "you are not login"})
|
|
}
|
|
}
|
|
|
|
func handler(rawEvt interface{}) {
|
|
switch evt := rawEvt.(type) {
|
|
case *events.AppStateSyncComplete:
|
|
if len(cli.Store.PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock {
|
|
err := cli.SendPresence(types.PresenceAvailable)
|
|
if err != nil {
|
|
log.Warnf("Failed to send available presence: %v", err)
|
|
} else {
|
|
log.Infof("Marked self as available")
|
|
}
|
|
}
|
|
case *events.Connected, *events.PushNameSetting:
|
|
if len(cli.Store.PushName) == 0 {
|
|
return
|
|
}
|
|
// Send presence available when connecting and when the pushname is changed.
|
|
// This makes sure that outgoing messages always have the right pushname.
|
|
err := cli.SendPresence(types.PresenceAvailable)
|
|
if err != nil {
|
|
log.Warnf("Failed to send available presence: %v", err)
|
|
} else {
|
|
log.Infof("Marked self as available")
|
|
}
|
|
case *events.StreamReplaced:
|
|
os.Exit(0)
|
|
case *events.Message:
|
|
metaParts := []string{fmt.Sprintf("pushname: %s", evt.Info.PushName), fmt.Sprintf("timestamp: %s", evt.Info.Timestamp)}
|
|
if evt.Info.Type != "" {
|
|
metaParts = append(metaParts, fmt.Sprintf("type: %s", evt.Info.Type))
|
|
}
|
|
if evt.Info.Category != "" {
|
|
metaParts = append(metaParts, fmt.Sprintf("category: %s", evt.Info.Category))
|
|
}
|
|
if evt.IsViewOnce {
|
|
metaParts = append(metaParts, "view once")
|
|
}
|
|
if evt.IsViewOnce {
|
|
metaParts = append(metaParts, "ephemeral")
|
|
}
|
|
|
|
log.Infof("Received message %s from %s (%s): %+v", evt.Info.ID, evt.Info.SourceString(), strings.Join(metaParts, ", "), evt.Message)
|
|
|
|
img := evt.Message.GetImageMessage()
|
|
if img != nil {
|
|
data, err := cli.Download(img)
|
|
if err != nil {
|
|
log.Errorf("Failed to download image: %v", err)
|
|
return
|
|
}
|
|
exts, _ := mime.ExtensionsByType(img.GetMimetype())
|
|
path := fmt.Sprintf("%s%s", evt.Info.ID, exts[0])
|
|
err = os.WriteFile(path, data, 0600)
|
|
if err != nil {
|
|
log.Errorf("Failed to save image: %v", err)
|
|
return
|
|
}
|
|
log.Infof("Saved image in message to %s", path)
|
|
}
|
|
case *events.Receipt:
|
|
if evt.Type == events.ReceiptTypeRead || evt.Type == events.ReceiptTypeReadSelf {
|
|
log.Infof("%v was read by %s at %s", evt.MessageIDs, evt.SourceString(), evt.Timestamp)
|
|
} else if evt.Type == events.ReceiptTypeDelivered {
|
|
log.Infof("%s was delivered to %s at %s", evt.MessageIDs[0], evt.SourceString(), evt.Timestamp)
|
|
}
|
|
case *events.Presence:
|
|
if evt.Unavailable {
|
|
if evt.LastSeen.IsZero() {
|
|
log.Infof("%s is now offline", evt.From)
|
|
} else {
|
|
log.Infof("%s is now offline (last seen: %s)", evt.From, evt.LastSeen)
|
|
}
|
|
} else {
|
|
log.Infof("%s is now online", evt.From)
|
|
}
|
|
case *events.HistorySync:
|
|
id := atomic.AddInt32(&historySyncID, 1)
|
|
fileName := fmt.Sprintf("history-%d-%d.json", startupTime, id)
|
|
file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0600)
|
|
if err != nil {
|
|
log.Errorf("Failed to open file to write history sync: %v", err)
|
|
return
|
|
}
|
|
enc := json.NewEncoder(file)
|
|
enc.SetIndent("", " ")
|
|
err = enc.Encode(evt.Data)
|
|
if err != nil {
|
|
log.Errorf("Failed to write history sync: %v", err)
|
|
return
|
|
}
|
|
log.Infof("Wrote history sync to %s", fileName)
|
|
_ = file.Close()
|
|
case *events.AppState:
|
|
log.Debugf("App state event: %+v / %+v", evt.Index, evt.SyncActionValue)
|
|
}
|
|
}
|