Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OneBot] WebServices partially refactor. #42

Merged
2 commits merged into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lagrange.Core.Test/Tests/NTLoginTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async Task LoginByPassword()
if (captcha != null && randStr != null) bot.SubmitCaptcha(captcha, randStr);
};

bot.Invoker.OnGroupInvitationReceived += async (_, @event) =>
bot.Invoker.OnGroupInvitationReceived += (_, @event) =>
{
Console.WriteLine(@event.ToString());
};
Expand Down
4 changes: 2 additions & 2 deletions Lagrange.OneBot/Core/Message/MessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ private void OnFriendMessageReceived(BotContext bot, FriendMessageEvent e)
Message = Convert(e.Chain)
};

_service.SendJsonAsync(request);
_ =_service.SendJsonAsync(request);
}

private void OnGroupMessageReceived(BotContext bot, GroupMessageEvent e)
{
var request = new OneBotGroupMsg(bot.UpdateKeystore().Uin, e.Chain.GroupUin ?? 0,Convert(e.Chain),
e.Chain.GroupMemberInfo ?? throw new Exception("Group member not found"));

_service.SendJsonAsync(request);
_ = _service.SendJsonAsync(request);
}

private void OnTempMessageReceived(BotContext bot, TempMessageEvent e)
Expand Down
149 changes: 98 additions & 51 deletions Lagrange.OneBot/Core/Network/LagrangeWebSvcCollection.cs
Original file line number Diff line number Diff line change
@@ -1,85 +1,132 @@
using Lagrange.Core;
using Lagrange.OneBot.Core.Network.Service;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Lagrange.OneBot.Core.Network;

public class LagrangeWebSvcCollection : Dictionary<string, ILagrangeWebService>, IHostedService
public sealed partial class LagrangeWebSvcCollection : IHostedService
{
private const string Tag = nameof(LagrangeWebSvcCollection);

public event EventHandler<MsgRecvEventArgs> OnMessageReceived = delegate { };
public event EventHandler<MsgRecvEventArgs>? OnMessageReceived;

public LagrangeWebSvcCollection(BotContext context, IConfiguration global, ILogger<LagrangeApp> logger)
{
uint uin = context.BotUin;

if (global.GetSection("Implementations").Exists())
{
logger.LogInformation($"[{Tag}]: Multi Connection has been configured");
private readonly IServiceProvider _services;

foreach (var section in global.GetSection("Implementations").GetChildren())
{
ILagrangeWebService? service = section["Type"] switch
{
"ReverseWebSocket" => new ReverseWSService(section, logger, uin),
"ForwardWebSocket" => new ForwardWSService(section, logger, uin),
_ => null
};
private readonly IConfiguration _config;

if (service == null) logger.LogWarning($"[{Tag}]: unknown type of service of {section["Type"]} is configured, skipped");
else Add(Guid.NewGuid().ToString(), service);
}
private readonly ILogger<LagrangeApp> _logger;

private readonly List<(IServiceScope, ILagrangeWebService)> _webServices;

public LagrangeWebSvcCollection(IServiceProvider services, IConfiguration config, ILogger<LagrangeApp> logger)
{
_services = services;
_config = config;
_logger = logger;
_webServices = new List<(IServiceScope, ILagrangeWebService)>();
}

public async Task StartAsync(CancellationToken cancellationToken)
{
var implsSection = _config.GetSection("Implementations");
if (implsSection.Exists())
{
Log.LogMultiConnection(_logger, Tag);
}
else if (global.GetSection("Implementation").Exists())
else
{
logger.LogInformation($"[{Tag}]: Single Connection has been configured");

string identifier = Guid.NewGuid().ToString();
if (global.GetSection("Implementation:ReverseWebSocket").Exists())
{
this[identifier] = new ReverseWSService(global.GetSection("Implementation:ReverseWebSocket"), logger, uin);
}
else if (global.GetSection("Implementation:ForwardWebSocket").Exists())
implsSection = _config.GetSection("Implementation");
if (!implsSection.Exists())
{
this[identifier] = new ForwardWSService(global.GetSection("Implementation:ForwardWebSocket"), logger, uin);
Log.LogNoConnection(_logger, Tag);
return;
}
}
else
{
logger.LogWarning($"[{Tag}]: No implementation has been configured");
Log.LogSingleConnection(_logger, Tag);
}

foreach (var (identifier, service) in this)
foreach (var section in implsSection.GetChildren())
{
service.OnMessageReceived += (sender, args) =>
var scope = _services.CreateScope();
var services = scope.ServiceProvider;
var factory = services.GetRequiredService<ILagrangeWebServiceFactory>();
factory.SetConfig(section);
var webService = services.GetRequiredService<ILagrangeWebService>();
webService.OnMessageReceived += (sender, args) =>
{
OnMessageReceived.Invoke(sender, new MsgRecvEventArgs(args.Data, identifier));
OnMessageReceived?.Invoke(sender, new MsgRecvEventArgs(args.Data));
};
try
{
await webService.StartAsync(cancellationToken);
_webServices.Add((scope, webService));
}
catch (Exception e)
{
Log.LogWebServiceStartFailed(_logger, e, Tag);
scope.Dispose();
}
}
}

public async Task StartAsync(CancellationToken cancellationToken)
{
foreach (var (_, service) in this) await service.StartAsync(cancellationToken);
}

public async Task StopAsync(CancellationToken cancellationToken)
{
foreach (var (_, service) in this) await service.StopAsync(cancellationToken);
foreach (var (scope, service) in _webServices)
{
try
{
await service.StopAsync(cancellationToken);
}
catch (Exception e)
{
Log.LogWebServiceStopFailed(_logger, e, Tag);
}
finally
{
scope.Dispose();
}
}
}

public async Task SendJsonAsync<T>(T json, string? identifier = null, CancellationToken cancellationToken = default)
public async Task SendJsonAsync<T>(T json, CancellationToken cancellationToken = default)
{
if (identifier == null)
foreach (var (_, service) in _webServices)
{
foreach (var (_, service) in this) await service.SendJsonAsync(json, cancellationToken);
}
else
{
if (TryGetValue(identifier, out var service)) await service.SendJsonAsync(json, cancellationToken);
try
{
var vt = service.SendJsonAsync(json, cancellationToken);
if (!vt.IsCompletedSuccessfully)
{
var t = vt.AsTask();
await t.WaitAsync(TimeSpan.FromSeconds(5), cancellationToken);
}
}
catch (Exception e)
{
Log.LogWebServiceSendFailed(_logger, e, Tag);
}
}
}

private static partial class Log
{
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "[{tag}]: Multi Connection has been configured")]
public static partial void LogMultiConnection(ILogger logger, string tag);

[LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "[{tag}]: Single Connection has been configured")]
public static partial void LogSingleConnection(ILogger logger, string tag);

[LoggerMessage(EventId = 3, Level = LogLevel.Warning, Message = "[{Tag}]: No implementation has been configured")]
public static partial void LogNoConnection(ILogger logger, string tag);

[LoggerMessage(EventId = 4, Level = LogLevel.Warning, Message = "[{Tag}]: WebService start failed.")]
public static partial void LogWebServiceStartFailed(ILogger logger, Exception e, string tag);

[LoggerMessage(EventId = 5, Level = LogLevel.Warning, Message = "[{Tag}]: WebService stop failed.")]
public static partial void LogWebServiceStopFailed(ILogger logger, Exception e, string tag);

[LoggerMessage(EventId = 6, Level = LogLevel.Warning, Message = "[{Tag}]: WebService send message failed.")]
public static partial void LogWebServiceSendFailed(ILogger logger, Exception e, string tag);
}
}
4 changes: 1 addition & 3 deletions Lagrange.OneBot/Core/Network/MsgRecvEventArgs.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
namespace Lagrange.OneBot.Core.Network;

public class MsgRecvEventArgs(string data, string? identifier = null) : EventArgs
public class MsgRecvEventArgs(string data) : EventArgs
{
public string? Identifier { get; init; } = identifier;

public string Data { get; init; } = data;
}
10 changes: 10 additions & 0 deletions Lagrange.OneBot/Core/Network/Options/ForwardWSServiceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Lagrange.OneBot.Core.Network.Options
{
public class ForwardWSServiceOptions : WSServiceOptions
{
public ForwardWSServiceOptions(string host, uint port, uint heartBeatInterval, string? accessToken) : base(host, port, heartBeatInterval, accessToken)
{

}
}
}
12 changes: 12 additions & 0 deletions Lagrange.OneBot/Core/Network/Options/ReverseWSServiceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Lagrange.OneBot.Core.Network.Options
{
public sealed class ReverseWSServiceOptions : WSServiceOptions
{
public string Suffix { get; set; }

public ReverseWSServiceOptions(string host, uint port, uint heartBeatInterval, string suffix, string? accessToken) : base(host, port, heartBeatInterval, accessToken)
{
Suffix = suffix;
}
}
}
21 changes: 21 additions & 0 deletions Lagrange.OneBot/Core/Network/Options/WSServiceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Lagrange.OneBot.Core.Network.Options
{
public abstract class WSServiceOptions
{
public string Host { get; set; }

public uint Port { get; set; }

public uint HeartBeatInterval { get; set; }

public string? AccessToken { get; set; }

protected WSServiceOptions(string host, uint port, uint heartBeatInterval, string? accessToken)
{
Host = host;
Port = port;
HeartBeatInterval = heartBeatInterval;
AccessToken = accessToken;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Lagrange.OneBot.Core.Network.Service
{
public class DefaultLagrangeWebServiceFactory : LagrangeWebServiceFactory
{
public DefaultLagrangeWebServiceFactory(IServiceProvider services) : base(services)
{

}

public override ILagrangeWebService? Create()
{
var config = _config ?? throw new InvalidOperationException("Configuration must be provided");
var type = config["Type"];
if (!string.IsNullOrEmpty(type))
{
return type switch
{
"ReverseWebSocket" => Create<ReverseWSService>(config),
"ForwardWebSocket" => Create<ForwardWSService>(config),
_ => null
};
}
var rws = config.GetSection("ReverseWebSocket");
if (rws.Exists())
{
return Create<ReverseWSService>(rws);
}
var fws = config.GetSection("ForwardWebSocket");
if (fws.Exists())
{
return Create<ReverseWSService>(fws);
}
return null;
}

protected ILagrangeWebService? Create<TService>(IConfiguration config) where TService : ILagrangeWebService
{
var factory = _services.GetService<ILagrangeWebServiceFactory<TService>>();
if (factory == null)
{
return null;
}
factory.SetConfig(config);
return factory.Create();
}
}
}
Loading
Loading