diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc2a6d05..52a8cd3b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/go.mod b/go.mod index 5d8a1377..17c75544 100644 --- a/go.mod +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..f0445aab --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go index da29a2ca..4dd3a826 100644 --- a/main.go +++ b/main.go @@ -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. } diff --git a/msgconv/from-tg.go b/msgconv/from-tg.go new file mode 100644 index 00000000..e524f0fc --- /dev/null +++ b/msgconv/from-tg.go @@ -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 +} diff --git a/msgconv/msgconv.go b/msgconv/msgconv.go new file mode 100644 index 00000000..4e8d8128 --- /dev/null +++ b/msgconv/msgconv.go @@ -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() +}