Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# waSocket
[![Go Reference](https://pkg.go.dev/badge/github.com/techwiz37/waSocket.svg)](https://pkg.go.dev/github.com/techwiz37/waSocket)
[![Go Reference](https://pkg.go.dev/badge/github.com/techwiz37/waSocket/.svg)](https://pkg.go.dev/github.com/techwiz37/waSocket/)
> [!NOTE]
> This is just a fork of whatsmeow

waSocket is a Go library for the WhatsApp web multidevice API.
This is a fork that use a workaround for slow groupm essage, and some unmerged PR
This is a fork that use a workaround for slow group message, and some unmerged PR

## Discussion
<!-- Matrix room: [#waSocket:maunium.net](https://matrix.to/#/#waSocket:maunium.net) -->
Whatsapp Group: https://chat.whatsapp.com/BDI3NMjO7vW7RlOqgdxtmw
Expand All @@ -14,8 +17,8 @@ discussions.
<!-- [WhatsApp protocol Q&A]: https://github.com/tulir/waSocket/discussions/categories/whatsapp-protocol-q-a -->

## Usage
The [godoc](https://pkg.go.dev/github.com/techwiz37/waSocket) includes docs for all methods and event types.
There's also a [simple example](https://pkg.go.dev/github.com/techwiz37/waSocket#example-package) at the top.
The [godoc](https://pkg.go.dev/github.com/techwiz37/waSocket/) includes docs for all methods and event types.
There's also a [simple example](https://pkg.go.dev/github.com/techwiz37/waSocket/#example-package) at the top.

## Features
Most core features are already present:
Expand Down
54 changes: 28 additions & 26 deletions appstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ import (

// FetchAppState fetches updates to the given type of app state. If fullSync is true, the current
// cached state will be removed and all app state patches will be re-fetched from the server.
func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) error {
func (cli *Client) FetchAppState(ctx context.Context, name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) error {
if cli == nil {
return ErrClientIsNil
}
cli.appStateSyncLock.Lock()
defer cli.appStateSyncLock.Unlock()
if fullSync {
err := cli.Store.AppState.DeleteAppStateVersion(string(name))
err := cli.Store.AppState.DeleteAppStateVersion(ctx, string(name))
if err != nil {
return fmt.Errorf("failed to reset app state %s version: %w", name, err)
}
}
version, hash, err := cli.Store.AppState.GetAppStateVersion(string(name))
version, hash, err := cli.Store.AppState.GetAppStateVersion(ctx, string(name))
if err != nil {
return fmt.Errorf("failed to get app state %s version: %w", name, err)
}
Expand All @@ -51,17 +51,17 @@ func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotS
hasMore := true
wantSnapshot := fullSync
for hasMore {
patches, err := cli.fetchAppStatePatches(name, state.Version, wantSnapshot)
patches, err := cli.fetchAppStatePatches(ctx, name, state.Version, wantSnapshot)
wantSnapshot = false
if err != nil {
return fmt.Errorf("failed to fetch app state %s patches: %w", name, err)
}
hasMore = patches.HasMorePatches

mutations, newState, err := cli.appStateProc.DecodePatches(patches, state, true)
mutations, newState, err := cli.appStateProc.DecodePatches(ctx, patches, state, true)
if err != nil {
if errors.Is(err, appstate.ErrKeyNotFound) {
go cli.requestMissingAppStateKeys(context.TODO(), patches)
go cli.requestMissingAppStateKeys(context.WithoutCancel(ctx), patches)
}
return fmt.Errorf("failed to decode app state %s patches: %w", name, err)
}
Expand All @@ -71,14 +71,14 @@ func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotS
var contacts []store.ContactEntry
mutations, contacts = cli.filterContacts(mutations)
cli.Log.Debugf("Mass inserting app state snapshot with %d contacts into the store", len(contacts))
err = cli.Store.Contacts.PutAllContactNames(contacts)
err = cli.Store.Contacts.PutAllContactNames(ctx, contacts)
if err != nil {
// This is a fairly serious failure, so just abort the whole thing
return fmt.Errorf("failed to update contact store with data from snapshot: %v", err)
}
}
for _, mutation := range mutations {
cli.dispatchAppState(mutation, fullSync, cli.EmitAppStateEventsOnFullSync)
cli.dispatchAppState(ctx, mutation, fullSync, cli.EmitAppStateEventsOnFullSync)
}
}
if fullSync {
Expand Down Expand Up @@ -109,10 +109,10 @@ func (cli *Client) filterContacts(mutations []appstate.Mutation) ([]appstate.Mut
return filteredMutations, contacts
}

func (cli *Client) dispatchAppState(mutation appstate.Mutation, fullSync bool, emitOnFullSync bool) {
func (cli *Client) dispatchAppState(ctx context.Context, mutation appstate.Mutation, fullSync bool, emitOnFullSync bool) {
dispatchEvts := !fullSync || emitOnFullSync

if (mutation.Action != nil && mutation.Action.ContactAction == nil) && mutation.Operation != waServerSync.SyncdMutation_SET {
if mutation.Operation != waServerSync.SyncdMutation_SET {
return
}

Expand All @@ -137,25 +137,25 @@ func (cli *Client) dispatchAppState(mutation appstate.Mutation, fullSync bool, e
mutedUntil = time.UnixMilli(act.GetMuteEndTimestamp())
}
if cli.Store.ChatSettings != nil {
storeUpdateError = cli.Store.ChatSettings.PutMutedUntil(jid, mutedUntil)
storeUpdateError = cli.Store.ChatSettings.PutMutedUntil(ctx, jid, mutedUntil)
}
case appstate.IndexPin:
act := mutation.Action.GetPinAction()
eventToDispatch = &events.Pin{JID: jid, Timestamp: ts, Action: act, FromFullSync: fullSync}
if cli.Store.ChatSettings != nil {
storeUpdateError = cli.Store.ChatSettings.PutPinned(jid, act.GetPinned())
storeUpdateError = cli.Store.ChatSettings.PutPinned(ctx, jid, act.GetPinned())
}
case appstate.IndexArchive:
act := mutation.Action.GetArchiveChatAction()
eventToDispatch = &events.Archive{JID: jid, Timestamp: ts, Action: act, FromFullSync: fullSync}
if cli.Store.ChatSettings != nil {
storeUpdateError = cli.Store.ChatSettings.PutArchived(jid, act.GetArchived())
storeUpdateError = cli.Store.ChatSettings.PutArchived(ctx, jid, act.GetArchived())
}
case appstate.IndexContact:
act := mutation.Action.GetContactAction()
eventToDispatch = &events.Contact{JID: jid, Timestamp: ts, Action: act, FromFullSync: fullSync}
if cli.Store.Contacts != nil {
storeUpdateError = cli.Store.Contacts.PutContactName(jid, act.GetFirstName(), act.GetFullName())
storeUpdateError = cli.Store.Contacts.PutContactName(ctx, jid, act.GetFirstName(), act.GetFullName())
}
case appstate.IndexClearChat:
act := mutation.Action.GetClearChatAction()
Expand Down Expand Up @@ -209,7 +209,7 @@ func (cli *Client) dispatchAppState(mutation appstate.Mutation, fullSync bool, e
FromFullSync: fullSync,
}
cli.Store.PushName = mutation.Action.GetPushNameSetting().GetName()
err := cli.Store.Save()
err := cli.Store.Save(ctx)
if err != nil {
cli.Log.Errorf("Failed to save device store after updating push name: %v", err)
}
Expand Down Expand Up @@ -270,11 +270,11 @@ func (cli *Client) dispatchAppState(mutation appstate.Mutation, fullSync bool, e
}
}

func (cli *Client) downloadExternalAppStateBlob(ref *waServerSync.ExternalBlobReference) ([]byte, error) {
return cli.Download(ref)
func (cli *Client) downloadExternalAppStateBlob(ctx context.Context, ref *waServerSync.ExternalBlobReference) ([]byte, error) {
return cli.Download(ctx, ref)
}

func (cli *Client) fetchAppStatePatches(name appstate.WAPatchName, fromVersion uint64, snapshot bool) (*appstate.PatchList, error) {
func (cli *Client) fetchAppStatePatches(ctx context.Context, name appstate.WAPatchName, fromVersion uint64, snapshot bool) (*appstate.PatchList, error) {
attrs := waBinary.Attrs{
"name": string(name),
"return_snapshot": snapshot,
Expand All @@ -283,6 +283,7 @@ func (cli *Client) fetchAppStatePatches(name appstate.WAPatchName, fromVersion u
attrs["version"] = fromVersion
}
resp, err := cli.sendIQ(infoQuery{
Context: ctx,
Namespace: "w:sync:app:state",
Type: "set",
To: types.ServerJID,
Expand All @@ -297,12 +298,12 @@ func (cli *Client) fetchAppStatePatches(name appstate.WAPatchName, fromVersion u
if err != nil {
return nil, err
}
return appstate.ParsePatchList(resp, cli.downloadExternalAppStateBlob)
return appstate.ParsePatchList(ctx, resp, cli.downloadExternalAppStateBlob)
}

func (cli *Client) requestMissingAppStateKeys(ctx context.Context, patches *appstate.PatchList) {
cli.appStateKeyRequestsLock.Lock()
rawKeyIDs := cli.appStateProc.GetMissingKeyIDs(patches)
rawKeyIDs := cli.appStateProc.GetMissingKeyIDs(ctx, patches)
filteredKeyIDs := make([][]byte, 0, len(rawKeyIDs))
now := time.Now()
for _, keyID := range rawKeyIDs {
Expand Down Expand Up @@ -348,17 +349,17 @@ func (cli *Client) requestAppStateKeys(ctx context.Context, rawKeyIDs [][]byte)
//
// You can use the Build methods in the appstate package to build the parameter for this method, e.g.
//
// cli.SendAppState(appstate.BuildMute(targetJID, true, 24 * time.Hour))
func (cli *Client) SendAppState(patch appstate.PatchInfo) error {
// cli.SendAppState(ctx, appstate.BuildMute(targetJID, true, 24 * time.Hour))
func (cli *Client) SendAppState(ctx context.Context, patch appstate.PatchInfo) error {
if cli == nil {
return ErrClientIsNil
}
version, hash, err := cli.Store.AppState.GetAppStateVersion(string(patch.Type))
version, hash, err := cli.Store.AppState.GetAppStateVersion(ctx, string(patch.Type))
if err != nil {
return err
}
// TODO create new key instead of reusing the primary client's keys
latestKeyID, err := cli.Store.AppStateKeys.GetLatestAppStateSyncKeyID()
latestKeyID, err := cli.Store.AppStateKeys.GetLatestAppStateSyncKeyID(ctx)
if err != nil {
return fmt.Errorf("failed to get latest app state key ID: %w", err)
} else if latestKeyID == nil {
Expand All @@ -367,12 +368,13 @@ func (cli *Client) SendAppState(patch appstate.PatchInfo) error {

state := appstate.HashState{Version: version, Hash: hash}

encodedPatch, err := cli.appStateProc.EncodePatch(latestKeyID, state, patch)
encodedPatch, err := cli.appStateProc.EncodePatch(ctx, latestKeyID, state, patch)
if err != nil {
return err
}

resp, err := cli.sendIQ(infoQuery{
Context: ctx,
Namespace: "w:sync:app:state",
Type: iqSet,
To: types.ServerJID,
Expand Down Expand Up @@ -403,5 +405,5 @@ func (cli *Client) SendAppState(patch appstate.PatchInfo) error {
return fmt.Errorf("%w: %s", ErrAppStateUpdate, respCollection.XMLString())
}

return cli.FetchAppState(patch.Type, false, false)
return cli.FetchAppState(ctx, patch.Type, false, false)
}
Loading
Loading