mirror of
https://github.com/mautrix/whatsapp.git
synced 2025-03-14 14:15:38 +00:00
handlewhatsapp: implement various special event types
Includes: * Identity change notices * Call start notices * Delete chat for me events * Delete message for me events * Mark chat as read events
This commit is contained in:
parent
3ccafa4dc9
commit
acc6017308
7 changed files with 125 additions and 13 deletions
2
go.mod
2
go.mod
|
@ -22,7 +22,7 @@ require (
|
|||
golang.org/x/sync v0.8.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
maunium.net/go/mautrix v0.21.1-0.20240925131409-0c7f701828f0
|
||||
maunium.net/go/mautrix v0.21.1-0.20240926091654-7a5f15b03c2e
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
4
go.sum
4
go.sum
|
@ -114,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||
maunium.net/go/mautrix v0.21.1-0.20240925131409-0c7f701828f0 h1:nX8gIPDHf2UXkcGMX982mtdTFXgbXtJGIDUQta+mRbE=
|
||||
maunium.net/go/mautrix v0.21.1-0.20240925131409-0c7f701828f0/go.mod h1:qm9oDhcHxF/Xby5RUuONIGpXw1SXXqLZj/GgvMxJxu0=
|
||||
maunium.net/go/mautrix v0.21.1-0.20240926091654-7a5f15b03c2e h1:elQExbBoPGaI0jKMt2IzhpGT7NoTUqZCFxNSvRw0s1A=
|
||||
maunium.net/go/mautrix v0.21.1-0.20240926091654-7a5f15b03c2e/go.mod h1:qm9oDhcHxF/Xby5RUuONIGpXw1SXXqLZj/GgvMxJxu0=
|
||||
|
|
|
@ -103,7 +103,7 @@ func applyHistoryInfo(info *bridgev2.ChatInfo, conv *wadb.Conversation) {
|
|||
} else if ptr.Val(conv.Archived) {
|
||||
info.UserLocal.Tag = ptr.Ptr(event.RoomTagLowPriority)
|
||||
}
|
||||
if ptr.Val(conv.EphemeralExpiration) > 0 {
|
||||
if info.Disappear == nil && ptr.Val(conv.EphemeralExpiration) > 0 {
|
||||
info.Disappear = &database.DisappearingSetting{
|
||||
Type: database.DisappearingTypeAfterRead,
|
||||
Timer: time.Duration(*conv.EphemeralExpiration) * time.Second,
|
||||
|
|
|
@ -34,6 +34,7 @@ type Config struct {
|
|||
DisplaynameTemplate string `yaml:"displayname_template"`
|
||||
|
||||
CallStartNotices bool `yaml:"call_start_notices"`
|
||||
IdentityChangeNotices bool `yaml:"identity_change_notices"`
|
||||
SendPresenceOnTyping bool `yaml:"send_presence_on_typing"`
|
||||
EnableStatusBroadcast bool `yaml:"enable_status_broadcast"`
|
||||
DisableStatusBroadcastSend bool `yaml:"disable_status_broadcast_send"`
|
||||
|
@ -91,6 +92,7 @@ func upgradeConfig(helper up.Helper) {
|
|||
helper.Copy(up.Str, "displayname_template")
|
||||
|
||||
helper.Copy(up.Bool, "call_start_notices")
|
||||
helper.Copy(up.Bool, "identity_change_notices")
|
||||
helper.Copy(up.Bool, "send_presence_on_typing")
|
||||
helper.Copy(up.Bool, "enable_status_broadcast")
|
||||
helper.Copy(up.Bool, "disable_status_broadcast_send")
|
||||
|
|
|
@ -21,6 +21,8 @@ displayname_template: "{{or .FullName .BusinessName .PushName .Phone}} (WA)"
|
|||
|
||||
# Should incoming calls send a message to the Matrix room?
|
||||
call_start_notices: true
|
||||
# Should another user's cryptographic identity changing send a message to Matrix?
|
||||
identity_change_notices: false
|
||||
# Send the presence as "available" to whatsapp when users start typing on a portal.
|
||||
# This works as a workaround for homeservers that do not support presence, and allows
|
||||
# users to see when the whatsapp user on the other side is typing during a conversation.
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package connector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -12,6 +14,7 @@ import (
|
|||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||
"maunium.net/go/mautrix/bridgev2/simplevent"
|
||||
"maunium.net/go/mautrix/event"
|
||||
|
||||
"maunium.net/go/mautrix-whatsapp/pkg/waid"
|
||||
)
|
||||
|
@ -72,14 +75,11 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
|
|||
case *events.IdentityChange:
|
||||
wa.handleWAIdentityChange(evt)
|
||||
case *events.MarkChatAsRead:
|
||||
// TODO
|
||||
//wa.handleWAMarkChatAsRead(evt)
|
||||
wa.handleWAMarkChatAsRead(evt)
|
||||
case *events.DeleteForMe:
|
||||
// TODO
|
||||
//wa.handleWADeleteForMe(evt)
|
||||
wa.handleWADeleteForMe(evt)
|
||||
case *events.DeleteChat:
|
||||
// TODO
|
||||
//wa.handleWADeleteChat(evt)
|
||||
wa.handleWADeleteChat(evt)
|
||||
case *events.Mute:
|
||||
// TODO
|
||||
case *events.Archive:
|
||||
|
@ -330,9 +330,110 @@ func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onC
|
|||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWACallStart(sender types.JID, id, callType string, ts time.Time) {
|
||||
// TODO
|
||||
if !wa.Main.Config.CallStartNotices {
|
||||
return
|
||||
}
|
||||
wa.UserLogin.QueueRemoteEvent(&simplevent.Message[string]{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventMessage,
|
||||
LogContext: nil,
|
||||
PortalKey: wa.makeWAPortalKey(sender),
|
||||
Sender: wa.makeEventSender(sender),
|
||||
CreatePortal: true,
|
||||
Timestamp: ts,
|
||||
},
|
||||
Data: callType,
|
||||
ID: waid.MakeFakeMessageID(sender, sender, "call-"+id),
|
||||
ConvertMessageFunc: convertCallStart,
|
||||
})
|
||||
}
|
||||
|
||||
func convertCallStart(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, callType string) (*bridgev2.ConvertedMessage, error) {
|
||||
text := "Incoming call. Use the WhatsApp app to answer."
|
||||
if callType != "" {
|
||||
text = fmt.Sprintf("Incoming %s call. Use the WhatsApp app to answer.", callType)
|
||||
}
|
||||
return &bridgev2.ConvertedMessage{
|
||||
Parts: []*bridgev2.ConvertedMessagePart{{
|
||||
Type: event.EventMessage,
|
||||
Content: &event.MessageEventContent{
|
||||
MsgType: event.MsgText,
|
||||
Body: text,
|
||||
},
|
||||
}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAIdentityChange(evt *events.IdentityChange) {
|
||||
// TODO
|
||||
if !wa.Main.Config.IdentityChangeNotices {
|
||||
return
|
||||
}
|
||||
wa.UserLogin.QueueRemoteEvent(&simplevent.Message[*events.IdentityChange]{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventMessage,
|
||||
LogContext: nil,
|
||||
PortalKey: wa.makeWAPortalKey(evt.JID),
|
||||
Sender: wa.makeEventSender(evt.JID),
|
||||
CreatePortal: false,
|
||||
Timestamp: evt.Timestamp,
|
||||
},
|
||||
Data: evt,
|
||||
ID: waid.MakeFakeMessageID(evt.JID, evt.JID, "idchange-"+strconv.FormatInt(evt.Timestamp.UnixMilli(), 10)),
|
||||
ConvertMessageFunc: convertIdentityChange,
|
||||
})
|
||||
}
|
||||
|
||||
func convertIdentityChange(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, data *events.IdentityChange) (*bridgev2.ConvertedMessage, error) {
|
||||
ghost, err := portal.Bridge.GetGhostByID(ctx, waid.MakeUserID(data.JID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
text := fmt.Sprintf("Your security code with %s changed.", ghost.Name)
|
||||
if data.Implicit {
|
||||
text = fmt.Sprintf("Your security code with %s (device #%d) changed.", ghost.Name, data.JID.Device)
|
||||
}
|
||||
return &bridgev2.ConvertedMessage{
|
||||
Parts: []*bridgev2.ConvertedMessagePart{{
|
||||
Type: event.EventMessage,
|
||||
Content: &event.MessageEventContent{
|
||||
MsgType: event.MsgNotice,
|
||||
Body: text,
|
||||
},
|
||||
}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) {
|
||||
wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventChatDelete,
|
||||
PortalKey: wa.makeWAPortalKey(evt.JID),
|
||||
Timestamp: evt.Timestamp,
|
||||
},
|
||||
OnlyForMe: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) {
|
||||
wa.UserLogin.QueueRemoteEvent(&simplevent.MessageRemove{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventMessageRemove,
|
||||
PortalKey: wa.makeWAPortalKey(evt.ChatJID),
|
||||
Timestamp: evt.Timestamp,
|
||||
},
|
||||
TargetMessage: waid.MakeMessageID(evt.ChatJID, evt.SenderJID, evt.MessageID),
|
||||
OnlyForMe: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (wa *WhatsAppClient) handleWAMarkChatAsRead(evt *events.MarkChatAsRead) {
|
||||
wa.UserLogin.QueueRemoteEvent(&simplevent.Receipt{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventReadReceipt,
|
||||
PortalKey: wa.makeWAPortalKey(evt.JID),
|
||||
Sender: wa.makeEventSender(wa.JID),
|
||||
Timestamp: evt.Timestamp,
|
||||
},
|
||||
ReadUpTo: evt.Timestamp,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -60,6 +60,10 @@ func MakeMessageID(chat, sender types.JID, id types.MessageID) networkid.Message
|
|||
return networkid.MessageID(fmt.Sprintf("%s:%s:%s", chat.ToNonAD().String(), sender.ToNonAD().String(), id))
|
||||
}
|
||||
|
||||
func MakeFakeMessageID(chat, sender types.JID, data string) networkid.MessageID {
|
||||
return networkid.MessageID(fmt.Sprintf("fake:%s:%s:%s", chat.ToNonAD().String(), sender.ToNonAD().String(), data))
|
||||
}
|
||||
|
||||
type ParsedMessageID struct {
|
||||
Chat types.JID
|
||||
Sender types.JID
|
||||
|
@ -69,6 +73,9 @@ type ParsedMessageID struct {
|
|||
func ParseMessageID(messageID networkid.MessageID) (*ParsedMessageID, error) {
|
||||
parts := strings.SplitN(string(messageID), ":", 3)
|
||||
if len(parts) == 3 {
|
||||
if parts[0] == "fake" || strings.HasPrefix(parts[2], "FAKE::") {
|
||||
return nil, fmt.Errorf("fake message ID")
|
||||
}
|
||||
chat, err := types.ParseJID(parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -79,6 +86,6 @@ func ParseMessageID(messageID networkid.MessageID) (*ParsedMessageID, error) {
|
|||
}
|
||||
return &ParsedMessageID{Chat: chat, Sender: sender, ID: parts[2]}, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid message id")
|
||||
return nil, fmt.Errorf("invalid message ID")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue