Basic message converter and login

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-05-14 10:03:15 -06:00
parent 043cb7f854
commit 0d502a8c55
6 changed files with 437 additions and 4 deletions
+5 -3
View File
@@ -12,10 +12,12 @@ repos:
rev: v1.0.0-rc.1
hooks:
- id: go-imports
exclude: "pb\\.go$"
args:
- "-local"
- "go.mau.fi/mautrix-telegram"
- "-w"
- id: go-vet-mod
#- id: go-staticcheck-repo-mod
# TODO: reenable this and fix all the problems
- id: go-staticcheck-repo-mod
- repo: https://github.com/beeper/pre-commit-go
rev: v0.3.1
+32
View File
@@ -1,3 +1,35 @@
module go.mau.fi/mautrix-telegram
go 1.21
require (
github.com/gotd/td v0.102.0
github.com/rs/zerolog v1.32.0
go.mau.fi/zerozap v0.1.1
go.uber.org/zap v1.27.0
maunium.net/go/mautrix v0.18.1
)
require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-faster/jx v1.1.0 // indirect
github.com/go-faster/xor v1.0.0 // indirect
github.com/gotd/ige v0.2.2 // indirect
github.com/gotd/neo v0.1.5 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/segmentio/asm v1.2.0 // indirect
go.mau.fi/util v0.4.2 // indirect
go.opentelemetry.io/otel v1.26.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
nhooyr.io/websocket v1.8.11 // indirect
rsc.io/qr v0.2.0 // indirect
)
+76
View File
@@ -0,0 +1,76 @@
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-faster/jx v1.1.0 h1:ZsW3wD+snOdmTDy9eIVgQdjUpXRRV4rqW8NS3t+20bg=
github.com/go-faster/jx v1.1.0/go.mod h1:vKDNikrKoyUmpzaJ0OkIkRQClNHFX/nF3dnTJZb3skg=
github.com/go-faster/xor v0.3.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ=
github.com/go-faster/xor v1.0.0 h1:2o8vTOgErSGHP3/7XwA5ib1FTtUsNtwCoLLBjl31X38=
github.com/go-faster/xor v1.0.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gotd/ige v0.2.2 h1:XQ9dJZwBfDnOGSTxKXBGP4gMud3Qku2ekScRjDWWfEk=
github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0=
github.com/gotd/neo v0.1.5 h1:oj0iQfMbGClP8xI59x7fE/uHoTJD7NZH9oV1WNuPukQ=
github.com/gotd/neo v0.1.5/go.mod h1:9A2a4bn9zL6FADufBdt7tZt+WMhvZoc5gWXihOPoiBQ=
github.com/gotd/td v0.102.0 h1:V6zNba9FV21YiBm1t42ak5jyBFSQzY8+8fwZpOT5lGM=
github.com/gotd/td v0.102.0/go.mod h1:k9JQ7ktxOs4yTpE7X2ZvNtAl+blARhz1ak+Aw0VUHiQ=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
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=
go.mau.fi/util v0.4.2 h1:RR3TOcRHmCF9Bx/3YG4S65MYfa+nV6/rn8qBWW4Mi30=
go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw=
go.mau.fi/zerozap v0.1.1 h1:mxE/dW4wtkqBYOXOEEzXldk5qKB+ahsZXjoTGnvEhZQ=
go.mau.fi/zerozap v0.1.1/go.mod h1:eRYfQIyL4nTvxaBtVoFqfhdd2vp7pxiHdtvMy2w7XVg=
go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/mautrix v0.18.1 h1:a6mUsJixegBNTXUoqC5RQ9gsumIPzKvCubKwF+zmCt4=
maunium.net/go/mautrix v0.18.1/go.mod h1:2oHaq792cSXFGvxLvYw3Gf1L4WVVP4KZcYys5HVk/h8=
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=
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
+166 -1
View File
@@ -1,4 +1,169 @@
package main
func main() {
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"github.com/gotd/td/session"
"github.com/gotd/td/telegram"
"github.com/gotd/td/telegram/auth"
"github.com/gotd/td/telegram/updates"
updhook "github.com/gotd/td/telegram/updates/hook"
"github.com/gotd/td/tg"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"go.mau.fi/zerozap"
"go.uber.org/zap"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-telegram/msgconv"
)
type FileSession struct{}
func (s *FileSession) LoadSession(context.Context) ([]byte, error) {
if data, err := os.ReadFile("session"); err != nil {
return nil, session.ErrNotFound
} else {
return data, nil
}
}
func (s *FileSession) StoreSession(ctx context.Context, data []byte) error {
return os.WriteFile("session", data, 0600)
}
type Authenticator struct{}
func (a *Authenticator) Phone(ctx context.Context) (string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Phone (include country code with +): ")
raw, err := reader.ReadString('\n')
return strings.TrimSpace(raw), err
}
func (a *Authenticator) Password(ctx context.Context) (string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Password: ")
raw, err := reader.ReadString('\n')
return strings.TrimSpace(raw), err
}
func (a *Authenticator) AcceptTermsOfService(ctx context.Context, tos tg.HelpTermsOfService) error {
return nil
}
func (a *Authenticator) SignUp(ctx context.Context) (auth.UserInfo, error) {
panic("not supported")
}
func (a *Authenticator) Code(ctx context.Context, sentCode *tg.AuthSentCode) (string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Code: ")
return reader.ReadString('\n')
}
type FakePortal struct {
}
func (*FakePortal) DownloadMedia(ctx context.Context, uri id.ContentURIString, file *event.EncryptedFileInfo) ([]byte, error) {
return nil, nil
}
func (*FakePortal) UploadMedia(ctx context.Context, roomID id.RoomID, data []byte, fileName, mimeType string) (url id.ContentURIString, file *event.EncryptedFileInfo, err error) {
return id.ContentURIString("mxc://test"), nil, nil
}
func main() {
apiID, err := strconv.ParseInt(os.Args[1], 10, 32)
if err != nil {
panic(err)
}
apiHash := os.Args[2]
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
zaplog := zap.New(zerozap.New(log.Logger))
var authenticator Authenticator
var sessionStorage FileSession
d := tg.NewUpdateDispatcher()
gaps := updates.New(updates.Config{
Handler: d,
Logger: zaplog.Named("gaps"),
})
// https://core.telegram.org/api/obtaining_api_id
client := telegram.NewClient(int(apiID), apiHash, telegram.Options{
SessionStorage: &sessionStorage,
Logger: zaplog,
UpdateHandler: gaps,
Middlewares: []telegram.Middleware{
updhook.UpdateHook(gaps.Handle),
},
})
portal := &FakePortal{}
mc := msgconv.MessageConverter{
PortalMethods: portal,
Client: client,
}
// Setup message update handlers.
d.OnNewChannelMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateNewChannelMessage) error {
log.Info().Any("update", update.Message).Msg("Channel message")
converted := mc.ToMatrix(ctx, update.Message)
fmt.Printf("CONVERTED\n")
fmt.Printf("CONVERTED\n")
for _, part := range converted.Parts {
wrapped := &event.Content{Parsed: part.Content, Raw: part.Extra}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(wrapped)
}
fmt.Printf("CONVERTED\n")
fmt.Printf("CONVERTED\n")
return nil
})
d.OnNewMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateNewMessage) error {
log.Info().Any("update", update.Message).Msg("Message")
return nil
})
d.OnEditChannelMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateEditChannelMessage) error {
fmt.Printf("on edit channel message %v\n", update)
return nil
})
d.OnEditMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateEditMessage) error {
fmt.Printf("on edit message %v\n", update)
return nil
})
if err := client.Run(context.Background(), func(ctx context.Context) error {
authFlow := auth.NewFlow(&authenticator, auth.SendCodeOptions{})
err := client.Auth().IfNecessary(ctx, authFlow)
if err != nil {
return err
}
user, err := client.Self(ctx)
if err != nil {
return fmt.Errorf("error getting self: %w", err)
}
return gaps.Run(ctx, client.API(), user.ID, updates.AuthOptions{
OnStart: func(ctx context.Context) {
log.Info().Msg("gaps started")
},
})
}); err != nil {
panic(err)
}
// Client is closed.
}
+133
View File
@@ -0,0 +1,133 @@
package msgconv
import (
"bytes"
"context"
"fmt"
"os"
"github.com/gotd/td/telegram/downloader"
"github.com/gotd/td/tg"
"maunium.net/go/mautrix/event"
)
type ConvertedMessage struct {
Parts []*ConvertedMessagePart
}
type ConvertedMessagePart struct {
Type event.Type
Content *event.MessageEventContent
Extra map[string]any
}
func getLargestPhotoSize(sizes []tg.PhotoSizeClass) (largest tg.PhotoSizeClass) {
var maxSize int
for _, s := range sizes {
var currentSize int
switch size := s.(type) {
case *tg.PhotoSize:
currentSize = size.GetSize()
case *tg.PhotoCachedSize:
currentSize = max(size.GetW(), size.GetH())
case *tg.PhotoSizeProgressive:
currentSize = max(size.GetW(), size.GetH())
case *tg.PhotoPathSize:
currentSize = len(size.GetBytes())
case *tg.PhotoStrippedSize:
currentSize = len(size.GetBytes())
}
if currentSize > maxSize {
maxSize = currentSize
largest = s
}
}
return
}
func (mc *MessageConverter) downloadFile(ctx context.Context, file tg.InputFileLocationClass) ([]byte, error) {
var buf bytes.Buffer
_, err := downloader.NewDownloader().Download(mc.Client.API(), file).Stream(ctx, &buf)
return buf.Bytes(), err
}
func (mc *MessageConverter) ToMatrix(ctx context.Context, msg tg.MessageClass) *ConvertedMessage {
log := mc.getLogger(ctx).With().Str("action", "to_matrix").Logger()
cm := &ConvertedMessage{
Parts: make([]*ConvertedMessagePart, 0),
}
switch v := msg.(type) {
case *tg.Message:
if v.Message != "" {
converted := ConvertedMessagePart{
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgText,
Body: v.Message,
},
}
cm.Parts = append(cm.Parts, &converted)
}
if m, ok := v.GetMedia(); ok {
switch media := m.(type) {
case *tg.MessageMediaPhoto: // messageMediaPhoto#695150d7
fmt.Printf("photo %v\n", media)
if media.GetSpoiler() {
// TODO do something
fmt.Printf("SPOILER\n")
}
if p, ok := media.GetPhoto(); ok {
switch photo := p.(type) {
case *tg.Photo: // photo#fb197a65
fmt.Printf("photo: %v\n", photo)
largest := getLargestPhotoSize(photo.GetSizes())
file := tg.InputPhotoFileLocation{
ID: photo.GetID(),
AccessHash: photo.GetAccessHash(),
FileReference: photo.GetFileReference(),
ThumbSize: largest.GetType(),
}
data, err := mc.downloadFile(ctx, &file)
if err != nil {
panic(err)
}
err = os.WriteFile("/home/sumner/tmp/test.jpg", data, 0644)
if err != nil {
panic(err)
}
default:
log.Error().Type("msg", msg).Msg("Unhandled photo type")
}
}
case *tg.MessageMediaGeo: // messageMediaGeo#56e0d474
case *tg.MessageMediaContact: // messageMediaContact#70322949
case *tg.MessageMediaUnsupported: // messageMediaUnsupported#9f84f49e
case *tg.MessageMediaDocument: // messageMediaDocument#4cf4d72d
case *tg.MessageMediaWebPage: // messageMediaWebPage#ddf10c3b
case *tg.MessageMediaVenue: // messageMediaVenue#2ec0533f
case *tg.MessageMediaGame: // messageMediaGame#fdb19008
case *tg.MessageMediaInvoice: // messageMediaInvoice#f6a548d3
case *tg.MessageMediaGeoLive: // messageMediaGeoLive#b940c666
case *tg.MessageMediaPoll: // messageMediaPoll#4bd6e798
case *tg.MessageMediaDice: // messageMediaDice#3f7ee58b
case *tg.MessageMediaStory: // messageMediaStory#68cb6283
case *tg.MessageMediaGiveaway: // messageMediaGiveaway#daad85b0
case *tg.MessageMediaGiveawayResults: // messageMediaGiveawayResults#c6991068
default:
log.Error().Type("msg", msg).Msg("Unhandled media type")
}
}
case *tg.MessageService:
fmt.Printf("%v\n", v)
default:
log.Error().Type("msg", msg).Msg("Unhandled message type")
}
return cm
}
+25
View File
@@ -0,0 +1,25 @@
package msgconv
import (
"context"
"github.com/gotd/td/telegram"
"github.com/rs/zerolog"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type PortalMethods interface {
DownloadMedia(ctx context.Context, uri id.ContentURIString, file *event.EncryptedFileInfo) ([]byte, error)
UploadMedia(ctx context.Context, roomID id.RoomID, data []byte, fileName, mimeType string) (url id.ContentURIString, file *event.EncryptedFileInfo, err error)
}
type MessageConverter struct {
PortalMethods
Client *telegram.Client
}
func (*MessageConverter) getLogger(ctx context.Context) zerolog.Logger {
return zerolog.Ctx(ctx).With().Str("component", "message_converter").Logger()
}