(telegram|matrix)fmt: mention formatting
Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
@@ -6,12 +6,12 @@ require (
|
||||
github.com/gotd/td v0.105.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.mau.fi/util v0.6.0
|
||||
go.mau.fi/util v0.6.1-0.20240802175451-b430ebbffc98
|
||||
go.mau.fi/zerozap v0.1.1
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/net v0.27.0
|
||||
maunium.net/go/mautrix v0.19.1-0.20240719130542-cc5f225bc61c
|
||||
maunium.net/go/mautrix v0.19.1-0.20240805194656-9fffe6e54d7e
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -34,7 +34,7 @@ require (
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/tidwall/gjson v1.17.1 // indirect
|
||||
github.com/tidwall/gjson v1.17.3 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
@@ -47,6 +47,7 @@ require (
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
maunium.net/go/mauflag v1.0.0 // indirect
|
||||
|
||||
@@ -58,8 +58,8 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDq
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
|
||||
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
@@ -69,8 +69,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
|
||||
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
go.mau.fi/util v0.6.0 h1:W6SyB3Bm/GjenQ5iq8Z8WWdN85Gy2xS6L0wmnR7SVjg=
|
||||
go.mau.fi/util v0.6.0/go.mod h1:ljYdq3sPfpICc3zMU+/mHV/sa4z0nKxc67hSBwnrk8U=
|
||||
go.mau.fi/util v0.6.1-0.20240802175451-b430ebbffc98 h1:gJ0peWecBm6TtlxKFVIc1KbooXSCHtPfsfb2Eha5A0A=
|
||||
go.mau.fi/util v0.6.1-0.20240802175451-b430ebbffc98/go.mod h1:S1juuPWGau2GctPY3FR/4ec/MDLhAG2QPhdnUwpzWIo=
|
||||
go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM=
|
||||
go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
|
||||
go.mau.fi/zerozap v0.1.1 h1:mxE/dW4wtkqBYOXOEEzXldk5qKB+ahsZXjoTGnvEhZQ=
|
||||
@@ -89,8 +89,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7 h1:wDLEX9a7YQoKdKNQt88rtydkqDxeGaBUTnIYc3iG/mA=
|
||||
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -101,6 +101,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -110,8 +112,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||
maunium.net/go/mautrix v0.19.1-0.20240719130542-cc5f225bc61c h1:wjQNGvMn85CRf8bqwqXsg55hvEuxIO8WH0SvOhzv/1E=
|
||||
maunium.net/go/mautrix v0.19.1-0.20240719130542-cc5f225bc61c/go.mod h1:UE+mSQ4sDUuJMbjN0aB9EjQSGgXd48AzMvZ6+QJV1k8=
|
||||
maunium.net/go/mautrix v0.19.1-0.20240805194656-9fffe6e54d7e h1:OE04/iUnv8oG7UvjzMk40vkkrJvXs3NfbgvdJjAbsFg=
|
||||
maunium.net/go/mautrix v0.19.1-0.20240805194656-9fffe6e54d7e/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q=
|
||||
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
|
||||
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||
|
||||
+43
-4
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -18,8 +19,10 @@ import (
|
||||
"maunium.net/go/mautrix/bridge/status"
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
||||
"go.mau.fi/mautrix-telegram/pkg/connector/matrixfmt"
|
||||
"go.mau.fi/mautrix-telegram/pkg/connector/media"
|
||||
"go.mau.fi/mautrix-telegram/pkg/connector/store"
|
||||
"go.mau.fi/mautrix-telegram/pkg/connector/telegramfmt"
|
||||
@@ -42,6 +45,7 @@ type TelegramClient struct {
|
||||
appConfigHash int
|
||||
|
||||
telegramFmtParams *telegramfmt.FormatParams
|
||||
matrixParser *matrixfmt.HTMLParser
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -150,13 +154,31 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
|
||||
client.reactionMessageLocks = map[int]*sync.Mutex{}
|
||||
|
||||
client.telegramFmtParams = &telegramfmt.FormatParams{
|
||||
GetUserInfo: func(ctx context.Context, id networkid.UserID) (telegramfmt.UserInfo, error) {
|
||||
ghost, err := tc.Bridge.GetGhostByID(ctx, id)
|
||||
GetUserInfoByID: func(ctx context.Context, id int64) (telegramfmt.UserInfo, error) {
|
||||
ghost, err := tc.Bridge.GetGhostByID(ctx, ids.MakeUserID(id))
|
||||
if err != nil {
|
||||
return telegramfmt.UserInfo{}, err
|
||||
}
|
||||
userInfo := telegramfmt.UserInfo{MXID: ghost.Intent.GetMXID(), Name: ghost.Name}
|
||||
if id == client.userID {
|
||||
if id == client.telegramUserID {
|
||||
userInfo.MXID = client.userLogin.UserMXID
|
||||
}
|
||||
return userInfo, nil
|
||||
},
|
||||
GetUserInfoByUsername: func(ctx context.Context, username string) (telegramfmt.UserInfo, error) {
|
||||
ghosts, err := tc.Bridge.DB.Ghost.GetByMetadata(ctx, "username", username)
|
||||
if err != nil {
|
||||
return telegramfmt.UserInfo{}, err
|
||||
}
|
||||
if len(ghosts) != 1 {
|
||||
return telegramfmt.UserInfo{}, fmt.Errorf("username %s not found", username)
|
||||
}
|
||||
ghost, err := tc.Bridge.GetGhostByID(ctx, ghosts[0].ID)
|
||||
if err != nil {
|
||||
return telegramfmt.UserInfo{}, err
|
||||
}
|
||||
userInfo := telegramfmt.UserInfo{MXID: ghost.Intent.GetMXID(), Name: ghost.Name}
|
||||
if ghosts[0].ID == client.userID {
|
||||
userInfo.MXID = client.userLogin.UserMXID
|
||||
}
|
||||
return userInfo, nil
|
||||
@@ -204,6 +226,17 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
|
||||
return fmt.Sprintf("https://matrix.to/#/%s/%s", portal.MXID, message.MXID)
|
||||
},
|
||||
}
|
||||
client.matrixParser = &matrixfmt.HTMLParser{
|
||||
GetGhostDetails: func(ctx context.Context, ui id.UserID) (networkid.UserID, string, int64, bool) {
|
||||
if userID, ok := tc.Bridge.Matrix.ParseGhostMXID(ui); !ok {
|
||||
return "", "", 0, false
|
||||
} else if ghost, err := tc.Bridge.GetGhostByID(ctx, userID); err != nil {
|
||||
return "", "", 0, false
|
||||
} else {
|
||||
return userID, ghost.Metadata.(*GhostMetadata).Username, ghost.Metadata.(*GhostMetadata).AccessHash, true
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
err = updatesManager.Run(ctx, client.client.API(), telegramUserID, updates.AuthOptions{})
|
||||
@@ -302,6 +335,9 @@ func (t *TelegramClient) getUserInfoFromTelegramUser(u tg.UserClass) (*bridgev2.
|
||||
}
|
||||
var identifiers []string
|
||||
if !user.Min {
|
||||
if username, ok := user.GetUsername(); ok {
|
||||
identifiers = append(identifiers, fmt.Sprintf("telegram:%s", username))
|
||||
}
|
||||
for _, username := range user.Usernames {
|
||||
identifiers = append(identifiers, fmt.Sprintf("telegram:%s", username.Username))
|
||||
}
|
||||
@@ -309,6 +345,8 @@ func (t *TelegramClient) getUserInfoFromTelegramUser(u tg.UserClass) (*bridgev2.
|
||||
identifiers = append(identifiers, fmt.Sprintf("tel:+%s", strings.TrimPrefix(phone, "+")))
|
||||
}
|
||||
}
|
||||
slices.Sort(identifiers)
|
||||
identifiers = slices.Compact(identifiers)
|
||||
|
||||
var avatar *bridgev2.Avatar
|
||||
if p, ok := user.GetPhoto(); ok && p.TypeID() == tg.UserProfilePhotoTypeID {
|
||||
@@ -331,9 +369,10 @@ func (t *TelegramClient) getUserInfoFromTelegramUser(u tg.UserClass) (*bridgev2.
|
||||
ExtraUpdates: func(ctx context.Context, ghost *bridgev2.Ghost) (changed bool) {
|
||||
meta := ghost.Metadata.(*GhostMetadata)
|
||||
if !user.Min {
|
||||
changed = changed || meta.IsPremium != user.Premium || meta.IsBot != user.Bot
|
||||
changed = changed || meta.IsPremium != user.Premium || meta.IsBot != user.Bot || meta.Username != user.Username
|
||||
meta.IsPremium = user.Premium
|
||||
meta.IsBot = user.Bot
|
||||
meta.Username = user.Username
|
||||
}
|
||||
changed = changed || meta.AccessHash != user.AccessHash
|
||||
meta.AccessHash = user.AccessHash
|
||||
|
||||
@@ -88,9 +88,10 @@ func (tg *TelegramConnector) GetDBMetaTypes() database.MetaTypes {
|
||||
}
|
||||
|
||||
type GhostMetadata struct {
|
||||
IsPremium bool `json:"is_premium"`
|
||||
IsBot bool `json:"is_bot"`
|
||||
AccessHash int64 `json:"access_hash"`
|
||||
AccessHash int64 `json:"access_hash"`
|
||||
IsPremium bool `json:"is_premium,omitempty"`
|
||||
IsBot bool `json:"is_bot,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
}
|
||||
|
||||
type MessageMetadata struct {
|
||||
|
||||
@@ -87,9 +87,7 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.
|
||||
|
||||
noWebpage := msg.Content.BeeperLinkPreviews != nil && len(msg.Content.BeeperLinkPreviews) == 0
|
||||
|
||||
message, entities := matrixfmt.Parse(ctx, &matrixfmt.HTMLParser{
|
||||
ParseGhostMXID: t.main.Bridge.Matrix.ParseGhostMXID,
|
||||
}, msg.Content)
|
||||
message, entities := matrixfmt.Parse(ctx, t.matrixParser, msg.Content)
|
||||
|
||||
var replyTo tg.InputReplyToClass
|
||||
if msg.ReplyTo != nil {
|
||||
@@ -212,9 +210,7 @@ func (t *TelegramClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Mat
|
||||
return err
|
||||
}
|
||||
|
||||
message, entities := matrixfmt.Parse(ctx, &matrixfmt.HTMLParser{
|
||||
ParseGhostMXID: t.main.Bridge.Matrix.ParseGhostMXID,
|
||||
}, msg.Content)
|
||||
message, entities := matrixfmt.Parse(ctx, t.matrixParser, msg.Content)
|
||||
|
||||
var newContentURI id.ContentURIString
|
||||
req := tg.MessagesEditMessageRequest{
|
||||
|
||||
@@ -29,11 +29,15 @@ import (
|
||||
func toTelegramEntity(br telegramfmt.BodyRange) tg.MessageEntityClass {
|
||||
switch val := br.Value.(type) {
|
||||
case telegramfmt.Mention:
|
||||
userID, _ := ids.ParseUserID(val.UserID)
|
||||
return &tg.MessageEntityMentionName{
|
||||
Offset: br.Start,
|
||||
Length: br.Length,
|
||||
UserID: userID,
|
||||
if val.Username != "" {
|
||||
return &tg.MessageEntityMention{Offset: br.Start, Length: br.Length}
|
||||
} else {
|
||||
userID, _ := ids.ParseUserID(val.UserID)
|
||||
return &tg.InputMessageEntityMentionName{
|
||||
Offset: br.Start,
|
||||
Length: br.Length,
|
||||
UserID: &tg.InputUser{UserID: userID, AccessHash: val.AccessHash},
|
||||
}
|
||||
}
|
||||
case telegramfmt.Style:
|
||||
switch val.Type {
|
||||
@@ -92,12 +96,12 @@ func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventC
|
||||
if parsed == nil {
|
||||
return "", nil
|
||||
}
|
||||
var bodyRanges []tg.MessageEntityClass
|
||||
var entities []tg.MessageEntityClass
|
||||
if len(parsed.Entities) > 0 {
|
||||
bodyRanges = make([]tg.MessageEntityClass, len(parsed.Entities))
|
||||
entities = make([]tg.MessageEntityClass, len(parsed.Entities))
|
||||
for i, ent := range parsed.Entities {
|
||||
bodyRanges[i] = toTelegramEntity(ent)
|
||||
entities[i] = toTelegramEntity(ent)
|
||||
}
|
||||
}
|
||||
return parsed.String.String(), bodyRanges
|
||||
return parsed.String.String(), entities
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ func (ctx Context) WithWhitespace() Context {
|
||||
|
||||
// HTMLParser is a somewhat customizable Matrix HTML parser.
|
||||
type HTMLParser struct {
|
||||
ParseGhostMXID func(id.UserID) (networkid.UserID, bool)
|
||||
GetGhostDetails func(context.Context, id.UserID) (networkid.UserID, string, int64, bool)
|
||||
}
|
||||
|
||||
// TaggedString is a string that also contains a HTML tag.
|
||||
@@ -367,11 +367,14 @@ func (parser *HTMLParser) linkToString(node *html.Node, ctx Context) *EntityStri
|
||||
// Mention not allowed, use name as-is
|
||||
return str
|
||||
}
|
||||
userID, ok := parser.ParseGhostMXID(mxid)
|
||||
userID, username, accessHash, ok := parser.GetGhostDetails(ctx.Ctx, mxid)
|
||||
if !ok {
|
||||
return str
|
||||
} else if username == "" {
|
||||
return ent.Format(telegramfmt.Mention{UserID: userID, AccessHash: accessHash})
|
||||
} else {
|
||||
return NewEntityString("@" + username).Format(telegramfmt.Mention{UserID: userID, Username: username})
|
||||
}
|
||||
return ent.Format(telegramfmt.Mention{UserID: userID})
|
||||
}
|
||||
if str.String.String() == href {
|
||||
return ent.Format(telegramfmt.Style{Type: telegramfmt.StyleURL, URL: href})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- v0 -> v1: Latest revision
|
||||
-- v0 -> v2: Latest revision
|
||||
|
||||
CREATE TABLE telegram_session (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
@@ -37,3 +37,5 @@ CREATE TABLE telegram_file (
|
||||
mime_type TEXT,
|
||||
size BIGINT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ghost_username ON ghost ((metadata->>'username'));
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- v2: Add index for ghost username metadata field
|
||||
|
||||
CREATE INDEX idx_ghost_username ON ghost ((metadata->>'username'));
|
||||
@@ -18,11 +18,11 @@ package telegramfmt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html"
|
||||
"strings"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/exp/maps"
|
||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||
"maunium.net/go/mautrix/event"
|
||||
@@ -37,9 +37,10 @@ type UserInfo struct {
|
||||
}
|
||||
|
||||
type FormatParams struct {
|
||||
CustomEmojis map[networkid.EmojiID]string
|
||||
GetUserInfo func(ctx context.Context, id networkid.UserID) (UserInfo, error)
|
||||
NormalizeURL func(ctx context.Context, url string) string
|
||||
CustomEmojis map[networkid.EmojiID]string
|
||||
GetUserInfoByUsername func(ctx context.Context, username string) (UserInfo, error)
|
||||
GetUserInfoByID func(ctx context.Context, id int64) (UserInfo, error)
|
||||
NormalizeURL func(ctx context.Context, url string) string
|
||||
}
|
||||
|
||||
func (fp FormatParams) GetCustomEmoji(emojiID networkid.EmojiID) (string, id.ContentURIString) {
|
||||
@@ -52,9 +53,10 @@ func (fp FormatParams) GetCustomEmoji(emojiID networkid.EmojiID) (string, id.Con
|
||||
|
||||
func (fp FormatParams) WithCustomEmojis(emojis map[networkid.EmojiID]string) FormatParams {
|
||||
return FormatParams{
|
||||
CustomEmojis: emojis,
|
||||
GetUserInfo: fp.GetUserInfo,
|
||||
NormalizeURL: fp.NormalizeURL,
|
||||
CustomEmojis: emojis,
|
||||
GetUserInfoByUsername: fp.GetUserInfoByUsername,
|
||||
GetUserInfoByID: fp.GetUserInfoByID,
|
||||
NormalizeURL: fp.NormalizeURL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +72,7 @@ func (ctx formatContext) TextToHTML(text string) string {
|
||||
}
|
||||
|
||||
func Parse(ctx context.Context, message string, entities []tg.MessageEntityClass, params FormatParams) (*event.MessageEventContent, error) {
|
||||
log := zerolog.Ctx(ctx).With().Str("func", "Parse").Logger()
|
||||
content := &event.MessageEventContent{
|
||||
MsgType: event.MsgText,
|
||||
Body: message,
|
||||
@@ -90,8 +93,14 @@ func Parse(ctx context.Context, message string, entities []tg.MessageEntityClass
|
||||
}.TruncateEnd(maxLength)
|
||||
switch entity := e.(type) {
|
||||
case *tg.MessageEntityMention:
|
||||
// TODO
|
||||
fmt.Printf("mention = %+v\n", entity)
|
||||
username := utf16Message[e.GetOffset()+1 : e.GetOffset()+e.GetLength()].String()
|
||||
userInfo, err := params.GetUserInfoByUsername(ctx, username)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("username", username).Msg("Failed to get user info for mention")
|
||||
continue // Skip this mention
|
||||
}
|
||||
mentions[userInfo.MXID] = struct{}{}
|
||||
br.Value = Mention{UserInfo: userInfo, Username: username}
|
||||
case *tg.MessageEntityHashtag:
|
||||
br.Value = Style{Type: StyleHashtag}
|
||||
case *tg.MessageEntityBotCommand:
|
||||
@@ -111,13 +120,13 @@ func Parse(ctx context.Context, message string, entities []tg.MessageEntityClass
|
||||
case *tg.MessageEntityTextURL:
|
||||
br.Value = Style{Type: StyleURL, URL: params.NormalizeURL(ctx, entity.URL)}
|
||||
case *tg.MessageEntityMentionName:
|
||||
userID := ids.MakeUserID(entity.UserID)
|
||||
userInfo, err := params.GetUserInfo(ctx, userID)
|
||||
userInfo, err := params.GetUserInfoByID(ctx, entity.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Warn().Err(err).Int64("user_id", entity.UserID).Msg("Failed to get user info for mention")
|
||||
continue // Skip this mention
|
||||
}
|
||||
mentions[userInfo.MXID] = struct{}{}
|
||||
br.Value = Mention{UserInfo: userInfo, UserID: userID}
|
||||
br.Value = Mention{UserInfo: userInfo}
|
||||
case *tg.MessageEntityPhone:
|
||||
br.Value = Style{Type: StylePhone}
|
||||
case *tg.MessageEntityCashtag:
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
@@ -33,15 +32,15 @@ import (
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
formatParams := telegramfmt.FormatParams{
|
||||
GetUserInfo: func(ctx context.Context, userID networkid.UserID) (telegramfmt.UserInfo, error) {
|
||||
if userID == "real" {
|
||||
GetUserInfoByID: func(ctx context.Context, userID int64) (telegramfmt.UserInfo, error) {
|
||||
if userID == 1 {
|
||||
return telegramfmt.UserInfo{
|
||||
MXID: "@test:example.com",
|
||||
Name: "Matrix User",
|
||||
}, nil
|
||||
} else {
|
||||
return telegramfmt.UserInfo{
|
||||
MXID: id.UserID(fmt.Sprintf("@telegram_%s:example.com", userID)),
|
||||
MXID: id.UserID(fmt.Sprintf("@telegram_%d:example.com", userID)),
|
||||
Name: "Signal User",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ import (
|
||||
)
|
||||
|
||||
func (m Mention) Format(message string) string {
|
||||
return fmt.Sprintf(`<a href="%s">%s</a>`, m.MXID.URI().MatrixToURL(), m.Name)
|
||||
if m.Username != "" {
|
||||
return fmt.Sprintf(`<a href="%s">@%s</a>`, m.MXID.URI().MatrixToURL(), m.Username)
|
||||
} else {
|
||||
return fmt.Sprintf(`<a href="%s">%s</a>`, m.MXID.URI().MatrixToURL(), m.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Style) Format(message string) string {
|
||||
|
||||
@@ -31,13 +31,15 @@ type BodyRangeValue interface {
|
||||
|
||||
type Mention struct {
|
||||
UserInfo
|
||||
UserID networkid.UserID
|
||||
UserID networkid.UserID
|
||||
AccessHash int64
|
||||
Username string
|
||||
}
|
||||
|
||||
var _ BodyRangeValue = Mention{}
|
||||
|
||||
func (m Mention) String() string {
|
||||
return fmt.Sprintf("Mention{MXID: id.UserID(%q), Name: %q}", m.MXID, m.Name)
|
||||
return fmt.Sprintf("Mention{MXID: id.UserID(%q), Username: %q, Name: %q}", m.MXID, m.Username, m.Name)
|
||||
}
|
||||
|
||||
func (m Mention) IsCode() bool {
|
||||
|
||||
@@ -182,7 +182,6 @@ func (t *TelegramClient) parseBodyAndHTML(ctx context.Context, message string, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("ce %+v\n", customEmojis) // TODO DEBUG
|
||||
return telegramfmt.Parse(ctx, message, entities, t.telegramFmtParams.WithCustomEmojis(customEmojis))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user