summaryrefslogblamecommitdiffstats
path: root/vendor/github.com/Rhymen/go-whatsapp/chat_history.go
blob: 55c7ae6301194f9162aeb75661865dc8d4605cb7 (plain) (tree)





















































































































































































                                                                                                                                   
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)

	}

}