using System.Security.Authentication; using System.Text.RegularExpressions; using YandexMusic.API.Models.Account; using YandexMusic.API.Models.Passport; using YandexMusic.API.Requests.Account; using YandexMusic.API.Requests.Passport; namespace YandexMusic.API; /// API для работы с яндекс паспортом public class YPassportAPI : YCommonAPI { public YPassportAPI(YandexMusicApi api) : base(api) { } public async Task GetMusicTokenByCookiesAsync() { if (string.IsNullOrEmpty(Api.Storage.AuthToken.TrackId)) { if (!await GetCsrfTokenAsync()) throw new Exception("Не удалось инициализировать сессию"); await CreateTrackAsync(); // ваш приватный метод создания track_id } return await new YGetAuthCookiesBuilder(Api).ExecuteAsync(null!); } public async Task GetMusicTokenByPassportTokenAsync(string passportToken) => await GetAccessTokenAsync(passportToken); public async Task GetAuthQRLinkAsync() { if (!await GetCsrfTokenAsync()) throw new Exception("Не удалось инициализировать сессию"); await CreateTrackAsync(); return $"https://passport.yandex.ru/auth/magic/code/?track_id={Api.Storage.AuthToken.TrackId}"; } public async Task CheckQRStatusAsync() { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); var status = await new YGetQrStatus(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 async Task MultistepStartAsync(string login) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована. Вызовите GetAuthQRLinkAsync или CreateTrackAsync"); return await new YMultistepStartBuilder(Api).ExecuteAsync(login); } /// Многоступенчатая авторизация: ввод пароля. public async Task MultistepPasswordAsync(string password) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YMultiStepPasswordBuilder(Api).ExecuteAsync(password); } /// Авторизация с помощью RFC OTP. public async Task RfcOtpPasswordAsync(string rfcOtp) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YRfcOtpBuilder(Api).ExecuteAsync(rfcOtp); } /// Создание сессии пользователя. public async Task CreateUserSessionAsync() { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YGetSessionBuilder(Api).ExecuteAsync(null!); } /// Проверка состояния сессии. public async Task GetSessionStateAsync() { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YCheckSessionBuilder(Api).ExecuteAsync(null!); } /// Проверка номера телефона (валидация). public async Task ValidatePhoneNumberAsync(string phone) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YValidatePhoneNumberBuilder(Api).ExecuteAsync(phone); } /// Проверка доступности номера. public async Task CheckPhoneAvailabilityAsync(string phone) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YCheckPhoneAvailabilityBuilder(Api).ExecuteAsync(phone); } /// Запрос на отправку push-уведомления. public async Task SuggestSendPushAsync(string phone) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YSendPushBuilder(Api).ExecuteAsync(phone); } /// Проверка push-кода. public async Task CheckPushCodeAsync(string code) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); await new YCheckPushCodeBuilder(Api).ExecuteAsync(code); } /// Проверка на "сквоттера" (захват номера). public async Task ValidateSquatterAsync(string phone) { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YValidateSquatterBuilder(Api).ExecuteAsync(phone); } /// Получение списка аккаунтов по номеру телефона. public async Task SuggestByPhoneAsync() { if (Api.Storage.AuthToken == null) throw new Exception("Сессия не инициализирована"); return await new YSuggestByPhoneBuilder(Api).ExecuteAsync(null!); } /// Вход по паролю (упрощённый, если не нужна многоступенчатость). public async Task LoginByPasswordAsync(string password) { // Сначала запускаем мультистеп (если track_id уже есть) if (string.IsNullOrEmpty(Api.Storage.AuthToken.TrackId)) throw new Exception("TrackId не найден. Вызовите MultistepStartAsync сначала."); return await MultistepPasswordAsync(password); } private async Task CreateTrackAsync() { if (!await GetCsrfTokenAsync()) throw new Exception("Невозможно инициализировать сессию входа."); var track = await new YCreateTrackBuilder(Api).ExecuteAsync(null!); if (string.IsNullOrWhiteSpace(track?.TrackId) || string.IsNullOrWhiteSpace(track?.CsrfToken)) throw new Exception("Не удалось создать трек паспорта."); Api.Storage.AuthToken.TrackId = track.TrackId; Api.Storage.AuthToken.CsfrToken = track.CsrfToken; } internal 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 }; return true; } internal async Task GetAccessTokenAsync(string passportToken) { if (string.IsNullOrEmpty(passportToken)) throw new Exception("Сессия не инициализована"); var token = await new YGetMusicTokenBuilder(Api).ExecuteAsync(passportToken); if (token?.AccessToken != null) Api.Storage.Token = token.AccessToken; return token; } internal 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; await GetAccessTokenAsync(accessToken.AccessToken); return true; } }