package bot import ( "context" "log/slog" "strconv" "sync" "gitea.mrixs.me/Mrixs/yamusic-bot/internal/interfaces" "gitea.mrixs.me/Mrixs/yamusic-bot/internal/model" "gitea.mrixs.me/Mrixs/yamusic-bot/internal/processor" "gitea.mrixs.me/Mrixs/yamusic-bot/pkg/yamusic" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) // InlineHandler обрабатывает inline-запросы. type InlineHandler struct { yandex interfaces.YandexMusicClient processor *processor.TrackProcessor telegram interfaces.TelegramClient } // NewInlineHandler создает новый обработчик inline-запросов. func NewInlineHandler(yandex interfaces.YandexMusicClient, processor *processor.TrackProcessor, telegram interfaces.TelegramClient) *InlineHandler { return &InlineHandler{ yandex: yandex, processor: processor, telegram: telegram, } } // HandleInlineQuery обрабатывает входящий inline-запрос. func (h *InlineHandler) HandleInlineQuery(ctx context.Context, query *tgbotapi.InlineQuery) { slog.Info("Handling inline query", "user_id", query.From.ID, "query", query.Query) urlInfo, err := yamusic.ParseYandexURL(query.Query) if err != nil { h.answerWithError(ctx, query.ID, "Неверный формат ссылки. Поддерживаются ссылки на треки, альбомы и исполнителей Yandex.Music.") return } var trackInfos []*model.TrackInfo switch urlInfo.Type { case "track": info, err := h.yandex.GetTrackInfo(ctx, urlInfo.TrackID) if err != nil { slog.Error("Failed to get track info", "track_id", urlInfo.TrackID, "error", err) h.answerWithError(ctx, query.ID, "Не удалось получить информацию о треке.") return } trackInfos = append(trackInfos, info) case "album": infos, err := h.yandex.GetAlbumTrackInfos(ctx, urlInfo.AlbumID) if err != nil { slog.Error("Failed to get album info", "album_id", urlInfo.AlbumID, "error", err) h.answerWithError(ctx, query.ID, "Не удалось получить информацию об альбоме.") return } trackInfos = infos case "artist": infos, err := h.yandex.GetArtistTrackInfos(ctx, urlInfo.ArtistID) if err != nil { slog.Error("Failed to get artist info", "artist_id", urlInfo.ArtistID, "error", err) h.answerWithError(ctx, query.ID, "Не удалось получить информацию об исполнителе.") return } trackInfos = infos } if len(trackInfos) == 0 { h.answerWithError(ctx, query.ID, "По этой ссылке ничего не найдено.") return } h.processAndAnswer(ctx, query.ID, trackInfos) } func (h *InlineHandler) processAndAnswer(ctx context.Context, queryID string, trackInfos []*model.TrackInfo) { var wg sync.WaitGroup resultsChan := make(chan interface{}, len(trackInfos)) for i, info := range trackInfos { wg.Add(1) go func(trackInfo *model.TrackInfo, resultID int) { defer wg.Done() fileID, err := h.processor.Process(ctx, trackInfo) if err != nil { slog.Error("Failed to process track", "track_id", trackInfo.YandexTrackID, "error", err) return } result := tgbotapi.NewInlineQueryResultAudio(strconv.Itoa(resultID), fileID, trackInfo.Title) result.Performer = trackInfo.Artist resultsChan <- result }(info, i) } wg.Wait() close(resultsChan) var finalResults []interface{} for result := range resultsChan { finalResults = append(finalResults, result) } if len(finalResults) == 0 { slog.Warn("No results to send after processing", "query_id", queryID) h.answerWithError(ctx, queryID, "Не удалось обработать ни один трек.") return } if err := h.telegram.AnswerInlineQuery(ctx, queryID, finalResults); err != nil { slog.Error("Failed to send final answer to inline query", "error", err) } } func (h *InlineHandler) answerWithError(ctx context.Context, queryID, message string) { article := tgbotapi.NewInlineQueryResultArticle(queryID, "Ошибка", message) if err := h.telegram.AnswerInlineQuery(ctx, queryID, []interface{}{article}); err != nil { slog.Error("Failed to answer with error", "error", err) } }