2018-08-13 22:24:44 +02:00
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
2022-05-22 15:15:54 +02:00
// Copyright (C) 2022 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 (
2019-05-15 22:04:09 +02:00
"fmt"
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"
2022-05-22 00:06:30 +02:00
"maunium.net/go/mautrix/bridge"
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
2022-05-22 15:15:54 +02:00
func ( br * WABridge ) CreatePrivatePortal ( roomID id . RoomID , brInviter bridge . User , brGhost bridge . Ghost ) {
inviter := brInviter . ( * User )
puppet := brGhost . ( * Puppet )
key := database . NewPortalKey ( puppet . JID , inviter . JID )
portal := br . GetPortalByJID ( key )
2020-07-10 14:23:32 +02:00
if len ( portal . MXID ) == 0 {
2022-05-22 15:15:54 +02:00
br . createPrivatePortalFromInvite ( roomID , inviter , puppet , portal )
2020-07-10 14:23:32 +02:00
return
}
2022-07-12 11:24:31 +02:00
ok := portal . ensureUserInvited ( inviter )
if ! ok {
br . Log . Warnfln ( "Failed to invite %s to existing private chat portal %s with %s. Redirecting portal to new room..." , inviter . MXID , portal . MXID , puppet . JID )
2022-05-22 15:15:54 +02:00
br . 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 )
2022-05-22 15:15:54 +02:00
br . 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 )
}
2022-05-22 15:15:54 +02:00
func ( br * WABridge ) createPrivatePortalFromInvite ( roomID id . RoomID , inviter * User , puppet * Puppet , portal * Portal ) {
2022-10-07 20:01:04 +02:00
// TODO check if room is already encrypted
var existingEncryption event . EncryptionEventContent
var encryptionEnabled bool
err := portal . MainIntent ( ) . StateEvent ( roomID , event . StateEncryption , "" , & existingEncryption )
if err != nil {
portal . log . Warnfln ( "Failed to check if encryption is enabled in private chat room %s" , roomID )
} else {
encryptionEnabled = existingEncryption . Algorithm == id . AlgorithmMegolmV1
}
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 )
2022-10-07 20:01:04 +02:00
if portal . bridge . Config . Bridge . PrivateChatPortalMeta || br . Config . Bridge . Encryption . Default || encryptionEnabled {
2020-07-05 17:57:03 +02:00
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 ( )
2022-10-07 20:01:04 +02:00
if br . Config . Bridge . Encryption . Default || encryptionEnabled {
2022-05-22 15:15:54 +02:00
_ , err := intent . InviteUser ( roomID , & mautrix . ReqInviteUser { UserID : br . Bot . UserID } )
2020-07-05 17:57:03 +02:00
if err != nil {
portal . log . Warnln ( "Failed to invite bridge bot to enable e2be:" , err )
}
2022-05-22 15:15:54 +02:00
err = br . Bot . EnsureJoined ( roomID )
2020-07-05 17:57:03 +02:00
if err != nil {
portal . log . Warnln ( "Failed to join as bridge bot to enable e2be:" , err )
}
2022-10-07 20:01:04 +02:00
if ! encryptionEnabled {
_ , err = intent . SendStateEvent ( roomID , event . StateEncryption , "" , portal . GetEncryptionEventContent ( ) )
if err != nil {
portal . log . Warnln ( "Failed to enable e2be:" , err )
}
2020-07-05 17:57:03 +02:00
}
2022-05-22 15:15:54 +02:00
br . AS . StateStore . SetMembership ( roomID , inviter . MXID , event . MembershipJoin )
br . AS . StateStore . SetMembership ( roomID , puppet . MXID , event . MembershipJoin )
br . AS . StateStore . SetMembership ( roomID , br . Bot . UserID , event . MembershipJoin )
2020-07-05 17:57:03 +02:00
portal . Encrypted = true
}
2022-05-13 01:56:40 +02:00
portal . Update ( nil )
2020-07-05 17:57:03 +02:00
portal . UpdateBridgeInfo ( )
_ , _ = intent . SendNotice ( roomID , "Private chat portal created" )
}
2022-05-22 15:15:54 +02:00
func ( br * WABridge ) HandlePresence ( evt * event . Event ) {
user := br . GetUserByMXIDIfExists ( evt . Sender )
2021-12-07 15:02:51 +01:00
if user == nil || ! user . IsLoggedIn ( ) {
return
}
2022-05-22 15:15:54 +02:00
customPuppet := br . GetPuppetByCustomMXID ( user . MXID )
2021-12-07 15:02:51 +01:00
// 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 )
}
}
}