From 20090e5bb11c1a3f811b4d036e19c96e3d43cd12 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Mon, 23 Jun 2025 08:30:27 +0300 Subject: [PATCH] contracts and models --- go.mod | 14 +------- go.sum | 2 ++ internal/config/config.go | 56 +++++++++++++++++++++++++++++++ internal/interfaces/interfaces.go | 42 +++++++++++++++++++++++ internal/model/model.go | 15 +++++++++ 5 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 go.sum create mode 100644 internal/config/config.go create mode 100644 internal/interfaces/interfaces.go create mode 100644 internal/model/model.go diff --git a/go.mod b/go.mod index 45d33f8..fb8a94a 100644 --- a/go.mod +++ b/go.mod @@ -2,16 +2,4 @@ module gitea.mrixs.me/Mrixs/yamusic-bot go 1.24 -require ( - github.com/bogem/id3v2 v1.2.1 - github.com/caarlos0/env/v10 v10.0.0 - github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 - github.com/mattn/go-sqlite3 v1.14.22 - golang.org/x/sync v0.7.0 -) - -require ( - github.com/lmittmann/tint v1.0.4 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/text v0.14.0 // indirect -) +require github.com/caarlos0/env/v10 v10.0.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ac2ad93 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA= +github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..25ed4d5 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,56 @@ +package config + +import ( + "fmt" + "log" + "strings" + + "github.com/caarlos0/env/v10" +) + +// Config содержит всю конфигурацию приложения, получаемую из переменных окружения. +type Config struct { + TelegramBotToken string `env:"TELEGRAM_BOT_TOKEN,required"` + TelegramAdminIDsRaw string `env:"TELEGRAM_ADMIN_IDS,required"` + TelegramCacheChatID int64 `env:"TELEGRAM_CACHE_CHAT_ID,required"` + YandexMusicToken string `env:"YANDEX_MUSIC_TOKEN"` + DatabasePath string `env:"DATABASE_PATH" envDefault:"/data/bot.db"` + LogLevel string `env:"LOG_LEVEL" envDefault:"info"` + ProcessorWorkers int `env:"PROCESSOR_WORKERS" envDefault:"4"` + YandexAPIRateLimit int `env:"YANDEX_API_RATE_LIMIT" envDefault:"5"` + TelegramAdminIDs []int64 `env:"-"` // Это поле будет заполнено после парсинга +} + +// New загружает конфигурацию из переменных окружения и парсит необходимые поля. +func New() *Config { + cfg := &Config{} + if err := env.Parse(cfg); err != nil { + log.Fatalf("failed to parse config: %+v", err) + } + + // Парсим ID администраторов из строки + if cfg.TelegramAdminIDsRaw != "" { + ids := strings.Split(cfg.TelegramAdminIDsRaw, ",") + cfg.TelegramAdminIDs = make([]int64, 0, len(ids)) + for _, idStr := range ids { + var id int64 + if _, err := Sscanf(strings.TrimSpace(idStr), "%d", &id); err == nil { + cfg.TelegramAdminIDs = append(cfg.TelegramAdminIDs, id) + } else { + log.Printf("warning: could not parse admin ID: %s", idStr) + } + } + } + + if len(cfg.TelegramAdminIDs) == 0 { + log.Fatalf("no valid admin IDs provided in TELEGRAM_ADMIN_IDS") + } + + return cfg +} + +// Sscanf - простая реализация для парсинга, чтобы избежать лишних зависимостей. +// В стандартной библиотеке fmt.Sscanf требует, чтобы вся строка была разобрана. +func Sscanf(str, format string, a ...interface{}) (int, error) { + return fmt.Sscanf(str, format, a...) +} diff --git a/internal/interfaces/interfaces.go b/internal/interfaces/interfaces.go new file mode 100644 index 0000000..9751c92 --- /dev/null +++ b/internal/interfaces/interfaces.go @@ -0,0 +1,42 @@ +package interfaces + +import ( + "context" + + "gitea.mrixs.me/Mrixs/yamusic-bot/internal/model" +) + +// YandexMusicClient определяет методы для взаимодействия с API Yandex.Music. +type YandexMusicClient interface { + GetTrackInfo(ctx context.Context, trackID string) (*model.TrackInfo, error) + GetAlbumTrackInfos(ctx context.Context, albumID string) ([]*model.TrackInfo, error) + GetArtistTrackInfos(ctx context.Context, artistID string) ([]*model.TrackInfo, error) + GetDownloadURL(ctx context.Context, trackID string) (string, error) +} + +// TrackStorage определяет методы для работы с постоянным кэшем. +type TrackStorage interface { + Get(ctx context.Context, yandexTrackID string) (telegramFileID string, err error) + Set(ctx context.Context, yandexTrackID, telegramFileID string) error + Count(ctx context.Context) (int, error) + Close() error +} + +// TelegramClient определяет методы для взаимодействия с Telegram Bot API. +// Мы определяем свой интерфейс, чтобы не зависеть напрямую от библиотеки +// и упростить тестирование. +type TelegramClient interface { + SendAudioToCacheChannel(ctx context.Context, audioPath, title, performer string) (string, error) + AnswerInlineQuery(ctx context.Context, queryID string, results []interface{}) error + SendMessage(ctx context.Context, chatID int64, text string) error +} + +// Tagger определяет методы для работы с метаданными аудиофайлов. +type Tagger interface { + WriteTags(filePath string, coverPath string, info *model.TrackInfo) error +} + +// FileDownloader определяет метод для скачивания файла. +type FileDownloader interface { + Download(ctx context.Context, url string) (filePath string, err error) +} diff --git a/internal/model/model.go b/internal/model/model.go new file mode 100644 index 0000000..011301f --- /dev/null +++ b/internal/model/model.go @@ -0,0 +1,15 @@ +package model + +// TrackInfo содержит всю необходимую информацию о треке для его обработки и тегирования. +type TrackInfo struct { + YandexTrackID string + YandexAlbumID string + Title string + Album string + Artist string + Year int + Genre string + TrackPosition int + CoverURL string + DownloadURL string +}