Files
better-genshin-impact/BetterGenshinImpact/GameTask/UseRedeemCode/GetLiveRedeemCode.cs
2025-07-20 20:24:00 +08:00

181 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.UseRedeemCode.Model;
using BetterGenshinImpact.Helpers.Http;
using Newtonsoft.Json.Linq;
namespace BetterGenshinImpact.GameTask.UseRedeemCode;
/// <summary>
/// 获取直播的前瞻兑换码
/// </summary>
public class GetLiveRedeemCode
{
private static readonly string BBS_URL = "https://bbs-api.mihoyo.com";
private readonly HttpClient _httpClient = HttpClientFactory.GetCommonSendClient();
private readonly Dictionary<string, string> _url = new()
{
{ "act_id_1", $"{BBS_URL}/painter/api/user_instant/list?offset=0&size=20&uid=75276539" },
{ "act_id_2", $"{BBS_URL}/painter/api/user_instant/list?offset=0&size=20&uid=75276550" },
{ "index", "https://api-takumi.mihoyo.com/event/miyolive/index" },
{ "code", "https://api-takumi-static.mihoyo.com/event/miyolive/refreshCode" }
};
private async Task<JObject> GetDataAsync(string type, Dictionary<string, string>? data = null)
{
try
{
HttpResponseMessage res;
var request = new HttpRequestMessage(HttpMethod.Get, _url[type]);
// 为所有需要的请求添加 act_id header
if ((type == "index" || type == "code") && data != null && data.TryGetValue("actId", out var actId))
{
request.Headers.Add("x-rpc-act_id", actId);
}
// 为code类型添加查询参数
if (type == "code" && data != null)
{
var uriBuilder = new UriBuilder(_url[type]);
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
query["version"] = data.GetValueOrDefault("version", "");
query["time"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
uriBuilder.Query = query.ToString();
request.RequestUri = uriBuilder.Uri;
}
res = await _httpClient.SendAsync(request);
var content = await res.Content.ReadAsStringAsync();
return JObject.Parse(content);
}
catch (Exception e)
{
return JObject.Parse($"{{\"error\":\"[{e.GetType().Name}] {type} 接口请求错误\",\"retcode\":1}}");
}
}
private async Task<string> GetActIdAsync(string id)
{
var ret = await GetDataAsync($"act_id_{id}");
if (ret == null) return "";
// 检查error或retcode != 0
if (ret["error"] != null || ret["retcode"]?.Value<int>() != 0)
return "";
string actId = "";
var keywords = new List<string> { "前瞻特别节目" };
var list = ret["data"]?["list"] as JArray;
if (list == null) return "";
foreach (var p in list)
{
var post = p["post"]?["post"];
if (post == null) continue;
var subject = post["subject"]?.Value<string>();
if (string.IsNullOrEmpty(subject)) continue;
bool containsAll = keywords.All(word => subject.Contains(word));
if (!containsAll) continue;
var structContent = post["structured_content"]?.Value<string>();
if (string.IsNullOrEmpty(structContent)) continue;
var segments = JArray.Parse(structContent);
foreach (var segment in segments)
{
var link = segment["attributes"]?["link"]?.Value<string>() ?? "";
// 优化安全获取insert字段值
string insert = "";
var insertToken = segment["insert"];
if (insertToken != null)
{
if (insertToken.Type == JTokenType.String)
{
insert = insertToken.Value<string>() ?? "";
}
else if (insertToken.Type == JTokenType.Object)
{
// 如果是对象,尝试转换为字符串
insert = insertToken.ToString();
}
}
if ((insert.Contains("观看") || insert.Contains("米游社直播间")) && !string.IsNullOrEmpty(link))
{
var match = Regex.Match(link, @"act_id=(.*?)\&");
if (match.Success)
actId = match.Groups[1].Value;
}
}
if (!string.IsNullOrEmpty(actId)) break;
}
return actId;
}
private async Task<(string codeVer, string title)> GetLiveDataAsync(string actId)
{
var ret = await GetDataAsync("index", new Dictionary<string, string> { { "actId", actId } });
if (ret == null || ret["error"] != null || ret["retcode"]?.Value<int>() != 0)
return (null, null);
var liveRaw = ret["data"]?["live"];
if (liveRaw == null) return (null, null);
string codeVer = liveRaw["code_ver"]?.Value<string>();
string title = liveRaw["title"]?.Value<string>();
return (codeVer, title);
}
private async Task<List<RedeemCode>> GetCodeAsync(string version, string actId)
{
var ret = await GetDataAsync("code", new Dictionary<string, string> { { "version", version }, { "actId", actId } });
var result = new List<RedeemCode>();
if (ret == null || ret["error"] != null || ret["retcode"]?.Value<int>() != 0)
return result;
var removeTag = new Regex("<.*?>", RegexOptions.Compiled);
var codeList = ret["data"]?["code_list"] as JArray;
if (codeList == null) return result;
foreach (var codeInfo in codeList)
{
string items = removeTag.Replace(codeInfo["title"]?.Value<string>() ?? "", "");
string code = codeInfo["code"]?.Value<string>();
if (!string.IsNullOrEmpty(code))
result.Add(new RedeemCode(code, items));
}
return result;
}
/// <summary>
/// 获取前瞻直播兑换码信息。返回格式List[("奖励内容", "兑换码")]
/// </summary>
public async Task<List<RedeemCode>> GetCodeMsgAsync()
{
string actId = await GetActIdAsync("1");
if (string.IsNullOrEmpty(actId))
{
actId = await GetActIdAsync("2");
if (string.IsNullOrEmpty(actId))
throw new Exception("暂无前瞻直播资讯!");
}
var (codeVer, title) = await GetLiveDataAsync(actId);
if (string.IsNullOrEmpty(codeVer))
throw new Exception("前瞻直播数据异常");
var codeList = await GetCodeAsync(codeVer, actId);
return codeList;
}
}