From 28565ea0a42a8b0d200254c5288a765898749276 Mon Sep 17 00:00:00 2001 From: Linwenxuan <116782992+Linwenxuan05@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:28:34 +0800 Subject: [PATCH] [OneBot] Forward Websocket Supported --- .../Core/Message/MessageService.cs | 2 +- .../Core/Network/ReverseWSService.cs | 96 ------------------- .../Core/Network/Service/ForwardWSService.cs | 81 ++++++++++++++++ .../Network/{ => Service}/HttpPostService.cs | 2 +- .../{ => Service}/ILagrangeWebService.cs | 2 +- .../Core/Network/Service/LagrangeWSService.cs | 30 ++++++ .../Core/Network/Service/ReverseWSService.cs | 77 +++++++++++++++ .../Core/Operation/OperationService.cs | 2 +- Lagrange.OneBot/Lagrange.OneBot.csproj | 1 + Lagrange.OneBot/LagrangeApp.cs | 2 +- Lagrange.OneBot/LagrangeAppBuilder.cs | 12 ++- 11 files changed, 204 insertions(+), 103 deletions(-) delete mode 100644 Lagrange.OneBot/Core/Network/ReverseWSService.cs create mode 100644 Lagrange.OneBot/Core/Network/Service/ForwardWSService.cs rename Lagrange.OneBot/Core/Network/{ => Service}/HttpPostService.cs (97%) rename Lagrange.OneBot/Core/Network/{ => Service}/ILagrangeWebService.cs (84%) create mode 100644 Lagrange.OneBot/Core/Network/Service/LagrangeWSService.cs create mode 100644 Lagrange.OneBot/Core/Network/Service/ReverseWSService.cs diff --git a/Lagrange.OneBot/Core/Message/MessageService.cs b/Lagrange.OneBot/Core/Message/MessageService.cs index a448ceabe..4f5c3c97f 100644 --- a/Lagrange.OneBot/Core/Message/MessageService.cs +++ b/Lagrange.OneBot/Core/Message/MessageService.cs @@ -5,7 +5,7 @@ using Lagrange.Core.Message; using Lagrange.Core.Utility.Extension; using Lagrange.OneBot.Core.Entity.Message; -using Lagrange.OneBot.Core.Network; +using Lagrange.OneBot.Core.Network.Service; using Lagrange.OneBot.Database; namespace Lagrange.OneBot.Core.Message; diff --git a/Lagrange.OneBot/Core/Network/ReverseWSService.cs b/Lagrange.OneBot/Core/Network/ReverseWSService.cs deleted file mode 100644 index eca412108..000000000 --- a/Lagrange.OneBot/Core/Network/ReverseWSService.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Net.WebSockets; -using System.Text.Json; -using Lagrange.OneBot.Core.Entity.Meta; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Websocket.Client; -using Timer = System.Threading.Timer; - -namespace Lagrange.OneBot.Core.Network; - -public sealed class ReverseWSService : ILagrangeWebService -{ - private const string Tag = nameof(ReverseWSService); - - public event EventHandler OnMessageReceived = delegate { }; - - private readonly WebsocketClient _socket; - - private readonly IConfiguration _config; - - private readonly ILogger _logger; - - private readonly Timer _timer; - - public ReverseWSService(IConfiguration config, ILogger logger) - { - _config = config; - _logger = logger; - - var ws = _config.GetSection("Implementation").GetSection("ReverseWebSocket"); - string url = $"ws://{ws["Host"]}:{ws["Port"]}{ws["Suffix"]}"; - - _socket = new WebsocketClient(new Uri(url), () => - { - var socket = new ClientWebSocket(); - - SetRequestHeader(socket, new Dictionary - { - { "X-Client-Role", "Universal" }, - { "X-Self-ID", _config.GetValue("Account:Uin").ToString() }, - { "User-Agent", Constant.OneBotImpl } - }); - socket.Options.KeepAliveInterval = TimeSpan.FromSeconds(5); - if (_config["AccessToken"] != null) socket.Options.SetRequestHeader("Authorization", $"Bearer {_config["AccessToken"]}"); - - return socket; - }); - - _timer = new Timer(OnHeartbeat, null, int.MaxValue, config.GetValue("Implementation:ReverseWebSocket:HeartBeatInterval")); - _socket.MessageReceived.Subscribe(resp => - { - _logger.LogTrace($"[{Tag}] Receive: {resp.Text}"); - OnMessageReceived.Invoke(this, new MsgRecvEventArgs(resp.Text ?? "")); - }); - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - await _socket.Start(); - - var lifecycle = new OneBotLifecycle(_config.GetValue("Account:Uin"), "connect"); - await SendJsonAsync(lifecycle, cancellationToken); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _timer.Dispose(); - _socket.Dispose(); - - return Task.CompletedTask; - } - - public Task SendJsonAsync(T json, CancellationToken cancellationToken = default) - { - string payload = JsonSerializer.Serialize(json); - - _logger.LogTrace($"[{Tag}] Send: {payload}"); - return _socket.SendInstant(payload); - } - - private void OnHeartbeat(object? sender) - { - var status = new OneBotStatus(true, true); - var heartBeat = new OneBotHeartBeat( - _config.GetValue("Account:Uin"), - _config.GetValue("Implementation:ReverseWebSocket:HeartBeatInterval"), - status); - - SendJsonAsync(heartBeat); - } - - private static void SetRequestHeader(ClientWebSocket webSocket, Dictionary headers) - { - foreach (var (key, value) in headers) webSocket.Options.SetRequestHeader(key, value); - } -} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Network/Service/ForwardWSService.cs b/Lagrange.OneBot/Core/Network/Service/ForwardWSService.cs new file mode 100644 index 000000000..08b407481 --- /dev/null +++ b/Lagrange.OneBot/Core/Network/Service/ForwardWSService.cs @@ -0,0 +1,81 @@ +using System.Net.NetworkInformation; +using System.Text.Json; +using Fleck; +using Lagrange.OneBot.Core.Entity.Meta; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Lagrange.OneBot.Core.Network.Service; + +public sealed class ForwardWSService : LagrangeWSService +{ + private const string Tag = nameof(ForwardWSService); + public override event EventHandler? OnMessageReceived = delegate { }; + + private readonly WebSocketServer _server; + + private IWebSocketConnection? _connection; + + private readonly Timer _timer; + + public ForwardWSService(IConfiguration config, ILogger logger) : base(config, logger) + { + var ws = config.GetSection("Implementation").GetSection("ForwardWebSocket"); + string url = $"ws://{ws["Host"]}:{ws["Port"]}"; + + _server = new WebSocketServer(url) + { + RestartAfterListenError = true + }; + + _timer = new Timer(OnHeartbeat, null, int.MaxValue, ws.GetValue("HeartBeatInterval")); + } + + public override Task StartAsync(CancellationToken cancellationToken) + { + return Task.Run(() => + { + _server.Start(conn => + { + _connection = conn; + + conn.OnMessage = s => + { + Logger.LogTrace($"[{Tag}] Receive: {s}"); + OnMessageReceived?.Invoke(this, new MsgRecvEventArgs(s)); + }; + + conn.OnOpen = () => + { + Logger.LogInformation($"[{Tag}]: Connected"); + + var lifecycle = new OneBotLifecycle(Config.GetValue("Account:Uin"), "connect"); + SendJsonAsync(lifecycle, cancellationToken).GetAwaiter().GetResult(); + }; + }); + }, cancellationToken); + } + + public override Task StopAsync(CancellationToken cancellationToken) + { + _timer.Dispose(); + _server.ListenerSocket.Close(); + _server.Dispose(); + + return Task.CompletedTask; + } + + public override Task SendJsonAsync(T json, CancellationToken cancellationToken = default) + { + string payload = JsonSerializer.Serialize(json); + + Logger.LogTrace($"[{Tag}] Send: {payload}"); + return _connection?.Send(payload) ?? Task.CompletedTask; + } + + private static bool IsPortInUse(uint port) + { + return IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners().Any(endpoint => endpoint.Port == port); + } + +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Network/HttpPostService.cs b/Lagrange.OneBot/Core/Network/Service/HttpPostService.cs similarity index 97% rename from Lagrange.OneBot/Core/Network/HttpPostService.cs rename to Lagrange.OneBot/Core/Network/Service/HttpPostService.cs index a055d224e..582d38967 100644 --- a/Lagrange.OneBot/Core/Network/HttpPostService.cs +++ b/Lagrange.OneBot/Core/Network/Service/HttpPostService.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -namespace Lagrange.OneBot.Core.Network; +namespace Lagrange.OneBot.Core.Network.Service; public sealed class HttpPostService : ILagrangeWebService { diff --git a/Lagrange.OneBot/Core/Network/ILagrangeWebService.cs b/Lagrange.OneBot/Core/Network/Service/ILagrangeWebService.cs similarity index 84% rename from Lagrange.OneBot/Core/Network/ILagrangeWebService.cs rename to Lagrange.OneBot/Core/Network/Service/ILagrangeWebService.cs index c4f75b610..643226047 100644 --- a/Lagrange.OneBot/Core/Network/ILagrangeWebService.cs +++ b/Lagrange.OneBot/Core/Network/Service/ILagrangeWebService.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Hosting; -namespace Lagrange.OneBot.Core.Network; +namespace Lagrange.OneBot.Core.Network.Service; public interface ILagrangeWebService : IHostedService { diff --git a/Lagrange.OneBot/Core/Network/Service/LagrangeWSService.cs b/Lagrange.OneBot/Core/Network/Service/LagrangeWSService.cs new file mode 100644 index 000000000..68117ef0a --- /dev/null +++ b/Lagrange.OneBot/Core/Network/Service/LagrangeWSService.cs @@ -0,0 +1,30 @@ +using Lagrange.OneBot.Core.Entity.Meta; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Lagrange.OneBot.Core.Network.Service; + +public abstract class LagrangeWSService(IConfiguration config, ILogger logger) : ILagrangeWebService +{ + protected readonly ILogger Logger = logger; + + protected readonly IConfiguration Config = config; + + protected void OnHeartbeat(object? sender) + { + var status = new OneBotStatus(true, true); + var heartBeat = new OneBotHeartBeat( + Config.GetValue("Account:Uin"), + Config.GetValue("Implementation:ReverseWebSocket:HeartBeatInterval"), + status); + + SendJsonAsync(heartBeat); + } + + public abstract Task StartAsync(CancellationToken cancellationToken); + + public abstract Task StopAsync(CancellationToken cancellationToken); + + public abstract event EventHandler? OnMessageReceived; + public abstract Task SendJsonAsync(T json, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Network/Service/ReverseWSService.cs b/Lagrange.OneBot/Core/Network/Service/ReverseWSService.cs new file mode 100644 index 000000000..0dd94dbab --- /dev/null +++ b/Lagrange.OneBot/Core/Network/Service/ReverseWSService.cs @@ -0,0 +1,77 @@ +using System.Net.WebSockets; +using System.Text.Json; +using Lagrange.OneBot.Core.Entity.Meta; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Websocket.Client; +using Timer = System.Threading.Timer; + +namespace Lagrange.OneBot.Core.Network.Service; + +public sealed class ReverseWSService : LagrangeWSService +{ + private const string Tag = nameof(ReverseWSService); + public override event EventHandler? OnMessageReceived = delegate { }; + + private readonly WebsocketClient _socket; + + private readonly Timer _timer; + + public ReverseWSService(IConfiguration config, ILogger logger) : base(config, logger) + { + var ws = Config.GetSection("Implementation").GetSection("ReverseWebSocket"); + string url = $"ws://{ws["Host"]}:{ws["Port"]}{ws["Suffix"]}"; + + _socket = new WebsocketClient(new Uri(url), () => + { + var socket = new ClientWebSocket(); + + SetRequestHeader(socket, new Dictionary + { + { "X-Client-Role", "Universal" }, + { "X-Self-ID", Config.GetValue("Account:Uin").ToString() }, + { "User-Agent", Constant.OneBotImpl } + }); + socket.Options.KeepAliveInterval = TimeSpan.FromSeconds(5); + if (Config["AccessToken"] != null) socket.Options.SetRequestHeader("Authorization", $"Bearer {Config["AccessToken"]}"); + + return socket; + }); + + _timer = new Timer(OnHeartbeat, null, int.MaxValue, ws.GetValue("HeartBeatInterval")); + _socket.MessageReceived.Subscribe(resp => + { + Logger.LogTrace($"[{Tag}] Receive: {resp.Text}"); + OnMessageReceived?.Invoke(this, new MsgRecvEventArgs(resp.Text ?? "")); + }); + } + + public override async Task StartAsync(CancellationToken cancellationToken) + { + await _socket.Start(); + + var lifecycle = new OneBotLifecycle(Config.GetValue("Account:Uin"), "connect"); + await SendJsonAsync(lifecycle, cancellationToken); + } + + public override Task StopAsync(CancellationToken cancellationToken) + { + _timer.Dispose(); + _socket.Dispose(); + + return Task.CompletedTask; + } + + public override Task SendJsonAsync(T json, CancellationToken cancellationToken = default) + { + string payload = JsonSerializer.Serialize(json); + + Logger.LogTrace($"[{Tag}] Send: {payload}"); + return _socket.SendInstant(payload); + } + + private static void SetRequestHeader(ClientWebSocket webSocket, Dictionary headers) + { + foreach (var (key, value) in headers) webSocket.Options.SetRequestHeader(key, value); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/OperationService.cs b/Lagrange.OneBot/Core/Operation/OperationService.cs index 1d8f4368d..553352330 100644 --- a/Lagrange.OneBot/Core/Operation/OperationService.cs +++ b/Lagrange.OneBot/Core/Operation/OperationService.cs @@ -3,7 +3,7 @@ using Lagrange.Core; using Lagrange.Core.Utility.Extension; using Lagrange.OneBot.Core.Entity.Action; -using Lagrange.OneBot.Core.Network; +using Lagrange.OneBot.Core.Network.Service; namespace Lagrange.OneBot.Core.Operation; diff --git a/Lagrange.OneBot/Lagrange.OneBot.csproj b/Lagrange.OneBot/Lagrange.OneBot.csproj index ce70ff129..aa6de4f22 100644 --- a/Lagrange.OneBot/Lagrange.OneBot.csproj +++ b/Lagrange.OneBot/Lagrange.OneBot.csproj @@ -9,6 +9,7 @@ + diff --git a/Lagrange.OneBot/LagrangeApp.cs b/Lagrange.OneBot/LagrangeApp.cs index 88574f539..b6775a242 100644 --- a/Lagrange.OneBot/LagrangeApp.cs +++ b/Lagrange.OneBot/LagrangeApp.cs @@ -3,7 +3,7 @@ using Lagrange.Core.Common.Interface.Api; using Lagrange.Core.Utility.Sign; using Lagrange.OneBot.Core.Message; -using Lagrange.OneBot.Core.Network; +using Lagrange.OneBot.Core.Network.Service; using Lagrange.OneBot.Core.Operation; using Lagrange.OneBot.Utility; using Microsoft.Extensions.Configuration; diff --git a/Lagrange.OneBot/LagrangeAppBuilder.cs b/Lagrange.OneBot/LagrangeAppBuilder.cs index c5238572b..ee1e6d6b4 100644 --- a/Lagrange.OneBot/LagrangeAppBuilder.cs +++ b/Lagrange.OneBot/LagrangeAppBuilder.cs @@ -3,7 +3,7 @@ using Lagrange.Core.Common.Interface; using Lagrange.Core.Utility.Sign; using Lagrange.OneBot.Core.Message; -using Lagrange.OneBot.Core.Network; +using Lagrange.OneBot.Core.Network.Service; using Lagrange.OneBot.Core.Operation; using Lagrange.OneBot.Database; using Lagrange.OneBot.Utility; @@ -77,8 +77,16 @@ public LagrangeAppBuilder ConfigureBots() public LagrangeAppBuilder ConfigureOneBot() { + if (Configuration.GetSection("Implementation:ReverseWebSocket").Value != null) + { + Services.AddSingleton(); + } + else if (Configuration.GetSection("Implementation:ForwardWebSocket").Value != null) + { + Services.AddSingleton(); + } + Services.AddSingleton(); - Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton();