mirror of
https://github.com/Megghy/MegghysAPI.git
synced 2025-12-06 14:16:56 +08:00
137 lines
5.5 KiB
C#
137 lines
5.5 KiB
C#
using MegghysAPI.Modules;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
|
|
namespace MegghysAPI.Controllers
|
|
{
|
|
[Route("api/bili")]
|
|
[ApiController]
|
|
public class BiliController : MControllerBase
|
|
{
|
|
[HttpGet("test-cap")]
|
|
public async Task<IActionResult> TestCap([FromQuery] string gt, [FromQuery] string challenge)
|
|
{
|
|
var solution = await CapResolver.SolveGeetestAsync("https://www.bilibili.com", gt, challenge);
|
|
return Ok(ResponseOK(solution));
|
|
}
|
|
|
|
[HttpGet("resolve-grisk-id")]
|
|
public async Task<IActionResult> ResolveGriskId([FromQuery] string v_voucher, [FromQuery] string? csrf)
|
|
{
|
|
if (string.IsNullOrEmpty(v_voucher))
|
|
{
|
|
return BadRequest(ResponseBadRequest("v_voucher 不能为空"));
|
|
}
|
|
|
|
try
|
|
{
|
|
// 1. 调用B站注册接口获取 gt, challenge, token
|
|
var (gt, challenge, token) = await RegisterFromVVoucher(v_voucher, csrf);
|
|
|
|
// 2. 使用 CapResolver 解决验证码, 获取 validate
|
|
var solution = await CapResolver.SolveGeetestAsync("https://live.bilibili.com", gt, challenge);
|
|
if (solution == null)
|
|
{
|
|
throw new Exception("从 CapSolver 获取 validate 失败");
|
|
}
|
|
string validate = solution.Validate;
|
|
|
|
// 3. 调用B站验证接口获取 grisk_id
|
|
var griskId = await ValidateToGriskId(challenge, token, validate, csrf);
|
|
|
|
return Ok(ResponseOK(new { grisk_id = griskId }));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logs.Error($"ResolveGriskId 失败: {ex.Message}");
|
|
return StatusCode(500, ResponseInternalError(ex.Message));
|
|
}
|
|
}
|
|
public const string FirstHost = "https://i.ukamnads.icu/api-bilibili/";
|
|
public const string SecondHost = "https://api.bilibili.com/";
|
|
private static readonly string[] PreferredHosts = { FirstHost, SecondHost };
|
|
|
|
private async Task<(string gt, string challenge, string token)> RegisterFromVVoucher(string vVoucher, string? csrf)
|
|
{
|
|
var path = "x/gaia-vgate/v1/register";
|
|
var payload = new Dictionary<string, string> { { "v_voucher", vVoucher } };
|
|
if (!string.IsNullOrEmpty(csrf))
|
|
{
|
|
payload["csrf"] = csrf;
|
|
}
|
|
|
|
var root = await PostFormWithFailoverAsync(path, payload, "注册接口");
|
|
var data = root["data"] ?? throw new Exception("注册接口返回缺少 data 字段");
|
|
|
|
var geetest = data["geetest"] ?? throw new Exception("注册返回缺少 geetest 字段");
|
|
var gt = (string?)geetest["gt"] ?? throw new Exception("注册返回缺少 gt 字段");
|
|
var challenge = (string?)geetest["challenge"] ?? throw new Exception("注册返回缺少 challenge 字段");
|
|
var apiToken = (string?)data["token"] ?? throw new Exception("注册返回缺少 token 字段");
|
|
|
|
return (gt, challenge, apiToken);
|
|
}
|
|
|
|
private async Task<string> ValidateToGriskId(string challenge, string token, string validate, string? csrf)
|
|
{
|
|
var path = "x/gaia-vgate/v1/validate";
|
|
var seccode = $"{validate}|jordan";
|
|
var payload = new Dictionary<string, string>
|
|
{
|
|
{ "challenge", challenge },
|
|
{ "token", token },
|
|
{ "validate", validate },
|
|
{ "seccode", seccode },
|
|
};
|
|
if (!string.IsNullOrEmpty(csrf))
|
|
{
|
|
payload["csrf"] = csrf;
|
|
}
|
|
|
|
var root = await PostFormWithFailoverAsync(path, payload, "验证接口");
|
|
var data = root["data"] ?? throw new Exception("验证接口返回缺少 data 字段");
|
|
|
|
var isValid = (int?)data["is_valid"];
|
|
var griskId = (string?)data["grisk_id"];
|
|
|
|
if (isValid != 1 || string.IsNullOrEmpty(griskId))
|
|
{
|
|
throw new Exception($"验证失败或缺少 grisk_id: is_valid={isValid} grisk_id={griskId}");
|
|
}
|
|
|
|
return griskId;
|
|
}
|
|
|
|
private async Task<JsonNode> PostFormWithFailoverAsync(string path, Dictionary<string, string> payload, string operation)
|
|
{
|
|
Exception? lastException = null;
|
|
foreach (var host in PreferredHosts)
|
|
{
|
|
try
|
|
{
|
|
using var content = new FormUrlEncodedContent(payload);
|
|
var response = await Utils._client.PostAsync($"{host}{path}", content);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
return JsonNode.Parse(json) ?? throw new Exception($"{operation} 返回内容解析失败");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lastException = ex;
|
|
if (host == FirstHost)
|
|
{
|
|
Logs.Warn($"{operation} 使用 FirstHost 调用失败: {ex.Message}");
|
|
}
|
|
else
|
|
{
|
|
Logs.Error($"{operation} 使用 SecondHost 调用失败: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new Exception($"{operation} 调用失败: {lastException?.Message}", lastException);
|
|
}
|
|
}
|
|
}
|