userinfo: add support for avoiding contact names/avatars
This commit is contained in:
@@ -449,7 +449,7 @@ func (t *TelegramClient) onConnected(self *tg.User) {
|
|||||||
ghost, err := t.main.Bridge.GetGhostByID(ctx, t.userID)
|
ghost, err := t.main.Bridge.GetGhostByID(ctx, t.userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("Failed to get own ghost")
|
log.Err(err).Msg("Failed to get own ghost")
|
||||||
} else if wrapped, err := t.wrapUserInfo(ctx, self); err != nil {
|
} else if wrapped, err := t.wrapUserInfo(ctx, self, ghost); err != nil {
|
||||||
log.Err(err).Msg("Failed to wrap own user info")
|
log.Err(err).Msg("Failed to wrap own user info")
|
||||||
} else {
|
} else {
|
||||||
ghost.UpdateInfo(ctx, wrapped)
|
ghost.UpdateInfo(ctx, wrapped)
|
||||||
|
|||||||
+17
-18
@@ -56,16 +56,9 @@ type TelegramConfig struct {
|
|||||||
APIID int `yaml:"api_id"`
|
APIID int `yaml:"api_id"`
|
||||||
APIHash string `yaml:"api_hash"`
|
APIHash string `yaml:"api_hash"`
|
||||||
|
|
||||||
DeviceInfo DeviceInfo `yaml:"device_info"`
|
DeviceInfo DeviceInfo `yaml:"device_info"`
|
||||||
|
AnimatedSticker media.AnimatedStickerConfig `yaml:"animated_sticker"`
|
||||||
AnimatedSticker media.AnimatedStickerConfig `yaml:"animated_sticker"`
|
MemberList MemberListConfig `yaml:"member_list"`
|
||||||
ImageAsFilePixels int `yaml:"image_as_file_pixels"`
|
|
||||||
|
|
||||||
DisableViewOnce bool `yaml:"disable_view_once"`
|
|
||||||
|
|
||||||
MemberList MemberListConfig `yaml:"member_list"`
|
|
||||||
|
|
||||||
MaxMemberCount int `yaml:"max_member_count"`
|
|
||||||
|
|
||||||
Ping struct {
|
Ping struct {
|
||||||
IntervalSeconds int `yaml:"interval_seconds"`
|
IntervalSeconds int `yaml:"interval_seconds"`
|
||||||
@@ -78,11 +71,14 @@ type TelegramConfig struct {
|
|||||||
DirectChats bool `yaml:"direct_chats"`
|
DirectChats bool `yaml:"direct_chats"`
|
||||||
} `yaml:"sync"`
|
} `yaml:"sync"`
|
||||||
|
|
||||||
AlwaysCustomEmojiReaction bool `yaml:"always_custom_emoji_reaction"`
|
ContactAvatars bool `yaml:"contact_avatars"`
|
||||||
|
ContactNames bool `yaml:"contact_names"`
|
||||||
SavedMessagesAvatar id.ContentURIString `yaml:"saved_message_avatar"`
|
MaxMemberCount int `yaml:"max_member_count"`
|
||||||
|
AlwaysCustomEmojiReaction bool `yaml:"always_custom_emoji_reaction"`
|
||||||
AlwaysTombstoneOnSupergroupMigration bool `yaml:"always_tombstone_on_supergroup_migration"`
|
SavedMessagesAvatar id.ContentURIString `yaml:"saved_message_avatar"`
|
||||||
|
AlwaysTombstoneOnSupergroupMigration bool `yaml:"always_tombstone_on_supergroup_migration"`
|
||||||
|
ImageAsFilePixels int `yaml:"image_as_file_pixels"`
|
||||||
|
DisableViewOnce bool `yaml:"disable_view_once"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c TelegramConfig) ShouldBridge(participantCount int) bool {
|
func (c TelegramConfig) ShouldBridge(participantCount int) bool {
|
||||||
@@ -107,20 +103,22 @@ func upgradeConfig(helper up.Helper) {
|
|||||||
helper.Copy(up.Int, "animated_sticker", "args", "width")
|
helper.Copy(up.Int, "animated_sticker", "args", "width")
|
||||||
helper.Copy(up.Int, "animated_sticker", "args", "height")
|
helper.Copy(up.Int, "animated_sticker", "args", "height")
|
||||||
helper.Copy(up.Int, "animated_sticker", "args", "fps")
|
helper.Copy(up.Int, "animated_sticker", "args", "fps")
|
||||||
helper.Copy(up.Int, "image_as_file_pixels")
|
|
||||||
helper.Copy(up.Bool, "disable_view_once")
|
|
||||||
helper.Copy(up.Int, "member_list", "max_initial_sync")
|
helper.Copy(up.Int, "member_list", "max_initial_sync")
|
||||||
helper.Copy(up.Bool, "member_list", "sync_broadcast_channels")
|
helper.Copy(up.Bool, "member_list", "sync_broadcast_channels")
|
||||||
helper.Copy(up.Bool, "member_list", "skip_deleted")
|
helper.Copy(up.Bool, "member_list", "skip_deleted")
|
||||||
helper.Copy(up.Int, "max_member_count")
|
|
||||||
helper.Copy(up.Int, "ping", "interval_seconds")
|
helper.Copy(up.Int, "ping", "interval_seconds")
|
||||||
helper.Copy(up.Int, "ping", "timeout_seconds")
|
helper.Copy(up.Int, "ping", "timeout_seconds")
|
||||||
helper.Copy(up.Int, "sync", "update_limit")
|
helper.Copy(up.Int, "sync", "update_limit")
|
||||||
helper.Copy(up.Int, "sync", "create_limit")
|
helper.Copy(up.Int, "sync", "create_limit")
|
||||||
helper.Copy(up.Bool, "sync", "direct_chats")
|
helper.Copy(up.Bool, "sync", "direct_chats")
|
||||||
|
helper.Copy(up.Bool, "contact_avatars")
|
||||||
|
helper.Copy(up.Bool, "contact_names")
|
||||||
|
helper.Copy(up.Int, "max_member_count")
|
||||||
helper.Copy(up.Bool, "always_custom_emoji_reaction")
|
helper.Copy(up.Bool, "always_custom_emoji_reaction")
|
||||||
helper.Copy(up.Str, "saved_message_avatar")
|
helper.Copy(up.Str, "saved_message_avatar")
|
||||||
helper.Copy(up.Bool, "always_tombstone_on_supergroup_migration")
|
helper.Copy(up.Bool, "always_tombstone_on_supergroup_migration")
|
||||||
|
helper.Copy(up.Int, "image_as_file_pixels")
|
||||||
|
helper.Copy(up.Bool, "disable_view_once")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) {
|
func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) {
|
||||||
@@ -132,6 +130,7 @@ func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.
|
|||||||
{"member_list"},
|
{"member_list"},
|
||||||
{"ping"},
|
{"ping"},
|
||||||
{"sync"},
|
{"sync"},
|
||||||
|
{"max_member_count"},
|
||||||
},
|
},
|
||||||
Base: ExampleConfig,
|
Base: ExampleConfig,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,13 +30,6 @@ animated_sticker:
|
|||||||
height: 256
|
height: 256
|
||||||
fps: 25 # only for webm, webp and gif (2, 5, 10, 20 or 25 recommended)
|
fps: 25 # only for webm, webp and gif (2, 5, 10, 20 or 25 recommended)
|
||||||
|
|
||||||
# Maximum number of pixels in an image before sending to Telegram as a
|
|
||||||
# document. Defaults to 4096x4096 = 16777216.
|
|
||||||
image_as_file_pixels: 16777216
|
|
||||||
|
|
||||||
# Should view-once messages be disabled entirely?
|
|
||||||
disable_view_once: false
|
|
||||||
|
|
||||||
# Settings for syncing the member list for portals.
|
# Settings for syncing the member list for portals.
|
||||||
member_list:
|
member_list:
|
||||||
# Maximum number of members to sync per portal when starting up. Other
|
# Maximum number of members to sync per portal when starting up. Other
|
||||||
@@ -53,12 +46,6 @@ member_list:
|
|||||||
sync_broadcast_channels: false
|
sync_broadcast_channels: false
|
||||||
# Whether or not to skip deleted members when syncing members.
|
# Whether or not to skip deleted members when syncing members.
|
||||||
skip_deleted: true
|
skip_deleted: true
|
||||||
# Maximum number of participants in chats to bridge. Only applies when the
|
|
||||||
# portal is being created. If there are more members when trying to create a
|
|
||||||
# room, the room creation will be cancelled.
|
|
||||||
#
|
|
||||||
# -1 means no limit (which means all chats can be bridged)
|
|
||||||
max_member_count: -1
|
|
||||||
|
|
||||||
# Settings for pings to the Telegram server.
|
# Settings for pings to the Telegram server.
|
||||||
ping:
|
ping:
|
||||||
@@ -78,13 +65,26 @@ sync:
|
|||||||
# Whether or not to sync and create portals for direct chats at startup.
|
# Whether or not to sync and create portals for direct chats at startup.
|
||||||
direct_chats: false
|
direct_chats: false
|
||||||
|
|
||||||
|
# Maximum number of participants in chats to bridge. Only applies when the
|
||||||
|
# portal is being created. If there are more members when trying to create a
|
||||||
|
# room, the room creation will be cancelled.
|
||||||
|
#
|
||||||
|
# -1 means no limit (which means all chats can be bridged)
|
||||||
|
max_member_count: -1
|
||||||
|
# Should personal avatars (that are only visible to specific users) be allowed?
|
||||||
|
contact_avatars: false
|
||||||
|
# Should contact names be updated from any source even if a name is already set?
|
||||||
|
contact_names: false
|
||||||
# Should the bridge send all unicode reactions as custom emoji reactions to
|
# Should the bridge send all unicode reactions as custom emoji reactions to
|
||||||
# Telegram? By default, the bridge only uses custom emojis for unicode emojis
|
# Telegram? By default, the bridge only uses custom emojis for unicode emojis
|
||||||
# that aren't allowed in reactions.
|
# that aren't allowed in reactions.
|
||||||
always_custom_emoji_reaction: false
|
always_custom_emoji_reaction: false
|
||||||
|
|
||||||
# The avatar to use for the Telegram Saved Messages chat
|
# The avatar to use for the Telegram Saved Messages chat
|
||||||
saved_message_avatar: mxc://maunium.net/XhhfHoPejeneOngMyBbtyWDk
|
saved_message_avatar: mxc://maunium.net/XhhfHoPejeneOngMyBbtyWDk
|
||||||
|
|
||||||
# Create a new room and tombstone the old one when upgrading rooms
|
# Create a new room and tombstone the old one when upgrading rooms
|
||||||
always_tombstone_on_supergroup_migration: false
|
always_tombstone_on_supergroup_migration: false
|
||||||
|
# Maximum number of pixels in an image before sending to Telegram as a
|
||||||
|
# document. Defaults to 4096x4096 = 16777216.
|
||||||
|
image_as_file_pixels: 16777216
|
||||||
|
# Should view-once messages be disabled entirely?
|
||||||
|
disable_view_once: false
|
||||||
|
|||||||
@@ -716,12 +716,16 @@ func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
meta := ghost.Metadata.(*GhostMetadata)
|
||||||
|
|
||||||
var userInfo bridgev2.UserInfo
|
var userInfo bridgev2.UserInfo
|
||||||
|
|
||||||
// TODO anti-contact-name logic
|
|
||||||
name := util.FormatFullName(update.FirstName, update.LastName, false, update.UserID)
|
name := util.FormatFullName(update.FirstName, update.LastName, false, update.UserID)
|
||||||
userInfo.Name = &name
|
userInfo.Name = &name
|
||||||
|
if meta.ContactSource != 0 && meta.ContactSource != t.telegramUserID && !t.main.Config.ContactNames {
|
||||||
|
// TODO fetch full info to accurately detect if the user is a contact or not
|
||||||
|
userInfo.Name = nil
|
||||||
|
}
|
||||||
|
|
||||||
if len(update.Usernames) > 0 {
|
if len(update.Usernames) > 0 {
|
||||||
for _, ident := range ghost.Identifiers {
|
for _, ident := range ghost.Identifiers {
|
||||||
@@ -800,7 +804,7 @@ func (t *TelegramClient) updateGhost(ctx context.Context, userID int64, user *tg
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
userInfo, err := t.wrapUserInfo(ctx, user)
|
userInfo, err := t.wrapUserInfo(ctx, user, ghost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ type GhostMetadata struct {
|
|||||||
IsBot bool `json:"is_bot,omitempty"`
|
IsBot bool `json:"is_bot,omitempty"`
|
||||||
IsChannel bool `json:"is_channel,omitempty"`
|
IsChannel bool `json:"is_channel,omitempty"`
|
||||||
Deleted bool `json:"deleted,omitempty"`
|
Deleted bool `json:"deleted,omitempty"`
|
||||||
|
NotMin bool `json:"not_min,omitempty"`
|
||||||
|
|
||||||
|
ContactSource int64 `json:"contact_source,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gm *GhostMetadata) IsMin() bool {
|
||||||
|
return !gm.NotMin
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortalMetadata struct {
|
type PortalMetadata struct {
|
||||||
|
|||||||
+20
-13
@@ -44,19 +44,23 @@ var (
|
|||||||
|
|
||||||
func (t *TelegramClient) resolveUser(ctx context.Context, user tg.UserClass) (*bridgev2.ResolveIdentifierResponse, error) {
|
func (t *TelegramClient) resolveUser(ctx context.Context, user tg.UserClass) (*bridgev2.ResolveIdentifierResponse, error) {
|
||||||
networkUserID := ids.MakeUserID(user.GetID())
|
networkUserID := ids.MakeUserID(user.GetID())
|
||||||
if userInfo, err := t.wrapUserInfo(ctx, user); err != nil {
|
if ghost, err := t.main.Bridge.GetGhostByID(ctx, networkUserID); err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user info: %w", err)
|
|
||||||
} else if ghost, err := t.main.Bridge.GetGhostByID(ctx, networkUserID); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get ghost: %w", err)
|
return nil, fmt.Errorf("failed to get ghost: %w", err)
|
||||||
|
} else if userInfo, err := t.wrapUserInfo(ctx, user, ghost); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get user info: %w", err)
|
||||||
} else {
|
} else {
|
||||||
return &bridgev2.ResolveIdentifierResponse{
|
return t.makeResolveIdentifierResponse(ghost, user, userInfo), nil
|
||||||
Ghost: ghost,
|
}
|
||||||
UserID: networkUserID,
|
}
|
||||||
UserInfo: userInfo,
|
|
||||||
Chat: &bridgev2.CreateChatResponse{
|
func (t *TelegramClient) makeResolveIdentifierResponse(ghost *bridgev2.Ghost, user tg.UserClass, info *bridgev2.UserInfo) *bridgev2.ResolveIdentifierResponse {
|
||||||
PortalKey: t.makePortalKeyFromID(ids.PeerTypeUser, user.GetID(), 0),
|
return &bridgev2.ResolveIdentifierResponse{
|
||||||
},
|
Ghost: ghost,
|
||||||
}, nil
|
UserID: ids.MakeUserID(user.GetID()),
|
||||||
|
UserInfo: info,
|
||||||
|
Chat: &bridgev2.CreateChatResponse{
|
||||||
|
PortalKey: t.makePortalKeyFromID(ids.PeerTypeUser, user.GetID(), 0),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +97,13 @@ func (t *TelegramClient) resolveUserID(ctx context.Context, userID int64) (resp
|
|||||||
return nil, fmt.Errorf("failed to get user with ID %d: %w", userID, err)
|
return nil, fmt.Errorf("failed to get user with ID %d: %w", userID, err)
|
||||||
} else if user.TypeID() != tg.UserTypeID {
|
} else if user.TypeID() != tg.UserTypeID {
|
||||||
return nil, fmt.Errorf("unexpected user type: %T", user)
|
return nil, fmt.Errorf("unexpected user type: %T", user)
|
||||||
} else if _, err = t.updateGhost(ctx, userID, user.(*tg.User)); err != nil {
|
} else if userInfo, err := t.updateGhost(ctx, userID, user.(*tg.User)); err != nil {
|
||||||
return nil, fmt.Errorf("failed to update ghost: %w", err)
|
return nil, fmt.Errorf("failed to update ghost: %w", err)
|
||||||
} else {
|
} else {
|
||||||
return t.resolveUser(ctx, user)
|
if resp.Ghost == nil {
|
||||||
|
resp.Ghost, _ = t.main.Bridge.GetExistingGhostByID(ctx, networkUserID)
|
||||||
|
}
|
||||||
|
return t.makeResolveIdentifierResponse(resp.Ghost, user, userInfo), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (t *TelegramClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost)
|
|||||||
if user, err := t.getSingleUser(ctx, id); err != nil {
|
if user, err := t.getSingleUser(ctx, id); err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user %d: %w", id, err)
|
return nil, fmt.Errorf("failed to get user %d: %w", id, err)
|
||||||
} else {
|
} else {
|
||||||
return t.wrapUserInfo(ctx, user)
|
return t.wrapUserInfo(ctx, user, ghost)
|
||||||
}
|
}
|
||||||
case ids.PeerTypeChannel:
|
case ids.PeerTypeChannel:
|
||||||
if channel, err := t.getSingleChannel(ctx, id); err != nil {
|
if channel, err := t.getSingleChannel(ctx, id); err != nil {
|
||||||
@@ -151,7 +151,8 @@ func (t *TelegramClient) wrapChannelGhostInfo(ctx context.Context, channel *tg.C
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TelegramClient) wrapUserInfo(ctx context.Context, u tg.UserClass) (*bridgev2.UserInfo, error) {
|
func (t *TelegramClient) wrapUserInfo(ctx context.Context, u tg.UserClass, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) {
|
||||||
|
oldMeta := ghost.Metadata.(*GhostMetadata)
|
||||||
user, ok := u.(*tg.User)
|
user, ok := u.(*tg.User)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("user is %T not *tg.User", user)
|
return nil, fmt.Errorf("user is %T not *tg.User", user)
|
||||||
@@ -186,9 +187,10 @@ func (t *TelegramClient) wrapUserInfo(ctx context.Context, u tg.UserClass) (*bri
|
|||||||
identifiers = slices.Compact(identifiers)
|
identifiers = slices.Compact(identifiers)
|
||||||
|
|
||||||
var avatar *bridgev2.Avatar
|
var avatar *bridgev2.Avatar
|
||||||
// TODO don't apply min avatars without checking apply_min_photo or existing min status
|
photo, ok := user.Photo.(*tg.UserProfilePhoto)
|
||||||
if p, ok := user.GetPhoto(); ok && p.TypeID() == tg.UserProfilePhotoTypeID {
|
if ok &&
|
||||||
photo := p.(*tg.UserProfilePhoto)
|
(!user.Min || user.ApplyMinPhoto || oldMeta.IsMin()) &&
|
||||||
|
(!photo.Personal || t.main.Config.ContactAvatars) {
|
||||||
var err error
|
var err error
|
||||||
avatar, err = t.convertUserProfilePhoto(ctx, user, photo)
|
avatar, err = t.convertUserProfilePhoto(ctx, user, photo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -197,18 +199,26 @@ func (t *TelegramClient) wrapUserInfo(ctx context.Context, u tg.UserClass) (*bri
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := util.FormatFullName(user.FirstName, user.LastName, user.Deleted, user.ID)
|
name := util.FormatFullName(user.FirstName, user.LastName, user.Deleted, user.ID)
|
||||||
|
namePtr := &name
|
||||||
|
if user.Contact && ghost.Name != "" && oldMeta.ContactSource != t.telegramUserID && oldMeta.ContactSource != 0 && !t.main.Config.ContactNames {
|
||||||
|
namePtr = nil
|
||||||
|
}
|
||||||
return &bridgev2.UserInfo{
|
return &bridgev2.UserInfo{
|
||||||
IsBot: &user.Bot,
|
IsBot: &user.Bot,
|
||||||
Name: &name,
|
Name: namePtr,
|
||||||
Avatar: avatar,
|
Avatar: avatar,
|
||||||
Identifiers: identifiers,
|
Identifiers: identifiers,
|
||||||
ExtraUpdates: func(ctx context.Context, ghost *bridgev2.Ghost) (changed bool) {
|
ExtraUpdates: func(ctx context.Context, ghost *bridgev2.Ghost) (changed bool) {
|
||||||
meta := ghost.Metadata.(*GhostMetadata)
|
meta := ghost.Metadata.(*GhostMetadata)
|
||||||
if !user.Min {
|
if !user.Min {
|
||||||
changed = changed || meta.IsPremium != user.Premium || meta.IsBot != user.Bot
|
changed = changed || meta.IsPremium != user.Premium || meta.IsBot != user.Bot || meta.IsMin()
|
||||||
meta.IsPremium = user.Premium
|
meta.IsPremium = user.Premium
|
||||||
meta.IsBot = user.Bot
|
meta.IsBot = user.Bot
|
||||||
meta.Deleted = user.Deleted
|
meta.Deleted = user.Deleted
|
||||||
|
meta.NotMin = true
|
||||||
|
if meta.ContactSource == 0 {
|
||||||
|
meta.ContactSource = t.telegramUserID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return changed
|
return changed
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user