-
-
Notifications
You must be signed in to change notification settings - Fork 271
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: null webhook message when edit (#221)
* fix: update deps & bug fix fix(go.mod): update module dependencies to latest versions to ensure compatibility and security fix(whatsapp.go): enhance event message handling by adding quoted message field and streamline message building and media extraction functions * fix: make webhook call async and add retry mechanism fix(init.go): wrap forwardToWebhook call in a goroutine to make it async feat(webhook.go): add timestamp field to the webhook payload fix(webhook.go): implement retry logic for submitWebhook with exponential backoff * feat: update version * feat: add max download size validation for media files - settings.go: add WhatsappSettingMaxDownloadSize for file size validation (500MB) - utils.go: check file size before writing media to disk, returning an error if it exceeds the max limit * fix: improve error handling and logging fix(utils.go): replace panic with logrus error logging and return false fix(webhook.go): add logrus error logging when media download fails fix(webhook.go): add logrus info and warning logging for webhook submission attempts * fix: add omitempty tags to JSON fields to avoid empty values refactor(webhook.go): restructure createPayload to check and add fields conditionally, improving clarity and preventing unnecessary fields refactor(webhook.go): handle media extraction only when media is present, improving readability and maintainability * fix: add nil check for device in whatsapp init fix(init.go): add check for nil device and log error before panic Ensure proper error handling when no device is found
- Loading branch information
1 parent
f86453b
commit c2c94d5
Showing
7 changed files
with
661 additions
and
518 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package whatsapp | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"strings" | ||
"sync/atomic" | ||
"time" | ||
|
||
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" | ||
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/websocket" | ||
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" | ||
"github.com/sirupsen/logrus" | ||
"go.mau.fi/whatsmeow" | ||
"go.mau.fi/whatsmeow/appstate" | ||
"go.mau.fi/whatsmeow/proto/waE2E" | ||
"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" | ||
) | ||
|
||
var ( | ||
cli *whatsmeow.Client | ||
log waLog.Logger | ||
historySyncID int32 | ||
startupTime = time.Now().Unix() | ||
) | ||
|
||
type ExtractedMedia struct { | ||
MediaPath string `json:"media_path"` | ||
MimeType string `json:"mime_type"` | ||
Caption string `json:"caption"` | ||
} | ||
|
||
type evtReaction struct { | ||
ID string `json:"id,omitempty"` | ||
Message string `json:"message,omitempty"` | ||
} | ||
|
||
type evtMessage struct { | ||
ID string `json:"id,omitempty"` | ||
Text string `json:"text,omitempty"` | ||
RepliedId string `json:"replied_id,omitempty"` | ||
QuotedMessage string `json:"quoted_message,omitempty"` | ||
} | ||
|
||
func InitWaDB() *sqlstore.Container { | ||
// Running Whatsapp | ||
log = waLog.Stdout("Main", config.WhatsappLogLevel, true) | ||
dbLog := waLog.Stdout("Database", config.WhatsappLogLevel, true) | ||
storeContainer, err := sqlstore.New("sqlite3", fmt.Sprintf("file:%s/%s?_foreign_keys=off", config.PathStorages, config.DBName), dbLog) | ||
if err != nil { | ||
log.Errorf("Failed to connect to database: %v", err) | ||
panic(pkgError.InternalServerError(fmt.Sprintf("Failed to connect to database: %v", 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) | ||
} | ||
|
||
if device == nil { | ||
log.Errorf("No device found") | ||
panic("No device found") | ||
} | ||
|
||
osName := fmt.Sprintf("%s %s", config.AppOs, config.AppVersion) | ||
store.DeviceProps.PlatformType = &config.AppPlatform | ||
store.DeviceProps.Os = &osName | ||
cli = whatsmeow.NewClient(device, waLog.Stdout("Client", config.WhatsappLogLevel, true)) | ||
cli.EnableAutoReconnect = true | ||
cli.AutoTrustIdentity = true | ||
cli.AddEventHandler(handler) | ||
|
||
return cli | ||
} | ||
|
||
func handler(rawEvt interface{}) { | ||
switch evt := rawEvt.(type) { | ||
case *events.DeleteForMe: | ||
log.Infof("Deleted message %s for %s", evt.MessageID, evt.SenderJID.String()) | ||
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.PairSuccess: | ||
websocket.Broadcast <- websocket.BroadcastMessage{ | ||
Code: "LOGIN_SUCCESS", | ||
Message: fmt.Sprintf("Successfully pair with %s", evt.ID.String()), | ||
} | ||
case *events.LoggedOut: | ||
websocket.Broadcast <- websocket.BroadcastMessage{ | ||
Code: "LIST_DEVICES", | ||
Result: nil, | ||
} | ||
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") | ||
} | ||
|
||
log.Infof("Received message %s from %s (%s): %+v", evt.Info.ID, evt.Info.SourceString(), strings.Join(metaParts, ", "), evt.Message) | ||
|
||
if img := evt.Message.GetImageMessage(); img != nil { | ||
if path, err := ExtractMedia(config.PathStorages, img); err != nil { | ||
log.Errorf("Failed to download image: %v", err) | ||
} else { | ||
log.Infof("Image downloaded to %s", path) | ||
} | ||
} | ||
|
||
if config.WhatsappAutoReplyMessage != "" && | ||
!isGroupJid(evt.Info.Chat.String()) && | ||
!strings.Contains(evt.Info.SourceString(), "broadcast") { | ||
_, _ = cli.SendMessage(context.Background(), evt.Info.Sender, &waE2E.Message{Conversation: proto.String(config.WhatsappAutoReplyMessage)}) | ||
} | ||
|
||
if config.WhatsappWebhook != "" && | ||
!strings.Contains(evt.Info.SourceString(), "broadcast") && | ||
!isFromMySelf(evt.Info.SourceString()) { | ||
go func(evt *events.Message) { | ||
if err := forwardToWebhook(evt); err != nil { | ||
logrus.Error("Failed forward to webhook: ", err) | ||
} | ||
}(evt) | ||
} | ||
case *events.Receipt: | ||
if evt.Type == types.ReceiptTypeRead || evt.Type == types.ReceiptTypeReadSelf { | ||
log.Infof("%v was read by %s at %s", evt.MessageIDs, evt.SourceString(), evt.Timestamp) | ||
} else if evt.Type == types.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("%s/history-%d-%s-%d-%s.json", config.PathStorages, startupTime, cli.Store.ID.String(), id, evt.Data.SyncType.String()) | ||
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) | ||
} | ||
} |
Oops, something went wrong.