config: re-add displayname template

Fixes #1057
This commit is contained in:
Tulir Asokan
2026-04-03 15:09:18 +03:00
parent 9b92aa3d50
commit cbff082e4d
8 changed files with 73 additions and 47 deletions

View File

@@ -47,7 +47,6 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/matrixfmt"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
"go.mau.fi/mautrix-telegram/pkg/connector/telegramfmt"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram"
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/auth"
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/updates"
@@ -430,12 +429,12 @@ func (t *TelegramClient) onPing() {
}
}
func userToRemoteProfile(
func (t *TelegramConnector) userToRemoteProfile(
self *tg.User,
ghost *bridgev2.Ghost,
prevState *status.RemoteProfile,
) (profile status.RemoteProfile, name string) {
profile.Name = util.FormatFullName(self.FirstName, self.LastName, self.Deleted, self.ID)
profile.Name = t.Config.FormatDisplayname(self.FirstName, self.LastName, self.Username, self.Deleted, self.ID)
if self.Phone != "" {
profile.Phone = "+" + strings.TrimPrefix(self.Phone, "+")
} else if prevState != nil {
@@ -455,7 +454,7 @@ func userToRemoteProfile(
}
func (t *TelegramClient) updateRemoteProfile(ctx context.Context, self *tg.User, ghost *bridgev2.Ghost) bool {
newProfile, newName := userToRemoteProfile(self, ghost, &t.userLogin.RemoteProfile)
newProfile, newName := t.main.userToRemoteProfile(self, ghost, &t.userLogin.RemoteProfile)
if t.userLogin.RemoteProfile != newProfile || t.userLogin.RemoteName != newName {
t.userLogin.RemoteProfile = newProfile
t.userLogin.RemoteName = newName

View File

@@ -20,8 +20,11 @@ import (
_ "embed"
"fmt"
"slices"
"strings"
"text/template"
up "go.mau.fi/util/configupgrade"
"gopkg.in/yaml.v3"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/bridgeconfig"
"maunium.net/go/mautrix/id"
@@ -86,12 +89,55 @@ type TelegramConfig struct {
AlwaysTombstoneOnSupergroupMigration bool `yaml:"always_tombstone_on_supergroup_migration"`
ImageAsFilePixels int `yaml:"image_as_file_pixels"`
DisableViewOnce bool `yaml:"disable_view_once"`
DisplaynameTemplate string `yaml:"displayname_template"`
displaynameTemplate *template.Template `yaml:"-"`
}
func (c TelegramConfig) ShouldBridge(participantCount int) bool {
return c.MaxMemberCount < 0 || participantCount <= c.MaxMemberCount
}
type DisplaynameParams struct {
FullName string
FirstName string
LastName string
Username string
UserID int64
Deleted bool
}
func (c *TelegramConfig) FormatDisplayname(firstName, lastName, username string, deleted bool, userID int64) string {
var buf strings.Builder
err := c.displaynameTemplate.Execute(&buf, DisplaynameParams{
FullName: strings.TrimSpace(firstName + " " + lastName),
FirstName: firstName,
LastName: lastName,
Username: username,
UserID: userID,
Deleted: deleted,
})
if err != nil {
panic(fmt.Errorf("displayname template is broken: %w", err))
}
return buf.String()
}
type umConfig TelegramConfig
func (c *TelegramConfig) UnmarshalYAML(node *yaml.Node) error {
err := node.Decode((*umConfig)(c))
if err != nil {
return err
}
return c.PostProcess()
}
func (c *TelegramConfig) PostProcess() error {
var err error
c.displaynameTemplate, err = template.New("displayname").Parse(c.DisplaynameTemplate)
return err
}
//go:embed example-config.yaml
var ExampleConfig string
@@ -130,6 +176,7 @@ func upgradeConfig(helper up.Helper) {
helper.Copy(up.Bool, "always_tombstone_on_supergroup_migration")
helper.Copy(up.Int, "image_as_file_pixels")
helper.Copy(up.Bool, "disable_view_once")
helper.Copy(up.Str, "displayname_template")
}
func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) {

View File

@@ -104,3 +104,11 @@ always_tombstone_on_supergroup_migration: false
image_as_file_pixels: 16777216
# Should view-once messages be disabled entirely?
disable_view_once: false
# Displayname template for Telegram users.
# {{ .FullName }} - the full name of the Telegram user
# {{ .FirstName }} - the first name of the Telegram user
# {{ .LastName }} - the last name of the Telegram user
# {{ .Username }} - the primary username of the Telegram user, if the user has one
# {{ .UserID }} - the internal user ID of the Telegram user
# {{ .Deleted }} - true if the user has been deleted, false otherwise
displayname_template: "{{ if .Deleted }}Deleted account {{ .UserID }}{{ else }}{{ .FullName }}{{ end }}"

View File

@@ -42,7 +42,6 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
"go.mau.fi/mautrix-telegram/pkg/connector/tljson"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
"go.mau.fi/mautrix-telegram/pkg/gotd/tgerr"
)
@@ -728,13 +727,7 @@ func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *
var userInfo bridgev2.UserInfo
name := util.FormatFullName(update.FirstName, update.LastName, false, update.UserID)
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
}
var firstUsername string
if len(update.Usernames) > 0 {
for _, ident := range ghost.Identifiers {
if !strings.HasPrefix(ident, "telegram:") {
@@ -742,7 +735,10 @@ func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *
}
}
for _, username := range update.Usernames {
for i, username := range update.Usernames {
if i == 0 {
firstUsername = username.Username
}
userInfo.Identifiers = append(userInfo.Identifiers, fmt.Sprintf("telegram:%s", username.Username))
}
@@ -750,6 +746,13 @@ func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *
userInfo.Identifiers = slices.Compact(userInfo.Identifiers)
}
name := t.main.Config.FormatDisplayname(update.FirstName, update.LastName, firstUsername, false, update.UserID)
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
}
ghost.UpdateInfo(ctx, &userInfo)
if ghost.ID == t.userID {
var firstUsername string

View File

@@ -216,7 +216,7 @@ func (bl *baseLogin) finalizeLogin(
}
metadata.Session = bl.session
metadata.LoginMethod = bl.flowID
profile, name := userToRemoteProfile(self, nil, nil)
profile, name := bl.main.userToRemoteProfile(self, nil, nil)
userLoginID := ids.MakeUserLoginID(authorization.User.GetID())
ul, err := bl.user.NewLogin(ctx, &database.UserLogin{
ID: userLoginID,

View File

@@ -41,7 +41,6 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
"go.mau.fi/mautrix-telegram/pkg/connector/telegramfmt"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
"go.mau.fi/mautrix-telegram/pkg/connector/waveform"
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
"go.mau.fi/mautrix-telegram/pkg/gotd/tgerr"
@@ -729,7 +728,7 @@ func (c *TelegramClient) convertMediaRequiringUpload(
func (c *TelegramClient) convertContact(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
contact := media.(*tg.MessageMediaContact)
name := util.FormatFullName(contact.FirstName, contact.LastName, false, contact.UserID)
name := c.main.Config.FormatDisplayname(contact.FirstName, contact.LastName, "", false, contact.UserID)
formattedPhone := fmt.Sprintf("+%s", strings.TrimPrefix(contact.PhoneNumber, "+"))
content := event.MessageEventContent{

View File

@@ -13,7 +13,6 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
)
@@ -258,7 +257,7 @@ func (t *TelegramClient) wrapUserInfo(ctx context.Context, u tg.UserClass, ghost
}
}
name := util.FormatFullName(user.FirstName, user.LastName, user.Deleted, user.ID)
name := t.main.Config.FormatDisplayname(user.FirstName, user.LastName, user.Username, user.Deleted, user.ID)
namePtr := &name
if user.Contact && ghost.Name != "" && oldMeta.ContactSource != t.telegramUserID && oldMeta.ContactSource != 0 && !t.main.Config.ContactNames {
namePtr = nil

View File

@@ -1,29 +0,0 @@
// mautrix-telegram - A Matrix-Telegram puppeting bridge.
// Copyright (C) 2025 Sumner Evans
//
// 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 util
import (
"fmt"
"strings"
)
func FormatFullName(first, last string, deleted bool, userID int64) string {
if deleted {
return fmt.Sprintf("Deleted account %d", userID)
}
return strings.TrimSpace(fmt.Sprintf("%s %s", first, last))
}