mirror of
https://github.com/tulir/mautrix-whatsapp
synced 2024-11-17 15:31:26 +01:00
Add support for disappearing messages
This commit is contained in:
parent
a6adf61417
commit
18ea5af45e
12 changed files with 375 additions and 33 deletions
|
@ -5,6 +5,7 @@
|
|||
very obscure extensions in their mime type database.
|
||||
* Added support for personal filtering spaces (started by [@HelderFSFerreira] and [@clmnin] in [#413]).
|
||||
* Added support for multi-contact messages.
|
||||
* Added support for disappearing messages.
|
||||
* Fixed avatar remove events from WhatsApp being ignored.
|
||||
* Fixed the bridge using the wrong Olm session if a client established a new
|
||||
one due to corruption.
|
||||
|
|
|
@ -73,6 +73,8 @@ type BridgeConfig struct {
|
|||
AllowUserInvite bool `yaml:"allow_user_invite"`
|
||||
FederateRooms bool `yaml:"federate_rooms"`
|
||||
|
||||
DisappearingMessagesInGroups bool `yaml:"disappearing_messages_in_groups"`
|
||||
|
||||
CommandPrefix string `yaml:"command_prefix"`
|
||||
|
||||
ManagementRoomText struct {
|
||||
|
|
|
@ -102,6 +102,7 @@ func (helper *UpgradeHelper) doUpgrade() {
|
|||
helper.Copy(Bool, "bridge", "allow_user_invite")
|
||||
helper.Copy(Str, "bridge", "command_prefix")
|
||||
helper.Copy(Bool, "bridge", "federate_rooms")
|
||||
helper.Copy(Bool, "bridge", "disappearing_messages_in_groups")
|
||||
helper.Copy(Str, "bridge", "management_room_text", "welcome")
|
||||
helper.Copy(Str, "bridge", "management_room_text", "welcome_connected")
|
||||
helper.Copy(Str, "bridge", "management_room_text", "welcome_unconnected")
|
||||
|
|
|
@ -41,6 +41,8 @@ type Database struct {
|
|||
Portal *PortalQuery
|
||||
Puppet *PuppetQuery
|
||||
Message *MessageQuery
|
||||
|
||||
DisappearingMessage *DisappearingMessageQuery
|
||||
}
|
||||
|
||||
func New(dbType string, uri string, baseLog log.Logger) (*Database, error) {
|
||||
|
@ -70,6 +72,10 @@ func New(dbType string, uri string, baseLog log.Logger) (*Database, error) {
|
|||
db: db,
|
||||
log: db.log.Sub("Message"),
|
||||
}
|
||||
db.DisappearingMessage = &DisappearingMessageQuery{
|
||||
db: db,
|
||||
log: db.log.Sub("DisappearingMessage"),
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
|
|
140
database/disappearingmessage.go
Normal file
140
database/disappearingmessage.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2022 Tulir Asokan
|
||||
//
|
||||
// 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 database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type DisappearingMessageQuery struct {
|
||||
db *Database
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (dmq *DisappearingMessageQuery) New() *DisappearingMessage {
|
||||
return &DisappearingMessage{
|
||||
db: dmq.db,
|
||||
log: dmq.log,
|
||||
}
|
||||
}
|
||||
|
||||
func (dmq *DisappearingMessageQuery) NewWithValues(roomID id.RoomID, eventID id.EventID, expireIn time.Duration, startNow bool) *DisappearingMessage {
|
||||
dm := &DisappearingMessage{
|
||||
db: dmq.db,
|
||||
log: dmq.log,
|
||||
RoomID: roomID,
|
||||
EventID: eventID,
|
||||
ExpireIn: expireIn,
|
||||
}
|
||||
if startNow {
|
||||
dm.ExpireAt = time.Now().Add(dm.ExpireIn)
|
||||
}
|
||||
return dm
|
||||
}
|
||||
|
||||
const (
|
||||
getAllScheduledDisappearingMessagesQuery = `
|
||||
SELECT room_id, event_id, expire_in, expire_at FROM disappearing_message WHERE expire_at IS NOT NULL
|
||||
`
|
||||
startUnscheduledDisappearingMessagesInRoomQuery = `
|
||||
UPDATE disappearing_message SET expire_at=$1+expire_in WHERE room_id=$2 AND expire_at IS NULL
|
||||
RETURNING room_id, event_id, expire_in, expire_at
|
||||
`
|
||||
)
|
||||
|
||||
func (dmq *DisappearingMessageQuery) GetAllScheduled() (messages []*DisappearingMessage) {
|
||||
rows, err := dmq.db.Query(getAllScheduledDisappearingMessagesQuery)
|
||||
if err != nil || rows == nil {
|
||||
return nil
|
||||
}
|
||||
for rows.Next() {
|
||||
messages = append(messages, dmq.New().Scan(rows))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dmq *DisappearingMessageQuery) StartAllUnscheduledInRoom(roomID id.RoomID) (messages []*DisappearingMessage) {
|
||||
rows, err := dmq.db.Query(startUnscheduledDisappearingMessagesInRoomQuery, time.Now().UnixMilli(), roomID)
|
||||
if err != nil || rows == nil {
|
||||
return nil
|
||||
}
|
||||
for rows.Next() {
|
||||
messages = append(messages, dmq.New().Scan(rows))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type DisappearingMessage struct {
|
||||
db *Database
|
||||
log log.Logger
|
||||
|
||||
RoomID id.RoomID
|
||||
EventID id.EventID
|
||||
ExpireIn time.Duration
|
||||
ExpireAt time.Time
|
||||
}
|
||||
|
||||
func (msg *DisappearingMessage) Scan(row Scannable) *DisappearingMessage {
|
||||
var expireIn int64
|
||||
var expireAt sql.NullInt64
|
||||
err := row.Scan(&msg.RoomID, &msg.EventID, &expireIn, &expireAt)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
msg.log.Errorln("Database scan failed:", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
msg.ExpireIn = time.Duration(expireIn) * time.Millisecond
|
||||
if expireAt.Valid {
|
||||
msg.ExpireAt = time.UnixMilli(expireAt.Int64)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *DisappearingMessage) Insert() {
|
||||
var expireAt sql.NullInt64
|
||||
if !msg.ExpireAt.IsZero() {
|
||||
expireAt.Valid = true
|
||||
expireAt.Int64 = msg.ExpireAt.UnixMilli()
|
||||
}
|
||||
_, err := msg.db.Exec(`INSERT INTO disappearing_message (room_id, event_id, expire_in, expire_at) VALUES ($1, $2, $3, $4)`,
|
||||
msg.RoomID, msg.EventID, msg.ExpireIn.Milliseconds(), expireAt)
|
||||
if err != nil {
|
||||
msg.log.Warnfln("Failed to insert %s/%s: %v", msg.RoomID, msg.EventID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *DisappearingMessage) StartTimer() {
|
||||
msg.ExpireAt = time.Now().Add(msg.ExpireIn * time.Second)
|
||||
_, err := msg.db.Exec("UPDATE disappearing_message SET expire_at=$1 WHERE room_id=$2 AND event_id=$3", msg.ExpireAt.Unix(), msg.RoomID, msg.EventID)
|
||||
if err != nil {
|
||||
msg.log.Warnfln("Failed to update %s/%s: %v", msg.RoomID, msg.EventID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *DisappearingMessage) Delete() {
|
||||
_, err := msg.db.Exec("DELETE FROM disappearing_message WHERE room_id=$1 AND event_id=$2", msg.RoomID, msg.EventID)
|
||||
if err != nil {
|
||||
msg.log.Warnfln("Failed to delete %s/%s: %v", msg.RoomID, msg.EventID, err)
|
||||
}
|
||||
}
|
|
@ -140,11 +140,13 @@ type Portal struct {
|
|||
NextBatchID id.BatchID
|
||||
|
||||
RelayUserID id.UserID
|
||||
|
||||
ExpirationTime uint32
|
||||
}
|
||||
|
||||
func (portal *Portal) Scan(row Scannable) *Portal {
|
||||
var mxid, avatarURL, firstEventID, nextBatchID, relayUserID sql.NullString
|
||||
err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.Topic, &portal.Avatar, &avatarURL, &portal.Encrypted, &firstEventID, &nextBatchID, &relayUserID)
|
||||
err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.Topic, &portal.Avatar, &avatarURL, &portal.Encrypted, &firstEventID, &nextBatchID, &relayUserID, &portal.ExpirationTime)
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
portal.log.Errorln("Database scan failed:", err)
|
||||
|
@ -174,16 +176,16 @@ func (portal *Portal) relayUserPtr() *id.UserID {
|
|||
}
|
||||
|
||||
func (portal *Portal) Insert() {
|
||||
_, err := portal.db.Exec("INSERT INTO portal (jid, receiver, mxid, name, topic, avatar, avatar_url, encrypted, first_event_id, next_batch_id, relay_user_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
|
||||
portal.Key.JID, portal.Key.Receiver, portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted, portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr())
|
||||
_, err := portal.db.Exec("INSERT INTO portal (jid, receiver, mxid, name, topic, avatar, avatar_url, encrypted, first_event_id, next_batch_id, relay_user_id, expiration_time) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
|
||||
portal.Key.JID, portal.Key.Receiver, portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted, portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime)
|
||||
if err != nil {
|
||||
portal.log.Warnfln("Failed to insert %s: %v", portal.Key, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) Update() {
|
||||
_, err := portal.db.Exec("UPDATE portal SET mxid=$1, name=$2, topic=$3, avatar=$4, avatar_url=$5, encrypted=$6, first_event_id=$7, next_batch_id=$8, relay_user_id=$9 WHERE jid=$10 AND receiver=$11",
|
||||
portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted, portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.Key.JID, portal.Key.Receiver)
|
||||
_, err := portal.db.Exec("UPDATE portal SET mxid=$1, name=$2, topic=$3, avatar=$4, avatar_url=$5, encrypted=$6, first_event_id=$7, next_batch_id=$8, relay_user_id=$9, expiration_time=$10 WHERE jid=$11 AND receiver=$12",
|
||||
portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted, portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime, portal.Key.JID, portal.Key.Receiver)
|
||||
if err != nil {
|
||||
portal.log.Warnfln("Failed to update %s: %v", portal.Key, err)
|
||||
}
|
||||
|
|
20
database/upgrades/2022-01-07-disappearing-messages.go
Normal file
20
database/upgrades/2022-01-07-disappearing-messages.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package upgrades
|
||||
|
||||
import "database/sql"
|
||||
|
||||
func init() {
|
||||
upgrades[34] = upgrade{"Add support for disappearing messages", func(tx *sql.Tx, ctx context) error {
|
||||
_, err := tx.Exec(`ALTER TABLE portal ADD COLUMN expiration_time BIGINT NOT NULL DEFAULT 0 CHECK (expiration_time >= 0 AND expiration_time < 4294967296)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE disappearing_message (
|
||||
room_id TEXT,
|
||||
event_id TEXT,
|
||||
expire_in BIGINT NOT NULL,
|
||||
expire_at BIGINT,
|
||||
PRIMARY KEY (room_id, event_id)
|
||||
)`)
|
||||
return err
|
||||
}}
|
||||
}
|
|
@ -39,7 +39,7 @@ type upgrade struct {
|
|||
fn upgradeFunc
|
||||
}
|
||||
|
||||
const NumberOfUpgrades = 34
|
||||
const NumberOfUpgrades = 35
|
||||
|
||||
var upgrades [NumberOfUpgrades]upgrade
|
||||
|
||||
|
|
71
disappear.go
Normal file
71
disappear.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2022 Tulir Asokan
|
||||
//
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
)
|
||||
|
||||
func (portal *Portal) MarkDisappearing(eventID id.EventID, expiresIn uint32, startNow bool) {
|
||||
if expiresIn == 0 || (!portal.bridge.Config.Bridge.DisappearingMessagesInGroups && portal.IsGroupChat()) {
|
||||
return
|
||||
}
|
||||
|
||||
msg := portal.bridge.DB.DisappearingMessage.NewWithValues(portal.MXID, eventID, time.Duration(expiresIn)*time.Second, startNow)
|
||||
msg.Insert()
|
||||
if startNow {
|
||||
go portal.sleepAndDelete(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) ScheduleDisappearing() {
|
||||
if !portal.bridge.Config.Bridge.DisappearingMessagesInGroups && portal.IsGroupChat() {
|
||||
return
|
||||
}
|
||||
for _, msg := range portal.bridge.DB.DisappearingMessage.StartAllUnscheduledInRoom(portal.MXID) {
|
||||
go portal.sleepAndDelete(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (bridge *Bridge) RestartAllDisappearing() {
|
||||
for _, msg := range bridge.DB.DisappearingMessage.GetAllScheduled() {
|
||||
portal := bridge.GetPortalByMXID(msg.RoomID)
|
||||
go portal.sleepAndDelete(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) sleepAndDelete(msg *database.DisappearingMessage) {
|
||||
sleepTime := msg.ExpireAt.Sub(time.Now())
|
||||
portal.log.Debugfln("Sleeping for %s to make %s disappear", sleepTime, msg.EventID)
|
||||
time.Sleep(sleepTime)
|
||||
_, err := portal.MainIntent().RedactEvent(msg.RoomID, msg.EventID, mautrix.ReqRedact{
|
||||
Reason: "Message expired",
|
||||
TxnID: fmt.Sprintf("mxwa_disappear_%s", msg.EventID),
|
||||
})
|
||||
if err != nil {
|
||||
portal.log.Warnfln("Failed to make %s disappear: %v", msg.EventID, err)
|
||||
} else {
|
||||
portal.log.Debugfln("Disappeared %s", msg.EventID)
|
||||
}
|
||||
msg.Delete()
|
||||
}
|
|
@ -187,6 +187,10 @@ bridge:
|
|||
# Whether or not created rooms should have federation enabled.
|
||||
# If false, created portal rooms will never be federated.
|
||||
federate_rooms: true
|
||||
# Whether to enable disappearing messages in groups. If enabled, then the expiration time of
|
||||
# the messages will be determined by the first user to read the message, rather than individually.
|
||||
# If the bridge only has a single user, this can be turned on safely.
|
||||
disappearing_messages_in_groups: false
|
||||
|
||||
# The prefix for commands. Only required in non-management rooms.
|
||||
command_prefix: "!wa"
|
||||
|
|
1
main.go
1
main.go
|
@ -333,6 +333,7 @@ func (bridge *Bridge) Start() {
|
|||
if bridge.Config.Bridge.ResendBridgeInfo {
|
||||
go bridge.ResendBridgeInfo()
|
||||
}
|
||||
go bridge.RestartAllDisappearing()
|
||||
bridge.AS.Ready = true
|
||||
}
|
||||
|
||||
|
|
148
portal.go
148
portal.go
|
@ -300,6 +300,8 @@ func getMessageType(waMsg *waProto.Message) string {
|
|||
switch waMsg.GetProtocolMessage().GetType() {
|
||||
case waProto.ProtocolMessage_REVOKE:
|
||||
return "revoke"
|
||||
case waProto.ProtocolMessage_EPHEMERAL_SETTING:
|
||||
return "disappearing timer change"
|
||||
case waProto.ProtocolMessage_APP_STATE_SYNC_KEY_SHARE, waProto.ProtocolMessage_HISTORY_SYNC_NOTIFICATION, waProto.ProtocolMessage_INITIAL_SECURITY_NOTIFICATION_SETTING_SYNC:
|
||||
return "ignore"
|
||||
default:
|
||||
|
@ -342,6 +344,52 @@ func getMessageType(waMsg *waProto.Message) string {
|
|||
}
|
||||
}
|
||||
|
||||
func pluralUnit(val int, name string) string {
|
||||
if val == 1 {
|
||||
return fmt.Sprintf("%d %s", val, name)
|
||||
} else if val == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d %ss", val, name)
|
||||
}
|
||||
|
||||
func naturalJoin(parts []string) string {
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
} else if len(parts) == 1 {
|
||||
return parts[0]
|
||||
} else if len(parts) == 2 {
|
||||
return fmt.Sprintf("%s and %s", parts[0], parts[1])
|
||||
} else {
|
||||
return fmt.Sprintf("%s and %s", strings.Join(parts[:len(parts)-1], ", "), parts[len(parts)-1])
|
||||
}
|
||||
}
|
||||
|
||||
func formatDuration(d time.Duration) string {
|
||||
const Day = time.Hour * 24
|
||||
|
||||
var days, hours, minutes, seconds int
|
||||
days, d = int(d/Day), d%Day
|
||||
hours, d = int(d/time.Hour), d%time.Hour
|
||||
minutes, d = int(d/time.Minute), d%time.Minute
|
||||
seconds = int(d / time.Second)
|
||||
|
||||
parts := make([]string, 0, 4)
|
||||
if days > 0 {
|
||||
parts = append(parts, pluralUnit(days, "day"))
|
||||
}
|
||||
if hours > 0 {
|
||||
parts = append(parts, pluralUnit(hours, "hour"))
|
||||
}
|
||||
if minutes > 0 {
|
||||
parts = append(parts, pluralUnit(seconds, "minute"))
|
||||
}
|
||||
if seconds > 0 {
|
||||
parts = append(parts, pluralUnit(seconds, "second"))
|
||||
}
|
||||
return naturalJoin(parts)
|
||||
}
|
||||
|
||||
func (portal *Portal) convertMessage(intent *appservice.IntentAPI, source *User, info *types.MessageInfo, waMsg *waProto.Message) *ConvertedMessage {
|
||||
switch {
|
||||
case waMsg.Conversation != nil || waMsg.ExtendedTextMessage != nil:
|
||||
|
@ -366,6 +414,23 @@ func (portal *Portal) convertMessage(intent *appservice.IntentAPI, source *User,
|
|||
return portal.convertLiveLocationMessage(intent, waMsg.GetLiveLocationMessage())
|
||||
case waMsg.GroupInviteMessage != nil:
|
||||
return portal.convertGroupInviteMessage(intent, info, waMsg.GetGroupInviteMessage())
|
||||
case waMsg.ProtocolMessage != nil && waMsg.ProtocolMessage.GetType() == waProto.ProtocolMessage_EPHEMERAL_SETTING:
|
||||
portal.ExpirationTime = waMsg.ProtocolMessage.GetEphemeralExpiration()
|
||||
portal.Update()
|
||||
var msg string
|
||||
if portal.ExpirationTime == 0 {
|
||||
msg = "Turned off disappearing messages"
|
||||
} else {
|
||||
msg = fmt.Sprintf("Set the disappearing message timer to %s", formatDuration(time.Duration(portal.ExpirationTime)*time.Second))
|
||||
}
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: &event.MessageEventContent{
|
||||
Body: msg,
|
||||
MsgType: event.MsgNotice,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
@ -477,6 +542,7 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message) {
|
|||
}
|
||||
var eventID id.EventID
|
||||
if existingMsg != nil {
|
||||
portal.MarkDisappearing(existingMsg.MXID, converted.ExpiresIn, false)
|
||||
converted.Content.SetEdit(existingMsg.MXID)
|
||||
} else if len(converted.ReplyTo) > 0 {
|
||||
portal.SetReply(converted.Content, converted.ReplyTo)
|
||||
|
@ -485,6 +551,7 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message) {
|
|||
if err != nil {
|
||||
portal.log.Errorfln("Failed to send %s to Matrix: %v", msgID, err)
|
||||
} else {
|
||||
portal.MarkDisappearing(resp.EventID, converted.ExpiresIn, false)
|
||||
eventID = resp.EventID
|
||||
}
|
||||
// TODO figure out how to handle captions with undecryptable messages turning decryptable
|
||||
|
@ -493,6 +560,7 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message) {
|
|||
if err != nil {
|
||||
portal.log.Errorfln("Failed to send caption of %s to Matrix: %v", msgID, err)
|
||||
} else {
|
||||
portal.MarkDisappearing(resp.EventID, converted.ExpiresIn, false)
|
||||
eventID = resp.EventID
|
||||
}
|
||||
}
|
||||
|
@ -501,11 +569,13 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message) {
|
|||
resp, err = portal.sendMessage(converted.Intent, converted.Type, subEvt, nil, evt.Info.Timestamp.UnixMilli())
|
||||
if err != nil {
|
||||
portal.log.Errorfln("Failed to send sub-event %d of %s to Matrix: %v", index+1, msgID, err)
|
||||
} else {
|
||||
portal.MarkDisappearing(resp.EventID, converted.ExpiresIn, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(eventID) != 0 {
|
||||
portal.finishHandling(existingMsg, &evt.Info, resp.EventID, false)
|
||||
portal.finishHandling(existingMsg, &evt.Info, eventID, false)
|
||||
}
|
||||
} else if msgType == "revoke" {
|
||||
portal.HandleMessageRevoke(source, &evt.Info, evt.Message.GetProtocolMessage().GetKey())
|
||||
|
@ -1194,6 +1264,10 @@ func (portal *Portal) IsPrivateChat() bool {
|
|||
return portal.Key.JID.Server == types.DefaultUserServer
|
||||
}
|
||||
|
||||
func (portal *Portal) IsGroupChat() bool {
|
||||
return portal.Key.JID.Server == types.GroupServer
|
||||
}
|
||||
|
||||
func (portal *Portal) IsBroadcastList() bool {
|
||||
return portal.Key.JID.Server == types.BroadcastServer
|
||||
}
|
||||
|
@ -1337,7 +1411,8 @@ type ConvertedMessage struct {
|
|||
|
||||
MultiEvent []*event.MessageEventContent
|
||||
|
||||
ReplyTo types.MessageID
|
||||
ReplyTo types.MessageID
|
||||
ExpiresIn uint32
|
||||
}
|
||||
|
||||
func (portal *Portal) convertTextMessage(intent *appservice.IntentAPI, msg *waProto.Message) *ConvertedMessage {
|
||||
|
@ -1346,6 +1421,7 @@ func (portal *Portal) convertTextMessage(intent *appservice.IntentAPI, msg *waPr
|
|||
MsgType: event.MsgText,
|
||||
}
|
||||
var replyTo types.MessageID
|
||||
var expiresIn uint32
|
||||
if msg.GetExtendedTextMessage() != nil {
|
||||
content.Body = msg.GetExtendedTextMessage().GetText()
|
||||
|
||||
|
@ -1354,9 +1430,16 @@ func (portal *Portal) convertTextMessage(intent *appservice.IntentAPI, msg *waPr
|
|||
portal.bridge.Formatter.ParseWhatsApp(content, contextInfo.GetMentionedJid())
|
||||
replyTo = contextInfo.GetStanzaId()
|
||||
}
|
||||
expiresIn = contextInfo.GetExpiration()
|
||||
}
|
||||
|
||||
return &ConvertedMessage{Intent: intent, Type: event.EventMessage, Content: content, ReplyTo: replyTo}
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: replyTo,
|
||||
ExpiresIn: expiresIn,
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) convertLiveLocationMessage(intent *appservice.IntentAPI, msg *waProto.LiveLocationMessage) *ConvertedMessage {
|
||||
|
@ -1368,10 +1451,11 @@ func (portal *Portal) convertLiveLocationMessage(intent *appservice.IntentAPI, m
|
|||
content.Body += ": " + msg.GetCaption()
|
||||
}
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
ExpiresIn: msg.GetContextInfo().GetExpiration(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1419,10 +1503,11 @@ func (portal *Portal) convertLocationMessage(intent *appservice.IntentAPI, msg *
|
|||
}
|
||||
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
ExpiresIn: msg.GetContextInfo().GetExpiration(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1447,11 +1532,12 @@ func (portal *Portal) convertGroupInviteMessage(intent *appservice.IntentAPI, in
|
|||
},
|
||||
}
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
Extra: extraAttrs,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
Extra: extraAttrs,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
ExpiresIn: msg.GetContextInfo().GetExpiration(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1483,10 +1569,11 @@ func (portal *Portal) convertContactMessage(intent *appservice.IntentAPI, msg *w
|
|||
}
|
||||
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
Intent: intent,
|
||||
Type: event.EventMessage,
|
||||
Content: content,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
ExpiresIn: msg.GetContextInfo().GetExpiration(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1510,6 +1597,7 @@ func (portal *Portal) convertContactsArrayMessage(intent *appservice.IntentAPI,
|
|||
Body: fmt.Sprintf("Sent %s", name),
|
||||
},
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
ExpiresIn: msg.GetContextInfo().GetExpiration(),
|
||||
MultiEvent: contacts,
|
||||
}
|
||||
}
|
||||
|
@ -1815,12 +1903,13 @@ func (portal *Portal) convertMediaMessage(intent *appservice.IntentAPI, source *
|
|||
}
|
||||
|
||||
return &ConvertedMessage{
|
||||
Intent: intent,
|
||||
Type: eventType,
|
||||
Content: content,
|
||||
Caption: captionContent,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
Extra: extraContent,
|
||||
Intent: intent,
|
||||
Type: eventType,
|
||||
Content: content,
|
||||
Caption: captionContent,
|
||||
ReplyTo: msg.GetContextInfo().GetStanzaId(),
|
||||
ExpiresIn: msg.GetContextInfo().GetExpiration(),
|
||||
Extra: extraContent,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2047,6 +2136,9 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) (*waP
|
|||
ctxInfo.QuotedMessage = &waProto.Message{Conversation: proto.String("")}
|
||||
}
|
||||
}
|
||||
if portal.ExpirationTime != 0 {
|
||||
ctxInfo.Expiration = proto.Uint32(portal.ExpirationTime)
|
||||
}
|
||||
relaybotFormatted := false
|
||||
if !sender.IsLoggedIn() || (portal.IsPrivateChat() && sender.JID.User != portal.Key.Receiver.User) {
|
||||
if !portal.HasRelaybot() {
|
||||
|
@ -2075,7 +2167,7 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) (*waP
|
|||
if content.MsgType == event.MsgEmote && !relaybotFormatted {
|
||||
text = "/me " + text
|
||||
}
|
||||
if ctxInfo.StanzaId != nil || ctxInfo.MentionedJid != nil {
|
||||
if ctxInfo.StanzaId != nil || ctxInfo.MentionedJid != nil || ctxInfo.Expiration != nil {
|
||||
msg.ExtendedTextMessage = &waProto.ExtendedTextMessage{
|
||||
Text: &text,
|
||||
ContextInfo: &ctxInfo,
|
||||
|
@ -2227,6 +2319,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
|
|||
if msg == nil {
|
||||
return
|
||||
}
|
||||
portal.MarkDisappearing(evt.ID, portal.ExpirationTime, true)
|
||||
info := portal.generateMessageInfo(sender)
|
||||
dbMsg := portal.markHandled(nil, info, evt.ID, false, true, false)
|
||||
portal.log.Debugln("Sending event", evt.ID, "to WhatsApp", info.ID)
|
||||
|
@ -2333,6 +2426,7 @@ func (portal *Portal) HandleMatrixReadReceipt(sender *User, eventID id.EventID,
|
|||
portal.log.Warnfln("Failed to mark %v as read by %s: %v", ids, sender.JID, err)
|
||||
}
|
||||
}
|
||||
portal.ScheduleDisappearing()
|
||||
}
|
||||
|
||||
func typingDiff(prev, new []id.UserID) (started, stopped []id.UserID) {
|
||||
|
|
Loading…
Reference in a new issue