using System.Collections.Specialized; using System.Net; using System.Net.Http.Headers; using System.Reflection; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Web; using YandexMusic.API.Common; using YandexMusic.API.Extensions; using YandexMusic.API.Requests.Common.Attributes; namespace YandexMusic.API.Requests.Common; /// Базовый строитель HTTP-запросов к API Яндекс.Музыки. /// Тип ответа. /// Тип параметров запроса. public abstract class YRequestBuilder { private readonly YRequestAttribute _requestInfo; private Dictionary _substitutions = null!; private readonly JsonSerializerOptions _jsonOptions; protected readonly YandexMusicApi api; protected readonly AuthStorage storage; protected string device; protected YRequestBuilder(YandexMusicApi yandex, AuthStorage auth) { _requestInfo = GetType().GetCustomAttribute() ?? throw new NotImplementedException($"Отсутствует атрибут {nameof(YRequestAttribute)}"); api = yandex; storage = auth; device = $"os=CSharp; os_version=; manufacturer=FrigaT; model=Yandex Music API; clid=; device_id={storage.DeviceId}; uuid=random"; _jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) } }; } private Uri BuildUri(TParams tuple) { var queryParams = GetQueryParams(tuple); var modifiedParams = HttpUtility.ParseQueryString(string.Empty); foreach (string? key in queryParams) if (key != null) modifiedParams[key] = ReplaceSubs(queryParams[key]!); var endpoint = ReplaceSubs(_requestInfo.Url); var builder = new UriBuilder(endpoint) { Query = modifiedParams.ToString() ?? string.Empty }; return builder.Uri; } private HttpRequestMessage CreateMessage(TParams tuple) { var msg = new HttpRequestMessage { RequestUri = BuildUri(tuple), Method = new HttpMethod(_requestInfo.Method), Content = GetContent(tuple) }; msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptCharset.GetName(), Encoding.UTF8.WebName); msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptEncoding.GetName(), "gzip"); if (!string.IsNullOrEmpty(storage.Token)) msg.Headers.TryAddWithoutValidation(HttpRequestHeader.Authorization.GetName(), $"OAuth {storage.Token}"); SetCustomHeaders(msg.Headers); return msg; } protected string ReplaceSubs(string str) { var subs = str.GetMatches(@"\{.+?\}"); foreach (var s in subs) { var key = s.ReplaceRegex(@"[\{\}]", string.Empty); if (!_substitutions.TryGetValue(key, out var value)) throw new Exception($"Не найдена подстановка {s}"); str = str.Replace(s, value); } return str; } protected virtual Dictionary GetSubstitutions(TParams tuple) => []; protected virtual NameValueCollection GetQueryParams(TParams tuple) => []; protected virtual HttpContent? GetContent(TParams tuple) => null; protected virtual void SetCustomHeaders(HttpRequestHeaders headers) { } protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions); internal YRequest Build(TParams tuple) { _substitutions = GetSubstitutions(tuple); var msg = CreateMessage(tuple); return new YRequest(msg, api, storage); } }