From 1f84705d262f38e80a823be7976eb773e9df0c74 Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Thu, 26 Dec 2024 23:06:50 +0800 Subject: [PATCH 01/18] [Test] fix test error (#714) --- Lagrange.Core.Test/Tests/NTLoginTest.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lagrange.Core.Test/Tests/NTLoginTest.cs b/Lagrange.Core.Test/Tests/NTLoginTest.cs index 85410c283..fbc45fc74 100644 --- a/Lagrange.Core.Test/Tests/NTLoginTest.cs +++ b/Lagrange.Core.Test/Tests/NTLoginTest.cs @@ -1,7 +1,7 @@ using Lagrange.Core.Common; using Lagrange.Core.Common.Interface; using Lagrange.Core.Common.Interface.Api; -using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Message; namespace Lagrange.Core.Test.Tests; @@ -13,33 +13,33 @@ public async Task LoginByPassword() { var deviceInfo = WtLoginTest.GetDeviceInfo(); var keyStore = WtLoginTest.LoadKeystore(); - + if (keyStore == null) { Console.WriteLine("Please login by QrCode first"); return; } - var bot = BotFactory.Create(new BotConfig() + var bot = BotFactory.Create(new BotConfig() { UseIPv6Network = false, GetOptimumServer = true, AutoReconnect = true, Protocol = Protocols.Linux }, deviceInfo, keyStore); - + bot.Invoker.OnBotLogEvent += (_, @event) => { Utility.Console.ChangeColorByTitle(@event.Level); Console.WriteLine(@event.ToString()); }; - + bot.Invoker.OnBotOnlineEvent += (_, @event) => { Console.WriteLine(@event.ToString()); WtLoginTest.SaveKeystore(bot.UpdateKeystore()); }; - + bot.Invoker.OnBotCaptchaEvent += (_, @event) => { Console.WriteLine(@event.ToString()); @@ -47,7 +47,7 @@ public async Task LoginByPassword() var randStr = Console.ReadLine(); if (captcha != null && randStr != null) bot.SubmitCaptcha(captcha, randStr); }; - + bot.Invoker.OnGroupInvitationReceived += (_, @event) => { Console.WriteLine(@event.ToString()); From b651e20e99a4920858fc8d0cfb6234464bdc5522 Mon Sep 17 00:00:00 2001 From: Linwenxuan04 Date: Sat, 28 Dec 2024 11:02:45 +0800 Subject: [PATCH 02/18] [OneBot] Add net9.0 as target framework --- Lagrange.OneBot/Lagrange.OneBot.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lagrange.OneBot/Lagrange.OneBot.csproj b/Lagrange.OneBot/Lagrange.OneBot.csproj index 04eafb91b..53085aa3c 100644 --- a/Lagrange.OneBot/Lagrange.OneBot.csproj +++ b/Lagrange.OneBot/Lagrange.OneBot.csproj @@ -9,7 +9,7 @@ Lagrange.OneBot https://github.com/LagrangeDev/Lagrange.Core git - net7.0;net8.0 + net7.0;net8.0;net9.0 From 6a5660aeb387e4524c7190733835375ba3b69093 Mon Sep 17 00:00:00 2001 From: Linwenxuan04 Date: Sat, 28 Dec 2024 11:09:21 +0800 Subject: [PATCH 03/18] [ci] Depreciate net7.0 target --- .github/workflows/Lagrange.OneBot-build.yml | 22 ++++++++++----------- Lagrange.OneBot/Lagrange.OneBot.csproj | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/Lagrange.OneBot-build.yml b/.github/workflows/Lagrange.OneBot-build.yml index 7c6b151c6..b6c6907e7 100644 --- a/.github/workflows/Lagrange.OneBot-build.yml +++ b/.github/workflows/Lagrange.OneBot-build.yml @@ -49,20 +49,13 @@ jobs: - name: Install .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0.x' - - - name: Build Lagrange.OneBot .NET 7.0 - run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --no-self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net7.0 - + dotnet-version: '9.0.x' + - name: Build Lagrange.OneBot .NET 8.0 run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --no-self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net8.0 - - name: Upload binary files(${{ matrix.runtimeIdentifier }}) for .NET 7.0 - uses: actions/upload-artifact@v4 - if: github.event_name != 'pull_request' - with: - name: Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net7.0_NoSelfContained - path: Lagrange.OneBot/bin/Debug/net7.0/${{ matrix.runtimeIdentifier }}/publish + - name: Build Lagrange.OneBot .NET 9.0 + run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --no-self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net9.0 - name: Upload binary files(${{ matrix.runtimeIdentifier }}) for .NET 8.0 uses: actions/upload-artifact@v4 @@ -70,3 +63,10 @@ jobs: with: name: Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net8.0_NoSelfContained path: Lagrange.OneBot/bin/Release/net8.0/${{ matrix.runtimeIdentifier }}/publish + + - name: Upload binary files(${{ matrix.runtimeIdentifier }}) for .NET 9.0 + uses: actions/upload-artifact@v4 + if: github.event_name != 'pull_request' + with: + name: Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_NoSelfContained + path: Lagrange.OneBot/bin/Release/net8.0/${{ matrix.runtimeIdentifier }}/publish diff --git a/Lagrange.OneBot/Lagrange.OneBot.csproj b/Lagrange.OneBot/Lagrange.OneBot.csproj index 53085aa3c..354262ec6 100644 --- a/Lagrange.OneBot/Lagrange.OneBot.csproj +++ b/Lagrange.OneBot/Lagrange.OneBot.csproj @@ -9,7 +9,7 @@ Lagrange.OneBot https://github.com/LagrangeDev/Lagrange.Core git - net7.0;net8.0;net9.0 + net8.0;net9.0 From 2540b069d552ce374fc719d0afa2e4c626b1ec5a Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Sat, 28 Dec 2024 11:56:08 +0800 Subject: [PATCH 04/18] [Chroe] Fix dockerfile error (#716) --- Lagrange.OneBot/Resources/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lagrange.OneBot/Resources/Dockerfile b/Lagrange.OneBot/Resources/Dockerfile index bd894156f..23693b8ee 100644 --- a/Lagrange.OneBot/Resources/Dockerfile +++ b/Lagrange.OneBot/Resources/Dockerfile @@ -1,15 +1,15 @@ -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.18 AS build-env +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build-env WORKDIR /root/build ARG TARGETARCH COPY . /root/build -RUN dotnet publish -p:DebugType="none" -a $TARGETARCH -f "net8.0" -o "/root/out" "Lagrange.OneBot" +RUN dotnet publish -p:DebugType="none" -a $TARGETARCH -f "net9.0" -o "/root/out" "Lagrange.OneBot" # === -FROM mcr.microsoft.com/dotnet/runtime:8.0-alpine3.18 +FROM mcr.microsoft.com/dotnet/runtime:9.0-alpine WORKDIR /app/data From 16454c13c33c81ffe6ac4942d2d7ed1df51b862e Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Sun, 29 Dec 2024 20:10:09 +0800 Subject: [PATCH 05/18] [All] Multiple repairs and features (#717) 1. Added fetch app info from the signer server (onebot) (server first, appinfo.json second, and finally use Account.Protocol to configure the node) 2. You can exit the program during the login process --- Lagrange.OneBot/Core/Login/LoginService.cs | 83 +++++++ .../HostApplicationBuilderExtension.cs | 163 ++++++++++++++ Lagrange.OneBot/LagrangeApp.cs | 142 ------------ Lagrange.OneBot/LagrangeAppBuilder.cs | 203 ------------------ Lagrange.OneBot/Program.cs | 14 +- Lagrange.OneBot/Utility/FallbackAsync.cs | 29 +++ Lagrange.OneBot/Utility/FallbackAsyncT.cs | 27 +++ Lagrange.OneBot/Utility/OneBotSigner.cs | 53 ++++- 8 files changed, 354 insertions(+), 360 deletions(-) create mode 100644 Lagrange.OneBot/Core/Login/LoginService.cs create mode 100644 Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs delete mode 100644 Lagrange.OneBot/LagrangeApp.cs delete mode 100644 Lagrange.OneBot/LagrangeAppBuilder.cs create mode 100644 Lagrange.OneBot/Utility/FallbackAsync.cs create mode 100644 Lagrange.OneBot/Utility/FallbackAsyncT.cs diff --git a/Lagrange.OneBot/Core/Login/LoginService.cs b/Lagrange.OneBot/Core/Login/LoginService.cs new file mode 100644 index 000000000..5e715fa08 --- /dev/null +++ b/Lagrange.OneBot/Core/Login/LoginService.cs @@ -0,0 +1,83 @@ +using Lagrange.Core; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.Core.Event.EventArg; +using Lagrange.OneBot.Core.Network; +using Lagrange.OneBot.Core.Notify; +using Lagrange.OneBot.Utility; +using Lagrange.OneBot.Utility.Fallbacks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using LagrangeLogLevel = Lagrange.Core.Event.EventArg.LogLevel; +using MicrosoftLogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace Lagrange.OneBot.Core.Login; + +public class LoginService(IConfiguration configuration, ILogger logger, ILogger botLogger, BotContext lagrange, LagrangeWebSvcCollection web, NotifyService notify) : IHostedService +{ + private readonly ILogger _logger = logger; + + private readonly ILogger _botLogger = botLogger; + + private readonly BotContext _lagrange = lagrange; + + private readonly LagrangeWebSvcCollection _web = web; + + private readonly NotifyService _notify = notify; + + private readonly bool _isCompatibility = configuration.GetValue("QrCode:ConsoleCompatibilityMode"); + + public async Task StartAsync(CancellationToken token) + { + _logger.LogInformation("Protocol Version: {}", _lagrange.AppInfo.CurrentVersion); + + _lagrange.Invoker.OnBotLogEvent += BotLogHandler; + _notify.RegisterEvents(); + + bool isSucceed = await FallbackAsync.Create() + .Add((token) => + { + var keystore = _lagrange.UpdateKeystore(); + if (keystore.Session.TempPassword == null) return Task.FromResult(false); + if (keystore.Session.TempPassword.Length == 0) return Task.FromResult(false); + return _lagrange.LoginByPassword(token); + }) + .Add(async (token) => + { + (string Url, byte[] QrCode)? qrcode = await _lagrange.FetchQrCode().WaitAsync(token); + if (!qrcode.HasValue) return false; + QrCodeHelper.Output(qrcode.Value.Url, _isCompatibility); + + return await (Task)_lagrange.LoginByQrCode(token); + }) + .ExecuteAsync(token); + + if (!isSucceed) throw new Exception("All login failed!"); + + _logger.LogInformation("Bot Uin: {}", _lagrange.BotUin); + + await _web.StartAsync(token); + } + + private void BotLogHandler(BotContext context, BotLogEvent e) + { + _botLogger.Log(e.Level switch + { + LagrangeLogLevel.Debug => MicrosoftLogLevel.Trace, + LagrangeLogLevel.Verbose => MicrosoftLogLevel.Information, + LagrangeLogLevel.Information => MicrosoftLogLevel.Information, + LagrangeLogLevel.Warning => MicrosoftLogLevel.Warning, + LagrangeLogLevel.Fatal => MicrosoftLogLevel.Error, + _ => MicrosoftLogLevel.Error + }, "{}", e.ToString()); + } + + public Task StopAsync(CancellationToken token) + { + _lagrange.Invoker.OnBotLogEvent -= BotLogHandler; + _lagrange.Dispose(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs b/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs new file mode 100644 index 000000000..d0dc7a1c7 --- /dev/null +++ b/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs @@ -0,0 +1,163 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Interface; +using Lagrange.OneBot.Core.Login; +using Lagrange.OneBot.Core.Network; +using Lagrange.OneBot.Core.Network.Service; +using Lagrange.OneBot.Core.Notify; +using Lagrange.OneBot.Core.Operation; +using Lagrange.OneBot.Database; +using Lagrange.OneBot.Message; +using Lagrange.OneBot.Utility; +using LiteDB; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using JsonSerializer = System.Text.Json.JsonSerializer; + +namespace Lagrange.OneBot.Extensions; + +public static class HostApplicationBuilderExtension +{ + public static HostApplicationBuilder ConfigureLagrangeCore(this HostApplicationBuilder builder) + { + builder.Services + .AddSingleton() // Signer + .AddSingleton((services) => // BotConfig + { + var configuration = services.GetRequiredService(); + + return new BotConfig + { + Protocol = configuration["Account:Protocol"] switch + { + "Windows" => Protocols.Windows, + "MacOs" => Protocols.MacOs, + _ => Protocols.Linux, + }, + AutoReconnect = configuration.GetValue("Account:AutoReconnect", true), + UseIPv6Network = configuration.GetValue("Account:UseIPv6Network", false), + GetOptimumServer = configuration.GetValue("Account:GetOptimumServer", true), + AutoReLogin = configuration.GetValue("Account:AutoReLogin", true), + CustomSignProvider = services.GetRequiredService() + }; + }) + .AddSingleton((services) => // Device + { + var configuration = services.GetRequiredService(); + string path = configuration["ConfigPath:DeviceInfo"] ?? "device.json"; + + return File.Exists(path) + ? JsonSerializer.Deserialize(File.ReadAllText(path)) ?? BotDeviceInfo.GenerateInfo() + : BotDeviceInfo.GenerateInfo(); + }) + .AddSingleton((services) => // Keystore + { + var configuration = services.GetRequiredService(); + string path = configuration["ConfigPath:Keystore"] ?? "keystore.json"; + + return File.Exists(path) + ? JsonSerializer.Deserialize(File.ReadAllText(path)) ?? new() + : new(); + }) + .AddSingleton((services) => services.GetRequiredService().GetAppInfo()) // AppInfo + .AddSingleton((services) => BotFactory.Create( // BotContext + services.GetRequiredService(), + services.GetRequiredService(), + services.GetRequiredService(), + services.GetRequiredService() + )) + .AddHostedService(); + + return builder; + } + + public static HostApplicationBuilder ConfigureOneBot(this HostApplicationBuilder builder) + { + builder.Services.AddOptions() + .AddSingleton(services => // Database + { + var configuration = services.GetRequiredService(); + var logger = services.GetRequiredService>(); + + BsonMapper.Global.TrimWhitespace = false; + BsonMapper.Global.EmptyStringToNull = false; + + // Specify ctor for some classes + BsonMapper.Global.RegisterType( + LiteDbUtility.IMessageEntitySerialize, + LiteDbUtility.IMessageEntityDeserialize + ); + + string path = configuration["ConfigPath:Database"] ?? "lagrange.db"; + + bool isFirstCreate = false; + if (!File.Exists(path)) isFirstCreate = true; + + var db = new LiteDatabase(path) + { + CheckpointSize = 50 + }; + + string[] expressions = ["$.Sequence", "$.MessageId", "$.FriendUin", "$.GroupUin"]; + + bool hasFirstIndex = false; + var indexes = db.GetCollection("$indexes"); + foreach (var expression in expressions) + { + if (indexes.Exists(Query.EQ("expression", expression))) continue; + + logger.LogWarning("In the database index"); + logger.LogWarning("Depending on the size of the database will consume some time and memory"); + logger.LogWarning("Not yet finished, please wait..."); + + hasFirstIndex = true; + break; + } + + var records = db.GetCollection(); + foreach (var expression in expressions) + { + records.EnsureIndex(BsonExpression.Create(expression)); + } + + // Skipping the first database creation is a restart after indexing + if (!isFirstCreate && hasFirstIndex) + { + db.Dispose(); // Ensure that the database is written correctly + logger.LogInformation("Indexing Complete! Press any key to close and restart the program manually!"); + Console.ReadKey(true); + Environment.Exit(0); + } + return db; + }) + + // // OneBot Netword Service + .AddSingleton() + + .AddScoped, ForwardWSServiceFactory>() + .AddScoped() + + .AddScoped, ReverseWSServiceFactory>() + .AddScoped() + + .AddScoped, HttpServiceFactory>() + .AddScoped() + + .AddScoped, HttpPostServiceFactory>() + .AddScoped() + + .AddScoped() + .AddScoped(services => services.GetRequiredService().Create() + ?? throw new Exception("Invalid conf detected")) + + // // OneBot Misc Service + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + return builder; + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/LagrangeApp.cs b/Lagrange.OneBot/LagrangeApp.cs deleted file mode 100644 index d67daf2ba..000000000 --- a/Lagrange.OneBot/LagrangeApp.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System.Text; -using System.Text.Json; -using Lagrange.Core; -using Lagrange.Core.Common.Interface.Api; -using Lagrange.Core.Event.EventArg; -using Lagrange.Core.Utility.Extension; -using Lagrange.Core.Utility.Sign; -using Lagrange.OneBot.Core.Network; -using Lagrange.OneBot.Core.Notify; -using Lagrange.OneBot.Core.Operation; -using Lagrange.OneBot.Message; -using Lagrange.OneBot.Utility; -using LiteDB; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using JsonSerializer = System.Text.Json.JsonSerializer; -using LogLevel = Lagrange.Core.Event.EventArg.LogLevel; - -namespace Lagrange.OneBot; - -public class LagrangeApp : IHost -{ - private readonly IHost _hostApp; - - public IServiceProvider Services => _hostApp.Services; - - public ILogger Logger { get; } - - public IConfiguration Configuration => Services.GetRequiredService(); - - public BotContext Instance => Services.GetRequiredService(); - - public LagrangeWebSvcCollection WebService => Services.GetRequiredService(); - - public MessageService MessageService { get; set; } - - public OperationService OperationService { get; set; } - - private bool _isFirstLogin; - - internal LagrangeApp(IHost host) - { - _hostApp = host; - Logger = Services.GetRequiredService>(); - - Services.GetRequiredService(); - MessageService = Services.GetRequiredService(); - OperationService = Services.GetRequiredService(); - - _isFirstLogin = true; - } - - public async Task StartAsync(CancellationToken cancellationToken = new()) - { - await _hostApp.StartAsync(cancellationToken); - Logger.LogInformation("Lagrange.OneBot Implementation has started"); - Logger.LogInformation($"Protocol: {Configuration["Protocol"]} | {Instance.ContextCollection.AppInfo.CurrentVersion}"); - - Instance.ContextCollection.Packet.SignProvider = Services.GetRequiredService(); - if (!string.IsNullOrEmpty(Configuration["Account:Password"])) - Instance.ContextCollection.Keystore.PasswordMd5 = await Encoding.UTF8.GetBytes(Configuration["Account:Password"] ?? "").Md5Async(); - - Instance.Invoker.OnBotLogEvent += (_, args) => Services.GetRequiredService>().Log(args.Level switch - { - LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Trace, - LogLevel.Verbose => Microsoft.Extensions.Logging.LogLevel.Information, - LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information, - LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, - LogLevel.Fatal => Microsoft.Extensions.Logging.LogLevel.Error, - _ => Microsoft.Extensions.Logging.LogLevel.Error - }, args.ToString()); - - Instance.Invoker.OnBotOnlineEvent += async (_, args) => - { - if (args.Reason == BotOnlineEvent.OnlineReason.Reconnect && !_isFirstLogin) return; - - var keystore = Instance.UpdateKeystore(); - Logger.LogInformation($"Bot Online: {keystore.Uin}"); - string json = JsonSerializer.Serialize(keystore, new JsonSerializerOptions { WriteIndented = true }); - - // Adapters should be started here instead of at the start of application - await WebService.StartAsync(cancellationToken); - Services.GetRequiredService().RegisterEvents(); - - await File.WriteAllTextAsync(Configuration["ConfigPath:Keystore"] ?? "keystore.json", json, cancellationToken); - - _isFirstLogin = false; - }; - - if (string.IsNullOrEmpty(Configuration["Account:Password"]) && - Instance.ContextCollection.Keystore.Session.TempPassword == null) // EasyLogin and PasswordLogin is both disabled - { - Logger.LogInformation("Session expired or Password not filled in, try to login by QrCode"); - - if (await Instance.FetchQrCode() is { } qrCode) - { - QrCodeHelper.Output(qrCode.Url ?? "", Configuration.GetValue("QrCode:ConsoleCompatibilityMode")); - Logger.LogInformation($"Please scan the QR code above, Url: {qrCode.Url}"); - await File.WriteAllBytesAsync($"qr-{Instance.BotUin}.png", qrCode.QrCode ?? Array.Empty(), cancellationToken); - - await Instance.LoginByQrCode(cancellationToken); - } - } - else - { - Instance.Invoker.OnBotCaptchaEvent += async (_, args) => - { - Logger.LogWarning($"Captcha: {args.Url}"); - Logger.LogWarning("Please input ticket and randomString:"); - - await Task.Run(() => - { - var ticket = Console.ReadLine(); - var randomString = Console.ReadLine(); - - if (ticket != null && randomString != null) Instance.SubmitCaptcha(ticket, randomString); - }, cancellationToken); - }; - - await Instance.LoginByPassword(cancellationToken); - } - } - - public async Task StopAsync(CancellationToken cancellationToken = new()) - { - Logger.LogInformation("Lagrange.OneBot Implementation has stopped"); - - Instance.Dispose(); - - Services.GetRequiredService().Dispose(); - await WebService.StopAsync(cancellationToken); - await _hostApp.StopAsync(cancellationToken); - } - - public void Dispose() - { - _hostApp.Dispose(); - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/Lagrange.OneBot/LagrangeAppBuilder.cs b/Lagrange.OneBot/LagrangeAppBuilder.cs deleted file mode 100644 index a7ca0d1fa..000000000 --- a/Lagrange.OneBot/LagrangeAppBuilder.cs +++ /dev/null @@ -1,203 +0,0 @@ -using Lagrange.Core.Common; -using Lagrange.Core.Common.Interface; -using Lagrange.Core.Utility.Sign; -using Lagrange.OneBot.Core.Network; -using Lagrange.OneBot.Core.Network.Service; -using Lagrange.OneBot.Core.Notify; -using Lagrange.OneBot.Core.Operation; -using Lagrange.OneBot.Database; -using Lagrange.OneBot.Message; -using Lagrange.OneBot.Utility; -using LiteDB; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using JsonSerializer = System.Text.Json.JsonSerializer; - -namespace Lagrange.OneBot; - -public sealed class LagrangeAppBuilder -{ - private IServiceCollection Services => _hostAppBuilder.Services; - - private ConfigurationManager Configuration => _hostAppBuilder.Configuration; - - private readonly HostApplicationBuilder _hostAppBuilder; - - public LagrangeAppBuilder(string[] args) - { - _hostAppBuilder = new HostApplicationBuilder(args); - } - - public LagrangeAppBuilder ConfigureConfiguration(string path, bool optional = false, bool reloadOnChange = false) - { - Configuration.AddJsonFile(path, optional, reloadOnChange); - Configuration.AddEnvironmentVariables(); // use this to configure appsettings.json with environment variables in docker container - return this; - } - - public LagrangeAppBuilder ConfigureBots() - { - string keystorePath = Configuration["ConfigPath:Keystore"] ?? "keystore.json"; - string deviceInfoPath = Configuration["ConfigPath:DeviceInfo"] ?? "device.json"; - string appInfoPath = Configuration["ConfigPath:AppInfo"] ?? "appinfo.json"; - - bool isSuccess = Enum.TryParse(Configuration["Account:Protocol"], out var protocol); - var config = new BotConfig - { - Protocol = isSuccess ? protocol : Protocols.Linux, - AutoReconnect = bool.Parse(Configuration["Account:AutoReconnect"] ?? "true"), - UseIPv6Network = bool.Parse(Configuration["Account:UseIPv6Network"] ?? "false"), - GetOptimumServer = bool.Parse(Configuration["Account:GetOptimumServer"] ?? "true"), - AutoReLogin = bool.Parse(Configuration["Account:AutoReLogin"] ?? "true"), - }; - - BotKeystore keystore; - if (!File.Exists(keystorePath)) - { - keystore = Configuration["Account:Uin"] is { } uin && Configuration["Account:Password"] is { } password - ? new BotKeystore(uint.Parse(uin), password) - : new BotKeystore(); - string? directoryPath = Path.GetDirectoryName(keystorePath); - if (!string.IsNullOrEmpty(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - } - else - { - keystore = JsonSerializer.Deserialize(File.ReadAllText(keystorePath)) ?? new BotKeystore(); - } - - BotDeviceInfo deviceInfo; - if (!File.Exists(deviceInfoPath)) - { - deviceInfo = BotDeviceInfo.GenerateInfo(); - string json = JsonSerializer.Serialize(deviceInfo); - string? directoryPath = Path.GetDirectoryName(deviceInfoPath); - if (!string.IsNullOrEmpty(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - File.WriteAllText(deviceInfoPath, json); - } - else - { - deviceInfo = JsonSerializer.Deserialize(File.ReadAllText(deviceInfoPath)) ?? BotDeviceInfo.GenerateInfo(); - } - - BotAppInfo appInfo; - if (!File.Exists(appInfoPath)) - { - appInfo = BotAppInfo.ProtocolToAppInfo[config.Protocol]; - string json = JsonSerializer.Serialize(appInfo); - string? directoryPath = Path.GetDirectoryName(appInfoPath); - if (!string.IsNullOrEmpty(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - File.WriteAllText(appInfoPath, json); - } - else - { - appInfo = JsonSerializer.Deserialize(File.ReadAllText(appInfoPath)) ?? BotAppInfo.ProtocolToAppInfo[config.Protocol]; - } - - Services.AddSingleton(BotFactory.Create(config, deviceInfo, keystore, appInfo)); - - return this; - } - - public LagrangeAppBuilder ConfigureOneBot() - { - Services.AddSingleton(); - Services.AddOptions(); - - Services.AddScoped, ForwardWSServiceFactory>(); - Services.AddScoped(); - Services.AddScoped, ReverseWSServiceFactory>(); - Services.AddScoped(); - Services.AddScoped, HttpServiceFactory>(); - Services.AddScoped(); - Services.AddScoped, HttpPostServiceFactory>(); - Services.AddScoped(); - Services.AddScoped(); - Services.AddScoped(services => - { - return services.GetRequiredService().Create() ?? throw new Exception("Invalid conf detected"); - }); - - // Database - Services.AddSingleton(provider => - { - var logger = provider.GetRequiredService>(); - - BsonMapper.Global.TrimWhitespace = false; - BsonMapper.Global.EmptyStringToNull = false; - - // Specify ctor for some classes - BsonMapper.Global.RegisterType( - LiteDbUtility.IMessageEntitySerialize, - LiteDbUtility.IMessageEntityDeserialize - ); - - string path = Configuration["ConfigPath:Database"] ?? $"lagrange-{Configuration["Account:Uin"]}.db"; - - bool isFirstCreate = false; - if (!File.Exists(path)) isFirstCreate = true; - - var db = new LiteDatabase(path) - { - CheckpointSize = 50 - }; - - string[] expressions = ["$.Sequence", "$.MessageId", "$.FriendUin", "$.GroupUin"]; - - bool hasFirstIndex = false; - var indexes = db.GetCollection("$indexes"); - foreach (var expression in expressions) - { - if (indexes.Exists(Query.EQ("expression", expression))) continue; - - logger.LogWarning("In the database index"); - logger.LogWarning("Depending on the size of the database will consume some time and memory"); - logger.LogWarning("Not yet finished, please wait..."); - - hasFirstIndex = true; - break; - } - - var records = db.GetCollection(); - foreach (var expression in expressions) - { - records.EnsureIndex(BsonExpression.Create(expression)); - } - - // Skipping the first database creation is a restart after indexing - if (!isFirstCreate && hasFirstIndex) - { - db.Dispose(); // Ensure that the database is written correctly - logger.LogInformation("Indexing Complete! Press any key to close and restart the program manually!"); - Console.ReadKey(true); - Environment.Exit(0); - } - return db; - }); - Services.AddSingleton(); - - Services.AddSingleton(); - Services.AddSingleton(); - Services.AddSingleton(); - Services.AddSingleton(); - return this; - } - - public LagrangeAppBuilder ConfigureLogging(Action configureLogging) - { - Services.AddLogging(configureLogging); - return this; - } - - public LagrangeApp Build() => new(_hostAppBuilder.Build()); -} diff --git a/Lagrange.OneBot/Program.cs b/Lagrange.OneBot/Program.cs index bb47932f6..d4bc45b21 100644 --- a/Lagrange.OneBot/Program.cs +++ b/Lagrange.OneBot/Program.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime; using System.Text; +using Lagrange.OneBot.Extensions; using Microsoft.Extensions.Hosting; namespace Lagrange.OneBot; @@ -32,14 +33,13 @@ public static void Main(string[] args) temp.Close(); Console.WriteLine("Please Edit the appsettings.json to set configs and press any key to continue"); - Console.ReadLine(); + Console.ReadKey(true); } - var hostBuilder = new LagrangeAppBuilder(args) - .ConfigureConfiguration("appsettings.json", false, true) - .ConfigureBots() - .ConfigureOneBot(); - - hostBuilder.Build().Run(); + Host.CreateApplicationBuilder() + .ConfigureLagrangeCore() + .ConfigureOneBot() + .Build() + .Run(); } } \ No newline at end of file diff --git a/Lagrange.OneBot/Utility/FallbackAsync.cs b/Lagrange.OneBot/Utility/FallbackAsync.cs new file mode 100644 index 000000000..2d1b84b54 --- /dev/null +++ b/Lagrange.OneBot/Utility/FallbackAsync.cs @@ -0,0 +1,29 @@ + +using System.Diagnostics.CodeAnalysis; + +namespace Lagrange.OneBot.Utility.Fallbacks; + +public class FallbackAsync +{ + private readonly List>> _executors = []; + + internal static FallbackAsync Create() + { + return new(); + } + + public FallbackAsync Add(Func> executor) + { + _executors.Add(executor); + return this; + } + + public async Task ExecuteAsync(CancellationToken token = default) + { + foreach (var executor in _executors) + { + if (await executor(token)) return true; + } + return false; + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Utility/FallbackAsyncT.cs b/Lagrange.OneBot/Utility/FallbackAsyncT.cs new file mode 100644 index 000000000..ca52fd0bf --- /dev/null +++ b/Lagrange.OneBot/Utility/FallbackAsyncT.cs @@ -0,0 +1,27 @@ +namespace Lagrange.OneBot.Utility.Fallbacks; + +public class FallbackAsync +{ + private readonly List>> _executors = []; + + internal static FallbackAsync Create() + { + return new(); + } + + public FallbackAsync Add(Func> executor) + { + _executors.Add(executor); + return this; + } + + public async Task ExecuteAsync(Func> @default, CancellationToken token = default) + { + foreach (var executor in _executors) + { + var result = await executor(token); + if (result != null) return result; + } + return await @default(token); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Utility/OneBotSigner.cs b/Lagrange.OneBot/Utility/OneBotSigner.cs index 2aaa218e4..7de6e18a0 100644 --- a/Lagrange.OneBot/Utility/OneBotSigner.cs +++ b/Lagrange.OneBot/Utility/OneBotSigner.cs @@ -4,8 +4,9 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; -using Lagrange.Core; +using Lagrange.Core.Common; using Lagrange.Core.Utility.Sign; +using Lagrange.OneBot.Utility.Fallbacks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -13,7 +14,9 @@ namespace Lagrange.OneBot.Utility; public class OneBotSigner : SignProvider { - private ILogger _logger; + private readonly IConfiguration _configuration; + + private readonly ILogger _logger; private const string Url = "https://sign.lagrangecore.org/api/sign/25765"; @@ -21,12 +24,15 @@ public class OneBotSigner : SignProvider private readonly HttpClient _client; + private readonly BotAppInfo? _info; + private readonly string platform; private readonly string version; - public OneBotSigner(IConfiguration config, ILogger logger, BotContext bot) + public OneBotSigner(IConfiguration config, ILogger logger) { + _configuration = config; _logger = logger; _signServer = string.IsNullOrEmpty(config["SignServerUrl"]) ? Url : config["SignServerUrl"]; @@ -44,14 +50,15 @@ public OneBotSigner(IConfiguration config, ILogger logger, BotCont if (string.IsNullOrEmpty(_signServer)) logger.LogWarning("Signature Service is not available, login may be failed"); - platform = bot.Config.Protocol switch + _info ??= GetAppInfo(); + platform = _info.Os switch { - Lagrange.Core.Common.Protocols.Windows => "Windows", - Lagrange.Core.Common.Protocols.MacOs => "MacOs", - Lagrange.Core.Common.Protocols.Linux => "Linux", + "Windows" => "Windows", + "Mac" => "MacOs", + "Linux" => "Linux", _ => "Unknown" }; - version = bot.AppInfo.CurrentVersion; + version = _info.CurrentVersion; } public override byte[]? Sign(string cmd, uint seq, byte[] body, [UnscopedRef] out byte[]? e, [UnscopedRef] out string? t) @@ -108,4 +115,34 @@ public OneBotSigner(IConfiguration config, ILogger logger, BotCont 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.Create() + .Add(async token => + { + try { return await _client.GetFromJsonAsync($"{_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(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; + } } \ No newline at end of file From d00c8da7c863a2ab2e0068a902193bbacf671849 Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Sun, 29 Dec 2024 20:41:26 +0800 Subject: [PATCH 06/18] [Core] write qrcode to local file (#718) --- Lagrange.OneBot/Core/Login/LoginService.cs | 2 + .../HostApplicationBuilderExtension.cs | 2 +- Lagrange.OneBot/Resources/appsettings.json | 1 - README.md | 149 +++++++++--------- README_zh.md | 7 +- 5 files changed, 80 insertions(+), 81 deletions(-) diff --git a/Lagrange.OneBot/Core/Login/LoginService.cs b/Lagrange.OneBot/Core/Login/LoginService.cs index 5e715fa08..9114f4699 100644 --- a/Lagrange.OneBot/Core/Login/LoginService.cs +++ b/Lagrange.OneBot/Core/Login/LoginService.cs @@ -47,6 +47,8 @@ public async Task StartAsync(CancellationToken token) { (string Url, byte[] QrCode)? qrcode = await _lagrange.FetchQrCode().WaitAsync(token); if (!qrcode.HasValue) return false; + + await File.WriteAllBytesAsync($"qr-{configuration["Account:Uin"]}.png", qrcode.Value.QrCode, token); QrCodeHelper.Output(qrcode.Value.Url, _isCompatibility); return await (Task)_lagrange.LoginByQrCode(token); diff --git a/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs b/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs index d0dc7a1c7..33478f2e8 100644 --- a/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs +++ b/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs @@ -90,7 +90,7 @@ public static HostApplicationBuilder ConfigureOneBot(this HostApplicationBuilder LiteDbUtility.IMessageEntityDeserialize ); - string path = configuration["ConfigPath:Database"] ?? "lagrange.db"; + string path = configuration["ConfigPath:Database"] ?? $"lagrange-{configuration["Account:Uin"]}.db"; bool isFirstCreate = false; if (!File.Exists(path)) isFirstCreate = true; diff --git a/Lagrange.OneBot/Resources/appsettings.json b/Lagrange.OneBot/Resources/appsettings.json index 6e138431c..8a3f016bf 100644 --- a/Lagrange.OneBot/Resources/appsettings.json +++ b/Lagrange.OneBot/Resources/appsettings.json @@ -11,7 +11,6 @@ "MusicSignServerUrl": "", "Account": { "Uin": 0, - "Password": "", "Protocol": "Linux", "AutoReconnect": true, "GetOptimumServer": true diff --git a/README.md b/README.md index c76281e40..e47cc55e4 100644 --- a/README.md +++ b/README.md @@ -72,20 +72,20 @@ Thanks for 外国热心网友 for Provision of Azure Servlet | Protocol | Support | Login | Support | Messages | Support | Operations | Support | Events | Support | | -------- | :-----: | ------------------------- | :-----: | :-------- | :-----: | :---------------- | :-----: | :------------------ | :-----: | -| Windows | 🟢 | QrCode | 🟢 | Images | 🟢 | Poke | 🟢 | Captcha | 🟢 | -| macOS | 🟢 | Password | 🟢 | Text / At | 🟢 | Recall | 🟢 | BotOnline | 🟢 | +| Windows | 🟢 | QrCode | 🟢 | Images | 🟢 | Poke | 🟢 | Captcha | 🟢 | +| macOS | 🟢 | Password | 🔴 | Text / At | 🟢 | Recall | 🟢 | BotOnline | 🟢 | | Linux | 🟢 | EasyLogin | 🟢 | Records | 🟢 | Leave Group | 🟢 | BotOffline | 🟢 | -| | | UnusalDevice
Password | 🔴 | QFace | 🟢 | Set Special Title | 🟢 | Message | 🟢 | -| | | UnusalDevice
Easy | 🟢 | Json | 🟢 | Kick Member | 🟢 | Poke | 🟢 | -| | | NewDeviceVerify | 🟢 | Xml | 🟢 | Mute Member | 🟢 | MessageRecall | 🟢 | -| | | | | Forward | 🟢 | Set Admin | 🟢 | GroupMemberDecrease | 🟢 | -| | | | | Video | 🟢 | Friend Request | 🟢 | GroupMemberIncrease | 🟢 | -| | | | | Reply | 🟢 | Group Request | 🟢 | GroupPromoteAdmin | 🟢 | -| | | | | File | 🟢 | ~~Voice Call~~ | 🔴 | GroupInvite | 🟢 | -| | | | | Poke | 🟢 | Client Key | 🟢 | GroupRequestJoin | 🟢 | -| | | | | LightApp | 🟢 | Cookies | 🟢 | FriendRequest | 🟢 | -| | | | | | | Send Message | 🟢 | ~~FriendTyping~~ | 🔴 | -| | | | | | | | | ~~FriendVoiceCall~~ | 🔴 | +| | | UnusalDevice
Password | 🔴 | QFace | 🟢 | Set Special Title | 🟢 | Message | 🟢 | +| | | UnusalDevice
Easy | 🟢 | Json | 🟢 | Kick Member | 🟢 | Poke | 🟢 | +| | | NewDeviceVerify | 🔴 | Xml | 🟢 | Mute Member | 🟢 | MessageRecall | 🟢 | +| | | | | Forward | 🟢 | Set Admin | 🟢 | GroupMemberDecrease | 🟢 | +| | | | | Video | 🟢 | Friend Request | 🟢 | GroupMemberIncrease | 🟢 | +| | | | | Reply | 🟢 | Group Request | 🟢 | GroupPromoteAdmin | 🟢 | +| | | | | File | 🟢 | ~~Voice Call~~ | 🔴 | GroupInvite | 🟢 | +| | | | | Poke | 🟢 | Client Key | 🟢 | GroupRequestJoin | 🟢 | +| | | | | LightApp | 🟢 | Cookies | 🟢 | FriendRequest | 🟢 | +| | | | | | | Send Message | 🟢 | ~~FriendTyping~~ | 🔴 | +| | | | | | | | | ~~FriendVoiceCall~~ | 🔴 | ## Lagrange.OneBot @@ -144,7 +144,7 @@ Thanks for 外国热心网友 for Provision of Azure Servlet API | API | Support | -|--------------------------------| :-----: | +| ------------------------------ | :-----: | | [/send_private_msg] | 🟢 | | [/send_group_msg] | 🟢 | | [/send_msg] | 🟢 | @@ -291,86 +291,85 @@ Thanks for 外国热心网友 for Provision of Azure Servlet ```json { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "SignServerUrl": "", - "Account": { - "Uin": 0, - "Password": "", - "Protocol": "Linux", - "AutoReconnect": true, - "GetOptimumServer": true - }, - "Message": { - "IgnoreSelf": true, - "StringPost": false - }, - "QrCode": { - "ConsoleCompatibilityMode": false - }, - "Implementations": [ - { - "Type": "ReverseWebSocket", - "Host": "127.0.0.1", - "Port": 8080, - "Suffix": "/onebot/v11/ws", - "ReconnectInterval": 5000, - "HeartBeatInterval": 5000, - "HeartBeatEnable": true, - "AccessToken": "" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } }, - { - "Type": "ForwardWebSocket", - "Host": "*", - "Port": 8081, - "HeartBeatInterval": 5000, - "HeartBeatEnable": true, - "AccessToken": "" + "SignServerUrl": "", + "Account": { + "Uin": 0, // Only used to create lagrange-{uin}.db and qr-{uin}.png + "Protocol": "Linux", + "AutoReconnect": true, + "GetOptimumServer": true }, - { - "Type": "HttpPost", - "Host": "127.0.0.1", - "Port": 8082, - "Suffix": "/", - "HeartBeatInterval": 5000, - "HeartBeatEnable": true, - "AccessToken": "" + "Message": { + "IgnoreSelf": true, + "StringPost": false }, - { - "Type": "Http", - "Host": "*", - "Port": 8083, - "AccessToken": "" - } - ] + "QrCode": { + "ConsoleCompatibilityMode": false + }, + "Implementations": [ + { + "Type": "ReverseWebSocket", + "Host": "127.0.0.1", + "Port": 8080, + "Suffix": "/onebot/v11/ws", + "ReconnectInterval": 5000, + "HeartBeatInterval": 5000, + "HeartBeatEnable": true, + "AccessToken": "" + }, + { + "Type": "ForwardWebSocket", + "Host": "*", + "Port": 8081, + "HeartBeatInterval": 5000, + "HeartBeatEnable": true, + "AccessToken": "" + }, + { + "Type": "HttpPost", + "Host": "127.0.0.1", + "Port": 8082, + "Suffix": "/", + "HeartBeatInterval": 5000, + "HeartBeatEnable": true, + "AccessToken": "" + }, + { + "Type": "Http", + "Host": "*", + "Port": 8083, + "AccessToken": "" + } + ] } ``` > [!WARNING] > Currently, `ForwardWebSocket` and `Http` are implemented based on `HttpListener`, which has the following problems: -> +> > 1. On Linux, the `Host` header of an Http request must match the value of `Prefix` unless it is `+` or `*`, so configure the `Host` of `ForwardWebSocket` and `Http` to be the domain name or IP you are using to access it. -> +> > 2. On Windows, the `HttpListener` is based on the `http.sys` implementation, so you need to register `urlacl` before using it. see [netsh](https://learn.microsoft.com/en-us/windows-server/networking/technologies/netsh/netsh-http). You can also start `Lagrange.OneBot` using the administrator, at which point `HttpListener` will automatically register the required `urlacl`. ## NOTICE BEFORE LOGIN -- The NewDeviceLogin feature has not been implemented yet. It is recommended to use QRCode login for now. -- Currently, only the signature server implementation for Linux protocol is available. It is recommended to use the Linux protocol. +- The NewDeviceLogin feature has not been implemented yet. It is recommended to use QRCode login for now. +- Currently, only the signature server implementation for Linux protocol is available. It is recommended to use the Linux protocol. ## Known Problem -- ~~[ ] Signature Service is currently not established, so the login tend to be failed and return code may be 45, you can establish your own sign service by rewriting the `Signature` static class.~~ +- ~~[ ] Signature Service is currently not established, so the login tend to be failed and return code may be 45, you can establish your own sign service by rewriting the `Signature` static class.~~ ~~Thanks KonataDev/TheSnowfield for Provision of Signature API~~ ~~Signature API is now not provided, you may need to find it somewhere and inherit `SignProvider` class for `CustomSignProvider` in `BotConfig`~~ -- ~~Built-in SignServer is now provided, Enjoy!~~ +- ~~Built-in SignServer is now provided, Enjoy!~~ -- Signature of Windows and macOS is missing, you need to figure out by your self +- Signature of Windows and macOS is missing, you need to figure out by your self diff --git a/README_zh.md b/README_zh.md index 72d67baf1..716f432ab 100644 --- a/README_zh.md +++ b/README_zh.md @@ -67,11 +67,11 @@ Please use Lagrange.Core responsibly and in accordance with the law. | 协议 | 支持情况 | 登录类型 | 支持情况 | 消息段 | 支持情况 | 操作 | 支持情况 | 事件 | 支持情况 | | ------- | :------: | ----------------- | :------: | :--------------- | :------: | :--------------- | :------: | :--------------- | :------: | | Windows | 🟢 | 扫码登录 | 🟢 | 图片 | 🟢 | 戳一戳 | 🟢 | 验证码 | 🟢 | -| macOS | 🟢 | 密码登录 | 🟢 | 文本 / At | 🟢 | 撤回消息 | 🟢 | 机器人在线 | 🟢 | +| macOS | 🟢 | 密码登录 | 🔴 | 文本 / At | 🟢 | 撤回消息 | 🟢 | 机器人在线 | 🟢 | | Linux | 🟢 | 快速登录 | 🟢 | 语音 | 🟢 | 退出群组 | 🟢 | 机器人离线 | 🟢 | | | | 异常设备
密码 | 🔴 | QQ 黄脸表情 | 🟢 | 特殊头衔 | 🟢 | 消息事件 | 🟢 | | | | 异常设备
快速 | 🟢 | Json | 🟢 | 移除群成员 | 🟢 | 戳一戳事件 | 🟢 | -| | | 新设备验证 | 🟢 | Xml | 🟢 | 禁言群成员 | 🟢 | 消息撤回事件 | 🟢 | +| | | 新设备验证 | 🔴 | Xml | 🟢 | 禁言群成员 | 🟢 | 消息撤回事件 | 🟢 | | | | | | 合并转发 | 🟢 | 设置管理员 | 🟢 | 群成员减少 | 🟢 | | | | | | 视频 | 🟢 | 处理添加好友请求 | 🟢 | 群成员增加 | 🟢 | | | | | | 回复 | 🟢 | 处理加群请求 | 🟢 | 群管理员变动 | 🟢 | @@ -296,8 +296,7 @@ Please use Lagrange.Core responsibly and in accordance with the law. }, "SignServerUrl": "", "Account": { - "Uin": 0, - "Password": "", + "Uin": 0, // 仅用于创建 lagrange-{uin}.db 和 qr-{uin}.png "Protocol": "Linux", "AutoReconnect": true, "GetOptimumServer": true From 89c4c1778a051cc11246fcb010a99ac975b37f8d Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Sun, 29 Dec 2024 21:04:00 +0800 Subject: [PATCH 07/18] [Core] fix one-time login (#719) --- Lagrange.OneBot/Core/Login/LoginService.cs | 4 ++++ .../Extensions/HostApplicationBuilderExtension.cs | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lagrange.OneBot/Core/Login/LoginService.cs b/Lagrange.OneBot/Core/Login/LoginService.cs index 9114f4699..b38b9df45 100644 --- a/Lagrange.OneBot/Core/Login/LoginService.cs +++ b/Lagrange.OneBot/Core/Login/LoginService.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using Lagrange.Core; using Lagrange.Core.Common.Interface.Api; using Lagrange.Core.Event.EventArg; @@ -57,6 +58,9 @@ public async Task StartAsync(CancellationToken token) if (!isSucceed) throw new Exception("All login failed!"); + string keystoreJson = JsonSerializer.Serialize(_lagrange.UpdateKeystore()); + File.WriteAllText(configuration["ConfigPath:Keystore"] ?? "keystore.json", keystoreJson); + _logger.LogInformation("Bot Uin: {}", _lagrange.BotUin); await _web.StartAsync(token); diff --git a/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs b/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs index 33478f2e8..63c45f1e9 100644 --- a/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs +++ b/Lagrange.OneBot/Extensions/HostApplicationBuilderExtension.cs @@ -48,9 +48,14 @@ public static HostApplicationBuilder ConfigureLagrangeCore(this HostApplicationB var configuration = services.GetRequiredService(); string path = configuration["ConfigPath:DeviceInfo"] ?? "device.json"; - return File.Exists(path) + var device = File.Exists(path) ? JsonSerializer.Deserialize(File.ReadAllText(path)) ?? BotDeviceInfo.GenerateInfo() : BotDeviceInfo.GenerateInfo(); + + string deviceJson = JsonSerializer.Serialize(device); + File.WriteAllText(path, deviceJson); + + return device; }) .AddSingleton((services) => // Keystore { From d7be22f0f2a1ce4ed9034cf3ad1c2b2bb5eb43e6 Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Mon, 30 Dec 2024 15:38:25 +0800 Subject: [PATCH 08/18] ci: update Lagrange.OneBot .NET SDK to 9.0.x (#721) --- .github/workflows/Lagrange.OneBot-release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Lagrange.OneBot-release.yml b/.github/workflows/Lagrange.OneBot-release.yml index 4092ede77..c7fb0a511 100644 --- a/.github/workflows/Lagrange.OneBot-release.yml +++ b/.github/workflows/Lagrange.OneBot-release.yml @@ -62,15 +62,15 @@ jobs: - uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: '9.0.x' - - run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net8.0 + - run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net9.0 - run: | if [[ ${{ matrix.runtimeIdentifier }} == 'win-x64' || ${{ matrix.runtimeIdentifier }} == 'win-x86' ]]; then - zip -r Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net8.0_SelfContained.zip ./Lagrange.OneBot/bin/Release/net8.0/${{ matrix.runtimeIdentifier }}/publish - gh release upload nightly Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net8.0_SelfContained.zip -R ${{ github.repository }} + zip -r Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.zip ./Lagrange.OneBot/bin/Release/net9.0/${{ matrix.runtimeIdentifier }}/publish + gh release upload nightly Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.zip -R ${{ github.repository }} else - tar -czvf Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net8.0_SelfContained.tar.gz ./Lagrange.OneBot/bin/Release/net8.0/${{ matrix.runtimeIdentifier }}/publish - gh release upload nightly Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net8.0_SelfContained.tar.gz -R ${{ github.repository }} + tar -czvf Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.tar.gz ./Lagrange.OneBot/bin/Release/net9.0/${{ matrix.runtimeIdentifier }}/publish + gh release upload nightly Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.tar.gz -R ${{ github.repository }} fi From 41633fff61e26a1189cf35ae4b78f182b5bc0bca Mon Sep 17 00:00:00 2001 From: Linwenxuan04 Date: Mon, 30 Dec 2024 22:11:42 +0800 Subject: [PATCH 09/18] [Core] Updated definition for SsoReserveFields Co-Authored-By: Simplxs --- Lagrange.Core/Internal/Packets/SsoPacker.cs | 6 +-- .../Internal/Packets/System/NTDeviceSign.cs | 16 ------ .../Packets/System/SsoReserveFields.cs | 49 +++++++++++++++++++ .../System/{SecInfo.cs => SsoSecureInfo.cs} | 2 +- 4 files changed, 53 insertions(+), 20 deletions(-) delete mode 100644 Lagrange.Core/Internal/Packets/System/NTDeviceSign.cs create mode 100644 Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs rename Lagrange.Core/Internal/Packets/System/{SecInfo.cs => SsoSecureInfo.cs} (94%) diff --git a/Lagrange.Core/Internal/Packets/SsoPacker.cs b/Lagrange.Core/Internal/Packets/SsoPacker.cs index 283f86cf7..ffea2c22d 100644 --- a/Lagrange.Core/Internal/Packets/SsoPacker.cs +++ b/Lagrange.Core/Internal/Packets/SsoPacker.cs @@ -19,15 +19,15 @@ public static BinaryPacket Build(SsoPacket packet, BotAppInfo appInfo, BotDevice var writer = new BinaryPacket(); var sign = signProvider.Sign(packet.Command, packet.Sequence, packet.Payload.ToArray(), out var extra, out var token); - var signature = new NTDeviceSign + var signature = new SsoReserveFields { - Sign = sign == null ? null : new SecInfo + SecInfo = sign == null ? null : new SsoSecureInfo { SecSign = sign, SecToken = token, SecExtra = extra }, - Trace = StringGen.GenerateTrace(), + TraceParent = StringGen.GenerateTrace(), Uid = keystore.Uid }; var stream = new MemoryStream(); diff --git a/Lagrange.Core/Internal/Packets/System/NTDeviceSign.cs b/Lagrange.Core/Internal/Packets/System/NTDeviceSign.cs deleted file mode 100644 index 8dbf44b55..000000000 --- a/Lagrange.Core/Internal/Packets/System/NTDeviceSign.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ProtoBuf; - -// ReSharper disable InconsistentNaming -#pragma warning disable CS8618 - -namespace Lagrange.Core.Internal.Packets.System; - -[ProtoContract] -internal class NTDeviceSign -{ - [ProtoMember(15)] public string Trace { get; set; } - - [ProtoMember(16)] public string? Uid { get; set; } - - [ProtoMember(24)] public SecInfo? Sign { get; set; } -} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs b/Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs new file mode 100644 index 000000000..12e93e4b9 --- /dev/null +++ b/Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs @@ -0,0 +1,49 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class SsoReserveFields +{ + [ProtoMember(4)] public byte[]? ClientIpcookie { get; set; } + + [ProtoMember(9)] public uint? Flag { get; set; } + + [ProtoMember(10)] public uint? EnvId { get; set; } + + [ProtoMember(11)] public uint? LocaleId { get; set; } + + [ProtoMember(12)] public string? Qimei { get; set; } + + [ProtoMember(13)] public byte[]? Env { get; set; } + + [ProtoMember(14)] public uint? NewconnFlag { get; set; } + + [ProtoMember(15)] public string? TraceParent { get; set; } + + [ProtoMember(16)] public string? Uid { get; set; } + + [ProtoMember(18)] public uint? Imsi { get; set; } + + [ProtoMember(19)] public uint? NetworkType { get; set; } + + [ProtoMember(20)] public uint? IpStackType { get; set; } + + [ProtoMember(21)] public uint? MsgType { get; set; } + + [ProtoMember(22)] public string? TrpcRsp { get; set; } + + [ProtoMember(23)] public Dictionary? TransInfo { get; set; } + + [ProtoMember(24)] public SsoSecureInfo? SecInfo { get; set; } + + [ProtoMember(25)] public uint? SecSigFlag { get; set; } + + [ProtoMember(26)] public uint? NtCoreVersion { get; set; } + + [ProtoMember(27)] public uint? SsoRouteCost { get; set; } + + [ProtoMember(28)] public uint? SsoIpOrigin { get; set; } + + [ProtoMember(30)] public byte[]? PresureToken { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/System/SecInfo.cs b/Lagrange.Core/Internal/Packets/System/SsoSecureInfo.cs similarity index 94% rename from Lagrange.Core/Internal/Packets/System/SecInfo.cs rename to Lagrange.Core/Internal/Packets/System/SsoSecureInfo.cs index a66e6e7f1..c9a5e843a 100644 --- a/Lagrange.Core/Internal/Packets/System/SecInfo.cs +++ b/Lagrange.Core/Internal/Packets/System/SsoSecureInfo.cs @@ -7,7 +7,7 @@ namespace Lagrange.Core.Internal.Packets.System; /// 没错 这个就是臭名昭著的0C03算法 每一个包都有这个几把 /// [ProtoContract] -internal class SecInfo +internal class SsoSecureInfo { [ProtoMember(1)] public byte[]? SecSign { get; set; } From 91d297512f1e6704ee0ac869aa027ebd54deb44c Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Thu, 2 Jan 2025 00:15:26 +0800 Subject: [PATCH 10/18] [Core] Fix: Music Signer not initialized (#724) --- Lagrange.OneBot/Extensions/HostExtension.cs | 15 +++++++++++++++ Lagrange.OneBot/Program.cs | 1 + 2 files changed, 16 insertions(+) create mode 100644 Lagrange.OneBot/Extensions/HostExtension.cs diff --git a/Lagrange.OneBot/Extensions/HostExtension.cs b/Lagrange.OneBot/Extensions/HostExtension.cs new file mode 100644 index 000000000..ca8bc1751 --- /dev/null +++ b/Lagrange.OneBot/Extensions/HostExtension.cs @@ -0,0 +1,15 @@ +using Lagrange.OneBot.Utility; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Lagrange.OneBot.Extensions; + +public static class HostExtension +{ + public static T InitializeMusicSigner(this T host) where T : IHost + { + host.Services.GetRequiredService(); + + return host; + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Program.cs b/Lagrange.OneBot/Program.cs index d4bc45b21..f865235c9 100644 --- a/Lagrange.OneBot/Program.cs +++ b/Lagrange.OneBot/Program.cs @@ -40,6 +40,7 @@ public static void Main(string[] args) .ConfigureLagrangeCore() .ConfigureOneBot() .Build() + .InitializeMusicSigner() // Very ugly ( .Run(); } } \ No newline at end of file From 05d793ef13e9343533dc98e2006089d223d7ef5f Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sat, 4 Jan 2025 12:58:11 +0800 Subject: [PATCH 11/18] [OneBot] adjust SendGroupAiRecordOperation (#727) --- .../Message/SendGroupAiRecordOperation.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs b/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs index 858734ced..618ae4620 100644 --- a/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs +++ b/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs @@ -11,7 +11,7 @@ namespace Lagrange.OneBot.Core.Operation.Message; [Operation("send_group_ai_record")] -public class GetAiRecordOperation : IOperation +public class SendGroupAiRecordOperation : IOperation { public async Task HandleOperation(BotContext context, JsonNode? payload) { @@ -24,17 +24,9 @@ public async Task HandleOperation(BotContext context, JsonNode? pa message.Text, message.ChatType ); - if (code != 0 || recordEntity == null) return new OneBotResult(null, code, "failed"); - - var chain = MessageBuilder.Group(message.GroupId).Add(recordEntity).Build(); - var result = await context.SendMessage(chain); - - if (result.Result != 0) return new OneBotResult(null, (int)result.Result, "failed"); - if (result.Sequence == null || result.Sequence == 0) return new OneBotResult(null, 9000, "failed"); - - int hash = MessageRecord.CalcMessageHash(chain.MessageId, result.Sequence ?? 0); - - return new OneBotResult(new OneBotMessageResponse(hash), (int)result.Result, "ok"); + return recordEntity != null && code == 0 + ? new OneBotResult(new OneBotMessageResponse(0), code, "ok") + : new OneBotResult(null, code, "failed"); } throw new Exception(); From 17b66f76590548e62407a6be0a92f559b51be954 Mon Sep 17 00:00:00 2001 From: Linwenxuan04 Date: Sun, 5 Jan 2025 21:23:31 +0800 Subject: [PATCH 12/18] [Core] Improved Implementation of TEA (#728) --- .../Crypto/Provider/Tea/TeaProvider.cs | 222 +++++++++--------- 1 file changed, 105 insertions(+), 117 deletions(-) diff --git a/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs b/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs index e748608f0..5048a8e40 100644 --- a/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs +++ b/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs @@ -1,138 +1,126 @@ -using System.Runtime.CompilerServices; +using System.Buffers.Binary; namespace Lagrange.Core.Utility.Crypto.Provider.Tea; -internal static unsafe class TeaProvider +internal static class TeaProvider { - private const long Delta = 2654435769L; // 0x9E3779B9, Fibonacci's hashing constant. - - private const long SumMax = (Delta << 4) & uint.MaxValue; // 0x9E3779B9 * 16 = 0x3C6EF35F, max sum value. - - public static byte[] Encrypt(Span data, Span key) + public static byte[] Encrypt(byte[] data, byte[] key) { - var keyStruct = new Key(key); - int inputLength = data.Length; - int fill = ((8 - ((inputLength + 10) & 7)) & 7) + 2; - int length = fill + inputLength + 8; - - var cipher = new byte[length]; - cipher[0] = (byte)(RandomByte(248) | (fill - 2)); - for (int i = 1; i <= fill; ++i) cipher[i] = RandomByte(); - - fixed (byte* dataPtr = cipher, rawPtr = data) - { - Buffer.MemoryCopy(rawPtr, dataPtr + fill + 1, inputLength, inputLength); + uint a = BinaryPrimitives.ReadUInt32BigEndian(key.AsSpan(0)); + uint b = BinaryPrimitives.ReadUInt32BigEndian(key.AsSpan(4)); + uint c = BinaryPrimitives.ReadUInt32BigEndian(key.AsSpan(8)); + uint d = BinaryPrimitives.ReadUInt32BigEndian(key.AsSpan(12)); - byte* plainXorPrev = stackalloc byte[8]; - byte* tempCipher = stackalloc byte[8]; - byte* plainXor = stackalloc byte[8]; - byte* encipher = stackalloc byte[8]; + int fill = 10 - ((data.Length + 1) & 7); + var result = new byte[fill + data.Length + 7]; + Random.Shared.NextBytes(result.AsSpan(0, fill)); + result[0] = (byte)((fill - 3) | 0xF8); + data.CopyTo(result.AsSpan(fill)); - for (int i = 0; i < length; i += 8) - { - *(long*)plainXor = *(long*)(dataPtr + i) ^ *(long*)(tempCipher); + ulong plainXor = 0, prevXor = 0; + + for (int i = 0; i < result.Length; i += 8) + { + ulong plain = BinaryPrimitives.ReadUInt64BigEndian(result.AsSpan(i)) ^ plainXor; + uint x = (uint)(plain >> 32); + uint y = (uint)(plain); - long sum = 0; - long y = ReadUInt32(plainXor, 0); - long z = ReadUInt32(plainXor, 4); - for (int j = 0; j < 16; ++j) - { - sum += Delta; - sum &= uint.MaxValue; - y += ((z << 4) + keyStruct.A) ^ (z + sum) ^ ((z >> 5) + keyStruct.B); - y &= uint.MaxValue; - z += ((y << 4) + keyStruct.C) ^ (y + sum) ^ ((y >> 5) + keyStruct.D); - z &= uint.MaxValue; - } - - WriteUInt32(encipher, 0, (uint)y); - WriteUInt32(encipher, 4, (uint)z); - - *(long*)tempCipher = *(long*)(encipher) ^ *(long*)(plainXorPrev); // Xor8(EnCipher(plainXor, key), plainXorPrev); - *(long*)(plainXorPrev) = *(long*)(plainXor); // write data back to plainXorPrev - - *(long*)(dataPtr + i) = *(long*)(tempCipher); // write data back to cipher - } + x += (y + 0x9e3779b9) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x9e3779b9) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x3c6ef372) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x3c6ef372) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0xdaa66d2b) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0xdaa66d2b) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x78dde6e4) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x78dde6e4) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x1715609d) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x1715609d) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0xb54cda56) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0xb54cda56) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x5384540f) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x5384540f) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0xf1bbcdc8) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0xf1bbcdc8) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x8ff34781) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x8ff34781) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x2e2ac13a) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x2e2ac13a) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0xcc623af3) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0xcc623af3) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x6a99b4ac) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x6a99b4ac) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x08d12e65) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x08d12e65) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0xa708a81e) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0xa708a81e) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0x454021d7) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0x454021d7) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x += (y + 0xe3779b90) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y += (x + 0xe3779b90) ^ ((x << 4) + c) ^ ((x >> 5) + d); + + plainXor = ((ulong)x << 32 | y) ^ prevXor; + prevXor = plain; + BinaryPrimitives.WriteUInt64BigEndian(result.AsSpan(i), plainXor); } - - return cipher; + + return result; } public static byte[] Decrypt(Span data, Span key) { - var keyStruct = new Key(key); - int length = data.Length; - if ((length & 7) != 0 || (length >> 4) == 0) throw new Exception("Invalid cipher data length."); - - var plain = new byte[length]; - byte* plainSub = stackalloc byte[8]; - byte* plainXor = stackalloc byte[8]; + uint a = BinaryPrimitives.ReadUInt32BigEndian(key[..]); + uint b = BinaryPrimitives.ReadUInt32BigEndian(key[4..]); + uint c = BinaryPrimitives.ReadUInt32BigEndian(key[8..]); + uint d = BinaryPrimitives.ReadUInt32BigEndian(key[12..]); + + var dest = new byte[data.Length]; - fixed (byte* rawPtr = data, dataPtr = plain) + ulong plainXor = 0, prevXor = 0; + for (int i = 0; i < data.Length; i += 8) { - for (int i = 0; i < length; i += 8) // Decrypt data. - { - *(long*)plainXor = *(long*)(rawPtr + i) ^ *(long*)(plainSub); - - long sum = SumMax; - long y = ReadUInt32(plainXor, 0); - long z = ReadUInt32(plainXor, 4); - for (int j = 0; j < 16; ++j) - { - z -= ((y << 4) + keyStruct.C) ^ (y + sum) ^ ((y >> 5) + keyStruct.D); - z &= uint.MaxValue; - y -= ((z << 4) + keyStruct.A) ^ (z + sum) ^ ((z >> 5) + keyStruct.B); - y &= uint.MaxValue; - sum -= Delta; - sum &= uint.MaxValue; - } - - WriteUInt32(plainSub, 0, (uint)y); - WriteUInt32(plainSub, 4, (uint)z); - - *(long*)(dataPtr + i) = *(long*)(plainSub) ^ *(long*)(rawPtr + i - 8); - } - - for (int i = length - 7; i < length; ++i) // Verify that the last 7 bytes are 0. - { - if (plain[i] != 0) throw new Exception("Verification failed."); - } - int from = (plain[0] & 7) + 3; // Extract valid data. - return plain[from..(length - 7)]; - } - } + ulong plain = BinaryPrimitives.ReadUInt64BigEndian(data[i..]); + plainXor ^= plain; + uint x = (uint)(plainXor >> 32); + uint y = (uint)(plainXor); - private readonly struct Key - { - public uint A { get; } - public uint B { get; } - public uint C { get; } - public uint D { get; } + y -= (x + 0xe3779b90) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0xe3779b90) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x454021d7) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x454021d7) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0xa708a81e) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0xa708a81e) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x08d12e65) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x08d12e65) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x6a99b4ac) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x6a99b4ac) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0xcc623af3) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0xcc623af3) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x2e2ac13a) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x2e2ac13a) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x8ff34781) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x8ff34781) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0xf1bbcdc8) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0xf1bbcdc8) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x5384540f) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x5384540f) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0xb54cda56) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0xb54cda56) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x1715609d) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x1715609d) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x78dde6e4) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x78dde6e4) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0xdaa66d2b) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0xdaa66d2b) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x3c6ef372) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x3c6ef372) ^ ((y << 4) + a) ^ ((y >> 5) + b); + y -= (x + 0x9e3779b9) ^ ((x << 4) + c) ^ ((x >> 5) + d); + x -= (y + 0x9e3779b9) ^ ((y << 4) + a) ^ ((y >> 5) + b); - public Key(Span rawKey) - { - fixed (byte* rawKeyPtr = rawKey) - { - A = ReadUInt32(rawKeyPtr, 0); - B = ReadUInt32(rawKeyPtr, 4); - C = ReadUInt32(rawKeyPtr, 8); - D = ReadUInt32(rawKeyPtr, 12); - } + plainXor = ((ulong)x << 32) | y; + BinaryPrimitives.WriteUInt64BigEndian(dest.AsSpan(i), plainXor ^ prevXor); + prevXor = plain; } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint ReadUInt32(byte* data, int index) => (uint)(data[index] << 24 | data[index + 1] << 16 | data[index + 2] << 8 | data[index + 3]); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WriteUInt32(byte* data, int index, uint value) - { - data[index] = (byte)(value >> 24); - data[index + 1] = (byte)(value >> 16); - data[index + 2] = (byte)(value >> 8); - data[index + 3] = (byte)value; + return dest[((dest[0] & 7) + 3)..^7]; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte RandomByte(int max = byte.MaxValue) => (byte) (Random.Shared.Next() & max); } \ No newline at end of file From 116c61f953cef6de468890c3184846104a587715 Mon Sep 17 00:00:00 2001 From: Shua Date: Tue, 7 Jan 2025 07:10:26 +0800 Subject: [PATCH 13/18] =?UTF-8?q?[OneBot]=20=E6=B7=BB=E5=8A=A0=E6=88=B3?= =?UTF-8?q?=E4=B8=80=E6=88=B3=E4=BA=8C=E5=90=88=E4=B8=80=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=20(#725)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 传入group_id按group_poke处理, 否则按friend_poke处理 Update Lagrange.OneBot/Core/Operation/Message/SendPokeOperation.cs Co-authored-by: DarkRRb <177549718+DarkRRb@users.noreply.github.com> 忘删了 合并 失败返回-1并对提交签名 --- .../Core/Entity/Action/OneBotSendPoke.cs | 11 +++++++++ .../Operation/Message/SendPokeOperation.cs | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Lagrange.OneBot/Core/Entity/Action/OneBotSendPoke.cs create mode 100644 Lagrange.OneBot/Core/Operation/Message/SendPokeOperation.cs diff --git a/Lagrange.OneBot/Core/Entity/Action/OneBotSendPoke.cs b/Lagrange.OneBot/Core/Entity/Action/OneBotSendPoke.cs new file mode 100644 index 000000000..f017e1b3e --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Action/OneBotSendPoke.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Entity.Action; + +[Serializable] +public class OneBotSendPoke +{ + [JsonPropertyName("user_id")] public uint UserId { get; set; } + + [JsonPropertyName("group_id")] public uint? GroupId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/Message/SendPokeOperation.cs b/Lagrange.OneBot/Core/Operation/Message/SendPokeOperation.cs new file mode 100644 index 000000000..e6fb9b5ff --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Message/SendPokeOperation.cs @@ -0,0 +1,23 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Lagrange.Core; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.OneBot.Core.Entity.Action; +using Lagrange.OneBot.Core.Operation.Converters; + +namespace Lagrange.OneBot.Core.Operation.Message; + +[Operation("send_poke")] +public class SendPokeOperation : IOperation +{ + public async Task HandleOperation(BotContext context, JsonNode? payload) + { + if (payload.Deserialize(SerializerOptions.DefaultOptions) is { } poke) + { + bool result = poke.GroupId.HasValue ? await context.GroupPoke(poke.GroupId.Value, poke.UserId) : await context.FriendPoke(poke.UserId); + return new OneBotResult(null, result ? 0 : -1, result ? "ok" : "failed"); + } + + throw new Exception(); + } +} \ No newline at end of file From 07a5d28bf372abf67ca37238981c58623e139863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=2E=E5=96=9C?= <863450594@qq.com> Date: Tue, 7 Jan 2025 10:47:32 +0800 Subject: [PATCH 14/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8DSetFriendRequest?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF=20(#729)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lagrange.Core/Common/Interface/Api/GroupExt.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lagrange.Core/Common/Interface/Api/GroupExt.cs b/Lagrange.Core/Common/Interface/Api/GroupExt.cs index 6e7270e9f..470bb4624 100644 --- a/Lagrange.Core/Common/Interface/Api/GroupExt.cs +++ b/Lagrange.Core/Common/Interface/Api/GroupExt.cs @@ -88,7 +88,7 @@ public static Task SetGroupRequest(this BotContext bot, BotGroupRequest re public static Task SetGroupFilteredRequest(this BotContext bot, BotGroupRequest request, bool accept = true, string reason = "") => bot.ContextCollection.Business.OperationLogic.SetGroupFilteredRequest(request.GroupUin, request.Sequence, (uint)request.EventType, accept, reason); - public static Task SetFriendRequest(this BotContext bot, FriendRequestEvent request, bool accept = true) + public static Task SetFriendRequest(this BotContext bot, BotFriendRequest request, bool accept = true) => bot.ContextCollection.Business.OperationLogic.SetFriendRequest(request.SourceUid, accept); public static Task GroupPoke(this BotContext bot, uint groupUin, uint friendUin) From fbf1a06344f09ff31850823c700efe0fa1287c3b Mon Sep 17 00:00:00 2001 From: dogdie233 Date: Wed, 8 Jan 2025 22:22:49 +0800 Subject: [PATCH 15/18] [Core] Add bounce face (Recvive not available) (#730) --- .../Logic/Implementation/MessagingLogic.cs | 18 +++ .../Implementation/Extra/QBounceFaceExtra.cs | 23 ++++ .../Message/Entity/BounceFaceEntity.cs | 112 ++++++++++++++++++ Lagrange.Core/Message/MessageBuilder.cs | 29 +++++ 4 files changed, 182 insertions(+) create mode 100644 Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs create mode 100644 Lagrange.Core/Message/Entity/BounceFaceEntity.cs diff --git a/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs b/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs index 71c7df0e7..b3e7679ae 100644 --- a/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs +++ b/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs @@ -425,6 +425,24 @@ private async Task ResolveOutgoingChain(MessageChain chain) face.SysFaceEntry ??= await cache.GetCachedFaceEntity(face.FaceId); break; } + case BounceFaceEntity bounceFace: + { + var cache = Collection.Business.CachingLogic; + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (bounceFace.Name != null) + break; + + string name = (await cache.GetCachedFaceEntity(bounceFace.FaceId))?.QDes ?? string.Empty; + + // Because the name is used as a preview text, it should not start with '/' + // But the QDes of the face may start with '/', so remove it + if (name.StartsWith('/')) + name = name[1..]; + + bounceFace.Name = name; + break; + } case ForwardEntity forward when forward.TargetUin != 0: { var cache = Collection.Business.CachingLogic; diff --git a/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs b/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs new file mode 100644 index 000000000..4c2523f6e --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS8618 + +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +[ProtoContract] +internal class QBounceFaceExtra +{ + [ProtoMember(1)] public int Field1 { get; set; } = 13; + + [ProtoMember(2)] public uint Count { get; set; } = 1; + + [ProtoMember(3)] public string Name { get; set; } = string.Empty; + + [ProtoMember(6)] public QSmallFaceExtra Face { get; set; } + + [ProtoContract] + public class FallbackPreviewTextPb + { + [ProtoMember(1)] public string Text { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/BounceFaceEntity.cs b/Lagrange.Core/Message/Entity/BounceFaceEntity.cs new file mode 100644 index 000000000..b8cd02f38 --- /dev/null +++ b/Lagrange.Core/Message/Entity/BounceFaceEntity.cs @@ -0,0 +1,112 @@ +using System.Text; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(CommonElem))] +public class BounceFaceEntity : IMessageEntity +{ + public uint FaceId { get; set; } + + public uint Count { get; set; } + + public string Name { get; set; } + + public bool ShouldAddPreviewText { get; set; } + + public BounceFaceEntity() : this(0, 0, string.Empty) {} + + public BounceFaceEntity(SysFaceEntry face, uint count, bool shouldAddPreviewText = true) + { + if (!face.AniStickerId.HasValue) + throw new ArgumentException("Face does not have a sticker ID", nameof(face)); + + FaceId = (uint)face.AniStickerId.Value; + Count = count; + Name = face.QDes ?? string.Empty; + ShouldAddPreviewText = shouldAddPreviewText; + + // Because the name is used as a preview text, it should not start with '/' + // But the QDes of the face may start with '/', so remove it + if (Name.StartsWith('/')) + Name = Name[1..]; + } + + public BounceFaceEntity(uint faceId, uint count, string? name = null, bool shouldAddPreviewText = true) + { + FaceId = faceId; + Count = count; + + // If the name is null, it will be assigned in MessagingLogic.ResolveOutgoingChain + Name = name!; + ShouldAddPreviewText = shouldAddPreviewText; + } + + IEnumerable IMessageEntity.PackElement() + { + byte[] pbElem; + using (var ms = new MemoryStream()) + { + Serializer.Serialize(ms, new QBounceFaceExtra + { + Field1 = 13, + Face = new QSmallFaceExtra + { + FaceId = FaceId, + Text = Name, + CompatText = Name + }, + Count = Count, + Name = Name + }); + pbElem = ms.ToArray(); + } + + var common = new CommonElem + { + ServiceType = 23, + PbElem = pbElem, + BusinessType = 13 + }; + + if (!ShouldAddPreviewText) + return new Elem[] { new() { CommonElem = common } }; + + byte[] textFallbackPb; + using (var ms = new MemoryStream()) + { + Serializer.Serialize(ms, new QBounceFaceExtra.FallbackPreviewTextPb { Text = $"[{Name}]请使用最新版手机QQ体验新功能。" }); + textFallbackPb = ms.ToArray(); + } + return new Elem[] + { + new() { CommonElem = common }, + new() + { + Text = new Text + { + Str = ToPreviewText(), + PbReserve = textFallbackPb + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elem) + { + if (elem.CommonElem is not { ServiceType: 23 } common) + return null; + + var extra = Serializer.Deserialize(common.PbElem.AsSpan()); + return new BounceFaceEntity(extra.Face.FaceId, extra.Count, extra.Name); + } + + public string ToPreviewString() => "$[BounceFace | Name: {Name}({FaceId}) | Count: {Count}]"; + + public string ToPreviewText() => $"[{Name}]x{Count}"; +} \ No newline at end of file diff --git a/Lagrange.Core/Message/MessageBuilder.cs b/Lagrange.Core/Message/MessageBuilder.cs index 3c83cb618..f1acefd29 100644 --- a/Lagrange.Core/Message/MessageBuilder.cs +++ b/Lagrange.Core/Message/MessageBuilder.cs @@ -298,6 +298,35 @@ public MessageBuilder SpecialPoke(SpecialPokeFaceType type, uint count = 1) return this; } + /// + /// Add a bounce face entity to message chain + /// + /// The face entry of the face to be added to the message chain + /// The number of face entities to generate + /// Should the entity add a preview text + public MessageBuilder BounceFace(SysFaceEntry face, uint count, bool shouldAddPreviewText = true) + { + var bounceFaceEntity = new BounceFaceEntity(face, count, shouldAddPreviewText); + _chain.Add(bounceFaceEntity); + + return this; + } + + /// + /// Add a bounce face entity to message chain + /// + /// The face ID of the face to be added to the message chain + /// The number of face entities to generate, default is 1 + /// The name of the face + /// Should the entity add a preview text + public MessageBuilder BounceFace(uint faceId, uint count, string? name = null, bool shouldAddPreviewText = true) + { + var bounceFaceEntity = new BounceFaceEntity(faceId, count, name, shouldAddPreviewText); + _chain.Add(bounceFaceEntity); + + return this; + } + /// /// Add a dedicated LightApp entity to message chain /// From e052ebf5e07dd3ad00e16f7d75f7a4b4523e5a21 Mon Sep 17 00:00:00 2001 From: Linwenxuan04 Date: Thu, 9 Jan 2025 06:52:32 -0500 Subject: [PATCH 16/18] [Core] Fixed the parse of the Group/Private ImageEntity --- .../Logic/Implementation/MessagingLogic.cs | 4 +-- .../Service/Oidb/Common/NTV2RichMediaReq.cs | 4 +-- Lagrange.Core/Message/Entity/ImageEntity.cs | 27 ++++++++----------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs b/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs index b3e7679ae..3777ef5eb 100644 --- a/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs +++ b/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs @@ -397,8 +397,8 @@ private async Task ResolveIncomingChain(MessageChain chain) case ImageEntity image when !image.ImageUrl.Contains("&rkey=") && image.MsgInfo is not null: { var @event = image.IsGroup - ? ImageGroupDownloadEvent.Create(image.GroupUin ?? 0, image.MsgInfo) - : ImageDownloadEvent.Create(image.FriendUid ?? string.Empty, image.MsgInfo); + ? ImageGroupDownloadEvent.Create(chain.GroupUin ?? 0, image.MsgInfo) + : ImageDownloadEvent.Create(chain.Uid ?? string.Empty, image.MsgInfo); var results = await Collection.Business.SendEvent(@event); if (results.Count != 0) diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs index 297d2b558..793d67b2a 100644 --- a/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs @@ -295,9 +295,9 @@ internal class PicExtBizInfo [ProtoMember(2)] public string TextSummary { get; set; } - [ProtoMember(11)] public byte[] BytesPbReserveC2c { get; set; } + [ProtoMember(11)] public byte[]? BytesPbReserveC2c { get; set; } - [ProtoMember(12)] public byte[] BytesPbReserveTroop { get; set; } + [ProtoMember(12)] public byte[]? BytesPbReserveTroop { get; set; } [ProtoMember(1001)] public uint FromScene { get; set; } diff --git a/Lagrange.Core/Message/Entity/ImageEntity.cs b/Lagrange.Core/Message/Entity/ImageEntity.cs index bc4924771..9fa67c92b 100644 --- a/Lagrange.Core/Message/Entity/ImageEntity.cs +++ b/Lagrange.Core/Message/Entity/ImageEntity.cs @@ -27,11 +27,7 @@ public class ImageEntity : IMessageEntity public string ImageUrl { get; set; } = string.Empty; internal Lazy? ImageStream { get; set; } - - internal string? Path { get; set; } - - internal uint FileId { get; set; } - + internal MsgInfo? MsgInfo { get; set; } internal NotOnlineImage? CompatImage { get; set; } @@ -42,11 +38,7 @@ public class ImageEntity : IMessageEntity public int SubType { get; set; } - internal bool IsGroup => GroupUin.HasValue; - - internal uint? GroupUin { get; set; } - - internal string? FriendUid { get; set; } + internal bool IsGroup { get; set; } public ImageEntity() { } @@ -110,8 +102,7 @@ IEnumerable IMessageEntity.PackElement() ImageSize = index.Info.FileSize, MsgInfo = extra, SubType = (int)extra.ExtBizInfo.Pic.BizType, - GroupUin = msgInfoBody.HashSum.TroopSource?.GroupUin, - FriendUid = msgInfoBody.HashSum.BytesPbReserveC2c?.FriendUid + IsGroup = extra.ExtBizInfo.Pic.BytesPbReserveTroop != null, }; } @@ -127,7 +118,8 @@ IEnumerable IMessageEntity.PackElement() ImageSize = image.FileLen, ImageUrl = $"{BaseUrl}{image.OrigUrl}", Summary = image.PbRes.Summary, - SubType = image.PbRes.SubType + SubType = image.PbRes.SubType, + IsGroup = false }; } @@ -139,7 +131,8 @@ IEnumerable IMessageEntity.PackElement() ImageSize = image.FileLen, ImageUrl = $"{LegacyBaseUrl}{image.OrigUrl}", Summary = image.PbRes.Summary, - SubType = image.PbRes.SubType + SubType = image.PbRes.SubType, + IsGroup = false }; } @@ -155,7 +148,8 @@ IEnumerable IMessageEntity.PackElement() ImageSize = face.Size, ImageUrl = $"{BaseUrl}{face.OrigUrl}", Summary = face.PbReserve?.Summary, - SubType = face.PbReserve?.SubType ?? GetImageTypeFromFaceOldData(face) + SubType = face.PbReserve?.SubType ?? GetImageTypeFromFaceOldData(face), + IsGroup = true }; } @@ -167,7 +161,8 @@ IEnumerable IMessageEntity.PackElement() ImageSize = face.Size, ImageUrl = $"{LegacyBaseUrl}{face.OrigUrl}", Summary = face.PbReserve?.Summary, - SubType = face.PbReserve?.SubType ?? GetImageTypeFromFaceOldData(face) + SubType = face.PbReserve?.SubType ?? GetImageTypeFromFaceOldData(face), + IsGroup = true }; } From 8e3ffc483a8ed1b4290b2004e61ab1f2f44d6368 Mon Sep 17 00:00:00 2001 From: Linwenxuan04 Date: Thu, 9 Jan 2025 06:57:12 -0500 Subject: [PATCH 17/18] [Core] Fixed Summary of NTV2Richmedia Picture --- Lagrange.Core/Message/Entity/ImageEntity.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Lagrange.Core/Message/Entity/ImageEntity.cs b/Lagrange.Core/Message/Entity/ImageEntity.cs index 9fa67c92b..7ca29200a 100644 --- a/Lagrange.Core/Message/Entity/ImageEntity.cs +++ b/Lagrange.Core/Message/Entity/ImageEntity.cs @@ -103,6 +103,7 @@ IEnumerable IMessageEntity.PackElement() MsgInfo = extra, SubType = (int)extra.ExtBizInfo.Pic.BizType, IsGroup = extra.ExtBizInfo.Pic.BytesPbReserveTroop != null, + Summary = string.IsNullOrEmpty(extra.ExtBizInfo.Pic.TextSummary) ? "[图片]" : extra.ExtBizInfo.Pic.TextSummary, }; } From 20c2b1013b773f859391877555c3127b89d9d91b Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Thu, 9 Jan 2025 21:45:29 +0800 Subject: [PATCH 18/18] [CI] Fix wrong artifact upload path (#734) --- .github/workflows/Lagrange.OneBot-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Lagrange.OneBot-build.yml b/.github/workflows/Lagrange.OneBot-build.yml index b6c6907e7..8d73aa526 100644 --- a/.github/workflows/Lagrange.OneBot-build.yml +++ b/.github/workflows/Lagrange.OneBot-build.yml @@ -69,4 +69,4 @@ jobs: if: github.event_name != 'pull_request' with: name: Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_NoSelfContained - path: Lagrange.OneBot/bin/Release/net8.0/${{ matrix.runtimeIdentifier }}/publish + path: Lagrange.OneBot/bin/Release/net9.0/${{ matrix.runtimeIdentifier }}/publish