package telegram import ( "errors" "fmt" "github.com/gofiber/fiber/v2" "github.com/rs/xid" "path/filepath" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors" "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram" "penahub.gitlab.yandexcloud.net/backend/tdlib/client" "strconv" ) type Deps struct { DAL *dal.DAL TelegramClient *telegram.TelegramClient } type Telegram struct { dal *dal.DAL telegramClient *telegram.TelegramClient } func NewTelegramController(deps Deps) *Telegram { return &Telegram{ dal: deps.DAL, telegramClient: deps.TelegramClient, } } type Message struct { Type string `json:"type"` Data string `json:"data"` } func (r *Telegram) GetPoolTgAccounts(ctx *fiber.Ctx) error { allAccounts, err := r.dal.TgRepo.GetAllTgAccounts(ctx.Context()) if err != nil { switch { case errors.Is(err, pj_errors.ErrNotFound): return ctx.Status(fiber.StatusNotFound).SendString("not found") default: return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } } return ctx.Status(fiber.StatusOK).JSON(allAccounts) } func (r *Telegram) AddingTgAccount(ctx *fiber.Ctx) error { var req telegram.AuthTgUserReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } if req.ApiID == 0 || req.ApiHash == "" || req.Password == "" || req.PhoneNumber == "" { return ctx.Status(fiber.StatusBadRequest).SendString("empty required fields") } allAccounts, err := r.dal.TgRepo.GetAllTgAccounts(ctx.Context()) if err != nil && !errors.Is(err, pj_errors.ErrNotFound) { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } if !errors.Is(err, pj_errors.ErrNotFound) { for _, account := range allAccounts { if account.ApiID == req.ApiID && account.ApiHash == req.ApiHash && account.Status == model.ActiveTg { return ctx.Status(fiber.StatusConflict).SendString("this account already exist and active") } } } authorizer := client.ClientAuthorizerr() authorizer.TdlibParameters <- &client.SetTdlibParametersRequest{ UseTestDc: false, DatabaseDirectory: filepath.Join(".tdlib", "database"), FilesDirectory: filepath.Join(".tdlib", "files"), UseFileDatabase: true, UseChatInfoDatabase: true, UseMessageDatabase: true, UseSecretChats: true, ApiId: req.ApiID, ApiHash: req.ApiHash, SystemLanguageCode: "en", DeviceModel: "Server", SystemVersion: "1.0.0", ApplicationVersion: "1.0.0", } _, err = client.SetLogVerbosityLevel(&client.SetLogVerbosityLevelRequest{ NewVerbosityLevel: 1, }) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } var tdlibClient *client.Client // завершается уже в другом контроллере var goErr error // todo ужно продумать завершение горутины если код вставлять не пошли go func() { tdlibClient, goErr = client.NewClient(authorizer) if goErr != nil { fmt.Println("new client failed", err) return } r.telegramClient.SaveTgAccount(req.ApiID, req.ApiHash, tdlibClient) fmt.Println("i am down") }() if goErr != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(goErr.Error()) } for { state, ok := <-authorizer.State if !ok { return ctx.Status(fiber.StatusOK).SendString("state chan is close auth maybe ok") } fmt.Println("currnet state:", state) switch state.AuthorizationStateType() { case client.TypeAuthorizationStateWaitPhoneNumber: authorizer.PhoneNumber <- req.PhoneNumber case client.TypeAuthorizationStateWaitCode: signature := xid.New() r.telegramClient.AddedToMap(telegram.WaitingClient{ PreviousReq: req, Authorizer: authorizer, }, signature.String()) return ctx.Status(fiber.StatusOK).JSON(fiber.Map{"signature": signature.String()}) case client.TypeAuthorizationStateLoggingOut, client.TypeAuthorizationStateClosing, client.TypeAuthorizationStateClosed: return ctx.Status(fiber.StatusForbidden).SendString(fmt.Sprintf("auth failed, last state is %s", state)) } } } func (r *Telegram) SettingTgCode(ctx *fiber.Ctx) error { var req struct { Code string `json:"code"` Signature string `json:"signature"` } if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } if req.Code == "" || req.Signature == "" { return ctx.Status(fiber.StatusBadRequest).SendString("empty required fields") } data, ok := r.telegramClient.GetFromMap(req.Signature) if !ok { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid id, don't have data") } data.Authorizer.Code <- req.Code for { state, ok := <-data.Authorizer.State if !ok { return ctx.Status(fiber.StatusNoContent).SendString("state chan is close auth maybe ok") } fmt.Println("currnet state:", state) switch state.AuthorizationStateType() { case client.TypeAuthorizationStateReady: id, err := r.dal.TgRepo.CreateTgAccount(ctx.Context(), model.TgAccount{ ApiID: data.PreviousReq.ApiID, ApiHash: data.PreviousReq.ApiHash, PhoneNumber: data.PreviousReq.PhoneNumber, Status: model.ActiveTg, Password: data.PreviousReq.Password, }) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.Status(fiber.StatusOK).JSON(fiber.Map{"id": id}) case client.TypeAuthorizationStateWaitPassword: data.Authorizer.Password <- data.PreviousReq.Password case client.TypeAuthorizationStateLoggingOut, client.TypeAuthorizationStateClosing, client.TypeAuthorizationStateClosed: return ctx.Status(fiber.StatusForbidden).SendString(fmt.Sprintf("auth failed, last state is %s", state)) } } } func (r *Telegram) DeleteTgAccountByID(ctx *fiber.Ctx) error { id, err := strconv.ParseInt(ctx.Params("id"), 10, 64) if err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("invalid id format") } err = r.dal.TgRepo.SoftDeleteTgAccount(ctx.Context(), id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.SendStatus(fiber.StatusOK) }