Implement avatars
This commit is contained in:
parent
75468e87d8
commit
a415444fc0
18 changed files with 529 additions and 460 deletions
|
@ -44,19 +44,19 @@ func (pq *PortalQuery) getDB() *Database {
|
|||
}
|
||||
|
||||
func (pq *PortalQuery) GetAll(ctx context.Context) ([]*Portal, error) {
|
||||
return getAll[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, avatar_id, avatar_mxc, avatar_set, encrypted, in_space FROM portal")
|
||||
return getAll[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space FROM portal")
|
||||
}
|
||||
|
||||
func (pq *PortalQuery) GetAllForUser(ctx context.Context, receiver int) ([]*Portal, error) {
|
||||
return getAll[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, avatar_id, avatar_mxc, avatar_set, encrypted, in_space FROM portal WHERE receiver=$1", receiver)
|
||||
return getAll[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space FROM portal WHERE receiver=$1", receiver)
|
||||
}
|
||||
|
||||
func (pq *PortalQuery) GetByKey(ctx context.Context, key Key) (*Portal, error) {
|
||||
return get[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, avatar_id, avatar_mxc, avatar_set, encrypted, in_space FROM portal WHERE id=$1 AND receiver=$2", key.ID, key.Receiver)
|
||||
return get[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space FROM portal WHERE id=$1 AND receiver=$2", key.ID, key.Receiver)
|
||||
}
|
||||
|
||||
func (pq *PortalQuery) GetByMXID(ctx context.Context, mxid id.RoomID) (*Portal, error) {
|
||||
return get[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, avatar_id, avatar_mxc, avatar_set, encrypted, in_space FROM portal WHERE mxid=$1", mxid)
|
||||
return get[*Portal](pq, ctx, "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space FROM portal WHERE mxid=$1", mxid)
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
|
@ -83,9 +83,6 @@ type Portal struct {
|
|||
Type gmproto.ConversationType
|
||||
Name string
|
||||
NameSet bool
|
||||
AvatarID string
|
||||
AvatarMXC id.ContentURI
|
||||
AvatarSet bool
|
||||
Encrypted bool
|
||||
InSpace bool
|
||||
}
|
||||
|
@ -93,7 +90,7 @@ type Portal struct {
|
|||
func (portal *Portal) Scan(row dbutil.Scannable) (*Portal, error) {
|
||||
var mxid, selfUserID, otherUserID sql.NullString
|
||||
var convType int
|
||||
err := row.Scan(&portal.ID, &portal.Receiver, &selfUserID, &otherUserID, &convType, &mxid, &portal.Name, &portal.NameSet, &portal.AvatarID, &portal.AvatarMXC, &portal.AvatarSet, &portal.Encrypted, &portal.InSpace)
|
||||
err := row.Scan(&portal.ID, &portal.Receiver, &selfUserID, &otherUserID, &convType, &mxid, &portal.Name, &portal.NameSet, &portal.Encrypted, &portal.InSpace)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
|
@ -117,13 +114,16 @@ func (portal *Portal) sqlVariables() []any {
|
|||
if portal.OtherUserID != "" {
|
||||
otherUserID = &portal.OtherUserID
|
||||
}
|
||||
return []any{portal.ID, portal.Receiver, selfUserID, otherUserID, int(portal.Type), mxid, portal.Name, portal.NameSet, portal.AvatarID, &portal.AvatarMXC, portal.AvatarSet, portal.Encrypted, portal.InSpace}
|
||||
return []any{
|
||||
portal.ID, portal.Receiver, selfUserID, otherUserID, int(portal.Type), mxid, portal.Name, portal.NameSet,
|
||||
portal.Encrypted, portal.InSpace,
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) Insert(ctx context.Context) error {
|
||||
_, err := portal.db.Conn(ctx).ExecContext(ctx, `
|
||||
INSERT INTO portal (id, receiver, self_user, other_user, type, mxid, name, name_set, avatar_id, avatar_mxc, avatar_set, encrypted, in_space)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
INSERT INTO portal (id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
`, portal.sqlVariables()...)
|
||||
return err
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func (portal *Portal) Insert(ctx context.Context) error {
|
|||
func (portal *Portal) Update(ctx context.Context) error {
|
||||
_, err := portal.db.Conn(ctx).ExecContext(ctx, `
|
||||
UPDATE portal
|
||||
SET self_user=$3, other_user=$4, type=$5, mxid=$6, name=$7, name_set=$8, avatar_id=$9, avatar_mxc=$10, avatar_set=$11, encrypted=$12, in_space=$13
|
||||
SET self_user=$3, other_user=$4, type=$5, mxid=$6, name=$7, name_set=$8, encrypted=$9, in_space=$10
|
||||
WHERE id=$1 AND receiver=$2
|
||||
`, portal.sqlVariables()...)
|
||||
return err
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/util/dbutil"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
@ -40,7 +41,7 @@ func (pq *PuppetQuery) getDB() *Database {
|
|||
}
|
||||
|
||||
func (pq *PuppetQuery) GetAll(ctx context.Context) ([]*Puppet, error) {
|
||||
return getAll[*Puppet](pq, ctx, "SELECT id, receiver, phone, name, name_set, avatar_id, avatar_mxc, avatar_set, contact_info_set FROM puppet")
|
||||
return getAll[*Puppet](pq, ctx, "SELECT id, receiver, phone, contact_id, name, name_set, avatar_hash, avatar_mxc, avatar_set, avatar_update_ts, contact_info_set FROM puppet")
|
||||
}
|
||||
|
||||
func (pq *PuppetQuery) DeleteAllForUser(ctx context.Context, userID int) error {
|
||||
|
@ -49,7 +50,7 @@ func (pq *PuppetQuery) DeleteAllForUser(ctx context.Context, userID int) error {
|
|||
}
|
||||
|
||||
func (pq *PuppetQuery) Get(ctx context.Context, key Key) (*Puppet, error) {
|
||||
return get[*Puppet](pq, ctx, "SELECT id, receiver, phone, name, name_set, avatar_id, avatar_mxc, avatar_set, contact_info_set FROM puppet WHERE id=$1 AND receiver=$2", key.ID, key.Receiver)
|
||||
return get[*Puppet](pq, ctx, "SELECT id, receiver, phone, contact_id, name, name_set, avatar_hash, avatar_mxc, avatar_set, avatar_update_ts, contact_info_set FROM puppet WHERE id=$1 AND receiver=$2", key.ID, key.Receiver)
|
||||
}
|
||||
|
||||
type Puppet struct {
|
||||
|
@ -57,32 +58,40 @@ type Puppet struct {
|
|||
|
||||
Key
|
||||
Phone string
|
||||
ContactID string
|
||||
Name string
|
||||
NameSet bool
|
||||
AvatarID string
|
||||
AvatarHash [32]byte
|
||||
AvatarMXC id.ContentURI
|
||||
AvatarSet bool
|
||||
AvatarUpdateTS time.Time
|
||||
ContactInfoSet bool
|
||||
}
|
||||
|
||||
func (puppet *Puppet) Scan(row dbutil.Scannable) (*Puppet, error) {
|
||||
err := row.Scan(&puppet.ID, &puppet.Receiver, &puppet.Phone, &puppet.Name, &puppet.NameSet, &puppet.AvatarID, &puppet.AvatarMXC, &puppet.AvatarSet, &puppet.ContactInfoSet)
|
||||
var avatarHash []byte
|
||||
var avatarUpdateTS int64
|
||||
err := row.Scan(&puppet.ID, &puppet.Receiver, &puppet.Phone, &puppet.ContactID, &puppet.Name, &puppet.NameSet, &avatarHash, &puppet.AvatarMXC, &puppet.AvatarSet, &avatarUpdateTS, &puppet.ContactInfoSet)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(avatarHash) == 32 {
|
||||
puppet.AvatarHash = *(*[32]byte)(avatarHash)
|
||||
}
|
||||
puppet.AvatarUpdateTS = time.UnixMilli(avatarUpdateTS)
|
||||
return puppet, nil
|
||||
}
|
||||
|
||||
func (puppet *Puppet) sqlVariables() []any {
|
||||
return []any{puppet.ID, puppet.Receiver, puppet.Phone, puppet.Name, puppet.NameSet, puppet.AvatarID, &puppet.AvatarMXC, puppet.AvatarSet, puppet.ContactInfoSet}
|
||||
return []any{puppet.ID, puppet.Receiver, puppet.Phone, puppet.ContactID, puppet.Name, puppet.NameSet, puppet.AvatarHash[:], &puppet.AvatarMXC, puppet.AvatarSet, puppet.AvatarUpdateTS.UnixMilli(), puppet.ContactInfoSet}
|
||||
}
|
||||
|
||||
func (puppet *Puppet) Insert(ctx context.Context) error {
|
||||
_, err := puppet.db.Conn(ctx).ExecContext(ctx, `
|
||||
INSERT INTO puppet (id, receiver, phone, name, name_set, avatar_id, avatar_mxc, avatar_set, contact_info_set)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
INSERT INTO puppet (id, receiver, phone, contact_id, name, name_set, avatar_hash, avatar_mxc, avatar_set, avatar_update_ts, contact_info_set)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
`, puppet.sqlVariables()...)
|
||||
return err
|
||||
}
|
||||
|
@ -90,7 +99,7 @@ func (puppet *Puppet) Insert(ctx context.Context) error {
|
|||
func (puppet *Puppet) Update(ctx context.Context) error {
|
||||
_, err := puppet.db.Conn(ctx).ExecContext(ctx, `
|
||||
UPDATE puppet
|
||||
SET phone=$3, name=$4, name_set=$5, avatar_id=$6, avatar_mxc=$7, avatar_set=$8, contact_info_set=$9
|
||||
SET phone=$3, contact_id=$4, name=$5, name_set=$6, avatar_hash=$7, avatar_mxc=$8, avatar_set=$9, avatar_update_ts=$10, contact_info_set=$11
|
||||
WHERE id=$1 AND receiver=$2
|
||||
`, puppet.sqlVariables()...)
|
||||
return err
|
||||
|
|
|
@ -24,9 +24,11 @@ CREATE TABLE puppet (
|
|||
id TEXT NOT NULL,
|
||||
receiver BIGINT NOT NULL,
|
||||
phone TEXT NOT NULL,
|
||||
contact_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
name_set BOOLEAN NOT NULL DEFAULT false,
|
||||
avatar_id TEXT NOT NULL,
|
||||
avatar_hash bytea CHECK ( LENGTH(avatar_hash) = 32 ),
|
||||
avatar_update_ts BIGINT NOT NULL,
|
||||
avatar_mxc TEXT NOT NULL,
|
||||
avatar_set BOOLEAN NOT NULL DEFAULT false,
|
||||
contact_info_set BOOLEAN NOT NULL DEFAULT false,
|
||||
|
@ -45,9 +47,6 @@ CREATE TABLE portal (
|
|||
mxid TEXT UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
name_set BOOLEAN NOT NULL DEFAULT false,
|
||||
avatar_id TEXT NOT NULL,
|
||||
avatar_mxc TEXT NOT NULL,
|
||||
avatar_set BOOLEAN NOT NULL DEFAULT false,
|
||||
encrypted BOOLEAN NOT NULL DEFAULT false,
|
||||
in_space BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
|
|
10
database/upgrades/07-contact-id-fix.sql
Normal file
10
database/upgrades/07-contact-id-fix.sql
Normal file
|
@ -0,0 +1,10 @@
|
|||
-- v7: Fix contact ID field
|
||||
ALTER TABLE puppet RENAME COLUMN avatar_id TO contact_id;
|
||||
ALTER TABLE puppet ADD COLUMN avatar_hash bytea CHECK ( LENGTH(avatar_hash) = 32 );
|
||||
ALTER TABLE puppet ADD COLUMN avatar_update_ts BIGINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE portal DROP COLUMN avatar_id;
|
||||
ALTER TABLE portal DROP COLUMN avatar_mxc;
|
||||
ALTER TABLE portal DROP COLUMN avatar_set;
|
||||
|
||||
-- only: postgres
|
||||
ALTER TABLE puppet ALTER COLUMN avatar_update_ts DROP DEFAULT;
|
|
@ -31,7 +31,8 @@ var responseType = map[gmproto.ActionType]proto.Message{
|
|||
gmproto.ActionType_SEND_MESSAGE: &gmproto.SendMessageResponse{},
|
||||
gmproto.ActionType_SEND_REACTION: &gmproto.SendReactionResponse{},
|
||||
gmproto.ActionType_DELETE_MESSAGE: &gmproto.DeleteMessageResponse{},
|
||||
gmproto.ActionType_GET_PARTICIPANTS_THUMBNAIL: &gmproto.GetParticipantThumbnailResponse{},
|
||||
gmproto.ActionType_GET_PARTICIPANTS_THUMBNAIL: &gmproto.GetThumbnailResponse{},
|
||||
gmproto.ActionType_GET_CONTACTS_THUMBNAIL: &gmproto.GetThumbnailResponse{},
|
||||
gmproto.ActionType_LIST_CONTACTS: &gmproto.ListContactsResponse{},
|
||||
gmproto.ActionType_LIST_TOP_CONTACTS: &gmproto.ListTopContactsResponse{},
|
||||
gmproto.ActionType_GET_OR_CREATE_CONVERSATION: &gmproto.GetOrCreateConversationResponse{},
|
||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
@ -67,27 +67,29 @@ message UploadedMedia {
|
|||
int64 mediaNumber = 2;
|
||||
}
|
||||
|
||||
message GetParticipantThumbnailRequest {
|
||||
string conversationID = 1;
|
||||
message GetThumbnailRequest {
|
||||
repeated string identifiers = 1;
|
||||
}
|
||||
|
||||
message GetParticipantThumbnailResponse {
|
||||
repeated ParticipantThumbnail thumbnail = 1;
|
||||
}
|
||||
message GetThumbnailResponse {
|
||||
message Thumbnail {
|
||||
// ID depends on request, it's always the same as the input.
|
||||
string identifier = 1;
|
||||
ThumbnailData data = 2;
|
||||
}
|
||||
|
||||
message ParticipantThumbnail {
|
||||
string participantID = 1;
|
||||
ThumbnailData data = 2;
|
||||
}
|
||||
|
||||
message GetContactsThumbnailRequest {
|
||||
repeated string avatarIDs = 1;
|
||||
repeated Thumbnail thumbnail = 1;
|
||||
}
|
||||
|
||||
message ThumbnailData {
|
||||
message MysteriousData {
|
||||
fixed64 maybeAHash = 13;
|
||||
}
|
||||
// 2 -> 13: 16 mysterious bytes
|
||||
bytes imageBuffer = 3;
|
||||
int32 someInt = 4;
|
||||
conversations.Dimensions dimensions = 5;
|
||||
MysteriousData mysteriousData = 2;
|
||||
}
|
||||
|
||||
message Cursor {
|
||||
|
|
|
@ -714,12 +714,12 @@ type Contact struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
ParticipantID string `protobuf:"bytes,1,opt,name=participantID,proto3" json:"participantID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Number *ContactNumber `protobuf:"bytes,3,opt,name=number,proto3" json:"number,omitempty"`
|
||||
AvatarHexColor string `protobuf:"bytes,7,opt,name=avatarHexColor,proto3" json:"avatarHexColor,omitempty"`
|
||||
UnknownBool bool `protobuf:"varint,10,opt,name=unknownBool,proto3" json:"unknownBool,omitempty"`
|
||||
AvatarID string `protobuf:"bytes,11,opt,name=avatarID,proto3" json:"avatarID,omitempty"`
|
||||
ContactID string `protobuf:"bytes,11,opt,name=contactID,proto3" json:"contactID,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Contact) Reset() {
|
||||
|
@ -754,9 +754,9 @@ func (*Contact) Descriptor() ([]byte, []int) {
|
|||
return file_conversations_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Contact) GetID() string {
|
||||
func (x *Contact) GetParticipantID() string {
|
||||
if x != nil {
|
||||
return x.ID
|
||||
return x.ParticipantID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -789,9 +789,9 @@ func (x *Contact) GetUnknownBool() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (x *Contact) GetAvatarID() string {
|
||||
func (x *Contact) GetContactID() string {
|
||||
if x != nil {
|
||||
return x.AvatarID
|
||||
return x.ContactID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -1947,7 +1947,7 @@ type Participant struct {
|
|||
IsMe bool `protobuf:"varint,6,opt,name=isMe,proto3" json:"isMe,omitempty"`
|
||||
Muted *Muted `protobuf:"bytes,7,opt,name=muted,proto3" json:"muted,omitempty"`
|
||||
SomeInt int64 `protobuf:"varint,8,opt,name=someInt,proto3" json:"someInt,omitempty"`
|
||||
AvatarID string `protobuf:"bytes,10,opt,name=avatarID,proto3" json:"avatarID,omitempty"`
|
||||
ContactID string `protobuf:"bytes,10,opt,name=contactID,proto3" json:"contactID,omitempty"`
|
||||
Bs int64 `protobuf:"varint,14,opt,name=bs,proto3" json:"bs,omitempty"`
|
||||
FormattedNumber string `protobuf:"bytes,15,opt,name=formattedNumber,proto3" json:"formattedNumber,omitempty"`
|
||||
SomeInt1 int64 `protobuf:"varint,19,opt,name=someInt1,proto3" json:"someInt1,omitempty"`
|
||||
|
@ -2035,9 +2035,9 @@ func (x *Participant) GetSomeInt() int64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (x *Participant) GetAvatarID() string {
|
||||
func (x *Participant) GetContactID() string {
|
||||
if x != nil {
|
||||
return x.AvatarID
|
||||
return x.ContactID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,12 +4,12 @@ package conversations;
|
|||
option go_package = "../gmproto";
|
||||
|
||||
message Contact {
|
||||
string ID = 1;
|
||||
string participantID = 1;
|
||||
string name = 2;
|
||||
ContactNumber number = 3;
|
||||
string avatarHexColor = 7;
|
||||
bool unknownBool = 10;
|
||||
string avatarID = 11;
|
||||
string contactID = 11;
|
||||
}
|
||||
|
||||
message ContactNumber {
|
||||
|
@ -153,7 +153,7 @@ message Participant {
|
|||
bool isMe = 6;
|
||||
Muted muted = 7;
|
||||
int64 someInt = 8;
|
||||
string avatarID = 10;
|
||||
string contactID = 10;
|
||||
int64 bs = 14;
|
||||
string formattedNumber = 15;
|
||||
int64 someInt1 = 19;
|
||||
|
|
|
@ -791,6 +791,7 @@ type OutgoingRPCResponse struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
SomeIdentifier *OutgoingRPCResponse_SomeIdentifier `protobuf:"bytes,1,opt,name=someIdentifier,proto3" json:"someIdentifier,omitempty"`
|
||||
// This is not present for AckMessage responses, only for SendMessage
|
||||
Timestamp *string `protobuf:"bytes,2,opt,name=timestamp,proto3,oneof" json:"timestamp,omitempty"`
|
||||
}
|
||||
|
@ -827,6 +828,13 @@ func (*OutgoingRPCResponse) Descriptor() ([]byte, []int) {
|
|||
return file_rpc_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *OutgoingRPCResponse) GetSomeIdentifier() *OutgoingRPCResponse_SomeIdentifier {
|
||||
if x != nil {
|
||||
return x.SomeIdentifier
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OutgoingRPCResponse) GetTimestamp() string {
|
||||
if x != nil && x.Timestamp != nil {
|
||||
return *x.Timestamp
|
||||
|
@ -1125,16 +1133,17 @@ var file_rpc_proto_depIdxs = []int32{
|
|||
10, // 11: rpc.OutgoingRPCMessage.auth:type_name -> rpc.OutgoingRPCMessage.Auth
|
||||
14, // 12: rpc.OutgoingRPCMessage.emptyArr:type_name -> util.EmptyArr
|
||||
1, // 13: rpc.OutgoingRPCData.action:type_name -> rpc.ActionType
|
||||
16, // 14: rpc.OutgoingRPCMessage.Auth.configVersion:type_name -> authentication.ConfigVersion
|
||||
0, // 15: rpc.OutgoingRPCMessage.Data.bugleRoute:type_name -> rpc.BugleRoute
|
||||
12, // 16: rpc.OutgoingRPCMessage.Data.messageTypeData:type_name -> rpc.OutgoingRPCMessage.Data.Type
|
||||
14, // 17: rpc.OutgoingRPCMessage.Data.Type.emptyArr:type_name -> util.EmptyArr
|
||||
2, // 18: rpc.OutgoingRPCMessage.Data.Type.messageType:type_name -> rpc.MessageType
|
||||
19, // [19:19] is the sub-list for method output_type
|
||||
19, // [19:19] is the sub-list for method input_type
|
||||
19, // [19:19] is the sub-list for extension type_name
|
||||
19, // [19:19] is the sub-list for extension extendee
|
||||
0, // [0:19] is the sub-list for field type_name
|
||||
13, // 14: rpc.OutgoingRPCResponse.someIdentifier:type_name -> rpc.OutgoingRPCResponse.SomeIdentifier
|
||||
16, // 15: rpc.OutgoingRPCMessage.Auth.configVersion:type_name -> authentication.ConfigVersion
|
||||
0, // 16: rpc.OutgoingRPCMessage.Data.bugleRoute:type_name -> rpc.BugleRoute
|
||||
12, // 17: rpc.OutgoingRPCMessage.Data.messageTypeData:type_name -> rpc.OutgoingRPCMessage.Data.Type
|
||||
14, // 18: rpc.OutgoingRPCMessage.Data.Type.emptyArr:type_name -> util.EmptyArr
|
||||
2, // 19: rpc.OutgoingRPCMessage.Data.Type.messageType:type_name -> rpc.MessageType
|
||||
20, // [20:20] is the sub-list for method output_type
|
||||
20, // [20:20] is the sub-list for method input_type
|
||||
20, // [20:20] is the sub-list for extension type_name
|
||||
20, // [20:20] is the sub-list for extension extendee
|
||||
0, // [0:20] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_rpc_proto_init() }
|
||||
|
|
Binary file not shown.
|
@ -91,6 +91,7 @@ message OutgoingRPCResponse {
|
|||
string someNumber = 2;
|
||||
}
|
||||
|
||||
SomeIdentifier someIdentifier = 1;
|
||||
// This is not present for AckMessage responses, only for SendMessage
|
||||
optional string timestamp = 2;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ var requestType = map[gmproto.ActionType]proto.Message{
|
|||
gmproto.ActionType_SEND_MESSAGE: &gmproto.SendMessageRequest{},
|
||||
gmproto.ActionType_SEND_REACTION: &gmproto.SendReactionRequest{},
|
||||
gmproto.ActionType_DELETE_MESSAGE: &gmproto.DeleteMessageRequest{},
|
||||
gmproto.ActionType_GET_PARTICIPANTS_THUMBNAIL: &gmproto.GetParticipantThumbnailRequest{},
|
||||
gmproto.ActionType_GET_PARTICIPANTS_THUMBNAIL: &gmproto.GetThumbnailRequest{},
|
||||
gmproto.ActionType_GET_CONTACTS_THUMBNAIL: &gmproto.GetThumbnailRequest{},
|
||||
gmproto.ActionType_LIST_CONTACTS: &gmproto.ListContactsRequest{},
|
||||
gmproto.ActionType_LIST_TOP_CONTACTS: &gmproto.ListTopContactsRequest{},
|
||||
gmproto.ActionType_GET_OR_CREATE_CONVERSATION: &gmproto.GetOrCreateConversationRequest{},
|
||||
|
|
|
@ -70,10 +70,16 @@ func (c *Client) SendMessage(payload *gmproto.SendMessageRequest) (*gmproto.Send
|
|||
return typedResponse[*gmproto.SendMessageResponse](c.sessionHandler.sendMessage(actionType, payload))
|
||||
}
|
||||
|
||||
func (c *Client) GetParticipantThumbnail(convID string) (*gmproto.GetParticipantThumbnailResponse, error) {
|
||||
payload := &gmproto.GetParticipantThumbnailRequest{ConversationID: convID}
|
||||
func (c *Client) GetParticipantThumbnail(participantIDs ...string) (*gmproto.GetThumbnailResponse, error) {
|
||||
payload := &gmproto.GetThumbnailRequest{Identifiers: participantIDs}
|
||||
actionType := gmproto.ActionType_GET_PARTICIPANTS_THUMBNAIL
|
||||
return typedResponse[*gmproto.GetParticipantThumbnailResponse](c.sessionHandler.sendMessage(actionType, payload))
|
||||
return typedResponse[*gmproto.GetThumbnailResponse](c.sessionHandler.sendMessage(actionType, payload))
|
||||
}
|
||||
|
||||
func (c *Client) GetContactThumbnail(contactIDs ...string) (*gmproto.GetThumbnailResponse, error) {
|
||||
payload := &gmproto.GetThumbnailRequest{Identifiers: contactIDs}
|
||||
actionType := gmproto.ActionType_GET_CONTACTS_THUMBNAIL
|
||||
return typedResponse[*gmproto.GetThumbnailResponse](c.sessionHandler.sendMessage(actionType, payload))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateConversation(payload *gmproto.UpdateConversationRequest) (*gmproto.UpdateConversationResponse, error) {
|
||||
|
|
13
portal.go
13
portal.go
|
@ -1423,7 +1423,6 @@ func (portal *Portal) getBridgeInfo() (string, event.BridgeEventContent) {
|
|||
Channel: event.BridgeInfoSection{
|
||||
ID: portal.ID,
|
||||
DisplayName: portal.Name,
|
||||
AvatarURL: portal.AvatarMXC.CUString(),
|
||||
},
|
||||
}
|
||||
if portal.Type == gmproto.ConversationType_SMS {
|
||||
|
@ -1531,17 +1530,6 @@ func (portal *Portal) CreateMatrixRoom(user *User, conv *gmproto.Conversation, i
|
|||
invite = append(invite, portal.bridge.Bot.UserID)
|
||||
}
|
||||
}
|
||||
if !portal.AvatarMXC.IsEmpty() && portal.shouldSetDMRoomMetadata() {
|
||||
initialState = append(initialState, &event.Event{
|
||||
Type: event.StateRoomAvatar,
|
||||
Content: event.Content{
|
||||
Parsed: event.RoomAvatarEventContent{URL: portal.AvatarMXC},
|
||||
},
|
||||
})
|
||||
portal.AvatarSet = true
|
||||
} else {
|
||||
portal.AvatarSet = false
|
||||
}
|
||||
|
||||
creationContent := make(map[string]interface{})
|
||||
if !portal.bridge.Config.Bridge.FederateRooms {
|
||||
|
@ -2103,7 +2091,6 @@ func (portal *Portal) RemoveMXID(ctx context.Context) {
|
|||
delete(portal.bridge.portalsByMXID, portal.MXID)
|
||||
portal.MXID = ""
|
||||
portal.NameSet = false
|
||||
portal.AvatarSet = false
|
||||
portal.InSpace = false
|
||||
portal.Encrypted = false
|
||||
portal.bridge.portalsLock.Unlock()
|
||||
|
|
69
puppet.go
69
puppet.go
|
@ -18,9 +18,12 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"maunium.net/go/mautrix"
|
||||
|
@ -198,20 +201,47 @@ func (puppet *Puppet) DefaultIntent() *appservice.IntentAPI {
|
|||
return puppet.bridge.AS.Intent(puppet.MXID)
|
||||
}
|
||||
|
||||
func (puppet *Puppet) UpdateAvatar(source *User, avatarID string) bool {
|
||||
if puppet.AvatarID == avatarID && puppet.AvatarSet {
|
||||
const MinAvatarUpdateInterval = 24 * time.Hour
|
||||
|
||||
func (puppet *Puppet) UpdateAvatar(source *User) bool {
|
||||
if (puppet.AvatarSet && time.Since(puppet.AvatarUpdateTS) < MinAvatarUpdateInterval) || puppet.ContactID == "" {
|
||||
return false
|
||||
}
|
||||
puppet.AvatarID = avatarID
|
||||
puppet.AvatarMXC = id.ContentURI{}
|
||||
puppet.AvatarSet = false
|
||||
// TODO bridge avatar
|
||||
if puppet.AvatarMXC.IsEmpty() {
|
||||
return false
|
||||
}
|
||||
err := puppet.DefaultIntent().SetAvatarURL(puppet.AvatarMXC)
|
||||
resp, err := source.Client.GetParticipantThumbnail(puppet.ID)
|
||||
if err != nil {
|
||||
puppet.log.Warn().Err(err).Msg("Failed to set avatar")
|
||||
puppet.log.Err(err).Msg("Failed to get avatar thumbnail")
|
||||
return false
|
||||
}
|
||||
puppet.AvatarUpdateTS = time.Now()
|
||||
if len(resp.Thumbnail) == 0 {
|
||||
if puppet.AvatarHash == [32]byte{} {
|
||||
return true
|
||||
}
|
||||
puppet.AvatarHash = [32]byte{}
|
||||
puppet.AvatarMXC = id.ContentURI{}
|
||||
puppet.AvatarSet = false
|
||||
} else {
|
||||
thumbData := resp.Thumbnail[0].GetData()
|
||||
hash := sha256.Sum256(thumbData.GetImageBuffer())
|
||||
if hash == puppet.AvatarHash {
|
||||
return true
|
||||
}
|
||||
puppet.AvatarHash = hash
|
||||
puppet.AvatarSet = false
|
||||
avatarBytes := thumbData.GetImageBuffer()
|
||||
uploadResp, err := puppet.DefaultIntent().UploadMedia(mautrix.ReqUploadMedia{
|
||||
ContentBytes: avatarBytes,
|
||||
ContentType: http.DetectContentType(avatarBytes),
|
||||
})
|
||||
if err != nil {
|
||||
puppet.log.Err(err).Msg("Failed to upload avatar")
|
||||
return true
|
||||
}
|
||||
puppet.AvatarMXC = uploadResp.ContentURI
|
||||
}
|
||||
err = puppet.DefaultIntent().SetAvatarURL(puppet.AvatarMXC)
|
||||
if err != nil {
|
||||
puppet.log.Err(err).Msg("Failed to set avatar")
|
||||
} else {
|
||||
puppet.AvatarSet = true
|
||||
}
|
||||
|
@ -250,6 +280,7 @@ func (puppet *Puppet) UpdateContactInfo() bool {
|
|||
contactInfo := map[string]any{
|
||||
"com.beeper.bridge.identifiers": []string{
|
||||
fmt.Sprintf("tel:%s", puppet.Phone),
|
||||
fmt.Sprintf("gmsg-contact:%s", puppet.ContactID),
|
||||
},
|
||||
"com.beeper.bridge.remote_id": puppet.Key.String(),
|
||||
"com.beeper.bridge.service": "gmessages",
|
||||
|
@ -280,14 +311,16 @@ func (puppet *Puppet) Sync(source *User, contact *gmproto.Participant) {
|
|||
}
|
||||
|
||||
update := false
|
||||
if contact != nil {
|
||||
if contact.ID.Number != "" && puppet.Phone != contact.ID.Number {
|
||||
puppet.Phone = contact.ID.Number
|
||||
update = true
|
||||
}
|
||||
update = puppet.UpdateName(contact.GetFormattedNumber(), contact.GetFullName(), contact.GetFirstName()) || update
|
||||
update = puppet.UpdateAvatar(source, contact.GetAvatarID()) || update
|
||||
if contact.ID.Number != "" && puppet.Phone != contact.ID.Number {
|
||||
puppet.Phone = contact.ID.Number
|
||||
update = true
|
||||
}
|
||||
if contact.ContactID != puppet.ContactID {
|
||||
puppet.ContactID = contact.ContactID
|
||||
update = true
|
||||
}
|
||||
update = puppet.UpdateName(contact.GetFormattedNumber(), contact.GetFullName(), contact.GetFirstName()) || update
|
||||
update = puppet.UpdateAvatar(source) || update
|
||||
update = puppet.UpdateContactInfo() || update
|
||||
if update {
|
||||
err = puppet.Update(context.TODO())
|
||||
|
|
Loading…
Reference in a new issue