Очень долгое время слушал музыку непосредственно в Telegram. Там и пообщаться можно и проигрыватель довольно удобный. Но рано или поздно - каждый сталкивается с проблемой, когда свой плейлист надоедает и хочется чего-то нового. В YouTube Music есть довольно удобная функция, которая позволяет каждый день довольствоваться новой музыкой. Что я имею ввиду? При включении любой песни, далее проигрывается трек, который похож на предыдущий. Таким образом можно постоянно открывать для себя новые музыкальные горизонты :)
В Телеграмме у меня было сохранено более 1000 песен. Я решил написать бота, который будет считывать пересланную музыку из ТГ, создавать плейлист в YouTube и загружать все песни туда. Таким образом и чему-то новому научусь и песни загружу(ну и если кому-то из знакомых понадобятся услуги моего бота, это может сэкономить им время, хотя я мало верю, что кому-то это пригодится, это больше небольшой, учебный проектик).
Для начала нужно зайти в тг, и с помощью этого бота, создать своего:

После этого, я создал проект .NET 5 в Visual Studio 2019. Используя NuGet, а именно, эти 4 библиотеки:

Newtonsoft.Json - является не обязательной, добавил исключительно для удобства. Можно использовать встроенную в .NET: using System.Text.Json;
Создал класс Brain, который будет управлять всеми жизненными основными процессами бота:
class Brain { public static ITelegramBotClient Bot { get; set; } = new TelegramBotClient("token"); // Заменить на свой токен public static async Task HandleMessageAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) { Console.WriteLine(JsonConvert.SerializeObject(update)); if (update.Type == Telegram.Bot.Types.Enums.UpdateType.Message) { var message = update.Message; String queryString = ""; if (message.Text != null) { if (message.Text.ToLower() == "/start") { // Тут можно описать какую-то логику для приветствия, или произвести // инициализацию бота, если такая требуется await botClient.SendTextMessageAsync(message.Chat, "Добро пожаловать, полосатый"); return; } queryString = message.Text;// Если написать название песни текстом, вместо того, // чтобы переслать ее. Это должно тоже работать. Поэтому мы отслеживаем текст, // который присылают боту } if (message.Audio != null) //если это музыка, берем имя автора и песни { Audio audio = message.Audio; queryString = audio.Performer; queryString += " - " + audio.Title; } String url = "https://music.youtube.com/search?q="; queryString = queryString.Replace(" ", "+"); Console.WriteLine(url += queryString); await botClient.SendTextMessageAsync(message.Chat, url); return; } } public static async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) { Console.WriteLine(JsonConvert.SerializeObject(exception)); } }
Класс Program и непосредственно сам Main:
class Program { static void Main(string[] args) { Console.WriteLine($"Bot has been started: {Brain.Bot.GetMeAsync().Result.FirstName}"); var cts = new CancellationTokenSource(); var cancellationToken = cts.Token; var receiverOptions = new ReceiverOptions { AllowedUpdates = { }, }; Brain.Bot.StartReceiving( Brain.HandleMessageAsync, Brain.HandleErrorAsync, receiverOptions, cancellationToken ); Console.ReadLine(); } }
Итак, на данный момент, наш бот уже умеет распознавать текст и готовые песни. После чего он берет их название, преобразовывает в query string, добавляет к стандартному url'у и возвращает готовую ссылку пользователю в ответ. Выглядит это примерно вот так:

Кстати, ему уже сейчас можно пересылать песни огромными пачками, в ответ от присылает столько же ссылок на них :)
Это не совсем то, что нам нужно, но мы уже ближе подобрались к решению.
Теперь нам нужно создавать плейлист, и загружать песни туда. Чтобы сделать это необходимо залогиниться в гугл, я буду использовать OAuth2. Прежде всего, я создал приложение в консоли: подключил необходимый инструмент для вызова апи YouTube(YouTube Data API v3) и настроил OAuth2 логин.
Описал класс YTMusicController:
class YTMusicController { private static String client_id = "your client id"; private static String client_secret = "your client secret"; internal static UserCredential credential; private static YouTubeService youTubeService = new YouTubeService(new BaseClientService.Initializer() { ApiKey = "your api key", // можно получить в приложении гугл консоли HttpClientInitializer = Brain.credential, ApplicationName = "YTMusic" }); private static void Reinit() { youTubeService = new YouTubeService(new BaseClientService.Initializer() { ApiKey = "AIzaSyDLk83ykqHtYOOjDwtvZvSf5S7wOIvGeGY", HttpClientInitializer = credential, ApplicationName = "YTMusic" }); } public static async Task Login() // функция которая позволяет залогинится с помощью OAuth2 { // вызывается один раз при запуске бота var client_secrets = new ClientSecrets(); client_secrets.ClientId = YTMusicController.client_id; client_secrets.ClientSecret = YTMusicController.client_secret credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( client_secrets, new[] { YouTubeService.Scope.Youtube }, "user", CancellationToken.None, new FileDataStore("YTMusicBrain") ); Reinit(); // переприсваиваем объект YouTubeService с УЖЕ залогиненным юзером } public static async Task CreateNewPlaylist(Chat chatId) { Playlist playlist = new Playlist(); playlist.Snippet = new PlaylistSnippet(); var t = Guid.NewGuid().ToString(); // плейлист с случайным имене playlist.Snippet.Title = t; playlist.Snippet.Description = "This playlist created automatically"; playlist.Status = new PlaylistStatus(); playlist.Status.PrivacyStatus = "public"; playlist = await youTubeService.Playlists.Insert(playlist, "snippet, status").ExecuteAsync(); // после создания плейлиста я записываю его в мапу которая хранит значение // плейлиста по значению чат Brain.chat_playlist.Add(chatId.Id, playlist); public static async Task<SearchResult> Search(String searchQuery) { var searchListResult = youTubeService.Search.List("snippet"); // формируем запрос для searchListResult.Q = searchQuery; // для получения указанной песни searchListResult.MaxResults = 1 var response = await searchListResult.ExecuteAsync();// отправляем запро return response.Items[0]; public static async Task AddToTestPlaylist(Chat chatId, String searchQuery) { Playlist main = Brain.chat_playlist[chatId.Id]; var temp = new PlaylistItem() var resultOfSearch = await Search(searchQuery); // получаем указанную песн temp.Snippet = new PlaylistItemSnippet(); temp.Snippet.PlaylistId = main.Id; temp.Snippet.ResourceId = new ResourceId(); temp.Snippet.ResourceId.Kind = "youtube#video"; temp.Snippet.ResourceId.VideoId = resultOfSearch.Id.VideoId; // создаем новый плейлист, который помещаем в наш уже созданный await youTubeService.PlaylistItems.Insert(temp, "snippet").ExecuteAsync(); return; } }
Добавил команду "/create" через упомянутого выше бота. После ее вызова у нас будет создаваться плейлист в YouTube. Дополним функцию HandleMessageAsync следующим кодом:
if (YTMusicController.credential == null) { await YTMusicController.Login(); } // ... ..... ... // if (message.Text.ToLower() == "/create") { await YTMusicController.CreateNewPlaylist(message.Chat); } if ( chat_playlist != null && chat_playlist.ContainsKey(message.Chat.Id)) { await YTMusicController.AddToTestPlaylist(message.Chat, queryString); } // если плейлист есть в списке, мы записываем в него найденную песню // иначе мы просто возвращаем ссылку на песню else { String url = "https://music.youtube.com/search?q="; queryString = queryString.Replace(" ", "+"); Console.WriteLine(url += queryString); await botClient.SendTextMessageAsync(message.Chat, url); return; }
Полный код класса Brain:
class Brain { public static ITelegramBotClient Bot { get; set; } = new TelegramBotClient("Api Key"); public static Dictionary<long, Playlist> chat_playlist = new Dictionary<long, Playlist>(); public static async Task HandleMessageAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) { if (YTMusicController.credential == null) await YTMusicController.Login(); Console.WriteLine(JsonConvert.SerializeObject(update)); if (update.Type == Telegram.Bot.Types.Enums.UpdateType.Message) { var message = update.Message; String queryString = ""; if (message.Text != null) { if (message.Text.ToLower() == "/start") { await botClient.SendTextMessageAsync(message.Chat, "Добро пожаловать, полосатый"); return; } if (message.Text.ToLower() == "/create") { await YTMusicController.CreateNewPlaylist(message.Chat); } queryString = message.Text; return; } if (message.Audio != null) { Audio audio = message.Audio; queryString = audio.Performer; queryString += " - " + audio.Title; } if ( chat_playlist != null && chat_playlist.ContainsKey(message.Chat.Id)) { await YTMusicController.AddToTestPlaylist(message.Chat, queryString); } else { String url = "https://music.youtube.com/search?q="; queryString = queryString.Replace(" ", "+"); Console.WriteLine(url += queryString); await botClient.SendTextMessageAsync(message.Chat, url); return; } } } public static async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) { Console.WriteLine(JsonConvert.SerializeObject(exception)); } }
Собственно, готово. Вот как это работает:
Единоразово при запуске бота, выполняется логин.
При вызове команды /create, создается новый плейлист, который записывается по ID чата в коллекцию(до вызова команды /create бот просто возвращает ссылку на песню).
Когда боту присылаешь песню, или название песни, он ищет ее в YouTube, возвращает, и там мы добавляем эту песню в наш плейлист.
Вообще, в идеале, было бы хранить данные плейлистов в какой-нибудь базе данных или на облаке, в крайнем случае, хотя бы записывать в файл(однако, я не заморачивался).
Проект исключительно "for fun", так что не судите строго:)
