149 lines
5.0 KiB
C#
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;
|
|
}
|
|
}
|