Очень долгое время слушал музыку непосредственно в 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));
	}
}


Собственно, готово. Вот как это работает:

  1. Единоразово при запуске бота, выполняется логин.

  2. При вызове команды /create, создается новый плейлист, который записывается по ID чата в коллекцию(до вызова команды /create бот просто возвращает ссылку на песню).

  3. Когда боту присылаешь песню, или название песни, он ищет ее в YouTube, возвращает, и там мы добавляем эту песню в наш плейлист.

Вообще, в идеале, было бы хранить данные плейлистов в какой-нибудь базе данных или на облаке, в крайнем случае, хотя бы записывать в файл(однако, я не заморачивался).

Проект исключительно "for fun", так что не судите строго:)

Комментарии (4)


  1. makar_crypt
    03.06.2022 23:53

    Привет клювая штука. Вопросик , а можно всё тоже самое , но только не от бота , а от своего логина и пароля? тоесть позволяет библиотека\телеграм апи такое делать?


    1. vabka
      04.06.2022 00:10

      Телеграм позволяет такое, но для этого уже нужно использовать tdlib


  1. AkshinM
    04.06.2022 01:33
    -2

    А можно было еще легче. Поставить вот это https://github.com/infrabot-io/infrabot и написать простой PowerShell скрипт для выполнения нужных действий, там же и сохранение данных в файл и чтение реализовать.


  1. Mansur_85
    06.06.2022 07:44

    Нее, я не настолько люблю музыку чтобы часами заняться с созданием бота. Но автор молодец. Не ленился.