using System.Security.Authentication;
using System.Text.RegularExpressions;
using YandexMusic.API.Models.Account;
using YandexMusic.API.Requests.Account;
namespace YandexMusic.API;
/// API для работы с пользователем и авторизации.
public class YUserAPI : YCommonAPI
{
public YUserAPI(YandexMusicApi api) : base(api) { }
private async Task GetCsrfTokenAsync()
{
using var response = await new YGetAuthMethodsBuilder(Api).ExecuteRawAsync(null!);
if (response == null || !response.IsSuccessStatusCode)
throw new HttpRequestException("Не удалось получить CSRF-токен");
var content = await response.Content.ReadAsStringAsync();
var csrfMatch = Regex.Match(content, @"window\.__CSRF__\s*=\s*""([^""]+)""");
var processMatch = Regex.Match(content, @"'process_uuid'\s*:\s*'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})'");
if (!csrfMatch.Success || !processMatch.Success)
return false;
Api.Storage.HeaderToken = new YAuthToken
{
CsfrToken = csrfMatch.Groups[1].Value,
ProcessUuid = processMatch.Groups[1].Value
};
await new YPostAuthStats(Api).ExecuteAsync(null!);
return true;
}
private async Task LoginByCookiesAsync()
{
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Сессия входа не инициализирована");
var accessToken = await new YGetAuthCookiesBuilder(Api).ExecuteAsync(null!);
if (accessToken == null || string.IsNullOrEmpty(accessToken.AccessToken))
return false;
Api.Storage.AccessToken = accessToken;
Api.Storage.Token = accessToken.AccessToken;
await AuthorizeByPassportAsync(accessToken.AccessToken);
return true;
}
public async Task AuthorizeAsync(string token)
{
if (string.IsNullOrEmpty(token))
throw new Exception("Токен не может быть пустым");
Api.Storage.Token = token;
var authInfo = await new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
if (authInfo?.Account?.Uid == null)
throw new Exception("Пользователь не авторизован");
Api.Storage.SetAuthorized(authInfo.Account, token);
}
public async Task AuthorizeByPassportAsync(string token)
{
if (string.IsNullOrEmpty(token))
throw new Exception("Токен не может быть пустым");
Api.Storage.Token = token;
await GetAccessTokenAsync();
await AuthorizeAsync(Api.Storage.Token);
}
public Task GetUserAuthAsync()
=> new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
public async Task CreateAuthSessionAsync(string userName)
{
if (!await GetCsrfTokenAsync())
throw new Exception("Не удалось инициализировать сессию");
var result = await new YGetAuthLoginUserBuilder(Api).ExecuteAsync((Api.Storage.AuthToken.CsfrToken, userName));
if (result?.TrackId != null)
Api.Storage.AuthToken.TrackId = result.TrackId;
return result;
}
public async Task GetAuthQRLinkAsync()
{
if (!await GetCsrfTokenAsync())
throw new Exception("Не удалось инициализировать сессию");
var qr = await new YGetAuthQRBuilder(Api).ExecuteAsync(null!);
if (qr?.Status != YAuthStatus.Ok || string.IsNullOrEmpty(qr.TrackId))
return null;
Api.Storage.AuthToken = new YAuthToken
{
TrackId = qr.TrackId,
CsfrToken = qr.CsrfToken
};
return $"https://passport.yandex.ru/auth/magic/code/?track_id={qr.TrackId}";
}
public async Task CheckQRStatusAsync()
{
if (Api.Storage.AuthToken == null)
throw new Exception("Сессия не инициализирована");
var status = await new YPostQrStatus(Api).ExecuteAsync(null!);
if (!string.IsNullOrWhiteSpace(status?.TrackId))
{
Api.Storage.AuthToken.SessionTrackId = status.TrackId;
}
return status;
}
public async Task AuthorizeByQRAsync()
{
if (Api.Storage.AuthToken == null)
throw new Exception("Сессия не инициализирована");
if (string.IsNullOrWhiteSpace(Api.Storage.AuthToken.SessionTrackId))
throw new Exception("Токен сессии не инициализирован");
var status = await new YGetAuthLoginQRBuilder(Api).ExecuteAsync(null!);
if (status != null && status.DefaultUid != 0 && await LoginByCookiesAsync())
return status;
throw new AuthenticationException("Ошибка авторизации по QR");
}
public Task GetCaptchaAsync()
{
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!);
}
public Task AuthorizeByCaptchaAsync(string captchaValue)
{
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue);
}
public Task GetAuthLetterAsync()
=> new YGetAuthLetterBuilder(Api).ExecuteAsync(null!);
public async Task AuthorizeByLetterAsync()
{
var status = await new YGetAuthLoginLetterBuilder(Api).ExecuteAsync(null!);
if (status?.Status != YAuthStatus.Ok || !status.MagicLinkConfirmed)
throw new Exception("Письмо не подтверждено");
return await LoginByCookiesAsync();
}
public async Task AuthorizeByAppPasswordAsync(string password)
{
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
var result = await new YGetAuthAppPasswordBuilder(Api).ExecuteAsync(password);
if (result?.Status == YAuthStatus.Ok && await LoginByCookiesAsync())
return result;
throw new AuthenticationException("Ошибка авторизации по паролю");
}
public async Task GetAccessTokenAsync()
{
if (Api.Storage.AuthToken == null)
throw new Exception("Сессия не инициализована");
var token = await new YGetMusicTokenBuilder(Api).ExecuteAsync(null!);
if (token?.AccessToken != null)
Api.Storage.Token = token.AccessToken;
return token;
}
public Task GetLoginInfoAsync()
=> new YGetLoginInfoBuilder(Api).ExecuteAsync(null!);
}