2018-08-13 22:24:44 +02:00
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
2021-12-07 15:02:51 +01:00
// Copyright (C) 2021 Tulir Asokan
2018-08-13 00:00:23 +02:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package main
2018-08-13 22:24:44 +02:00
import (
2020-09-24 14:25:36 +02:00
"errors"
2019-05-15 22:04:09 +02:00
"fmt"
2018-08-26 16:02:32 +02:00
"strings"
2020-09-24 14:25:36 +02:00
"time"
2018-08-26 16:02:32 +02:00
2019-01-11 20:17:31 +01:00
"maunium.net/go/maulogger/v2"
2020-05-09 13:31:06 +02:00
2021-12-09 13:17:13 +01:00
"go.mau.fi/whatsmeow/types"
2020-09-24 14:25:36 +02:00
"maunium.net/go/mautrix"
2020-05-09 13:31:06 +02:00
"maunium.net/go/mautrix/appservice"
2020-05-08 21:32:22 +02:00
"maunium.net/go/mautrix/event"
2019-05-15 22:04:09 +02:00
"maunium.net/go/mautrix/format"
2020-05-08 21:32:22 +02:00
"maunium.net/go/mautrix/id"
2020-07-05 17:57:03 +02:00
"maunium.net/go/mautrix-whatsapp/database"
2018-08-13 22:24:44 +02:00
)
2018-08-13 00:00:23 +02:00
2018-08-18 21:57:08 +02:00
type MatrixHandler struct {
bridge * Bridge
as * appservice . AppService
log maulogger . Logger
cmd * CommandHandler
}
func NewMatrixHandler ( bridge * Bridge ) * MatrixHandler {
handler := & MatrixHandler {
bridge : bridge ,
2018-08-28 23:40:54 +02:00
as : bridge . AS ,
2018-08-18 21:57:08 +02:00
log : bridge . Log . Sub ( "Matrix" ) ,
cmd : NewCommandHandler ( bridge ) ,
}
2020-05-08 21:32:22 +02:00
bridge . EventProcessor . On ( event . EventMessage , handler . HandleMessage )
2020-05-09 01:03:59 +02:00
bridge . EventProcessor . On ( event . EventEncrypted , handler . HandleEncrypted )
2020-05-08 21:32:22 +02:00
bridge . EventProcessor . On ( event . EventSticker , handler . HandleMessage )
2021-11-09 14:12:03 +01:00
bridge . EventProcessor . On ( event . EventReaction , handler . HandleReaction )
2020-05-08 21:32:22 +02:00
bridge . EventProcessor . On ( event . EventRedaction , handler . HandleRedaction )
bridge . EventProcessor . On ( event . StateMember , handler . HandleMembership )
bridge . EventProcessor . On ( event . StateRoomName , handler . HandleRoomMetadata )
bridge . EventProcessor . On ( event . StateRoomAvatar , handler . HandleRoomMetadata )
bridge . EventProcessor . On ( event . StateTopic , handler . HandleRoomMetadata )
2020-05-09 01:03:59 +02:00
bridge . EventProcessor . On ( event . StateEncryption , handler . HandleEncryption )
2021-12-07 15:02:51 +01:00
bridge . EventProcessor . On ( event . EphemeralEventPresence , handler . HandlePresence )
bridge . EventProcessor . On ( event . EphemeralEventReceipt , handler . HandleReceipt )
bridge . EventProcessor . On ( event . EphemeralEventTyping , handler . HandleTyping )
2018-08-18 21:57:08 +02:00
return handler
}
2020-05-09 01:03:59 +02:00
func ( mx * MatrixHandler ) HandleEncryption ( evt * event . Event ) {
2021-06-25 14:33:37 +02:00
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
2020-05-09 01:03:59 +02:00
if evt . Content . AsEncryption ( ) . Algorithm != id . AlgorithmMegolmV1 {
return
}
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
if portal != nil && ! portal . Encrypted {
mx . log . Debugfln ( "%s enabled encryption in %s" , evt . Sender , evt . RoomID )
portal . Encrypted = true
portal . Update ( )
}
}
2020-07-05 17:57:03 +02:00
func ( mx * MatrixHandler ) joinAndCheckMembers ( evt * event . Event , intent * appservice . IntentAPI ) * mautrix . RespJoinedMembers {
2020-05-08 21:32:22 +02:00
resp , err := intent . JoinRoomByID ( evt . RoomID )
2018-08-16 14:59:18 +02:00
if err != nil {
2020-07-05 17:57:03 +02:00
mx . log . Debugfln ( "Failed to join room %s as %s with invite from %s: %v" , evt . RoomID , intent . UserID , evt . Sender , err )
return nil
2018-08-16 14:59:18 +02:00
}
2018-08-16 18:20:07 +02:00
members , err := intent . JoinedMembers ( resp . RoomID )
2018-08-16 14:59:18 +02:00
if err != nil {
2020-07-05 17:57:03 +02:00
mx . log . Debugfln ( "Failed to get members in room %s after accepting invite from %s as %s: %v" , resp . RoomID , evt . Sender , intent . UserID , err )
2020-06-25 16:01:40 +02:00
_ , _ = intent . LeaveRoom ( resp . RoomID )
2020-07-05 17:57:03 +02:00
return nil
2018-08-16 14:59:18 +02:00
}
if len ( members . Joined ) < 2 {
2020-07-05 17:57:03 +02:00
mx . log . Debugln ( "Leaving empty room" , resp . RoomID , "after accepting invite from" , evt . Sender , "as" , intent . UserID )
2020-06-25 16:01:40 +02:00
_ , _ = intent . LeaveRoom ( resp . RoomID )
2020-07-05 17:57:03 +02:00
return nil
}
return members
}
2021-10-28 12:58:20 +02:00
func ( mx * MatrixHandler ) sendNoticeWithMarkdown ( roomID id . RoomID , message string ) ( * mautrix . RespSendEvent , error ) {
intent := mx . as . BotIntent ( )
content := format . RenderMarkdown ( message , true , false )
content . MsgType = event . MsgNotice
return intent . SendMessageEvent ( roomID , event . EventMessage , content )
}
2020-07-05 17:57:03 +02:00
func ( mx * MatrixHandler ) HandleBotInvite ( evt * event . Event ) {
intent := mx . as . BotIntent ( )
user := mx . bridge . GetUserByMXID ( evt . Sender )
if user == nil {
return
}
members := mx . joinAndCheckMembers ( evt , intent )
if members == nil {
2018-08-16 14:59:18 +02:00
return
}
2018-08-16 23:11:28 +02:00
2018-08-26 16:08:37 +02:00
if ! user . Whitelisted {
2020-07-05 17:57:03 +02:00
_ , _ = intent . SendNotice ( evt . RoomID , "You are not whitelisted to use this bridge.\n" +
2018-08-26 16:08:37 +02:00
"If you're the owner of this bridge, see the bridge.permissions section in your config file." )
2020-07-05 17:57:03 +02:00
_ , _ = intent . LeaveRoom ( evt . RoomID )
2018-08-26 16:08:37 +02:00
return
}
2021-10-28 12:58:20 +02:00
_ , _ = mx . sendNoticeWithMarkdown ( evt . RoomID , mx . bridge . Config . Bridge . ManagementRoomText . Welcome )
2021-10-28 13:01:03 +02:00
if len ( members . Joined ) == 2 && ( len ( user . ManagementRoom ) == 0 || evt . Content . AsMember ( ) . IsDirect ) {
2020-07-05 17:57:03 +02:00
user . SetManagementRoom ( evt . RoomID )
2021-10-28 12:58:20 +02:00
_ , _ = intent . SendNotice ( user . ManagementRoom , "This room has been registered as your bridge management/status room." )
2020-07-05 17:57:03 +02:00
mx . log . Debugln ( evt . RoomID , "registered as a management room with" , evt . Sender )
}
2021-10-28 12:58:20 +02:00
if evt . RoomID == user . ManagementRoom {
if user . HasSession ( ) {
_ , _ = mx . sendNoticeWithMarkdown ( evt . RoomID , mx . bridge . Config . Bridge . ManagementRoomText . WelcomeConnected )
} else {
_ , _ = mx . sendNoticeWithMarkdown ( evt . RoomID , mx . bridge . Config . Bridge . ManagementRoomText . WelcomeUnconnected )
}
additionalHelp := mx . bridge . Config . Bridge . ManagementRoomText . AdditionalHelp
if len ( additionalHelp ) > 0 {
_ , _ = mx . sendNoticeWithMarkdown ( evt . RoomID , additionalHelp )
}
}
2020-07-05 17:57:03 +02:00
}
2020-07-10 14:23:32 +02:00
func ( mx * MatrixHandler ) handlePrivatePortal ( roomID id . RoomID , inviter * User , puppet * Puppet , key database . PortalKey ) {
portal := mx . bridge . GetPortalByJID ( key )
if len ( portal . MXID ) == 0 {
mx . createPrivatePortalFromInvite ( roomID , inviter , puppet , portal )
return
}
2020-07-05 17:57:03 +02:00
err := portal . MainIntent ( ) . EnsureInvited ( portal . MXID , inviter . MXID )
if err != nil {
mx . log . Warnfln ( "Failed to invite %s to existing private chat portal %s with %s: %v. Redirecting portal to new room..." , inviter . MXID , portal . MXID , puppet . JID , err )
2020-07-10 14:23:32 +02:00
mx . createPrivatePortalFromInvite ( roomID , inviter , puppet , portal )
2020-07-05 17:57:03 +02:00
return
}
intent := puppet . DefaultIntent ( )
2021-02-11 12:19:54 +01:00
errorMessage := fmt . Sprintf ( "You already have a private chat portal with me at [%[1]s](https://matrix.to/#/%[1]s)" , portal . MXID )
errorContent := format . RenderMarkdown ( errorMessage , true , false )
_ , _ = intent . SendMessageEvent ( roomID , event . EventMessage , errorContent )
mx . log . Debugfln ( "Leaving private chat room %s as %s after accepting invite from %s as we already have chat with the user" , roomID , puppet . MXID , inviter . MXID )
2020-07-05 17:57:03 +02:00
_ , _ = intent . LeaveRoom ( roomID )
}
2020-07-10 14:23:32 +02:00
func ( mx * MatrixHandler ) createPrivatePortalFromInvite ( roomID id . RoomID , inviter * User , puppet * Puppet , portal * Portal ) {
2020-07-05 17:57:03 +02:00
portal . MXID = roomID
2021-06-01 14:28:15 +02:00
portal . Topic = PrivateChatTopic
2020-07-05 17:57:03 +02:00
_ , _ = portal . MainIntent ( ) . SetRoomTopic ( portal . MXID , portal . Topic )
if portal . bridge . Config . Bridge . PrivateChatPortalMeta {
portal . Name = puppet . Displayname
portal . AvatarURL = puppet . AvatarURL
portal . Avatar = puppet . Avatar
_ , _ = portal . MainIntent ( ) . SetRoomName ( portal . MXID , portal . Name )
_ , _ = portal . MainIntent ( ) . SetRoomAvatar ( portal . MXID , portal . AvatarURL )
} else {
portal . Name = ""
}
2021-02-11 12:19:54 +01:00
portal . log . Infofln ( "Created private chat portal in %s after invite from %s" , roomID , inviter . MXID )
2020-07-05 17:57:03 +02:00
intent := puppet . DefaultIntent ( )
if mx . bridge . Config . Bridge . Encryption . Default {
_ , err := intent . InviteUser ( roomID , & mautrix . ReqInviteUser { UserID : mx . bridge . Bot . UserID } )
if err != nil {
portal . log . Warnln ( "Failed to invite bridge bot to enable e2be:" , err )
}
err = mx . bridge . Bot . EnsureJoined ( roomID )
if err != nil {
portal . log . Warnln ( "Failed to join as bridge bot to enable e2be:" , err )
}
_ , err = intent . SendStateEvent ( roomID , event . StateEncryption , "" , & event . EncryptionEventContent { Algorithm : id . AlgorithmMegolmV1 } )
if err != nil {
portal . log . Warnln ( "Failed to enable e2be:" , err )
}
mx . as . StateStore . SetMembership ( roomID , inviter . MXID , event . MembershipJoin )
mx . as . StateStore . SetMembership ( roomID , puppet . MXID , event . MembershipJoin )
mx . as . StateStore . SetMembership ( roomID , mx . bridge . Bot . UserID , event . MembershipJoin )
portal . Encrypted = true
}
portal . Update ( )
portal . UpdateBridgeInfo ( )
_ , _ = intent . SendNotice ( roomID , "Private chat portal created" )
2021-10-22 19:14:34 +02:00
//err := portal.FillInitialHistory(inviter)
//if err != nil {
// portal.log.Errorln("Failed to fill history:", err)
//}
2020-07-05 17:57:03 +02:00
}
func ( mx * MatrixHandler ) HandlePuppetInvite ( evt * event . Event , inviter * User , puppet * Puppet ) {
intent := puppet . DefaultIntent ( )
members := mx . joinAndCheckMembers ( evt , intent )
if members == nil {
return
}
var hasBridgeBot , hasOtherUsers bool
for mxid , _ := range members . Joined {
if mxid == intent . UserID || mxid == inviter . MXID {
continue
} else if mxid == mx . bridge . Bot . UserID {
hasBridgeBot = true
} else {
hasOtherUsers = true
}
}
if ! hasBridgeBot && ! hasOtherUsers {
key := database . NewPortalKey ( puppet . JID , inviter . JID )
2020-07-10 14:23:32 +02:00
mx . handlePrivatePortal ( evt . RoomID , inviter , puppet , key )
2020-07-05 17:57:03 +02:00
} else if ! hasBridgeBot {
mx . log . Debugln ( "Leaving multi-user room" , evt . RoomID , "as" , puppet . MXID , "after accepting invite from" , evt . Sender )
_ , _ = intent . SendNotice ( evt . RoomID , "Please invite the bridge bot first if you want to bridge to a WhatsApp group." )
_ , _ = intent . LeaveRoom ( evt . RoomID )
} else {
_ , _ = intent . SendNotice ( evt . RoomID , "This puppet will remain inactive until this room is bridged to a WhatsApp group." )
2018-08-16 14:59:18 +02:00
}
}
2020-05-08 21:32:22 +02:00
func ( mx * MatrixHandler ) HandleMembership ( evt * event . Event ) {
2020-05-12 21:25:55 +02:00
if _ , isPuppet := mx . bridge . ParsePuppetMXID ( evt . Sender ) ; evt . Sender == mx . bridge . Bot . UserID || isPuppet {
return
}
2021-06-25 14:33:37 +02:00
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
2020-05-12 21:25:55 +02:00
2020-05-09 01:03:59 +02:00
if mx . bridge . Crypto != nil {
mx . bridge . Crypto . HandleMemberEvent ( evt )
}
2020-05-08 21:32:22 +02:00
content := evt . Content . AsMember ( )
if content . Membership == event . MembershipInvite && id . UserID ( evt . GetStateKey ( ) ) == mx . as . BotMXID ( ) {
2018-08-18 21:57:08 +02:00
mx . HandleBotInvite ( evt )
2020-06-25 22:33:11 +02:00
return
2018-08-16 23:11:28 +02:00
}
2019-05-16 19:14:32 +02:00
2021-02-17 00:21:30 +01:00
if mx . shouldIgnoreEvent ( evt ) {
return
}
2020-07-05 17:57:03 +02:00
user := mx . bridge . GetUserByMXID ( evt . Sender )
2021-10-22 19:14:34 +02:00
if user == nil || ! user . Whitelisted || ! user . IsLoggedIn ( ) {
2019-05-16 19:14:32 +02:00
return
}
2020-07-05 17:57:03 +02:00
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
if portal == nil {
puppet := mx . bridge . GetPuppetByMXID ( id . UserID ( evt . GetStateKey ( ) ) )
if content . Membership == event . MembershipInvite && puppet != nil {
mx . HandlePuppetInvite ( evt , user , puppet )
}
2019-05-16 19:14:32 +02:00
return
}
2020-06-25 22:58:35 +02:00
isSelf := id . UserID ( evt . GetStateKey ( ) ) == evt . Sender
2020-05-08 21:32:22 +02:00
if content . Membership == event . MembershipLeave {
2021-11-01 11:52:51 +01:00
if evt . Unsigned . PrevContent != nil {
_ = evt . Unsigned . PrevContent . ParseRaw ( evt . Type )
prevContent , ok := evt . Unsigned . PrevContent . Parsed . ( * event . MemberEventContent )
if ok && prevContent . Membership != "join" {
return
2019-05-16 19:14:32 +02:00
}
2021-11-01 11:52:51 +01:00
}
if isSelf {
2021-11-01 11:42:34 +01:00
portal . HandleMatrixLeave ( user )
2019-05-16 19:14:32 +02:00
} else {
portal . HandleMatrixKick ( user , evt )
}
2020-06-25 22:58:35 +02:00
} else if content . Membership == event . MembershipInvite && ! isSelf {
portal . HandleMatrixInvite ( user , evt )
2019-05-16 19:14:32 +02:00
}
2018-08-16 14:59:18 +02:00
}
2020-05-08 21:32:22 +02:00
func ( mx * MatrixHandler ) HandleRoomMetadata ( evt * event . Event ) {
2021-06-25 14:33:37 +02:00
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
2021-02-09 22:41:14 +01:00
if mx . shouldIgnoreEvent ( evt ) {
return
}
2020-05-09 01:03:59 +02:00
user := mx . bridge . GetUserByMXID ( evt . Sender )
2021-10-22 19:14:34 +02:00
if user == nil || ! user . Whitelisted || ! user . IsLoggedIn ( ) {
2018-08-26 16:02:32 +02:00
return
}
2018-08-28 23:40:54 +02:00
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
2018-08-26 16:02:32 +02:00
if portal == nil || portal . IsPrivateChat ( ) {
return
}
2021-02-09 22:41:14 +01:00
portal . HandleMatrixMeta ( user , evt )
2018-08-26 16:02:32 +02:00
}
2020-05-09 01:03:59 +02:00
func ( mx * MatrixHandler ) shouldIgnoreEvent ( evt * event . Event ) bool {
2018-09-01 22:38:03 +02:00
if _ , isPuppet := mx . bridge . ParsePuppetMXID ( evt . Sender ) ; evt . Sender == mx . bridge . Bot . UserID || isPuppet {
2020-05-09 01:03:59 +02:00
return true
2018-09-01 22:38:03 +02:00
}
2021-12-17 00:56:07 +01:00
if val , ok := evt . Content . Raw [ doublePuppetKey ] ; ok && val == doublePuppetValue && mx . bridge . GetPuppetByCustomMXID ( evt . Sender ) != nil {
2020-05-09 01:03:59 +02:00
return true
}
user := mx . bridge . GetUserByMXID ( evt . Sender )
2021-10-28 13:03:55 +02:00
if ! user . RelayWhitelisted {
2020-05-09 01:03:59 +02:00
return true
}
return false
}
2020-10-05 21:32:35 +02:00
const sessionWaitTimeout = 5 * time . Second
2020-09-24 14:25:36 +02:00
2020-05-09 01:03:59 +02:00
func ( mx * MatrixHandler ) HandleEncrypted ( evt * event . Event ) {
2021-06-25 14:33:37 +02:00
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
2020-05-09 01:03:59 +02:00
if mx . shouldIgnoreEvent ( evt ) || mx . bridge . Crypto == nil {
2019-05-24 01:33:26 +02:00
return
}
2018-09-01 22:38:03 +02:00
2020-05-09 01:03:59 +02:00
decrypted , err := mx . bridge . Crypto . Decrypt ( evt )
2021-12-04 01:50:55 +01:00
decryptionRetryCount := 0
2020-10-05 21:32:35 +02:00
if errors . Is ( err , NoSessionFound ) {
2020-09-24 14:25:36 +02:00
content := evt . Content . AsEncrypted ( )
2020-10-05 21:32:35 +02:00
mx . log . Debugfln ( "Couldn't find session %s trying to decrypt %s, waiting %d seconds..." , content . SessionID , evt . ID , int ( sessionWaitTimeout . Seconds ( ) ) )
2021-12-07 15:14:56 +01:00
mx . as . SendErrorMessageSendCheckpoint ( evt , appservice . StepDecrypted , err , false , decryptionRetryCount )
decryptionRetryCount ++
2020-10-05 21:32:35 +02:00
if mx . bridge . Crypto . WaitForSession ( evt . RoomID , content . SenderKey , content . SessionID , sessionWaitTimeout ) {
mx . log . Debugfln ( "Got session %s after waiting, trying to decrypt %s again" , content . SessionID , evt . ID )
2020-09-24 14:25:36 +02:00
decrypted , err = mx . bridge . Crypto . Decrypt ( evt )
2020-10-05 21:32:35 +02:00
} else {
2021-12-04 01:50:55 +01:00
mx . as . SendErrorMessageSendCheckpoint ( evt , appservice . StepDecrypted , fmt . Errorf ( "didn't receive encryption keys" ) , false , decryptionRetryCount )
2020-10-05 21:32:35 +02:00
go mx . waitLongerForSession ( evt )
return
2020-09-24 14:25:36 +02:00
}
}
2020-05-09 01:03:59 +02:00
if err != nil {
2021-12-04 01:50:55 +01:00
mx . as . SendErrorMessageSendCheckpoint ( evt , appservice . StepDecrypted , err , true , decryptionRetryCount )
2021-11-17 22:52:16 +01:00
2020-05-09 19:23:30 +02:00
mx . log . Warnfln ( "Failed to decrypt %s: %v" , evt . ID , err )
2020-09-17 21:01:17 +02:00
_ , _ = mx . bridge . Bot . SendNotice ( evt . RoomID , fmt . Sprintf (
2020-10-05 21:32:35 +02:00
"\u26a0 Your message was not bridged: %v" , err ) )
2020-05-09 01:03:59 +02:00
return
}
2021-12-04 01:50:55 +01:00
mx . as . SendMessageSendCheckpoint ( decrypted , appservice . StepDecrypted , decryptionRetryCount )
2020-05-09 01:03:59 +02:00
mx . bridge . EventProcessor . Dispatch ( decrypted )
}
2018-08-18 21:57:08 +02:00
2020-10-05 21:32:35 +02:00
func ( mx * MatrixHandler ) waitLongerForSession ( evt * event . Event ) {
2021-12-03 17:24:24 +01:00
const extendedTimeout = sessionWaitTimeout * 3
2020-10-05 21:32:35 +02:00
content := evt . Content . AsEncrypted ( )
mx . log . Debugfln ( "Couldn't find session %s trying to decrypt %s, waiting %d more seconds..." ,
content . SessionID , evt . ID , int ( extendedTimeout . Seconds ( ) ) )
2021-12-03 17:24:24 +01:00
go mx . bridge . Crypto . RequestSession ( evt . RoomID , content . SenderKey , content . SessionID , evt . Sender , content . DeviceID )
2020-10-05 21:32:35 +02:00
resp , err := mx . bridge . Bot . SendNotice ( evt . RoomID , fmt . Sprintf (
"\u26a0 Your message was not bridged: the bridge hasn't received the decryption keys. " +
"The bridge will retry for %d seconds. If this error keeps happening, try restarting your client." ,
int ( extendedTimeout . Seconds ( ) ) ) )
if err != nil {
mx . log . Errorfln ( "Failed to send decryption error to %s: %v" , evt . RoomID , err )
}
update := event . MessageEventContent { MsgType : event . MsgNotice }
if mx . bridge . Crypto . WaitForSession ( evt . RoomID , content . SenderKey , content . SessionID , extendedTimeout ) {
mx . log . Debugfln ( "Got session %s after waiting more, trying to decrypt %s again" , content . SessionID , evt . ID )
decrypted , err := mx . bridge . Crypto . Decrypt ( evt )
2020-11-14 18:39:14 +01:00
if err == nil {
2021-12-07 15:14:56 +01:00
mx . as . SendMessageSendCheckpoint ( decrypted , appservice . StepDecrypted , 2 )
2020-10-05 21:32:35 +02:00
mx . bridge . EventProcessor . Dispatch ( decrypted )
_ , _ = mx . bridge . Bot . RedactEvent ( evt . RoomID , resp . EventID )
return
}
2021-11-17 22:52:16 +01:00
mx . log . Warnfln ( "Failed to decrypt %s: %v" , evt . ID , err )
2021-12-07 15:14:56 +01:00
mx . as . SendErrorMessageSendCheckpoint ( evt , appservice . StepDecrypted , err , true , 2 )
2020-10-05 21:32:35 +02:00
update . Body = fmt . Sprintf ( "\u26a0 Your message was not bridged: %v" , err )
} else {
2021-12-03 17:24:24 +01:00
mx . log . Debugfln ( "Didn't get %s, giving up on %s" , content . SessionID , evt . ID )
2021-12-07 15:14:56 +01:00
mx . as . SendErrorMessageSendCheckpoint ( evt , appservice . StepDecrypted , fmt . Errorf ( "didn't receive encryption keys" ) , true , 2 )
2020-10-05 21:32:35 +02:00
update . Body = "\u26a0 Your message was not bridged: the bridge hasn't received the decryption keys. " +
2021-12-03 17:24:24 +01:00
"If this error keeps happening, try restarting your client."
2020-10-05 21:32:35 +02:00
}
newContent := update
update . NewContent = & newContent
if resp != nil {
update . RelatesTo = & event . RelatesTo {
Type : event . RelReplace ,
EventID : resp . EventID ,
}
}
2021-11-17 22:52:16 +01:00
_ , err = mx . bridge . Bot . SendMessageEvent ( evt . RoomID , event . EventMessage , & update )
if err != nil {
mx . log . Debugfln ( "Failed to update decryption error notice %s: %v" , resp . EventID , err )
}
2020-10-05 21:32:35 +02:00
}
2020-05-09 01:03:59 +02:00
func ( mx * MatrixHandler ) HandleMessage ( evt * event . Event ) {
2021-06-25 14:33:37 +02:00
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
2020-05-09 01:03:59 +02:00
if mx . shouldIgnoreEvent ( evt ) {
2018-08-26 16:08:37 +02:00
return
}
2020-05-09 01:03:59 +02:00
user := mx . bridge . GetUserByMXID ( evt . Sender )
2021-11-01 15:46:03 +01:00
if user == nil {
return
}
2020-05-08 21:32:22 +02:00
content := evt . Content . AsMessage ( )
2021-10-31 19:42:53 +01:00
content . RemoveReplyFallback ( )
2020-05-08 21:32:22 +02:00
if user . Whitelisted && content . MsgType == event . MsgText {
2018-08-18 21:57:08 +02:00
commandPrefix := mx . bridge . Config . Bridge . CommandPrefix
2020-05-08 21:32:22 +02:00
hasCommandPrefix := strings . HasPrefix ( content . Body , commandPrefix )
2018-08-18 21:57:08 +02:00
if hasCommandPrefix {
2020-05-08 21:32:22 +02:00
content . Body = strings . TrimLeft ( content . Body [ len ( commandPrefix ) : ] , " " )
2018-08-18 21:57:08 +02:00
}
2020-05-08 21:32:22 +02:00
if hasCommandPrefix || evt . RoomID == user . ManagementRoom {
2021-10-31 19:42:53 +01:00
mx . cmd . Handle ( evt . RoomID , user , content . Body , content . GetReplyTo ( ) )
2018-08-18 21:57:08 +02:00
return
}
}
2020-05-08 21:32:22 +02:00
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
2019-11-10 20:22:11 +01:00
if portal != nil && ( user . Whitelisted || portal . HasRelaybot ( ) ) {
2021-12-14 16:47:30 +01:00
portal . matrixMessages <- PortalMatrixMessage { user : user , evt : evt }
2018-08-18 21:57:08 +02:00
}
2018-08-13 00:00:23 +02:00
}
2019-05-16 00:59:36 +02:00
2021-11-09 14:12:03 +01:00
func ( mx * MatrixHandler ) HandleReaction ( evt * event . Event ) {
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
if mx . shouldIgnoreEvent ( evt ) {
return
}
user := mx . bridge . GetUserByMXID ( evt . Sender )
2021-11-09 14:17:23 +01:00
if user == nil || ! user . RelayWhitelisted {
2021-11-09 14:12:03 +01:00
return
}
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
2021-11-09 14:17:23 +01:00
if portal != nil && ( user . Whitelisted || portal . HasRelaybot ( ) ) && mx . bridge . Config . Bridge . ReactionNotices {
_ , _ = portal . sendMainIntentMessage ( & event . MessageEventContent {
MsgType : event . MsgNotice ,
Body : fmt . Sprintf ( "\u26a0 Reactions are not yet supported by WhatsApp." ) ,
} )
2021-11-09 14:12:03 +01:00
}
}
2020-05-08 21:32:22 +02:00
func ( mx * MatrixHandler ) HandleRedaction ( evt * event . Event ) {
2021-06-25 14:33:37 +02:00
defer mx . bridge . Metrics . TrackMatrixEvent ( evt . Type ) ( )
2019-05-16 00:59:36 +02:00
2020-05-09 01:03:59 +02:00
user := mx . bridge . GetUserByMXID ( evt . Sender )
2021-11-01 15:46:03 +01:00
if user == nil {
2019-05-16 00:59:36 +02:00
return
}
2020-05-08 21:32:22 +02:00
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
2021-11-01 15:46:03 +01:00
if portal != nil && ( user . Whitelisted || portal . HasRelaybot ( ) ) {
2021-12-14 16:47:30 +01:00
portal . matrixMessages <- PortalMatrixMessage { user : user , evt : evt }
2019-05-16 00:59:36 +02:00
}
}
2021-12-07 15:02:51 +01:00
func ( mx * MatrixHandler ) HandlePresence ( evt * event . Event ) {
user := mx . bridge . GetUserByMXIDIfExists ( evt . Sender )
if user == nil || ! user . IsLoggedIn ( ) {
return
}
customPuppet := mx . bridge . GetPuppetByCustomMXID ( user . MXID )
// TODO move this flag to the user and/or portal data
if customPuppet != nil && ! customPuppet . EnablePresence {
return
}
presence := types . PresenceAvailable
if evt . Content . AsPresence ( ) . Presence != event . PresenceOnline {
presence = types . PresenceUnavailable
user . log . Debugln ( "Marking offline" )
} else {
user . log . Debugln ( "Marking online" )
}
user . lastPresence = presence
if user . Client . Store . PushName != "" {
err := user . Client . SendPresence ( presence )
if err != nil {
user . log . Warnln ( "Failed to set presence:" , err )
}
}
}
func ( mx * MatrixHandler ) HandleReceipt ( evt * event . Event ) {
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
if portal == nil {
return
}
for eventID , receipts := range * evt . Content . AsReceipt ( ) {
for userID , receipt := range receipts . Read {
if user := mx . bridge . GetUserByMXIDIfExists ( userID ) ; user == nil {
// Not a bridge user
} else if customPuppet := mx . bridge . GetPuppetByCustomMXID ( user . MXID ) ; customPuppet != nil && ! customPuppet . EnableReceipts {
// TODO move this flag to the user and/or portal data
continue
2021-12-15 12:51:20 +01:00
} else if val , ok := receipt . Extra [ doublePuppetKey ] . ( string ) ; ok && customPuppet != nil && val == doublePuppetValue {
2021-12-07 15:02:51 +01:00
// Ignore double puppeted read receipts.
user . log . Debugfln ( "Ignoring double puppeted read receipt %+v" , evt . Content . Raw )
} else {
portal . HandleMatrixReadReceipt ( user , eventID , time . UnixMilli ( receipt . Timestamp ) )
}
}
}
}
func ( mx * MatrixHandler ) HandleTyping ( evt * event . Event ) {
portal := mx . bridge . GetPortalByMXID ( evt . RoomID )
if portal == nil {
return
}
portal . HandleMatrixTyping ( evt . Content . AsTyping ( ) . UserIDs )
}