contracts and models

This commit is contained in:
2025-06-23 08:30:27 +03:00
parent 15686b146a
commit 20090e5bb1
5 changed files with 116 additions and 13 deletions

14
go.mod
View File

@@ -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

2
go.sum Normal file
View File

@@ -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=

56
internal/config/config.go Normal file
View File

@@ -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...)
}

View File

@@ -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)
}

15
internal/model/model.go Normal file
View File

@@ -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
}