diff --git a/PuppeteerExtraSharp/PuppeteerExtraSharp.sln b/PuppeteerExtraSharp.sln similarity index 89% rename from PuppeteerExtraSharp/PuppeteerExtraSharp.sln rename to PuppeteerExtraSharp.sln index f6d2bab..9cb4874 100644 --- a/PuppeteerExtraSharp/PuppeteerExtraSharp.sln +++ b/PuppeteerExtraSharp.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PuppeteerExtraSharp", "PuppeteerExtraSharp.csproj", "{B2221B32-DFAE-4F87-B0EC-EE4922B81F64}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PuppeteerExtraSharp", "PuppeteerExtraSharp\PuppeteerExtraSharp.csproj", "{B2221B32-DFAE-4F87-B0EC-EE4922B81F64}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extra.Tests", "..\tests\Extra.Tests.csproj", "{FB432A25-A844-4C78-9194-E46D806B46E5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extra.Tests", "Tests\Extra.Tests.csproj", "{FB432A25-A844-4C78-9194-E46D806B46E5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/PuppeteerExtraSharp/Plugins/AnonymizeUa/AnonymizeUaPlugin.cs b/PuppeteerExtraSharp/Plugins/AnonymizeUa/AnonymizeUaPlugin.cs index 5a104dc..2d96c47 100644 --- a/PuppeteerExtraSharp/Plugins/AnonymizeUa/AnonymizeUaPlugin.cs +++ b/PuppeteerExtraSharp/Plugins/AnonymizeUa/AnonymizeUaPlugin.cs @@ -5,12 +5,8 @@ namespace PuppeteerExtraSharp.Plugins.AnonymizeUa { - public class AnonymizeUaPlugin: PuppeteerExtraPlugin + public class AnonymizeUaPlugin() : PuppeteerExtraPlugin("anonymize-ua") { - public AnonymizeUaPlugin(): base("anonymize-ua") - { - } - private Func _customAction; public void CustomizeUa(Func uaAction) { diff --git a/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/Languages.cs b/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/Languages.cs index 2aa7d84..ac701ba 100644 --- a/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/Languages.cs +++ b/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/Languages.cs @@ -1,32 +1,34 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using PuppeteerSharp; namespace PuppeteerExtraSharp.Plugins.ExtraStealth.Evasions { - public class Languages : PuppeteerExtraPlugin + public class Languages(StealthLanguagesOptions options = null) : PuppeteerExtraPlugin("stealth-language") { - public StealthLanguagesOptions Options { get; } + public StealthLanguagesOptions Options { get; } = options ?? new StealthLanguagesOptions("en-US", "en"); - public Languages(StealthLanguagesOptions options = null) : base("stealth-language") + public override async Task OnPageCreated(IPage page) { - Options = options ?? new StealthLanguagesOptions("en-US", "en"); - } + if (Options.Languages.Length > 0) + { + await page.SetExtraHttpHeadersAsync(new() + { + ["Accept-Language"] = string.Join(',', Options.Languages), + }); + + List langs = Options.Languages.Select(l => "\"" + l.ToString() + "\"").ToList(); + await page.EvaluateExpressionOnNewDocumentAsync("Object.defineProperty(Object.getPrototypeOf(navigator), 'languages', { get: function () { '[native code]'; return '[" + string.Join(",", langs) + "]'; } });"); + } - public override Task OnPageCreated(IPage page) - { var script = Utils.GetScript("Language.js"); - return Utils.EvaluateOnNewPage(page,script, Options.Languages); + await Utils.EvaluateOnNewPage(page, script, Options.Languages); } } - public class StealthLanguagesOptions : IPuppeteerExtraPluginOptions + public class StealthLanguagesOptions(params string[] languages) : IPuppeteerExtraPluginOptions { - public object[] Languages { get; } - - public StealthLanguagesOptions(params string[] languages) - { - Languages = languages.Cast().ToArray(); - } + public object[] Languages { get; } = languages.Cast().ToArray(); } } diff --git a/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/SourceUrl.cs b/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/SourceUrl.cs index bbb2e5c..2f35f56 100644 --- a/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/SourceUrl.cs +++ b/PuppeteerExtraSharp/Plugins/ExtraStealth/Evasions/SourceUrl.cs @@ -10,7 +10,7 @@ public SourceUrl() : base("SourceUrl") { } - public override async Task OnPageCreated(IPage page) + public override Task OnPageCreated(IPage page) { var mainWordProperty = page.MainFrame.GetType().GetProperty("MainWorld", BindingFlags.NonPublic @@ -31,6 +31,8 @@ public override async Task OnPageCreated(IPage page) suffixField?.SetValue(execution, "//# sourceURL=''"); } }; + + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/PuppeteerExtraSharp/Plugins/ExtraStealth/StealthPlugin.cs b/PuppeteerExtraSharp/Plugins/ExtraStealth/StealthPlugin.cs index f59dedf..2893c06 100644 --- a/PuppeteerExtraSharp/Plugins/ExtraStealth/StealthPlugin.cs +++ b/PuppeteerExtraSharp/Plugins/ExtraStealth/StealthPlugin.cs @@ -19,25 +19,25 @@ public StealthPlugin(params IPuppeteerExtraPluginOptions[] options) : base("stea private List GetStandardEvasions() { - return new List() - { - new WebDriver(), - // new ChromeApp(), - new ChromeSci(), - new ChromeRuntime(), - new Codec(), - new Languages(GetOptionByType()), - new OutDimensions(), - new Permissions(), - new UserAgent(), - new Vendor(GetOptionByType()), - new WebGl(GetOptionByType()), - new PluginEvasion(), - new StackTrace(), - new HardwareConcurrency(GetOptionByType()), - new ContentWindow(), - new SourceUrl() - }; + return + [ + new WebDriver(), + // new ChromeApp(), + new ChromeSci(), + new ChromeRuntime(), + new Codec(), + new Languages(GetOptionByType()), + new OutDimensions(), + new Permissions(), + new UserAgent(), + new Vendor(GetOptionByType()), + new WebGl(GetOptionByType()), + new PluginEvasion(), + new StackTrace(), + new HardwareConcurrency(GetOptionByType()), + new ContentWindow(), + new SourceUrl() + ]; } public override ICollection GetDependencies() => _standardEvasions; diff --git a/PuppeteerExtraSharp/Plugins/Proxies/ProxiesPlugin.cs b/PuppeteerExtraSharp/Plugins/Proxies/ProxiesPlugin.cs new file mode 100644 index 0000000..78c8b49 --- /dev/null +++ b/PuppeteerExtraSharp/Plugins/Proxies/ProxiesPlugin.cs @@ -0,0 +1,28 @@ +using PuppeteerSharp; +using System.Linq; +using System.Threading.Tasks; + +namespace PuppeteerExtraSharp.Plugins.Proxies +{ + public class ProxiesPlugin(ProxyInfo proxy) : PuppeteerExtraPlugin("proxies") + { + private ProxyInfo Proxy { get; } = proxy; + + public override void BeforeLaunch(LaunchOptions options) + { + var args = options.Args.Where(a => !a.StartsWith("--proxy-server=")).ToList(); + args.Add("--proxy-server=" + Proxy.Ip + ":" + Proxy.Port); + + options.Args = [..args]; + } + + public override async Task OnPageCreated(IPage page) + { + await page.AuthenticateAsync(new() + { + Username = Proxy.Login, + Password = Proxy.Password, + }); + } + } +} diff --git a/PuppeteerExtraSharp/Plugins/Proxies/ProxyInfo.cs b/PuppeteerExtraSharp/Plugins/Proxies/ProxyInfo.cs new file mode 100644 index 0000000..c212e7d --- /dev/null +++ b/PuppeteerExtraSharp/Plugins/Proxies/ProxyInfo.cs @@ -0,0 +1,12 @@ +namespace PuppeteerExtraSharp.Plugins.Proxies +{ +#nullable enable + public class ProxyInfo(string ip, string port, string? login = null, string? password = null) + { + public string Ip { get; set; } = ip; + public string Port { get; set; } = port; + public string? Login { get; set; } = login; + public string? Password { get; set; } = password; + } +#nullable disable +} diff --git a/PuppeteerExtraSharp/Plugins/Proxies/README.MD b/PuppeteerExtraSharp/Plugins/Proxies/README.MD new file mode 100644 index 0000000..3ac8e6b --- /dev/null +++ b/PuppeteerExtraSharp/Plugins/Proxies/README.MD @@ -0,0 +1,15 @@ +# Quickstart + +Work's with IPv4 and IPv6 proxies! + +```c# +var extra = new PuppeteerExtra(); +// initialize proxies plugin +var proxies = new ProxiesPlugin(new ProxyInfo("localhost", "9000")); + +var browser = await extra.Use(proxies).LaunchAsync(new LaunchOptions()); + +var page = await browser.NewPageAsync(); + +await page.GoToAsync("https://gimly.su/"); +``` \ No newline at end of file diff --git a/PuppeteerExtraSharp/Plugins/Recaptcha/Provider/AntiCaptcha/AntiCaptchaApi.cs b/PuppeteerExtraSharp/Plugins/Recaptcha/Provider/AntiCaptcha/AntiCaptchaApi.cs index 52eaeb5..fe4221b 100644 --- a/PuppeteerExtraSharp/Plugins/Recaptcha/Provider/AntiCaptcha/AntiCaptchaApi.cs +++ b/PuppeteerExtraSharp/Plugins/Recaptcha/Provider/AntiCaptcha/AntiCaptchaApi.cs @@ -1,5 +1,6 @@ using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json.Linq; using PuppeteerExtraSharp.Plugins.Recaptcha.Provider.AntiCaptcha.Models; using PuppeteerExtraSharp.Plugins.Recaptcha.RestClient; using RestSharp; @@ -17,7 +18,7 @@ public AntiCaptchaApi(string userKey, ProviderOptions options) _options = options; } - public Task CreateTaskAsync(string pageUrl, string key, CancellationToken token = default) + public async Task CreateTaskAsync(string pageUrl, string key, CancellationToken token = default) { var content = new AntiCaptchaRequest() { @@ -32,8 +33,8 @@ public Task CreateTaskAsync(string pageUrl, string key, C - var result = _client.PostWithJsonAsync("createTask", content, token); - return result; + var result = await _client.PostWithJsonAsync("createTask", content, token); + return result.ToObject(); } diff --git a/PuppeteerExtraSharp/Plugins/Recaptcha/RestClient/RestClient.cs b/PuppeteerExtraSharp/Plugins/Recaptcha/RestClient/RestClient.cs index af007b6..14e180b 100644 --- a/PuppeteerExtraSharp/Plugins/Recaptcha/RestClient/RestClient.cs +++ b/PuppeteerExtraSharp/Plugins/Recaptcha/RestClient/RestClient.cs @@ -1,32 +1,31 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using PuppeteerExtraSharp.Utils; using RestSharp; namespace PuppeteerExtraSharp.Plugins.Recaptcha.RestClient { - public class RestClient + public class RestClient(string url = null) { - private readonly RestSharp.RestClient _client; - - public RestClient(string url = null) - { - _client = string.IsNullOrWhiteSpace(url) ? new RestSharp.RestClient() : new RestSharp.RestClient(url); - } + private readonly RestSharp.RestClient _client = string.IsNullOrWhiteSpace(url) ? new RestSharp.RestClient() : new RestSharp.RestClient(url); public PollingBuilder CreatePollingBuilder(RestRequest request) { return new PollingBuilder(_client, request); } - public async Task PostWithJsonAsync(string url, object content, CancellationToken token) + public async Task PostWithJsonAsync(string url, object content, CancellationToken token) where T : JToken { var request = new RestRequest(url); request.AddHeader("Content-type", "application/json"); - request.AddJsonBody(content); + request.AddJsonBody(JsonConvert.SerializeObject(content)); request.Method = Method.Post; - return await _client.PostAsync(request, token); + + var response = await _client.PostAsync(request, token); + return JsonConvert.DeserializeObject(response.Content); } public async Task PostWithQueryAsync(string url, Dictionary query, CancellationToken token = default) diff --git a/PuppeteerExtraSharp/PuppeteerExtra.cs b/PuppeteerExtraSharp/PuppeteerExtra.cs index 1803fbf..c2c1485 100644 --- a/PuppeteerExtraSharp/PuppeteerExtra.cs +++ b/PuppeteerExtraSharp/PuppeteerExtra.cs @@ -10,7 +10,7 @@ namespace PuppeteerExtraSharp { public class PuppeteerExtra { - private List _plugins = new List(); + private List _plugins = []; public PuppeteerExtra Use(PuppeteerExtraPlugin plugin) { @@ -76,7 +76,7 @@ private void ResolveDependencies(PuppeteerExtraPlugin plugin) private void OrderPlugins() { - _plugins = _plugins.OrderBy(e => e.Requirements?.Contains(PluginRequirements.RunLast)).ToList(); + _plugins = [.. _plugins.OrderBy(e => e.Requirements?.Contains(PluginRequirements.RunLast))]; } private void CheckPluginRequirements(BrowserStartContext context) diff --git a/PuppeteerExtraSharp/PuppeteerExtraSharp.csproj b/PuppeteerExtraSharp/PuppeteerExtraSharp.csproj index 6a6b02e..a2629d1 100644 --- a/PuppeteerExtraSharp/PuppeteerExtraSharp.csproj +++ b/PuppeteerExtraSharp/PuppeteerExtraSharp.csproj @@ -1,15 +1,20 @@  - netstandard2.0 + netstandard2.1 latest - 1.3.1 - https://github.com/Overmiind/Puppeteer-sharp-extra + 1.4.4 + https://github.com/VaKKuumDEV/Puppeteer-sharp-extra git puppeteer-extra recaptcha browser-automation browser-extension browser puppeteer netcore netcore31 stealth-client browser-testing c# - https://github.com/Overmiind/Puppeteer-sharp-extra + https://github.com/VaKKuumDEV/Puppeteer-sharp-extra MIT 945328fb-3e7e-4518-99f8-ec578bf688b1 + True + VaKKuum.PuppeteerExtraSharpJune + VaKKuum + README.md + VaKKuum.PuppeteerExtraSharp @@ -53,8 +58,32 @@ - - + + True + \ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PuppeteerExtraSharp/README.md b/PuppeteerExtraSharp/README.md index f9bbf2a..e43f588 100644 --- a/PuppeteerExtraSharp/README.md +++ b/PuppeteerExtraSharp/README.md @@ -40,6 +40,9 @@ await page.ScreenshotAsync("extra.png"); 💀[Puppeteer recaptcha plugin](https://github.com/Overmiind/PuppeteerExtraSharp/tree/master/Plugins/Recaptcha) - Solves recaptcha automatically +🌐 [Puppeteer proxies plugin](https://github.com/VaKKuumDEV/Puppeteer-sharp-extra/tree/master/PuppeteerExtraSharp/Plugins/Proxies) +- Applies proxies settings on every page +- TODO: rotation and whitelist ✋**More plugins will be soon** diff --git a/README.md b/README.md index cd7c1c9..ea050ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PuppeteerExtraSharp -[![NuGet Badge](https://buildstats.info/nuget/PuppeteerExtraSharp)](https://www.nuget.org/packages/PuppeteerExtraSharp) +[![NuGet Badge](https://buildstats.info/nuget/VaKKuum.PuppeteerExtraSharp)](https://www.nuget.org/packages/VaKKuum.PuppeteerExtraSharp/) Puppeteer extra sharp is a .NET port of the [Node.js library](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra) ## Quickstart @@ -31,20 +31,21 @@ await page.ScreenshotAsync("extra.png"); ``` ## Plugin list -🏴 [Puppeteer stealth plugin](https://github.com/Overmiind/Puppeteer-sharp-extra/tree/master/PuppeteerExtraSharp/Plugins/ExtraStealth) +🏴 [Puppeteer stealth plugin](https://github.com/Overmiind/PuppeteerExtraSharp/tree/master/Plugins/ExtraStealth) - Applies various evasion techniques to make detection of headless puppeteer harder. -📃 [Puppeteer anonymize UA plugin](https://github.com/Overmiind/Puppeteer-sharp-extra/tree/master/PuppeteerExtraSharp/Plugins/AnonymizeUa) +📃 [Puppeteer anonymize UA plugin](https://github.com/Overmiind/PuppeteerExtraSharp/tree/master/Plugins/AnonymizeUa) - Anonymizes the user-agent on all pages. -💀[Puppeteer recaptcha plugin](https://github.com/Overmiind/Puppeteer-sharp-extra/tree/master/PuppeteerExtraSharp/Plugins/Recaptcha) +💀[Puppeteer recaptcha plugin](https://github.com/Overmiind/PuppeteerExtraSharp/tree/master/Plugins/Recaptcha) - Solves recaptcha automatically -🔧[Puppeteer block resources plugin](https://github.com/Overmiind/Puppeteer-sharp-extra/tree/master/PuppeteerExtraSharp/Plugins/BlockResources) -- Blocks images, documents etc. +🌐 [Puppeteer proxies plugin](https://github.com/VaKKuumDEV/Puppeteer-sharp-extra/tree/master/PuppeteerExtraSharp/Plugins/Proxies) +- Applies proxies settings on every page +- TODO: rotation and whitelist -✋**More plugins coming soon** +✋**More plugins will be soon** ## API #### Use(IPuppeteerExtraPlugin) diff --git a/Tests/BrowserDefault.cs b/Tests/BrowserDefault.cs index 94182e6..9f345ac 100644 --- a/Tests/BrowserDefault.cs +++ b/Tests/BrowserDefault.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using PuppeteerExtraSharp; using PuppeteerExtraSharp.Plugins; using PuppeteerSharp; @@ -13,7 +10,7 @@ namespace Extra.Tests { public abstract class BrowserDefault : IDisposable { - private readonly List _launchedBrowsers = new List(); + private readonly List _launchedBrowsers = []; protected BrowserDefault() { } @@ -41,11 +38,9 @@ protected async Task LaunchWithPluginAsync(PuppeteerExtraPlugin plugin protected async Task LaunchAndGetPage(PuppeteerExtraPlugin plugin = null) { - IBrowser browser = null; - if (plugin != null) - browser = await LaunchWithPluginAsync(plugin); - else - browser = await LaunchAsync(); + IBrowser browser; + if (plugin != null) browser = await LaunchWithPluginAsync(plugin); + else browser = await LaunchAsync(); var page = (await browser.PagesAsync())[0]; @@ -53,18 +48,17 @@ protected async Task LaunchAndGetPage(PuppeteerExtraPlugin plugin = null) } - private async void DownloadChromeIfNotExists() + private static async void DownloadChromeIfNotExists() { - if (File.Exists(Constants.PathToChrome)) - return; + if (File.Exists(Constants.PathToChrome)) return; await new BrowserFetcher(new BrowserFetcherOptions() { Path = Constants.PathToChrome - }).DownloadAsync(BrowserFetcher.DefaultChromiumRevision); + }).DownloadAsync(BrowserTag.Latest); } - protected LaunchOptions CreateDefaultOptions() + protected static LaunchOptions CreateDefaultOptions() { return new LaunchOptions() { diff --git a/Tests/Constants.cs b/Tests/Constants.cs index 191da1a..3cd3456 100644 --- a/Tests/Constants.cs +++ b/Tests/Constants.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Extra.Tests +namespace Extra.Tests { public static class Constants { diff --git a/Tests/Extra.Tests.csproj b/Tests/Extra.Tests.csproj index 879898d..5ea5cbf 100644 --- a/Tests/Extra.Tests.csproj +++ b/Tests/Extra.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 false @@ -18,15 +18,15 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Tests/ExtraLaunchTest.cs b/Tests/ExtraLaunchTest.cs index 68c1fbc..7c808ed 100644 --- a/Tests/ExtraLaunchTest.cs +++ b/Tests/ExtraLaunchTest.cs @@ -1,6 +1,5 @@ -using System; using System.Net; -using PuppeteerExtraSharp.Plugins.Recaptcha.Provider.AntiCaptcha; +using System.Threading.Tasks; using Xunit; namespace Extra.Tests @@ -8,9 +7,9 @@ namespace Extra.Tests public class ExtraLaunchTest: BrowserDefault { [Fact] - public async void ShouldReturnOkPage() + public async Task ShouldReturnOkPage() { - var browser = await this.LaunchAsync(); + var browser = await LaunchAsync(); var page = await browser.NewPageAsync(); var response = await page.GoToAsync("http://google.com"); Assert.Equal(HttpStatusCode.OK, response.Status); diff --git a/Tests/Properties/Resources.Designer.cs b/Tests/Properties/Resources.Designer.cs index bb385da..470a178 100644 --- a/Tests/Properties/Resources.Designer.cs +++ b/Tests/Properties/Resources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// Этот код создан программой. +// Исполняемая версия:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае +// повторной генерации кода. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace Extra.Tests.Properties { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д. /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. + // Этот класс создан автоматически классом StronglyTypedResourceBuilder + // с помощью такого средства, как ResGen или Visual Studio. + // Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen + // с параметром /str или перестройте свой проект VS. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ internal Resources() { } /// - /// Returns the cached ResourceManager instance used by this class. + /// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ internal Resources() { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// Перезаписывает свойство CurrentUICulture текущего потока для всех + /// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized string similar to "". + /// Ищет локализованную строку, похожую на "". /// internal static string AntiCaptchaKey { get { @@ -70,7 +70,43 @@ internal static string AntiCaptchaKey { } /// - /// Looks up a localized string similar to "". + /// Ищет локализованную строку, похожую на "". + /// + internal static string ProxyIp { + get { + return ResourceManager.GetString("ProxyIp", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на "". + /// + internal static string ProxyLogin { + get { + return ResourceManager.GetString("ProxyLogin", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на "". + /// + internal static string ProxyPassword { + get { + return ResourceManager.GetString("ProxyPassword", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на "". + /// + internal static string ProxyPort { + get { + return ResourceManager.GetString("ProxyPort", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на "". /// internal static string TwoCaptchaKey { get { diff --git a/Tests/Properties/Resources.resx b/Tests/Properties/Resources.resx index 4953991..4d5c9f0 100644 --- a/Tests/Properties/Resources.resx +++ b/Tests/Properties/Resources.resx @@ -120,6 +120,18 @@ "" + + "" + + + "" + + + "" + + + "" + "" diff --git a/Tests/Proxies/ProxiesTest.cs b/Tests/Proxies/ProxiesTest.cs new file mode 100644 index 0000000..403767f --- /dev/null +++ b/Tests/Proxies/ProxiesTest.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit; +using PuppeteerExtraSharp.Plugins.Proxies; +using Extra.Tests.Properties; + +namespace Extra.Tests.Proxies +{ + [Collection("Proxies")] + public class ProxiesTest(ITestOutputHelper output) : BrowserDefault + { + private readonly ITestOutputHelper output = output; + + [Fact] + public async Task ShouldWorkWithProxy() + { + var plugin = new ProxiesPlugin(new(Resources.ProxyIp, Resources.ProxyPort, Resources.ProxyLogin, Resources.ProxyPassword)); + var browser = await LaunchWithPluginAsync(plugin); + + var page = await browser.NewPageAsync(); + await page.GoToAsync("http://api.ipify.org"); + string content = await page.EvaluateExpressionAsync("document.body.innerText"); + output.WriteLine(content); + await browser.CloseAsync(); + + Assert.Equal(content, Resources.ProxyIp); + } + } +} diff --git a/Tests/Recaptcha/AntiCaptcha/AntiCaptchaTests.cs b/Tests/Recaptcha/AntiCaptcha/AntiCaptchaTests.cs index 6cd3b46..e9c3b61 100644 --- a/Tests/Recaptcha/AntiCaptcha/AntiCaptchaTests.cs +++ b/Tests/Recaptcha/AntiCaptcha/AntiCaptchaTests.cs @@ -8,17 +8,12 @@ namespace Extra.Tests.Recaptcha.AntiCaptcha { [Collection("Captcha")] - public class AntiCaptchaTests : BrowserDefault + public class AntiCaptchaTests(ITestOutputHelper _logger) : BrowserDefault { - private readonly ITestOutputHelper _logger; - - public AntiCaptchaTests(ITestOutputHelper _logger) - { - this._logger = _logger; - } + private readonly ITestOutputHelper _logger = _logger; [Fact] - public async void ShouldThrowCaptchaExceptionWhenCaptchaNotFound() + public async Task ShouldThrowCaptchaExceptionWhenCaptchaNotFound() { var plugin = new RecaptchaPlugin(new PuppeteerExtraSharp.Plugins.Recaptcha.Provider.AntiCaptcha.AntiCaptcha(Resources.AntiCaptchaKey)); @@ -47,12 +42,12 @@ public async Task ShouldSolveCaptchaWithSubmitButton() var button = await page.QuerySelectorAsync("input[type='submit']"); await button.ClickAsync(); - await page.WaitForTimeoutAsync(1000); + await Task.Delay(1000); await CheckSuccessVerify(page); } [Fact] - public async void ShouldSolveCaptchaWithCallback() + public async Task ShouldSolveCaptchaWithCallback() { var plugin = new RecaptchaPlugin(new PuppeteerExtraSharp.Plugins.Recaptcha.Provider.AntiCaptcha.AntiCaptcha(Resources.AntiCaptchaKey)); var browser = await LaunchWithPluginAsync(plugin); @@ -62,11 +57,11 @@ public async void ShouldSolveCaptchaWithCallback() Assert.Null(result.Exception); - await page.WaitForTimeoutAsync(1000); + await Task.Delay(1000); await CheckSuccessVerify(page); } - private async Task CheckSuccessVerify(IPage page) + private static async Task CheckSuccessVerify(IPage page) { var successElement = await page.QuerySelectorAsync("div[id='main'] div[class='description'] h2"); var elementValue = await (await successElement.GetPropertyAsync("textContent")).JsonValueAsync(); diff --git a/Tests/Recaptcha/RestClientTest/RestClientTests.cs b/Tests/Recaptcha/RestClientTest/RestClientTests.cs index 4fbf32c..6bfaa5a 100644 --- a/Tests/Recaptcha/RestClientTest/RestClientTests.cs +++ b/Tests/Recaptcha/RestClientTest/RestClientTests.cs @@ -1,28 +1,33 @@ using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Xunit; +using Xunit.Abstractions; using RestClient = PuppeteerExtraSharp.Plugins.Recaptcha.RestClient.RestClient; namespace Extra.Tests.Recaptcha.RestClientTest { [Collection("Captcha")] - public class RestClientTests + public class RestClientTests(ITestOutputHelper output) { + private readonly ITestOutputHelper output = output; + [Fact] public async Task ShouldWorkPostWithJson() { var client = new RestClient("https://postman-echo.com"); - var data = ("test", "123"); + Dictionary data = new() + { + ["test"] = "test" + }; - var result = await client.PostWithJsonAsync>("post", data, new CancellationToken()); + var result = await client.PostWithJsonAsync("post", data, new CancellationToken()); - var actual = JsonConvert.DeserializeObject<(string, string)>(result.First(e => e.Key == "json").Value); + output.WriteLine(result.ToString()); + var actual = result["json"].ToObject>(); Assert.Equal(data, actual); } - } } diff --git a/Tests/Recaptcha/TwoCaptcha/TwoCaptchaProviderTest.cs b/Tests/Recaptcha/TwoCaptcha/TwoCaptchaProviderTest.cs index b8b4ae6..f041910 100644 --- a/Tests/Recaptcha/TwoCaptcha/TwoCaptchaProviderTest.cs +++ b/Tests/Recaptcha/TwoCaptcha/TwoCaptchaProviderTest.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; using Extra.Tests.Properties; using PuppeteerExtraSharp.Plugins.Recaptcha; -using PuppeteerExtraSharp.Plugins.Recaptcha.Provider; -using PuppeteerSharp; using Xunit; namespace Extra.Tests.Recaptcha.TwoCaptcha diff --git a/Tests/StealthPluginTests/EvasionsTests/ChromeSciTest.cs b/Tests/StealthPluginTests/EvasionsTests/ChromeSciTest.cs index 1efd87b..c4b2978 100644 --- a/Tests/StealthPluginTests/EvasionsTests/ChromeSciTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/ChromeSciTest.cs @@ -23,18 +23,18 @@ public async Task ShouldWork() dataOK: { onloadT: csi.onloadT === timing.domContentLoadedEventEnd, startE: csi.startE === timing.navigationStart, - pageT: Number.isInteger(csi.pageT), + pageT: !Number.isNaN(Number.parseFloat(csi.pageT)), tran: Number.isInteger(csi.tran) } } }"); - Assert.True(sci["csi"].Value("exists")); - Assert.Equal("function () { [native code] }", sci["csi"]["toString"]); - Assert.True(sci["dataOK"].Value("onloadT")); - Assert.True(sci["dataOK"].Value("pageT")); - Assert.True(sci["dataOK"].Value("startE")); - Assert.True(sci["dataOK"].Value("tran")); + Assert.True(sci.Value.GetProperty("csi").GetProperty("exists").GetBoolean()); + Assert.Equal("function () { [native code] }", sci.Value.GetProperty("csi").GetProperty("toString").GetString()); + Assert.True(sci.Value.GetProperty("dataOK").GetProperty("onloadT").GetBoolean()); + Assert.True(sci.Value.GetProperty("dataOK").GetProperty("pageT").GetBoolean()); + Assert.True(sci.Value.GetProperty("dataOK").GetProperty("startE").GetBoolean()); + Assert.True(sci.Value.GetProperty("dataOK").GetProperty("tran").GetBoolean()); } } } \ No newline at end of file diff --git a/Tests/StealthPluginTests/EvasionsTests/CodecTest.cs b/Tests/StealthPluginTests/EvasionsTests/CodecTest.cs index 0b7cd22..2528d94 100644 --- a/Tests/StealthPluginTests/EvasionsTests/CodecTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/CodecTest.cs @@ -14,9 +14,9 @@ public async Task SupportsCodec() var plugin = new Codec(); var page = await LaunchAndGetPage(plugin); await page.GoToAsync("https://google.com"); - var fingerPrint = await new FingerPrint().GetFingerPrint(page); + var fingerPrint = await FingerPrint.GetFingerPrint(page); - Assert.Equal("probably", fingerPrint["videoCodecs"]["ogg"].Value()); + Assert.Equal("", fingerPrint["videoCodecs"]["ogg"].Value()); Assert.Equal("probably", fingerPrint["videoCodecs"]["h264"].Value()); Assert.Equal("probably", fingerPrint["videoCodecs"]["webm"].Value()); diff --git a/Tests/StealthPluginTests/EvasionsTests/ContentWindowTest.cs b/Tests/StealthPluginTests/EvasionsTests/ContentWindowTest.cs index bcb0021..89d2032 100644 --- a/Tests/StealthPluginTests/EvasionsTests/ContentWindowTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/ContentWindowTest.cs @@ -16,7 +16,7 @@ public async Task IFrameShouldBeObject() var page = await LaunchAndGetPage(plugin); await page.GoToAsync("https://google.com"); - var finger = await new FingerPrint().GetFingerPrint(page); + var finger = await FingerPrint.GetFingerPrint(page); Assert.Equal("object", finger["iframeChrome"]); } @@ -44,7 +44,7 @@ await page.EvaluateFunctionAsync(@"(testFuncReturnValue) => { await page.EvaluateExpressionAsync( "document.querySelector('iframe').contentWindow.mySuperFunction()"); - Assert.Equal(testFuncReturnValue, result); + Assert.Equal(testFuncReturnValue, result.Value.GetString()); } [Fact] @@ -83,10 +83,10 @@ public async Task ShouldCoverAllFrames() return typeof(el.contentWindow.chrome) }"); - Assert.Equal("object", basicFrame); - Assert.Equal("object", sandboxSOIFrame); - Assert.Equal("object", sandboxSOASIFrame); - Assert.Equal("object", srcdocIFrame); + Assert.Equal("object", basicFrame.Value.GetString()); + Assert.Equal("object", sandboxSOIFrame.Value.GetString()); + Assert.Equal("object", sandboxSOASIFrame.Value.GetString()); + Assert.Equal("object", srcdocIFrame.Value.GetString()); } @@ -169,15 +169,15 @@ public async Task ShouldEmulateFeatures() }"); - Assert.True(results.Value("descriptorsOK")); - Assert.True(results.Value("doesExist")); - Assert.True(results.Value("isNotAClone")); - Assert.True(results.Value("hasSameNumberOfPlugins")); - Assert.True(results.Value("SelfIsNotWindow")); - Assert.True(results.Value("SelfIsNotWindowTop")); - Assert.True(results.Value("TopIsNotSame")); + Assert.True(results.Value.GetProperty("descriptorsOK").GetBoolean()); + Assert.True(results.Value.GetProperty("doesExist").GetBoolean()); + Assert.True(results.Value.GetProperty("isNotAClone").GetBoolean()); + Assert.True(results.Value.GetProperty("hasSameNumberOfPlugins").GetBoolean()); + Assert.True(results.Value.GetProperty("SelfIsNotWindow").GetBoolean()); + Assert.True(results.Value.GetProperty("SelfIsNotWindowTop").GetBoolean()); + Assert.True(results.Value.GetProperty("TopIsNotSame").GetBoolean()); - Assert.DoesNotContain("at Object.apply", results["StackTraces"]); + Assert.DoesNotContain("at Object.apply", results.Value.GetProperty("StackTraces").GetString()); } } } \ No newline at end of file diff --git a/Tests/StealthPluginTests/EvasionsTests/LanguagesTest.cs b/Tests/StealthPluginTests/EvasionsTests/LanguagesTest.cs index b2ace62..76e1eb6 100644 --- a/Tests/StealthPluginTests/EvasionsTests/LanguagesTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/LanguagesTest.cs @@ -17,23 +17,26 @@ public async Task ShouldWork() await page.GoToAsync("https://google.com"); - var fingerPrint = await new FingerPrint().GetFingerPrint(page); + var fingerPrint = await FingerPrint.GetFingerPrint(page); + var fingerprintLanguages = JArray.Parse(fingerPrint["languages"].Value()).Select(t => t.Value()).ToList(); - Assert.Contains("en-US", fingerPrint["languages"].Select(e => e.Value())); + Assert.Contains("en-US", fingerprintLanguages); } [Fact] public async Task ShouldWorkWithCustomSettings() { + string[] langs = ["fr-FR"]; var plugin = new Languages(new StealthLanguagesOptions("fr-FR")); var page = await LaunchAndGetPage(plugin); await page.GoToAsync("https://google.com"); - var fingerPrint = await new FingerPrint().GetFingerPrint(page); + var fingerPrint = await FingerPrint.GetFingerPrint(page); + var fingerprintLanguages = JArray.Parse(fingerPrint["languages"].Value()).Select(t => t.Value()).ToList(); - Assert.Contains("fr-FR", fingerPrint["languages"].Select(e => e.Value())); + Assert.False(fingerprintLanguages.Except(langs).Any()); } } } diff --git a/Tests/StealthPluginTests/EvasionsTests/PermissionsTest.cs b/Tests/StealthPluginTests/EvasionsTests/PermissionsTest.cs index 19fe479..ce7d0f3 100644 --- a/Tests/StealthPluginTests/EvasionsTests/PermissionsTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/PermissionsTest.cs @@ -14,7 +14,7 @@ public async Task ShouldBeDeniedInHttpSite() var page = await LaunchAndGetPage(plugin); await page.GoToAsync("http://info.cern.ch/"); - var finger = await new FingerPrint().GetFingerPrint(page); + var finger = await FingerPrint.GetFingerPrint(page); Assert.Equal("denied", finger["permissions"]["state"]); Assert.Equal("denied", finger["permissions"]["permission"]); diff --git a/Tests/StealthPluginTests/EvasionsTests/PluginEvasionTest.cs b/Tests/StealthPluginTests/EvasionsTests/PluginEvasionTest.cs index 396d47c..1235666 100644 --- a/Tests/StealthPluginTests/EvasionsTests/PluginEvasionTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/PluginEvasionTest.cs @@ -17,7 +17,7 @@ public async Task ShouldNotHaveModifications() await page.GoToAsync("https://google.com"); - var fingerPrint = await new FingerPrint().GetFingerPrint(page); + var fingerPrint = await FingerPrint.GetFingerPrint(page); Assert.Equal(3, fingerPrint["plugins"].Count()); Assert.Equal(4, fingerPrint["mimeTypes"].Count()); diff --git a/Tests/StealthPluginTests/EvasionsTests/PluginTest.cs b/Tests/StealthPluginTests/EvasionsTests/PluginTest.cs index 29f836b..180394a 100644 --- a/Tests/StealthPluginTests/EvasionsTests/PluginTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/PluginTest.cs @@ -15,7 +15,7 @@ public async Task HasMimeTypes() var page = await LaunchAndGetPage(plugin); await page.GoToAsync("https://google.com"); - var finger = await new FingerPrint().GetFingerPrint(page); + var finger = await FingerPrint.GetFingerPrint(page); Assert.Equal(3, finger["plugins"].Count()); Assert.Equal(4, finger["mimeTypes"].Count()); diff --git a/Tests/StealthPluginTests/EvasionsTests/UserAgentTest.cs b/Tests/StealthPluginTests/EvasionsTests/UserAgentTest.cs index cf8fcf9..d779353 100644 --- a/Tests/StealthPluginTests/EvasionsTests/UserAgentTest.cs +++ b/Tests/StealthPluginTests/EvasionsTests/UserAgentTest.cs @@ -15,7 +15,7 @@ public async Task ShouldWork() await page.GoToAsync("https://google.com"); var userAgent = await page.Browser.GetUserAgentAsync(); - var finger = await new FingerPrint().GetFingerPrint(page); + var finger = await FingerPrint.GetFingerPrint(page); Assert.DoesNotContain("HeadlessChrome", finger.Value("userAgent")); } } diff --git a/Tests/Utils/FingerPrint.cs b/Tests/Utils/FingerPrint.cs index 2aa1bec..7239216 100644 --- a/Tests/Utils/FingerPrint.cs +++ b/Tests/Utils/FingerPrint.cs @@ -13,13 +13,12 @@ public class FingerPrint /// /// /// - public async Task GetFingerPrint(IPage page) + public static async Task GetFingerPrint(IPage page) { var script = ResourcesReader.ReadFile("Extra.Tests.StealthPluginTests.Script.fpCollect.js", Assembly.GetExecutingAssembly()); await page.EvaluateExpressionAsync(script); - var fingerPrint = - await page.EvaluateFunctionAsync("async () => await fpCollect().generateFingerprint()"); + var fingerPrint = await page.EvaluateFunctionAsync("async () => await fpCollect().generateFingerprint()"); return fingerPrint; }