185 lines
7.1 KiB
C#
185 lines
7.1 KiB
C#
using System.Security.Authentication;
|
||
using System.Text.RegularExpressions;
|
||
using YandexMusic.API.Models.Account;
|
||
using YandexMusic.API.Requests.Account;
|
||
|
||
namespace YandexMusic.API;
|
||
|
||
/// <summary>API для работы с пользователем и авторизации.</summary>
|
||
public class YUserAPI : YCommonAPI
|
||
{
|
||
public YUserAPI(YandexMusicApi api) : base(api) { }
|
||
|
||
private async Task<bool> 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<bool> 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<YAccountResult?> GetUserAuthAsync()
|
||
=> new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
|
||
|
||
public async Task<YAuthTypes?> 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<string?> 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<YAuthQRStatus?> 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<YAuthQRSession?> 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<YAuthCaptcha?> GetCaptchaAsync()
|
||
{
|
||
if (Api.Storage.AuthToken == null)
|
||
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
|
||
return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!);
|
||
}
|
||
|
||
public Task<YAuthBase?> AuthorizeByCaptchaAsync(string captchaValue)
|
||
{
|
||
if (Api.Storage.AuthToken == null)
|
||
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
|
||
return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue);
|
||
}
|
||
|
||
public Task<YAuthLetter?> GetAuthLetterAsync()
|
||
=> new YGetAuthLetterBuilder(Api).ExecuteAsync(null!);
|
||
|
||
public async Task<bool> 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<YAuthBase?> 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<YAccessToken?> 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<YLoginInfo?> GetLoginInfoAsync()
|
||
=> new YGetLoginInfoBuilder(Api).ExecuteAsync(null!);
|
||
} |