package main import ( "context" "log/slog" "os" "os/signal" "strconv" "strings" "syscall" tgbot "github.com/go-telegram/bot" "github.com/go-telegram/bot/models" "github.com/joho/godotenv" "uptodownBot/internal/dl" "uptodownBot/internal/handler" "uptodownBot/internal/repo" ) func main() { slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelInfo, }))) godotenv.Load() token := os.Getenv("BOT_TOKEN") dbPath := getEnvDefault("DB_PATH", "data/uptodown.db") downloadDir := getEnvDefault("DOWNLOAD_DIR", "/tmp/uptodown") maxFileSizeMB := getEnvIntDefault("MAX_FILE_SIZE_MB", 2000) cookiesFile := os.Getenv("COOKIES_FILE") if token == "" { slog.Error("BOT_TOKEN is required") os.Exit(1) } store, err := repo.NewStore(dbPath) if err != nil { slog.Error("failed to open database", "path", dbPath, "error", err) os.Exit(1) } ytdlp, err := dl.NewClient(cookiesFile) if err != nil { slog.Error("failed to init yt-dlp", "error", err) os.Exit(1) } allowedUsers := parseAllowedUsers(os.Getenv("BOT_ALLOWED_USERS")) var h *handler.Handler dispatch := func(ctx context.Context, b *tgbot.Bot, update *models.Update) { if update.Message != nil { h.HandleMessage(ctx, update.Message) } if update.CallbackQuery != nil { h.HandleCallback(ctx, update.CallbackQuery) } } b, err := tgbot.New(token, tgbot.WithDefaultHandler(dispatch)) if err != nil { slog.Error("failed to create bot", "error", err) os.Exit(1) } h = handler.NewHandler(b, store, ytdlp, allowedUsers, downloadDir, int64(maxFileSizeMB)*1024*1024) ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() if url := os.Getenv("BOT_WEBHOOK_URL"); url != "" { startWebhook(ctx, b, h, url) } else { if _, err := b.DeleteWebhook(ctx, &tgbot.DeleteWebhookParams{}); err != nil { slog.Warn("failed to delete webhook", "error", err) } slog.Info("bot started", "allowed_users", len(allowedUsers), "mode", "polling", ) b.Start(ctx) } store.Close() } func parseAllowedUsers(raw string) map[int64]bool { m := make(map[int64]bool) for _, s := range strings.Split(raw, ",") { s = strings.TrimSpace(s) if s == "" { continue } id, err := strconv.ParseInt(s, 10, 64) if err == nil { m[id] = true } else { slog.Warn("invalid user ID in BOT_ALLOWED_USERS", "value", s) } } return m } func getEnvDefault(key, defaultValue string) string { if val := os.Getenv(key); val != "" { return val } return defaultValue } func getEnvIntDefault(key string, defaultVal int) int { if val := os.Getenv(key); val != "" { n, err := strconv.Atoi(val) if err == nil { return n } } return defaultVal }