Add basic support for incoming reactions
This commit is contained in:
parent
e1603932aa
commit
376f908a03
12 changed files with 404 additions and 234 deletions
|
@ -28,10 +28,11 @@ import (
|
|||
type Database struct {
|
||||
*dbutil.Database
|
||||
|
||||
User *UserQuery
|
||||
Portal *PortalQuery
|
||||
Puppet *PuppetQuery
|
||||
Message *MessageQuery
|
||||
User *UserQuery
|
||||
Portal *PortalQuery
|
||||
Puppet *PuppetQuery
|
||||
Message *MessageQuery
|
||||
Reaction *ReactionQuery
|
||||
}
|
||||
|
||||
func New(baseDB *dbutil.Database) *Database {
|
||||
|
@ -41,6 +42,7 @@ func New(baseDB *dbutil.Database) *Database {
|
|||
db.Portal = &PortalQuery{db: db}
|
||||
db.Puppet = &PuppetQuery{db: db}
|
||||
db.Message = &MessageQuery{db: db}
|
||||
db.Reaction = &ReactionQuery{db: db}
|
||||
return db
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
|
||||
"go.mau.fi/mautrix-gmessages/libgm/binary"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/util/dbutil"
|
||||
|
||||
"go.mau.fi/mautrix-gmessages/libgm/binary"
|
||||
)
|
||||
|
||||
type MessageQuery struct {
|
||||
|
@ -78,8 +77,7 @@ type MessageStatus struct {
|
|||
}
|
||||
|
||||
type Message struct {
|
||||
db *Database
|
||||
log log.Logger
|
||||
db *Database
|
||||
|
||||
Chat Key
|
||||
ID string
|
||||
|
|
127
database/reaction.go
Normal file
127
database/reaction.go
Normal 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
|
||||
}
|
|
@ -55,11 +55,25 @@ CREATE TABLE message (
|
|||
conv_id TEXT NOT NULL,
|
||||
conv_receiver BIGINT NOT NULL,
|
||||
id TEXT NOT NULL,
|
||||
mxid TEXT NOT NULL UNIQUE,
|
||||
mxid TEXT NOT NULL,
|
||||
sender TEXT NOT NULL,
|
||||
timestamp BIGINT NOT NULL,
|
||||
status jsonb NOT NULL,
|
||||
|
||||
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)
|
||||
)
|
||||
|
|
|
@ -21,3 +21,61 @@ func DecodeProtoMessage(data []byte, message proto.Message) error {
|
|||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,13 @@ message SendReactionResponse {
|
|||
}
|
||||
|
||||
message ReactionData {
|
||||
bytes emojiUnicode = 1;
|
||||
int64 emojiType = 2;
|
||||
string unicode = 1;
|
||||
EmojiType type = 2;
|
||||
}
|
||||
|
||||
message ReactionResponse {
|
||||
ReactionData data = 1;
|
||||
repeated string reactorParticipantsID = 2; // participants reacted with this emoji
|
||||
repeated string participantIDs = 2;
|
||||
}
|
||||
|
||||
message EmojiMeta {
|
||||
|
@ -35,11 +35,11 @@ message EmojiMeta {
|
|||
}
|
||||
|
||||
message EmojiMetaData {
|
||||
bytes emojiUnicode = 1;
|
||||
string unicode = 1;
|
||||
repeated string names = 2;
|
||||
}
|
||||
|
||||
enum Emojis {
|
||||
enum EmojiType {
|
||||
REACTION_TYPE_UNSPECIFIED = 0;
|
||||
LIKE = 1;
|
||||
LOVE = 2;
|
||||
|
|
|
@ -72,27 +72,27 @@ func (Reaction) EnumDescriptor() ([]byte, []int) {
|
|||
return file_reactions_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type Emojis int32
|
||||
type EmojiType int32
|
||||
|
||||
const (
|
||||
Emojis_REACTION_TYPE_UNSPECIFIED Emojis = 0
|
||||
Emojis_LIKE Emojis = 1
|
||||
Emojis_LOVE Emojis = 2
|
||||
Emojis_LAUGH Emojis = 3
|
||||
Emojis_SURPRISED Emojis = 4
|
||||
Emojis_SAD Emojis = 5
|
||||
Emojis_ANGRY Emojis = 6
|
||||
Emojis_DISLIKE Emojis = 7
|
||||
Emojis_CUSTOM Emojis = 8
|
||||
Emojis_QUESTIONING Emojis = 9
|
||||
Emojis_CRYING_FACE Emojis = 10
|
||||
Emojis_POUTING_FACE Emojis = 11
|
||||
Emojis_RED_HEART Emojis = 12
|
||||
EmojiType_REACTION_TYPE_UNSPECIFIED EmojiType = 0
|
||||
EmojiType_LIKE EmojiType = 1
|
||||
EmojiType_LOVE EmojiType = 2
|
||||
EmojiType_LAUGH EmojiType = 3
|
||||
EmojiType_SURPRISED EmojiType = 4
|
||||
EmojiType_SAD EmojiType = 5
|
||||
EmojiType_ANGRY EmojiType = 6
|
||||
EmojiType_DISLIKE EmojiType = 7
|
||||
EmojiType_CUSTOM EmojiType = 8
|
||||
EmojiType_QUESTIONING EmojiType = 9
|
||||
EmojiType_CRYING_FACE EmojiType = 10
|
||||
EmojiType_POUTING_FACE EmojiType = 11
|
||||
EmojiType_RED_HEART EmojiType = 12
|
||||
)
|
||||
|
||||
// Enum value maps for Emojis.
|
||||
// Enum value maps for EmojiType.
|
||||
var (
|
||||
Emojis_name = map[int32]string{
|
||||
EmojiType_name = map[int32]string{
|
||||
0: "REACTION_TYPE_UNSPECIFIED",
|
||||
1: "LIKE",
|
||||
2: "LOVE",
|
||||
|
@ -107,7 +107,7 @@ var (
|
|||
11: "POUTING_FACE",
|
||||
12: "RED_HEART",
|
||||
}
|
||||
Emojis_value = map[string]int32{
|
||||
EmojiType_value = map[string]int32{
|
||||
"REACTION_TYPE_UNSPECIFIED": 0,
|
||||
"LIKE": 1,
|
||||
"LOVE": 2,
|
||||
|
@ -124,30 +124,30 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func (x Emojis) Enum() *Emojis {
|
||||
p := new(Emojis)
|
||||
func (x EmojiType) Enum() *EmojiType {
|
||||
p := new(EmojiType)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x Emojis) String() string {
|
||||
func (x EmojiType) String() string {
|
||||
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()
|
||||
}
|
||||
|
||||
func (Emojis) Type() protoreflect.EnumType {
|
||||
func (EmojiType) Type() protoreflect.EnumType {
|
||||
return &file_reactions_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x Emojis) Number() protoreflect.EnumNumber {
|
||||
func (x EmojiType) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Emojis.Descriptor instead.
|
||||
func (Emojis) EnumDescriptor() ([]byte, []int) {
|
||||
// Deprecated: Use EmojiType.Descriptor instead.
|
||||
func (EmojiType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_reactions_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
|
@ -266,8 +266,8 @@ type ReactionData struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
EmojiUnicode []byte `protobuf:"bytes,1,opt,name=emojiUnicode,proto3" json:"emojiUnicode,omitempty"`
|
||||
EmojiType int64 `protobuf:"varint,2,opt,name=emojiType,proto3" json:"emojiType,omitempty"`
|
||||
Unicode string `protobuf:"bytes,1,opt,name=unicode,proto3" json:"unicode,omitempty"`
|
||||
Type EmojiType `protobuf:"varint,2,opt,name=type,proto3,enum=reactions.EmojiType" json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ReactionData) Reset() {
|
||||
|
@ -302,18 +302,18 @@ func (*ReactionData) Descriptor() ([]byte, []int) {
|
|||
return file_reactions_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ReactionData) GetEmojiUnicode() []byte {
|
||||
func (x *ReactionData) GetUnicode() string {
|
||||
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 {
|
||||
return x.EmojiType
|
||||
return x.Type
|
||||
}
|
||||
return 0
|
||||
return EmojiType_REACTION_TYPE_UNSPECIFIED
|
||||
}
|
||||
|
||||
type ReactionResponse struct {
|
||||
|
@ -321,8 +321,8 @@ type ReactionResponse struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
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
|
||||
Data *ReactionData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
|
||||
ParticipantIDs []string `protobuf:"bytes,2,rep,name=participantIDs,proto3" json:"participantIDs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ReactionResponse) Reset() {
|
||||
|
@ -364,9 +364,9 @@ func (x *ReactionResponse) GetData() *ReactionData {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *ReactionResponse) GetReactorParticipantsID() []string {
|
||||
func (x *ReactionResponse) GetParticipantIDs() []string {
|
||||
if x != nil {
|
||||
return x.ReactorParticipantsID
|
||||
return x.ParticipantIDs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -423,8 +423,8 @@ type EmojiMetaData struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
EmojiUnicode []byte `protobuf:"bytes,1,opt,name=emojiUnicode,proto3" json:"emojiUnicode,omitempty"`
|
||||
Names []string `protobuf:"bytes,2,rep,name=names,proto3" json:"names,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"`
|
||||
}
|
||||
|
||||
func (x *EmojiMetaData) Reset() {
|
||||
|
@ -459,11 +459,11 @@ func (*EmojiMetaData) Descriptor() ([]byte, []int) {
|
|||
return file_reactions_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *EmojiMetaData) GetEmojiUnicode() []byte {
|
||||
func (x *EmojiMetaData) GetUnicode() string {
|
||||
if x != nil {
|
||||
return x.EmojiUnicode
|
||||
return x.Unicode
|
||||
}
|
||||
return nil
|
||||
return ""
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x50,
|
||||
0x0a, 0x0c, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22,
|
||||
0x0a, 0x0c, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x55, 0x6e, 0x69, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65,
|
||||
0x22, 0x75, 0x0a, 0x10, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52,
|
||||
0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74,
|
||||
0x61, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74,
|
||||
0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x49, 0x44, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x15, 0x72, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69,
|
||||
0x70, 0x61, 0x6e, 0x74, 0x73, 0x49, 0x44, 0x22, 0x4b, 0x0a, 0x09, 0x45, 0x6d, 0x6f, 0x6a, 0x69,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x65,
|
||||
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x44, 0x61, 0x74, 0x61, 0x22, 0x49, 0x0a, 0x0d, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x55, 0x6e,
|
||||
0x69, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x6d, 0x6f,
|
||||
0x6a, 0x69, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2a,
|
||||
0x3c, 0x0a, 0x08, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0f, 0x0a, 0x0b, 0x55,
|
||||
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03,
|
||||
0x41, 0x44, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x10,
|
||||
0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x57, 0x49, 0x54, 0x43, 0x48, 0x10, 0x03, 0x2a, 0xc5, 0x01,
|
||||
0x0a, 0x06, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x43,
|
||||
0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
|
||||
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4b, 0x45, 0x10,
|
||||
0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x4f, 0x56, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c,
|
||||
0x41, 0x55, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x52, 0x50, 0x52, 0x49,
|
||||
0x53, 0x45, 0x44, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x41, 0x44, 0x10, 0x05, 0x12, 0x09,
|
||||
0x0a, 0x05, 0x41, 0x4e, 0x47, 0x52, 0x59, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53,
|
||||
0x4c, 0x49, 0x4b, 0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d,
|
||||
0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x51, 0x55, 0x45, 0x53, 0x54, 0x49, 0x4f, 0x4e, 0x49, 0x4e,
|
||||
0x47, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x52, 0x59, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x41,
|
||||
0x43, 0x45, 0x10, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x4f, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x5f,
|
||||
0x46, 0x41, 0x43, 0x45, 0x10, 0x0b, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x44, 0x5f, 0x48, 0x45,
|
||||
0x41, 0x52, 0x54, 0x10, 0x0c, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x62,
|
||||
0x69, 0x6e, 0x61, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
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, 0x18,
|
||||
0x0a, 0x07, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x2e, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79,
|
||||
0x70, 0x65, 0x22, 0x67, 0x0a, 0x10, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61,
|
||||
0x6e, 0x74, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72,
|
||||
0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x22, 0x4b, 0x0a, 0x09, 0x45,
|
||||
0x6d, 0x6f, 0x6a, 0x69, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0d, 0x65, 0x6d, 0x6f, 0x6a,
|
||||
0x69, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x18, 0x2e, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x6d, 0x6f, 0x6a,
|
||||
0x69, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x22, 0x3f, 0x0a, 0x0d, 0x45, 0x6d, 0x6f, 0x6a,
|
||||
0x69, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x6e, 0x69,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x6e, 0x69, 0x63,
|
||||
0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2a, 0x3c, 0x0a, 0x08, 0x52, 0x65, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
|
||||
0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x44, 0x44, 0x10, 0x01, 0x12,
|
||||
0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53,
|
||||
0x57, 0x49, 0x54, 0x43, 0x48, 0x10, 0x03, 0x2a, 0xc8, 0x01, 0x0a, 0x09, 0x45, 0x6d, 0x6f, 0x6a,
|
||||
0x69, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x43, 0x54, 0x49, 0x4f,
|
||||
0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
|
||||
0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4b, 0x45, 0x10, 0x01, 0x12, 0x08,
|
||||
0x0a, 0x04, 0x4c, 0x4f, 0x56, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x41, 0x55, 0x47,
|
||||
0x48, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x52, 0x50, 0x52, 0x49, 0x53, 0x45, 0x44,
|
||||
0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x41, 0x44, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x41,
|
||||
0x4e, 0x47, 0x52, 0x59, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x4c, 0x49, 0x4b,
|
||||
0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x08, 0x12,
|
||||
0x0f, 0x0a, 0x0b, 0x51, 0x55, 0x45, 0x53, 0x54, 0x49, 0x4f, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x09,
|
||||
0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x52, 0x59, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x41, 0x43, 0x45, 0x10,
|
||||
0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x4f, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x41, 0x43,
|
||||
0x45, 0x10, 0x0b, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x52, 0x54,
|
||||
0x10, 0x0c, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x62, 0x69, 0x6e, 0x61,
|
||||
0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
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_goTypes = []interface{}{
|
||||
(Reaction)(0), // 0: reactions.Reaction
|
||||
(Emojis)(0), // 1: reactions.Emojis
|
||||
(EmojiType)(0), // 1: reactions.EmojiType
|
||||
(*SendReactionPayload)(nil), // 2: reactions.SendReactionPayload
|
||||
(*SendReactionResponse)(nil), // 3: reactions.SendReactionResponse
|
||||
(*ReactionData)(nil), // 4: reactions.ReactionData
|
||||
|
@ -560,13 +559,14 @@ var file_reactions_proto_goTypes = []interface{}{
|
|||
var file_reactions_proto_depIdxs = []int32{
|
||||
4, // 0: reactions.SendReactionPayload.reactionData:type_name -> reactions.ReactionData
|
||||
0, // 1: reactions.SendReactionPayload.action:type_name -> reactions.Reaction
|
||||
4, // 2: reactions.ReactionResponse.data:type_name -> reactions.ReactionData
|
||||
7, // 3: reactions.EmojiMeta.emojiMetaData:type_name -> reactions.EmojiMetaData
|
||||
4, // [4:4] is the sub-list for method output_type
|
||||
4, // [4:4] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
1, // 2: reactions.ReactionData.type:type_name -> reactions.EmojiType
|
||||
4, // 3: reactions.ReactionResponse.data:type_name -> reactions.ReactionData
|
||||
7, // 4: reactions.EmojiMeta.emojiMetaData:type_name -> reactions.EmojiMetaData
|
||||
5, // [5:5] is the sub-list for method output_type
|
||||
5, // [5:5] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension 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() }
|
||||
|
|
|
@ -10,12 +10,7 @@ type Messages struct {
|
|||
client *Client
|
||||
}
|
||||
|
||||
func (m *Messages) React(reactionBuilder *ReactionBuilder) (*binary.SendReactionResponse, error) {
|
||||
payload, buildErr := reactionBuilder.Build()
|
||||
if buildErr != nil {
|
||||
return nil, buildErr
|
||||
}
|
||||
|
||||
func (m *Messages) React(payload *binary.SendReactionPayload) (*binary.SendReactionResponse, error) {
|
||||
actionType := binary.ActionType_SEND_REACTION
|
||||
|
||||
sentRequestId, sendErr := m.client.sessionHandler.completeSendMessage(actionType, true, payload)
|
||||
|
|
|
@ -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, // ❤️
|
||||
}
|
|
@ -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{}
|
||||
}
|
115
portal.go
115
portal.go
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/rs/zerolog"
|
||||
"maunium.net/go/maulogger/v2"
|
||||
"maunium.net/go/mautrix/util/variationselector"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/appservice"
|
||||
|
@ -358,7 +359,9 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) {
|
|||
return
|
||||
}
|
||||
eventTS := time.UnixMicro(evt.GetTimestamp())
|
||||
portal.lastMessageTS = eventTS
|
||||
if eventTS.After(portal.lastMessageTS) {
|
||||
portal.lastMessageTS = eventTS
|
||||
}
|
||||
log := portal.zlog.With().
|
||||
Str("message_id", evt.MessageID).
|
||||
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:
|
||||
log.Debug().Msg("Not handling incoming message that is auto downloading")
|
||||
return
|
||||
case binary.MessageStatusType_MESSAGE_DELETED:
|
||||
// TODO handle deletion
|
||||
return
|
||||
}
|
||||
if hasInProgressMedia(evt) {
|
||||
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 != "" {
|
||||
log.Debug().Str("event_id", evtID.String()).Msg("Got echo for outgoing message")
|
||||
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)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to check if message is duplicate")
|
||||
} else if existingMsg != nil {
|
||||
log.Debug().Msg("Not handling duplicate message")
|
||||
portal.syncReactions(ctx, source, existingMsg, evt.Reactions)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -408,6 +412,73 @@ func (portal *Portal) handleMessage(source *User, evt *binary.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 {
|
||||
Content *event.MessageEventContent
|
||||
Extra map[string]any
|
||||
|
@ -423,6 +494,24 @@ type ConvertedMessage struct {
|
|||
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 {
|
||||
log := zerolog.Ctx(ctx)
|
||||
|
||||
|
@ -430,21 +519,9 @@ func (portal *Portal) convertGoogleMessage(ctx context.Context, source *User, ev
|
|||
cm.SenderID = evt.ParticipantID
|
||||
cm.ID = evt.MessageID
|
||||
cm.Timestamp = time.UnixMicro(evt.Timestamp)
|
||||
|
||||
// TODO is there a fromMe flag?
|
||||
if evt.GetParticipantID() == portal.SelfUserID {
|
||||
cm.Intent = source.DoublePuppetIntent
|
||||
if cm.Intent == nil {
|
||||
log.Debug().Msg("Dropping message from self as double puppeting is not enabled")
|
||||
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)
|
||||
cm.Intent = portal.getIntent(ctx, source, evt.ParticipantID)
|
||||
if cm.Intent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var replyTo id.EventID
|
||||
|
|
1
user.go
1
user.go
|
@ -568,6 +568,7 @@ func (user *User) syncConversation(v *binary.Conversation) {
|
|||
portal := user.GetPortalByID(v.GetConversationID())
|
||||
if portal.MXID != "" {
|
||||
switch updateType {
|
||||
// TODO also delete if blocked?
|
||||
case binary.ConvUpdateTypes_DELETED:
|
||||
user.zlog.Info().Str("conversation_id", portal.ID).Msg("Got delete event, cleaning up portal")
|
||||
portal.Delete()
|
||||
|
|
Loading…
Reference in a new issue