147 lines
4.4 KiB
Go
147 lines
4.4 KiB
Go
// mautrix-telegram - A Matrix-Telegram puppeting bridge.
|
|
// Copyright (C) 2024 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 connector
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"maunium.net/go/mautrix/bridgev2"
|
|
"maunium.net/go/mautrix/bridgev2/database"
|
|
|
|
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
|
)
|
|
|
|
const (
|
|
LoginFlowIDPhone = "phone"
|
|
LoginFlowIDQR = "qr"
|
|
|
|
LoginStepIDComplete = "fi.mau.telegram.login.complete"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidPassword = bridgev2.RespError{
|
|
ErrCode: "FI.MAU.TELEGRAM.INVALID_PASSWORD",
|
|
Err: "Invalid password",
|
|
StatusCode: http.StatusBadRequest,
|
|
}
|
|
ErrPhoneCodeInvalid = bridgev2.RespError{
|
|
ErrCode: "FI.MAU.TELEGRAM.PHONE_CODE_INVALID",
|
|
Err: "Invalid phone code",
|
|
StatusCode: http.StatusBadRequest,
|
|
}
|
|
ErrSignUpNotSupported = bridgev2.RespError{
|
|
ErrCode: "FI.MAU.TELEGRAM.SIGN_UP_NOT_SUPPORTED",
|
|
Err: "New account creation is not supported",
|
|
StatusCode: http.StatusBadRequest,
|
|
}
|
|
)
|
|
|
|
func (tg *TelegramConnector) GetLoginFlows() []bridgev2.LoginFlow {
|
|
return []bridgev2.LoginFlow{
|
|
{
|
|
Name: "Phone Number",
|
|
Description: "Login using your Telegram phone number",
|
|
ID: LoginFlowIDPhone,
|
|
},
|
|
{
|
|
Name: "QR Code",
|
|
Description: "Login by scanning a QR code from your phone",
|
|
ID: LoginFlowIDQR,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (tg *TelegramConnector) CreateLogin(ctx context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) {
|
|
switch flowID {
|
|
case LoginFlowIDPhone:
|
|
return &PhoneLogin{user: user, main: tg}, nil
|
|
case LoginFlowIDQR:
|
|
return &QRLogin{user: user, main: tg}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unknown flow ID %s", flowID)
|
|
}
|
|
}
|
|
|
|
func finalizeLogin(ctx context.Context, user *bridgev2.User, authorization *tg.AuthAuthorization, metadata UserLoginMetadata) (*bridgev2.LoginStep, error) {
|
|
userLoginID := ids.MakeUserLoginID(authorization.User.GetID())
|
|
ul, err := user.NewLogin(ctx, &database.UserLogin{
|
|
ID: userLoginID,
|
|
Metadata: &metadata,
|
|
}, &bridgev2.NewLoginParams{
|
|
DeleteOnConflict: true,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to save new login: %w", err)
|
|
}
|
|
ul.Client.Connect(ul.Log.WithContext(ctx))
|
|
client := ul.Client.(*TelegramClient)
|
|
|
|
// Connecting is non-blocking so wait for gotd to initialize before doing anythign to avoid deadlocking
|
|
err = client.clientInitialized.Wait(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
me, err := client.client.Self(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
go func() {
|
|
log := ul.Log.With().Str("component", "login_sync_chats").Logger()
|
|
if err := client.SyncChats(log.WithContext(client.clientCtx)); err != nil {
|
|
log.Err(err).Msg("Failed to sync chats")
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
log := ul.Log.With().Str("component", "login_takeout").Logger()
|
|
client.takeoutLock.Lock()
|
|
defer client.takeoutLock.Unlock()
|
|
_, err = client.getTakeoutID(ctx)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to get takeout")
|
|
return
|
|
}
|
|
if client.stopTakeoutTimer == nil {
|
|
client.stopTakeoutTimer = time.AfterFunc(max(time.Hour, time.Duration(client.main.Bridge.Config.Backfill.Queue.BatchDelay*2)), sync.OnceFunc(func() { client.stopTakeout(ctx) }))
|
|
} else {
|
|
client.stopTakeoutTimer.Reset(max(time.Hour, time.Duration(client.main.Bridge.Config.Backfill.Queue.BatchDelay*2)))
|
|
}
|
|
}()
|
|
|
|
ul.RemoteProfile, ul.RemoteName = userToRemoteProfile(me, nil, nil)
|
|
err = ul.Save(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to save login: %w", err)
|
|
}
|
|
|
|
return &bridgev2.LoginStep{
|
|
Type: bridgev2.LoginStepTypeComplete,
|
|
StepID: LoginStepIDComplete,
|
|
Instructions: fmt.Sprintf("Successfully logged in as %s (`%d`)", ul.RemoteName, me.ID),
|
|
CompleteParams: &bridgev2.LoginCompleteParams{
|
|
UserLoginID: ul.ID,
|
|
UserLogin: ul,
|
|
},
|
|
}, nil
|
|
}
|