Add basic support for incoming reactions

This commit is contained in:
Tulir Asokan 2023-07-13 00:44:57 +03:00
parent e1603932aa
commit 376f908a03
12 changed files with 404 additions and 234 deletions

View file

@ -32,6 +32,7 @@ type Database struct {
Portal *PortalQuery Portal *PortalQuery
Puppet *PuppetQuery Puppet *PuppetQuery
Message *MessageQuery Message *MessageQuery
Reaction *ReactionQuery
} }
func New(baseDB *dbutil.Database) *Database { func New(baseDB *dbutil.Database) *Database {
@ -41,6 +42,7 @@ func New(baseDB *dbutil.Database) *Database {
db.Portal = &PortalQuery{db: db} db.Portal = &PortalQuery{db: db}
db.Puppet = &PuppetQuery{db: db} db.Puppet = &PuppetQuery{db: db}
db.Message = &MessageQuery{db: db} db.Message = &MessageQuery{db: db}
db.Reaction = &ReactionQuery{db: db}
return db return db
} }

View file

@ -24,11 +24,10 @@ import (
"strings" "strings"
"time" "time"
log "maunium.net/go/maulogger/v2"
"go.mau.fi/mautrix-gmessages/libgm/binary"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/util/dbutil" "maunium.net/go/mautrix/util/dbutil"
"go.mau.fi/mautrix-gmessages/libgm/binary"
) )
type MessageQuery struct { type MessageQuery struct {
@ -79,7 +78,6 @@ type MessageStatus struct {
type Message struct { type Message struct {
db *Database db *Database
log log.Logger
Chat Key Chat Key
ID string ID string

127
database/reaction.go Normal file
View file

@ -0,0 +1,127 @@
// mautrix-gmessages - A Matrix-Google Messages puppeting bridge.
// Copyright (C) 2023 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 (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/util/dbutil"
)
type ReactionQuery struct {
db *Database
}
func (rq *ReactionQuery) New() *Reaction {
return &Reaction{
db: rq.db,
}
}
func (rq *ReactionQuery) getDB() *Database {
return rq.db
}
const (
getReactionByIDQuery = `
SELECT conv_id, conv_receiver, msg_id, sender, reaction, mxid FROM reaction
WHERE conv_id=$1 AND conv_receiver=$2 AND msg_id=$3 AND sender=$4
`
getReactionByMXIDQuery = `
SELECT conv_id, conv_receiver, msg_id, sender, reaction, mxid FROM reaction
WHERE mxid=$1
`
getReactionsByMessageIDQuery = `
SELECT conv_id, conv_receiver, msg_id, sender, reaction, mxid FROM reaction
WHERE conv_id=$1 AND conv_receiver=$2 AND msg_id=$3
`
insertReaction = `
INSERT INTO reaction (conv_id, conv_receiver, msg_id, sender, reaction, mxid)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (conv_id, conv_receiver, msg_id, sender)
DO UPDATE SET reaction=excluded.reaction, mxid=excluded.mxid
`
)
func (rq *ReactionQuery) GetByID(ctx context.Context, chat Key, messageID, sender string) (*Reaction, error) {
return get[*Reaction](rq, ctx, getReactionByIDQuery, chat.ID, chat.Receiver, messageID, sender)
}
func (rq *ReactionQuery) GetByMXID(ctx context.Context, mxid id.EventID) (*Reaction, error) {
return get[*Reaction](rq, ctx, getReactionByMXIDQuery, mxid)
}
func (rq *ReactionQuery) GetAllByMessage(ctx context.Context, chat Key, messageID string) ([]*Reaction, error) {
return getAll[*Reaction](rq, ctx, getReactionsByMessageIDQuery, chat.ID, chat.Receiver, messageID)
}
type Reaction struct {
db *Database
Chat Key
MessageID string
Sender string
Reaction string
MXID id.EventID
}
func (r *Reaction) Scan(row dbutil.Scannable) (*Reaction, error) {
err := row.Scan(&r.Chat.ID, &r.Chat.Receiver, &r.MessageID, &r.Sender, &r.Reaction, &r.MXID)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
} else if err != nil {
return nil, err
}
return r, nil
}
func (r *Reaction) Insert(ctx context.Context) error {
_, err := r.db.Conn(ctx).ExecContext(ctx, insertReaction, r.Chat.ID, r.Chat.Receiver, r.MessageID, r.Sender, r.Reaction, r.MXID)
return err
}
func (rq *ReactionQuery) MassInsert(ctx context.Context, reactions []*Reaction) error {
valueStringFormat := "($1, $2, $%d, $%d, $%d, $%d)"
if rq.db.Dialect == dbutil.SQLite {
valueStringFormat = strings.ReplaceAll(valueStringFormat, "$", "?")
}
placeholders := make([]string, len(reactions))
params := make([]any, 2+len(reactions)*4)
params[0] = reactions[0].Chat.ID
params[1] = reactions[0].Chat.Receiver
for i, msg := range reactions {
baseIndex := 2 + i*4
params[baseIndex] = msg.MessageID
params[baseIndex+1] = msg.Sender
params[baseIndex+2] = msg.Reaction
params[baseIndex+3] = msg.MXID
placeholders[i] = fmt.Sprintf(valueStringFormat, baseIndex+1, baseIndex+2, baseIndex+3, baseIndex+4)
}
query := strings.Replace(insertReaction, "($1, $2, $3, $4, $5, $6)", strings.Join(placeholders, ","), 1)
_, err := rq.db.Conn(ctx).ExecContext(ctx, query, params...)
return err
}
func (r *Reaction) Delete(ctx context.Context) error {
_, err := r.db.Conn(ctx).ExecContext(ctx, "DELETE FROM reaction WHERE conv_id=$1 AND conv_receiver=$2 AND msg_id=$3 AND sender=$4", r.Chat.ID, r.Chat.Receiver, r.MessageID, r.Sender)
return err
}

View file

@ -55,11 +55,25 @@ CREATE TABLE message (
conv_id TEXT NOT NULL, conv_id TEXT NOT NULL,
conv_receiver BIGINT NOT NULL, conv_receiver BIGINT NOT NULL,
id TEXT NOT NULL, id TEXT NOT NULL,
mxid TEXT NOT NULL UNIQUE, mxid TEXT NOT NULL,
sender TEXT NOT NULL, sender TEXT NOT NULL,
timestamp BIGINT NOT NULL, timestamp BIGINT NOT NULL,
status jsonb NOT NULL, status jsonb NOT NULL,
PRIMARY KEY (conv_id, conv_receiver, id), PRIMARY KEY (conv_id, conv_receiver, id),
CONSTRAINT message_portal_fkey FOREIGN KEY (conv_id, conv_receiver) REFERENCES portal(id, receiver) ON DELETE CASCADE CONSTRAINT message_portal_fkey FOREIGN KEY (conv_id, conv_receiver) REFERENCES portal(id, receiver) ON DELETE CASCADE,
CONSTRAINT message_mxid_unique UNIQUE (mxid)
); );
CREATE TABLE reaction (
conv_id TEXT NOT NULL,
conv_receiver BIGINT NOT NULL,
msg_id TEXT NOT NULL,
sender TEXT NOT NULL,
reaction TEXT NOT NULL,
mxid TEXT NOT NULL,
PRIMARY KEY (conv_id, conv_receiver, msg_id, sender),
CONSTRAINT reaction_message_fkey FOREIGN KEY (conv_id, conv_receiver, msg_id) REFERENCES message(conv_id, conv_receiver, id) ON DELETE CASCADE,
CONSTRAINT reaction_mxid_unique UNIQUE (mxid)
)

View file

@ -21,3 +21,61 @@ func DecodeProtoMessage(data []byte, message proto.Message) error {
} }
return nil return nil
} }
func (et EmojiType) Unicode() string {
switch et {
case EmojiType_LIKE:
return "👍"
case EmojiType_LOVE:
return "😍"
case EmojiType_LAUGH:
return "😂"
case EmojiType_SURPRISED:
return "😮"
case EmojiType_SAD:
return "😥"
case EmojiType_ANGRY:
return "😠"
case EmojiType_DISLIKE:
return "👎"
case EmojiType_QUESTIONING:
return "🤔"
case EmojiType_CRYING_FACE:
return "😢"
case EmojiType_POUTING_FACE:
return "😡"
case EmojiType_RED_HEART:
return "❤️"
default:
return ""
}
}
func UnicodeToEmojiType(emoji string) EmojiType {
switch emoji {
case "👍":
return EmojiType_LIKE
case "😍":
return EmojiType_LOVE
case "😂":
return EmojiType_LAUGH
case "😮":
return EmojiType_SURPRISED
case "😥":
return EmojiType_SAD
case "😠":
return EmojiType_ANGRY
case "👎":
return EmojiType_DISLIKE
case "🤔":
return EmojiType_QUESTIONING
case "😢":
return EmojiType_CRYING_FACE
case "😡":
return EmojiType_POUTING_FACE
case "❤", "❤️":
return EmojiType_RED_HEART
default:
return EmojiType_CUSTOM
}
}

View file

@ -21,13 +21,13 @@ message SendReactionResponse {
} }
message ReactionData { message ReactionData {
bytes emojiUnicode = 1; string unicode = 1;
int64 emojiType = 2; EmojiType type = 2;
} }
message ReactionResponse { message ReactionResponse {
ReactionData data = 1; ReactionData data = 1;
repeated string reactorParticipantsID = 2; // participants reacted with this emoji repeated string participantIDs = 2;
} }
message EmojiMeta { message EmojiMeta {
@ -35,11 +35,11 @@ message EmojiMeta {
} }
message EmojiMetaData { message EmojiMetaData {
bytes emojiUnicode = 1; string unicode = 1;
repeated string names = 2; repeated string names = 2;
} }
enum Emojis { enum EmojiType {
REACTION_TYPE_UNSPECIFIED = 0; REACTION_TYPE_UNSPECIFIED = 0;
LIKE = 1; LIKE = 1;
LOVE = 2; LOVE = 2;

View file

@ -72,27 +72,27 @@ func (Reaction) EnumDescriptor() ([]byte, []int) {
return file_reactions_proto_rawDescGZIP(), []int{0} return file_reactions_proto_rawDescGZIP(), []int{0}
} }
type Emojis int32 type EmojiType int32
const ( const (
Emojis_REACTION_TYPE_UNSPECIFIED Emojis = 0 EmojiType_REACTION_TYPE_UNSPECIFIED EmojiType = 0
Emojis_LIKE Emojis = 1 EmojiType_LIKE EmojiType = 1
Emojis_LOVE Emojis = 2 EmojiType_LOVE EmojiType = 2
Emojis_LAUGH Emojis = 3 EmojiType_LAUGH EmojiType = 3
Emojis_SURPRISED Emojis = 4 EmojiType_SURPRISED EmojiType = 4
Emojis_SAD Emojis = 5 EmojiType_SAD EmojiType = 5
Emojis_ANGRY Emojis = 6 EmojiType_ANGRY EmojiType = 6
Emojis_DISLIKE Emojis = 7 EmojiType_DISLIKE EmojiType = 7
Emojis_CUSTOM Emojis = 8 EmojiType_CUSTOM EmojiType = 8
Emojis_QUESTIONING Emojis = 9 EmojiType_QUESTIONING EmojiType = 9
Emojis_CRYING_FACE Emojis = 10 EmojiType_CRYING_FACE EmojiType = 10
Emojis_POUTING_FACE Emojis = 11 EmojiType_POUTING_FACE EmojiType = 11
Emojis_RED_HEART Emojis = 12 EmojiType_RED_HEART EmojiType = 12
) )
// Enum value maps for Emojis. // Enum value maps for EmojiType.
var ( var (
Emojis_name = map[int32]string{ EmojiType_name = map[int32]string{
0: "REACTION_TYPE_UNSPECIFIED", 0: "REACTION_TYPE_UNSPECIFIED",
1: "LIKE", 1: "LIKE",
2: "LOVE", 2: "LOVE",
@ -107,7 +107,7 @@ var (
11: "POUTING_FACE", 11: "POUTING_FACE",
12: "RED_HEART", 12: "RED_HEART",
} }
Emojis_value = map[string]int32{ EmojiType_value = map[string]int32{
"REACTION_TYPE_UNSPECIFIED": 0, "REACTION_TYPE_UNSPECIFIED": 0,
"LIKE": 1, "LIKE": 1,
"LOVE": 2, "LOVE": 2,
@ -124,30 +124,30 @@ var (
} }
) )
func (x Emojis) Enum() *Emojis { func (x EmojiType) Enum() *EmojiType {
p := new(Emojis) p := new(EmojiType)
*p = x *p = x
return p return p
} }
func (x Emojis) String() string { func (x EmojiType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
} }
func (Emojis) Descriptor() protoreflect.EnumDescriptor { func (EmojiType) Descriptor() protoreflect.EnumDescriptor {
return file_reactions_proto_enumTypes[1].Descriptor() return file_reactions_proto_enumTypes[1].Descriptor()
} }
func (Emojis) Type() protoreflect.EnumType { func (EmojiType) Type() protoreflect.EnumType {
return &file_reactions_proto_enumTypes[1] return &file_reactions_proto_enumTypes[1]
} }
func (x Emojis) Number() protoreflect.EnumNumber { func (x EmojiType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x) return protoreflect.EnumNumber(x)
} }
// Deprecated: Use Emojis.Descriptor instead. // Deprecated: Use EmojiType.Descriptor instead.
func (Emojis) EnumDescriptor() ([]byte, []int) { func (EmojiType) EnumDescriptor() ([]byte, []int) {
return file_reactions_proto_rawDescGZIP(), []int{1} return file_reactions_proto_rawDescGZIP(), []int{1}
} }
@ -266,8 +266,8 @@ type ReactionData struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
EmojiUnicode []byte `protobuf:"bytes,1,opt,name=emojiUnicode,proto3" json:"emojiUnicode,omitempty"` Unicode string `protobuf:"bytes,1,opt,name=unicode,proto3" json:"unicode,omitempty"`
EmojiType int64 `protobuf:"varint,2,opt,name=emojiType,proto3" json:"emojiType,omitempty"` Type EmojiType `protobuf:"varint,2,opt,name=type,proto3,enum=reactions.EmojiType" json:"type,omitempty"`
} }
func (x *ReactionData) Reset() { func (x *ReactionData) Reset() {
@ -302,18 +302,18 @@ func (*ReactionData) Descriptor() ([]byte, []int) {
return file_reactions_proto_rawDescGZIP(), []int{2} return file_reactions_proto_rawDescGZIP(), []int{2}
} }
func (x *ReactionData) GetEmojiUnicode() []byte { func (x *ReactionData) GetUnicode() string {
if x != nil { if x != nil {
return x.EmojiUnicode return x.Unicode
} }
return nil return ""
} }
func (x *ReactionData) GetEmojiType() int64 { func (x *ReactionData) GetType() EmojiType {
if x != nil { if x != nil {
return x.EmojiType return x.Type
} }
return 0 return EmojiType_REACTION_TYPE_UNSPECIFIED
} }
type ReactionResponse struct { type ReactionResponse struct {
@ -322,7 +322,7 @@ type ReactionResponse struct {
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Data *ReactionData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` Data *ReactionData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
ReactorParticipantsID []string `protobuf:"bytes,2,rep,name=reactorParticipantsID,proto3" json:"reactorParticipantsID,omitempty"` // participants reacted with this emoji ParticipantIDs []string `protobuf:"bytes,2,rep,name=participantIDs,proto3" json:"participantIDs,omitempty"`
} }
func (x *ReactionResponse) Reset() { func (x *ReactionResponse) Reset() {
@ -364,9 +364,9 @@ func (x *ReactionResponse) GetData() *ReactionData {
return nil return nil
} }
func (x *ReactionResponse) GetReactorParticipantsID() []string { func (x *ReactionResponse) GetParticipantIDs() []string {
if x != nil { if x != nil {
return x.ReactorParticipantsID return x.ParticipantIDs
} }
return nil return nil
} }
@ -423,7 +423,7 @@ type EmojiMetaData struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
EmojiUnicode []byte `protobuf:"bytes,1,opt,name=emojiUnicode,proto3" json:"emojiUnicode,omitempty"` Unicode string `protobuf:"bytes,1,opt,name=unicode,proto3" json:"unicode,omitempty"`
Names []string `protobuf:"bytes,2,rep,name=names,proto3" json:"names,omitempty"` Names []string `protobuf:"bytes,2,rep,name=names,proto3" json:"names,omitempty"`
} }
@ -459,11 +459,11 @@ func (*EmojiMetaData) Descriptor() ([]byte, []int) {
return file_reactions_proto_rawDescGZIP(), []int{5} return file_reactions_proto_rawDescGZIP(), []int{5}
} }
func (x *EmojiMetaData) GetEmojiUnicode() []byte { func (x *EmojiMetaData) GetUnicode() string {
if x != nil { if x != nil {
return x.EmojiUnicode return x.Unicode
} }
return nil return ""
} }
func (x *EmojiMetaData) GetNames() []string { func (x *EmojiMetaData) GetNames() []string {
@ -490,47 +490,46 @@ var file_reactions_proto_rawDesc = []byte{
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x30, 0x0a, 0x14, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x30, 0x0a, 0x14,
0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x50, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x52,
0x0a, 0x0c, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18,
0x0a, 0x0c, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x0a, 0x07, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x07, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x18, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f,
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x6e, 0x73, 0x2e, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79,
0x22, 0x75, 0x0a, 0x10, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x70, 0x65, 0x22, 0x67, 0x0a, 0x10, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64,
0x61, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61,
0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x49, 0x44, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72,
0x52, 0x15, 0x72, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x22, 0x4b, 0x0a, 0x09, 0x45,
0x70, 0x61, 0x6e, 0x74, 0x73, 0x49, 0x44, 0x22, 0x4b, 0x0a, 0x09, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0d, 0x65, 0x6d, 0x6f, 0x6a,
0x4d, 0x65, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x69, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x61, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x65, 0x18, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x6d, 0x6f, 0x6a,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x69, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69,
0x61, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x22, 0x3f, 0x0a, 0x0d, 0x45, 0x6d, 0x6f, 0x6a,
0x44, 0x61, 0x74, 0x61, 0x22, 0x49, 0x0a, 0x0d, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x69, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x6e, 0x69,
0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x55, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x6e, 0x69, 0x63,
0x69, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x6d, 0x6f, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
0x6a, 0x69, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2a, 0x3c, 0x0a, 0x08, 0x52, 0x65, 0x61,
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2a, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
0x3c, 0x0a, 0x08, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x44, 0x44, 0x10, 0x01, 0x12,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53,
0x41, 0x44, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x57, 0x49, 0x54, 0x43, 0x48, 0x10, 0x03, 0x2a, 0xc8, 0x01, 0x0a, 0x09, 0x45, 0x6d, 0x6f, 0x6a,
0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x57, 0x49, 0x54, 0x43, 0x48, 0x10, 0x03, 0x2a, 0xc5, 0x01, 0x69, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x43, 0x54, 0x49, 0x4f,
0x0a, 0x06, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x43, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4b, 0x45, 0x10, 0x01, 0x12, 0x08,
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4b, 0x45, 0x10, 0x0a, 0x04, 0x4c, 0x4f, 0x56, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x41, 0x55, 0x47,
0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x4f, 0x56, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x48, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x52, 0x50, 0x52, 0x49, 0x53, 0x45, 0x44,
0x41, 0x55, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x52, 0x50, 0x52, 0x49, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x41, 0x44, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x41,
0x53, 0x45, 0x44, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x41, 0x44, 0x10, 0x05, 0x12, 0x09, 0x4e, 0x47, 0x52, 0x59, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x4c, 0x49, 0x4b,
0x0a, 0x05, 0x41, 0x4e, 0x47, 0x52, 0x59, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x08, 0x12,
0x4c, 0x49, 0x4b, 0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x0f, 0x0a, 0x0b, 0x51, 0x55, 0x45, 0x53, 0x54, 0x49, 0x4f, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x09,
0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x51, 0x55, 0x45, 0x53, 0x54, 0x49, 0x4f, 0x4e, 0x49, 0x4e, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x52, 0x59, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x41, 0x43, 0x45, 0x10,
0x47, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x52, 0x59, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x41, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x4f, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x41, 0x43,
0x43, 0x45, 0x10, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x4f, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x45, 0x10, 0x0b, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x52, 0x54,
0x46, 0x41, 0x43, 0x45, 0x10, 0x0b, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x44, 0x5f, 0x48, 0x45, 0x10, 0x0c, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x62, 0x69, 0x6e, 0x61,
0x41, 0x52, 0x54, 0x10, 0x0c, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x62, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x69, 0x6e, 0x61, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -549,7 +548,7 @@ var file_reactions_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_reactions_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_reactions_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_reactions_proto_goTypes = []interface{}{ var file_reactions_proto_goTypes = []interface{}{
(Reaction)(0), // 0: reactions.Reaction (Reaction)(0), // 0: reactions.Reaction
(Emojis)(0), // 1: reactions.Emojis (EmojiType)(0), // 1: reactions.EmojiType
(*SendReactionPayload)(nil), // 2: reactions.SendReactionPayload (*SendReactionPayload)(nil), // 2: reactions.SendReactionPayload
(*SendReactionResponse)(nil), // 3: reactions.SendReactionResponse (*SendReactionResponse)(nil), // 3: reactions.SendReactionResponse
(*ReactionData)(nil), // 4: reactions.ReactionData (*ReactionData)(nil), // 4: reactions.ReactionData
@ -560,13 +559,14 @@ var file_reactions_proto_goTypes = []interface{}{
var file_reactions_proto_depIdxs = []int32{ var file_reactions_proto_depIdxs = []int32{
4, // 0: reactions.SendReactionPayload.reactionData:type_name -> reactions.ReactionData 4, // 0: reactions.SendReactionPayload.reactionData:type_name -> reactions.ReactionData
0, // 1: reactions.SendReactionPayload.action:type_name -> reactions.Reaction 0, // 1: reactions.SendReactionPayload.action:type_name -> reactions.Reaction
4, // 2: reactions.ReactionResponse.data:type_name -> reactions.ReactionData 1, // 2: reactions.ReactionData.type:type_name -> reactions.EmojiType
7, // 3: reactions.EmojiMeta.emojiMetaData:type_name -> reactions.EmojiMetaData 4, // 3: reactions.ReactionResponse.data:type_name -> reactions.ReactionData
4, // [4:4] is the sub-list for method output_type 7, // 4: reactions.EmojiMeta.emojiMetaData:type_name -> reactions.EmojiMetaData
4, // [4:4] is the sub-list for method input_type 5, // [5:5] is the sub-list for method output_type
4, // [4:4] is the sub-list for extension type_name 5, // [5:5] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension extendee 5, // [5:5] is the sub-list for extension type_name
0, // [0:4] is the sub-list for field type_name 5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
} }
func init() { file_reactions_proto_init() } func init() { file_reactions_proto_init() }

View file

@ -10,12 +10,7 @@ type Messages struct {
client *Client client *Client
} }
func (m *Messages) React(reactionBuilder *ReactionBuilder) (*binary.SendReactionResponse, error) { func (m *Messages) React(payload *binary.SendReactionPayload) (*binary.SendReactionResponse, error) {
payload, buildErr := reactionBuilder.Build()
if buildErr != nil {
return nil, buildErr
}
actionType := binary.ActionType_SEND_REACTION actionType := binary.ActionType_SEND_REACTION
sentRequestId, sendErr := m.client.sessionHandler.completeSendMessage(actionType, true, payload) sentRequestId, sendErr := m.client.sessionHandler.completeSendMessage(actionType, true, payload)

View file

@ -1,14 +0,0 @@
package metadata
var Emojis = map[string]int64{
"\U0001F44D": 1, // 👍
"\U0001F60D": 2, // 😍
"\U0001F602": 3, // 😂
"\U0001F62E": 4, // 😮
"\U0001F625": 5, // 😥
"\U0001F622": 10, // 😢
"\U0001F620": 6, // 😠
"\U0001F621": 11, // 😡
"\U0001F44E": 7, // 👎
"\U00002764": 12, // ❤️
}

View file

@ -1,88 +0,0 @@
package libgm
import (
"fmt"
"go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/metadata"
)
type ReactionBuilderError struct {
errMsg string
}
func (rbe *ReactionBuilderError) Error() string {
return fmt.Sprintf("Failed to build reaction builder: %s", rbe.errMsg)
}
type ReactionBuilder struct {
messageID string
emoji []byte
action binary.Reaction
emojiType int64
}
func (rb *ReactionBuilder) SetAction(action binary.Reaction) *ReactionBuilder {
rb.action = action
return rb
}
/*
Emoji is a unicode string like "\U0001F44D" or a string like "👍"
*/
func (rb *ReactionBuilder) SetEmoji(emoji string) *ReactionBuilder {
emojiType, exists := metadata.Emojis[emoji]
if exists {
rb.emojiType = emojiType
} else {
rb.emojiType = 8
}
rb.emoji = []byte(emoji)
return rb
}
func (rb *ReactionBuilder) SetMessageID(messageId string) *ReactionBuilder {
rb.messageID = messageId
return rb
}
func (rb *ReactionBuilder) Build() (*binary.SendReactionPayload, error) {
if rb.messageID == "" {
return nil, &ReactionBuilderError{
errMsg: "messageID can not be empty",
}
}
if rb.action == 0 {
return nil, &ReactionBuilderError{
errMsg: "action can not be empty",
}
}
if rb.emojiType == 0 {
return nil, &ReactionBuilderError{
errMsg: "failed to set emojiType",
}
}
if rb.emoji == nil {
return nil, &ReactionBuilderError{
errMsg: "failed to set emoji",
}
}
return &binary.SendReactionPayload{
MessageID: rb.messageID,
ReactionData: &binary.ReactionData{
EmojiUnicode: rb.emoji,
EmojiType: rb.emojiType,
},
Action: rb.action,
}, nil
}
func (c *Client) NewReactionBuilder() *ReactionBuilder {
return &ReactionBuilder{}
}

109
portal.go
View file

@ -30,6 +30,7 @@ import (
"github.com/gabriel-vasile/mimetype" "github.com/gabriel-vasile/mimetype"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"maunium.net/go/maulogger/v2" "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/util/variationselector"
"maunium.net/go/mautrix" "maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/appservice"
@ -358,7 +359,9 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) {
return return
} }
eventTS := time.UnixMicro(evt.GetTimestamp()) eventTS := time.UnixMicro(evt.GetTimestamp())
if eventTS.After(portal.lastMessageTS) {
portal.lastMessageTS = eventTS portal.lastMessageTS = eventTS
}
log := portal.zlog.With(). log := portal.zlog.With().
Str("message_id", evt.MessageID). Str("message_id", evt.MessageID).
Str("participant_id", evt.ParticipantID). Str("participant_id", evt.ParticipantID).
@ -369,6 +372,9 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) {
case binary.MessageStatusType_INCOMING_AUTO_DOWNLOADING, binary.MessageStatusType_INCOMING_RETRYING_AUTO_DOWNLOAD: case binary.MessageStatusType_INCOMING_AUTO_DOWNLOADING, binary.MessageStatusType_INCOMING_RETRYING_AUTO_DOWNLOAD:
log.Debug().Msg("Not handling incoming message that is auto downloading") log.Debug().Msg("Not handling incoming message that is auto downloading")
return return
case binary.MessageStatusType_MESSAGE_DELETED:
// TODO handle deletion
return
} }
if hasInProgressMedia(evt) { if hasInProgressMedia(evt) {
log.Debug().Msg("Not handling incoming message that doesn't have full media yet") log.Debug().Msg("Not handling incoming message that doesn't have full media yet")
@ -377,15 +383,13 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) {
if evtID := portal.isOutgoingMessage(evt); evtID != "" { if evtID := portal.isOutgoingMessage(evt); evtID != "" {
log.Debug().Str("event_id", evtID.String()).Msg("Got echo for outgoing message") log.Debug().Str("event_id", evtID.String()).Msg("Got echo for outgoing message")
return return
} else if portal.isRecentlyHandled(evt.MessageID) {
log.Debug().Msg("Not handling recent duplicate message")
return
} }
existingMsg, err := portal.bridge.DB.Message.GetByID(ctx, portal.Key, evt.MessageID) existingMsg, err := portal.bridge.DB.Message.GetByID(ctx, portal.Key, evt.MessageID)
if err != nil { if err != nil {
log.Err(err).Msg("Failed to check if message is duplicate") log.Err(err).Msg("Failed to check if message is duplicate")
} else if existingMsg != nil { } else if existingMsg != nil {
log.Debug().Msg("Not handling duplicate message") log.Debug().Msg("Not handling duplicate message")
portal.syncReactions(ctx, source, existingMsg, evt.Reactions)
return return
} }
@ -408,6 +412,73 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) {
log.Debug().Interface("event_ids", eventIDs).Msg("Handled message") log.Debug().Interface("event_ids", eventIDs).Msg("Handled message")
} }
func (portal *Portal) syncReactions(ctx context.Context, source *User, message *database.Message, reactions []*binary.ReactionResponse) {
log := zerolog.Ctx(ctx)
existing, err := portal.bridge.DB.Reaction.GetAllByMessage(ctx, portal.Key, message.ID)
if err != nil {
log.Err(err).Msg("Failed to get existing reactions from db to sync reactions")
return
}
remove := make(map[string]*database.Reaction, len(existing))
for _, reaction := range existing {
remove[reaction.Sender] = reaction
}
for _, reaction := range reactions {
emoji := reaction.GetData().GetUnicode()
if emoji == "" {
emoji = reaction.GetData().GetType().Unicode()
if emoji == "" {
continue
}
}
for _, participant := range reaction.GetParticipantIDs() {
dbReaction, ok := remove[participant]
if ok {
delete(remove, participant)
if dbReaction.Reaction == emoji {
continue
}
}
intent := portal.getIntent(ctx, source, participant)
if intent == nil {
continue
}
var resp *mautrix.RespSendEvent
resp, err = intent.SendReaction(portal.MXID, message.MXID, variationselector.Add(emoji))
if err != nil {
log.Err(err).Str("reaction_sender_id", participant).Msg("Failed to send reaction")
continue
}
if dbReaction == nil {
dbReaction = portal.bridge.DB.Reaction.New()
dbReaction.Chat = portal.Key
dbReaction.Sender = participant
dbReaction.MessageID = message.ID
} else if _, err = intent.RedactEvent(portal.MXID, dbReaction.MXID); err != nil {
log.Err(err).Str("reaction_sender_id", participant).Msg("Failed to redact old reaction after adding new one")
}
dbReaction.Reaction = emoji
dbReaction.MXID = resp.EventID
err = dbReaction.Insert(ctx)
if err != nil {
log.Err(err).Str("reaction_sender_id", participant).Msg("Failed to insert added reaction into db")
}
}
}
for _, reaction := range remove {
intent := portal.getIntent(ctx, source, reaction.Sender)
if intent == nil {
continue
}
_, err = intent.RedactEvent(portal.MXID, reaction.MXID)
if err != nil {
log.Err(err).Str("reaction_sender_id", reaction.Sender).Msg("Failed to redact removed reaction")
} else if err = reaction.Delete(ctx); err != nil {
log.Err(err).Str("reaction_sender_id", reaction.Sender).Msg("Failed to remove reaction from db")
}
}
}
type ConvertedMessagePart struct { type ConvertedMessagePart struct {
Content *event.MessageEventContent Content *event.MessageEventContent
Extra map[string]any Extra map[string]any
@ -423,6 +494,24 @@ type ConvertedMessage struct {
Parts []ConvertedMessagePart Parts []ConvertedMessagePart
} }
func (portal *Portal) getIntent(ctx context.Context, source *User, participant string) *appservice.IntentAPI {
if participant == portal.SelfUserID {
intent := source.DoublePuppetIntent
if intent == nil {
zerolog.Ctx(ctx).Debug().Msg("Dropping message from self as double puppeting is not enabled")
return nil
}
return intent
} else {
puppet := source.GetPuppetByID(participant, "")
if puppet == nil {
zerolog.Ctx(ctx).Debug().Str("participant_id", participant).Msg("Dropping message from unknown participant")
return nil
}
return puppet.IntentFor(portal)
}
}
func (portal *Portal) convertGoogleMessage(ctx context.Context, source *User, evt *binary.Message, backfill bool) *ConvertedMessage { func (portal *Portal) convertGoogleMessage(ctx context.Context, source *User, evt *binary.Message, backfill bool) *ConvertedMessage {
log := zerolog.Ctx(ctx) log := zerolog.Ctx(ctx)
@ -430,22 +519,10 @@ func (portal *Portal) convertGoogleMessage(ctx context.Context, source *User, ev
cm.SenderID = evt.ParticipantID cm.SenderID = evt.ParticipantID
cm.ID = evt.MessageID cm.ID = evt.MessageID
cm.Timestamp = time.UnixMicro(evt.Timestamp) cm.Timestamp = time.UnixMicro(evt.Timestamp)
cm.Intent = portal.getIntent(ctx, source, evt.ParticipantID)
// TODO is there a fromMe flag?
if evt.GetParticipantID() == portal.SelfUserID {
cm.Intent = source.DoublePuppetIntent
if cm.Intent == nil { if cm.Intent == nil {
log.Debug().Msg("Dropping message from self as double puppeting is not enabled")
return nil return nil
} }
} else {
puppet := source.GetPuppetByID(evt.ParticipantID, "")
if puppet == nil {
log.Debug().Str("participant_id", evt.ParticipantID).Msg("Dropping message from unknown participant")
return nil
}
cm.Intent = puppet.IntentFor(portal)
}
var replyTo id.EventID var replyTo id.EventID
if evt.GetReplyMessage() != nil { if evt.GetReplyMessage() != nil {

View file

@ -568,6 +568,7 @@ func (user *User) syncConversation(v *binary.Conversation) {
portal := user.GetPortalByID(v.GetConversationID()) portal := user.GetPortalByID(v.GetConversationID())
if portal.MXID != "" { if portal.MXID != "" {
switch updateType { switch updateType {
// TODO also delete if blocked?
case binary.ConvUpdateTypes_DELETED: case binary.ConvUpdateTypes_DELETED:
user.zlog.Info().Str("conversation_id", portal.ID).Msg("Got delete event, cleaning up portal") user.zlog.Info().Str("conversation_id", portal.ID).Msg("Got delete event, cleaning up portal")
portal.Delete() portal.Delete()