Files

149 lines
5.0 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Lagrange.Core.Common;
using Lagrange.Core.Utility.Sign;
using Lagrange.OneBot.Utility.Fallbacks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Lagrange.OneBot.Utility;
public class OneBotSigner : SignProvider
{
private readonly IConfiguration _configuration;
private readonly ILogger<OneBotSigner> _logger;
private const string Url = "https://sign.lagrangecore.org/api/sign/30366";
private readonly string? _signServer;
private readonly HttpClient _client;
private readonly BotAppInfo? _info;
private readonly string platform;
private readonly string version;
public OneBotSigner(IConfiguration config, ILogger<OneBotSigner> logger)
{
_configuration = config;
_logger = logger;
_signServer = string.IsNullOrEmpty(config["SignServerUrl"]) ? Url : config["SignServerUrl"];
string? signProxyUrl = config["SignProxyUrl"]; // Only support HTTP proxy
_client = new HttpClient(handler: new HttpClientHandler
{
Proxy = string.IsNullOrEmpty(signProxyUrl) ? null : new WebProxy()
{
Address = new Uri(signProxyUrl),
BypassProxyOnLocal = false,
UseDefaultCredentials = false,
},
}, disposeHandler: true);
if (string.IsNullOrEmpty(_signServer)) logger.LogWarning("Signature Service is not available, login may be failed");
_info ??= GetAppInfo();
platform = _info.Os switch
{
"Windows" => "Windows",
"Mac" => "MacOs",
"Linux" => "Linux",
_ => "Unknown"
};
version = _info.CurrentVersion;
}
public override byte[]? Sign(string cmd, uint seq, byte[] body, [UnscopedRef] out byte[]? e, [UnscopedRef] out string? t)
{
e = null;
t = null;
if (!WhiteListCommand.Contains(cmd)) return null;
if (_signServer == null) throw new Exception("Sign server is not configured");
using var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(_signServer),
Content = JsonContent.Create(new JsonObject
{
{ "cmd", cmd },
{ "seq", seq },
{ "src", Convert.ToHexString(body) }
})
};
using var message = _client.Send(request);
if (message.StatusCode != HttpStatusCode.OK) throw new Exception($"Signer server returned a {message.StatusCode}");
var json = JsonDocument.Parse(message.Content.ReadAsStream()).RootElement;
if (json.TryGetProperty("platform", out JsonElement platformJson))
{
if (platformJson.GetString() != platform) throw new Exception("Signer platform mismatch");
}
else
{
_logger.LogWarning("Signer platform miss");
}
if (json.TryGetProperty("version", out JsonElement versionJson))
{
if (versionJson.GetString() != version) throw new Exception("Signer version mismatch");
}
else
{
_logger.LogWarning("Signer version miss");
}
var valueJson = json.GetProperty("value");
var extraJson = valueJson.GetProperty("extra");
var tokenJson = valueJson.GetProperty("token");
var signJson = valueJson.GetProperty("sign");
string? token = tokenJson.GetString();
string? extra = extraJson.GetString();
e = extra != null ? Convert.FromHexString(extra) : [];
t = token != null ? Encoding.UTF8.GetString(Convert.FromHexString(token)) : "";
string sign = signJson.GetString() ?? throw new Exception("Signer server returned an empty sign");
return Convert.FromHexString(sign);
}
public BotAppInfo GetAppInfo()
{
if (_info != null) return _info;
return FallbackAsync<BotAppInfo>.Create()
.Add(async token =>
{
try { return await _client.GetFromJsonAsync<BotAppInfo>($"{_signServer}/appinfo", token); }
catch { return null; }
})
.Add(token =>
{
string path = _configuration["ConfigPath:AppInfo"] ?? "appinfo.json";
if (!File.Exists(path)) return Task.FromResult(null as BotAppInfo);
try { return Task.FromResult(JsonSerializer.Deserialize<BotAppInfo>(File.ReadAllText(path))); }
catch { return Task.FromResult(null as BotAppInfo); }
})
.ExecuteAsync(token => Task.FromResult(
BotAppInfo.ProtocolToAppInfo[_configuration["Account:Protocol"] switch
{
"Windows" => Protocols.Windows,
"MacOs" => Protocols.MacOs,
_ => Protocols.Linux,
}]
))
.Result;
}
}