mirror of
https://github.com/mautrix/whatsapp.git
synced 2025-03-14 14:15:38 +00:00
handlewhatsapp: add placeholders for missing events
This commit is contained in:
parent
3c53ea5ed1
commit
d4991cb0b6
5 changed files with 391 additions and 118 deletions
|
@ -130,6 +130,8 @@ SELECT
|
|||
-- json_object
|
||||
(
|
||||
'sender_device_id', CAST(nullif(split_part(replace(sender, '@s.whatsapp.net', ''), ':', 2), '') AS INTEGER)
|
||||
'broadcast_list_jid', broadcast_list_jid,
|
||||
'error', CAST(error AS TEXT),
|
||||
) -- metadata
|
||||
FROM message_old;
|
||||
|
||||
|
|
|
@ -17,34 +17,34 @@ import (
|
|||
)
|
||||
|
||||
func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.UserLogin) error {
|
||||
w := &WhatsAppClient{
|
||||
Main: wa,
|
||||
UserLogin: login,
|
||||
}
|
||||
login.Client = w
|
||||
|
||||
loginMetadata := login.Metadata.(*UserLoginMetadata)
|
||||
if loginMetadata.WADeviceID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
jid := waid.ParseUserLoginID(login.ID, loginMetadata.WADeviceID)
|
||||
|
||||
device, err := wa.DeviceStore.GetDevice(jid)
|
||||
var err error
|
||||
w.JID = waid.ParseUserLoginID(login.ID, loginMetadata.WADeviceID)
|
||||
w.Device, err = wa.DeviceStore.GetDevice(w.JID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := &WhatsAppClient{
|
||||
Main: wa,
|
||||
UserLogin: login,
|
||||
Device: device,
|
||||
JID: jid,
|
||||
}
|
||||
|
||||
if device != nil {
|
||||
if w.Device != nil {
|
||||
log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger()
|
||||
w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log))
|
||||
w.Client.AddEventHandler(w.handleWAEvent)
|
||||
w.Client.AutomaticMessageRerequestFromPhone = true
|
||||
w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts)
|
||||
} else {
|
||||
w.UserLogin.Log.Debug().Stringer("jid", jid).Msg("No device found for user in whatsmeow store")
|
||||
w.UserLogin.Log.Warn().Stringer("jid", w.JID).Msg("No device found for user in whatsmeow store")
|
||||
}
|
||||
|
||||
login.Client = w
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package connector
|
|||
|
||||
import (
|
||||
"go.mau.fi/util/jsontime"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"maunium.net/go/mautrix/bridgev2/database"
|
||||
)
|
||||
|
||||
|
@ -26,12 +27,24 @@ func (wa *WhatsAppConnector) GetDBMetaTypes() database.MetaTypes {
|
|||
}
|
||||
|
||||
type UserLoginMetadata struct {
|
||||
WADeviceID uint16 `json:"wa_device_id"`
|
||||
//TODO: Add phone last ping/seen
|
||||
WADeviceID uint16 `json:"wa_device_id"`
|
||||
PhoneLastSeen jsontime.Unix `json:"phone_last_seen"`
|
||||
PhoneLastPinged jsontime.Unix `json:"phone_last_pinged"`
|
||||
Timezone string `json:"timezone"`
|
||||
}
|
||||
|
||||
type MessageErrorType string
|
||||
|
||||
const (
|
||||
MsgNoError MessageErrorType = ""
|
||||
MsgErrDecryptionFailed MessageErrorType = "decryption_failed"
|
||||
MsgErrMediaNotFound MessageErrorType = "media_not_found"
|
||||
)
|
||||
|
||||
type MessageMetadata struct {
|
||||
SenderDeviceID uint16 `json:"sender_device_id,omitempty"`
|
||||
SenderDeviceID uint16 `json:"sender_device_id,omitempty"`
|
||||
Error MessageErrorType `json:"error,omitempty"`
|
||||
BroadcastListJID *types.JID `json:"broadcast_list_jid,omitempty"`
|
||||
}
|
||||
|
||||
type ReactionMetadata struct {
|
||||
|
|
|
@ -11,17 +11,68 @@ import (
|
|||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/database"
|
||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/format"
|
||||
|
||||
"maunium.net/go/mautrix-whatsapp/pkg/waid"
|
||||
)
|
||||
|
||||
func (wa *WhatsAppClient) getPortalKeyByMessageSource(ms types.MessageSource) networkid.PortalKey {
|
||||
jid := ms.Chat
|
||||
if ms.IsIncomingBroadcast() {
|
||||
if ms.IsFromMe {
|
||||
jid = ms.BroadcastListOwner.ToNonAD()
|
||||
} else {
|
||||
jid = ms.Sender.ToNonAD()
|
||||
}
|
||||
}
|
||||
return wa.makeWAPortalKey(jid)
|
||||
}
|
||||
|
||||
type MessageInfoWrapper struct {
|
||||
Info types.MessageInfo
|
||||
wa *WhatsAppClient
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) ShouldCreatePortal() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) GetPortalKey() networkid.PortalKey {
|
||||
return evt.wa.getPortalKeyByMessageSource(evt.Info.MessageSource)
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) AddLogContext(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("message_id", evt.Info.ID).Stringer("sender_id", evt.Info.Sender)
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) GetTimestamp() time.Time {
|
||||
return evt.Info.Timestamp
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) GetSender() bridgev2.EventSender {
|
||||
return evt.wa.makeEventSender(evt.Info.Sender)
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) GetID() networkid.MessageID {
|
||||
return waid.MakeMessageID(evt.Info.Chat, evt.Info.Sender, evt.Info.ID)
|
||||
}
|
||||
|
||||
func (evt *MessageInfoWrapper) GetTransactionID() networkid.TransactionID {
|
||||
return networkid.TransactionID(evt.GetID())
|
||||
}
|
||||
|
||||
type WAMessageEvent struct {
|
||||
*events.Message
|
||||
wa *WhatsAppClient
|
||||
*MessageInfoWrapper
|
||||
Message *waE2E.Message
|
||||
MsgEvent *events.Message
|
||||
|
||||
isUndecryptableUpsertSubEvent bool
|
||||
}
|
||||
|
||||
var (
|
||||
_ bridgev2.RemoteMessage = (*WAMessageEvent)(nil)
|
||||
_ bridgev2.RemoteMessageUpsert = (*WAMessageEvent)(nil)
|
||||
_ bridgev2.RemoteMessageWithTransactionID = (*WAMessageEvent)(nil)
|
||||
_ bridgev2.RemoteEventWithTimestamp = (*WAMessageEvent)(nil)
|
||||
_ bridgev2.RemoteEventThatMayCreatePortal = (*WAMessageEvent)(nil)
|
||||
|
@ -36,7 +87,13 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por
|
|||
if len(existing) > 1 {
|
||||
zerolog.Ctx(ctx).Warn().Msg("Got edit to message with multiple parts")
|
||||
}
|
||||
editedMsg := evt.Message.Message.GetProtocolMessage().GetEditedMessage()
|
||||
var editedMsg *waE2E.Message
|
||||
if evt.isUndecryptableUpsertSubEvent {
|
||||
// TODO db metadata needs to be updated in this case to remove the error
|
||||
editedMsg = evt.Message
|
||||
} else {
|
||||
editedMsg = evt.Message.GetProtocolMessage().GetEditedMessage()
|
||||
}
|
||||
|
||||
// TODO edits to media captions may not contain the media
|
||||
cm := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, editedMsg)
|
||||
|
@ -46,7 +103,7 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por
|
|||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetTargetMessage() networkid.MessageID {
|
||||
if reactionMsg := evt.Message.Message.GetReactionMessage(); reactionMsg != nil {
|
||||
if reactionMsg := evt.Message.GetReactionMessage(); reactionMsg != nil {
|
||||
key := reactionMsg.GetKey()
|
||||
senderID := key.GetParticipant()
|
||||
if senderID == "" {
|
||||
|
@ -58,7 +115,7 @@ func (evt *WAMessageEvent) GetTargetMessage() networkid.MessageID {
|
|||
}
|
||||
senderJID, _ := types.ParseJID(senderID)
|
||||
return waid.MakeMessageID(evt.Info.Chat, senderJID, *key.ID)
|
||||
} else if protocolMsg := evt.Message.Message.GetProtocolMessage(); protocolMsg != nil {
|
||||
} else if protocolMsg := evt.Message.GetProtocolMessage(); protocolMsg != nil {
|
||||
key := protocolMsg.GetKey()
|
||||
senderID := key.GetParticipant()
|
||||
if senderID == "" {
|
||||
|
@ -75,7 +132,7 @@ func (evt *WAMessageEvent) GetTargetMessage() networkid.MessageID {
|
|||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetReactionEmoji() (string, networkid.EmojiID) {
|
||||
return evt.Message.Message.GetReactionMessage().GetText(), ""
|
||||
return evt.Message.GetReactionMessage().GetText(), ""
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetReactionDBMetadata() any {
|
||||
|
@ -88,12 +145,8 @@ func (evt *WAMessageEvent) GetRemovedEmojiID() networkid.EmojiID {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) ShouldCreatePortal() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetType() bridgev2.RemoteEventType {
|
||||
waMsg := evt.Message.Message
|
||||
waMsg := evt.Message
|
||||
if waMsg.ReactionMessage != nil {
|
||||
if waMsg.ReactionMessage.GetText() == "" {
|
||||
return bridgev2.RemoteEventReactionRemove
|
||||
|
@ -107,40 +160,109 @@ func (evt *WAMessageEvent) GetType() bridgev2.RemoteEventType {
|
|||
return bridgev2.RemoteEventEdit
|
||||
}
|
||||
}
|
||||
return bridgev2.RemoteEventMessage
|
||||
return bridgev2.RemoteEventMessageUpsert
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetPortalKey() networkid.PortalKey {
|
||||
return evt.wa.makeWAPortalKey(evt.Info.Chat)
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) AddLogContext(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("message_id", evt.Info.ID).Stringer("sender_id", evt.Info.Sender)
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetTimestamp() time.Time {
|
||||
return evt.Info.Timestamp
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetSender() bridgev2.EventSender {
|
||||
return evt.wa.makeEventSender(evt.Info.Sender)
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetID() networkid.MessageID {
|
||||
return waid.MakeMessageID(evt.Info.Chat, evt.Info.Sender, evt.Info.ID)
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) GetTransactionID() networkid.TransactionID {
|
||||
// TODO for newsletter messages, there's a different transaction ID
|
||||
return networkid.TransactionID(evt.GetID())
|
||||
func (evt *WAMessageEvent) HandleExisting(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, existing []*database.Message) (bridgev2.UpsertResult, error) {
|
||||
if existing[0].Metadata.(*MessageMetadata).Error == MsgErrDecryptionFailed {
|
||||
zerolog.Ctx(ctx).Debug().Stringer("existing_mxid", existing[0].MXID).Msg("Ignoring duplicate message")
|
||||
evt.isUndecryptableUpsertSubEvent = true
|
||||
return bridgev2.UpsertResult{SubEvents: []bridgev2.RemoteEvent{
|
||||
&WANowDecryptableMessage{WAMessageEvent: evt, editParts: existing}},
|
||||
}, nil
|
||||
}
|
||||
zerolog.Ctx(ctx).Debug().Stringer("existing_mxid", existing[0].MXID).Msg("Ignoring duplicate message")
|
||||
return bridgev2.UpsertResult{}, nil
|
||||
}
|
||||
|
||||
func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
|
||||
converted := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message.Message)
|
||||
converted := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message)
|
||||
for _, part := range converted.Parts {
|
||||
part.DBMetadata = &MessageMetadata{
|
||||
SenderDeviceID: evt.Info.Sender.Device,
|
||||
}
|
||||
if evt.Info.IsIncomingBroadcast() {
|
||||
part.DBMetadata.(*MessageMetadata).BroadcastListJID = &evt.Info.Chat
|
||||
if part.Extra == nil {
|
||||
part.Extra = map[string]any{}
|
||||
}
|
||||
part.Extra["fi.mau.whatsapp.source_broadcast_list"] = evt.Info.Chat.String()
|
||||
}
|
||||
}
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
type WANowDecryptableMessage struct {
|
||||
*WAMessageEvent
|
||||
editParts []*database.Message
|
||||
}
|
||||
|
||||
var (
|
||||
_ bridgev2.RemoteEdit = (*WANowDecryptableMessage)(nil)
|
||||
_ bridgev2.RemoteEventWithBundledParts = (*WANowDecryptableMessage)(nil)
|
||||
)
|
||||
|
||||
func (evt *WANowDecryptableMessage) GetTargetDBMessage() []*database.Message {
|
||||
return evt.editParts
|
||||
}
|
||||
|
||||
func (evt *WANowDecryptableMessage) GetTargetMessage() networkid.MessageID {
|
||||
return evt.GetID()
|
||||
}
|
||||
|
||||
func (evt *WANowDecryptableMessage) AddLogContext(c zerolog.Context) zerolog.Context {
|
||||
return c
|
||||
}
|
||||
|
||||
func (evt *WANowDecryptableMessage) GetType() bridgev2.RemoteEventType {
|
||||
return bridgev2.RemoteEventMessage
|
||||
}
|
||||
|
||||
type WAUndecryptableMessage struct {
|
||||
*MessageInfoWrapper
|
||||
}
|
||||
|
||||
var (
|
||||
_ bridgev2.RemoteMessage = (*WAUndecryptableMessage)(nil)
|
||||
_ bridgev2.RemoteMessageWithTransactionID = (*WAUndecryptableMessage)(nil)
|
||||
_ bridgev2.RemoteEventWithTimestamp = (*WAUndecryptableMessage)(nil)
|
||||
_ bridgev2.RemoteEventThatMayCreatePortal = (*WAUndecryptableMessage)(nil)
|
||||
)
|
||||
|
||||
func (evt *WAUndecryptableMessage) GetType() bridgev2.RemoteEventType {
|
||||
return bridgev2.RemoteEventMessage
|
||||
}
|
||||
|
||||
const UndecryptableMessageNotice = "Decrypting message from WhatsApp failed, waiting for sender to re-send... " +
|
||||
"([learn more](https://faq.whatsapp.com/general/security-and-privacy/seeing-waiting-for-this-message-this-may-take-a-while))"
|
||||
|
||||
var undecryptableMessageContent event.MessageEventContent
|
||||
|
||||
func init() {
|
||||
undecryptableMessageContent = format.RenderMarkdown(UndecryptableMessageNotice, true, false)
|
||||
undecryptableMessageContent.MsgType = event.MsgNotice
|
||||
}
|
||||
|
||||
func (evt *WAUndecryptableMessage) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
|
||||
extra := map[string]any{
|
||||
"fi.mau.whatsapp.undecryptable": true,
|
||||
}
|
||||
var broadcastListJID *types.JID
|
||||
if evt.Info.IsIncomingBroadcast() {
|
||||
broadcastListJID = &evt.Info.Chat
|
||||
extra["fi.mau.whatsapp.source_broadcast_list"] = evt.Info.Chat.String()
|
||||
}
|
||||
return &bridgev2.ConvertedMessage{
|
||||
Parts: []*bridgev2.ConvertedMessagePart{{
|
||||
Type: event.EventMessage,
|
||||
Content: &undecryptableMessageContent,
|
||||
Extra: extra,
|
||||
DBMetadata: &MessageMetadata{
|
||||
SenderDeviceID: evt.Info.Sender.Device,
|
||||
Error: MsgErrDecryptionFailed,
|
||||
BroadcastListJID: broadcastListJID,
|
||||
},
|
||||
}},
|
||||
Disappear: portal.Disappear,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -53,74 +53,54 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
|
|||
|
||||
switch evt := rawEvt.(type) {
|
||||
case *events.Message:
|
||||
log.Trace().
|
||||
Any("info", evt.Info).
|
||||
Any("payload", evt.Message).
|
||||
Msg("Received WhatsApp message")
|
||||
if evt.Info.Chat.Server == types.HiddenUserServer || evt.Info.Sender.Server == types.HiddenUserServer {
|
||||
return
|
||||
}
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAMessageEvent{
|
||||
Message: evt,
|
||||
wa: wa,
|
||||
})
|
||||
wa.handleWAMessage(evt)
|
||||
case *events.Receipt:
|
||||
var evtType bridgev2.RemoteEventType
|
||||
switch evt.Type {
|
||||
case types.ReceiptTypeRead, types.ReceiptTypeReadSelf:
|
||||
evtType = bridgev2.RemoteEventReadReceipt
|
||||
case types.ReceiptTypeDelivered:
|
||||
evtType = bridgev2.RemoteEventDeliveryReceipt
|
||||
case types.ReceiptTypeSender:
|
||||
fallthrough
|
||||
default:
|
||||
return
|
||||
}
|
||||
targets := make([]networkid.MessageID, len(evt.MessageIDs))
|
||||
for i, id := range evt.MessageIDs {
|
||||
targets[i] = waid.MakeMessageID(evt.Chat, *wa.Device.ID, id)
|
||||
}
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Receipt{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: evtType,
|
||||
PortalKey: wa.makeWAPortalKey(evt.Chat),
|
||||
Sender: wa.makeEventSender(evt.Sender),
|
||||
Timestamp: evt.Timestamp,
|
||||
},
|
||||
Targets: targets,
|
||||
})
|
||||
wa.handleWAReceipt(evt)
|
||||
case *events.ChatPresence:
|
||||
typingType := bridgev2.TypingTypeText
|
||||
timeout := 15 * time.Second
|
||||
if evt.Media == types.ChatPresenceMediaAudio {
|
||||
typingType = bridgev2.TypingTypeRecordingMedia
|
||||
}
|
||||
if evt.State == types.ChatPresencePaused {
|
||||
timeout = 0
|
||||
}
|
||||
wa.handleWAChatPresence(evt)
|
||||
case *events.UndecryptableMessage:
|
||||
wa.handleWAUndecryptableMessage(evt)
|
||||
|
||||
case *events.CallOffer:
|
||||
wa.handleWACallStart(evt.CallCreator, evt.CallID, "", evt.Timestamp)
|
||||
case *events.CallOfferNotice:
|
||||
wa.handleWACallStart(evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp)
|
||||
case *events.CallTerminate, *events.CallRelayLatency, *events.CallAccept, *events.UnknownCallEvent:
|
||||
// ignore
|
||||
case *events.IdentityChange:
|
||||
wa.handleWAIdentityChange(evt)
|
||||
case *events.MarkChatAsRead:
|
||||
// TODO
|
||||
//wa.handleWAMarkChatAsRead(evt)
|
||||
case *events.DeleteForMe:
|
||||
// TODO
|
||||
//wa.handleWADeleteForMe(evt)
|
||||
case *events.DeleteChat:
|
||||
// TODO
|
||||
//wa.handleWADeleteChat(evt)
|
||||
case *events.Mute:
|
||||
// TODO
|
||||
case *events.Archive:
|
||||
// TODO
|
||||
case *events.Pin:
|
||||
// TODO
|
||||
|
||||
case *events.HistorySync:
|
||||
// TODO
|
||||
case *events.MediaRetry:
|
||||
// TODO
|
||||
|
||||
case *events.GroupInfo:
|
||||
// TODO
|
||||
case *events.JoinedGroup:
|
||||
// TODO
|
||||
case *events.NewsletterJoin:
|
||||
// TODO
|
||||
case *events.NewsletterLeave:
|
||||
// TODO
|
||||
case *events.Picture:
|
||||
// TODO
|
||||
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Typing{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventTyping,
|
||||
LogContext: nil,
|
||||
PortalKey: wa.makeWAPortalKey(evt.Chat),
|
||||
Sender: wa.makeEventSender(evt.Sender),
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
Timeout: timeout,
|
||||
Type: typingType,
|
||||
})
|
||||
case *events.Connected:
|
||||
log.Debug().Msg("Connected to WhatsApp socket")
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
||||
if len(wa.Client.Store.PushName) > 0 {
|
||||
go func() {
|
||||
err := wa.Client.SendPresence(types.PresenceUnavailable)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to send initial presence after connecting")
|
||||
}
|
||||
}()
|
||||
}
|
||||
case *events.AppStateSyncComplete:
|
||||
if len(wa.Client.Store.PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock {
|
||||
err := wa.Client.SendPresence(types.PresenceUnavailable)
|
||||
|
@ -136,6 +116,8 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
|
|||
//}
|
||||
}()
|
||||
}
|
||||
case *events.AppState:
|
||||
// Intentionally ignored
|
||||
case *events.PushNameSetting:
|
||||
// Send presence available when connecting and when the pushname is changed.
|
||||
// This makes sure that outgoing messages always have the right pushname.
|
||||
|
@ -149,6 +131,45 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
|
|||
}
|
||||
// TODO update own ghost info
|
||||
//go user.syncPuppet(user.JID.ToNonAD(), "push name setting")
|
||||
case *events.Contact:
|
||||
// TODO
|
||||
//go user.syncPuppet(v.JID, "contact event")
|
||||
case *events.PushName:
|
||||
// TODO
|
||||
//go user.syncPuppet(v.JID, "push name event")
|
||||
case *events.BusinessName:
|
||||
// TODO
|
||||
//go user.syncPuppet(v.JID, "business name event")
|
||||
|
||||
case *events.Connected:
|
||||
log.Debug().Msg("Connected to WhatsApp socket")
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
||||
if len(wa.Client.Store.PushName) > 0 {
|
||||
go func() {
|
||||
err := wa.Client.SendPresence(types.PresenceUnavailable)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to send initial presence after connecting")
|
||||
}
|
||||
}()
|
||||
}
|
||||
case *events.OfflineSyncPreview:
|
||||
log.Info().
|
||||
Int("message_count", evt.Messages).
|
||||
Int("receipt_count", evt.Receipts).
|
||||
Int("notification_count", evt.Notifications).
|
||||
Int("app_data_change_count", evt.AppDataChanges).
|
||||
Msg("Server sent number of events that were missed during downtime")
|
||||
case *events.OfflineSyncCompleted:
|
||||
if !wa.PhoneRecentlySeen(true) {
|
||||
log.Info().
|
||||
Time("phone_last_seen", wa.UserLogin.Metadata.(*UserLoginMetadata).PhoneLastSeen.Time).
|
||||
Msg("Offline sync completed, but phone last seen date is still old - sending phone offline bridge status")
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: WAPhoneOffline})
|
||||
} else {
|
||||
log.Info().Msg("Offline sync completed")
|
||||
}
|
||||
case *events.LoggedOut:
|
||||
wa.handleWALogout(evt.Reason, evt.OnConnect)
|
||||
case *events.Disconnected:
|
||||
// Don't send the normal transient disconnect state if we're already in a different transient disconnect state.
|
||||
// TODO remove this if/when the phone offline state is moved to a sub-state of CONNECTED
|
||||
|
@ -171,6 +192,11 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
|
|||
})
|
||||
case *events.StreamReplaced:
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateUnknownError, Error: WAStreamReplaced})
|
||||
case *events.KeepAliveTimeout:
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: WAKeepaliveTimeout})
|
||||
case *events.KeepAliveRestored:
|
||||
log.Info().Msg("Keepalive restored after timeouts, sending connected event")
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
||||
case *events.ConnectFailure:
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{
|
||||
StateEvent: status.StateUnknownError,
|
||||
|
@ -190,3 +216,113 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
|
|||
log.Debug().Type("event_type", rawEvt).Msg("Unhandled WhatsApp event")
|
||||
}
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAMessage(evt *events.Message) {
|
||||
wa.UserLogin.Log.Trace().
|
||||
Any("info", evt.Info).
|
||||
Any("payload", evt.Message).
|
||||
Msg("Received WhatsApp message")
|
||||
if evt.Info.Chat.Server == types.HiddenUserServer || evt.Info.Sender.Server == types.HiddenUserServer {
|
||||
return
|
||||
}
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAMessageEvent{
|
||||
MessageInfoWrapper: &MessageInfoWrapper{
|
||||
Info: evt.Info,
|
||||
wa: wa,
|
||||
},
|
||||
Message: evt.Message,
|
||||
MsgEvent: evt,
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.UndecryptableMessage) {
|
||||
wa.UserLogin.Log.Debug().
|
||||
Any("info", evt.Info).
|
||||
Bool("unavailable", evt.IsUnavailable).
|
||||
Str("decrypt_fail", string(evt.DecryptFailMode)).
|
||||
Msg("Received undecryptable WhatsApp message")
|
||||
if evt.DecryptFailMode == events.DecryptFailHide || evt.Info.Chat.Server == types.HiddenUserServer || evt.Info.Sender.Server == types.HiddenUserServer {
|
||||
return
|
||||
}
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAUndecryptableMessage{
|
||||
MessageInfoWrapper: &MessageInfoWrapper{
|
||||
Info: evt.Info,
|
||||
wa: wa,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) {
|
||||
var evtType bridgev2.RemoteEventType
|
||||
switch evt.Type {
|
||||
case types.ReceiptTypeRead, types.ReceiptTypeReadSelf:
|
||||
evtType = bridgev2.RemoteEventReadReceipt
|
||||
case types.ReceiptTypeDelivered:
|
||||
evtType = bridgev2.RemoteEventDeliveryReceipt
|
||||
case types.ReceiptTypeSender:
|
||||
fallthrough
|
||||
default:
|
||||
return
|
||||
}
|
||||
targets := make([]networkid.MessageID, len(evt.MessageIDs))
|
||||
for i, id := range evt.MessageIDs {
|
||||
targets[i] = waid.MakeMessageID(evt.Chat, *wa.Device.ID, id)
|
||||
}
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Receipt{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: evtType,
|
||||
PortalKey: wa.makeWAPortalKey(evt.Chat),
|
||||
Sender: wa.makeEventSender(evt.Sender),
|
||||
Timestamp: evt.Timestamp,
|
||||
},
|
||||
Targets: targets,
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAChatPresence(evt *events.ChatPresence) {
|
||||
typingType := bridgev2.TypingTypeText
|
||||
timeout := 15 * time.Second
|
||||
if evt.Media == types.ChatPresenceMediaAudio {
|
||||
typingType = bridgev2.TypingTypeRecordingMedia
|
||||
}
|
||||
if evt.State == types.ChatPresencePaused {
|
||||
timeout = 0
|
||||
}
|
||||
|
||||
wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Typing{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventTyping,
|
||||
LogContext: nil,
|
||||
PortalKey: wa.makeWAPortalKey(evt.Chat),
|
||||
Sender: wa.makeEventSender(evt.Sender),
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
Timeout: timeout,
|
||||
Type: typingType,
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onConnect bool) {
|
||||
errorCode := WAUnknownLogout
|
||||
if reason == events.ConnectFailureLoggedOut {
|
||||
errorCode = WALoggedOut
|
||||
} else if reason == events.ConnectFailureMainDeviceGone {
|
||||
errorCode = WAMainDeviceGone
|
||||
}
|
||||
wa.Client.Disconnect()
|
||||
wa.Client = nil
|
||||
wa.JID = types.EmptyJID
|
||||
wa.UserLogin.Metadata.(*UserLoginMetadata).WADeviceID = 0
|
||||
wa.UserLogin.BridgeState.Send(status.BridgeState{
|
||||
StateEvent: status.StateBadCredentials,
|
||||
Error: errorCode,
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWACallStart(sender types.JID, id, callType string, ts time.Time) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAIdentityChange(evt *events.IdentityChange) {
|
||||
// TODO
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue