]> git.giacomolli.uk - onechat/blobdiff - whatsapp.go
Move whatsapp_find to its own file
[onechat] / whatsapp.go
index c5af946027b9c4341feadf848ac792f26d3123b3..5b0730013703fc0966b69ce918f720a05ab48205 100644 (file)
@@ -1,7 +1,6 @@
 package main
 
 import (
 package main
 
 import (
-       "cmp"
        "context"
        "crypto/sha256"
        "encoding/hex"
        "context"
        "crypto/sha256"
        "encoding/hex"
@@ -15,7 +14,6 @@ import (
        "strings"
        "sync"
        "time"
        "strings"
        "sync"
        "time"
-       "unicode"
 
        "go.mau.fi/whatsmeow"
        "go.mau.fi/whatsmeow/proto/waE2E"
 
        "go.mau.fi/whatsmeow"
        "go.mau.fi/whatsmeow/proto/waE2E"
@@ -24,9 +22,6 @@ import (
        "go.mau.fi/whatsmeow/types"
        "go.mau.fi/whatsmeow/types/events"
        waLog "go.mau.fi/whatsmeow/util/log"
        "go.mau.fi/whatsmeow/types"
        "go.mau.fi/whatsmeow/types/events"
        waLog "go.mau.fi/whatsmeow/util/log"
-       "golang.org/x/text/runes"
-       "golang.org/x/text/transform"
-       "golang.org/x/text/unicode/norm"
 
        // The session store of the library opens SQLite with this pure Go
        // driver.
 
        // The session store of the library opens SQLite with this pure Go
        // driver.
@@ -80,9 +75,6 @@ const (
        // session database or of the groups from WhatsApp.
        whatsappLookupTime = 10 * time.Second
 
        // session database or of the groups from WhatsApp.
        whatsappLookupTime = 10 * time.Second
 
-       // whatsappFindMatches is the most matches of whatsapp_find.
-       whatsappFindMatches = 20
-
        // whatsappNotLinked is the answer of a WhatsApp tool without a link.
        whatsappNotLinked = "WhatsApp is not linked. Tell the user to run /whatsapp pair PHONE in the terminal or in Telegram."
 
        // whatsappNotLinked is the answer of a WhatsApp tool without a link.
        whatsappNotLinked = "WhatsApp is not linked. Tell the user to run /whatsapp pair PHONE in the terminal or in Telegram."
 
@@ -99,10 +91,6 @@ const (
        whatsappSending = "sending"
        whatsappSent    = "sent"
        whatsappFailed  = "failed"
        whatsappSending = "sending"
        whatsappSent    = "sent"
        whatsappFailed  = "failed"
-
-       // whatsappNamesNotReady is the answer of whatsapp_find before the names
-       // arrive from the phone.
-       whatsappNamesNotReady = "The names of the contacts and groups have not arrived from the phone yet. They come some minutes after the link. Try again later."
 )
 
 // WhatsAppClient is the part of the WhatsApp client that the daemon uses. The
 )
 
 // WhatsAppClient is the part of the WhatsApp client that the daemon uses. The
@@ -363,6 +351,11 @@ func (w *WhatsApp) Close() error {
        return err
 }
 
        return err
 }
 
+// Find returns the answer of whatsapp_find for name, as whatsappFind says.
+func (w *WhatsApp) Find(ctx context.Context, name string) (string, error) {
+       return whatsappFind(ctx, w.link(), w.groups, name)
+}
+
 func (w *WhatsApp) sessionPath() string {
        return filepath.Join(w.dir, whatsappSessionFileName)
 }
 func (w *WhatsApp) sessionPath() string {
        return filepath.Join(w.dir, whatsappSessionFileName)
 }
@@ -522,80 +515,6 @@ func (w *WhatsApp) unlink(ctx context.Context, l *TurnLoop, answer func(text str
        return nil
 }
 
        return nil
 }
 
-// Find returns the contacts and the groups whose name contains name, with no
-// regard to case and accents, as lines "ID | name | contact" or "ID | name |
-// group", sorted by name. It gives at most whatsappFindMatches lines and the
-// number of the others. The contacts come from the session database, and the
-// groups from WhatsApp, read again after whatsappGroupsAge. When the groups
-// cannot be read and none are known, the answer says so. Before the names
-// arrive from the phone, it says that they are not ready.
-func (w *WhatsApp) Find(ctx context.Context, name string) (string, error) {
-       w.mutex.Lock()
-       linked := w.linked
-       device := w.device
-       own := slices.Clone(w.own)
-       w.mutex.Unlock()
-
-       stale := w.groups.stale()
-
-       if !linked {
-               return whatsappNotLinked, nil
-       }
-
-       query := whatsappFold(name)
-       if query == "" {
-               return "", errors.New("name is empty: give a part of the name of the contact or the group")
-       }
-
-       contacts, err := device.Contacts.GetAllContacts(ctx)
-       if err != nil {
-               return "", fmt.Errorf("read the contacts: %s", w.redact(err.Error()))
-       }
-
-       read := !stale || w.groups.read(ctx, w.link())
-       groups := w.groups.all()
-
-       if len(contacts) == 0 && len(groups) == 0 {
-               if !read {
-                       return "The names are not known yet, and WhatsApp is disconnected, so the groups cannot be read. Try again later.", nil
-               }
-
-               return whatsappNamesNotReady, nil
-       }
-
-       var matches []WhatsAppMatch
-       for jid, contact := range contacts {
-               names := []string{contact.FullName, contact.FirstName, contact.BusinessName, contact.PushName}
-               shown := slices.IndexFunc(names, func(name string) bool {
-                       return name != ""
-               })
-
-               if slices.Contains(own, jid.User) || shown < 0 || !slices.ContainsFunc(names, func(name string) bool {
-                       return strings.Contains(whatsappFold(name), query)
-               }) {
-                       continue
-               }
-
-               matches = append(matches, WhatsAppMatch{
-                       id:   jid.ToNonAD().String(),
-                       name: names[shown],
-                       kind: "contact",
-               })
-       }
-
-       for id, group := range groups {
-               if strings.Contains(whatsappFold(group), query) {
-                       matches = append(matches, WhatsAppMatch{
-                               id:   id,
-                               name: group,
-                               kind: "group",
-                       })
-               }
-       }
-
-       return whatsappFindAnswer(name, matches, read || len(groups) > 0), nil
-}
-
 // removeSession ends the connection, closes the session database, and deletes
 // its files.
 func (w *WhatsApp) removeSession() error {
 // removeSession ends the connection, closes the session database, and deletes
 // its files.
 func (w *WhatsApp) removeSession() error {
@@ -973,13 +892,6 @@ func (w *WhatsApp) Send(ctx context.Context, id, chat, text string) (string, err
        return record.Answer, nil
 }
 
        return record.Answer, nil
 }
 
-// WhatsAppMatch is one contact or group of Find.
-type WhatsAppMatch struct {
-       id   string
-       name string
-       kind string
-}
-
 // WhatsAppLink is a copy of the link of the device at one time, for the parts
 // of WhatsApp that do not own it: the groups, the batches, find, and send.
 type WhatsAppLink struct {
 // WhatsAppLink is a copy of the link of the device at one time, for the parts
 // of WhatsApp that do not own it: the groups, the batches, find, and send.
 type WhatsAppLink struct {
@@ -1055,44 +967,6 @@ func whatsappContactName(ctx context.Context, device *store.Device, jid types.JI
        }
 }
 
        }
 }
 
-// whatsappFold returns text in lower case with no accents and no space at its
-// ends, so that names match with no regard to case and accents.
-func whatsappFold(text string) string {
-       folded, _, err := transform.String(transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC), text)
-       if err != nil {
-               folded = text
-       }
-
-       return strings.ToLower(strings.TrimSpace(folded))
-}
-
-// whatsappFindAnswer returns the answer of Find for name with matches. Without
-// groups, the answer says that it has no groups.
-func whatsappFindAnswer(name string, matches []WhatsAppMatch, groups bool) string {
-       slices.SortFunc(matches, func(a, b WhatsAppMatch) int {
-               return cmp.Or(strings.Compare(whatsappFold(a.name), whatsappFold(b.name)), strings.Compare(a.id, b.id))
-       })
-
-       var lines []string
-       for _, match := range matches[:min(len(matches), whatsappFindMatches)] {
-               lines = append(lines, match.id+" | "+match.name+" | "+match.kind)
-       }
-
-       if len(matches) == 0 {
-               lines = append(lines, "No contact or group has a name with \""+name+"\".")
-       }
-
-       if len(matches) > whatsappFindMatches {
-               lines = append(lines, fmt.Sprintf("%d more: give a longer part of the name.", len(matches)-whatsappFindMatches))
-       }
-
-       if !groups {
-               lines = append(lines, "The groups are not in this answer, because WhatsApp is disconnected.")
-       }
-
-       return strings.Join(lines, "\n")
-}
-
 // whatsappSendCheck returns the JID of chat, and an error when id, chat, or
 // text cannot go to a send. The chat is a private chat or a group, and not the
 // chat of the user with itself.
 // whatsappSendCheck returns the JID of chat, and an error when id, chat, or
 // text cannot go to a send. The chat is a private chat or a group, and not the
 // chat of the user with itself.