using System.Net.Http; using System.Threading; using System.Threading.Tasks; using ytLive.Models; namespace ytLive.Services; public sealed class SocialLookupResult { public bool Success { get; init; } public string ProfileUrl { get; init; } = string.Empty; public string Handle { get; init; } = string.Empty; public string Error { get; init; } = string.Empty; } public interface ISocialValidator { Task LookupAsync(SocialService service, string handleOrUrl); } /// /// Validates a social handle/URL by constructing the canonical profile URL and /// issuing an HTTP GET — the "act of validation" the creator trusts before the /// icon lands on their bar. Best-effort: some platforms return challenges/blocks /// to HEAD requests, so a 200 OR a 301/302 redirect counts as "exists"; 404 or /// connection failure = rejected. Never blind trust. /// public sealed class HttpSocialValidator : ISocialValidator { private readonly HttpClient _client; public HttpSocialValidator(HttpClient? client = null) { _client = client ?? new HttpClient(); _client.DefaultRequestHeaders.UserAgent.ParseAdd( "Mozilla/5.0 (compatible; ytLlive social validator)"); } public async Task LookupAsync(SocialService service, string handleOrUrl) { var input = handleOrUrl.Trim(); if (string.IsNullOrWhiteSpace(input)) return new SocialLookupResult { Error = "Enter a handle or URL." }; var handle = input; if (input.StartsWith("http://", System.StringComparison.OrdinalIgnoreCase) || input.StartsWith("https://", System.StringComparison.OrdinalIgnoreCase)) { var uri = new System.Uri(input); handle = uri.AbsolutePath.Trim('/'); } var url = SocialServiceIcons.CanonicalUrlFor(service, handle); try { using var resp = await _client.GetAsync(url, CancellationToken.None); if (resp.IsSuccessStatusCode || (int)resp.StatusCode is 301 or 302 or 303 or 307 or 308) return new SocialLookupResult { Success = true, ProfileUrl = url, Handle = handle.TrimStart('@'), }; return new SocialLookupResult { Error = $"{service} returned {resp.StatusCode} for '{handle}'. The handle may be wrong or the page doesn't exist.", }; } catch (System.Net.Http.HttpRequestException) { return new SocialLookupResult { Error = $"Couldn't reach {service} to validate '{handle}'. Check your connection and try again.", }; } catch (System.Exception ex) { return new SocialLookupResult { Error = ex.Message }; } } }