diff --git a/MainCore.Test/ArchitectureTest.cs b/MainCore.Test/ArchitectureTest.cs index ed663b325..a3124926c 100644 --- a/MainCore.Test/ArchitectureTest.cs +++ b/MainCore.Test/ArchitectureTest.cs @@ -1,4 +1,4 @@ -using ArchUnitNET.Domain; +using ArchUnitNET.Domain; using ArchUnitNET.Loader; using ArchUnitNET.xUnit; using MainCore.Constraints; @@ -106,4 +106,4 @@ public void RequestShouldHaveCorrectConstraint() villageOnlyRule.Check(Architecture); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Behaviors/ErrorLoggingBehaviorTest.cs b/MainCore.Test/Behaviors/ErrorLoggingBehaviorTest.cs index ebd8d0d47..d429b1a91 100644 --- a/MainCore.Test/Behaviors/ErrorLoggingBehaviorTest.cs +++ b/MainCore.Test/Behaviors/ErrorLoggingBehaviorTest.cs @@ -1,4 +1,4 @@ -using FluentResults; +using FluentResults; using Immediate.Handlers.Shared; using MainCore.Behaviors; using MainCore.Constraints; @@ -70,4 +70,4 @@ public async Task ErrorLoggingBehaviorShouldLogCorrectErrorMessage(Result result new object[] { Result.Fail(Stop.EnglishRequired("abcxyz")), "Cannot parse abcxyz. Is language English ?. Bot must stop" }, }; } -} \ No newline at end of file +} diff --git a/MainCore.Test/Commands/Update/UpdateAccountInfoCommandTest.cs b/MainCore.Test/Commands/Update/UpdateAccountInfoCommandTest.cs index 60a1886ae..8907cd555 100644 --- a/MainCore.Test/Commands/Update/UpdateAccountInfoCommandTest.cs +++ b/MainCore.Test/Commands/Update/UpdateAccountInfoCommandTest.cs @@ -1,4 +1,4 @@ -using HtmlAgilityPack; +using HtmlAgilityPack; using MainCore.Commands.Update; using MainCore.Entities; using MainCore.Enums; @@ -17,7 +17,7 @@ public async Task UpdateAccountInfoCommandShouldRunWithNewAccount() using var context = new FakeDbContextFactory().CreateDbContext(true); var html = new HtmlDocument(); html.Load(PlusAccount); - var browser = Substitute.For(); + var browser = Substitute.For(); browser.Html.Returns(html); var handleBehavior = new UpdateAccountInfoCommand.HandleBehavior(browser, context); @@ -50,7 +50,7 @@ public async Task UpdateAccountInfoCommandShouldRunWithExistingAccount() context.SaveChanges(); var html = new HtmlDocument(); html.Load(PlusAccount); - var browser = Substitute.For(); + var browser = Substitute.For(); browser.Html.Returns(html); var handleBehavior = new UpdateAccountInfoCommand.HandleBehavior(browser, context); var command = new UpdateAccountInfoCommand.Command(new AccountId(1)); @@ -66,4 +66,4 @@ public async Task UpdateAccountInfoCommandShouldRunWithExistingAccount() accountInfo.HasPlusAccount.ShouldBeTrue(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/FakeDbContextFactory.cs b/MainCore.Test/FakeDbContextFactory.cs index 1999c4e6a..d65d7407c 100644 --- a/MainCore.Test/FakeDbContextFactory.cs +++ b/MainCore.Test/FakeDbContextFactory.cs @@ -1,4 +1,4 @@ -using MainCore.Entities; +using MainCore.Entities; using MainCore.Infrasturecture.Persistence; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; @@ -47,4 +47,4 @@ public void SetupDatabase(AppDbContext context) context.SaveChanges(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/HostingTest.cs b/MainCore.Test/HostingTest.cs index 4ebb5bc88..6df7e26c3 100644 --- a/MainCore.Test/HostingTest.cs +++ b/MainCore.Test/HostingTest.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting; namespace MainCore.Test { @@ -19,4 +19,4 @@ public void HostShouldNotBeNull() host.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/AdventureParser.Test.cs b/MainCore.Test/Parsers/AdventureParser.Test.cs index 13204939a..74e58d6a3 100644 --- a/MainCore.Test/Parsers/AdventureParser.Test.cs +++ b/MainCore.Test/Parsers/AdventureParser.Test.cs @@ -70,4 +70,4 @@ public void GetAdventureInfo() actual.ShouldNotContain("[~|~]"); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/BaseParser.Test.cs b/MainCore.Test/Parsers/BaseParser.Test.cs index d6c1796d9..303871658 100644 --- a/MainCore.Test/Parsers/BaseParser.Test.cs +++ b/MainCore.Test/Parsers/BaseParser.Test.cs @@ -1,4 +1,4 @@ -using HtmlAgilityPack; +using HtmlAgilityPack; namespace MainCore.Test.Parsers { @@ -11,4 +11,4 @@ protected BaseParser() _html = new HtmlDocument(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/BuildingLayoutParser.Test.cs b/MainCore.Test/Parsers/BuildingLayoutParser.Test.cs index 09595bd7f..89be64695 100644 --- a/MainCore.Test/Parsers/BuildingLayoutParser.Test.cs +++ b/MainCore.Test/Parsers/BuildingLayoutParser.Test.cs @@ -1,4 +1,4 @@ -using MainCore.Enums; +using MainCore.Enums; using Shouldly; namespace MainCore.Test.Parsers @@ -48,4 +48,4 @@ public void GetQueueBuilding(string path, int expected) actual.Count(x => x.Level != -1).ShouldBe(expected); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/BuildingTabParser.Test.cs b/MainCore.Test/Parsers/BuildingTabParser.Test.cs index 84ded1dbf..76990ae76 100644 --- a/MainCore.Test/Parsers/BuildingTabParser.Test.cs +++ b/MainCore.Test/Parsers/BuildingTabParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class BuildingTabParser : BaseParser { @@ -44,4 +44,4 @@ public void IsTabActive(string path, int expected) actual.ShouldBeTrue(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/CompleteImmediatelyParser.Test.cs b/MainCore.Test/Parsers/CompleteImmediatelyParser.Test.cs index 02c36a37c..45a8060c6 100644 --- a/MainCore.Test/Parsers/CompleteImmediatelyParser.Test.cs +++ b/MainCore.Test/Parsers/CompleteImmediatelyParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class CompleteImmediatelyParser : BaseParser { @@ -36,4 +36,4 @@ public void GetConfirmButton() actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/FarmListparser.Test.cs b/MainCore.Test/Parsers/FarmListparser.Test.cs index 8a24e756c..273a57632 100644 --- a/MainCore.Test/Parsers/FarmListparser.Test.cs +++ b/MainCore.Test/Parsers/FarmListparser.Test.cs @@ -1,4 +1,4 @@ -using MainCore.Entities; +using MainCore.Entities; namespace MainCore.Test.Parsers { @@ -51,4 +51,4 @@ public void GetStartAllButton() actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/InfoParser.Test.cs b/MainCore.Test/Parsers/InfoParser.Test.cs index e2e53d112..2ffa91685 100644 --- a/MainCore.Test/Parsers/InfoParser.Test.cs +++ b/MainCore.Test/Parsers/InfoParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class InfoParser : BaseParser { @@ -31,4 +31,4 @@ public void HasPlusAccount(string file, bool expected) actual.ShouldBe(expected); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/InventoryParser.Test.cs b/MainCore.Test/Parsers/InventoryParser.Test.cs index c5d715e40..ca9ed9250 100644 --- a/MainCore.Test/Parsers/InventoryParser.Test.cs +++ b/MainCore.Test/Parsers/InventoryParser.Test.cs @@ -1,4 +1,4 @@ -using MainCore.Enums; +using MainCore.Enums; namespace MainCore.Test.Parsers { @@ -64,4 +64,4 @@ public void GetConfirmButton() actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/LoginParser.Test.cs b/MainCore.Test/Parsers/LoginParser.Test.cs index 1b72fad83..92c2676e1 100644 --- a/MainCore.Test/Parsers/LoginParser.Test.cs +++ b/MainCore.Test/Parsers/LoginParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class LoginParser : BaseParser { @@ -49,4 +49,4 @@ public void GetPasswordInput() actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/NavigationBarParser.Test.cs b/MainCore.Test/Parsers/NavigationBarParser.Test.cs index 6bf35f181..3cd5c4013 100644 --- a/MainCore.Test/Parsers/NavigationBarParser.Test.cs +++ b/MainCore.Test/Parsers/NavigationBarParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class NavigationBarParser : BaseParser { @@ -14,4 +14,4 @@ public void GetDorfButton(int input) actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/NpcResourceParser.Test.cs b/MainCore.Test/Parsers/NpcResourceParser.Test.cs index 275dc84fa..9356aa21a 100644 --- a/MainCore.Test/Parsers/NpcResourceParser.Test.cs +++ b/MainCore.Test/Parsers/NpcResourceParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class NpcResourceParser : BaseParser { @@ -49,4 +49,4 @@ public void GetInputs() result.Count().ShouldBe(4); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/OptionParser.Test.cs b/MainCore.Test/Parsers/OptionParser.Test.cs index f8eb2c281..a07a49952 100644 --- a/MainCore.Test/Parsers/OptionParser.Test.cs +++ b/MainCore.Test/Parsers/OptionParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class OptionParser : BaseParser { @@ -40,4 +40,4 @@ public void GetSubmitButton() result.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/QuestParser.Test.cs b/MainCore.Test/Parsers/QuestParser.Test.cs index 4a7d45b16..2460069bb 100644 --- a/MainCore.Test/Parsers/QuestParser.Test.cs +++ b/MainCore.Test/Parsers/QuestParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class QuestParser : BaseParser { @@ -42,4 +42,4 @@ public void IsQuestPage() result.ShouldBeTrue(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/StorageParser.Test.cs b/MainCore.Test/Parsers/StorageParser.Test.cs index 4d525bd0e..6da716ed6 100644 --- a/MainCore.Test/Parsers/StorageParser.Test.cs +++ b/MainCore.Test/Parsers/StorageParser.Test.cs @@ -1,4 +1,4 @@ -namespace MainCore.Test.Parsers +namespace MainCore.Test.Parsers { public class StorageParser : BaseParser { @@ -60,4 +60,4 @@ public void GetGranaryCapacity() result.ShouldBeGreaterThan(-1); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/TrainTroopParser.Test.cs b/MainCore.Test/Parsers/TrainTroopParser.Test.cs index 9a83882ad..0e905ba1e 100644 --- a/MainCore.Test/Parsers/TrainTroopParser.Test.cs +++ b/MainCore.Test/Parsers/TrainTroopParser.Test.cs @@ -1,4 +1,4 @@ -using MainCore.Enums; +using MainCore.Enums; namespace MainCore.Test.Parsers { @@ -27,6 +27,26 @@ public void GetInputBox(string file, TroopEnums troop) result.ShouldNotBeNull(); } + [Theory] + [InlineData(BarrackPage, TroopEnums.Phalanx)] + [InlineData(StablePage, TroopEnums.Pathfinder)] + public void GetInputBoxAlternative(string file, TroopEnums troop) + { + _html.Load(file); + var result = MainCore.Parsers.TrainTroopParser.GetInputBoxAlternative(_html, troop); + result.ShouldNotBeNull(); + } + + [Theory] + [InlineData(BarrackPage, TroopEnums.Phalanx)] + [InlineData(StablePage, TroopEnums.Pathfinder)] + public void GetInputBoxNewStructure(string file, TroopEnums troop) + { + _html.Load(file); + var result = MainCore.Parsers.TrainTroopParser.GetInputBoxNewStructure(_html, troop); + result.ShouldNotBeNull(); + } + [Theory] [InlineData(BarrackPage, TroopEnums.Phalanx)] [InlineData(StablePage, TroopEnums.Pathfinder)] @@ -37,4 +57,4 @@ public void GetTrainTime(string file, TroopEnums troop) result.ShouldBeGreaterThan(-1); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/UpgradeParser.Test.cs b/MainCore.Test/Parsers/UpgradeParser.Test.cs index a11c84139..9252a8964 100644 --- a/MainCore.Test/Parsers/UpgradeParser.Test.cs +++ b/MainCore.Test/Parsers/UpgradeParser.Test.cs @@ -1,4 +1,4 @@ -using MainCore.Enums; +using MainCore.Enums; namespace MainCore.Test.Parsers { @@ -61,4 +61,4 @@ public void GetSpecialUpgradeButton(string path) actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/Parsers/VillagePanelParser.Test.cs b/MainCore.Test/Parsers/VillagePanelParser.Test.cs index dc95fe3b7..fe48b2f73 100644 --- a/MainCore.Test/Parsers/VillagePanelParser.Test.cs +++ b/MainCore.Test/Parsers/VillagePanelParser.Test.cs @@ -1,4 +1,4 @@ -using MainCore.Entities; +using MainCore.Entities; namespace MainCore.Test.Parsers { @@ -23,4 +23,4 @@ public void GetVillageNode() actual.ShouldNotBeNull(); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/UI/Models/Input/AccountInputTest.cs b/MainCore.Test/UI/Models/Input/AccountInputTest.cs index 8b642cd7d..bc658e0d2 100644 --- a/MainCore.Test/UI/Models/Input/AccountInputTest.cs +++ b/MainCore.Test/UI/Models/Input/AccountInputTest.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.Test.UI.Models.Input { @@ -42,4 +42,4 @@ public void ToDto_ServerUrlDontValid_ReturnEmpty() dto.Server.ShouldBe(""); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/UI/Models/Input/AccountSettingInputTest.cs b/MainCore.Test/UI/Models/Input/AccountSettingInputTest.cs index ede3e3dcf..3592d3139 100644 --- a/MainCore.Test/UI/Models/Input/AccountSettingInputTest.cs +++ b/MainCore.Test/UI/Models/Input/AccountSettingInputTest.cs @@ -19,4 +19,4 @@ public void Get_ReturnsDictionaryWithCorrectCount() Assert.Equal(enumCount, result.Count); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/UI/Models/Input/VillageSettingInputTest.cs b/MainCore.Test/UI/Models/Input/VillageSettingInputTest.cs index 48a7c43e5..0a0e3b7e4 100644 --- a/MainCore.Test/UI/Models/Input/VillageSettingInputTest.cs +++ b/MainCore.Test/UI/Models/Input/VillageSettingInputTest.cs @@ -1,4 +1,4 @@ -using MainCore.Enums; +using MainCore.Enums; using MainCore.UI.Models.Input; namespace MainCore.Test.UI.Models.Input @@ -19,4 +19,4 @@ public void Get_ReturnsDictionaryWithCorrectCount() Assert.Equal(enumCount, result.Count); } } -} \ No newline at end of file +} diff --git a/MainCore.Test/packages.lock.json b/MainCore.Test/packages.lock.json index 665329360..8ca1b137d 100644 --- a/MainCore.Test/packages.lock.json +++ b/MainCore.Test/packages.lock.json @@ -797,16 +797,16 @@ }, "Selenium.Support": { "type": "Transitive", - "resolved": "4.35.0", - "contentHash": "K6DiLdDQNDSWI/zh14bk9vRjW8vEX2mhMI2Cq8bD72FM5rOnLhQwZABJHrWCZ3U1wb7KFFd7iQeUhvpyHVt2AA==", + "resolved": "4.34.0", + "contentHash": "+GROvBcro1ayU9PlGu4Q6xGyRk+wlbwGrmL3lZqm+s6T+NVs0Fmp2r8t6DvKMY/ZaDh9svkvxqN808+EnFPhDw==", "dependencies": { - "Selenium.WebDriver": "4.35.0" + "Selenium.WebDriver": "4.34.0" } }, "Selenium.WebDriver": { "type": "Transitive", - "resolved": "4.35.0", - "contentHash": "C9OrObS2KLyUQDJNRY883JAc4n5q58pBYmY954hf+lGMQQcsz4Ias2mk91lHkLXFX+2qMttya4df9lGP1SLiGQ==" + "resolved": "4.34.0", + "contentHash": "uNx+GF7WugHDPV2zpGDPlbSn3STQ6n0xFskFSeJdhEuuUTkIDfAsYnjVUiQidWDpy7mKzTz53ae7Thjghl3dng==" }, "Serilog": { "type": "Transitive", @@ -1487,8 +1487,8 @@ "Polly": "[8.6.2, )", "ReactiveUI": "[20.4.1, )", "Riok.Mapperly": "[4.2.1, )", - "Selenium.Support": "[4.35.0, )", - "Selenium.WebDriver": "[4.35.0, )", + "Selenium.Support": "[4.34.0, )", + "Selenium.WebDriver": "[4.34.0, )", "Serilog": "[4.3.0, )", "Serilog.Expressions": "[5.0.0, )", "Serilog.Extensions.Hosting": "[9.0.0, )", @@ -1704,11 +1704,6 @@ "System.Private.Uri": "4.3.0" } }, - "Selenium.WebDriver": { - "type": "Transitive", - "resolved": "4.35.0", - "contentHash": "C9OrObS2KLyUQDJNRY883JAc4n5q58pBYmY954hf+lGMQQcsz4Ias2mk91lHkLXFX+2qMttya4df9lGP1SLiGQ==" - }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", "resolved": "2.1.10", diff --git a/MainCore/AppMixins.cs b/MainCore/AppMixins.cs index 4157d82fd..d3a9c92b5 100644 --- a/MainCore/AppMixins.cs +++ b/MainCore/AppMixins.cs @@ -1,4 +1,4 @@ -using MainCore.Behaviors; +using MainCore.Behaviors; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -55,15 +55,15 @@ public static IHostBuilder ConfigureServices(this IHostBuilder hostBuilder) => services.AddMainCoreBehaviors(); services.AddMainCoreHandlers(); - services.AddScoped(sp => + services.AddScoped(sp => { var dataService = sp.GetRequiredService(); if (dataService.AccountId == AccountId.Empty) throw new InvalidOperationException("AccountId is empty"); - var chromeManager = sp.GetRequiredService(); + var browserManager = sp.GetRequiredService(); var logger = Log .ForContext("Account", dataService.AccountData) .ForContext("AccountId", dataService.AccountId); - var browser = chromeManager.Get(dataService.AccountId); + var browser = browserManager.Get(dataService.AccountId); browser.Logger = logger; return browser; }); @@ -96,4 +96,4 @@ public static IHostBuilder GetHostBuilder() return hostBuilder; } } -} \ No newline at end of file +} diff --git a/MainCore/Behaviors/AccountDataLoggingBehavior.cs b/MainCore/Behaviors/AccountDataLoggingBehavior.cs index 9c23a4ebe..fcb0e06de 100644 --- a/MainCore/Behaviors/AccountDataLoggingBehavior.cs +++ b/MainCore/Behaviors/AccountDataLoggingBehavior.cs @@ -1,4 +1,4 @@ -using Serilog.Context; +using Serilog.Context; namespace MainCore.Behaviors { @@ -28,4 +28,4 @@ public override async ValueTask HandleAsync(TRequest request, Cancell } } } -} \ No newline at end of file +} diff --git a/MainCore/Behaviors/AccountTaskBehavior.cs b/MainCore/Behaviors/AccountTaskBehavior.cs index 984ee7c0f..5ea13c028 100644 --- a/MainCore/Behaviors/AccountTaskBehavior.cs +++ b/MainCore/Behaviors/AccountTaskBehavior.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.Behaviors { @@ -8,13 +8,13 @@ public sealed class AccountTaskBehavior where TResponse : Result { private readonly ITaskManager _taskManager; - private readonly IChromeBrowser _browser; + private readonly IBrowser _browser; private readonly UpdateAccountInfoCommand.Handler _updateAccountInfoCommand; private readonly UpdateVillageListCommand.Handler _updateVillageListCommand; private readonly UpdateAdventureCommand.Handler _updateAdventureCommand; - public AccountTaskBehavior(IChromeBrowser browser, ITaskManager taskManager, UpdateAccountInfoCommand.Handler updateAccountInfoCommand, UpdateVillageListCommand.Handler updateVillageListCommand, UpdateAdventureCommand.Handler updateAdventureCommand) + public AccountTaskBehavior(IBrowser browser, ITaskManager taskManager, UpdateAccountInfoCommand.Handler updateAccountInfoCommand, UpdateVillageListCommand.Handler updateVillageListCommand, UpdateAdventureCommand.Handler updateAdventureCommand) { _browser = browser; _taskManager = taskManager; @@ -30,7 +30,22 @@ public override async ValueTask HandleAsync(TRequest request, Cancell { if (!LoginParser.IsLoginPage(_browser.Html)) { - return (TResponse)Stop.NotTravianPage; + // Sayfa kaybedildiÄŸinde sayfayı yenile ve tekrar dene + try + { + await _browser.Refresh(cancellationToken); + await Task.Delay(2000, cancellationToken); // Sayfa yüklenmesi için bekle + + // Yeniden kontrol et + if (!LoginParser.IsIngamePage(_browser.Html) && !LoginParser.IsLoginPage(_browser.Html)) + { + return (TResponse)Stop.NotTravianPage; + } + } + catch (Exception) + { + return (TResponse)Stop.NotTravianPage; + } } if (request is not LoginTask.Task) @@ -59,4 +74,4 @@ public override async ValueTask HandleAsync(TRequest request, Cancell return response; } } -} \ No newline at end of file +} diff --git a/MainCore/Behaviors/CommandLoggingBehavior.cs b/MainCore/Behaviors/CommandLoggingBehavior.cs index 3d1b3bcfc..886bea366 100644 --- a/MainCore/Behaviors/CommandLoggingBehavior.cs +++ b/MainCore/Behaviors/CommandLoggingBehavior.cs @@ -1,4 +1,4 @@ -namespace MainCore.Behaviors +namespace MainCore.Behaviors { public sealed class CommandLoggingBehavior : Behavior @@ -50,4 +50,4 @@ public override async ValueTask HandleAsync(TRequest request, Cancell return response; } } -} \ No newline at end of file +} diff --git a/MainCore/Behaviors/ErrorLoggingBehavior.cs b/MainCore/Behaviors/ErrorLoggingBehavior.cs index fed016d92..df3525c20 100644 --- a/MainCore/Behaviors/ErrorLoggingBehavior.cs +++ b/MainCore/Behaviors/ErrorLoggingBehavior.cs @@ -1,4 +1,4 @@ -namespace MainCore.Behaviors +namespace MainCore.Behaviors { public sealed class ErrorLoggingBehavior : Behavior @@ -28,4 +28,4 @@ public override async ValueTask HandleAsync(TRequest request, Cancell return response; } } -} \ No newline at end of file +} diff --git a/MainCore/Behaviors/TaskNameLoggingBehavior.cs b/MainCore/Behaviors/TaskNameLoggingBehavior.cs index cf0338820..ad81b364c 100644 --- a/MainCore/Behaviors/TaskNameLoggingBehavior.cs +++ b/MainCore/Behaviors/TaskNameLoggingBehavior.cs @@ -1,4 +1,4 @@ -namespace MainCore.Behaviors +namespace MainCore.Behaviors { public sealed class TaskNameLoggingBehavior : Behavior @@ -21,4 +21,4 @@ public override async ValueTask HandleAsync(TRequest request, Cancell return response; } } -} \ No newline at end of file +} diff --git a/MainCore/Behaviors/VillageTaskBehavior.cs b/MainCore/Behaviors/VillageTaskBehavior.cs index 6a81326c3..1bba7ba77 100644 --- a/MainCore/Behaviors/VillageTaskBehavior.cs +++ b/MainCore/Behaviors/VillageTaskBehavior.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.Behaviors { @@ -52,4 +52,4 @@ public override async ValueTask HandleAsync(TRequest request, Cancell return response; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/ClaimQuest/ClaimQuestCommand.cs b/MainCore/Commands/Features/ClaimQuest/ClaimQuestCommand.cs index f00464706..2cb0f7fd4 100644 --- a/MainCore/Commands/Features/ClaimQuest/ClaimQuestCommand.cs +++ b/MainCore/Commands/Features/ClaimQuest/ClaimQuestCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.ClaimQuest { @@ -9,7 +9,7 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, IDelayService delayService, SwitchTabCommand.Handler switchTabCommand, CancellationToken cancellationToken) @@ -34,12 +34,12 @@ private static async ValueTask HandleAsync( quest = QuestParser.GetQuestCollectButton(browser.Html); if (quest is null) return Result.Ok(); - result = await browser.Click(By.XPath(quest.XPath), cancellationToken); + result = await browser.Click(By.XPath(quest.XPath)); if (result.IsFailed) return result; continue; } - result = await browser.Click(By.XPath(quest.XPath), cancellationToken); + result = await browser.Click(By.XPath(quest.XPath)); if (result.IsFailed) return result; await delayService.DelayClick(cancellationToken); } @@ -48,4 +48,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/ClaimQuest/ToQuestPageCommand.cs b/MainCore/Commands/Features/ClaimQuest/ToQuestPageCommand.cs index fb5c09b36..f9b31febe 100644 --- a/MainCore/Commands/Features/ClaimQuest/ToQuestPageCommand.cs +++ b/MainCore/Commands/Features/ClaimQuest/ToQuestPageCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.ClaimQuest { @@ -9,7 +9,7 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken) { var adventure = QuestParser.GetQuestMaster(browser.Html); @@ -22,7 +22,7 @@ static bool TableShow(IWebDriver driver) return QuestParser.IsQuestPage(doc); } - var result = await browser.Click(By.XPath(adventure.XPath), cancellationToken); + var result = await browser.Click(By.XPath(adventure.XPath)); if (result.IsFailed) return result; result = await browser.WaitPageChanged("tasks", TableShow, cancellationToken); @@ -31,4 +31,4 @@ static bool TableShow(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/CompleteImmediately/CompleteImmediatelyCommand.cs b/MainCore/Commands/Features/CompleteImmediately/CompleteImmediatelyCommand.cs index 2d2a3b360..bf135bfb8 100644 --- a/MainCore/Commands/Features/CompleteImmediately/CompleteImmediatelyCommand.cs +++ b/MainCore/Commands/Features/CompleteImmediately/CompleteImmediatelyCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.CompleteImmediately { @@ -9,7 +9,7 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken) { var oldQueueCount = CompleteImmediatelyParser.CountQueueBuilding(browser.Html); @@ -19,7 +19,7 @@ private static async ValueTask HandleAsync( var completeNowButton = CompleteImmediatelyParser.GetCompleteButton(browser.Html); if (completeNowButton is null) return Retry.ButtonNotFound("complete now"); - var result = await browser.Click(By.XPath(completeNowButton.XPath), cancellationToken); + var result = await browser.Click(By.XPath(completeNowButton.XPath)); if (result.IsFailed) return result; static bool ConfirmShown(IWebDriver driver) @@ -35,7 +35,7 @@ static bool ConfirmShown(IWebDriver driver) var confirmButton = CompleteImmediatelyParser.GetConfirmButton(browser.Html); if (confirmButton is null) return Retry.ButtonNotFound("confirm complete now"); - result = await browser.Click(By.XPath(confirmButton.XPath), cancellationToken); + result = await browser.Click(By.XPath(confirmButton.XPath)); if (result.IsFailed) return result; static bool QueueDifferent(IWebDriver driver, int oldQueueCount) @@ -52,4 +52,4 @@ static bool QueueDifferent(IWebDriver driver, int oldQueueCount) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/DisableContextualHelp/DisableContextualHelpCommand.cs b/MainCore/Commands/Features/DisableContextualHelp/DisableContextualHelpCommand.cs index e3bb16e85..fb0becfb2 100644 --- a/MainCore/Commands/Features/DisableContextualHelp/DisableContextualHelpCommand.cs +++ b/MainCore/Commands/Features/DisableContextualHelp/DisableContextualHelpCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.DisableContextualHelp { @@ -9,23 +9,22 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, - CancellationToken cancellationToken + IBrowser browser ) { var option = OptionParser.GetHideContextualHelpOption(browser.Html); if (option is null) return Retry.NotFound("hide contextual help", "option"); - var result = await browser.Click(By.XPath(option.XPath), cancellationToken); + var result = await browser.Click(By.XPath(option.XPath)); if (result.IsFailed) return result; var button = OptionParser.GetSubmitButton(browser.Html); if (button is null) return Retry.ButtonNotFound("submit"); - result = await browser.Click(By.XPath(button.XPath), cancellationToken); + result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/DisableContextualHelp/ToOptionsPageCommand.cs b/MainCore/Commands/Features/DisableContextualHelp/ToOptionsPageCommand.cs index 60513b8cf..baa0c5393 100644 --- a/MainCore/Commands/Features/DisableContextualHelp/ToOptionsPageCommand.cs +++ b/MainCore/Commands/Features/DisableContextualHelp/ToOptionsPageCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.DisableContextualHelp { @@ -9,17 +9,16 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, - CancellationToken cancellationToken + IBrowser browser ) { var button = OptionParser.GetOptionButton(browser.Html); if (button is null) return Retry.ButtonNotFound("options"); - var result = await browser.Click(By.XPath(button.XPath), cancellationToken); + var result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/LoginCommand.cs b/MainCore/Commands/Features/LoginCommand.cs index 3043da000..a47a845c5 100644 --- a/MainCore/Commands/Features/LoginCommand.cs +++ b/MainCore/Commands/Features/LoginCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features +namespace MainCore.Commands.Features { [Handler] public static partial class LoginCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, CancellationToken cancellationToken) { @@ -23,11 +23,11 @@ private static async ValueTask HandleAsync( var (username, password) = GetLoginInfo(command.AccountId, context); Result result; - result = await browser.Input(By.XPath(usernameNode.XPath), username, cancellationToken); + result = await browser.Input(By.XPath(usernameNode.XPath), username); if (result.IsFailed) return result; - result = await browser.Input(By.XPath(passwordNode.XPath), password, cancellationToken); + result = await browser.Input(By.XPath(passwordNode.XPath), password); if (result.IsFailed) return result; - result = await browser.Click(By.XPath(buttonNode.XPath), cancellationToken); + result = await browser.Click(By.XPath(buttonNode.XPath)); if (result.IsFailed) return result; result = await browser.WaitPageChanged("dorf", cancellationToken); if (result.IsFailed) return result; @@ -48,4 +48,4 @@ private static (string username, string password) GetLoginInfo(AccountId account return (data.Username, data.Password); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/NpcResource/NpcResourceCommand.cs b/MainCore/Commands/Features/NpcResource/NpcResourceCommand.cs index 9c76a8e6a..11c610d18 100644 --- a/MainCore/Commands/Features/NpcResource/NpcResourceCommand.cs +++ b/MainCore/Commands/Features/NpcResource/NpcResourceCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.NpcResource +namespace MainCore.Commands.Features.NpcResource { [Handler] public static partial class NpcResourceCommand @@ -15,7 +15,7 @@ public sealed record Command(VillageId VillageId) : IVillageCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, CancellationToken cancellationToken) { @@ -50,7 +50,7 @@ private static async ValueTask HandleAsync( } } - result = await InputAmount(browser, values, cancellationToken); + result = await InputAmount(browser, values); if (result.IsFailed) return result; browser.Logger.Information("Current resource:"); @@ -83,7 +83,7 @@ private static async ValueTask HandleAsync( return Result.Ok(); } - private static void LogResource(IChromeBrowser browser) + private static void LogResource(IBrowser browser) { var wood = StorageParser.GetWood(browser.Html); var clay = StorageParser.GetClay(browser.Html); @@ -96,7 +96,7 @@ private static void LogResource(IChromeBrowser browser) browser.Logger.Information("[{Warehouse}]: {Wood} - {Clay} - {Iron} | [{Granary}]: {Crop}", warehouse, wood, clay, iron, granary, crop); } - private static bool CanStart(IChromeBrowser browser, AppDbContext context, VillageId villageId) + private static bool CanStart(IBrowser browser, AppDbContext context, VillageId villageId) { var crop = StorageParser.GetCrop(browser.Html); var granary = StorageParser.GetGranaryCapacity(browser.Html); @@ -114,7 +114,7 @@ private static bool CanStart(IChromeBrowser browser, AppDbContext context, Villa return true; } - private static async Task OpenNPCDialog(IChromeBrowser browser, CancellationToken cancellationToken) + private static async Task OpenNPCDialog(IBrowser browser, CancellationToken cancellationToken) { var button = NpcResourceParser.GetExchangeResourcesButton(browser.Html); if (button is null) return Retry.ButtonNotFound("Exchange resources"); @@ -126,7 +126,7 @@ static bool DialogShown(IWebDriver driver) return NpcResourceParser.IsNpcDialog(doc); } - var result = await browser.Click(By.XPath(button.XPath), cancellationToken); + var result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; result = await browser.Wait(DialogShown, cancellationToken); @@ -135,20 +135,20 @@ static bool DialogShown(IWebDriver driver) return Result.Ok(); } - private static async Task InputAmount(IChromeBrowser browser, long[] values, CancellationToken cancellationToken) + private static async Task InputAmount(IBrowser browser, long[] values) { var inputs = NpcResourceParser.GetInputs(browser.Html).ToArray(); for (var i = 0; i < 4; i++) { - var result = await browser.Input(By.XPath(inputs[i].XPath), $"{values[i]}", cancellationToken); + var result = await browser.Input(By.XPath(inputs[i].XPath), $"{values[i]}"); if (result.IsFailed) return result; } return Result.Ok(); } - private static long[] GetValues(IChromeBrowser browser, long[] ratio) + private static long[] GetValues(IBrowser browser, long[] ratio) { var sum = NpcResourceParser.GetSum(browser.Html); var sumRatio = ratio.Sum(); @@ -181,7 +181,7 @@ private static long[] GetRatio(Dictionary settings) return ratio; } - private static async Task Distribute(IChromeBrowser browser, CancellationToken cancellationToken) + private static async Task Distribute(IBrowser browser, CancellationToken cancellationToken) { var result = await browser.Wait(driver => { @@ -198,13 +198,13 @@ private static async Task Distribute(IChromeBrowser browser, Cancellatio var button = NpcResourceParser.GetDistributeButton(browser.Html); if (button is null) return Retry.ButtonNotFound("distribute"); - result = await browser.Click(By.XPath(button.XPath), cancellationToken); + result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; return Result.Ok(); } - private static async Task Redeem(IChromeBrowser browser, CancellationToken cancellationToken) + private static async Task Redeem(IBrowser browser, CancellationToken cancellationToken) { var result = await browser.Wait(driver => { @@ -222,10 +222,10 @@ private static async Task Redeem(IChromeBrowser browser, CancellationTok var button = NpcResourceParser.GetRedeemButton(browser.Html); if (button is null) return Retry.ButtonNotFound("redeem"); - result = await browser.Click(By.XPath(button.XPath), cancellationToken); + result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/NpcResource/ToNpcResourcePageCommand.cs b/MainCore/Commands/Features/NpcResource/ToNpcResourcePageCommand.cs index af5810831..b76081eec 100644 --- a/MainCore/Commands/Features/NpcResource/ToNpcResourcePageCommand.cs +++ b/MainCore/Commands/Features/NpcResource/ToNpcResourcePageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.NpcResource +namespace MainCore.Commands.Features.NpcResource { [Handler] public static partial class ToNpcResourcePageCommand @@ -30,4 +30,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/SleepCommand.cs b/MainCore/Commands/Features/SleepCommand.cs index 6a38d80b6..c19a56973 100644 --- a/MainCore/Commands/Features/SleepCommand.cs +++ b/MainCore/Commands/Features/SleepCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features +namespace MainCore.Commands.Features { [Handler] public static partial class SleepCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, ISettingService settingService, ILogger logger, CancellationToken cancellationToken) @@ -35,4 +35,4 @@ private static async ValueTask HandleAsync( } } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/StartAdventure/ExploreAdventureCommand.cs b/MainCore/Commands/Features/StartAdventure/ExploreAdventureCommand.cs index ed5e7de5b..c4dfcf666 100644 --- a/MainCore/Commands/Features/StartAdventure/ExploreAdventureCommand.cs +++ b/MainCore/Commands/Features/StartAdventure/ExploreAdventureCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.StartAdventure { @@ -9,7 +9,7 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, ILogger logger, CancellationToken cancellationToken) { @@ -27,7 +27,7 @@ static bool ContinueShow(IWebDriver driver) return continueButton is not null; } - var result = await browser.Click(By.XPath(adventureButton.XPath), cancellationToken); + var result = await browser.Click(By.XPath(adventureButton.XPath)); if (result.IsFailed) return result; result = await browser.Wait(ContinueShow, cancellationToken); @@ -36,4 +36,4 @@ static bool ContinueShow(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/StartAdventure/ToAdventurePageCommand.cs b/MainCore/Commands/Features/StartAdventure/ToAdventurePageCommand.cs index 8e99b3399..53a7be313 100644 --- a/MainCore/Commands/Features/StartAdventure/ToAdventurePageCommand.cs +++ b/MainCore/Commands/Features/StartAdventure/ToAdventurePageCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.StartAdventure { @@ -9,7 +9,7 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken) { var adventure = AdventureParser.GetHeroAdventureButton(browser.Html); @@ -22,7 +22,7 @@ static bool TableShow(IWebDriver driver) return AdventureParser.IsAdventurePage(doc); } - var result = await browser.Click(By.XPath(adventure.XPath), cancellationToken); + var result = await browser.Click(By.XPath(adventure.XPath)); if (result.IsFailed) return result; result = await browser.WaitPageChanged("adventures", TableShow, cancellationToken); @@ -31,4 +31,4 @@ static bool TableShow(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/StartFarmList/GetHasRallypointVillageCommand.cs b/MainCore/Commands/Features/StartFarmList/GetHasRallypointVillageCommand.cs index 98555fa40..1b36d3b82 100644 --- a/MainCore/Commands/Features/StartFarmList/GetHasRallypointVillageCommand.cs +++ b/MainCore/Commands/Features/StartFarmList/GetHasRallypointVillageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.StartFarmList +namespace MainCore.Commands.Features.StartFarmList { [Handler] public static partial class GetHasRallypointVillageCommand @@ -22,4 +22,4 @@ private static async ValueTask HandleAsync( return hasRallypointVillageId; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/StartFarmList/StartActiveFarmListCommand.cs b/MainCore/Commands/Features/StartFarmList/StartActiveFarmListCommand.cs index df2b192cd..b38562e43 100644 --- a/MainCore/Commands/Features/StartFarmList/StartActiveFarmListCommand.cs +++ b/MainCore/Commands/Features/StartFarmList/StartActiveFarmListCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.StartFarmList +namespace MainCore.Commands.Features.StartFarmList { [Handler] public static partial class StartActiveFarmListCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, IDelayService delayService, AppDbContext context, CancellationToken cancellationToken) @@ -25,13 +25,17 @@ private static async ValueTask HandleAsync( var startButton = FarmListParser.GetStartButton(browser.Html, farmList); if (startButton is null) return Retry.ButtonNotFound($"Start farm {farmList}"); - var result = await browser.Click(By.XPath(startButton.XPath), cancellationToken); + var result = await browser.Click(By.XPath(startButton.XPath)); if (result.IsFailed) return result; await delayService.DelayClick(cancellationToken); } + // Tüm farm list attack'lar baþlatýldýktan sonra 7 saniye bekle + // Bu süre attack'larýn gönderilmesi ve sayfanýn stabilleþmesi için gerekli + await Task.Delay(7000, cancellationToken); + return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/StartFarmList/StartAllFarmListCommand.cs b/MainCore/Commands/Features/StartFarmList/StartAllFarmListCommand.cs index 71c7621c4..8635445a8 100644 --- a/MainCore/Commands/Features/StartFarmList/StartAllFarmListCommand.cs +++ b/MainCore/Commands/Features/StartFarmList/StartAllFarmListCommand.cs @@ -9,17 +9,21 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken ) { var startAllButton = FarmListParser.GetStartAllButton(browser.Html); if (startAllButton is null) return Retry.ButtonNotFound("Start all farms"); - var result = await browser.Click(By.XPath(startAllButton.XPath), cancellationToken); + var result = await browser.Click(By.XPath(startAllButton.XPath)); if (result.IsFailed) return result; + // Farm list attack'lar baÅŸlatıldıktan sonra 7 saniye bekle + // Bu süre tüm attack'ların gönderilmesi ve sayfanın stabilleÅŸmesi için gerekli + await Task.Delay(7000, cancellationToken); + return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/StartFarmList/ToFarmListPageCommand.cs b/MainCore/Commands/Features/StartFarmList/ToFarmListPageCommand.cs index 7ceaf8511..7b4160ac4 100644 --- a/MainCore/Commands/Features/StartFarmList/ToFarmListPageCommand.cs +++ b/MainCore/Commands/Features/StartFarmList/ToFarmListPageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.StartFarmList +namespace MainCore.Commands.Features.StartFarmList { [Handler] public static partial class ToFarmListPageCommand @@ -41,4 +41,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/TrainTroop/GetTrainTroopBuildingCommand.cs b/MainCore/Commands/Features/TrainTroop/GetTrainTroopBuildingCommand.cs index 4249e4637..1b206e5ff 100644 --- a/MainCore/Commands/Features/TrainTroop/GetTrainTroopBuildingCommand.cs +++ b/MainCore/Commands/Features/TrainTroop/GetTrainTroopBuildingCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.TrainTroop +namespace MainCore.Commands.Features.TrainTroop { [Handler] public static partial class GetTrainTroopBuildingCommand @@ -45,4 +45,4 @@ private static async ValueTask> HandleAsync( return buildings; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/TrainTroop/ToTrainTroopPageCommand.cs b/MainCore/Commands/Features/TrainTroop/ToTrainTroopPageCommand.cs index f77a3aef3..b4b0caf4d 100644 --- a/MainCore/Commands/Features/TrainTroop/ToTrainTroopPageCommand.cs +++ b/MainCore/Commands/Features/TrainTroop/ToTrainTroopPageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.TrainTroop +namespace MainCore.Commands.Features.TrainTroop { [Handler] public static partial class ToTrainTroopPageCommand @@ -26,4 +26,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/TrainTroop/TrainTroopCommand.cs b/MainCore/Commands/Features/TrainTroop/TrainTroopCommand.cs index 589154a58..b9aa58423 100644 --- a/MainCore/Commands/Features/TrainTroop/TrainTroopCommand.cs +++ b/MainCore/Commands/Features/TrainTroop/TrainTroopCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.TrainTroop { @@ -10,7 +10,7 @@ public sealed record Command(VillageId VillageId, BuildingEnums Building) : IVil private static async ValueTask HandleAsync( Command command, AppDbContext context, - IChromeBrowser browser, + IBrowser browser, ILogger logger, CancellationToken cancellationToken) { @@ -33,7 +33,7 @@ private static async ValueTask HandleAsync( } } - var result = await TrainTroop(browser, troop, amount, cancellationToken); + var result = await TrainTroop(browser, troop, amount, logger); if (result.IsFailed) return result; logger.Information("Troop training for {Troop} with amount {Amount} is done.", troop, amount); @@ -59,25 +59,160 @@ private static async ValueTask HandleAsync( }; private static async ValueTask TrainTroop( - IChromeBrowser browser, + IBrowser browser, TroopEnums troop, long amount, - CancellationToken cancellationToken) + ILogger logger) { var inputBox = TrainTroopParser.GetInputBox(browser.Html, troop); - if (inputBox is null) return Retry.TextboxNotFound("troop amount input"); + + // EÄŸer ana yöntem baÅŸarısız olursa, yeni yapıya özel metodu dene + if (inputBox is null) + { + logger.Warning("Primary input box method failed for troop {Troop}, trying new structure method.", troop); + inputBox = TrainTroopParser.GetInputBoxNewStructure(browser.Html, troop); + } + + // Hala bulunamazsa, alternatif yöntemi dene + if (inputBox is null) + { + logger.Warning("New structure method failed for troop {Troop}, trying alternative method.", troop); + inputBox = TrainTroopParser.GetInputBoxAlternative(browser.Html, troop); + } + + if (inputBox is null) + { + // Debug bilgisi ekle + logger.Warning("Input box not found for troop {Troop} with both methods. HTML structure may have changed.", troop); + return Retry.TextboxNotFound("troop amount input"); + } Result result; - result = await browser.Input(By.XPath(inputBox.XPath), $"{amount}", cancellationToken); - if (result.IsFailed) return result; + + // Önce XPath ile dene + result = await browser.Input(By.XPath(inputBox.XPath), $"{amount}"); + if (result.IsFailed) + { + logger.Warning("Failed to input amount {Amount} for troop {Troop} with XPath: {XPath}. Trying CSS selector.", amount, troop, inputBox.XPath); + + // XPath baÅŸarısız olursa, yeni yapıya göre CSS selector ile dene + var troopIndex = GetTroopIndex(troop); + if (troopIndex == -1) + { + logger.Warning("Unknown troop type: {Troop}", troop); + return result; + } + + // Önce yeni yapıya göre CSS selector dene + var newCssSelector = $"#nonFavouriteTroops div:nth-child({troopIndex}) input[type='text']"; + result = await browser.Input(By.CssSelector(newCssSelector), $"{amount}"); + + if (result.IsFailed) + { + // EÄŸer nonFavouriteTroops'ta bulunamazsa, favouriteTroops'ta dene + var favouriteCssSelector = $"#favouriteTroops div:nth-child({troopIndex}) input[type='text']"; + result = await browser.Input(By.CssSelector(favouriteCssSelector), $"{amount}"); + } + + if (result.IsFailed) + { + // Son çare: eski name attribute yöntemi + var nameAttribute = $"t{troopIndex}"; + var fallbackCssSelector = $"input[name='{nameAttribute}']"; + + result = await browser.Input(By.CssSelector(fallbackCssSelector), $"{amount}"); + if (result.IsFailed) + { + logger.Warning("Failed to input amount {Amount} for troop {Troop} with all CSS selectors", amount, troop); + return result; + } + } + } var trainButton = TrainTroopParser.GetTrainButton(browser.Html); - if (trainButton is null) return Retry.ButtonNotFound("train troop"); + if (trainButton is null) + { + logger.Warning("Train button not found. HTML structure may have changed."); + return Retry.ButtonNotFound("train troop"); + } - result = await browser.Click(By.XPath(trainButton.XPath), cancellationToken); - if (result.IsFailed) return result; + result = await browser.Click(By.XPath(trainButton.XPath)); + if (result.IsFailed) + { + logger.Warning("Failed to click train button. XPath: {XPath}", trainButton.XPath); + return result; + } return Result.Ok(); } + + // Troop enum deÄŸerini HTML name attribute'una çeviren yardımcı metod + private static int GetTroopIndex(TroopEnums troop) + { + return troop switch + { + // Romans + TroopEnums.Legionnaire => 1, + TroopEnums.Praetorian => 2, + TroopEnums.Imperian => 3, + TroopEnums.EquitesLegati => 4, + TroopEnums.EquitesImperatoris => 5, + TroopEnums.EquitesCaesaris => 6, + TroopEnums.RomanRam => 7, + TroopEnums.RomanCatapult => 8, + TroopEnums.RomanChief => 9, + TroopEnums.RomanSettler => 10, + + // Teutons + TroopEnums.Clubswinger => 1, + TroopEnums.Spearman => 2, + TroopEnums.Axeman => 3, + TroopEnums.Scout => 4, + TroopEnums.Paladin => 5, + TroopEnums.TeutonicKnight => 6, + TroopEnums.TeutonRam => 7, + TroopEnums.TeutonCatapult => 8, + TroopEnums.TeutonChief => 9, + TroopEnums.TeutonSettler => 10, + + // Gauls + TroopEnums.Phalanx => 1, + TroopEnums.Swordsman => 2, + TroopEnums.Pathfinder => 3, + TroopEnums.TheutatesThunder => 4, + TroopEnums.Druidrider => 5, + TroopEnums.Haeduan => 6, + TroopEnums.GaulRam => 7, + TroopEnums.GaulCatapult => 8, + TroopEnums.GaulChief => 9, + TroopEnums.GaulSettler => 10, + + // Egyptians + TroopEnums.SlaveMilitia => 1, + TroopEnums.AshWarden => 2, + TroopEnums.KhopeshWarrior => 3, + TroopEnums.SopduExplorer => 4, + TroopEnums.AnhurGuard => 5, + TroopEnums.ReshephChariot => 6, + TroopEnums.EgyptianRam => 7, + TroopEnums.EgyptianCatapult => 8, + TroopEnums.EgyptianChief => 9, + TroopEnums.EgyptianSettler => 10, + + // Huns + TroopEnums.Mercenary => 1, + TroopEnums.Bowman => 2, + TroopEnums.Spotter => 3, + TroopEnums.SteppeRider => 4, + TroopEnums.Marksman => 5, + TroopEnums.Marauder => 6, + TroopEnums.HunRam => 7, + TroopEnums.HunCatapult => 8, + TroopEnums.HunChief => 9, + TroopEnums.HunSettler => 10, + + _ => -1 + }; + } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UpgradeBuilding/AddCroplandCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/AddCroplandCommand.cs index 5ef2d678d..1f59c0810 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/AddCroplandCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/AddCroplandCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UpgradeBuilding +namespace MainCore.Commands.Features.UpgradeBuilding { [Handler] public static partial class AddCroplandCommand @@ -31,4 +31,4 @@ private static async ValueTask HandleAsync( rxQueue.Enqueue(new JobsModified(villageId)); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UpgradeBuilding/GetBuildPlanCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/GetBuildPlanCommand.cs index 49f59c0d3..a612baafe 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/GetBuildPlanCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/GetBuildPlanCommand.cs @@ -1,4 +1,6 @@ -using System.Text.Json; +using System.Text.Json; +using MainCore.Enums; +using MainCore.Errors; namespace MainCore.Commands.Features.UpgradeBuilding { @@ -16,6 +18,7 @@ private static async ValueTask> HandleAsync( DeleteJobByIdCommand.Handler deleteJobByIdCommand, AddJobCommand.Handler addJobCommand, ValidateJobCompleteCommand.Handler validateJobCompleteCommand, + AppDbContext context, ILogger logger, IRxQueue rxQueue, CancellationToken cancellationToken @@ -23,9 +26,26 @@ CancellationToken cancellationToken { var (accountId, villageId) = command; + // Infinite loop koruması - maksimum 10 deneme + var attemptCount = 0; + const int maxAttempts = 10; + while (true) { if (cancellationToken.IsCancellationRequested) return Cancel.Error; + + // Çok fazla deneme yapılmasını engelle + attemptCount++; + if (attemptCount > maxAttempts) + { + logger.Warning("GetBuildPlan reached maximum attempts ({MaxAttempts}) for village {VillageId}. This may indicate job validation issues.", maxAttempts, villageId); + return UpgradeBuildingError.BuildingJobQueueEmpty; + } + + if (attemptCount > 1) + { + logger.Information("GetBuildPlan attempt #{AttemptCount} for village {VillageId}", attemptCount, villageId); + } var result = await toDorfCommand.HandleAsync(new(2), cancellationToken); if (result.IsFailed) return result; @@ -34,23 +54,163 @@ CancellationToken cancellationToken if (result.IsFailed) return result; var (_, isFailed, job, errors) = await getJobQuery.HandleAsync(new(accountId, villageId), cancellationToken); - if (isFailed) return Result.Fail(errors); + if (isFailed) + { + // BuildingJobQueueEmpty ise direkt return et - yapacak iÅŸ yok + if (errors.Any(x => x is UpgradeBuildingError && x.Message.Contains("Building job queue is empty"))) + { + logger.Information("GetJob failed: Building job queue is empty"); + return UpgradeBuildingError.BuildingJobQueueEmpty; + } + + // NextExecuteError varsa task'ı schedule et (construction queue full durumları için) + var nextExecuteErrors = errors.OfType().ToList(); + if (nextExecuteErrors.Count > 0) + { + logger.Information("GetJob failed with NextExecuteError, scheduling task for: {NextExecute}", + nextExecuteErrors.First().NextExecute.ToString("HH:mm:ss")); + } + return Result.Fail(errors); + } if (job.Type == JobTypeEnums.ResourceBuild) { - logger.Information("{Content}", job); + logger.Information("Processing ResourceBuild job: {Content}", job.Content); var layoutBuildings = await getLayoutBuildingsQuery.HandleAsync(new(villageId, true)); var resourceBuildPlan = JsonSerializer.Deserialize(job.Content)!; - var normalBuildPlan = GetNormalBuildPlan(resourceBuildPlan, layoutBuildings); - if (normalBuildPlan is null) + + // Plus account ve Romans tribe için birden fazla job üret + var plusActive = context.AccountsInfo + .Where(x => x.AccountId == accountId.Value) + .Select(x => x.HasPlusAccount) + .FirstOrDefault(); + + var tribe = (TribeEnums)context.VillagesSetting + .Where(x => x.VillageId == villageId.Value) + .Where(x => x.Setting == VillageSettingEnums.Tribe) + .Select(x => x.Value) + .FirstOrDefault(); + + var isRomans = tribe == TribeEnums.Romans; + + // İnÅŸa kuyruÄŸu kapasitesini hesapla + var maxJobs = 1; // Base: 1 slot + if (plusActive) maxJobs = 2; // Plus: +1 slot + if (isRomans) maxJobs = 3; // Romans: +1 slot (Plus ile birlikte 3) + + // TOTAL construction queue kontrolü - resource + infrastructure birlikte + var currentTotalQueueCount = context.QueueBuildings + .Where(x => x.VillageId == villageId.Value) + .Count(); // TÜM buildings (resource + infrastructure) + + var remainingSlots = maxJobs - currentTotalQueueCount; + logger.Information("ResourceBuild analysis: Plus={Plus}, Romans={Romans}, MaxJobs={MaxJobs}, CurrentTotalQueue={CurrentQueue}, RemainingSlots={RemainingSlots}", + plusActive, isRomans, maxJobs, currentTotalQueueCount, remainingSlots); + + // EÄŸer kuyruk dolu ise task'ı schedule et + if (remainingSlots <= 0) { + var nextCompleteTime = context.QueueBuildings + .Where(x => x.VillageId == villageId.Value) + .OrderBy(x => x.CompleteTime) + .Select(x => x.CompleteTime) + .FirstOrDefault(); + + logger.Information("ResourceBuild queue full, scheduling task for: {NextExecute}", nextCompleteTime.ToString("HH:mm:ss")); + return NextExecuteError.ConstructionQueueFull(nextCompleteTime); + } + + // Resource field sayısını kontrol et - maksimum 2 resource aynı anda yapılabilir + var resourceTypes = new[] { BuildingEnums.Woodcutter, BuildingEnums.ClayPit, BuildingEnums.IronMine, BuildingEnums.Cropland }; + var currentResourceQueueCount = context.QueueBuildings + .Where(x => x.VillageId == villageId.Value) + .Count(x => resourceTypes.Contains(x.Type)); + + var maxResourceSlots = Math.Min(2, maxJobs); // Maksimum 2 resource field aynı anda + var remainingResourceSlots = maxResourceSlots - currentResourceQueueCount; + + logger.Information("Resource slot check: CurrentResourceQueue={Current}, MaxResourceSlots={Max}, RemainingResourceSlots={Remaining}", + currentResourceQueueCount, maxResourceSlots, remainingResourceSlots); + + var addedJobs = 0; + for (int i = 0; i < Math.Min(remainingSlots, remainingResourceSlots); i++) + { + var normalBuildPlan = GetNormalBuildPlan(resourceBuildPlan, layoutBuildings); + if (normalBuildPlan is null) + { + logger.Information("GetNormalBuildPlan returned null - no more upgradeable resources found"); + break; + } + + // Resource validation - Job eklemeden önce resource kontrolü yap + var requiredResource = normalBuildPlan.Type.GetCost(normalBuildPlan.Level); + var storage = context.Storages + .Where(x => x.VillageId == villageId.Value) + .FirstOrDefault(); + + if (storage != null && !HasEnoughResource(storage, requiredResource)) + { + logger.Information("Skipping ResourceBuild job #{JobNumber}: {Plan} - Insufficient resources: Wood={Wood}/{ReqWood}, Clay={Clay}/{ReqClay}, Iron={Iron}/{ReqIron}, Crop={Crop}/{ReqCrop}", + i + 1, $"{normalBuildPlan.Type} at location {normalBuildPlan.Location} to level {normalBuildPlan.Level}", + storage.Wood, requiredResource[0], storage.Clay, requiredResource[1], + storage.Iron, requiredResource[2], storage.Crop, requiredResource[3]); + + // Hero resource kullanma ayarını kontrol et + var useHeroResource = context.BooleanByName(villageId, VillageSettingEnums.UseHeroResourceForBuilding); + if (useHeroResource) + { + logger.Information("UseHeroResourceForBuilding is enabled - job will be added and resources will be used from hero inventory during construction"); + // Hero resource varsa job'u ekle, HandleResourceCommand'da hero resource kullanılacak + // Job eklemeye devam et + } + else + { + // Resource yetersizliÄŸi durumunda döngüden çık + // Timer parsing UpgradeBuildingTask'ta yapılacak + logger.Information("UseHeroResourceForBuilding is disabled - insufficient resources detected for {BuildingType} at location {Location} - will use Travian timer in UpgradeBuildingTask", + normalBuildPlan.Type, normalBuildPlan.Location); + break; + } + } + + logger.Information("Adding ResourceBuild job #{JobNumber}: {Plan}", i + 1, $"{normalBuildPlan.Type} at location {normalBuildPlan.Location} to level {normalBuildPlan.Level}"); + await addJobCommand.HandleAsync(new(villageId, normalBuildPlan.ToJob(), true)); + addedJobs++; + + // Bir sonraki job için building level'ı simüle et + var building = layoutBuildings.Find(x => x.Location == normalBuildPlan.Location); + if (building is not null) + { + building.JobLevel = Math.Max(building.JobLevel, normalBuildPlan.Level); + } + } + + // ResourceBuildPlan'ı sadece tüm resource'lar target level'a ulaÅŸtığında sil + // GetNormalBuildPlan null döndüyse ve hiç job eklenemiyorsa = tüm resources target level'da + var normalBuildPlanCheck = GetNormalBuildPlan(resourceBuildPlan, layoutBuildings); + if (normalBuildPlanCheck == null && addedJobs == 0) + { + logger.Information("All resources reached target level {Level}, deleting ResourceBuildPlan", resourceBuildPlan.Level); await deleteJobByIdCommand.HandleAsync(new(job.Id), cancellationToken); } + else if (addedJobs == 0) + { + logger.Information("ResourceBuild iteration skipped - no resource slots available ({CurrentResourceQueue}/{MaxResourceSlots}) or insufficient resources - keeping plan for next iteration", + currentResourceQueueCount, maxResourceSlots); + + // Resource yetersizliÄŸi durumunda - kısa bekleme yap + // Gerçek timer parsing UpgradeBuildingTask'ta yapılacak + logger.Information("ResourceBuild iteration skipped due to insufficient resources - short wait to retry with actual timer parsing"); + var nextExecuteTime = DateTime.Now.AddMinutes(2); // Kısa bekleme - gerçek timer UpgradeBuildingTask'ta parse edilecek + return NextExecuteError.MissingResource(nextExecuteTime); + } else { - await addJobCommand.HandleAsync(new(villageId, normalBuildPlan.ToJob(), true)); + logger.Information("ResourceBuild iteration completed: Added {AddedJobs} resource jobs from ResourceBuildPlan - keeping plan for next iteration", addedJobs); + // ResourceBuildPlan'ı koru - daha fazla upgrade gerekebilir } + rxQueue.Enqueue(new JobsModified(villageId)); continue; } @@ -64,17 +224,42 @@ CancellationToken cancellationToken result = await updateBuildingCommand.HandleAsync(new(villageId), cancellationToken); if (result.IsFailed) return result; - if (await validateJobCompleteCommand.HandleAsync(new ValidateJobCompleteCommand.Command(villageId, job), cancellationToken)) + var isJobComplete = await validateJobCompleteCommand.HandleAsync(new ValidateJobCompleteCommand.Command(villageId, job), cancellationToken); + logger.Information("Job validation result: {JobContent} - IsComplete={IsComplete}", job.Content, isJobComplete); + + if (isJobComplete) { + logger.Information("Deleting completed job: {JobId} - {JobContent}", job.Id, job.Content); await deleteJobByIdCommand.HandleAsync(new(job.Id), cancellationToken); rxQueue.Enqueue(new JobsModified(villageId)); continue; } + // Individual job'lar için ek validation gerekmez - zaten queue'ya eklenirken validate edildi + + logger.Information("Job validation passed, proceeding with: {JobContent}", job.Content); + + // Job'u ÅŸimdilik silme - construction baÅŸarılı olduktan sonra silinecek + // Bu sayede resource yetersizliÄŸi durumunda job korunacak + logger.Information("Construction attempt will be made for: {JobContent}", job.Content); + return plan; } } + private static bool HasEnoughResource(Storage storage, long[] requiredResource) + { + return storage.Wood >= requiredResource[0] && + storage.Clay >= requiredResource[1] && + storage.Iron >= requiredResource[2] && + storage.Crop >= requiredResource[3]; + } + + // CalculateMissingResources ve CalculateWaitTime fonksiyonları kaldırıldı + // Artık Travian'ın kendi timer'ını kullanıyoruz - UpgradeParser.GetTimeWhenEnoughResource + + // GetFirstMissingResource fonksiyonu kaldırıldı - artık kullanılmıyor + private static NormalBuildPlan? GetNormalBuildPlan( ResourceBuildPlan plan, List layoutBuildings @@ -86,21 +271,21 @@ List layoutBuildings { resourceFields = layoutBuildings .Where(x => x.Type == BuildingEnums.Woodcutter || x.Type == BuildingEnums.ClayPit || x.Type == BuildingEnums.IronMine) - .Where(x => x.Level < plan.Level) + .Where(x => IsResourceUpgradeable(x, plan.Level)) .ToList(); } else if (plan.Plan == ResourcePlanEnums.OnlyCrop) { resourceFields = layoutBuildings .Where(x => x.Type == BuildingEnums.Cropland) - .Where(x => x.Level < plan.Level) + .Where(x => IsResourceUpgradeable(x, plan.Level)) .ToList(); } else { resourceFields = layoutBuildings .Where(x => x.Type.IsResourceField()) - .Where(x => x.Level < plan.Level) + .Where(x => IsResourceUpgradeable(x, plan.Level)) .ToList(); } @@ -126,25 +311,48 @@ List layoutBuildings return normalBuildPlan; } + // Resource upgrade edilebilirlik kontrol fonksiyonu + private static bool IsResourceUpgradeable(BuildingItem building, int targetLevel) + { + // EÄŸer current level zaten hedef seviyeye ulaÅŸtıysa, upgrade edilemez + if (building.CurrentLevel >= targetLevel) return false; + + // EÄŸer job level hedef seviyeyi geçtiyse, daha fazla upgrade planlanmış demektir + if (building.JobLevel >= targetLevel) return false; + + // Queue'da bekleyen bir upgrade varsa, onun bitmesini bekle + // Yeni job ekleme - zaten queue'da var + if (building.QueueLevel > 0) return false; + + // Sadece current level'a göre kontrol et + return building.CurrentLevel < targetLevel; + } + private static bool IsJobComplete(JobDto job, List buildings, List queueBuildings) { if (job.Type == JobTypeEnums.ResourceBuild) return false; var plan = JsonSerializer.Deserialize(job.Content)!; - var queueBuilding = queueBuildings + // Gerçek building level kontrolü - en güvenilir yöntem + var villageBuilding = buildings .Where(x => x.Location == plan.Location) - .OrderByDescending(x => x.Level) .Select(x => x.Level) .FirstOrDefault(); + if (villageBuilding >= plan.Level) return true; - if (queueBuilding >= plan.Level) return true; - - var villageBuilding = buildings + // Queue building kontrolü - construction baÅŸladıktan sonra job'un tekrar iÅŸlenmesini engellemek için + var queueBuilding = queueBuildings .Where(x => x.Location == plan.Location) - .Select(x => x.Level) + .OrderByDescending(x => x.Level) .FirstOrDefault(); - if (villageBuilding >= plan.Level) return true; + + if (queueBuilding is not null && + queueBuilding.Level >= plan.Level && + queueBuilding.CompleteTime <= DateTime.Now) + { + return true; + } return false; } diff --git a/MainCore/Commands/Features/UpgradeBuilding/GetJobCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/GetJobCommand.cs index b6d8093c1..2f4552d10 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/GetJobCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/GetJobCommand.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; namespace MainCore.Commands.Misc { @@ -251,4 +251,4 @@ private static Result IsJobValid(JobDto job, List buildings, List HandleAsync( ValidateEnoughResourceCommand.Handler validateEnoughResourceCommand, GetMissingResourceCommand.Handler getMissingResourceCommand, ISettingService settingService, - IChromeBrowser browser, + IBrowser browser, ILogger logger, CancellationToken cancellationToken) { @@ -48,7 +48,7 @@ private static async ValueTask HandleAsync( return Result.Ok(); } - private static long[] GetRequiredResource(IChromeBrowser browser, BuildingEnums building) + private static long[] GetRequiredResource(IBrowser browser, BuildingEnums building) { var doc = browser.Html; @@ -64,4 +64,4 @@ private static long[] GetRequiredResource(IChromeBrowser browser, BuildingEnums return resourceBuilding; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UpgradeBuilding/HandleUpgradeCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/HandleUpgradeCommand.cs index 2839ae654..a2ab423dc 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/HandleUpgradeCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/HandleUpgradeCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UpgradeBuilding +namespace MainCore.Commands.Features.UpgradeBuilding { [Handler] public static partial class HandleUpgradeCommand @@ -7,7 +7,7 @@ public sealed record Command(VillageId VillageId, NormalBuildPlan Plan) : IVilla private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, ILogger logger, CancellationToken cancellationToken @@ -104,14 +104,14 @@ private static bool IsEmptySite(this AppDbContext context, VillageId villageId, } private static async Task SpecialUpgrade( - this IChromeBrowser browser, + this IBrowser browser, CancellationToken cancellationToken ) { var button = UpgradeParser.GetSpecialUpgradeButton(browser.Html); if (button is null) return Retry.ButtonNotFound("Watch ads upgrade"); - var result = await browser.Click(By.XPath(button.XPath), cancellationToken); + var result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; result = await browser.HandleAds(cancellationToken); @@ -121,13 +121,11 @@ CancellationToken cancellationToken } private static async Task HandleAds( - this IChromeBrowser browser, + this IBrowser browser, CancellationToken cancellationToken ) { var driver = browser.Driver; - if (driver is null) return Stop.DriverNotReady; - var current = driver.CurrentWindowHandle; while (driver.WindowHandles.Count > 1) { @@ -152,12 +150,12 @@ static bool videoFeatureShown(IWebDriver driver) { var checkbox = videoFeature.Descendants("div").FirstOrDefault(x => x.HasClass("checkbox")); if (checkbox is null) return Retry.ButtonNotFound("Don't show watch ads confirm again"); - result = await browser.Click(By.XPath(checkbox.XPath), cancellationToken); + result = await browser.Click(By.XPath(checkbox.XPath)); if (result.IsFailed) return result; var watchButton = videoFeature.Descendants("button").FirstOrDefault(x => x.HasClass("green")); if (watchButton is null) return Retry.ButtonNotFound("Watch ads"); - result = await browser.Click(By.XPath(watchButton.XPath), cancellationToken); + result = await browser.Click(By.XPath(watchButton.XPath)); if (result.IsFailed) return result; } @@ -166,7 +164,7 @@ static bool videoFeatureShown(IWebDriver driver) var node = browser.Html.GetElementbyId("videoFeature"); if (node is null) return Retry.ButtonNotFound($"play ads"); - result = await browser.Click(By.XPath(node.XPath), cancellationToken); + result = await browser.Click(By.XPath(node.XPath)); if (result.IsFailed) return result; driver.SwitchTo().DefaultContent(); @@ -184,7 +182,7 @@ static bool videoFeatureShown(IWebDriver driver) driver.Close(); driver.SwitchTo().Window(current); - result = await browser.Click(By.XPath(node.XPath), cancellationToken); + result = await browser.Click(By.XPath(node.XPath)); if (result.IsFailed) return result; driver.SwitchTo().DefaultContent(); @@ -198,12 +196,12 @@ static bool videoFeatureShown(IWebDriver driver) var dontShowThisAgain = browser.Html.GetElementbyId("dontShowThisAgain"); if (dontShowThisAgain is not null) { - result = await browser.Click(By.XPath(dontShowThisAgain.XPath), cancellationToken); + result = await browser.Click(By.XPath(dontShowThisAgain.XPath)); if (result.IsFailed) return result; var okButton = browser.Html.DocumentNode.Descendants("button").FirstOrDefault(x => x.HasClass("dialogButtonOk")); if (okButton is null) return Retry.ButtonNotFound("ok"); - result = await browser.Click(By.XPath(okButton.XPath), cancellationToken); + result = await browser.Click(By.XPath(okButton.XPath)); if (result.IsFailed) return result; } @@ -211,13 +209,13 @@ static bool videoFeatureShown(IWebDriver driver) } private static async Task Upgrade( - this IChromeBrowser browser, + this IBrowser browser, CancellationToken cancellationToken) { var button = UpgradeParser.GetUpgradeButton(browser.Html); if (button is null) return Retry.ButtonNotFound("upgrade"); - var result = await browser.Click(By.XPath(button.XPath), cancellationToken); + var result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; result = await browser.WaitPageChanged("dorf", cancellationToken); @@ -227,15 +225,27 @@ private static async Task Upgrade( } private static async Task Construct( - this IChromeBrowser browser, + this IBrowser browser, BuildingEnums building, CancellationToken cancellationToken ) { var button = UpgradeParser.GetConstructButton(browser.Html, building); - if (button is null) return Retry.ButtonNotFound("construct"); + if (button is null) + { + // Boþ arsalarda construct buton bulma sorunu için daha detaylý hata mesajý + var isEmptySite = browser.Html.DocumentNode + .Descendants() + .Any(x => x.HasClass("emptySlot") || x.InnerText.Contains("empty", StringComparison.InvariantCultureIgnoreCase)); + + var errorMessage = isEmptySite + ? $"construct button for {building} on empty site" + : $"construct button for {building}"; + + return Retry.ButtonNotFound(errorMessage); + } - var result = await browser.Click(By.XPath(button.XPath), cancellationToken); + var result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; result = await browser.WaitPageChanged("dorf", cancellationToken); @@ -244,4 +254,4 @@ CancellationToken cancellationToken return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UpgradeBuilding/ToBuildPageCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/ToBuildPageCommand.cs index 2e0db29d7..c02f67c8f 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/ToBuildPageCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/ToBuildPageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UpgradeBuilding +namespace MainCore.Commands.Features.UpgradeBuilding { [Handler] public static partial class ToBuildPageCommand @@ -32,4 +32,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UpgradeBuilding/ValidateEnoughResourceCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/ValidateEnoughResourceCommand.cs index 20d45279b..ac69d13ca 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/ValidateEnoughResourceCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/ValidateEnoughResourceCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UpgradeBuilding +namespace MainCore.Commands.Features.UpgradeBuilding { [Handler] public static partial class ValidateEnoughResourceCommand @@ -62,4 +62,4 @@ CancellationToken cancellationToken return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UpgradeBuilding/ValidateJobCompleteCommand.cs b/MainCore/Commands/Features/UpgradeBuilding/ValidateJobCompleteCommand.cs index dbbac3596..15a189ce1 100644 --- a/MainCore/Commands/Features/UpgradeBuilding/ValidateJobCompleteCommand.cs +++ b/MainCore/Commands/Features/UpgradeBuilding/ValidateJobCompleteCommand.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; namespace MainCore.Commands.Features.UpgradeBuilding { @@ -10,6 +10,7 @@ public sealed record Command(VillageId VillageId, JobDto job) : IVillageCommand; private static async ValueTask HandleAsync( Command command, AppDbContext context, + ILogger logger, CancellationToken cancellationToken ) { @@ -19,22 +20,55 @@ CancellationToken cancellationToken var plan = JsonSerializer.Deserialize(job.Content)!; - var queueBuilding = context.QueueBuildings + // Gerçek bina seviyesini kontrol ediyorum - bu asıl validation olmalı + var villageBuilding = context.Buildings .Where(x => x.VillageId == villageId.Value) .Where(x => x.Location == plan.Location) - .OrderByDescending(x => x.Level) .Select(x => x.Level) .FirstOrDefault(); - if (queueBuilding >= plan.Level) return true; - - var villageBuilding = context.Buildings + // Queue building kontrolü + var queueBuilding = context.QueueBuildings .Where(x => x.VillageId == villageId.Value) .Where(x => x.Location == plan.Location) - .Select(x => x.Level) .FirstOrDefault(); - if (villageBuilding >= plan.Level) return true; + // Detaylı logging + var queueCompleteTime = queueBuilding?.CompleteTime.ToString("HH:mm:ss") ?? "None"; + logger.Information("ValidateJobComplete: {Plan} - BuildingLevel={BuildingLevel}, QueueLevel={QueueLevel}, QueueCompleteTime={CompleteTime}", + $"{plan.Type} at location {plan.Location} to level {plan.Level}", + villageBuilding, + queueBuilding?.Level ?? 0, + queueCompleteTime); + + // Önce gerçek building level kontrolü + if (villageBuilding >= plan.Level) + { + logger.Information("Job complete: Building level {BuildingLevel} >= Target level {TargetLevel}", villageBuilding, plan.Level); + return true; + } + + // EÄŸer queue'da tamamlanan inÅŸa varsa, bu da job complete sayılabilir + // CRITICAL FIX: Queue level kontrolünü geri ekliyorum - construction baÅŸladıktan sonra job'un tekrar iÅŸlenmesini engellemek için + // Sadece queue'da hedef seviyeye ulaÅŸmış VE tamamlanmış inÅŸa varsa job complete sayılır + if (queueBuilding is not null && + queueBuilding.Level >= plan.Level && + queueBuilding.CompleteTime <= DateTime.Now) + { + logger.Information("Job complete: Queue building level {QueueLevel} >= Target level {TargetLevel} and CompleteTime passed at {CompleteTime}", + queueBuilding.Level, plan.Level, queueBuilding.CompleteTime.ToString("HH:mm:ss")); + return true; + } + + // Queue building detay logging + if (queueBuilding is not null) + { + logger.Information("Queue analysis: Level={QueueLevel} vs Target={TargetLevel}, CompleteTime={CompleteTime} vs Now={Now}, TimeCheck={TimeCheck}", + queueBuilding.Level, plan.Level, queueBuilding.CompleteTime.ToString("HH:mm:ss"), DateTime.Now.ToString("HH:mm:ss"), queueBuilding.CompleteTime <= DateTime.Now); + } + + // Job henüz tamamlanmamış + logger.Information("Job not complete: Building level {BuildingLevel} < Target level {TargetLevel}", villageBuilding, plan.Level); return false; } } diff --git a/MainCore/Commands/Features/UseHeroItem/ToHeroInventoryCommand.cs b/MainCore/Commands/Features/UseHeroItem/ToHeroInventoryCommand.cs index e21feeb14..ab05cfddd 100644 --- a/MainCore/Commands/Features/UseHeroItem/ToHeroInventoryCommand.cs +++ b/MainCore/Commands/Features/UseHeroItem/ToHeroInventoryCommand.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 namespace MainCore.Commands.Features.UseHeroItem { @@ -9,13 +9,13 @@ public sealed record Command : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken) { var avatar = InventoryParser.GetHeroAvatar(browser.Html); if (avatar is null) return Retry.ButtonNotFound("avatar hero"); - var result = await browser.Click(By.XPath(avatar.XPath), cancellationToken); + var result = await browser.Click(By.XPath(avatar.XPath)); if (result.IsFailed) return result; static bool TabActived(IWebDriver driver) @@ -30,4 +30,4 @@ static bool TabActived(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UseHeroItem/UseHeroItemCommand.cs b/MainCore/Commands/Features/UseHeroItem/UseHeroItemCommand.cs index 92a5b0930..dbf643491 100644 --- a/MainCore/Commands/Features/UseHeroItem/UseHeroItemCommand.cs +++ b/MainCore/Commands/Features/UseHeroItem/UseHeroItemCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UseHeroItem +namespace MainCore.Commands.Features.UseHeroItem { [Handler] public static partial class UseHeroItemCommand @@ -7,7 +7,7 @@ public sealed record Command(HeroItemEnums Item, long Amount) : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, ILogger logger, IDelayService delayService, CancellationToken cancellationToken) @@ -19,7 +19,7 @@ private static async ValueTask HandleAsync( if (result.IsFailed) return result; await delayService.DelayClick(cancellationToken); - result = await EnterAmount(browser, amount, cancellationToken); + result = await EnterAmount(browser, amount); if (result.IsFailed) return result; await delayService.DelayClick(cancellationToken); @@ -31,7 +31,7 @@ private static async ValueTask HandleAsync( } private static async Task ClickItem( - IChromeBrowser browser, + IBrowser browser, HeroItemEnums item, CancellationToken cancellationToken) { @@ -46,7 +46,7 @@ static bool loadingCompleted(IWebDriver driver) } Result result; - result = await browser.Click(By.XPath(node.XPath), cancellationToken); + result = await browser.Click(By.XPath(node.XPath)); if (result.IsFailed) return result; result = await browser.Wait(driver => loadingCompleted(driver), cancellationToken); @@ -55,21 +55,21 @@ static bool loadingCompleted(IWebDriver driver) } private static async Task EnterAmount( - IChromeBrowser browser, - long amount, - CancellationToken cancellationToken) + IBrowser browser, + long amount + ) { var node = InventoryParser.GetAmountBox(browser.Html); if (node is null) return Retry.TextboxNotFound("amount"); Result result; - result = await browser.Input(By.XPath(node.XPath), amount.ToString(), cancellationToken); + result = await browser.Input(By.XPath(node.XPath), amount.ToString()); if (result.IsFailed) return result; return Result.Ok(); } private static async Task Confirm( - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken) { var node = InventoryParser.GetConfirmButton(browser.Html); @@ -83,7 +83,7 @@ static bool loadingCompleted(IWebDriver driver) } Result result; - result = await browser.Click(By.XPath(node.XPath), cancellationToken); + result = await browser.Click(By.XPath(node.XPath)); if (result.IsFailed) return result; result = await browser.Wait(driver => loadingCompleted(driver), cancellationToken); @@ -92,4 +92,4 @@ static bool loadingCompleted(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UseHeroItem/UseHeroResourceCommand.cs b/MainCore/Commands/Features/UseHeroItem/UseHeroResourceCommand.cs index cfcbc7a2a..dc6cdb8c1 100644 --- a/MainCore/Commands/Features/UseHeroItem/UseHeroResourceCommand.cs +++ b/MainCore/Commands/Features/UseHeroItem/UseHeroResourceCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UseHeroItem +namespace MainCore.Commands.Features.UseHeroItem { [Handler] public static partial class UseHeroResourceCommand @@ -50,4 +50,4 @@ private static long RoundUpTo100(long res) return res + (100 - remainder); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Features/UseHeroItem/ValidateEnoughResourceCommand.cs b/MainCore/Commands/Features/UseHeroItem/ValidateEnoughResourceCommand.cs index b7e0693c4..f93b17f63 100644 --- a/MainCore/Commands/Features/UseHeroItem/ValidateEnoughResourceCommand.cs +++ b/MainCore/Commands/Features/UseHeroItem/ValidateEnoughResourceCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Features.UseHeroItem +namespace MainCore.Commands.Features.UseHeroItem { [Handler] public static partial class ValidateEnoughResourceCommand @@ -45,4 +45,4 @@ CancellationToken cancellationToken return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Misc/AddJobCommand.cs b/MainCore/Commands/Misc/AddJobCommand.cs index d6b02098f..6283246c9 100644 --- a/MainCore/Commands/Misc/AddJobCommand.cs +++ b/MainCore/Commands/Misc/AddJobCommand.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; namespace MainCore.Commands.Misc { @@ -56,4 +56,4 @@ public static JobDto ToJob(this ResourceBuildPlan plan) }; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Misc/DeleteJobByIdCommand.cs b/MainCore/Commands/Misc/DeleteJobByIdCommand.cs index 4bd78e364..ce90de83b 100644 --- a/MainCore/Commands/Misc/DeleteJobByIdCommand.cs +++ b/MainCore/Commands/Misc/DeleteJobByIdCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Misc +namespace MainCore.Commands.Misc { [Handler] public static partial class DeleteJobByIdCommand @@ -34,4 +34,4 @@ AppDbContext context .ExecuteUpdate(x => x.SetProperty(x => x.Position, x => x.Position - 1)); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Misc/GetLayoutBuildingsCommand.cs b/MainCore/Commands/Misc/GetLayoutBuildingsCommand.cs index 480b77032..5645921d0 100644 --- a/MainCore/Commands/Misc/GetLayoutBuildingsCommand.cs +++ b/MainCore/Commands/Misc/GetLayoutBuildingsCommand.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; namespace MainCore.Commands.Misc { @@ -105,4 +105,4 @@ AppDbContext context return villageBuildings; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Misc/GetValidAccessCommand.cs b/MainCore/Commands/Misc/GetValidAccessCommand.cs index b5b17acbc..4f3f2224a 100644 --- a/MainCore/Commands/Misc/GetValidAccessCommand.cs +++ b/MainCore/Commands/Misc/GetValidAccessCommand.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; namespace MainCore.Commands.Misc { @@ -103,4 +103,4 @@ private static HttpClient GetHttpClient(AccessDto access) return _proxyWithAuthHttpClient; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Misc/OpenBrowserCommand.cs b/MainCore/Commands/Misc/OpenBrowserCommand.cs index 6f7681697..a3dd3b309 100644 --- a/MainCore/Commands/Misc/OpenBrowserCommand.cs +++ b/MainCore/Commands/Misc/OpenBrowserCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Misc +namespace MainCore.Commands.Misc { [Handler] public static partial class OpenBrowserCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId, AccessDto Access) : IAccountCo private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, AppDbContext context, + IBrowser browser, AppDbContext context, CancellationToken cancellationToken ) { @@ -30,7 +30,7 @@ CancellationToken cancellationToken var headlessChrome = context.BooleanByName(accountId, AccountSettingEnums.HeadlessChrome); var profilePath = Path.Combine(serverFolderName, accountFolderName); - var chromeSetting = new ChromeSetting() + var BrowserSetting = new BrowserSetting() { UserAgent = access.Useragent, ProfilePath = profilePath, @@ -41,7 +41,7 @@ CancellationToken cancellationToken IsHeadless = headlessChrome, }; - await browser.Setup(chromeSetting); + await browser.Setup(BrowserSetting); await browser.Navigate($"{account.Server}", cancellationToken); context.Accesses @@ -49,4 +49,4 @@ CancellationToken cancellationToken .ExecuteUpdate(x => x.SetProperty(x => x.LastUsed, x => DateTime.Now)); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Navigate/SwitchManagementTabCommand.cs b/MainCore/Commands/Navigate/SwitchManagementTabCommand.cs index 0bde335ee..a6ef2837f 100644 --- a/MainCore/Commands/Navigate/SwitchManagementTabCommand.cs +++ b/MainCore/Commands/Navigate/SwitchManagementTabCommand.cs @@ -1,4 +1,6 @@ -namespace MainCore.Commands.Navigate +using MainCore.DTO; + +namespace MainCore.Commands.Navigate { [Handler] public static partial class SwitchManagementTabCommand @@ -7,7 +9,7 @@ public sealed record Command(VillageId VillageId, int Location) : IVillageComman private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, CancellationToken cancellationToken ) @@ -22,7 +24,9 @@ CancellationToken cancellationToken Result result; if (building.Type == BuildingEnums.Site) { - var tabIndex = building.Type.GetBuildingsCategory(); + // Boþ arsa için hedef bina türünü job'dan alýyorum + var targetBuildingType = GetTargetBuildingTypeFromJob(context, villageId, location); + var tabIndex = targetBuildingType.GetBuildingsCategory(); result = await SwitchTabCommand.SwitchTab(browser, tabIndex, cancellationToken); if (result.IsFailed) return result; @@ -38,5 +42,33 @@ CancellationToken cancellationToken return Result.Ok(); } + + private static BuildingEnums GetTargetBuildingTypeFromJob(AppDbContext context, VillageId villageId, int location) + { + // Önce QueueBuilding'den kontrol ediyorum - eðer zaten kuyruktaysa + var queueBuilding = context.QueueBuildings + .Where(x => x.VillageId == villageId.Value) + .Where(x => x.Location == location) + .OrderBy(x => x.Level) + .FirstOrDefault(); + + if (queueBuilding is not null) + { + return queueBuilding.Type; + } + + // Job'lardan hedef bina türünü alýyorum + var job = context.Jobs + .Where(x => x.VillageId == villageId.Value) + .Where(x => x.Type == JobTypeEnums.NormalBuild) + .Select(x => x.Content) + .AsEnumerable() + .Select(x => System.Text.Json.JsonSerializer.Deserialize(x)!) + .Where(x => x.Location == location) + .OrderBy(x => x.Level) + .FirstOrDefault(); + + return job?.Type ?? BuildingEnums.Site; + } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Navigate/SwitchTabCommand.cs b/MainCore/Commands/Navigate/SwitchTabCommand.cs index b62230617..a06117f79 100644 --- a/MainCore/Commands/Navigate/SwitchTabCommand.cs +++ b/MainCore/Commands/Navigate/SwitchTabCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Navigate +namespace MainCore.Commands.Navigate { [Handler] public static partial class SwitchTabCommand @@ -7,7 +7,7 @@ public sealed record Command(int TabIndex) : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken ) { @@ -15,7 +15,7 @@ CancellationToken cancellationToken } public static async ValueTask SwitchTab( - IChromeBrowser browser, + IBrowser browser, int tabIndex, CancellationToken cancellationToken) { @@ -38,7 +38,7 @@ bool tabActived(IWebDriver driver) } Result result; - result = await browser.Click(By.XPath(tab.XPath), cancellationToken); + result = await browser.Click(By.XPath(tab.XPath)); if (result.IsFailed) return result; result = await browser.Wait(tabActived, cancellationToken); if (result.IsFailed) return result; @@ -46,4 +46,4 @@ bool tabActived(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Navigate/SwitchVillageCommand.cs b/MainCore/Commands/Navigate/SwitchVillageCommand.cs index de4055161..f3ce18801 100644 --- a/MainCore/Commands/Navigate/SwitchVillageCommand.cs +++ b/MainCore/Commands/Navigate/SwitchVillageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Navigate +namespace MainCore.Commands.Navigate { [Handler] public static partial class SwitchVillageCommand @@ -7,14 +7,26 @@ public sealed record Command(VillageId VillageId) : IVillageCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken ) { var villageId = command.VillageId; + // Köy bulunamadığında sayfayı yenile ve tekrar dene var node = VillagePanelParser.GetVillageNode(browser.Html, villageId); - if (node is null) return Skip.VillageNotFound; + if (node is null) + { + // Sayfayı yenile ve tekrar dene + var refreshResult = await browser.Refresh(cancellationToken); + if (refreshResult.IsFailed) return refreshResult; + + // Kısa bekleme - sayfa yüklenmesi için + await Task.Delay(1000, cancellationToken); + + node = VillagePanelParser.GetVillageNode(browser.Html, villageId); + if (node is null) return Skip.VillageNotFound; + } if (VillagePanelParser.IsActive(node)) return Result.Ok(); @@ -28,7 +40,7 @@ bool villageChanged(IWebDriver driver) } Result result; - result = await browser.Click(By.XPath(node.XPath), cancellationToken); + result = await browser.Click(By.XPath(node.XPath)); if (result.IsFailed) return result; result = await browser.Wait(villageChanged, cancellationToken); @@ -37,4 +49,4 @@ bool villageChanged(IWebDriver driver) return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Navigate/ToBuildingByLocationCommand.cs b/MainCore/Commands/Navigate/ToBuildingByLocationCommand.cs index 55017736d..13c153252 100644 --- a/MainCore/Commands/Navigate/ToBuildingByLocationCommand.cs +++ b/MainCore/Commands/Navigate/ToBuildingByLocationCommand.cs @@ -1,4 +1,4 @@ -using System.Web; +using System.Web; namespace MainCore.Commands.Navigate { @@ -9,7 +9,7 @@ public sealed record Command(int Location) : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken ) { @@ -18,7 +18,7 @@ CancellationToken cancellationToken public static async ValueTask ToBuilding( int location, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken) { var node = GetBuilding(browser.Html, location); @@ -36,7 +36,7 @@ public static async ValueTask ToBuilding( else { var css = $"#villageContent > div.buildingSlot.a{location} > svg > path"; - result = await browser.Click(By.CssSelector(css), cancellationToken); + result = await browser.Click(By.CssSelector(css)); if (result.IsFailed) return result; result = await browser.WaitPageChanged("build.php", cancellationToken); if (result.IsFailed) return result; @@ -59,7 +59,7 @@ public static async ValueTask ToBuilding( } else { - result = await browser.Click(By.XPath(node.XPath), cancellationToken); + result = await browser.Click(By.XPath(node.XPath)); if (result.IsFailed) return result; } result = await browser.WaitPageChanged("build.php", cancellationToken); @@ -90,4 +90,4 @@ public static async ValueTask ToBuilding( return div; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Navigate/ToBuildingByTypeCommand.cs b/MainCore/Commands/Navigate/ToBuildingByTypeCommand.cs index 7901ac797..8a5a4a2d1 100644 --- a/MainCore/Commands/Navigate/ToBuildingByTypeCommand.cs +++ b/MainCore/Commands/Navigate/ToBuildingByTypeCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Navigate +namespace MainCore.Commands.Navigate { [Handler] public static partial class ToBuildingByTypeCommand @@ -7,7 +7,7 @@ public sealed record Command(VillageId VillageId, BuildingEnums Type) : IVillage private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, CancellationToken cancellationToken ) @@ -26,4 +26,4 @@ CancellationToken cancellationToken return await ToBuildingByLocationCommand.ToBuilding(marketLocation, browser, cancellationToken); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Navigate/ToDorfCommand.cs b/MainCore/Commands/Navigate/ToDorfCommand.cs index e04a3c8d6..ea1f217b3 100644 --- a/MainCore/Commands/Navigate/ToDorfCommand.cs +++ b/MainCore/Commands/Navigate/ToDorfCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Navigate +namespace MainCore.Commands.Navigate { [Handler] public static partial class ToDorfCommand @@ -7,7 +7,7 @@ public sealed record Command(int Dorf) : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, CancellationToken cancellationToken ) { @@ -26,11 +26,56 @@ CancellationToken cancellationToken return Result.Ok(); } + // Navigation bar bulunamadığında güçlü bekleme yap var button = NavigationBarParser.GetDorfButton(browser.Html, dorf); - if (button is null) return Retry.ButtonNotFound($"dorf{dorf}"); + if (button is null) + { + // Önce kısa bekleme - sayfa yüklenmesi için + await Task.Delay(2000, cancellationToken); + + button = NavigationBarParser.GetDorfButton(browser.Html, dorf); + if (button is not null) + { + // Button bulundu, devam et + } + else + { + // Hala bulunamıyorsa sayfayı yenile + var refreshResult = await browser.Refresh(cancellationToken); + if (refreshResult.IsFailed) return refreshResult; + + // Sayfa yenilendikten sonra daha uzun bekle + await Task.Delay(5000, cancellationToken); + + // Son bir kez daha dene + button = NavigationBarParser.GetDorfButton(browser.Html, dorf); + if (button is null) + { + // Hala bulunamıyorsa, direkt URL ile git + var browserUrl = browser.CurrentUrl; + var targetUrl = dorf switch + { + 1 => browserUrl.Replace("dorf2", "dorf1"), + 2 => browserUrl.Replace("dorf1", "dorf2"), + _ => browserUrl + }; + + if (targetUrl != browserUrl) + { + var navigateResult = await browser.Navigate(targetUrl, cancellationToken); + if (navigateResult.IsFailed) return navigateResult; + + await Task.Delay(2000, cancellationToken); + button = NavigationBarParser.GetDorfButton(browser.Html, dorf); + } + + if (button is null) return Retry.ButtonNotFound($"dorf{dorf}"); + } + } + } Result result; - result = await browser.Click(By.XPath(button.XPath), cancellationToken); + result = await browser.Click(By.XPath(button.XPath)); if (result.IsFailed) return result; result = await browser.WaitPageChanged($"dorf{dorf}", cancellationToken); if (result.IsFailed) return result; @@ -44,4 +89,4 @@ private static int GetCurrentDorf(string url) return 0; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/NextExecute/NextExecuteSleepTaskCommand.cs b/MainCore/Commands/NextExecute/NextExecuteSleepTaskCommand.cs index a89d042f9..6448a34a3 100644 --- a/MainCore/Commands/NextExecute/NextExecuteSleepTaskCommand.cs +++ b/MainCore/Commands/NextExecute/NextExecuteSleepTaskCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.NextExecute +namespace MainCore.Commands.NextExecute { [Handler] public static partial class NextExecuteSleepTaskCommand @@ -19,4 +19,4 @@ ISettingService settingService command.Task.ExecuteAt = DateTime.Now.AddSeconds(workTime); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/NextExecute/NextExecuteStartAdventureTaskCommand.cs b/MainCore/Commands/NextExecute/NextExecuteStartAdventureTaskCommand.cs index 267e51475..3052853df 100644 --- a/MainCore/Commands/NextExecute/NextExecuteStartAdventureTaskCommand.cs +++ b/MainCore/Commands/NextExecute/NextExecuteStartAdventureTaskCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.NextExecute +namespace MainCore.Commands.NextExecute { [Handler] public static partial class NextExecuteStartAdventureTaskCommand @@ -7,7 +7,7 @@ public sealed record Command(StartAdventureTask.Task Task) : ICommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser + IBrowser browser ) { await Task.CompletedTask; @@ -15,4 +15,4 @@ IChromeBrowser browser command.Task.ExecuteAt = DateTime.Now.Add(adventureDuration * 2); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/NextExecute/NextExecuteStartFarmListTaskCommand.cs b/MainCore/Commands/NextExecute/NextExecuteStartFarmListTaskCommand.cs index e80e1942b..ada84f3b2 100644 --- a/MainCore/Commands/NextExecute/NextExecuteStartFarmListTaskCommand.cs +++ b/MainCore/Commands/NextExecute/NextExecuteStartFarmListTaskCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.NextExecute +namespace MainCore.Commands.NextExecute { [Handler] public static partial class NextExecuteStartFarmListTaskCommand @@ -18,4 +18,4 @@ ISettingService settingService command.Task.ExecuteAt = DateTime.Now.AddSeconds(seconds); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/NextExecute/NextExecuteTrainTroopTaskCommand.cs b/MainCore/Commands/NextExecute/NextExecuteTrainTroopTaskCommand.cs index b1aeba5b7..e7549a3e1 100644 --- a/MainCore/Commands/NextExecute/NextExecuteTrainTroopTaskCommand.cs +++ b/MainCore/Commands/NextExecute/NextExecuteTrainTroopTaskCommand.cs @@ -21,4 +21,4 @@ ISettingService settingService command.Task.ExecuteAt = DateTime.Now.AddSeconds(seconds); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/NextExecute/NextExecuteUpdateVillageTaskCommand.cs b/MainCore/Commands/NextExecute/NextExecuteUpdateVillageTaskCommand.cs index c4a0cab78..08cdd268e 100644 --- a/MainCore/Commands/NextExecute/NextExecuteUpdateVillageTaskCommand.cs +++ b/MainCore/Commands/NextExecute/NextExecuteUpdateVillageTaskCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.NextExecute +namespace MainCore.Commands.NextExecute { [Handler] public static partial class NextExecuteUpdateVillageTaskCommand @@ -19,4 +19,4 @@ ISettingService settingService command.Task.ExecuteAt = DateTime.Now.AddSeconds(seconds); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/AddAccountViewModel/AddAccountCommand.cs b/MainCore/Commands/UI/AddAccountViewModel/AddAccountCommand.cs index 326d058cc..e8aa63d12 100644 --- a/MainCore/Commands/UI/AddAccountViewModel/AddAccountCommand.cs +++ b/MainCore/Commands/UI/AddAccountViewModel/AddAccountCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.AddAccountViewModel +namespace MainCore.Commands.UI.AddAccountViewModel { [Handler] public static partial class AddAccountCommand @@ -48,4 +48,4 @@ IRxQueue rxQueue return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/AddAccountsViewModel/AddAccountsCommand.cs b/MainCore/Commands/UI/AddAccountsViewModel/AddAccountsCommand.cs index 663e77761..7acb6eeba 100644 --- a/MainCore/Commands/UI/AddAccountsViewModel/AddAccountsCommand.cs +++ b/MainCore/Commands/UI/AddAccountsViewModel/AddAccountsCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.AddAccountsViewModel +namespace MainCore.Commands.UI.AddAccountsViewModel { [Handler] public static partial class AddAccountsCommand @@ -56,4 +56,4 @@ IRxQueue rxQueue return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/EditAccountViewModel/UpdateAccountCommand.cs b/MainCore/Commands/UI/EditAccountViewModel/UpdateAccountCommand.cs index 03b29a64b..16bbb61e0 100644 --- a/MainCore/Commands/UI/EditAccountViewModel/UpdateAccountCommand.cs +++ b/MainCore/Commands/UI/EditAccountViewModel/UpdateAccountCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.EditAccountViewModel +namespace MainCore.Commands.UI.EditAccountViewModel { [Handler] public static partial class UpdateAccountCommand @@ -35,4 +35,4 @@ IRxQueue rxQueue return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/MainLayoutViewModel/DeleteCommand.cs b/MainCore/Commands/UI/MainLayoutViewModel/DeleteCommand.cs index a4b0472da..e5fdeec88 100644 --- a/MainCore/Commands/UI/MainLayoutViewModel/DeleteCommand.cs +++ b/MainCore/Commands/UI/MainLayoutViewModel/DeleteCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.MainLayoutViewModel +namespace MainCore.Commands.UI.MainLayoutViewModel { [Handler] public static partial class DeleteCommand @@ -23,4 +23,4 @@ IRxQueue rxQueue return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/MainLayoutViewModel/LoginCommand.cs b/MainCore/Commands/UI/MainLayoutViewModel/LoginCommand.cs index 05b7ed32c..44471fdf1 100644 --- a/MainCore/Commands/UI/MainLayoutViewModel/LoginCommand.cs +++ b/MainCore/Commands/UI/MainLayoutViewModel/LoginCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.MainLayoutViewModel +namespace MainCore.Commands.UI.MainLayoutViewModel { [Handler] public static partial class LoginCommand @@ -17,7 +17,7 @@ CancellationToken cancellationToken { var (accountId, access) = command; - logger.Information("Using connection {Proxy} to start chrome", access.Proxy); + logger.Information("Using connection {Proxy} to start browser", access.Proxy); try { @@ -35,4 +35,4 @@ CancellationToken cancellationToken rxQueue.Enqueue(new AccountInit(accountId)); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/MainLayoutViewModel/LogoutCommand.cs b/MainCore/Commands/UI/MainLayoutViewModel/LogoutCommand.cs index 652552c5f..9ecf48e90 100644 --- a/MainCore/Commands/UI/MainLayoutViewModel/LogoutCommand.cs +++ b/MainCore/Commands/UI/MainLayoutViewModel/LogoutCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.MainLayoutViewModel +namespace MainCore.Commands.UI.MainLayoutViewModel { [Handler] public static partial class LogoutCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, ITaskManager taskManager ) { @@ -20,4 +20,4 @@ ITaskManager taskManager taskManager.SetStatus(accountId, StatusEnums.Offline); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Misc/SaveAccountSettingCommand.cs b/MainCore/Commands/UI/Misc/SaveAccountSettingCommand.cs index 01043e409..359dcd2e5 100644 --- a/MainCore/Commands/UI/Misc/SaveAccountSettingCommand.cs +++ b/MainCore/Commands/UI/Misc/SaveAccountSettingCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.Misc +namespace MainCore.Commands.UI.Misc { [Handler] public static partial class SaveAccountSettingCommand @@ -71,4 +71,4 @@ ITaskManager taskManager } } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Misc/SaveVillageSettingCommand.cs b/MainCore/Commands/UI/Misc/SaveVillageSettingCommand.cs index eabe1d864..988de3e26 100644 --- a/MainCore/Commands/UI/Misc/SaveVillageSettingCommand.cs +++ b/MainCore/Commands/UI/Misc/SaveVillageSettingCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.Misc +namespace MainCore.Commands.UI.Misc { [Handler] public static partial class SaveVillageSettingCommand @@ -123,4 +123,4 @@ ITaskManager taskManager } } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/StringExtension.cs b/MainCore/Commands/UI/StringExtension.cs index 44df6bfe0..ff222f215 100644 --- a/MainCore/Commands/UI/StringExtension.cs +++ b/MainCore/Commands/UI/StringExtension.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace MainCore.Common.Extensions { @@ -26,4 +26,4 @@ public static string GetServerUrl(this string input) return $"{uri.Scheme}://{uri.Host}"; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Villages/BuildViewModel/FixJobsCommand.cs b/MainCore/Commands/UI/Villages/BuildViewModel/FixJobsCommand.cs index 3bac1f740..6efffe69c 100644 --- a/MainCore/Commands/UI/Villages/BuildViewModel/FixJobsCommand.cs +++ b/MainCore/Commands/UI/Villages/BuildViewModel/FixJobsCommand.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; namespace MainCore.Commands.UI.Villages.BuildViewModel { @@ -193,4 +193,4 @@ private static int GetRandomLocation(List freeLocations) return freeLocations[Random.Shared.Next(0, freeLocations.Count - 1)]; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Villages/BuildViewModel/MoveCommand.cs b/MainCore/Commands/UI/Villages/BuildViewModel/MoveCommand.cs index dd9d0d2de..558313362 100644 --- a/MainCore/Commands/UI/Villages/BuildViewModel/MoveCommand.cs +++ b/MainCore/Commands/UI/Villages/BuildViewModel/MoveCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.Villages.BuildViewModel +namespace MainCore.Commands.UI.Villages.BuildViewModel { [Handler] public static partial class MoveCommand @@ -56,4 +56,4 @@ AppDbContext context return job.Position; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Villages/BuildViewModel/NormalBuildCommand.cs b/MainCore/Commands/UI/Villages/BuildViewModel/NormalBuildCommand.cs index 0d7c15378..0f0572b5e 100644 --- a/MainCore/Commands/UI/Villages/BuildViewModel/NormalBuildCommand.cs +++ b/MainCore/Commands/UI/Villages/BuildViewModel/NormalBuildCommand.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.Commands.UI.Villages.BuildViewModel { @@ -87,4 +87,4 @@ public static NormalBuildPlan ToPlan(this NormalBuildInput input, int location) }; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Villages/BuildViewModel/ResourceBuildCommand.cs b/MainCore/Commands/UI/Villages/BuildViewModel/ResourceBuildCommand.cs index bc59b9433..3c1999698 100644 --- a/MainCore/Commands/UI/Villages/BuildViewModel/ResourceBuildCommand.cs +++ b/MainCore/Commands/UI/Villages/BuildViewModel/ResourceBuildCommand.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.Commands.UI.Villages.BuildViewModel { @@ -26,4 +26,4 @@ public static ResourceBuildPlan ToPlan(this ResourceBuildInput input) }; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Villages/BuildViewModel/SwapCommand.cs b/MainCore/Commands/UI/Villages/BuildViewModel/SwapCommand.cs index 87f8e6d7c..6fb6b18b0 100644 --- a/MainCore/Commands/UI/Villages/BuildViewModel/SwapCommand.cs +++ b/MainCore/Commands/UI/Villages/BuildViewModel/SwapCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.Villages.BuildViewModel +namespace MainCore.Commands.UI.Villages.BuildViewModel { [Handler] public static partial class SwapCommand @@ -57,4 +57,4 @@ AppDbContext context return job.Position; } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/UI/Villages/BuildViewModel/UpgradeCommand.cs b/MainCore/Commands/UI/Villages/BuildViewModel/UpgradeCommand.cs index 432343436..b413b8320 100644 --- a/MainCore/Commands/UI/Villages/BuildViewModel/UpgradeCommand.cs +++ b/MainCore/Commands/UI/Villages/BuildViewModel/UpgradeCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.UI.Villages.BuildViewModel +namespace MainCore.Commands.UI.Villages.BuildViewModel { [Handler] public static partial class UpgradeCommand @@ -39,4 +39,4 @@ GetLayoutBuildingsCommand.Handler getLayoutBuildingsQuery await addJobCommand.HandleAsync(new(villageId, plan.ToJob())); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateAccountInfoCommand.cs b/MainCore/Commands/Update/UpdateAccountInfoCommand.cs index 4797b693d..f4f9cc4de 100644 --- a/MainCore/Commands/Update/UpdateAccountInfoCommand.cs +++ b/MainCore/Commands/Update/UpdateAccountInfoCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateAccountInfoCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context ) { @@ -50,4 +50,4 @@ private static void UpdateToDatabase(this AppDbContext context, AccountId accoun context.SaveChanges(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateAdventureCommand.cs b/MainCore/Commands/Update/UpdateAdventureCommand.cs index b343f46a4..2086f73c7 100644 --- a/MainCore/Commands/Update/UpdateAdventureCommand.cs +++ b/MainCore/Commands/Update/UpdateAdventureCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateAdventureCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountConstraint; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, ITaskManager taskManager ) @@ -23,4 +23,4 @@ ITaskManager taskManager taskManager.Add(startAdventureTask); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateBuildingCommand.cs b/MainCore/Commands/Update/UpdateBuildingCommand.cs index 0f57149dc..a98501e9a 100644 --- a/MainCore/Commands/Update/UpdateBuildingCommand.cs +++ b/MainCore/Commands/Update/UpdateBuildingCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateBuildingCommand @@ -7,7 +7,7 @@ public sealed record Command(VillageId VillageId) : IVillageCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, IRxQueue rxQueue ) @@ -199,4 +199,4 @@ private static void UpdateToDatabase(this AppDbContext context, VillageId villag context.SaveChanges(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateFarmlistCommand.cs b/MainCore/Commands/Update/UpdateFarmlistCommand.cs index 38a56ea07..425f171d0 100644 --- a/MainCore/Commands/Update/UpdateFarmlistCommand.cs +++ b/MainCore/Commands/Update/UpdateFarmlistCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateFarmlistCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, IRxQueue rxQueue ) @@ -60,4 +60,4 @@ private static IEnumerable Get(HtmlDocument doc) } } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateInventoryCommand.cs b/MainCore/Commands/Update/UpdateInventoryCommand.cs index 1afce4d6f..5a4d7adeb 100644 --- a/MainCore/Commands/Update/UpdateInventoryCommand.cs +++ b/MainCore/Commands/Update/UpdateInventoryCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateInventoryCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context ) { @@ -101,4 +101,4 @@ private static void Update(this AppDbContext context, AccountId accountId, List< context.SaveChanges(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateQuestCommand.cs b/MainCore/Commands/Update/UpdateQuestCommand.cs index 1835ce58f..a331c48a0 100644 --- a/MainCore/Commands/Update/UpdateQuestCommand.cs +++ b/MainCore/Commands/Update/UpdateQuestCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateQuestCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId, VillageId VillageId) : IAccoun private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, ITaskManager taskManager ) @@ -23,4 +23,4 @@ ITaskManager taskManager taskManager.Add(claimQuestTask); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateStorageCommand.cs b/MainCore/Commands/Update/UpdateStorageCommand.cs index e5cb65d45..7bf4fc0fd 100644 --- a/MainCore/Commands/Update/UpdateStorageCommand.cs +++ b/MainCore/Commands/Update/UpdateStorageCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateStorageCommand @@ -8,7 +8,7 @@ public sealed record Command(AccountId AccountId, VillageId VillageId) : IAccoun private static async ValueTask HandleAsync( Command command, AppDbContext context, - IChromeBrowser browser, + IBrowser browser, ITaskManager taskManager ) { @@ -59,4 +59,4 @@ private static void UpdateStorage(this AppDbContext context, VillageId villageId context.SaveChanges(); } } -} \ No newline at end of file +} diff --git a/MainCore/Commands/Update/UpdateVillageListCommand.cs b/MainCore/Commands/Update/UpdateVillageListCommand.cs index efde6a272..387a85dc6 100644 --- a/MainCore/Commands/Update/UpdateVillageListCommand.cs +++ b/MainCore/Commands/Update/UpdateVillageListCommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Commands.Update +namespace MainCore.Commands.Update { [Handler] public static partial class UpdateVillageListCommand @@ -7,7 +7,7 @@ public sealed record Command(AccountId AccountId) : IAccountCommand; private static async ValueTask HandleAsync( Command command, - IChromeBrowser browser, + IBrowser browser, AppDbContext context, IRxQueue rxQueue, ITaskManager taskManager) @@ -67,4 +67,4 @@ private static void UpdateToDatabase(this AppDbContext context, AccountId accoun context.SaveChanges(); } } -} \ No newline at end of file +} diff --git a/MainCore/Constraints/ICommand.cs b/MainCore/Constraints/ICommand.cs index 955ecb671..1ec02e8d2 100644 --- a/MainCore/Constraints/ICommand.cs +++ b/MainCore/Constraints/ICommand.cs @@ -1,4 +1,4 @@ -namespace MainCore.Constraints +namespace MainCore.Constraints { public interface ICommand : IConstraint; @@ -9,4 +9,4 @@ public interface IAccountCommand : ICommand, IAccountConstraint; public interface IVillageCommand : ICommand, IVillageConstraint; public interface IAccountVillageCommand : ICommand, IAccountVillageConstraint; -} \ No newline at end of file +} diff --git a/MainCore/Constraints/IConstraint.cs b/MainCore/Constraints/IConstraint.cs index 177aeb9de..e579cd217 100644 --- a/MainCore/Constraints/IConstraint.cs +++ b/MainCore/Constraints/IConstraint.cs @@ -1,4 +1,4 @@ -namespace MainCore.Constraints +namespace MainCore.Constraints { public interface IConstraint; @@ -22,4 +22,4 @@ public interface IAccountVillageConstraint : IAccountConstraint, IVillageConstra } public record AccountVillageConstraint(AccountId AccountId, VillageId VillageId) : IAccountVillageConstraint; -} \ No newline at end of file +} diff --git a/MainCore/Constraints/INotification.cs b/MainCore/Constraints/INotification.cs index 6a7acba6c..83b44a6e9 100644 --- a/MainCore/Constraints/INotification.cs +++ b/MainCore/Constraints/INotification.cs @@ -1,4 +1,4 @@ -namespace MainCore.Constraints +namespace MainCore.Constraints { public interface INotification : IConstraint; @@ -10,4 +10,4 @@ public interface IAccountVillageNotification : INotification, IAccountVillageCon public sealed record Notification() : INotification; public sealed record VillageNotification(AccountId AccountId, VillageId VillageId) : IAccountVillageNotification; -} \ No newline at end of file +} diff --git a/MainCore/Constraints/ITask.cs b/MainCore/Constraints/ITask.cs index ab9195358..1da37c051 100644 --- a/MainCore/Constraints/ITask.cs +++ b/MainCore/Constraints/ITask.cs @@ -1,4 +1,4 @@ -namespace MainCore.Constraints +namespace MainCore.Constraints { public interface ITask : IConstraint { @@ -6,4 +6,4 @@ public interface ITask : IConstraint StageEnums Stage { get; set; } string Description { get; } } -} \ No newline at end of file +} diff --git a/MainCore/DTO/AccessDto.cs b/MainCore/DTO/AccessDto.cs index ff9aa5a8d..4f4b916ea 100644 --- a/MainCore/DTO/AccessDto.cs +++ b/MainCore/DTO/AccessDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.DTO { @@ -45,4 +45,4 @@ public static Access ToEntity(this AccessDto dto, AccountId accountId) private static AccessId ToAccessId(this int value) => new(value); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/AccountDetailDto.cs b/MainCore/DTO/AccountDetailDto.cs index 8b655f604..1406c5d05 100644 --- a/MainCore/DTO/AccountDetailDto.cs +++ b/MainCore/DTO/AccountDetailDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.DTO { @@ -59,4 +59,4 @@ public static AccountDto ToDto(this AccountDetailDto dto) [MapperIgnoreTarget(nameof(AccessDto.LastUsed))] private static partial AccessDto ToAccess(this AccountDetailDto dto); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/AccountDto.cs b/MainCore/DTO/AccountDto.cs index 2b2ea6968..cb18ccf2a 100644 --- a/MainCore/DTO/AccountDto.cs +++ b/MainCore/DTO/AccountDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.DTO { @@ -32,4 +32,4 @@ public static partial class AccountMapper private static AccessDto ToAccessDto(this Access entity) => entity.ToDto(); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/AccountInfoDto.cs b/MainCore/DTO/AccountInfoDto.cs index 78b54f12f..4ba8ea619 100644 --- a/MainCore/DTO/AccountInfoDto.cs +++ b/MainCore/DTO/AccountInfoDto.cs @@ -1,4 +1,4 @@ -namespace MainCore.DTO +namespace MainCore.DTO { public class AccountInfoDto { @@ -26,4 +26,4 @@ public static AccountInfo ToEntity(this AccountInfoDto dto, AccountId accountId) [MapperIgnoreTarget(nameof(AccountInfo.AccountId))] private static partial AccountInfo ToEntity(AccountInfoDto dto); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/AccountSettingDto.cs b/MainCore/DTO/AccountSettingDto.cs index d7e03477e..08560b442 100644 --- a/MainCore/DTO/AccountSettingDto.cs +++ b/MainCore/DTO/AccountSettingDto.cs @@ -1,4 +1,4 @@ -namespace MainCore.DTO +namespace MainCore.DTO { public class AccountSettingDto { @@ -22,4 +22,4 @@ public static AccountSetting ToEntity(this AccountSettingDto dto, AccountId acco [MapperIgnoreTarget(nameof(AccountSetting.AccountId))] private static partial AccountSetting ToEntity(this AccountSettingDto dto); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/BuildingDto.cs b/MainCore/DTO/BuildingDto.cs index 75fa95808..a49cc604c 100644 --- a/MainCore/DTO/BuildingDto.cs +++ b/MainCore/DTO/BuildingDto.cs @@ -1,4 +1,4 @@ -namespace MainCore.DTO +namespace MainCore.DTO { public class BuildingDto { @@ -33,4 +33,4 @@ public static Building ToEntity(this BuildingDto dto, VillageId villageId) private static BuildingId ToBuildingId(this int value) => new(value); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/FarmDto.cs b/MainCore/DTO/FarmDto.cs index 5119b9574..eaab9e39c 100644 --- a/MainCore/DTO/FarmDto.cs +++ b/MainCore/DTO/FarmDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.DTO { @@ -28,4 +28,4 @@ public static Farm ToEntity(this FarmDto dto, AccountId accountId) private static int ToInt(this FarmId farmId) => farmId.Value; } -} \ No newline at end of file +} diff --git a/MainCore/DTO/HeroItemDto.cs b/MainCore/DTO/HeroItemDto.cs index 19ee766a2..aafc7865d 100644 --- a/MainCore/DTO/HeroItemDto.cs +++ b/MainCore/DTO/HeroItemDto.cs @@ -1,4 +1,4 @@ -namespace MainCore.DTO +namespace MainCore.DTO { public class HeroItemDto { @@ -24,4 +24,4 @@ public static HeroItem ToEntity(this HeroItemDto dto, AccountId accountId) [MapperIgnoreTarget(nameof(HeroItem.AccountId))] private static partial HeroItem ToEntity(this HeroItemDto dto); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/JobDto.cs b/MainCore/DTO/JobDto.cs index 4153d6dd9..8411c2bd6 100644 --- a/MainCore/DTO/JobDto.cs +++ b/MainCore/DTO/JobDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using Humanizer; using System.Text.Json; @@ -58,4 +58,4 @@ public static string GetContent(this JobDto job) } } } -} \ No newline at end of file +} diff --git a/MainCore/DTO/QueueBuildingDto.cs b/MainCore/DTO/QueueBuildingDto.cs index 351efc184..066531b9e 100644 --- a/MainCore/DTO/QueueBuildingDto.cs +++ b/MainCore/DTO/QueueBuildingDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.DTO { @@ -33,4 +33,4 @@ public static QueueBuilding ToEntity(this QueueBuildingDto dto, VillageId villag private static BuildingEnums ToBuildingEnums(string str) => Enum.Parse(str); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/StorageDto.cs b/MainCore/DTO/StorageDto.cs index 9c1e28a09..36c1be604 100644 --- a/MainCore/DTO/StorageDto.cs +++ b/MainCore/DTO/StorageDto.cs @@ -1,4 +1,4 @@ -namespace MainCore.DTO +namespace MainCore.DTO { public class StorageDto { @@ -29,4 +29,4 @@ public static Storage ToEntity(this StorageDto dto, VillageId villageId) [MapperIgnoreTarget(nameof(Storage.VillageId))] private static partial Storage ToEntity(this StorageDto dto); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/VillageDto.cs b/MainCore/DTO/VillageDto.cs index 1aa2b956d..8eff7e33c 100644 --- a/MainCore/DTO/VillageDto.cs +++ b/MainCore/DTO/VillageDto.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.DTO { @@ -48,4 +48,4 @@ public static Village ToEntity(this VillageDto dto, AccountId accountId) private static VillageId ToVillageId(this int value) => new(value); } -} \ No newline at end of file +} diff --git a/MainCore/DTO/VillageSettingDto.cs b/MainCore/DTO/VillageSettingDto.cs index a3fcf4bcf..0243cac0d 100644 --- a/MainCore/DTO/VillageSettingDto.cs +++ b/MainCore/DTO/VillageSettingDto.cs @@ -1,4 +1,4 @@ -namespace MainCore.DTO +namespace MainCore.DTO { public class VillageSettingDto { @@ -22,4 +22,4 @@ public static VillageSetting ToEntity(this VillageSettingDto dto, VillageId vill public static partial VillageSettingDto ToDto(this VillageSetting dto); } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Access.cs b/MainCore/Entities/Access.cs index f7865c5d2..a8f69bab2 100644 --- a/MainCore/Entities/Access.cs +++ b/MainCore/Entities/Access.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; using System.ComponentModel.DataAnnotations.Schema; #nullable disable @@ -26,4 +26,4 @@ public class Access [StronglyTypedId] public partial struct AccessId { } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Account.cs b/MainCore/Entities/Account.cs index 12d434e05..69fb33f83 100644 --- a/MainCore/Entities/Account.cs +++ b/MainCore/Entities/Account.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; using System.ComponentModel.DataAnnotations.Schema; #nullable disable @@ -26,4 +26,4 @@ public class Account [StronglyTypedId] public partial struct AccountId { } -} \ No newline at end of file +} diff --git a/MainCore/Entities/AccountInfo.cs b/MainCore/Entities/AccountInfo.cs index 1ab87f19f..4bd7c99b2 100644 --- a/MainCore/Entities/AccountInfo.cs +++ b/MainCore/Entities/AccountInfo.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities { @@ -13,4 +13,4 @@ public class AccountInfo public bool HasPlusAccount { get; set; } public int AccountId { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Entities/AccountSetting.cs b/MainCore/Entities/AccountSetting.cs index b71cb8f02..5e02577cb 100644 --- a/MainCore/Entities/AccountSetting.cs +++ b/MainCore/Entities/AccountSetting.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities { @@ -12,4 +12,4 @@ public class AccountSetting public AccountSettingEnums Setting { get; set; } public int Value { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Building.cs b/MainCore/Entities/Building.cs index f6dc0ec60..a3a0d3a52 100644 --- a/MainCore/Entities/Building.cs +++ b/MainCore/Entities/Building.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities @@ -18,4 +18,4 @@ public class Building [StronglyTypedId] public partial struct BuildingId { } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Farm.cs b/MainCore/Entities/Farm.cs index 94bfd2c6b..ebf2cb9af 100644 --- a/MainCore/Entities/Farm.cs +++ b/MainCore/Entities/Farm.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; #nullable disable @@ -16,4 +16,4 @@ public class Farm [StronglyTypedId] public partial struct FarmId { } -} \ No newline at end of file +} diff --git a/MainCore/Entities/HeroItem.cs b/MainCore/Entities/HeroItem.cs index 694d99066..f13e981a8 100644 --- a/MainCore/Entities/HeroItem.cs +++ b/MainCore/Entities/HeroItem.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities { @@ -12,4 +12,4 @@ public class HeroItem public int Amount { get; set; } public int AccountId { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Job.cs b/MainCore/Entities/Job.cs index d60f7228a..5815ce6da 100644 --- a/MainCore/Entities/Job.cs +++ b/MainCore/Entities/Job.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; using System.ComponentModel.DataAnnotations.Schema; #nullable disable @@ -19,4 +19,4 @@ public class Job [StronglyTypedId] public partial struct JobId { } -} \ No newline at end of file +} diff --git a/MainCore/Entities/QueueBuilding.cs b/MainCore/Entities/QueueBuilding.cs index d23bf83d2..9cd321ff9 100644 --- a/MainCore/Entities/QueueBuilding.cs +++ b/MainCore/Entities/QueueBuilding.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities { @@ -16,4 +16,4 @@ public class QueueBuilding public int Level { get; set; } public DateTime CompleteTime { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Storage.cs b/MainCore/Entities/Storage.cs index 71a80d94a..53dd0af45 100644 --- a/MainCore/Entities/Storage.cs +++ b/MainCore/Entities/Storage.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities { @@ -16,4 +16,4 @@ public class Storage public long Granary { get; set; } public long FreeCrop { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Entities/Village.cs b/MainCore/Entities/Village.cs index 02b1be4fa..2b991c4f4 100644 --- a/MainCore/Entities/Village.cs +++ b/MainCore/Entities/Village.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; #nullable disable @@ -26,4 +26,4 @@ public class Village [StronglyTypedId] public partial struct VillageId { } -} \ No newline at end of file +} diff --git a/MainCore/Entities/VillageSetting.cs b/MainCore/Entities/VillageSetting.cs index 97d63c678..ddb3e2b48 100644 --- a/MainCore/Entities/VillageSetting.cs +++ b/MainCore/Entities/VillageSetting.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace MainCore.Entities { @@ -12,4 +12,4 @@ public class VillageSetting public VillageSettingEnums Setting { get; set; } public int Value { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Enums/AccountSettingEnums.cs b/MainCore/Enums/AccountSettingEnums.cs index e48ef55c2..83393178a 100644 --- a/MainCore/Enums/AccountSettingEnums.cs +++ b/MainCore/Enums/AccountSettingEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum AccountSettingEnums { @@ -18,4 +18,4 @@ public enum AccountSettingEnums HeadlessChrome, EnableAutoStartAdventure, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/AccountTabType.cs b/MainCore/Enums/AccountTabType.cs index 376250994..5a461d177 100644 --- a/MainCore/Enums/AccountTabType.cs +++ b/MainCore/Enums/AccountTabType.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum AccountTabType { @@ -7,4 +7,4 @@ public enum AccountTabType AddAccounts, Normal } -} \ No newline at end of file +} diff --git a/MainCore/Enums/BuildingEnums.cs b/MainCore/Enums/BuildingEnums.cs index c6dfbb2c3..7019729c8 100644 --- a/MainCore/Enums/BuildingEnums.cs +++ b/MainCore/Enums/BuildingEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum BuildingEnums { @@ -434,4 +434,4 @@ public static long[] GetCost(this BuildingEnums building, int level) return cost.Select(x => (long)x).ToArray(); } } -} \ No newline at end of file +} diff --git a/MainCore/Enums/ChangeStateEnums.cs b/MainCore/Enums/ChangeStateEnums.cs index 62106948e..179c7903e 100644 --- a/MainCore/Enums/ChangeStateEnums.cs +++ b/MainCore/Enums/ChangeStateEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum ChangeStateEnums { @@ -7,4 +7,4 @@ public enum ChangeStateEnums Delete, Clear, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/HeroItemEnums.cs b/MainCore/Enums/HeroItemEnums.cs index ecbce25aa..807b0f58f 100644 --- a/MainCore/Enums/HeroItemEnums.cs +++ b/MainCore/Enums/HeroItemEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum HeroItemEnums { @@ -163,4 +163,4 @@ public enum HeroItemEnums Iron, Crop } -} \ No newline at end of file +} diff --git a/MainCore/Enums/JobTypeEnums.cs b/MainCore/Enums/JobTypeEnums.cs index feb31165a..cd11058f5 100644 --- a/MainCore/Enums/JobTypeEnums.cs +++ b/MainCore/Enums/JobTypeEnums.cs @@ -1,8 +1,8 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum JobTypeEnums { NormalBuild, ResourceBuild, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/MoveEnums.cs b/MainCore/Enums/MoveEnums.cs index 8f91bfc57..c31bcdae0 100644 --- a/MainCore/Enums/MoveEnums.cs +++ b/MainCore/Enums/MoveEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum MoveEnums { @@ -7,4 +7,4 @@ public enum MoveEnums Down, Bottom, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/ResourcePlanEnums.cs b/MainCore/Enums/ResourcePlanEnums.cs index 440f7f433..834d3effb 100644 --- a/MainCore/Enums/ResourcePlanEnums.cs +++ b/MainCore/Enums/ResourcePlanEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum ResourcePlanEnums { @@ -6,4 +6,4 @@ public enum ResourcePlanEnums ExcludeCrop, OnlyCrop } -} \ No newline at end of file +} diff --git a/MainCore/Enums/StageEnums.cs b/MainCore/Enums/StageEnums.cs index 16d8edc15..f626cca25 100644 --- a/MainCore/Enums/StageEnums.cs +++ b/MainCore/Enums/StageEnums.cs @@ -1,8 +1,8 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum StageEnums { Waiting, Executing, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/StatusEnums.cs b/MainCore/Enums/StatusEnums.cs index fe1237210..2fdd5872e 100644 --- a/MainCore/Enums/StatusEnums.cs +++ b/MainCore/Enums/StatusEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum StatusEnums { @@ -26,4 +26,4 @@ public static SplatColor GetColor(this StatusEnums status) }; } } -} \ No newline at end of file +} diff --git a/MainCore/Enums/TribeEnums.cs b/MainCore/Enums/TribeEnums.cs index 41169351f..4008f3666 100644 --- a/MainCore/Enums/TribeEnums.cs +++ b/MainCore/Enums/TribeEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum TribeEnums { @@ -11,4 +11,4 @@ public enum TribeEnums Egyptians, Huns, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/TroopEnums.cs b/MainCore/Enums/TroopEnums.cs index bbf44f8ac..df1176d87 100644 --- a/MainCore/Enums/TroopEnums.cs +++ b/MainCore/Enums/TroopEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum TroopEnums { @@ -116,4 +116,4 @@ public static TribeEnums GetTribe(this TroopEnums troop) }; } } -} \ No newline at end of file +} diff --git a/MainCore/Enums/VillageSettingEnums.cs b/MainCore/Enums/VillageSettingEnums.cs index f1242c7ba..2c4e77019 100644 --- a/MainCore/Enums/VillageSettingEnums.cs +++ b/MainCore/Enums/VillageSettingEnums.cs @@ -1,4 +1,4 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum VillageSettingEnums { @@ -62,4 +62,4 @@ public enum VillageSettingEnums CompleteImmediatelyTime, } -} \ No newline at end of file +} diff --git a/MainCore/Enums/VillageTabType.cs b/MainCore/Enums/VillageTabType.cs index 66d1b2a35..efd9d8587 100644 --- a/MainCore/Enums/VillageTabType.cs +++ b/MainCore/Enums/VillageTabType.cs @@ -1,8 +1,8 @@ -namespace MainCore.Enums +namespace MainCore.Enums { public enum VillageTabType { NoVillage = 0, Normal } -} \ No newline at end of file +} diff --git a/MainCore/Errors/AccountDuplicate.cs b/MainCore/Errors/AccountDuplicate.cs index 85a659ed5..ba0245461 100644 --- a/MainCore/Errors/AccountDuplicate.cs +++ b/MainCore/Errors/AccountDuplicate.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class AccountDuplicate : Error { @@ -8,4 +8,4 @@ private AccountDuplicate() : base("Account is duplicated") public static AccountDuplicate Error => new(); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/Cancel.cs b/MainCore/Errors/Cancel.cs index c75766301..a1d242db3 100644 --- a/MainCore/Errors/Cancel.cs +++ b/MainCore/Errors/Cancel.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class Cancel : Error { @@ -8,4 +8,4 @@ private Cancel() : base("Pause button is pressed") public static Cancel Error => new(); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/LackOfFreeCrop.cs b/MainCore/Errors/LackOfFreeCrop.cs index 5289f8550..73e0df1b3 100644 --- a/MainCore/Errors/LackOfFreeCrop.cs +++ b/MainCore/Errors/LackOfFreeCrop.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class LackOfFreeCrop : Error { @@ -8,4 +8,4 @@ protected LackOfFreeCrop(long storage, long required) : base($"Don't have enough public static LackOfFreeCrop Error(long storage, long required) => new(storage, required); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/MissingBuilding.cs b/MainCore/Errors/MissingBuilding.cs index a136063a0..b888b52f3 100644 --- a/MainCore/Errors/MissingBuilding.cs +++ b/MainCore/Errors/MissingBuilding.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class MissingBuilding : Error { @@ -8,4 +8,4 @@ private MissingBuilding(BuildingEnums building) : base($"{building} is missing." public static MissingBuilding Error(BuildingEnums building) => new(building); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/MissingResource.cs b/MainCore/Errors/MissingResource.cs index d2bea0ed4..cc11d9543 100644 --- a/MainCore/Errors/MissingResource.cs +++ b/MainCore/Errors/MissingResource.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class MissingResource : Error { @@ -22,4 +22,4 @@ private MissingResource(string resource, long storage, long required) : base($"D public static MissingResource Crop(long storage, long required) => new("Crop", storage, required); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/NextExecuteError.cs b/MainCore/Errors/NextExecuteError.cs index c702f72d6..398014f57 100644 --- a/MainCore/Errors/NextExecuteError.cs +++ b/MainCore/Errors/NextExecuteError.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class NextExecuteError : Error { @@ -13,5 +13,8 @@ public static NextExecuteError ConstructionQueueFull(DateTime nextExecute) public static NextExecuteError PrerequisiteBuildingInQueue(BuildingEnums prerequisiteBuilding, int level, DateTime completeTime) => new($"{prerequisiteBuilding} level {level} is in queue") { NextExecute = completeTime }; + + public static NextExecuteError MissingResource(DateTime nextExecute) + => new("Missing resource for building") { NextExecute = nextExecute }; } -} \ No newline at end of file +} diff --git a/MainCore/Errors/Retry.cs b/MainCore/Errors/Retry.cs index 43effb8ef..e4b1bd362 100644 --- a/MainCore/Errors/Retry.cs +++ b/MainCore/Errors/Retry.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class Retry : Error { @@ -20,4 +20,4 @@ private Retry(string message) : base($"{message}. Bot must retry") public static Retry OutOfIndexTab(int index, int count) => new($"Found {count} tabs but need tab {index + 1} active"); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/Skip.cs b/MainCore/Errors/Skip.cs index 16b9be187..acce7cae4 100644 --- a/MainCore/Errors/Skip.cs +++ b/MainCore/Errors/Skip.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class Skip : Error { @@ -19,4 +19,4 @@ private Skip(string message) : base(message) public static Skip OverflowNPC => new("Overflow NPC resources. Bot won't npc to save gold"); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/Stop.cs b/MainCore/Errors/Stop.cs index cfe3d0476..204bef001 100644 --- a/MainCore/Errors/Stop.cs +++ b/MainCore/Errors/Stop.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class Stop : Error { @@ -16,7 +16,5 @@ private Stop(string message) : base($"{message}. Bot must stop") public static Stop AllAccessNotWorking => new("All accesses not working"); public static Stop LackOfAccess => new("Last access is reused, it may get MH's attention"); - - public static Stop DriverNotReady => new("Driver is not ready."); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/StorageLimit.cs b/MainCore/Errors/StorageLimit.cs index 8ba057349..05b79df54 100644 --- a/MainCore/Errors/StorageLimit.cs +++ b/MainCore/Errors/StorageLimit.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class StorageLimit : Error { @@ -10,4 +10,4 @@ protected StorageLimit(string type, long storage, long required) : base($"{type} public static StorageLimit Granary(long storage, long required) => new("Granary", storage, required); } -} \ No newline at end of file +} diff --git a/MainCore/Errors/UpgradeBuildingError.cs b/MainCore/Errors/UpgradeBuildingError.cs index f76dfa7f5..ce38f9053 100644 --- a/MainCore/Errors/UpgradeBuildingError.cs +++ b/MainCore/Errors/UpgradeBuildingError.cs @@ -1,4 +1,4 @@ -namespace MainCore.Errors +namespace MainCore.Errors { public class UpgradeBuildingError : Error { @@ -18,4 +18,4 @@ public static UpgradeBuildingError JobNotAvailable(string type) public static UpgradeBuildingError PrerequisiteBuildingMissing(BuildingEnums prerequisiteBuilding, int level) => new($"{prerequisiteBuilding} level {level} is missing"); } -} \ No newline at end of file +} diff --git a/MainCore/Infrasturecture/Persistence/AppDbContext.cs b/MainCore/Infrasturecture/Persistence/AppDbContext.cs index fac461af0..2689dffa4 100644 --- a/MainCore/Infrasturecture/Persistence/AppDbContext.cs +++ b/MainCore/Infrasturecture/Persistence/AppDbContext.cs @@ -1,4 +1,4 @@ -using StronglyTypedIds; +using StronglyTypedIds; using System.Collections.Immutable; [assembly: StronglyTypedIdDefaults(backingType: StronglyTypedIdBackingType.Int, converters: StronglyTypedIdConverter.None)] @@ -225,4 +225,4 @@ public void FillVillageSettings(AccountId accountId, VillageId villageId) #endregion village setting } -} \ No newline at end of file +} diff --git a/MainCore/MainCore.csproj b/MainCore/MainCore.csproj index 72676fb2a..a3cb9d2cd 100644 --- a/MainCore/MainCore.csproj +++ b/MainCore/MainCore.csproj @@ -44,8 +44,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/MainCore/Models/BrowserSetting.cs b/MainCore/Models/BrowserSetting.cs new file mode 100644 index 000000000..9344d28ec --- /dev/null +++ b/MainCore/Models/BrowserSetting.cs @@ -0,0 +1,20 @@ +namespace MainCore.Models +{ + public class BrowserSetting + { + public required string ProfilePath { get; set; } + public string? ProxyHost { get; set; } + public int ProxyPort { get; set; } + public string? ProxyUsername { get; set; } + public string? ProxyPassword { get; set; } + public string? UserAgent { get; set; } + public bool IsHeadless { get; set; } + public BrowserType BrowserType { get; set; } = BrowserType.Firefox; + } + + public enum BrowserType + { + Chrome, + Firefox + } +} diff --git a/MainCore/Models/BuildingItem.cs b/MainCore/Models/BuildingItem.cs index ab2d32752..5b5c5fb88 100644 --- a/MainCore/Models/BuildingItem.cs +++ b/MainCore/Models/BuildingItem.cs @@ -1,4 +1,4 @@ -namespace MainCore.Models +namespace MainCore.Models { public class BuildingItem { @@ -10,6 +10,11 @@ public class BuildingItem public int QueueLevel { get; set; } public int JobLevel { get; set; } - public int Level => Math.Max(Math.Max(CurrentLevel, QueueLevel), JobLevel); + // Resource planning için mevcut gerçek seviyeyi kullan, queue seviyesini deðil + // Çünkü queue henüz tamamlanmamýþ inþayý temsil ediyor + public int Level => Math.Max(CurrentLevel, JobLevel); + + // UI gösterimi için tüm seviyeleri göster + public int DisplayLevel => Math.Max(Math.Max(CurrentLevel, QueueLevel), JobLevel); } -} \ No newline at end of file +} diff --git a/MainCore/Models/NormalBuildPlan.cs b/MainCore/Models/NormalBuildPlan.cs index e4d94861b..3663312ca 100644 --- a/MainCore/Models/NormalBuildPlan.cs +++ b/MainCore/Models/NormalBuildPlan.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; namespace MainCore.Models { @@ -13,4 +13,4 @@ public override string ToString() return $"{Type.Humanize()} at slot {Location} to level {Level}"; } } -} \ No newline at end of file +} diff --git a/MainCore/Models/PrerequisiteBuilding.cs b/MainCore/Models/PrerequisiteBuilding.cs index e08405d3d..4be6888cd 100644 --- a/MainCore/Models/PrerequisiteBuilding.cs +++ b/MainCore/Models/PrerequisiteBuilding.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; namespace MainCore.Models { @@ -18,4 +18,4 @@ public override string ToString() return $"{Type.Humanize()} level {Level}"; } } -} \ No newline at end of file +} diff --git a/MainCore/Models/ResourceBuildPlan.cs b/MainCore/Models/ResourceBuildPlan.cs index be3b5f651..c58b3130c 100644 --- a/MainCore/Models/ResourceBuildPlan.cs +++ b/MainCore/Models/ResourceBuildPlan.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; namespace MainCore.Models { @@ -12,4 +12,4 @@ public override string ToString() return $"{Plan.Humanize()} to level {Level}"; } } -} \ No newline at end of file +} diff --git a/MainCore/Notifications/AccountInit.cs b/MainCore/Notifications/AccountInit.cs index 6361baf67..dc5b6f5e0 100644 --- a/MainCore/Notifications/AccountInit.cs +++ b/MainCore/Notifications/AccountInit.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record AccountInit(AccountId AccountId) : IAccountNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/AccountsModified.cs b/MainCore/Notifications/AccountsModified.cs index fe7693d5e..a605fa906 100644 --- a/MainCore/Notifications/AccountsModified.cs +++ b/MainCore/Notifications/AccountsModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record AccountsModified : INotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/BuildingsModified.cs b/MainCore/Notifications/BuildingsModified.cs index 05669f8f9..28cef6803 100644 --- a/MainCore/Notifications/BuildingsModified.cs +++ b/MainCore/Notifications/BuildingsModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record BuildingsModified(VillageId VillageId) : IVillageNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/FarmsModified.cs b/MainCore/Notifications/FarmsModified.cs index c86c318d4..72a16afb0 100644 --- a/MainCore/Notifications/FarmsModified.cs +++ b/MainCore/Notifications/FarmsModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record FarmsModified(AccountId AccountId) : IAccountNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/JobsModified.cs b/MainCore/Notifications/JobsModified.cs index 0950978db..c7089f3ae 100644 --- a/MainCore/Notifications/JobsModified.cs +++ b/MainCore/Notifications/JobsModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record JobsModified(VillageId VillageId) : IVillageNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/LogEmitted.cs b/MainCore/Notifications/LogEmitted.cs index ceaf8a38f..ed06fc47b 100644 --- a/MainCore/Notifications/LogEmitted.cs +++ b/MainCore/Notifications/LogEmitted.cs @@ -1,6 +1,6 @@ -using Serilog.Events; +using Serilog.Events; namespace MainCore.Notifications { public record LogEmitted(AccountId AccountId, LogEvent LogEvent) : IAccountNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/StatusModified.cs b/MainCore/Notifications/StatusModified.cs index 80edcba14..3b08cb1ce 100644 --- a/MainCore/Notifications/StatusModified.cs +++ b/MainCore/Notifications/StatusModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record StatusModified(AccountId AccountId, StatusEnums Status) : IAccountNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/TasksModified.cs b/MainCore/Notifications/TasksModified.cs index 07cdb5c83..13d2321c5 100644 --- a/MainCore/Notifications/TasksModified.cs +++ b/MainCore/Notifications/TasksModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record TasksModified(AccountId AccountId) : IAccountNotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/VillageTaskAdded.cs b/MainCore/Notifications/VillageTaskAdded.cs index 75082ba1c..b5175968c 100644 --- a/MainCore/Notifications/VillageTaskAdded.cs +++ b/MainCore/Notifications/VillageTaskAdded.cs @@ -1,6 +1,6 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.Notifications { public record VillageTaskAdded(VillageTask Task) : INotification; -} \ No newline at end of file +} diff --git a/MainCore/Notifications/VillagesModified.cs b/MainCore/Notifications/VillagesModified.cs index 3ec18a531..7e9a48715 100644 --- a/MainCore/Notifications/VillagesModified.cs +++ b/MainCore/Notifications/VillagesModified.cs @@ -1,4 +1,4 @@ -namespace MainCore.Notifications +namespace MainCore.Notifications { public record VillagesModified(AccountId AccountId) : IAccountNotification; -} \ No newline at end of file +} diff --git a/MainCore/Parsers/AdventureParser.cs b/MainCore/Parsers/AdventureParser.cs index b54a7e793..53a525a83 100644 --- a/MainCore/Parsers/AdventureParser.cs +++ b/MainCore/Parsers/AdventureParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class AdventureParser { @@ -102,4 +102,4 @@ private static string GetAdventureCoordinates(HtmlNode node) return tdList[1].InnerText; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/BuildingLayoutParser.cs b/MainCore/Parsers/BuildingLayoutParser.cs index 7d0bc5f99..c213cdccb 100644 --- a/MainCore/Parsers/BuildingLayoutParser.cs +++ b/MainCore/Parsers/BuildingLayoutParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class BuildingLayoutParser { @@ -168,4 +168,4 @@ static TimeSpan GetDuration(HtmlNode node) } } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/BuildingTabParser.cs b/MainCore/Parsers/BuildingTabParser.cs index ba915af2e..cb4dfa2de 100644 --- a/MainCore/Parsers/BuildingTabParser.cs +++ b/MainCore/Parsers/BuildingTabParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class BuildingTabParser { @@ -40,4 +40,4 @@ public static bool IsTabActive(HtmlNode node) return node.HasClass("active"); } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/CompleteImmediatelyParser.cs b/MainCore/Parsers/CompleteImmediatelyParser.cs index bbf5d2ba5..39a473890 100644 --- a/MainCore/Parsers/CompleteImmediatelyParser.cs +++ b/MainCore/Parsers/CompleteImmediatelyParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class CompleteImmediatelyParser { @@ -38,4 +38,4 @@ public static int CountQueueBuilding(HtmlDocument doc) return confirmFinishbutton; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/FarmListParser.cs b/MainCore/Parsers/FarmListParser.cs index d4cfb6b0a..d119f9fc3 100644 --- a/MainCore/Parsers/FarmListParser.cs +++ b/MainCore/Parsers/FarmListParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class FarmListParser { @@ -62,4 +62,4 @@ public static string GetName(HtmlNode node) return startAllFarmListButton; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/InfoParser.cs b/MainCore/Parsers/InfoParser.cs index a3387cda6..203e7ee3c 100644 --- a/MainCore/Parsers/InfoParser.cs +++ b/MainCore/Parsers/InfoParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class InfoParser { @@ -28,4 +28,4 @@ public static bool HasPlusAccount(HtmlDocument doc) return false; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/InventoryParser.cs b/MainCore/Parsers/InventoryParser.cs index 9735386bf..73115681e 100644 --- a/MainCore/Parsers/InventoryParser.cs +++ b/MainCore/Parsers/InventoryParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class InventoryParser { @@ -81,4 +81,4 @@ public static bool IsInventoryLoaded(HtmlDocument doc) return buttonTransfer.ElementAt(1); } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/LoginParser.cs b/MainCore/Parsers/LoginParser.cs index 81a8de17d..ca17b5bbe 100644 --- a/MainCore/Parsers/LoginParser.cs +++ b/MainCore/Parsers/LoginParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class LoginParser { @@ -42,4 +42,4 @@ public static bool IsLoginPage(HtmlDocument doc) return loginButton is not null; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/NavigationBarParser.cs b/MainCore/Parsers/NavigationBarParser.cs index dc9e7ff1d..bc0b7616e 100644 --- a/MainCore/Parsers/NavigationBarParser.cs +++ b/MainCore/Parsers/NavigationBarParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class NavigationBarParser { @@ -15,11 +15,33 @@ public static class NavigationBarParser private static HtmlNode? GetButton(HtmlDocument doc, int key) { var navigationBar = doc.GetElementbyId("navigation"); - if (navigationBar is null) return null; + if (navigationBar is null) + { + // Debug: Navigation bar bulunamadı + Console.WriteLine($"[DEBUG] NavigationBarParser: Navigation bar not found for key={key}"); + return null; + } + + var buttons = navigationBar.Descendants("a").ToList(); + Console.WriteLine($"[DEBUG] NavigationBarParser: Found {buttons.Count} buttons in navigation bar"); + + foreach (var btn in buttons) + { + var accesskey = btn.GetAttributeValue("accesskey", 0); + var href = btn.GetAttributeValue("href", ""); + Console.WriteLine($"[DEBUG] NavigationBarParser: Button accesskey={accesskey}, href={href}"); + } - var button = navigationBar - .Descendants("a") - .FirstOrDefault(x => x.GetAttributeValue("accesskey", 0) == key); + var button = buttons.FirstOrDefault(x => x.GetAttributeValue("accesskey", 0) == key); + if (button is null) + { + Console.WriteLine($"[DEBUG] NavigationBarParser: Button with accesskey={key} not found"); + } + else + { + Console.WriteLine($"[DEBUG] NavigationBarParser: Button with accesskey={key} found"); + } + return button; } @@ -27,4 +49,4 @@ public static class NavigationBarParser private static HtmlNode? GetBuildingButton(HtmlDocument doc) => GetButton(doc, 2); } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/NpcResourceParser.cs b/MainCore/Parsers/NpcResourceParser.cs index a522487fb..dd97f378f 100644 --- a/MainCore/Parsers/NpcResourceParser.cs +++ b/MainCore/Parsers/NpcResourceParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class NpcResourceParser { @@ -73,4 +73,4 @@ public static IEnumerable GetInputs(HtmlDocument doc) yield return crop; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/NumberParser.cs b/MainCore/Parsers/NumberParser.cs index e4adfc9fa..199d43131 100644 --- a/MainCore/Parsers/NumberParser.cs +++ b/MainCore/Parsers/NumberParser.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; namespace MainCore.Parsers { @@ -27,8 +27,8 @@ private static string Normalized(this string value) var valueStrDecoded = WebUtility.HtmlDecode(value); if (string.IsNullOrEmpty(valueStrDecoded)) return ""; - var valueStr = new string(valueStrDecoded.Where(c => char.IsDigit(c) || c == '-' || c == '−').ToArray()); - valueStr = valueStr.Replace('−', '-'); + var valueStr = new string(valueStrDecoded.Where(c => char.IsDigit(c) || c == '-' || c == '-').ToArray()); + valueStr = valueStr.Replace('-', '-'); if (string.IsNullOrEmpty(valueStr)) return ""; return valueStr; @@ -48,4 +48,4 @@ public static long ParseLong(this string value) return long.Parse(normValue); } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/OptionParser.cs b/MainCore/Parsers/OptionParser.cs index 75d921c43..65c61697c 100644 --- a/MainCore/Parsers/OptionParser.cs +++ b/MainCore/Parsers/OptionParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class OptionParser { @@ -37,4 +37,4 @@ public static HtmlNode GetHideContextualHelpOption(HtmlDocument doc) return submitButton; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/QuestParser.cs b/MainCore/Parsers/QuestParser.cs index 8d3529d71..f38e52868 100644 --- a/MainCore/Parsers/QuestParser.cs +++ b/MainCore/Parsers/QuestParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class QuestParser { @@ -40,4 +40,4 @@ public static bool IsQuestPage(HtmlDocument doc) return table; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/StorageParser.cs b/MainCore/Parsers/StorageParser.cs index b623885e3..94f434885 100644 --- a/MainCore/Parsers/StorageParser.cs +++ b/MainCore/Parsers/StorageParser.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; namespace MainCore.Parsers { @@ -49,4 +49,4 @@ public static long GetGranaryCapacity(HtmlDocument doc) return valueNode.InnerText.ParseLong(); } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/TrainTroopParser.cs b/MainCore/Parsers/TrainTroopParser.cs index 44998d263..0fa8d5145 100644 --- a/MainCore/Parsers/TrainTroopParser.cs +++ b/MainCore/Parsers/TrainTroopParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class TrainTroopParser { @@ -10,8 +10,26 @@ public static class TrainTroopParser .FirstOrDefault(x => x.HasClass("cta")); if (cta is null) return null; + // Önce class="text" olan input'u ara var input = cta.Descendants("input") .FirstOrDefault(x => x.HasClass("text")); + + // EÄŸer bulunamazsa, name attribute'u ile ara (t1, t2, t3, t4 gibi) + if (input is null) + { + var troopIndex = (int)troop; + var nameAttribute = $"t{troopIndex}"; + input = cta.Descendants("input") + .FirstOrDefault(x => x.GetAttributeValue("name", "") == nameAttribute); + } + + // Hala bulunamazsa, cta içindeki herhangi bir input'u al + if (input is null) + { + input = cta.Descendants("input") + .FirstOrDefault(x => x.GetAttributeValue("type", "") == "text"); + } + return input; } @@ -34,6 +52,170 @@ public static HtmlNode GetTrainButton(HtmlDocument doc) return doc.GetElementbyId("s1"); } + // Alternatif input bulma yöntemi - yeni HTML yapısına göre + public static HtmlNode? GetInputBoxAlternative(HtmlDocument doc, TroopEnums troop) + { + // Önce yeni yapıya göre ara: nonFavouriteTroops veya favouriteTroops + var nonFavouriteTroops = doc.GetElementbyId("nonFavouriteTroops"); + var favouriteTroops = doc.GetElementbyId("favouriteTroops"); + + // Troop enum'ına göre hangi bölümde olduÄŸunu belirle + var troopIndex = (int)troop; + var troopDiv = GetTroopDiv(nonFavouriteTroops, troop) ?? GetTroopDiv(favouriteTroops, troop); + + if (troopDiv is not null) + { + // Yeni yapıya göre input'u bul: div/div[2]/div[4]/input + var input = troopDiv.Descendants("input") + .FirstOrDefault(x => x.GetAttributeValue("type", "") == "text"); + + if (input is not null) return input; + } + + // Fallback: Eski yöntem - form içindeki name attribute ile ara + var form = doc.DocumentNode.Descendants("form").FirstOrDefault(); + if (form is not null) + { + var nameAttribute = $"t{troopIndex}"; + var input = form.Descendants("input") + .FirstOrDefault(x => x.GetAttributeValue("name", "") == nameAttribute); + + if (input is not null) return input; + } + + return null; + } + + // Troop div'ini bulma yardımcı metodu + private static HtmlNode? GetTroopDiv(HtmlNode? container, TroopEnums troop) + { + if (container is null) return null; + + var troopIndex = (int)troop; + var troopDivs = container.Descendants("div") + .Where(x => x.HasClass("action") && x.HasClass("troop")) + .ToList(); + + // Troop index'e göre doÄŸru div'i bul + if (troopDivs.Count > troopIndex - 1) + { + return troopDivs[troopIndex - 1]; + } + + return null; + } + + // Yeni HTML yapısına özel input bulma metodu + public static HtmlNode? GetInputBoxNewStructure(HtmlDocument doc, TroopEnums troop) + { + // Troop enum deÄŸerini HTML name attribute'una çevir + var troopIndex = GetTroopIndex(troop); + if (troopIndex == -1) return null; + + // Önce nonFavouriteTroops'ta ara + var nonFavouriteTroops = doc.GetElementbyId("nonFavouriteTroops"); + if (nonFavouriteTroops is not null) + { + var troopDivs = nonFavouriteTroops.Descendants("div") + .Where(x => x.HasClass("action") && x.HasClass("troop")) + .ToList(); + + if (troopDivs.Count > troopIndex - 1) + { + var input = troopDivs[troopIndex - 1].Descendants("input") + .FirstOrDefault(x => x.GetAttributeValue("type", "") == "text"); + if (input is not null) return input; + } + } + + // Sonra favouriteTroops'ta ara + var favouriteTroops = doc.GetElementbyId("favouriteTroops"); + if (favouriteTroops is not null) + { + var troopDivs = favouriteTroops.Descendants("div") + .Where(x => x.HasClass("action") && x.HasClass("troop")) + .ToList(); + + if (troopDivs.Count > troopIndex - 1) + { + var input = troopDivs[troopIndex - 1].Descendants("input") + .FirstOrDefault(x => x.GetAttributeValue("type", "") == "text"); + if (input is not null) return input; + } + } + + return null; + } + + // Troop enum deÄŸerini HTML name attribute'una çeviren yardımcı metod + private static int GetTroopIndex(TroopEnums troop) + { + return troop switch + { + // Romans + TroopEnums.Legionnaire => 1, + TroopEnums.Praetorian => 2, + TroopEnums.Imperian => 3, + TroopEnums.EquitesLegati => 4, + TroopEnums.EquitesImperatoris => 5, + TroopEnums.EquitesCaesaris => 6, + TroopEnums.RomanRam => 7, + TroopEnums.RomanCatapult => 8, + TroopEnums.RomanChief => 9, + TroopEnums.RomanSettler => 10, + + // Teutons + TroopEnums.Clubswinger => 1, + TroopEnums.Spearman => 2, + TroopEnums.Axeman => 3, + TroopEnums.Scout => 4, + TroopEnums.Paladin => 5, + TroopEnums.TeutonicKnight => 6, + TroopEnums.TeutonRam => 7, + TroopEnums.TeutonCatapult => 8, + TroopEnums.TeutonChief => 9, + TroopEnums.TeutonSettler => 10, + + // Gauls + TroopEnums.Phalanx => 1, + TroopEnums.Swordsman => 2, + TroopEnums.Pathfinder => 3, + TroopEnums.TheutatesThunder => 4, + TroopEnums.Druidrider => 5, + TroopEnums.Haeduan => 6, + TroopEnums.GaulRam => 7, + TroopEnums.GaulCatapult => 8, + TroopEnums.GaulChief => 9, + TroopEnums.GaulSettler => 10, + + // Egyptians + TroopEnums.SlaveMilitia => 1, + TroopEnums.AshWarden => 2, + TroopEnums.KhopeshWarrior => 3, + TroopEnums.SopduExplorer => 4, + TroopEnums.AnhurGuard => 5, + TroopEnums.ReshephChariot => 6, + TroopEnums.EgyptianRam => 7, + TroopEnums.EgyptianCatapult => 8, + TroopEnums.EgyptianChief => 9, + TroopEnums.EgyptianSettler => 10, + + // Huns + TroopEnums.Mercenary => 1, + TroopEnums.Bowman => 2, + TroopEnums.Spotter => 3, + TroopEnums.SteppeRider => 4, + TroopEnums.Marksman => 5, + TroopEnums.Marauder => 6, + TroopEnums.HunRam => 7, + TroopEnums.HunCatapult => 8, + TroopEnums.HunChief => 9, + TroopEnums.HunSettler => 10, + + _ => -1 + }; + } + private static HtmlNode? GetNode(HtmlDocument doc, TroopEnums troop) { var nodes = doc.DocumentNode.Descendants("div") @@ -56,4 +238,4 @@ public static HtmlNode GetTrainButton(HtmlDocument doc) return null; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/UpgradeParser.cs b/MainCore/Parsers/UpgradeParser.cs index 9ae11e565..acbf31192 100644 --- a/MainCore/Parsers/UpgradeParser.cs +++ b/MainCore/Parsers/UpgradeParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class UpgradeParser { @@ -46,13 +46,53 @@ public static TimeSpan GetTimeWhenEnoughResource(HtmlDocument doc, BuildingEnums { if (building.IsResourceField()) return GetUpgradeButton(doc); + // Önce spesifik bina kontrat alanýný arýyorum var contract_building = doc.GetElementbyId($"contract_building{(int)building}"); - if (contract_building is null) return null; + if (contract_building is not null) + { + var button = contract_building + .Descendants("button") + .FirstOrDefault(x => x.HasClass("new")); + if (button is not null) return button; + } - var button = contract_building - .Descendants("button") - .FirstOrDefault(x => x.HasClass("new")); - return button; + // Spesifik bulamadýysam, genel contract alanýnda spesifik binayý arýyorum + var contract = doc.GetElementbyId("contract"); + if (contract is not null) + { + // Bina ismini ve ID'sini kullanarak doðru butonu buluyorum + var buildingId = (int)building; + var buildingName = building.ToString().ToLower(); + + // Önce onclick attribute'unda bina ID'si olan buton arýyorum + var specificButton = contract + .Descendants("button") + .FirstOrDefault(x => x.HasClass("new") && + (x.GetAttributeValue("onclick", "").Contains($"gid={buildingId}") || + x.GetAttributeValue("onclick", "").Contains($"gid%3D{buildingId}"))); + + if (specificButton is not null) return specificButton; + + // Alternatif olarak form action'ýnda bina ID'si olan buton arýyorum + var formButton = contract + .Descendants("button") + .Where(x => x.HasClass("new")) + .FirstOrDefault(x => + { + var form = x.Ancestors("form").FirstOrDefault(); + return form?.GetAttributeValue("action", "").Contains($"gid={buildingId}") == true; + }); + + if (formButton is not null) return formButton; + + // Son çare: genel "new" butonu (eski davranýþ) + var generalButton = contract + .Descendants("button") + .FirstOrDefault(x => x.HasClass("new")); + if (generalButton is not null) return generalButton; + } + + return null; } public static HtmlNode? GetSpecialUpgradeButton(HtmlDocument doc) @@ -79,4 +119,4 @@ public static TimeSpan GetTimeWhenEnoughResource(HtmlDocument doc, BuildingEnums return button; } } -} \ No newline at end of file +} diff --git a/MainCore/Parsers/VillagePanelParser.cs b/MainCore/Parsers/VillagePanelParser.cs index ce00a786d..b797a499e 100644 --- a/MainCore/Parsers/VillagePanelParser.cs +++ b/MainCore/Parsers/VillagePanelParser.cs @@ -1,4 +1,4 @@ -namespace MainCore.Parsers +namespace MainCore.Parsers { public static class VillagePanelParser { @@ -109,4 +109,4 @@ private static int GetY(HtmlNode node) return yNode.InnerText.ParseInt(); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/BrowserManager.cs b/MainCore/Services/BrowserManager.cs new file mode 100644 index 000000000..d3082b974 --- /dev/null +++ b/MainCore/Services/BrowserManager.cs @@ -0,0 +1,51 @@ +using System.Collections.Concurrent; + +namespace MainCore.Services +{ + [RegisterSingleton] + public sealed class BrowserManager(ILogger logger) : IBrowserManager + { + private readonly ILogger _logger = logger.ForContext(); + private readonly ConcurrentDictionary _dictionary = new(); + private string[] _extensionsPath = default!; + + public IBrowser Get(AccountId accountId) + { + if (_dictionary.TryGetValue(accountId, out IBrowser? browser)) + { + return browser; + } + + // Firefox browser'ı varsayılan olarak kullan + browser = new FirefoxBrowser(_extensionsPath); + _dictionary.TryAdd(accountId, browser); + return browser; + } + + public async Task Shutdown() + { + foreach (var id in _dictionary.Keys) + { + if (_dictionary.Remove(id, out IBrowser? browser)) + { + await browser.Shutdown(); + } + } + } + + public void LoadExtension() + { + var extenstionDir = Path.Combine(AppContext.BaseDirectory, "ExtensionFile"); + if (!Directory.Exists(extenstionDir)) + { + _logger.Warning("Extension directory not found: {ExtensionDir}", extenstionDir); + _extensionsPath = Array.Empty(); + return; + } + + var extensions = Directory.GetFiles(extenstionDir, "*.crx", SearchOption.AllDirectories); + _extensionsPath = extensions; + _logger.Information("Loaded {ExtensionCount} extensions", extensions.Length); + } + } +} diff --git a/MainCore/Services/ChromeBrowser.cs b/MainCore/Services/ChromeBrowser.cs index e54bc61fe..250c2b53a 100644 --- a/MainCore/Services/ChromeBrowser.cs +++ b/MainCore/Services/ChromeBrowser.cs @@ -1,11 +1,11 @@ -using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.UI; using System.IO.Compression; namespace MainCore.Services { - public sealed class ChromeBrowser : IChromeBrowser + public sealed class ChromeBrowser : IBrowser { private ChromeDriver? _driver; private readonly ChromeDriverService _chromeService; @@ -22,7 +22,7 @@ public ChromeBrowser(string[] extensionsPath) _chromeService.HideCommandPromptWindow = true; } - public async Task Setup(ChromeSetting setting) + public async Task Setup(BrowserSetting setting) { var options = new ChromeOptions(); @@ -74,10 +74,11 @@ public async Task Setup(ChromeSetting setting) _driver = await Task.Run(() => new ChromeDriver(_chromeService, options, TimeSpan.FromMinutes(3))); _driver.Manage().Timeouts().PageLoad = TimeSpan.FromMinutes(3); - _wait = new WebDriverWait(_driver, TimeSpan.FromMinutes(3)); // watch ads + // 120 saniye timeout - job validation sorunlar� i�in optimize edildi + _wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(120)); // building operations } - public ChromeDriver? Driver => _driver; + public IWebDriver Driver => _driver!; public HtmlDocument Html { @@ -95,22 +96,21 @@ public async Task Shutdown() _chromeService.Dispose(); } - public string CurrentUrl => Driver?.Url ?? ""; + public string CurrentUrl => Driver.Url; public ILogger Logger { get; set; } = null!; public async Task Screenshot() { - var screenshot = Driver?.GetScreenshot(); + var screenshot = ((ITakesScreenshot)Driver).GetScreenshot(); var fileName = Path.Combine(AppContext.BaseDirectory, "Screenshots", $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png"); Directory.CreateDirectory(Path.GetDirectoryName(fileName)!); - await File.WriteAllBytesAsync(fileName, screenshot?.AsByteArray ?? Array.Empty(), CancellationToken.None); + await File.WriteAllBytesAsync(fileName, screenshot.AsByteArray, CancellationToken.None); return fileName; } public async Task Refresh(CancellationToken cancellationToken) { - if (Driver is null) return Stop.DriverNotReady; await Driver.Navigate().RefreshAsync(); var result = await WaitPageLoaded(cancellationToken); return result; @@ -122,55 +122,27 @@ public async Task Refresh(CancellationToken cancellationToken) public async Task Navigate(string url, CancellationToken cancellationToken) { - if (Driver is null) return Stop.DriverNotReady; await Driver.Navigate().GoToUrlAsync(url); var result = await Wait(driver => PageChanged(driver, url), cancellationToken); return result; } - private async Task> GetElement(By by, CancellationToken cancellationToken) + public async Task Click(By by) { - IWebElement wait() - { - var element = _wait.Until((driver) => - { - var elements = driver.FindElements(by); - if (elements.Count == 0) return null; - var element = elements[0]; - if (!element.Displayed || !element.Enabled) return null; - return element; - }, cancellationToken); - return element; - } - - try - { - var element = await Task.Run(wait, cancellationToken); - return Result.Ok(element); - } - catch (OperationCanceledException) - { - return Cancel.Error; - } - catch (WebDriverTimeoutException ex) - { - return Retry.BrowserTimeout(ex.Message); - } - } - - public async Task Click(By by, CancellationToken cancellationToken) - { - if (Driver is null) return Stop.DriverNotReady; - var (_, isFailed, element, errors) = await GetElement(by, cancellationToken); - if (isFailed) return Result.Fail(errors); + var elements = Driver.FindElements(by); + if (elements.Count == 0) return Retry.ElementNotFound(by); + var element = elements[0]; + if (!element.Displayed || !element.Enabled) return Retry.ElementNotClickable(by); await Task.Run(new Actions(Driver).Click(element).Perform); return Result.Ok(); } - public async Task Input(By by, string content, CancellationToken cancellationToken) + public async Task Input(By by, string content) { - var (_, isFailed, element, errors) = await GetElement(by, cancellationToken); - if (isFailed) return Result.Fail(errors); + var elements = Driver.FindElements(by); + if (elements.Count == 0) return Retry.ElementNotFound(by); + var element = elements[0]; + if (!element.Displayed || !element.Enabled) return Retry.ElementNotClickable(by); void input() { @@ -185,10 +157,9 @@ void input() public async Task ExecuteJsScript(string javascript) { - if (Driver is null) return Stop.DriverNotReady; await Task.CompletedTask; var js = Driver as IJavaScriptExecutor; - js.ExecuteScript(javascript); + js?.ExecuteScript(javascript); return Result.Ok(); } @@ -336,4 +307,4 @@ private static string ReplaceTemplates(string str, string host, int port, string .Replace("{PASSWORD}", password); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/ChromeDriverInstaller.cs b/MainCore/Services/ChromeDriverInstaller.cs index 86b3d5927..3722de004 100644 --- a/MainCore/Services/ChromeDriverInstaller.cs +++ b/MainCore/Services/ChromeDriverInstaller.cs @@ -1,4 +1,4 @@ -using Microsoft.Win32; +using Microsoft.Win32; using System.Diagnostics; using System.IO.Compression; using System.Net.Http.Json; @@ -326,4 +326,4 @@ public class CftLink [JsonPropertyName("url")] public string Url { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Services/ChromeManager.cs b/MainCore/Services/ChromeManager.cs index cc0505313..3c2bcc0df 100644 --- a/MainCore/Services/ChromeManager.cs +++ b/MainCore/Services/ChromeManager.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Reflection; namespace MainCore.Services @@ -7,12 +7,12 @@ namespace MainCore.Services public sealed class ChromeManager(ILogger logger) : IChromeManager { private readonly ILogger _logger = logger.ForContext(); - private readonly ConcurrentDictionary _dictionary = new(); + private readonly ConcurrentDictionary _dictionary = new(); private string[] _extensionsPath = default!; - public IChromeBrowser Get(AccountId accountId) + public IBrowser Get(AccountId accountId) { - if (_dictionary.TryGetValue(accountId, out ChromeBrowser? browser)) + if (_dictionary.TryGetValue(accountId, out IBrowser? browser)) { return browser; } @@ -25,7 +25,7 @@ public async Task Shutdown() { foreach (var id in _dictionary.Keys) { - if (_dictionary.Remove(id, out ChromeBrowser? browser)) + if (_dictionary.Remove(id, out IBrowser? browser)) { await browser.Shutdown(); } @@ -64,4 +64,4 @@ public void LoadExtension() _logger.Information("Loaded {Count} extension files.", _extensionsPath.Length); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/CustomServiceScopeFactory.cs b/MainCore/Services/CustomServiceScopeFactory.cs index c7cc57be5..b29d33d3d 100644 --- a/MainCore/Services/CustomServiceScopeFactory.cs +++ b/MainCore/Services/CustomServiceScopeFactory.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; using Microsoft.Extensions.DependencyInjection; namespace MainCore.Services @@ -110,4 +110,4 @@ public static async Task Execute(this IServiceScope scope, BaseTask task } } } -} \ No newline at end of file +} diff --git a/MainCore/Services/DataService.cs b/MainCore/Services/DataService.cs index e85478e8f..8a631b1e4 100644 --- a/MainCore/Services/DataService.cs +++ b/MainCore/Services/DataService.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { [RegisterScoped] public sealed class DataService : IDataService @@ -7,4 +7,4 @@ public sealed class DataService : IDataService public string AccountData { get; set; } = ""; public bool IsLoggerConfigured { get; set; } = false; } -} \ No newline at end of file +} diff --git a/MainCore/Services/DelayService.cs b/MainCore/Services/DelayService.cs index c3f6a6f67..38b668233 100644 --- a/MainCore/Services/DelayService.cs +++ b/MainCore/Services/DelayService.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { [RegisterScoped] public class DelayService : IDelayService @@ -24,4 +24,4 @@ public async Task DelayTask(CancellationToken cancellationToken = default) await Task.Delay(delay, cancellationToken); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/DialogService.cs b/MainCore/Services/DialogService.cs index 2737f667e..6a275436b 100644 --- a/MainCore/Services/DialogService.cs +++ b/MainCore/Services/DialogService.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; namespace MainCore.Services { @@ -18,4 +18,4 @@ public DialogService() SaveFileDialog = new Interaction(); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/FirefoxBrowser.cs b/MainCore/Services/FirefoxBrowser.cs new file mode 100644 index 000000000..a17deef29 --- /dev/null +++ b/MainCore/Services/FirefoxBrowser.cs @@ -0,0 +1,400 @@ +using OpenQA.Selenium.Firefox; +using OpenQA.Selenium.Interactions; +using OpenQA.Selenium.Support.UI; + +namespace MainCore.Services +{ + public sealed class FirefoxBrowser : IBrowser + { + private FirefoxDriver? _driver; + private readonly FirefoxDriverService _firefoxService; + private WebDriverWait _wait = null!; + + private readonly string[] _extensionsPath; + private readonly HtmlDocument _htmlDoc = new(); + + public FirefoxBrowser(string[] extensionsPath) + { + _extensionsPath = extensionsPath; + + _firefoxService = FirefoxDriverService.CreateDefaultService(); + _firefoxService.HideCommandPromptWindow = true; + } + + public async Task Setup(BrowserSetting setting) + { + var options = new FirefoxOptions(); + + // Firefox'un yolunu bul ve belirt + var firefoxPath = FindFirefoxPath(); + if (!string.IsNullOrEmpty(firefoxPath)) + { + options.BinaryLocation = firefoxPath; + } + else + { + throw new InvalidOperationException("Firefox bulunamadı! Lütfen Firefox'u yükleyin veya PATH'e ekleyin."); + } + + // Firefox için extension ekleme (Chrome'dan farklı) + // Firefox'ta extension ekleme daha karmaşık, ÅŸimdilik atlayalım + // foreach (var extensionPath in _extensionsPath) + // { + // options.AddExtension(extensionPath); + // } + + // Proxy ayarları + if (!string.IsNullOrEmpty(setting.ProxyHost)) + { + if (!string.IsNullOrEmpty(setting.ProxyUsername) && !string.IsNullOrEmpty(setting.ProxyPassword)) + { + options.AddHttpProxy(setting.ProxyHost, setting.ProxyPort, setting.ProxyUsername, setting.ProxyPassword); + } + else + { + options.AddArgument($"--proxy-server={setting.ProxyHost}:{setting.ProxyPort}"); + } + } + + // User agent ayarı + if (!string.IsNullOrEmpty(setting.UserAgent)) + { + options.AddArgument($"--user-agent={setting.UserAgent}"); + } + + // Firefox'a özgü ayarlar + options.AddArgument("--ignore-certificate-errors"); + options.AddArguments("--no-default-browser-check", "--no-first-run"); + options.AddArguments("--mute-audio", "--disable-gpu"); + + // Firefox 138.0+ için gerekli argüman + options.AddArgument("--allow-system-access"); + + // Automation detection'ı engelle + options.AddArgument("--disable-blink-features=AutomationControlled"); + options.AddAdditionalOption("useAutomationExtension", "undefined"); + + // Headless mod + if (setting.IsHeadless) + { + options.AddArgument("--headless"); + } + else + { + options.AddArgument("--start-maximized"); + } + + // Firefox profil ayarları - geçerli bir yol oluÅŸtur + var baseProfilePath = Path.Combine(AppContext.BaseDirectory, "Data", "Cache", "FirefoxProfiles"); + if (!Directory.Exists(baseProfilePath)) Directory.CreateDirectory(baseProfilePath); + + // Profil yolu için geçerli karakterler kullan + var safeProfilePath = SanitizePath(setting.ProfilePath); + var safeProxyName = string.IsNullOrEmpty(setting.ProxyHost) ? "default" : SanitizePath(setting.ProxyHost); + + var profilePath = Path.Combine(baseProfilePath, safeProfilePath, safeProxyName); + if (!Directory.Exists(profilePath)) Directory.CreateDirectory(profilePath); + + // Firefox profil oluÅŸtur + var profile = new FirefoxProfile(profilePath); + options.Profile = profile; + + _driver = await Task.Run(() => new FirefoxDriver(_firefoxService, options, TimeSpan.FromMinutes(3))); + + _driver.Manage().Timeouts().PageLoad = TimeSpan.FromMinutes(2); + _driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); + // Firefox için daha uzun timeout - sayfa yükleme daha yavaÅŸ olabilir + _wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(150)); // building operations + } + + public IWebDriver Driver => _driver!; + + public HtmlDocument Html + { + get + { + if (_driver is not null) _htmlDoc.LoadHtml(_driver.PageSource); + return _htmlDoc; + } + } + + public async Task Shutdown() + { + if (_driver is null) return; + await Close(); + _firefoxService.Dispose(); + } + + public string CurrentUrl => Driver.Url; + + public ILogger Logger { get; set; } = null!; + + public async Task Screenshot() + { + var screenshot = ((ITakesScreenshot)Driver).GetScreenshot(); + var fileName = Path.Combine(AppContext.BaseDirectory, "Screenshots", $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png"); + Directory.CreateDirectory(Path.GetDirectoryName(fileName)!); + await File.WriteAllBytesAsync(fileName, screenshot.AsByteArray, CancellationToken.None); + return fileName; + } + + public async Task Refresh(CancellationToken cancellationToken) + { + await Driver.Navigate().RefreshAsync(); + var result = await WaitPageLoaded(cancellationToken); + return result; + } + + private static bool PageLoaded(IWebDriver driver) => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState")?.Equals("complete") ?? false; + + private static bool PageChanged(IWebDriver driver, string url_nested) => driver.Url.Contains(url_nested) && PageLoaded(driver); + + public async Task Navigate(string url, CancellationToken cancellationToken) + { + await Driver.Navigate().GoToUrlAsync(url); + var result = await Wait(driver => PageChanged(driver, url), cancellationToken); + return result; + } + + public async Task Click(By by) + { + var elements = Driver.FindElements(by); + if (elements.Count == 0) return Retry.ElementNotFound(by); + var element = elements[0]; + if (!element.Displayed || !element.Enabled) return Retry.ElementNotClickable(by); + + try + { + // Önce element'i viewport'a scroll et + await Task.Run(() => ((IJavaScriptExecutor)Driver).ExecuteScript("arguments[0].scrollIntoView({behavior: 'instant', block: 'center'});", element)); + + // Kısa bir bekleme + await Task.Delay(200); + + // Firefox için daha güvenilir: önce normal click dene + await Task.Run(() => element.Click()); + } + catch + { + // Normal click baÅŸarısız olursa, JavaScript click dene + try + { + await Task.Run(() => ((IJavaScriptExecutor)Driver).ExecuteScript("arguments[0].click();", element)); + } + catch + { + // Son çare olarak Actions kullan (viewport sınırları kontrolü ile) + try + { + var location = element.Location; + var size = element.Size; + var viewportSize = Driver.Manage().Window.Size; + + // Element viewport içindeyse Actions kullan + if (location.X >= 0 && location.Y >= 0 && + location.X + size.Width <= viewportSize.Width && + location.Y + size.Height <= viewportSize.Height) + { + await Task.Run(new Actions(Driver).Click(element).Perform); + } + else + { + // Viewport dışındaysa JavaScript ile zorla click + await Task.Run(() => ((IJavaScriptExecutor)Driver).ExecuteScript("arguments[0].dispatchEvent(new MouseEvent('click', {bubbles: true}));", element)); + } + } + catch + { + return Retry.ElementNotClickable(by); + } + } + } + + return Result.Ok(); + } + + public async Task Input(By by, string content) + { + var elements = Driver.FindElements(by); + if (elements.Count == 0) return Retry.ElementNotFound(by); + var element = elements[0]; + if (!element.Displayed || !element.Enabled) return Retry.ElementNotClickable(by); + + void input() + { + element.SendKeys(Keys.Home); + element.SendKeys(Keys.Shift + Keys.End); + element.SendKeys(content); + } + + await Task.Run(input); + return Result.Ok(); + } + + public async Task ExecuteJsScript(string javascript) + { + await Task.CompletedTask; + var js = Driver as IJavaScriptExecutor; + js?.ExecuteScript(javascript); + return Result.Ok(); + } + + public async Task Wait(Predicate condition, CancellationToken cancellationToken) + { + void wait() + { + _wait.Until(driver => condition(driver), cancellationToken); + } + + try + { + await Task.Run(wait, cancellationToken); + } + catch (OperationCanceledException) + { + return Cancel.Error; + } + catch (WebDriverTimeoutException ex) + { + return Retry.BrowserTimeout(ex.Message); + } + return Result.Ok(); + } + + public Task WaitPageLoaded(CancellationToken cancellationToken) + { + return Wait(PageLoaded, cancellationToken); + } + + public Task WaitPageChanged(string part, CancellationToken cancellationToken) + { + return Wait(driver => PageChanged(driver, part), cancellationToken); + } + + public Task WaitPageChanged(string part, Predicate customCondition, CancellationToken cancellationToken) + { + return Wait(driver => PageChanged(driver, part) && customCondition(driver), cancellationToken); + } + + public async Task Close() + { + await Task.Run(() => _driver?.Quit()); + } + + private static string? FindFirefoxPath() + { + // Yaygın Firefox kurulum yolları + var possiblePaths = new[] + { + @"C:\Program Files\Mozilla Firefox\firefox.exe", + @"C:\Program Files (x86)\Mozilla Firefox\firefox.exe", + @"C:\Users\{USERNAME}\AppData\Local\Mozilla Firefox\firefox.exe", + @"C:\ProgramData\Mozilla Firefox\firefox.exe" + }; + + foreach (var path in possiblePaths) + { + // {USERNAME} placeholder'ını gerçek kullanıcı adıyla deÄŸiÅŸtir + var expandedPath = path.Replace("{USERNAME}", Environment.UserName); + + if (File.Exists(expandedPath)) + { + return expandedPath; + } + } + + // PATH'de Firefox'u ara + try + { + var process = new System.Diagnostics.Process + { + StartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = "where", + Arguments = "firefox.exe", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + if (process.ExitCode == 0 && !string.IsNullOrEmpty(output)) + { + var firstPath = output.Split('\n')[0].Trim(); + if (File.Exists(firstPath)) + { + return firstPath; + } + } + } + catch + { + // Hata durumunda null döndür + } + + return null; + } + + private static string SanitizePath(string path) + { + if (string.IsNullOrEmpty(path)) + return "default"; + + // Windows dosya sistemi için geçersiz karakterleri temizle + var invalidChars = Path.GetInvalidFileNameChars(); + var sanitized = new string(path.Where(c => !invalidChars.Contains(c)).ToArray()); + + // BoÅŸ string olursa default döndür + if (string.IsNullOrWhiteSpace(sanitized)) + return "default"; + + return sanitized; + } + } + + public static class FirefoxOptionsExtensions + { + /// + /// Add HTTP-proxy by and + /// + /// Firefox options + /// Proxy host + /// Proxy port + /// Proxy username + /// Proxy password + public static void AddHttpProxy(this FirefoxOptions options, string host, int port, string userName, string password) + { + // Firefox için proxy ayarları profil üzerinden yapılır + var profile = options.Profile ?? new FirefoxProfile(); + + // Proxy ayarları + profile.SetPreference("network.proxy.type", 1); // Manual proxy + profile.SetPreference("network.proxy.http", host); + profile.SetPreference("network.proxy.http_port", port); + profile.SetPreference("network.proxy.ssl", host); + profile.SetPreference("network.proxy.ssl_port", port); + profile.SetPreference("network.proxy.ftp", host); + profile.SetPreference("network.proxy.ftp_port", port); + profile.SetPreference("network.proxy.socks", host); + profile.SetPreference("network.proxy.socks_port", port); + + // Proxy authentication + if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) + { + profile.SetPreference("network.proxy.http_use_dns", false); + profile.SetPreference("network.proxy.ssl_use_dns", false); + profile.SetPreference("network.proxy.ftp_use_dns", false); + profile.SetPreference("network.proxy.socks_use_dns", false); + + // Firefox için proxy authentication extension gerekebilir + // Bu durumda basit bir çözüm olarak proxy'yi authentication olmadan kullanabiliriz + } + + options.Profile = profile; + } + } +} diff --git a/MainCore/Services/GeckoDriverInstaller.cs b/MainCore/Services/GeckoDriverInstaller.cs new file mode 100644 index 000000000..c6ed7726a --- /dev/null +++ b/MainCore/Services/GeckoDriverInstaller.cs @@ -0,0 +1,129 @@ +using System.Diagnostics; +using System.IO.Compression; +using System.Runtime.InteropServices; +using Injectio.Attributes; + +namespace MainCore.Services +{ + [RegisterSingleton] + public sealed class GeckoDriverInstaller : IBrowserDriverInstaller + { + private readonly HttpClient httpClient = new(); + + private const string fallbackVersion = "0.36.0"; // GeckoDriver version to download + + public async Task Install() + { + CheckPlatform(); + + var geckoDriverVersion = await GetCurrentDriverVersion(); + + // EÄŸer GeckoDriver yoksa veya eskiyse indir + if (geckoDriverVersion == "0.0.0" || CompareVersions(geckoDriverVersion, fallbackVersion) < 0) + { + try + { + await Download(fallbackVersion); + } + catch (Exception ex) + { + // GeckoDriver indirme baÅŸarısız olursa, uygulama çalışmaya devam etsin + // Kullanıcı manuel olarak GeckoDriver'ı PATH'e ekleyebilir + Console.WriteLine($"GeckoDriver indirme baÅŸarısız: {ex.Message}"); + Console.WriteLine("Lütfen GeckoDriver'ı manuel olarak PATH'e ekleyin veya uygulama klasörüne koyun."); + } + } + } + + private async Task Download(string version) + { + // GitHub API yerine direkt download URL kullan + var downloadUrl = $"https://github.com/mozilla/geckodriver/releases/download/v{version}/geckodriver-v{version}-win64.zip"; + + var driverZipResponse = await httpClient.GetAsync(downloadUrl); + if (!driverZipResponse.IsSuccessStatusCode) + { + throw new Exception($"GeckoDriver download failed: {driverZipResponse.StatusCode}"); + } + + var targetPath = GetDriverPath(); + var targetDir = Path.GetDirectoryName(targetPath)!; + + if (!Directory.Exists(targetDir)) + { + Directory.CreateDirectory(targetDir); + } + + using var zipFileStream = await driverZipResponse.Content.ReadAsStreamAsync(); + using var zipArchive = new ZipArchive(zipFileStream, ZipArchiveMode.Read); + + // GeckoDriver zip dosyasından geckodriver.exe'yi çıkar + var entry = zipArchive.Entries.First(x => x.Name == "geckodriver.exe"); + using var geckoDriverStream = entry.Open(); + using var geckoDriverWriter = new FileStream(targetPath, FileMode.Create); + await geckoDriverStream.CopyToAsync(geckoDriverWriter); + } + + private static void CheckPlatform() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + throw new Exception("GeckoDriver installer only supports Windows"); + } + } + + private static async Task GetCurrentDriverVersion() + { + var driverPath = GetDriverPath(); + if (!File.Exists(driverPath)) + { + return "0.0.0"; + } + + try + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = driverPath, + Arguments = "--version", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + var output = await process.StandardOutput.ReadToEndAsync(); + await process.WaitForExitAsync(); + + // GeckoDriver version output: "geckodriver 0.36.0" + var versionMatch = System.Text.RegularExpressions.Regex.Match(output, @"geckodriver (\d+\.\d+\.\d+)"); + return versionMatch.Success ? versionMatch.Groups[1].Value : "0.0.0"; + } + catch + { + return "0.0.0"; + } + } + + + + private static string GetDriverPath() + { + // Uygulamanın çalıştığı dizini kullan (publish edildiÄŸinde de doÄŸru çalışır) + var currentDir = Directory.GetCurrentDirectory(); + return Path.Combine(currentDir, "geckodriver.exe"); + } + + private static int CompareVersions(string version1, string version2) + { + var v1 = Version.Parse(version1); + var v2 = Version.Parse(version2); + return v1.CompareTo(v2); + } + } + + +} diff --git a/MainCore/Services/IBrowser.cs b/MainCore/Services/IBrowser.cs new file mode 100644 index 000000000..d27888ca1 --- /dev/null +++ b/MainCore/Services/IBrowser.cs @@ -0,0 +1,36 @@ +namespace MainCore.Services +{ + public interface IBrowser + { + string CurrentUrl { get; } + IWebDriver Driver { get; } + HtmlDocument Html { get; } + ILogger Logger { get; set; } + + Task Click(By by); + + Task Close(); + + Task ExecuteJsScript(string javascript); + + Task Input(By by, string content); + + Task Navigate(string url, CancellationToken cancellationToken); + + Task Refresh(CancellationToken cancellationToken); + + Task Screenshot(); + + Task Setup(BrowserSetting setting); + + Task Shutdown(); + + Task Wait(Predicate condition, CancellationToken cancellationToken); + + Task WaitPageChanged(string part, CancellationToken cancellationToken); + + Task WaitPageChanged(string part, Predicate customCondition, CancellationToken cancellationToken); + + Task WaitPageLoaded(CancellationToken cancellationToken); + } +} diff --git a/MainCore/Services/IBrowserDriverInstaller.cs b/MainCore/Services/IBrowserDriverInstaller.cs new file mode 100644 index 000000000..a8d726760 --- /dev/null +++ b/MainCore/Services/IBrowserDriverInstaller.cs @@ -0,0 +1,7 @@ +namespace MainCore.Services +{ + public interface IBrowserDriverInstaller + { + Task Install(); + } +} diff --git a/MainCore/Services/IBrowserManager.cs b/MainCore/Services/IBrowserManager.cs new file mode 100644 index 000000000..381ebf055 --- /dev/null +++ b/MainCore/Services/IBrowserManager.cs @@ -0,0 +1,11 @@ +namespace MainCore.Services +{ + public interface IBrowserManager + { + IBrowser Get(AccountId accountId); + + void LoadExtension(); + + Task Shutdown(); + } +} diff --git a/MainCore/Services/IChromeDriverInstaller.cs b/MainCore/Services/IChromeDriverInstaller.cs index 10dadcfbf..f2bdae85c 100644 --- a/MainCore/Services/IChromeDriverInstaller.cs +++ b/MainCore/Services/IChromeDriverInstaller.cs @@ -1,7 +1,7 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface IChromeDriverInstaller { Task Install(); } -} \ No newline at end of file +} diff --git a/MainCore/Services/IChromeManager.cs b/MainCore/Services/IChromeManager.cs index aba828504..6d393a7d8 100644 --- a/MainCore/Services/IChromeManager.cs +++ b/MainCore/Services/IChromeManager.cs @@ -1,11 +1,11 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface IChromeManager { - IChromeBrowser Get(AccountId accountId); + IBrowser Get(AccountId accountId); void LoadExtension(); Task Shutdown(); } -} \ No newline at end of file +} diff --git a/MainCore/Services/ICustomServiceScopeFactory.cs b/MainCore/Services/ICustomServiceScopeFactory.cs index acd07bca5..cfd90c3a8 100644 --- a/MainCore/Services/ICustomServiceScopeFactory.cs +++ b/MainCore/Services/ICustomServiceScopeFactory.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; namespace MainCore.Services { @@ -7,4 +7,4 @@ public interface ICustomServiceScopeFactory IServiceScope CreateScope(AccountId accountId); IServiceScope CreateScope(); } -} \ No newline at end of file +} diff --git a/MainCore/Services/IDataService.cs b/MainCore/Services/IDataService.cs index 1d079248f..1091773dd 100644 --- a/MainCore/Services/IDataService.cs +++ b/MainCore/Services/IDataService.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface IDataService { @@ -6,4 +6,4 @@ public interface IDataService string AccountData { get; set; } bool IsLoggerConfigured { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/Services/IDelayService.cs b/MainCore/Services/IDelayService.cs index 23086e28d..7b7629033 100644 --- a/MainCore/Services/IDelayService.cs +++ b/MainCore/Services/IDelayService.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface IDelayService { @@ -6,4 +6,4 @@ public interface IDelayService Task DelayTask(CancellationToken cancellationToken = default); } -} \ No newline at end of file +} diff --git a/MainCore/Services/IDialogService.cs b/MainCore/Services/IDialogService.cs index 9cf8f3870..e3e5d6123 100644 --- a/MainCore/Services/IDialogService.cs +++ b/MainCore/Services/IDialogService.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; namespace MainCore.Services { @@ -9,4 +9,4 @@ public interface IDialogService Interaction OpenFileDialog { get; } Interaction SaveFileDialog { get; } } -} \ No newline at end of file +} diff --git a/MainCore/Services/ILogService.cs b/MainCore/Services/ILogService.cs index fcf6e1325..ec7ceb9f9 100644 --- a/MainCore/Services/ILogService.cs +++ b/MainCore/Services/ILogService.cs @@ -1,4 +1,4 @@ -using Serilog.Events; +using Serilog.Events; namespace MainCore.Services { @@ -10,4 +10,4 @@ public interface ILogService void Shutdown(); } -} \ No newline at end of file +} diff --git a/MainCore/Services/IRxQueue.cs b/MainCore/Services/IRxQueue.cs index aad7f254d..17fa8275d 100644 --- a/MainCore/Services/IRxQueue.cs +++ b/MainCore/Services/IRxQueue.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface IRxQueue { @@ -12,4 +12,4 @@ public interface IRxQueue void Setup(); } -} \ No newline at end of file +} diff --git a/MainCore/Services/ISettingService.cs b/MainCore/Services/ISettingService.cs index 6fc55f81c..c81cd5091 100644 --- a/MainCore/Services/ISettingService.cs +++ b/MainCore/Services/ISettingService.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface ISettingService { @@ -16,4 +16,4 @@ public interface ISettingService int ByName(VillageId villageId, VillageSettingEnums settingMin, VillageSettingEnums settingMax, int multiplier = 1); } -} \ No newline at end of file +} diff --git a/MainCore/Services/ITaskManager.cs b/MainCore/Services/ITaskManager.cs index 17fe2dc01..2334fa22f 100644 --- a/MainCore/Services/ITaskManager.cs +++ b/MainCore/Services/ITaskManager.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.Services { @@ -36,4 +36,4 @@ public interface ITaskManager Task StopCurrentTask(AccountId accountId); } -} \ No newline at end of file +} diff --git a/MainCore/Services/ITimerManager.cs b/MainCore/Services/ITimerManager.cs index 29bdfd27f..b7ef89120 100644 --- a/MainCore/Services/ITimerManager.cs +++ b/MainCore/Services/ITimerManager.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface ITimerManager { @@ -6,4 +6,4 @@ public interface ITimerManager void Start(AccountId accountId); } -} \ No newline at end of file +} diff --git a/MainCore/Services/IUseragentManager.cs b/MainCore/Services/IUseragentManager.cs index 30be227f4..d2878237a 100644 --- a/MainCore/Services/IUseragentManager.cs +++ b/MainCore/Services/IUseragentManager.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { public interface IUseragentManager : IDisposable { @@ -6,4 +6,4 @@ public interface IUseragentManager : IDisposable Task Load(); } -} \ No newline at end of file +} diff --git a/MainCore/Services/LogService.cs b/MainCore/Services/LogService.cs index 2a7ca7f27..4098256e8 100644 --- a/MainCore/Services/LogService.cs +++ b/MainCore/Services/LogService.cs @@ -1,4 +1,4 @@ -using Serilog; +using Serilog; using Serilog.Events; namespace MainCore.Services @@ -25,4 +25,4 @@ public LinkedList GetLog(AccountId accountId) return logs; } } -} \ No newline at end of file +} diff --git a/MainCore/Services/LogSink.cs b/MainCore/Services/LogSink.cs index d3e2f1e80..ba979daed 100644 --- a/MainCore/Services/LogSink.cs +++ b/MainCore/Services/LogSink.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Configuration; using Serilog.Core; @@ -58,4 +58,4 @@ public static LoggerConfiguration LogSink( return loggerConfiguration.Sink(Locator.Current.GetService()!); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/RxQueue.cs b/MainCore/Services/RxQueue.cs index b55841d00..bbeaef8c3 100644 --- a/MainCore/Services/RxQueue.cs +++ b/MainCore/Services/RxQueue.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using System.Reactive.Concurrency; using System.Reactive.Subjects; @@ -102,4 +102,4 @@ public IObservable GetObservable() where T : INotification return _connectableObservable.OfType(); } } -} \ No newline at end of file +} diff --git a/MainCore/Services/SettingService.cs b/MainCore/Services/SettingService.cs index e41590d65..5d195da51 100644 --- a/MainCore/Services/SettingService.cs +++ b/MainCore/Services/SettingService.cs @@ -1,4 +1,4 @@ -namespace MainCore.Services +namespace MainCore.Services { [RegisterScoped] public class SettingService : ISettingService @@ -193,4 +193,4 @@ public static bool BooleanByName(this AppDbContext context, AccountId accountId, return settingValue; } } -} \ No newline at end of file +} diff --git a/MainCore/Services/TaskManager.cs b/MainCore/Services/TaskManager.cs index d9d5dc880..6243abcc8 100644 --- a/MainCore/Services/TaskManager.cs +++ b/MainCore/Services/TaskManager.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.Services { @@ -214,4 +214,4 @@ public class TaskQueue public CancellationTokenSource? CancellationTokenSource { get; set; } public List Tasks { get; } = []; } -} \ No newline at end of file +} diff --git a/MainCore/Services/TimerManager.cs b/MainCore/Services/TimerManager.cs index ef198ab39..c5f9f1fb4 100644 --- a/MainCore/Services/TimerManager.cs +++ b/MainCore/Services/TimerManager.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; using Microsoft.Extensions.DependencyInjection; using Polly; using Polly.Retry; @@ -92,7 +92,7 @@ public async Task Execute(AccountId accountId) using var scope = _serviceScopeFactory.CreateScope(accountId); ///===========================================================/// - var browser = scope.ServiceProvider.GetRequiredService(); + var browser = scope.ServiceProvider.GetRequiredService(); var logger = browser.Logger; var contextData = new ContextData(task.Description, browser); @@ -216,6 +216,6 @@ public void Start(AccountId accountId) } } - public record ContextData(string TaskName, IChromeBrowser Browser); + public record ContextData(string TaskName, IBrowser Browser); } -} \ No newline at end of file +} diff --git a/MainCore/Services/UseragentManager.cs b/MainCore/Services/UseragentManager.cs index 4bd33aecc..eaf1d387d 100644 --- a/MainCore/Services/UseragentManager.cs +++ b/MainCore/Services/UseragentManager.cs @@ -1,4 +1,4 @@ -using System.Net.Http.Json; +using System.Net.Http.Json; using System.Text.Json; namespace MainCore.Services @@ -84,4 +84,4 @@ public void Dispose() _httpClient.Dispose(); } } -} \ No newline at end of file +} diff --git a/MainCore/Specifications/GetBuildingSpec.cs b/MainCore/Specifications/GetBuildingSpec.cs index 70a22d88b..4683bb445 100644 --- a/MainCore/Specifications/GetBuildingSpec.cs +++ b/MainCore/Specifications/GetBuildingSpec.cs @@ -1,4 +1,4 @@ -using Ardalis.Specification; +using Ardalis.Specification; namespace MainCore.Specifications { @@ -11,4 +11,4 @@ public GetBuildingSpec(VillageId villageId, int location) .Where(x => x.Location == location); } } -} \ No newline at end of file +} diff --git a/MainCore/Specifications/GetVillageNameSpec.cs b/MainCore/Specifications/GetVillageNameSpec.cs index ad75c0423..319cf1211 100644 --- a/MainCore/Specifications/GetVillageNameSpec.cs +++ b/MainCore/Specifications/GetVillageNameSpec.cs @@ -1,4 +1,4 @@ -using Ardalis.Specification; +using Ardalis.Specification; namespace MainCore.Specifications { @@ -11,4 +11,4 @@ public GetVillageNameSpec(VillageId villageId) .Select(x => x.Name); } } -} \ No newline at end of file +} diff --git a/MainCore/Specifications/HasBuildJobVillagesSpec.cs b/MainCore/Specifications/HasBuildJobVillagesSpec.cs index 822cd0ec8..839448502 100644 --- a/MainCore/Specifications/HasBuildJobVillagesSpec.cs +++ b/MainCore/Specifications/HasBuildJobVillagesSpec.cs @@ -1,4 +1,4 @@ -using Ardalis.Specification; +using Ardalis.Specification; namespace MainCore.Specifications { @@ -17,4 +17,4 @@ public HasBuildJobVillagesSpec(AccountId accountId) .Select(x => new VillageId(x.Id)); } } -} \ No newline at end of file +} diff --git a/MainCore/Specifications/MissingBuildingVillagesSpec.cs b/MainCore/Specifications/MissingBuildingVillagesSpec.cs index 7d162f29a..acba61337 100644 --- a/MainCore/Specifications/MissingBuildingVillagesSpec.cs +++ b/MainCore/Specifications/MissingBuildingVillagesSpec.cs @@ -1,4 +1,4 @@ -using Ardalis.Specification; +using Ardalis.Specification; namespace MainCore.Specifications { @@ -12,4 +12,4 @@ public MissingBuildingVillagesSpec(AccountId accountId) .Select(x => new VillageId(x.Id)); } } -} \ No newline at end of file +} diff --git a/MainCore/Specifications/VillagesSpec.cs b/MainCore/Specifications/VillagesSpec.cs index 057f10488..061cc9bdb 100644 --- a/MainCore/Specifications/VillagesSpec.cs +++ b/MainCore/Specifications/VillagesSpec.cs @@ -1,4 +1,4 @@ -using Ardalis.Specification; +using Ardalis.Specification; namespace MainCore.Specifications { @@ -11,4 +11,4 @@ public VillagesSpec(AccountId accountId) .Select(x => new VillageId(x.Id)); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/Base/AccountTask.cs b/MainCore/Tasks/Base/AccountTask.cs index fee33b895..e5b30316a 100644 --- a/MainCore/Tasks/Base/AccountTask.cs +++ b/MainCore/Tasks/Base/AccountTask.cs @@ -1,4 +1,4 @@ -namespace MainCore.Tasks.Base +namespace MainCore.Tasks.Base { public abstract class AccountTask(AccountId accountId) : BaseTask, IAccountConstraint { @@ -7,4 +7,4 @@ public abstract class AccountTask(AccountId accountId) : BaseTask, IAccountConst public override string Description => TaskName; public override string Key => $"{AccountId}"; } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/Base/BaseTask.cs b/MainCore/Tasks/Base/BaseTask.cs index 30b9e078a..b052cd674 100644 --- a/MainCore/Tasks/Base/BaseTask.cs +++ b/MainCore/Tasks/Base/BaseTask.cs @@ -1,4 +1,4 @@ -namespace MainCore.Tasks.Base +namespace MainCore.Tasks.Base { public abstract class BaseTask : ITask { @@ -10,4 +10,4 @@ public abstract class BaseTask : ITask public virtual bool CanStart(AppDbContext context) => true; } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/Base/VillageTask.cs b/MainCore/Tasks/Base/VillageTask.cs index 8046fe56e..3888582b2 100644 --- a/MainCore/Tasks/Base/VillageTask.cs +++ b/MainCore/Tasks/Base/VillageTask.cs @@ -1,4 +1,4 @@ -namespace MainCore.Tasks.Base +namespace MainCore.Tasks.Base { public abstract class VillageTask(AccountId accountId, VillageId villageId) : AccountTask(accountId), IAccountVillageConstraint { @@ -22,4 +22,4 @@ public void SetVillageName(AppDbContext context) public void Deconstruct(out AccountId accountId, out VillageId villageId) => (accountId, villageId) = (AccountId, VillageId); } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/ClaimQuestTask.cs b/MainCore/Tasks/ClaimQuestTask.cs index 6511a1592..036485fcc 100644 --- a/MainCore/Tasks/ClaimQuestTask.cs +++ b/MainCore/Tasks/ClaimQuestTask.cs @@ -1,4 +1,4 @@ -#pragma warning disable S1172 +#pragma warning disable S1172 using MainCore.Commands.Features.ClaimQuest; using MainCore.Tasks.Base; @@ -31,12 +31,30 @@ private static async ValueTask HandleAsync( ClaimQuestCommand.Handler claimQuestCommand, CancellationToken cancellationToken) { - Result result; - result = await toQuestPageCommand.HandleAsync(new(), cancellationToken); - if (result.IsFailed) return result; - result = await claimQuestCommand.HandleAsync(new(), cancellationToken); - if (result.IsFailed) return result; - return Result.Ok(); + try + { + Result result; + result = await toQuestPageCommand.HandleAsync(new(), cancellationToken); + if (result.IsFailed) + { + // Quest sayfasýna gidemiyor ise görev iptal edilir (quest yoktur) + return Result.Ok(); + } + + result = await claimQuestCommand.HandleAsync(new(), cancellationToken); + if (result.IsFailed) + { + // Quest claim edilemiyor ise görev iptal edilir (claim edilecek quest yoktur) + return Result.Ok(); + } + + return Result.Ok(); + } + catch (Exception) + { + // Herhangi bir exception durumunda task iptal edilir (timeout, network vs.) + return Result.Ok(); + } } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/CompleteImmediatelyTask.cs b/MainCore/Tasks/CompleteImmediatelyTask.cs index c77c4291a..57946bf47 100644 --- a/MainCore/Tasks/CompleteImmediatelyTask.cs +++ b/MainCore/Tasks/CompleteImmediatelyTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features.CompleteImmediately; +using MainCore.Commands.Features.CompleteImmediately; using MainCore.Tasks.Base; namespace MainCore.Tasks @@ -66,4 +66,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/LoginTask.cs b/MainCore/Tasks/LoginTask.cs index 3280e72de..d6129275e 100644 --- a/MainCore/Tasks/LoginTask.cs +++ b/MainCore/Tasks/LoginTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features; +using MainCore.Commands.Features; using MainCore.Commands.Features.DisableContextualHelp; using MainCore.Tasks.Base; @@ -18,7 +18,7 @@ private static async ValueTask HandleAsync( ToOptionsPageCommand.Handler toOptionsPageCommand, DisableContextualHelpCommand.Handler disableContextualHelpCommand, ToDorfCommand.Handler toDorfCommand, - IChromeBrowser chromeBrowser, + IBrowser chromeBrowser, CancellationToken cancellationToken) { Result result; @@ -37,4 +37,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/NPCTask.cs b/MainCore/Tasks/NPCTask.cs index de310f904..cc056f460 100644 --- a/MainCore/Tasks/NPCTask.cs +++ b/MainCore/Tasks/NPCTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features.NpcResource; +using MainCore.Commands.Features.NpcResource; using MainCore.Commands.UI.Misc; using MainCore.Tasks.Base; @@ -79,4 +79,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/SleepTask.cs b/MainCore/Tasks/SleepTask.cs index ba043f432..975d55141 100644 --- a/MainCore/Tasks/SleepTask.cs +++ b/MainCore/Tasks/SleepTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features; +using MainCore.Commands.Features; using MainCore.Commands.NextExecute; using MainCore.Tasks.Base; @@ -34,4 +34,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/StartAdventureTask.cs b/MainCore/Tasks/StartAdventureTask.cs index aa805b0fb..07ecf7269 100644 --- a/MainCore/Tasks/StartAdventureTask.cs +++ b/MainCore/Tasks/StartAdventureTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features.StartAdventure; +using MainCore.Commands.Features.StartAdventure; using MainCore.Commands.NextExecute; using MainCore.Tasks.Base; @@ -41,4 +41,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/StartFarmListTask.cs b/MainCore/Tasks/StartFarmListTask.cs index 2781c8853..6bec78940 100644 --- a/MainCore/Tasks/StartFarmListTask.cs +++ b/MainCore/Tasks/StartFarmListTask.cs @@ -1,4 +1,5 @@ -using MainCore.Commands.Features.StartFarmList; +using MainCore.Commands.Features.StartFarmList; +using MainCore.Commands.Navigate; using MainCore.Commands.NextExecute; using MainCore.Tasks.Base; @@ -22,6 +23,7 @@ private static async ValueTask HandleAsync( ToFarmListPageCommand.Handler toFarmListPageCommand, StartAllFarmListCommand.Handler startAllFarmListCommand, StartActiveFarmListCommand.Handler startActiveFarmListCommand, + ToDorfCommand.Handler toDorfCommand, NextExecuteStartFarmListTaskCommand.Handler nextExecuteStartFarmListTaskCommand, CancellationToken cancellationToken) { @@ -41,9 +43,13 @@ private static async ValueTask HandleAsync( if (result.IsFailed) return result; } + // Farm list baþlatýldýktan sonra dorf sayfasýna geç - sayfayý temizle + result = await toDorfCommand.HandleAsync(new(0), cancellationToken); + if (result.IsFailed) return result; + await nextExecuteStartFarmListTaskCommand.HandleAsync(new(task), cancellationToken); return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/TrainTroopTask.cs b/MainCore/Tasks/TrainTroopTask.cs index 6b8928ee8..ff0ee950d 100644 --- a/MainCore/Tasks/TrainTroopTask.cs +++ b/MainCore/Tasks/TrainTroopTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features.TrainTroop; +using MainCore.Commands.Features.TrainTroop; using MainCore.Commands.NextExecute; using MainCore.Commands.UI.Misc; using MainCore.Tasks.Base; @@ -70,4 +70,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/UpdateBuildingTask.cs b/MainCore/Tasks/UpdateBuildingTask.cs index d5209a20d..8f9befb27 100644 --- a/MainCore/Tasks/UpdateBuildingTask.cs +++ b/MainCore/Tasks/UpdateBuildingTask.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.Tasks { @@ -16,7 +16,7 @@ public Task(AccountId accountId, VillageId villageId) : base(accountId, villageI private static async ValueTask HandleAsync( Task task, - IChromeBrowser browser, + IBrowser browser, UpdateBuildingCommand.Handler updateBuildingCommand, ToDorfCommand.Handler toDorfCommand, CancellationToken cancellationToken) @@ -65,4 +65,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/UpdateFarmListTask.cs b/MainCore/Tasks/UpdateFarmListTask.cs index 1d28b3434..421bd5454 100644 --- a/MainCore/Tasks/UpdateFarmListTask.cs +++ b/MainCore/Tasks/UpdateFarmListTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.Features.StartFarmList; +using MainCore.Commands.Features.StartFarmList; using MainCore.Tasks.Base; namespace MainCore.Tasks @@ -30,4 +30,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/UpdateVillageTask.cs b/MainCore/Tasks/UpdateVillageTask.cs index 1ae401e8b..16f434f62 100644 --- a/MainCore/Tasks/UpdateVillageTask.cs +++ b/MainCore/Tasks/UpdateVillageTask.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.NextExecute; +using MainCore.Commands.NextExecute; using MainCore.Tasks.Base; namespace MainCore.Tasks @@ -25,7 +25,7 @@ public override bool CanStart(AppDbContext context) private static async ValueTask HandleAsync( Task task, - IChromeBrowser browser, + IBrowser browser, UpdateBuildingCommand.Handler updateBuildingCommand, ToDorfCommand.Handler toDorfCommand, NextExecuteUpdateVillageTaskCommand.Handler nextExecuteUpdateVillageTaskCommand, @@ -66,4 +66,4 @@ private static async ValueTask HandleAsync( return Result.Ok(); } } -} \ No newline at end of file +} diff --git a/MainCore/Tasks/UpgradeBuildingTask.cs b/MainCore/Tasks/UpgradeBuildingTask.cs index 6fd61beaa..dc44f66e4 100644 --- a/MainCore/Tasks/UpgradeBuildingTask.cs +++ b/MainCore/Tasks/UpgradeBuildingTask.cs @@ -1,5 +1,9 @@ -using MainCore.Commands.Features.UpgradeBuilding; +using MainCore.Commands.Features.UpgradeBuilding; +using MainCore.Commands.Misc; using MainCore.Tasks.Base; +using Microsoft.Extensions.DependencyInjection; +using System.Text.Json; +using Humanizer; namespace MainCore.Tasks { @@ -18,69 +22,303 @@ public Task(AccountId accountId, VillageId villageId) : base(accountId, villageI private static async ValueTask HandleAsync( Task task, ILogger logger, - IChromeBrowser browser, + IBrowser browser, GetBuildPlanCommand.Handler getBuildPlanCommand, ToBuildPageCommand.Handler toBuildPageCommand, HandleResourceCommand.Handler handleResourceCommand, AddCroplandCommand.Handler addCroplandCommand, HandleUpgradeCommand.Handler handleUpgradeCommand, UpdateBuildingCommand.Handler updateBuildingCommand, + ICustomServiceScopeFactory serviceScopeFactory, CancellationToken cancellationToken) { + logger.Information("=== TASK LIFECYCLE START === Task: {TaskName} | Village: {VillageId} | Account: {AccountId} | ExecuteAt: {ExecuteAt}", + "Upgrade building", task.VillageId, task.AccountId, task.ExecuteAt.ToString("HH:mm:ss")); + Result result; + var iterationCount = 0; while (true) { - if (cancellationToken.IsCancellationRequested) return Cancel.Error; + iterationCount++; + logger.Information("=== ITERATION #{IterationCount} START === Getting build plan for village {VillageId}", iterationCount, task.VillageId); + + if (cancellationToken.IsCancellationRequested) + { + logger.Warning("=== TASK CANCELLED === Task: {TaskName} | Village: {VillageId} | Iteration: #{IterationCount}", "Upgrade building", task.VillageId, iterationCount); + return Cancel.Error; + } - var (_, isFailed, plan, errors) = await getBuildPlanCommand.HandleAsync(new(task.AccountId, task.VillageId), cancellationToken); - if (isFailed) + NormalBuildPlan plan; + try { + var (_, isFailed, planResult, errors) = await getBuildPlanCommand.HandleAsync(new(task.AccountId, task.VillageId), cancellationToken); + if (isFailed) + { + // BuildingJobQueueEmpty ise task'ý bitir - yapacak iþ yok + if (errors.Any(x => x is UpgradeBuildingError && x.Message.Contains("Building job queue is empty"))) + { + logger.Information("=== TASK COMPLETED === Task: {TaskName} | Village: {VillageId} | Reason: Building job queue is empty | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, iterationCount); + return Result.Ok(); // Task'ý bitir + } + var nextExecuteErrors = errors.OfType().OrderBy(x => x.NextExecute).ToList(); if (nextExecuteErrors.Count > 0) { + // NextExecuteError'da MissingResource varsa Travian timer'ýný kullan + var missingResourceErrors = nextExecuteErrors.Where(x => x.Message.Contains("Missing resource for construction")).ToList(); + if (missingResourceErrors.Count > 0) + { + // Ýlk eksik resource için sayfaya git ve timer parse et + try + { + + using var timerScope = serviceScopeFactory.CreateScope(task.AccountId); + var getLayoutBuildingsCommand = timerScope.ServiceProvider.GetRequiredService(); + var layoutBuildings = await getLayoutBuildingsCommand.HandleAsync(new GetLayoutBuildingsCommand.Command(task.VillageId, false)); + + var resourceBuildings = layoutBuildings + .Where(x => x.Type == BuildingEnums.Woodcutter || + x.Type == BuildingEnums.ClayPit || + x.Type == BuildingEnums.IronMine || + x.Type == BuildingEnums.Cropland) + .Where(x => x.Level > 0) // Sadece mevcut building'ler + .Select(x => new { x.Type, x.Location, x.Level }) + .ToList(); + + if (resourceBuildings.Count == 0) + { + logger.Warning("No resource buildings found in layout for timer parsing"); + // Fallback: normal NextExecuteError zamanýný kullan + task.ExecuteAt = nextExecuteErrors.Select(x => x.NextExecute).Min(); + return new Skip(); + } + + TimeSpan shortestTime = TimeSpan.MaxValue; + DateTime earliestReadyTime = DateTime.MaxValue; + + // Her resource building için timer'ý kontrol et - GERÇEK LOCATION'LARI KULLAN + foreach (var building in resourceBuildings) + { + try + { + logger.Information("Checking timer for {BuildingType} at location {Location}", building.Type, building.Location); + result = await toBuildPageCommand.HandleAsync(new(task.VillageId, new NormalBuildPlan { Type = building.Type, Location = building.Location, Level = building.Level + 1 }), cancellationToken); + if (!result.IsFailed) + { + var time = UpgradeParser.GetTimeWhenEnoughResource(browser.Html, building.Type); + if (time > TimeSpan.Zero && time < shortestTime) + { + shortestTime = time; + earliestReadyTime = DateTime.Now.Add(time); + logger.Information("Found timer for {BuildingType} at location {Location}: {WaitTime}", building.Type, building.Location, time.Humanize()); + } + } + else + { + logger.Warning("Failed to navigate to {BuildingType} at location {Location}: {Error}", building.Type, building.Location, string.Join(", ", result.Reasons.Select(r => r.Message))); + } + } + catch (Exception buildingEx) + { + logger.Warning("Timer parsing failed for {BuildingType} at location {Location}: {Error}", building.Type, building.Location, buildingEx.Message); + } + } + + if (shortestTime < TimeSpan.MaxValue) + { + task.ExecuteAt = earliestReadyTime; + logger.Information("=== TASK SCHEDULED WITH TRAVIAN TIMER === Task: {TaskName} | Village: {VillageId} | NextExecute: {NextExecute} | WaitTime: {WaitTime} | Reason: MissingResource from GetBuildPlan | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), shortestTime.Humanize(), iterationCount); + return new Skip(); + } + else + { + logger.Warning("No valid timer found for any resource building - all timer parsing failed"); + } + } + catch (Exception ex) + { + logger.Warning("Timer parsing failed: {Error}", ex.Message); + } + } + + // Fallback: normal NextExecuteError zamanýný kullan task.ExecuteAt = nextExecuteErrors.Select(x => x.NextExecute).Min(); + logger.Information("=== TASK SCHEDULED === Task: {TaskName} | Village: {VillageId} | NextExecute: {NextExecute} | Reason: {Errors} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), string.Join(", ", errors.Select(e => e.Message)), iterationCount); + } + else + { + // Diðer hatalar için (timeout dahil) task'ý reschedule et + task.ExecuteAt = DateTime.Now.AddMinutes(2); + logger.Warning("=== TASK FAILED === Task: {TaskName} | Village: {VillageId} | Errors: {Errors} | NextExecute: {NextExecute} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, string.Join(", ", errors.Select(e => e.Message)), task.ExecuteAt.ToString("HH:mm:ss"), iterationCount); } return new Skip(); } + + plan = planResult; + } + catch (Exception ex) + { + // GetBuildPlanCommand timeout durumunda task'ý reschedule et - silme! + task.ExecuteAt = DateTime.Now.AddMinutes(3); + logger.Warning("=== TASK RESCHEDULED === Task: {TaskName} | Village: {VillageId} | Reason: GetBuildPlanCommand exception | NextExecute: {NextExecute} | Error: {Error} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), ex.Message, iterationCount); + return new Skip(); + } - logger.Information("Build {Type} to level {Level} at location {Location}", plan.Type, plan.Level, plan.Location); + logger.Information("=== CONSTRUCTION START === Task: {TaskName} | Village: {VillageId} | Building: {Type} level {Level} at location {Location} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, plan.Type, plan.Level, plan.Location, iterationCount); + logger.Information("=== STEP 1/4 === Navigating to build page | Village: {VillageId} | Building: {Type} location {Location}", + task.VillageId, plan.Type, plan.Location); + result = await toBuildPageCommand.HandleAsync(new(task.VillageId, plan), cancellationToken); - if (result.IsFailed) return result; + if (result.IsFailed) + { + // Navigation hatasý durumunda task'ý reschedule et - silme! + task.ExecuteAt = DateTime.Now.AddMinutes(2); + logger.Warning("=== TASK RESCHEDULED === Task: {TaskName} | Village: {VillageId} | Reason: ToBuildPageCommand failed | NextExecute: {NextExecute} | Error: {Error} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), string.Join(", ", result.Reasons.Select(r => r.Message)), iterationCount); + return new Skip(); + } + logger.Information("=== STEP 2/4 === Checking resources | Village: {VillageId} | Building: {Type} level {Level}", + task.VillageId, plan.Type, plan.Level); + result = await handleResourceCommand.HandleAsync(new(task.AccountId, task.VillageId, plan), cancellationToken); if (result.IsFailed) { if (result.HasError()) { + logger.Information("=== STEP 2/4 RETRY === Adding cropland | Village: {VillageId} | Reason: LackOfFreeCrop | Iteration: #{IterationCount}", + task.VillageId, iterationCount); await addCroplandCommand.HandleAsync(new(task.VillageId), cancellationToken); continue; } if (result.HasError()) { + logger.Warning("=== TASK STOPPED === Task: {TaskName} | Village: {VillageId} | Reason: StorageLimit | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, iterationCount); return new Stop(); } if (result.HasError()) { var time = UpgradeParser.GetTimeWhenEnoughResource(browser.Html, plan.Type); task.ExecuteAt = DateTime.Now.Add(time); + logger.Information("=== TASK SCHEDULED === Task: {TaskName} | Village: {VillageId} | Reason: MissingResource | NextExecute: {NextExecute} | WaitTime: {WaitTime} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), time.Humanize(), iterationCount); return new Skip(); } - return result; + // HandleResourceCommand'da beklenmeyen hata - reschedule et + task.ExecuteAt = DateTime.Now.AddMinutes(2); + logger.Warning("=== TASK RESCHEDULED === Task: {TaskName} | Village: {VillageId} | Reason: HandleResourceCommand failed | NextExecute: {NextExecute} | Error: {Error} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), string.Join(", ", result.Reasons.Select(r => r.Message)), iterationCount); + return new Skip(); } + logger.Information("=== STEP 3/4 === Starting construction | Village: {VillageId} | Building: {Type} level {Level} at location {Location}", + task.VillageId, plan.Type, plan.Level, plan.Location); + result = await handleUpgradeCommand.HandleAsync(new(task.VillageId, plan), cancellationToken); - if (result.IsFailed) return result; + if (result.IsFailed) + { + // Construction hatasý durumunda task'ý reschedule et - silme! + task.ExecuteAt = DateTime.Now.AddMinutes(2); + logger.Warning("=== TASK RESCHEDULED === Task: {TaskName} | Village: {VillageId} | Reason: HandleUpgradeCommand failed | NextExecute: {NextExecute} | Error: {Error} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), string.Join(", ", result.Reasons.Select(r => r.Message)), iterationCount); + return new Skip(); + } - logger.Information("Upgrade for {Type} at location {Location} completed successfully.", plan.Type, plan.Location); + logger.Information("=== STEP 3/4 SUCCESS === Construction completed | Village: {VillageId} | Building: {Type} level {Level} at location {Location} | Iteration: #{IterationCount}", + task.VillageId, plan.Type, plan.Level, plan.Location, iterationCount); + + // Construction baþarýlý olduktan sonra corresponding job'u sil + // ?? CRITICAL: plan.Level = TARGET level (örn: 20), ama þu an sadece 1 level construction baþlattýk! + using var scope = serviceScopeFactory.CreateScope(task.AccountId); + var context = scope.ServiceProvider.GetRequiredService(); + + // Gerçek construction level'ý bul - queue'dan veya current building level + 1 + var currentBuilding = context.Buildings + .Where(x => x.VillageId == task.VillageId.Value) + .Where(x => x.Location == plan.Location) + .FirstOrDefault(); + + var queueBuilding = context.QueueBuildings + .Where(x => x.VillageId == task.VillageId.Value) + .Where(x => x.Location == plan.Location) + .OrderByDescending(x => x.Level) + .FirstOrDefault(); + + // Construction level = Queue'daki en yüksek level VEYA current level + 1 + var actualConstructionLevel = queueBuilding?.Level ?? (currentBuilding?.Level + 1 ?? plan.Level); + + logger.Information("=== CONSTRUCTION LEVEL DETECTION === Building: {Type} | Current: {CurrentLevel} | Queue: {QueueLevel} | Target: {TargetLevel} | Actual Construction: {ConstructionLevel}", + plan.Type, currentBuilding?.Level ?? 0, queueBuilding?.Level ?? 0, plan.Level, actualConstructionLevel); + + // SADECE target level'a ulaþtýðýnda job'u sil! + if (actualConstructionLevel >= plan.Level) + { + logger.Information("=== TARGET REACHED === Target level {TargetLevel} reached with construction level {ConstructionLevel} - deleting job", + plan.Level, actualConstructionLevel); + + var jobToDelete = context.Jobs + .Where(x => x.VillageId == task.VillageId.Value) + .Where(x => x.Type == JobTypeEnums.NormalBuild) + .Select(x => new { x.Id, Content = x.Content }) + .AsEnumerable() + .Where(x => + { + var jobPlan = JsonSerializer.Deserialize(x.Content)!; + return jobPlan.Location == plan.Location && + jobPlan.Level == plan.Level && // ? TARGET level ile eþleþ (20)! + jobPlan.Type == plan.Type; + }) + .FirstOrDefault(); + + if (jobToDelete is not null) + { + logger.Information("=== JOB COMPLETED & DELETED === Target reached | Village: {VillageId} | JobId: {JobId} | Building: {Plan} | Iteration: #{IterationCount}", + task.VillageId, jobToDelete.Id, $"{plan.Type} at location {plan.Location} to level {plan.Level}", iterationCount); + var deleteJobByIdCommand = scope.ServiceProvider.GetRequiredService(); + await deleteJobByIdCommand.HandleAsync(new(new JobId(jobToDelete.Id)), cancellationToken); + + var rxQueue = scope.ServiceProvider.GetRequiredService(); + rxQueue.Enqueue(new JobsModified(task.VillageId)); + } + else + { + logger.Warning("=== TARGET JOB NOT FOUND === No target job found for deletion | Village: {VillageId} | Building: {Type} level {Level} at location {Location} | Iteration: #{IterationCount}", + task.VillageId, plan.Type, plan.Level, plan.Location, iterationCount); + } + } + else + { + logger.Information("=== TARGET NOT REACHED === Current construction: {ConstructionLevel}, Target: {TargetLevel} - keeping job for next iteration", + actualConstructionLevel, plan.Level); + } + logger.Information("=== STEP 4/4 === Updating building data | Village: {VillageId}", task.VillageId); + result = await updateBuildingCommand.HandleAsync(new(task.VillageId), cancellationToken); - if (result.IsFailed) return result; + if (result.IsFailed) + { + // UpdateBuilding hatasý durumunda task'ý reschedule et - silme! + task.ExecuteAt = DateTime.Now.AddMinutes(2); + logger.Warning("=== TASK RESCHEDULED === Task: {TaskName} | Village: {VillageId} | Reason: UpdateBuildingCommand failed | NextExecute: {NextExecute} | Error: {Error} | Iteration: #{IterationCount}", + "Upgrade building", task.VillageId, task.ExecuteAt.ToString("HH:mm:ss"), string.Join(", ", result.Reasons.Select(r => r.Message)), iterationCount); + return new Skip(); + } + + logger.Information("=== ITERATION #{IterationCount} COMPLETED === All steps successful | Village: {VillageId} | Building: {Type} level {Level} at location {Location} | Continuing to next job...", + iterationCount, task.VillageId, plan.Type, plan.Level, plan.Location); } } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/AccessInput.cs b/MainCore/UI/Models/Input/AccessInput.cs index 379d8827a..96e9f2bcb 100644 --- a/MainCore/UI/Models/Input/AccessInput.cs +++ b/MainCore/UI/Models/Input/AccessInput.cs @@ -1,4 +1,4 @@ -namespace MainCore.UI.Models.Input +namespace MainCore.UI.Models.Input { public partial class AccessInput : ReactiveObject { @@ -105,4 +105,4 @@ public static AccessDto ToDto(this AccessInput input) }; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/AccountInput.cs b/MainCore/UI/Models/Input/AccountInput.cs index 6a98baa95..b52328cdc 100644 --- a/MainCore/UI/Models/Input/AccountInput.cs +++ b/MainCore/UI/Models/Input/AccountInput.cs @@ -1,4 +1,4 @@ -using DynamicData; +using DynamicData; using System.Collections.ObjectModel; namespace MainCore.UI.Models.Input @@ -54,4 +54,4 @@ public static AccountDto ToDto(this AccountInput input) }; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/AccountSettingInput.cs b/MainCore/UI/Models/Input/AccountSettingInput.cs index ecac078eb..5d5e001b5 100644 --- a/MainCore/UI/Models/Input/AccountSettingInput.cs +++ b/MainCore/UI/Models/Input/AccountSettingInput.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.UserControls; namespace MainCore.UI.Models.Input @@ -77,4 +77,4 @@ public Dictionary Get() [Reactive] private bool _useStartAllButton; } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/AccountsInput.cs b/MainCore/UI/Models/Input/AccountsInput.cs index 85d550a97..a733c5bc3 100644 --- a/MainCore/UI/Models/Input/AccountsInput.cs +++ b/MainCore/UI/Models/Input/AccountsInput.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable namespace MainCore.UI.Models.Input { @@ -32,4 +32,4 @@ public Account ToEntity() }; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/NormalBuildInput.cs b/MainCore/UI/Models/Input/NormalBuildInput.cs index ac6163c75..346fca1f6 100644 --- a/MainCore/UI/Models/Input/NormalBuildInput.cs +++ b/MainCore/UI/Models/Input/NormalBuildInput.cs @@ -1,4 +1,4 @@ -using DynamicData; +using DynamicData; using Humanizer; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; @@ -60,4 +60,4 @@ public void Clear() [Reactive] private int _level; } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/ResourceBuildInput.cs b/MainCore/UI/Models/Input/ResourceBuildInput.cs index 13e401a91..3efd43466 100644 --- a/MainCore/UI/Models/Input/ResourceBuildInput.cs +++ b/MainCore/UI/Models/Input/ResourceBuildInput.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; using System.Collections.ObjectModel; @@ -31,4 +31,4 @@ public ResourceBuildInput() [Reactive] private int _level; } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Input/VillageSettingInput.cs b/MainCore/UI/Models/Input/VillageSettingInput.cs index 58d37db6d..10e998448 100644 --- a/MainCore/UI/Models/Input/VillageSettingInput.cs +++ b/MainCore/UI/Models/Input/VillageSettingInput.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.UserControls; namespace MainCore.UI.Models.Input @@ -207,4 +207,4 @@ public VillageSettingInput() }); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Output/ComboBoxItem.cs b/MainCore/UI/Models/Output/ComboBoxItem.cs index 35e96e47c..36229f993 100644 --- a/MainCore/UI/Models/Output/ComboBoxItem.cs +++ b/MainCore/UI/Models/Output/ComboBoxItem.cs @@ -1,4 +1,4 @@ -namespace MainCore.UI.Models.Output +namespace MainCore.UI.Models.Output { public class ComboBoxItem { @@ -11,4 +11,4 @@ public ComboBoxItem(T content, string name) public T Content { get; set; } public string Name { get; set; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Output/ListBoxItem.cs b/MainCore/UI/Models/Output/ListBoxItem.cs index 8ada7382d..45c71bf1e 100644 --- a/MainCore/UI/Models/Output/ListBoxItem.cs +++ b/MainCore/UI/Models/Output/ListBoxItem.cs @@ -1,4 +1,4 @@ -namespace MainCore.UI.Models.Output +namespace MainCore.UI.Models.Output { public partial class ListBoxItem : ReactiveObject { @@ -10,4 +10,4 @@ public partial class ListBoxItem : ReactiveObject [Reactive] private SplatColor _color = SplatColor.Black; } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Output/MessageBoxData.cs b/MainCore/UI/Models/Output/MessageBoxData.cs index e613e5743..0a0c01939 100644 --- a/MainCore/UI/Models/Output/MessageBoxData.cs +++ b/MainCore/UI/Models/Output/MessageBoxData.cs @@ -1,4 +1,4 @@ -namespace MainCore.UI.Models.Output +namespace MainCore.UI.Models.Output { public record struct MessageBoxData(string Title, string Message); -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Output/TaskItem.cs b/MainCore/UI/Models/Output/TaskItem.cs index 4d7e63c89..9b4618408 100644 --- a/MainCore/UI/Models/Output/TaskItem.cs +++ b/MainCore/UI/Models/Output/TaskItem.cs @@ -1,4 +1,4 @@ -using MainCore.Tasks.Base; +using MainCore.Tasks.Base; namespace MainCore.UI.Models.Output { @@ -27,4 +27,4 @@ public void CopyFrom(TaskItem taskItem) Stage = taskItem.Stage; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Output/TribeItem.cs b/MainCore/UI/Models/Output/TribeItem.cs index ff3af7a5a..4295b8ac3 100644 --- a/MainCore/UI/Models/Output/TribeItem.cs +++ b/MainCore/UI/Models/Output/TribeItem.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Drawing; namespace MainCore.UI.Models.Output @@ -33,4 +33,4 @@ public static string GetImageSource(TribeEnums tribe) public string ImageSource => GetImageSource(Tribe); public static Rectangle ImageMask => new(0, 0, 61, 61); } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Output/TroopItem.cs b/MainCore/UI/Models/Output/TroopItem.cs index b303681b8..bd07791ab 100644 --- a/MainCore/UI/Models/Output/TroopItem.cs +++ b/MainCore/UI/Models/Output/TroopItem.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Drawing; namespace MainCore.UI.Models.Output @@ -53,4 +53,4 @@ public static Rectangle GetImageMask(TroopEnums troop) public string ImageSource => GetImageSource(Troop); public Rectangle ImageMask => GetImageMask(Troop); } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Validators/AccessInputValidator.cs b/MainCore/UI/Models/Validators/AccessInputValidator.cs index 55a977511..02d6c0cbd 100644 --- a/MainCore/UI/Models/Validators/AccessInputValidator.cs +++ b/MainCore/UI/Models/Validators/AccessInputValidator.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.UI.Models.Validators { @@ -30,4 +30,4 @@ public AccessInputValidator() .WithName("Proxy's password"); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Validators/AccountInputValidator.cs b/MainCore/UI/Models/Validators/AccountInputValidator.cs index d77caeecd..72d3ba466 100644 --- a/MainCore/UI/Models/Validators/AccountInputValidator.cs +++ b/MainCore/UI/Models/Validators/AccountInputValidator.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.UI.Models.Validators { @@ -23,4 +23,4 @@ public AccountInputValidator() .WithName("Nick name"); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Validators/AccountSettingInputValidator.cs b/MainCore/UI/Models/Validators/AccountSettingInputValidator.cs index 525bd69b4..9b90e1360 100644 --- a/MainCore/UI/Models/Validators/AccountSettingInputValidator.cs +++ b/MainCore/UI/Models/Validators/AccountSettingInputValidator.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.UI.Models.Validators { @@ -30,4 +30,4 @@ public AccountSettingInputValidator() .WithMessage("Minimum farm interval ({PropertyValue}) should be positive number"); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Validators/NormalBuildInputValidator.cs b/MainCore/UI/Models/Validators/NormalBuildInputValidator.cs index aacdff21c..2099f7d3e 100644 --- a/MainCore/UI/Models/Validators/NormalBuildInputValidator.cs +++ b/MainCore/UI/Models/Validators/NormalBuildInputValidator.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.UI.Models.Validators { @@ -11,4 +11,4 @@ public NormalBuildInputValidator() .GreaterThan(0); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Validators/ResourceBuildInputValidator.cs b/MainCore/UI/Models/Validators/ResourceBuildInputValidator.cs index 8cb783902..fc1e78d52 100644 --- a/MainCore/UI/Models/Validators/ResourceBuildInputValidator.cs +++ b/MainCore/UI/Models/Validators/ResourceBuildInputValidator.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.UI.Models.Validators { @@ -11,4 +11,4 @@ public ResourceBuildInputValidator() .GreaterThan(0); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Models/Validators/VillageSettingInputValidator.cs b/MainCore/UI/Models/Validators/VillageSettingInputValidator.cs index afa7ac91d..cd1436a7b 100644 --- a/MainCore/UI/Models/Validators/VillageSettingInputValidator.cs +++ b/MainCore/UI/Models/Validators/VillageSettingInputValidator.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Input; +using MainCore.UI.Models.Input; namespace MainCore.UI.Models.Validators { @@ -53,4 +53,4 @@ public VillageSettingInputValidator() .WithMessage("Minimum workshop amount troop ({PropertyValue}) should be positive number"); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ObservableExceptionHandler.cs b/MainCore/UI/ObservableExceptionHandler.cs index 3fe5b68bf..b5bc94b36 100644 --- a/MainCore/UI/ObservableExceptionHandler.cs +++ b/MainCore/UI/ObservableExceptionHandler.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using System.Diagnostics; using System.Reactive.Concurrency; @@ -43,4 +43,4 @@ private void Handle(Exception exception) .Subscribe(); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/Stores/AccountTabStore.cs b/MainCore/UI/Stores/AccountTabStore.cs index 27e3add43..81bc6987e 100644 --- a/MainCore/UI/Stores/AccountTabStore.cs +++ b/MainCore/UI/Stores/AccountTabStore.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.Tabs; namespace MainCore.UI.Stores @@ -97,4 +97,4 @@ public void SetTabType(AccountTabType tabType) public DebugViewModel DebugViewModel => _debugViewModel; public FarmingViewModel FarmingViewModel => _farmingViewModel; } -} \ No newline at end of file +} diff --git a/MainCore/UI/Stores/SelectedItemStore.cs b/MainCore/UI/Stores/SelectedItemStore.cs index b421167db..0865b118c 100644 --- a/MainCore/UI/Stores/SelectedItemStore.cs +++ b/MainCore/UI/Stores/SelectedItemStore.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.Stores @@ -44,4 +44,4 @@ public SelectedItemStore() [ObservableAsProperty] private bool _isVillageNotSelected; } -} \ No newline at end of file +} diff --git a/MainCore/UI/Stores/VillageTabStore.cs b/MainCore/UI/Stores/VillageTabStore.cs index 44f4cafe2..992aaa9d7 100644 --- a/MainCore/UI/Stores/VillageTabStore.cs +++ b/MainCore/UI/Stores/VillageTabStore.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.Tabs.Villages; namespace MainCore.UI.Stores @@ -62,4 +62,4 @@ public void SetTabType(VillageTabType tabType) public VillageSettingViewModel VillageSettingViewModel => _villageSettingViewModel; public InfoViewModel InfoViewModel => _infoViewModel; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Abstract/AccountTabViewModelBase.cs b/MainCore/UI/ViewModels/Abstract/AccountTabViewModelBase.cs index c59942d0e..8d69528d4 100644 --- a/MainCore/UI/ViewModels/Abstract/AccountTabViewModelBase.cs +++ b/MainCore/UI/ViewModels/Abstract/AccountTabViewModelBase.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Stores; +using MainCore.UI.Stores; namespace MainCore.UI.ViewModels.Abstract { @@ -40,4 +40,4 @@ protected override async Task OnActive() protected abstract Task Load(AccountId accountId); } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Abstract/TabViewModelBase.cs b/MainCore/UI/ViewModels/Abstract/TabViewModelBase.cs index fddef08af..0cb79175e 100644 --- a/MainCore/UI/ViewModels/Abstract/TabViewModelBase.cs +++ b/MainCore/UI/ViewModels/Abstract/TabViewModelBase.cs @@ -1,4 +1,4 @@ -namespace MainCore.UI.ViewModels.Abstract +namespace MainCore.UI.ViewModels.Abstract { public abstract partial class TabViewModelBase : ViewModelBase { @@ -35,4 +35,4 @@ protected virtual Task OnDeactive() return Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Abstract/ViewModelBase.cs b/MainCore/UI/ViewModels/Abstract/ViewModelBase.cs index ced814d4a..2cce8efeb 100644 --- a/MainCore/UI/ViewModels/Abstract/ViewModelBase.cs +++ b/MainCore/UI/ViewModels/Abstract/ViewModelBase.cs @@ -1,4 +1,4 @@ -namespace MainCore.UI.ViewModels.Abstract +namespace MainCore.UI.ViewModels.Abstract { public abstract class ViewModelBase : ReactiveObject; -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Abstract/VillageTabViewModelBase.cs b/MainCore/UI/ViewModels/Abstract/VillageTabViewModelBase.cs index dd2ad1c16..25fb2b5a4 100644 --- a/MainCore/UI/ViewModels/Abstract/VillageTabViewModelBase.cs +++ b/MainCore/UI/ViewModels/Abstract/VillageTabViewModelBase.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Stores; +using MainCore.UI.Stores; namespace MainCore.UI.ViewModels.Abstract { @@ -49,4 +49,4 @@ protected override async Task OnActive() protected abstract Task Load(VillageId villageId); } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/MainViewModel.cs b/MainCore/UI/ViewModels/MainViewModel.cs index 399d08b19..47938edd4 100644 --- a/MainCore/UI/ViewModels/MainViewModel.cs +++ b/MainCore/UI/ViewModels/MainViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.UserControls; using Microsoft.Extensions.DependencyInjection; @@ -25,15 +25,15 @@ private async Task Load() await _waitingOverlayViewModel.Show(); using (var scope = _serviceScopeFactory.CreateScope()) { - await _waitingOverlayViewModel.ChangeMessage("installing chrome driver"); - var chromeDriverInstaller = scope.ServiceProvider.GetRequiredService(); + await _waitingOverlayViewModel.ChangeMessage("installing browser driver"); + var browserDriverInstaller = scope.ServiceProvider.GetRequiredService(); var useragentManager = scope.ServiceProvider.GetRequiredService(); - var installChromeDriver = Observable.StartAsync(chromeDriverInstaller.Install, RxApp.TaskpoolScheduler); + var installBrowserDriver = Observable.StartAsync(browserDriverInstaller.Install, RxApp.TaskpoolScheduler); var loadUseragent = Observable.StartAsync(useragentManager.Load, RxApp.TaskpoolScheduler); - var chromeManager = scope.ServiceProvider.GetRequiredService(); + var browserManager = scope.ServiceProvider.GetRequiredService(); var context = scope.ServiceProvider.GetRequiredService(); - var installExtension = Observable.Start(chromeManager.LoadExtension, RxApp.TaskpoolScheduler); + var installExtension = Observable.Start(browserManager.LoadExtension, RxApp.TaskpoolScheduler); var loadDatabase = Observable.StartAsync(async () => { var notExist = await context.Database.EnsureCreatedAsync(); @@ -49,7 +49,7 @@ private async Task Load() } }, RxApp.TaskpoolScheduler); - await Observable.Merge(installExtension, loadDatabase, installChromeDriver, loadUseragent); + await Observable.Merge(installExtension, loadDatabase, installBrowserDriver, loadUseragent); await _waitingOverlayViewModel.ChangeMessage("loading program layout"); MainLayoutViewModel = scope.ServiceProvider.GetRequiredService(); @@ -64,8 +64,8 @@ private async Task Unload() { using (var scope = _serviceScopeFactory.CreateScope()) { - var chromeManager = scope.ServiceProvider.GetRequiredService(); - await chromeManager.Shutdown(); + var browserManager = scope.ServiceProvider.GetRequiredService(); + await browserManager.Shutdown(); var path = Path.Combine(AppContext.BaseDirectory, "Plugins"); if (Directory.Exists(path)) @@ -78,4 +78,4 @@ private async Task Unload() } } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/AccountSettingViewModel.cs b/MainCore/UI/ViewModels/Tabs/AccountSettingViewModel.cs index c8ff05edd..ea91f3113 100644 --- a/MainCore/UI/ViewModels/Tabs/AccountSettingViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/AccountSettingViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.Misc; +using MainCore.Commands.UI.Misc; using MainCore.UI.Models.Input; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; @@ -106,4 +106,4 @@ private Dictionary LoadSettings(AccountId accountId) return settings; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/AddAccountViewModel.cs b/MainCore/UI/ViewModels/Tabs/AddAccountViewModel.cs index 8f8f7e9c7..0b848db37 100644 --- a/MainCore/UI/ViewModels/Tabs/AddAccountViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/AddAccountViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.AddAccountViewModel; +using MainCore.Commands.UI.AddAccountViewModel; using MainCore.UI.Models.Input; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; @@ -113,4 +113,4 @@ private async Task AddAccount() [Reactive] private AccessInput? _selectedAccess; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/AddAccountsViewModel.cs b/MainCore/UI/ViewModels/Tabs/AddAccountsViewModel.cs index 0cb414560..f05080115 100644 --- a/MainCore/UI/ViewModels/Tabs/AddAccountsViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/AddAccountsViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.AddAccountsViewModel; +using MainCore.Commands.UI.AddAccountsViewModel; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.UserControls; @@ -116,4 +116,4 @@ private static List Parse(string input) }; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/DebugViewModel.cs b/MainCore/UI/ViewModels/Tabs/DebugViewModel.cs index 4fb2e85b8..83c70f33d 100644 --- a/MainCore/UI/ViewModels/Tabs/DebugViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/DebugViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; using Serilog.Events; using Serilog.Templates; @@ -144,7 +144,9 @@ private string LoadLog(AccountId accountId) private string ReloadLog() { using var sw = new StringWriter(new StringBuilder()); - foreach (var log in _logEvents) + // Thread safety için snapshot alýyorum - collection'ýn o anki durumunu kopyalýyorum + var logSnapshot = _logEvents.ToArray(); + foreach (var log in logSnapshot) { _template.Format(log, sw); } @@ -177,4 +179,4 @@ private void Right() }); } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/EditAccountViewModel.cs b/MainCore/UI/ViewModels/Tabs/EditAccountViewModel.cs index 9dde0985e..1df89419f 100644 --- a/MainCore/UI/ViewModels/Tabs/EditAccountViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/EditAccountViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.EditAccountViewModel; +using MainCore.Commands.UI.EditAccountViewModel; using MainCore.UI.Models.Input; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; @@ -124,4 +124,4 @@ private void SetAccount(AccountDto account) [Reactive] private AccessInput? _selectedAccess; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/FarmingViewModel.cs b/MainCore/UI/ViewModels/Tabs/FarmingViewModel.cs index f0351ba7d..7efdadc09 100644 --- a/MainCore/UI/ViewModels/Tabs/FarmingViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/FarmingViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.Misc; +using MainCore.Commands.UI.Misc; using MainCore.UI.Models.Input; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; @@ -197,4 +197,4 @@ private int CountActive(AccountId accountId) return count; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/NoAccountViewModel.cs b/MainCore/UI/ViewModels/Tabs/NoAccountViewModel.cs index b65910b78..ae5187a0f 100644 --- a/MainCore/UI/ViewModels/Tabs/NoAccountViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/NoAccountViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.ViewModels.Tabs { @@ -6,4 +6,4 @@ namespace MainCore.UI.ViewModels.Tabs public class NoAccountViewModel : TabViewModelBase { } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/VillageViewModel.cs b/MainCore/UI/ViewModels/Tabs/VillageViewModel.cs index 52f88202b..501e7707b 100644 --- a/MainCore/UI/ViewModels/Tabs/VillageViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/VillageViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using MainCore.UI.Stores; using MainCore.UI.ViewModels.Abstract; using MainCore.UI.ViewModels.UserControls; @@ -128,4 +128,4 @@ private List LoadVillage(AccountId accountId) return items; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/Villages/BuildViewModel.cs b/MainCore/UI/ViewModels/Tabs/Villages/BuildViewModel.cs index de7e65c56..bc7b6bcd7 100644 --- a/MainCore/UI/ViewModels/Tabs/Villages/BuildViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/Villages/BuildViewModel.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; using MainCore.Commands.UI.Villages.BuildViewModel; using MainCore.UI.Models.Input; using MainCore.UI.Models.Output; @@ -522,4 +522,4 @@ private bool IsAccountPaused(AccountId accountId) return true; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/Villages/InfoViewModel.cs b/MainCore/UI/ViewModels/Tabs/Villages/InfoViewModel.cs index 570bba906..6ecfd5a38 100644 --- a/MainCore/UI/ViewModels/Tabs/Villages/InfoViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/Villages/InfoViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.ViewModels.Tabs.Villages { @@ -10,4 +10,4 @@ protected override Task Load(VillageId villageId) return Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/Villages/NoVillageViewModel.cs b/MainCore/UI/ViewModels/Tabs/Villages/NoVillageViewModel.cs index 24ab62de2..d3ca6123b 100644 --- a/MainCore/UI/ViewModels/Tabs/Villages/NoVillageViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/Villages/NoVillageViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.ViewModels.Tabs.Villages { @@ -6,4 +6,4 @@ namespace MainCore.UI.ViewModels.Tabs.Villages public class NoVillageViewModel : TabViewModelBase { } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/Tabs/Villages/VillageSettingViewModel.cs b/MainCore/UI/ViewModels/Tabs/Villages/VillageSettingViewModel.cs index 8e86d8328..4081e97bd 100644 --- a/MainCore/UI/ViewModels/Tabs/Villages/VillageSettingViewModel.cs +++ b/MainCore/UI/ViewModels/Tabs/Villages/VillageSettingViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.Misc; +using MainCore.Commands.UI.Misc; using MainCore.UI.Models.Input; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; @@ -112,4 +112,4 @@ private Dictionary LoadSetting(VillageId villageId) return settings; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/AmountInputViewModel.cs b/MainCore/UI/ViewModels/UserControls/AmountInputViewModel.cs index 9b55bf23a..9c815faf1 100644 --- a/MainCore/UI/ViewModels/UserControls/AmountInputViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/AmountInputViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.ViewModels.UserControls { @@ -11,4 +11,4 @@ public partial class AmountInputViewModel : ViewModelBase public void Set(int value) => Value = value; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/IWaitingOverlayViewModel.cs b/MainCore/UI/ViewModels/UserControls/IWaitingOverlayViewModel.cs index f11eac296..283693c1f 100644 --- a/MainCore/UI/ViewModels/UserControls/IWaitingOverlayViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/IWaitingOverlayViewModel.cs @@ -1,4 +1,4 @@ - + namespace MainCore.UI.ViewModels.UserControls { public interface IWaitingOverlayViewModel @@ -8,4 +8,4 @@ public interface IWaitingOverlayViewModel Task Show(); Task Show(string message); } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/ListBoxItemViewModel.cs b/MainCore/UI/ViewModels/UserControls/ListBoxItemViewModel.cs index a26502c88..40d0278c9 100644 --- a/MainCore/UI/ViewModels/UserControls/ListBoxItemViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/ListBoxItemViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; using System.Collections.ObjectModel; @@ -76,4 +76,4 @@ public ListBoxItem this[int i] public int Count => Items.Count; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/MainLayoutViewModel.cs b/MainCore/UI/ViewModels/UserControls/MainLayoutViewModel.cs index 553906ba7..e7d9abe0a 100644 --- a/MainCore/UI/ViewModels/UserControls/MainLayoutViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/MainLayoutViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.Commands.UI.MainLayoutViewModel; +using MainCore.Commands.UI.MainLayoutViewModel; using MainCore.UI.Models.Output; using MainCore.UI.Stores; using MainCore.UI.ViewModels.Abstract; @@ -361,4 +361,4 @@ private void SetPauseText(StatusEnums status) [Reactive] private string _pauseText = "[~~!~~]"; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/RangeInputViewModel.cs b/MainCore/UI/ViewModels/UserControls/RangeInputViewModel.cs index 22c758fee..697b3e6f4 100644 --- a/MainCore/UI/ViewModels/UserControls/RangeInputViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/RangeInputViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.ViewModels.UserControls { @@ -21,4 +21,4 @@ public void Set(int min, int max) [Reactive] private int _max; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/ResourceInputViewModel.cs b/MainCore/UI/ViewModels/UserControls/ResourceInputViewModel.cs index 08f7d3007..172cde6d6 100644 --- a/MainCore/UI/ViewModels/UserControls/ResourceInputViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/ResourceInputViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; namespace MainCore.UI.ViewModels.UserControls { @@ -29,4 +29,4 @@ public void Set(int wood, int clay, int iron, int crop) [Reactive] private int _crop; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/TribeSelectorViewModel.cs b/MainCore/UI/ViewModels/UserControls/TribeSelectorViewModel.cs index 79ecdc3af..8e050325a 100644 --- a/MainCore/UI/ViewModels/UserControls/TribeSelectorViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/TribeSelectorViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; using System.Collections.ObjectModel; @@ -33,4 +33,4 @@ public TribeEnums Get() [Reactive] private TribeItem _selectedItem; } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/TroopSelectorViewModel.cs b/MainCore/UI/ViewModels/UserControls/TroopSelectorViewModel.cs index 43c3f1b09..f7b5effdb 100644 --- a/MainCore/UI/ViewModels/UserControls/TroopSelectorViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/TroopSelectorViewModel.cs @@ -1,4 +1,4 @@ -using DynamicData; +using DynamicData; using MainCore.UI.Models.Output; using MainCore.UI.ViewModels.Abstract; using System.Collections.ObjectModel; @@ -164,4 +164,4 @@ private static List GetSiegeTroops(TribeEnums tribe) }; } } -} \ No newline at end of file +} diff --git a/MainCore/UI/ViewModels/UserControls/WaitingOverlayViewModel.cs b/MainCore/UI/ViewModels/UserControls/WaitingOverlayViewModel.cs index a101079e6..9b06f2f36 100644 --- a/MainCore/UI/ViewModels/UserControls/WaitingOverlayViewModel.cs +++ b/MainCore/UI/ViewModels/UserControls/WaitingOverlayViewModel.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Abstract; +using MainCore.UI.ViewModels.Abstract; using Microsoft.Extensions.DependencyInjection; namespace MainCore.UI.ViewModels.UserControls @@ -62,4 +62,4 @@ public string Message } } } -} \ No newline at end of file +} diff --git a/MainCore/packages.lock.json b/MainCore/packages.lock.json index 2ed9eac74..2e5ac08a0 100644 --- a/MainCore/packages.lock.json +++ b/MainCore/packages.lock.json @@ -158,18 +158,18 @@ }, "Selenium.Support": { "type": "Direct", - "requested": "[4.35.0, )", - "resolved": "4.35.0", - "contentHash": "K6DiLdDQNDSWI/zh14bk9vRjW8vEX2mhMI2Cq8bD72FM5rOnLhQwZABJHrWCZ3U1wb7KFFd7iQeUhvpyHVt2AA==", + "requested": "[4.34.0, )", + "resolved": "4.34.0", + "contentHash": "+GROvBcro1ayU9PlGu4Q6xGyRk+wlbwGrmL3lZqm+s6T+NVs0Fmp2r8t6DvKMY/ZaDh9svkvxqN808+EnFPhDw==", "dependencies": { - "Selenium.WebDriver": "4.35.0" + "Selenium.WebDriver": "4.34.0" } }, "Selenium.WebDriver": { "type": "Direct", - "requested": "[4.35.0, )", - "resolved": "4.35.0", - "contentHash": "C9OrObS2KLyUQDJNRY883JAc4n5q58pBYmY954hf+lGMQQcsz4Ias2mk91lHkLXFX+2qMttya4df9lGP1SLiGQ==" + "requested": "[4.34.0, )", + "resolved": "4.34.0", + "contentHash": "uNx+GF7WugHDPV2zpGDPlbSn3STQ6n0xFskFSeJdhEuuUTkIDfAsYnjVUiQidWDpy7mKzTz53ae7Thjghl3dng==" }, "Serilog": { "type": "Direct", @@ -705,12 +705,6 @@ } }, "net8.0/win-x64": { - "Selenium.WebDriver": { - "type": "Direct", - "requested": "[4.35.0, )", - "resolved": "4.35.0", - "contentHash": "C9OrObS2KLyUQDJNRY883JAc4n5q58pBYmY954hf+lGMQQcsz4Ias2mk91lHkLXFX+2qMttya4df9lGP1SLiGQ==" - }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", "resolved": "2.1.10", diff --git a/WPFUI/App.xaml.cs b/WPFUI/App.xaml.cs index 1db8659a9..8adffa2c2 100644 --- a/WPFUI/App.xaml.cs +++ b/WPFUI/App.xaml.cs @@ -1,4 +1,4 @@ -using MainCore; +using MainCore; using MainCore.Services; using MainCore.UI; using Microsoft.Extensions.DependencyInjection; @@ -103,4 +103,4 @@ private static string OpenFileDialog() return ofd.FileName; } } -} \ No newline at end of file +} diff --git a/WPFUI/Converter/DrawingColorToMediaColorTypeConverter.cs b/WPFUI/Converter/DrawingColorToMediaColorTypeConverter.cs index 0ac2008ad..efde813d9 100644 --- a/WPFUI/Converter/DrawingColorToMediaColorTypeConverter.cs +++ b/WPFUI/Converter/DrawingColorToMediaColorTypeConverter.cs @@ -1,4 +1,4 @@ -using Splat; +using Splat; using System; using System.Globalization; using System.Windows.Data; @@ -23,4 +23,4 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu return SplatColor.FromArgb(color.A, color.R, color.G, color.B); } } -} \ No newline at end of file +} diff --git a/WPFUI/Converter/TribeItemToCroppedBitmap.cs b/WPFUI/Converter/TribeItemToCroppedBitmap.cs index ccdba9dc1..b7679f510 100644 --- a/WPFUI/Converter/TribeItemToCroppedBitmap.cs +++ b/WPFUI/Converter/TribeItemToCroppedBitmap.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using System; using System.Globalization; using System.Windows; @@ -35,4 +35,4 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/WPFUI/Converter/TroopItemToCroppedBitmap.cs b/WPFUI/Converter/TroopItemToCroppedBitmap.cs index 6c01a0bfb..1a54bc364 100644 --- a/WPFUI/Converter/TroopItemToCroppedBitmap.cs +++ b/WPFUI/Converter/TroopItemToCroppedBitmap.cs @@ -1,4 +1,4 @@ -using MainCore.UI.Models.Output; +using MainCore.UI.Models.Output; using System; using System.Globalization; using System.Windows; @@ -35,4 +35,4 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/MainWindow.xaml.cs b/WPFUI/Views/MainWindow.xaml.cs index 2838296df..3494091c6 100644 --- a/WPFUI/Views/MainWindow.xaml.cs +++ b/WPFUI/Views/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels; +using MainCore.UI.ViewModels; using MainCore.UI.ViewModels.UserControls; using ReactiveMarbles.Extensions.Hosting.Wpf; using ReactiveUI; @@ -65,4 +65,4 @@ private async void OnClosing(object sender, CancelEventArgs e) Close(); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/AccountSettingTab.xaml.cs b/WPFUI/Views/Tabs/AccountSettingTab.xaml.cs index 692084dab..ed932915c 100644 --- a/WPFUI/Views/Tabs/AccountSettingTab.xaml.cs +++ b/WPFUI/Views/Tabs/AccountSettingTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -33,4 +33,4 @@ public AccountSettingTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/AddAccountTab.xaml.cs b/WPFUI/Views/Tabs/AddAccountTab.xaml.cs index 44d65aac6..de8253eff 100644 --- a/WPFUI/Views/Tabs/AddAccountTab.xaml.cs +++ b/WPFUI/Views/Tabs/AddAccountTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -37,4 +37,4 @@ public AddAccountTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/AddAccountsTab.xaml.cs b/WPFUI/Views/Tabs/AddAccountsTab.xaml.cs index f48cd752c..fc5e04b92 100644 --- a/WPFUI/Views/Tabs/AddAccountsTab.xaml.cs +++ b/WPFUI/Views/Tabs/AddAccountsTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -26,4 +26,4 @@ public AddAccountsTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/DebugTag.xaml.cs b/WPFUI/Views/Tabs/DebugTag.xaml.cs index a5be193e2..b757f4297 100644 --- a/WPFUI/Views/Tabs/DebugTag.xaml.cs +++ b/WPFUI/Views/Tabs/DebugTag.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -27,4 +27,4 @@ public DebugTag() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/EditAccountTab.xaml.cs b/WPFUI/Views/Tabs/EditAccountTab.xaml.cs index 8cc07e0d0..4b8b488b9 100644 --- a/WPFUI/Views/Tabs/EditAccountTab.xaml.cs +++ b/WPFUI/Views/Tabs/EditAccountTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -39,4 +39,4 @@ public EditAccountTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/FarmingTab.xaml.cs b/WPFUI/Views/Tabs/FarmingTab.xaml.cs index 7f28392b6..01b686022 100644 --- a/WPFUI/Views/Tabs/FarmingTab.xaml.cs +++ b/WPFUI/Views/Tabs/FarmingTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -34,4 +34,4 @@ public FarmingTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/NoAccountTab.xaml.cs b/WPFUI/Views/Tabs/NoAccountTab.xaml.cs index f6650ca01..daf5daefe 100644 --- a/WPFUI/Views/Tabs/NoAccountTab.xaml.cs +++ b/WPFUI/Views/Tabs/NoAccountTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; namespace WPFUI.Views.Tabs @@ -17,4 +17,4 @@ public NoAccountTab() InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/VillageTab.xaml.cs b/WPFUI/Views/Tabs/VillageTab.xaml.cs index 381b7be25..7b6d59122 100644 --- a/WPFUI/Views/Tabs/VillageTab.xaml.cs +++ b/WPFUI/Views/Tabs/VillageTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs; +using MainCore.UI.ViewModels.Tabs; using ReactiveUI; using System.Reactive.Disposables; @@ -45,4 +45,4 @@ public VillageTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/Villages/BuildTab.xaml.cs b/WPFUI/Views/Tabs/Villages/BuildTab.xaml.cs index 61d8c958d..5411fcca4 100644 --- a/WPFUI/Views/Tabs/Villages/BuildTab.xaml.cs +++ b/WPFUI/Views/Tabs/Villages/BuildTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs.Villages; +using MainCore.UI.ViewModels.Tabs.Villages; using ReactiveUI; using System.Reactive.Disposables; @@ -55,4 +55,4 @@ public BuildTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/Villages/InfoTab.xaml.cs b/WPFUI/Views/Tabs/Villages/InfoTab.xaml.cs index d18242454..76547ad4b 100644 --- a/WPFUI/Views/Tabs/Villages/InfoTab.xaml.cs +++ b/WPFUI/Views/Tabs/Villages/InfoTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs.Villages; +using MainCore.UI.ViewModels.Tabs.Villages; using ReactiveUI; namespace WPFUI.Views.Tabs.Villages @@ -17,4 +17,4 @@ public InfoTab() InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/Villages/NoVillageTab.xaml.cs b/WPFUI/Views/Tabs/Villages/NoVillageTab.xaml.cs index 08f2046f7..108ae2667 100644 --- a/WPFUI/Views/Tabs/Villages/NoVillageTab.xaml.cs +++ b/WPFUI/Views/Tabs/Villages/NoVillageTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs.Villages; +using MainCore.UI.ViewModels.Tabs.Villages; using ReactiveUI; namespace WPFUI.Views.Tabs.Villages @@ -17,4 +17,4 @@ public NoVillageTab() InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/Tabs/Villages/VillageSettingTab.xaml.cs b/WPFUI/Views/Tabs/Villages/VillageSettingTab.xaml.cs index 2cdd457a6..c0434aea1 100644 --- a/WPFUI/Views/Tabs/Villages/VillageSettingTab.xaml.cs +++ b/WPFUI/Views/Tabs/Villages/VillageSettingTab.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.Tabs.Villages; +using MainCore.UI.ViewModels.Tabs.Villages; using ReactiveUI; using System.Reactive.Disposables; @@ -56,4 +56,4 @@ public VillageSettingTab() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/AmountInputUc.xaml.cs b/WPFUI/Views/UserControls/AmountInputUc.xaml.cs index 601c40ea0..b86464fc4 100644 --- a/WPFUI/Views/UserControls/AmountInputUc.xaml.cs +++ b/WPFUI/Views/UserControls/AmountInputUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; using System.Windows; @@ -42,4 +42,4 @@ public string Unit set => SetValue(UnitProperty, value); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/MainLayoutUc.xaml.cs b/WPFUI/Views/UserControls/MainLayoutUc.xaml.cs index 6959378f2..b8a8710b1 100644 --- a/WPFUI/Views/UserControls/MainLayoutUc.xaml.cs +++ b/WPFUI/Views/UserControls/MainLayoutUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; @@ -68,4 +68,4 @@ public MainLayoutUc() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/RangeInputUc.xaml.cs b/WPFUI/Views/UserControls/RangeInputUc.xaml.cs index f466fb38f..3821f0cc7 100644 --- a/WPFUI/Views/UserControls/RangeInputUc.xaml.cs +++ b/WPFUI/Views/UserControls/RangeInputUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; using System.Windows; @@ -42,4 +42,4 @@ public string Unit set => SetValue(UnitProperty, value); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/ResourceInputUc.xaml.cs b/WPFUI/Views/UserControls/ResourceInputUc.xaml.cs index 8c8e99918..8a7882184 100644 --- a/WPFUI/Views/UserControls/ResourceInputUc.xaml.cs +++ b/WPFUI/Views/UserControls/ResourceInputUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; using System.Windows; @@ -35,4 +35,4 @@ public string Text set => SetValue(TextProperty, value); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/TribeSelectorUc.xaml.cs b/WPFUI/Views/UserControls/TribeSelectorUc.xaml.cs index d8a5a22d4..a16ded8b7 100644 --- a/WPFUI/Views/UserControls/TribeSelectorUc.xaml.cs +++ b/WPFUI/Views/UserControls/TribeSelectorUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; using System.Windows; @@ -33,4 +33,4 @@ public string Text set => SetValue(TextProperty, value); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/TroopSelectorUc.xaml.cs b/WPFUI/Views/UserControls/TroopSelectorUc.xaml.cs index d3694d42b..0f50aa6c3 100644 --- a/WPFUI/Views/UserControls/TroopSelectorUc.xaml.cs +++ b/WPFUI/Views/UserControls/TroopSelectorUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; using System.Windows; @@ -33,4 +33,4 @@ public string Text set => SetValue(TextProperty, value); } } -} \ No newline at end of file +} diff --git a/WPFUI/Views/UserControls/WaitingOverlayUc.xaml.cs b/WPFUI/Views/UserControls/WaitingOverlayUc.xaml.cs index a78ab870b..411d2f126 100644 --- a/WPFUI/Views/UserControls/WaitingOverlayUc.xaml.cs +++ b/WPFUI/Views/UserControls/WaitingOverlayUc.xaml.cs @@ -1,4 +1,4 @@ -using MainCore.UI.ViewModels.UserControls; +using MainCore.UI.ViewModels.UserControls; using ReactiveUI; using System.Reactive.Disposables; @@ -24,4 +24,4 @@ public WaitingOverlayUc() }); } } -} \ No newline at end of file +} diff --git a/WPFUI/packages.lock.json b/WPFUI/packages.lock.json index f0a7287dc..d33b0d791 100644 --- a/WPFUI/packages.lock.json +++ b/WPFUI/packages.lock.json @@ -564,16 +564,16 @@ }, "Selenium.Support": { "type": "Transitive", - "resolved": "4.35.0", - "contentHash": "K6DiLdDQNDSWI/zh14bk9vRjW8vEX2mhMI2Cq8bD72FM5rOnLhQwZABJHrWCZ3U1wb7KFFd7iQeUhvpyHVt2AA==", + "resolved": "4.34.0", + "contentHash": "+GROvBcro1ayU9PlGu4Q6xGyRk+wlbwGrmL3lZqm+s6T+NVs0Fmp2r8t6DvKMY/ZaDh9svkvxqN808+EnFPhDw==", "dependencies": { - "Selenium.WebDriver": "4.35.0" + "Selenium.WebDriver": "4.34.0" } }, "Selenium.WebDriver": { "type": "Transitive", - "resolved": "4.35.0", - "contentHash": "C9OrObS2KLyUQDJNRY883JAc4n5q58pBYmY954hf+lGMQQcsz4Ias2mk91lHkLXFX+2qMttya4df9lGP1SLiGQ==" + "resolved": "4.34.0", + "contentHash": "uNx+GF7WugHDPV2zpGDPlbSn3STQ6n0xFskFSeJdhEuuUTkIDfAsYnjVUiQidWDpy7mKzTz53ae7Thjghl3dng==" }, "Serilog": { "type": "Transitive", @@ -743,8 +743,8 @@ "Polly": "[8.6.2, )", "ReactiveUI": "[20.4.1, )", "Riok.Mapperly": "[4.2.1, )", - "Selenium.Support": "[4.35.0, )", - "Selenium.WebDriver": "[4.35.0, )", + "Selenium.Support": "[4.34.0, )", + "Selenium.WebDriver": "[4.34.0, )", "Serilog": "[4.3.0, )", "Serilog.Expressions": "[5.0.0, )", "Serilog.Extensions.Hosting": "[9.0.0, )", @@ -757,11 +757,6 @@ } }, "net8.0-windows10.0.19041/win-x64": { - "Selenium.WebDriver": { - "type": "Transitive", - "resolved": "4.35.0", - "contentHash": "C9OrObS2KLyUQDJNRY883JAc4n5q58pBYmY954hf+lGMQQcsz4Ias2mk91lHkLXFX+2qMttya4df9lGP1SLiGQ==" - }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", "resolved": "2.1.10",