diff --git a/commands.go b/commands.go index 58e0c94..c699e81 100644 --- a/commands.go +++ b/commands.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/skip2/go-qrcode" + "go.mau.fi/whatsmeow/appstate" "maunium.net/go/maulogger/v2" @@ -150,6 +151,8 @@ func (handler *CommandHandler) CommandMux(ce *CommandEvent) { handler.CommandUnsetRelay(ce) case "login-matrix": handler.CommandLoginMatrix(ce) + case "sync": + handler.CommandSync(ce) case "list": handler.CommandList(ce) case "open": @@ -705,6 +708,7 @@ func (handler *CommandHandler) CommandHelp(ce *CommandEvent) { cmdPrefix + cmdLogoutMatrixHelp, cmdPrefix + cmdToggleHelp, cmdPrefix + cmdListHelp, + cmdPrefix + cmdSyncHelp, cmdPrefix + cmdOpenHelp, cmdPrefix + cmdPMHelp, cmdPrefix + cmdInviteLinkHelp, @@ -987,6 +991,48 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) { ce.Reply("Created portal room with +%s and invited you to it.", puppet.JID.User) } +const cmdSyncHelp = `sync [--create-portals] - Synchronize data from WhatsApp.` + +func (handler *CommandHandler) CommandSync(ce *CommandEvent) { + if len(ce.Args) == 0 { + ce.Reply("**Usage:** `sync [--create-portals]`") + return + } + args := strings.ToLower(strings.Join(ce.Args, " ")) + contacts := strings.Contains(args, "contacts") + appState := strings.Contains(args, "appstate") + groups := strings.Contains(args, "groups") + createPortals := strings.Contains(args, "--create-portals") + + if appState { + for _, name := range appstate.AllPatchNames { + err := ce.User.Client.FetchAppState(name, true, false) + if err != nil { + ce.Reply("Error syncing app state %s", name) + } else if name == appstate.WAPatchCriticalUnblockLow { + ce.Reply("Synced app state %s, contact sync running in background", name) + } else { + ce.Reply("Synced app state %s", name) + } + } + } else if contacts { + err := ce.User.ResyncContacts() + if err != nil { + ce.Reply("Error resyncing contacts: %v", err) + } else { + ce.Reply("Resynced contacts") + } + } + if groups { + err := ce.User.ResyncGroups(createPortals) + if err != nil { + ce.Reply("Error resyncing groups: %v", err) + } else { + ce.Reply("Resynced groups") + } + } +} + const cmdLoginMatrixHelp = `login-matrix <_access token_> - Replace your WhatsApp account's Matrix puppet with your real Matrix account.` func (handler *CommandHandler) CommandLoginMatrix(ce *CommandEvent) { diff --git a/user.go b/user.go index 82ede80..46ad722 100644 --- a/user.go +++ b/user.go @@ -387,7 +387,12 @@ func (user *User) HandleEvent(event interface{}) { user.log.Warnln("Failed to send presence after app state sync:", err) } } else if v.Name == appstate.WAPatchCriticalUnblockLow { - go user.resyncPuppets() + go func() { + err := user.ResyncContacts() + if err != nil { + user.log.Errorln("Failed to resync puppets: %v", err) + } + }() } case *events.PushNameSetting: // Send presence available when connecting and when the pushname is changed. @@ -629,16 +634,38 @@ func (user *User) syncPuppet(jid types.JID, reason string) { user.bridge.GetPuppetByJID(jid).SyncContact(user, false, reason) } -func (user *User) resyncPuppets() { +func (user *User) ResyncContacts() error { contacts, err := user.Client.Store.Contacts.GetAllContacts() if err != nil { - user.log.Errorln("Failed to get all contacts to sync puppets:", err) - return + return fmt.Errorf("failed to get cached contacts: %w", err) } + user.log.Infofln("Resyncing displaynames with %d contacts", len(contacts)) for jid, contact := range contacts { puppet := user.bridge.GetPuppetByJID(jid) puppet.Sync(user, contact) } + return nil +} + +func (user *User) ResyncGroups(createPortals bool) error { + groups, err := user.Client.GetJoinedGroups() + if err != nil { + return fmt.Errorf("failed to get group list from server: %w", err) + } + for _, group := range groups { + portal := user.GetPortalByJID(group.JID) + if len(portal.MXID) == 0 { + if createPortals { + err = portal.CreateMatrixRoom(user, group, true) + if err != nil { + return fmt.Errorf("failed to create room for %s: %w", group.JID, err) + } + } + } else { + portal.UpdateMatrixRoom(user, group) + } + } + return nil } const WATypingTimeout = 15 * time.Second