package whatsapp import ( "github.com/Rhymen/go-whatsapp/binary" "github.com/Rhymen/go-whatsapp/binary/proto" "log" "strconv" "time" ) type MessageOffsetInfo struct { FirstMessageId string FirstMessageOwner bool } func decodeMessages(n *binary.Node) []*proto.WebMessageInfo { var messages = make([]*proto.WebMessageInfo, 0) if n == nil || n.Attributes == nil || n.Content == nil { return messages } for _, msg := range n.Content.([]interface{}) { switch msg.(type) { case *proto.WebMessageInfo: messages = append(messages, msg.(*proto.WebMessageInfo)) default: log.Println("decodeMessages: Non WebMessage encountered") } } return messages } // LoadChatMessages is useful to "scroll" messages, loading by count at a time // if handlers == nil the func will use default handlers // if after == true LoadChatMessages will load messages after the specified messageId, otherwise it will return // message before the messageId func (wac *Conn) LoadChatMessages(jid string, count int, messageId string, owner bool, after bool, handlers ...Handler) error { if count <= 0 { return nil } if handlers == nil { handlers = wac.handler } kind := "before" if after { kind = "after" } node, err := wac.query("message", jid, messageId, kind, strconv.FormatBool(owner), "", count, 0) if err != nil { wac.handleWithCustomHandlers(err, handlers) return err } for _, msg := range decodeMessages(node) { wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers) wac.handleWithCustomHandlers(msg, handlers) } return nil } // LoadFullChatHistory loads full chat history for the given jid // chunkSize = how many messages to load with one query; if handlers == nil the func will use default handlers; // pauseBetweenQueries = how much time to sleep between queries func (wac *Conn) LoadFullChatHistory(jid string, chunkSize int, pauseBetweenQueries time.Duration, handlers ...Handler) { if chunkSize <= 0 { return } if handlers == nil { handlers = wac.handler } beforeMsg := "" beforeMsgIsOwner := true for { node, err := wac.query("message", jid, beforeMsg, "before", strconv.FormatBool(beforeMsgIsOwner), "", chunkSize, 0) if err != nil { wac.handleWithCustomHandlers(err, handlers) } else { msgs := decodeMessages(node) for _, msg := range msgs { wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers) wac.handleWithCustomHandlers(msg, handlers) } if len(msgs) == 0 { break } beforeMsg = *msgs[0].Key.Id beforeMsgIsOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe } <-time.After(pauseBetweenQueries) } } // LoadFullChatHistoryAfter loads all messages after the specified messageId // useful to "catch up" with the message history after some specified message func (wac *Conn) LoadFullChatHistoryAfter(jid string, messageId string, chunkSize int, pauseBetweenQueries time.Duration, handlers ...Handler) { if chunkSize <= 0 { return } if handlers == nil { handlers = wac.handler } msgOwner := true prevNotFound := false for { node, err := wac.query("message", jid, messageId, "after", strconv.FormatBool(msgOwner), "", chunkSize, 0) if err != nil { // Whatsapp will return 404 status when there is wrong owner flag on the requested message id if err == ErrServerRespondedWith404 { // this will detect two consecutive "not found" errors. // this is done to prevent infinite loop when wrong message id supplied if prevNotFound { log.Println("LoadFullChatHistoryAfter: could not retrieve any messages, wrong message id?") return } prevNotFound = true // try to reverse the owner flag and retry if msgOwner { // reverse initial msgOwner value and retry msgOwner = false <-time.After(time.Second) continue } } // if the error isn't a 404 error, pass it to the error handler wac.handleWithCustomHandlers(err, handlers) } else { msgs := decodeMessages(node) for _, msg := range msgs { wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers) wac.handleWithCustomHandlers(msg, handlers) } if len(msgs) != chunkSize { break } messageId = *msgs[0].Key.Id msgOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe } // message was found prevNotFound = false <-time.After(pauseBetweenQueries) } }