Merge pull request #587 from Qtoss-AI/master

Add ExcludeResponseUrls to PageActionArgs
This commit is contained in:
Haiping 2024-08-11 15:17:45 -05:00 committed by GitHub
commit 2be4dd3053
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 103 additions and 38 deletions

View file

@ -4,6 +4,7 @@ namespace BotSharp.Abstraction.Browsing;
public interface IWebBrowser public interface IWebBrowser
{ {
void SetServiceProvider (IServiceProvider services);
Task<BrowserActionResult> LaunchBrowser(MessageInfo message, BrowserActionArgs args); Task<BrowserActionResult> LaunchBrowser(MessageInfo message, BrowserActionArgs args);
Task<BrowserActionResult> ScreenshotAsync(MessageInfo message, string path); Task<BrowserActionResult> ScreenshotAsync(MessageInfo message, string path);
Task<BrowserActionResult> ScrollPage(MessageInfo message, PageActionArgs args); Task<BrowserActionResult> ScrollPage(MessageInfo message, PageActionArgs args);

View file

@ -10,8 +10,22 @@ public class PageActionArgs
public string? Direction { get; set; } public string? Direction { get; set; }
public string Url { get; set; } = null!; public string Url { get; set; } = null!;
/// <summary>
/// This value has to be set to true if you want to get the page XHR/ Fetch responses
/// </summary>
public bool OpenNewTab { get; set; } = false; public bool OpenNewTab { get; set; } = false;
/// <summary>
/// Exclude urls for XHR/ Fetch responses
/// </summary>
public string[]? ExcludeResponseUrls { get; set; }
public bool UseExistingPage { get; set; } = false;
public bool WaitForNetworkIdle { get; set; } = true; public bool WaitForNetworkIdle { get; set; } = true;
public float? Timeout { get; set; } public float? Timeout { get; set; }
/// <summary>
/// Wait time in seconds after page is opened
/// </summary>
public int WaitTime { get; set; }
} }

View file

@ -9,4 +9,5 @@ public interface IUserIdentity
string LastName { get; } string LastName { get; }
string FullName { get; } string FullName { get; }
string? UserLanguage { get; } string? UserLanguage { get; }
string? Phone { get; }
} }

View file

@ -183,7 +183,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspects.Cache" Version="2.0.4" /> <PackageReference Include="Aspects.Cache" Version="2.0.4" />
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" /> <PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
<PackageReference Include="EntityFrameworkCore.BootKit" Version="8.5.0" /> <PackageReference Include="EntityFrameworkCore.BootKit" Version="8.5.1" />
<PackageReference Include="Fluid.Core" Version="2.11.1" /> <PackageReference Include="Fluid.Core" Version="2.11.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Nanoid" Version="3.1.0" /> <PackageReference Include="Nanoid" Version="3.1.0" />

View file

@ -66,4 +66,7 @@ public class UserIdentity : IUserIdentity
return languages.FirstOrDefault(); return languages.FirstOrDefault();
} }
} }
[JsonPropertyName("phone")]
public string? Phone => _claims?.FirstOrDefault(x => x.Type == "phone")?.Value;
} }

View file

@ -192,7 +192,8 @@ public class UserService : IUserService
new Claim(JwtRegisteredClaimNames.FamilyName, user?.LastName ?? string.Empty), new Claim(JwtRegisteredClaimNames.FamilyName, user?.LastName ?? string.Empty),
new Claim("source", user.Source), new Claim("source", user.Source),
new Claim("external_id", user.ExternalId ?? string.Empty), new Claim("external_id", user.ExternalId ?? string.Empty),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("phone", user.Phone ?? string.Empty)
}; };
var validators = _services.GetServices<IAuthenticationHook>(); var validators = _services.GetServices<IAuthenticationHook>();
@ -204,11 +205,12 @@ public class UserService : IUserService
var config = _services.GetRequiredService<IConfiguration>(); var config = _services.GetRequiredService<IConfiguration>();
var issuer = config["Jwt:Issuer"]; var issuer = config["Jwt:Issuer"];
var audience = config["Jwt:Audience"]; var audience = config["Jwt:Audience"];
var expireInMinutes = int.Parse(config["Jwt:ExpireInMinutes"] ?? "120");
var key = Encoding.ASCII.GetBytes(config["Jwt:Key"]); var key = Encoding.ASCII.GetBytes(config["Jwt:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor var tokenDescriptor = new SecurityTokenDescriptor
{ {
Subject = new ClaimsIdentity(claims), Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddHours(2), Expires = DateTime.UtcNow.AddMinutes(expireInMinutes),
Issuer = issuer, Issuer = issuer,
Audience = audience, Audience = audience,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),

View file

@ -11,15 +11,17 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Drivers\SeleniumDriver\**" />
<Compile Remove="packages\**" /> <Compile Remove="packages\**" />
<EmbeddedResource Remove="Drivers\SeleniumDriver\**" />
<EmbeddedResource Remove="packages\**" /> <EmbeddedResource Remove="packages\**" />
<None Remove="Drivers\SeleniumDriver\**" />
<None Remove="packages\**" /> <None Remove="packages\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Playwright" Version="1.45.1" /> <PackageReference Include="Microsoft.Playwright" Version="1.45.1" />
<PackageReference Include="Selenium.WebDriver" Version="4.23.0" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.62" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -5,8 +5,11 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public class PlaywrightInstance : IDisposable public class PlaywrightInstance : IDisposable
{ {
IPlaywright _playwright; IPlaywright _playwright;
IServiceProvider _services;
public IServiceProvider Services => _services;
Dictionary<string, IBrowserContext> _contexts = new Dictionary<string, IBrowserContext>(); Dictionary<string, IBrowserContext> _contexts = new Dictionary<string, IBrowserContext>();
Dictionary<string, List<IPage>> _pages = new Dictionary<string, List<IPage>>(); Dictionary<string, List<IPage>> _pages = new Dictionary<string, List<IPage>>();
IPage? _activePage = null;
/// <summary> /// <summary>
/// ContextId and BrowserContext /// ContextId and BrowserContext
@ -18,9 +21,29 @@ public class PlaywrightInstance : IDisposable
/// </summary> /// </summary>
public Dictionary<string, List<IPage>> Pages => _pages; public Dictionary<string, List<IPage>> Pages => _pages;
public IPage GetPage(string id, string? pattern = null) public void SetServiceProvider(IServiceProvider services)
{ {
return _contexts[id].Pages.LastOrDefault(); _services = services;
}
public IPage? GetPage(string contextId, string? pattern = null)
{
if (string.IsNullOrEmpty(pattern))
{
return _activePage ?? _contexts[contextId].Pages.LastOrDefault();
}
foreach (var page in _contexts[contextId].Pages)
{
if (page.Url.ToLower() == pattern.ToLower())
{
_activePage = page;
page.BringToFrontAsync().Wait();
return page;
}
}
return _contexts[contextId].Pages.LastOrDefault();
} }
public async Task<IBrowserContext> GetContext(string ctxId) public async Task<IBrowserContext> GetContext(string ctxId)
@ -64,6 +87,7 @@ public class PlaywrightInstance : IDisposable
_contexts[ctxId].Page += async (sender, page) => _contexts[ctxId].Page += async (sender, page) =>
{ {
_activePage = page;
_pages[ctxId].Add(page); _pages[ctxId].Add(page);
page.Close += async (sender, e) => page.Close += async (sender, e) =>
{ {
@ -93,7 +117,7 @@ public class PlaywrightInstance : IDisposable
return _contexts[ctxId]; return _contexts[ctxId];
} }
public async Task<IPage> NewPage(MessageInfo message, IServiceProvider services) public async Task<IPage> NewPage(MessageInfo message, string[]? excludeResponseUrls = null)
{ {
var context = await GetContext(message.ContextId); var context = await GetContext(message.ContextId);
var page = await context.NewPageAsync(); var page = await context.NewPageAsync();
@ -105,9 +129,11 @@ public class PlaywrightInstance : IDisposable
page.Response += async (sender, e) => page.Response += async (sender, e) =>
{ {
if (e.Headers.ContainsKey("content-type") && if (e.Status != 204 &&
e.Headers.ContainsKey("content-type") &&
e.Headers["content-type"].Contains("application/json") && e.Headers["content-type"].Contains("application/json") &&
(e.Request.ResourceType == "fetch" || e.Request.ResourceType == "xhr")) (e.Request.ResourceType == "fetch" || e.Request.ResourceType == "xhr") &&
(excludeResponseUrls == null || !excludeResponseUrls.Any(url => e.Url.ToLower().Contains(url))))
{ {
Serilog.Log.Information($"{e.Request.Method}: {e.Url}"); Serilog.Log.Information($"{e.Request.Method}: {e.Url}");
JsonElement? json = null; JsonElement? json = null;
@ -122,13 +148,17 @@ public class PlaywrightInstance : IDisposable
Serilog.Log.Warning($"Response status: {e.Status} {e.StatusText}, OK: {e.Ok}"); Serilog.Log.Warning($"Response status: {e.Status} {e.StatusText}, OK: {e.Ok}");
} }
var webPageResponseHooks = services.GetServices<IWebPageResponseHook>(); var webPageResponseHooks = _services.GetServices<IWebPageResponseHook>();
foreach (var hook in webPageResponseHooks) foreach (var hook in webPageResponseHooks)
{ {
hook.OnDataFetched(message, e.Url.ToLower(), e.Request?.PostData ?? string.Empty, JsonSerializer.Serialize(json)); hook.OnDataFetched(message, e.Url.ToLower(), e.Request?.PostData ?? string.Empty, JsonSerializer.Serialize(json));
} }
} }
catch(Exception ex) catch (ObjectDisposedException ex)
{
Serilog.Log.Information(ex.Message);
}
catch (Exception ex)
{ {
Serilog.Log.Error(ex.ToString()); Serilog.Log.Error(ex.ToString());
} }
@ -174,6 +204,7 @@ public class PlaywrightInstance : IDisposable
if (page != null) if (page != null)
{ {
await page.CloseAsync(); await page.CloseAsync();
_activePage = _pages[ctxId].LastOrDefault();
} }
} }
} }

View file

@ -8,26 +8,24 @@ public partial class PlaywrightWebDriver
var context = await _instance.GetContext(message.ContextId); var context = await _instance.GetContext(message.ContextId);
try try
{ {
// Check if the page is already open var page = args.UseExistingPage ?
/*if (!args.OpenNewTab && context.Pages.Count > 0) _instance.GetPage(message.ContextId, pattern: args.Url) :
{ await _instance.NewPage(message, excludeResponseUrls: args.ExcludeResponseUrls);
foreach (var p in context.Pages)
{
if (p.Url == args.Url)
{
// Disable this due to performance issue, some page is too large
// result.Body = await p.ContentAsync();
result.IsSuccess = true;
// await p.BringToFrontAsync();
return result;
}
}
}*/
var page = args.OpenNewTab ? await _instance.NewPage(message, _services) : if (args.UseExistingPage && page != null && page.Url == args.Url)
_instance.GetPage(message.ContextId); {
Serilog.Log.Information($"goto existing page: {args.Url}");
result.IsSuccess = true;
return result;
}
Serilog.Log.Information($"goto page: {args.Url}"); Serilog.Log.Information($"goto page: {args.Url}");
if (args.UseExistingPage && args.OpenNewTab && page != null && page.Url == "about:blank")
{
page = await _instance.NewPage(message, excludeResponseUrls: args.ExcludeResponseUrls);
}
var response = await page.GotoAsync(args.Url, new PageGotoOptions var response = await page.GotoAsync(args.Url, new PageGotoOptions
{ {
Timeout = args.Timeout Timeout = args.Timeout
@ -42,6 +40,11 @@ public partial class PlaywrightWebDriver
}); });
} }
if (args.WaitTime > 0)
{
await Task.Delay(args.WaitTime * 1000);
}
if (response.Status == 200) if (response.Status == 200)
{ {
// Disable this due to performance issue, some page is too large // Disable this due to performance issue, some page is too large

View file

@ -2,7 +2,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public partial class PlaywrightWebDriver : IWebBrowser public partial class PlaywrightWebDriver : IWebBrowser
{ {
private readonly IServiceProvider _services; private IServiceProvider _services => _instance.Services;
private readonly PlaywrightInstance _instance; private readonly PlaywrightInstance _instance;
private readonly ILogger _logger; private readonly ILogger _logger;
public PlaywrightInstance Instance => _instance; public PlaywrightInstance Instance => _instance;
@ -12,9 +12,9 @@ public partial class PlaywrightWebDriver : IWebBrowser
public PlaywrightWebDriver(IServiceProvider services, PlaywrightInstance instance, ILogger<PlaywrightWebDriver> logger) public PlaywrightWebDriver(IServiceProvider services, PlaywrightInstance instance, ILogger<PlaywrightWebDriver> logger)
{ {
_services = services;
_instance = instance; _instance = instance;
_logger = logger; _logger = logger;
_instance.SetServiceProvider(services);
} }
public void SetAgent(Agent agent) public void SetAgent(Agent agent)
@ -63,4 +63,9 @@ public partial class PlaywrightWebDriver : IWebBrowser
return element; return element;
} }
public void SetServiceProvider(IServiceProvider services)
{
_instance.SetServiceProvider(_services);
}
} }

View file

@ -81,4 +81,9 @@ public partial class SeleniumWebDriver : IWebBrowser
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SetServiceProvider(IServiceProvider services)
{
throw new NotImplementedException();
}
} }

View file

@ -9,7 +9,6 @@ global using Microsoft.Playwright;
global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using OpenQA.Selenium;
global using BotSharp.Abstraction.Browsing.Enums; global using BotSharp.Abstraction.Browsing.Enums;
global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Plugins; global using BotSharp.Abstraction.Plugins;

View file

@ -1,7 +1,6 @@
using BotSharp.Abstraction.Browsing.Settings; using BotSharp.Abstraction.Browsing.Settings;
using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Settings;
using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
using BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver;
using BotSharp.Plugin.WebDriver.Hooks; using BotSharp.Plugin.WebDriver.Hooks;
namespace BotSharp.Plugin.Playwrights; namespace BotSharp.Plugin.Playwrights;
@ -12,7 +11,7 @@ public class WebDriverPlugin : IBotSharpPlugin
public string Name => "Web Driver"; public string Name => "Web Driver";
public string Description => "Empower agent to manipulate web browser in automation tools."; public string Description => "Empower agent to manipulate web browser in automation tools.";
public string IconUrl => "https://cdn-icons-png.flaticon.com/512/8576/8576378.png"; public string IconUrl => "https://cdn-icons-png.flaticon.com/512/8576/8576378.png";
public string[] AgentIds => new[] { "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b" }; public string[] AgentIds => ["f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b"];
public void RegisterDI(IServiceCollection services, IConfiguration config) public void RegisterDI(IServiceCollection services, IConfiguration config)
{ {
@ -28,13 +27,13 @@ public class WebDriverPlugin : IBotSharpPlugin
services.AddScoped<PlaywrightWebDriver>(); services.AddScoped<PlaywrightWebDriver>();
services.AddSingleton<PlaywrightInstance>(); services.AddSingleton<PlaywrightInstance>();
services.AddScoped<SeleniumWebDriver>(); // services.AddScoped<SeleniumWebDriver>();
services.AddSingleton<SeleniumInstance>(); // services.AddSingleton<SeleniumInstance>();
services.AddScoped<IWebBrowser>(provider => settings.Driver switch services.AddScoped<IWebBrowser>(provider => settings.Driver switch
{ {
"Playwright" => provider.GetRequiredService<PlaywrightWebDriver>(), "Playwright" => provider.GetRequiredService<PlaywrightWebDriver>(),
"Selenium" => provider.GetRequiredService<SeleniumWebDriver>(), // "Selenium" => provider.GetRequiredService<SeleniumWebDriver>(),
_ => provider.GetRequiredService<PlaywrightWebDriver>(), _ => provider.GetRequiredService<PlaywrightWebDriver>(),
}); });