diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs new file mode 100644 index 000000000..98aaf4541 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.Agents.Enums; + +public class BuiltInAgentId +{ + public const string AIAssistant = "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a"; + public const string Chatbot = "01e2fc5c-2c89-4ec7-8470-7688608b496c"; + public const string HumanSupport = "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj index 65424adb4..b03aa12b8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj +++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj @@ -10,6 +10,12 @@ $(SolutionDir)packages + + + + + + True diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionArgs.cs new file mode 100644 index 000000000..1a6920c82 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionArgs.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Browsing.Models; + +public class BrowserActionArgs +{ + +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs index b3576b90c..63c592b77 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs @@ -8,6 +8,7 @@ public class BrowserActionResult public string Selector { get; set; } public string Body { get; set; } public bool IsHighlighted { get; set; } + public DateTime? ExecutedAt { get; set; } = DateTime.UtcNow; public override string ToString() { diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs index 3b96c7604..9dd5334ad 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs @@ -31,4 +31,5 @@ public class ElementLocatingArgs /// Draw outline around the element /// public bool Highlight { get; set; } + public string HighlightColor { get; set; } = "red"; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs index 99591b1e3..f583febba 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs @@ -11,4 +11,11 @@ public class PageActionArgs public string Url { get; set; } = null!; public bool OpenNewTab { get; set; } = false; + + /// + /// On page data fetched + /// + public DataFetched? OnDataFetched { get; set; } } + +public delegate void DataFetched(string url, string data); diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 9ada5f639..ad4f2e3fc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -37,6 +37,8 @@ public int Offset { get { return (Page - 1) * Size; } } + + public bool ReturnTotal { get; set; } = true; } public class PagedItems diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index fa572e67f..43bd9c7f4 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -17,9 +17,9 @@ public class AgentPlugin : IBotSharpPlugin public string[] AgentIds => new string[] { - "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", - "01e2fc5c-2c89-4ec7-8470-7688608b496c", - "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b" + BuiltInAgentId.AIAssistant, + BuiltInAgentId.Chatbot, + BuiltInAgentId.HumanSupport }; public object GetNewSettingsInstance() => diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 7bdd84645..1ca8b9439 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -160,8 +160,7 @@ - - + diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index af7bc560d..709285e2c 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -36,4 +36,3 @@ global using BotSharp.Core.Infrastructures; global using BotSharp.Core.Users.Services; global using Aspects.Cache; -global using Console = Colorful.Console; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs new file mode 100644 index 000000000..ac7686a1c --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs @@ -0,0 +1,30 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Translation; +using BotSharp.OpenAPI.ViewModels.Translations; + +namespace BotSharp.OpenAPI.Controllers; + +[Authorize] +[ApiController] +public class TranslationController : ControllerBase +{ + private readonly IServiceProvider _services; + + public TranslationController(IServiceProvider services) + { + _services = services; + } + + [HttpPost("/translate")] + public async Task Translate([FromBody] TranslationRequestModel model) + { + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(BuiltInAgentId.AIAssistant); + var translator = _services.GetRequiredService(); + var text = await translator.Translate(agent, Guid.NewGuid().ToString(), model.Text, language: model.ToLang); + return new TranslationResponseModel + { + Text = text + }; + } +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Translations/TranslationRequestModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Translations/TranslationRequestModel.cs new file mode 100644 index 000000000..588dedfcf --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Translations/TranslationRequestModel.cs @@ -0,0 +1,9 @@ +using BotSharp.Abstraction.Infrastructures.Enums; + +namespace BotSharp.OpenAPI.ViewModels.Translations; + +public class TranslationRequestModel +{ + public string Text { get; set; } = null!; + public string ToLang { get; set; } = LanguageType.CHINESE; +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Translations/TranslationResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Translations/TranslationResponseModel.cs new file mode 100644 index 000000000..0c547d2bf --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Translations/TranslationResponseModel.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Translations; + +public class TranslationResponseModel +{ + public string Text { get; set; } = null!; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string FromLang { get; set; } = null!; +} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs index 31780fb49..75d7f8ed9 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs @@ -21,4 +21,3 @@ global using BotSharp.Plugin.SqlDriver.Services; global using BotSharp.Plugin.SqlHero.Settings; global using System.Drawing; -global using Console = Colorful.Console; \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs index 3f7d4d3a4..69e94f139 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs @@ -1,3 +1,4 @@ +using Microsoft.Playwright; using System.IO; namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; @@ -6,31 +7,41 @@ public class PlaywrightInstance : IDisposable { IPlaywright _playwright; Dictionary _contexts = new Dictionary(); + Dictionary> _pages = new Dictionary>(); + + /// + /// ContextId and BrowserContext + /// public Dictionary Contexts => _contexts; - public IPage GetPage(string id) + /// + /// ContextId and Pages + /// + public Dictionary> Pages => _pages; + + public IPage GetPage(string id, string? pattern = null) { InitInstance(id).Wait(); return _contexts[id].Pages.LastOrDefault(); } - public async Task InitInstance(string id) + public async Task InitInstance(string ctxId) { if (_playwright == null) { _playwright = await Playwright.CreateAsync(); } - return await InitContext(id); + return await InitContext(ctxId); } - public async Task InitContext(string id) + public async Task InitContext(string ctxId) { - if (_contexts.ContainsKey(id)) - return _contexts[id]; + if (_contexts.ContainsKey(ctxId)) + return _contexts[ctxId]; - string tempFolderPath = $"{Path.GetTempPath()}\\playwright\\{id}"; + string tempFolderPath = $"{Path.GetTempPath()}\\playwright\\{ctxId}"; - _contexts[id] = await _playwright.Chromium.LaunchPersistentContextAsync(tempFolderPath, new BrowserTypeLaunchPersistentContextOptions + _contexts[ctxId] = await _playwright.Chromium.LaunchPersistentContextAsync(tempFolderPath, new BrowserTypeLaunchPersistentContextOptions { #if DEBUG Headless = false, @@ -42,63 +53,110 @@ public async Task InitContext(string id) [ "--enable-automation", ], - Args = + Args = [ "--disable-infobars", "--test-type" // "--start-maximized" ] }); + _pages[ctxId] = new List(); - _contexts[id].Page += async (sender, e) => + _contexts[ctxId].Page += async (sender, page) => { - e.Close += async (sender, e) => + _pages[ctxId].Add(page); + page.Close += async (sender, e) => { + _pages[ctxId].Remove(e); Serilog.Log.Information($"Page is closed: {e.Url}"); }; - Serilog.Log.Information($"New page is created: {e.Url}"); - await e.SetViewportSizeAsync(1280, 800); + Serilog.Log.Information($"New page is created: {page.Url}"); + if (!page.IsClosed) + { + await page.SetViewportSizeAsync(1600, 900); + } + + /*page.Response += async (sender, e) => + { + Serilog.Log.Information($"Response: {e.Url}"); + if (e.Headers.ContainsKey("content-type") && e.Headers["content-type"].Contains("application/json")) + { + var json = await e.JsonAsync(); + Serilog.Log.Information(json.ToString()); + } + };*/ }; - _contexts[id].Close += async (sender, e) => + _contexts[ctxId].Close += async (sender, e) => { Serilog.Log.Warning($"Playwright browser context is closed"); - _contexts.Remove(id); + _pages.Remove(ctxId); + _contexts.Remove(ctxId); }; - return _contexts[id]; + return _contexts[ctxId]; } - public async Task NewPage(string id) + public async Task NewPage(string ctxId, DataFetched? fetched) { - await InitContext(id); - return await _contexts[id].NewPageAsync(); + await InitContext(ctxId); + var page = await _contexts[ctxId].NewPageAsync(); + + if (fetched != null) + { + page.Response += async (sender, e) => + { + if (e.Headers.ContainsKey("content-type") && + e.Headers["content-type"].Contains("application/json") && + e.Request.ResourceType == "fetch") + { + Serilog.Log.Information($"Response: {e.Url}"); + var json = await e.JsonAsync(); + fetched(e.Url.ToLower(), JsonSerializer.Serialize(json)); + } + }; + } + + return page; } - public async Task Wait(string id) + /// + /// Wait page and network until timeout in seconds + /// + /// + /// seconds + /// + public async Task Wait(string ctxId, int timeout = 60) { - if (_contexts.ContainsKey(id)) + foreach (var page in _pages[ctxId]) { - var page = _contexts[id].Pages.Last(); await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); - await page.WaitForLoadStateAsync(LoadState.NetworkIdle); + await page.WaitForLoadStateAsync(LoadState.NetworkIdle, new PageWaitForLoadStateOptions + { + Timeout = 1000 * timeout + }); } await Task.Delay(100); } - public async Task Close(string id) + public async Task Close(string ctxId) { - if (_contexts.ContainsKey(id)) + if (_contexts.ContainsKey(ctxId)) { - await _contexts[id].CloseAsync(); + await _contexts[ctxId].CloseAsync(); } } - public async Task CloseCurrentPage(string id) + public async Task CloseCurrentPage(string ctxId) { - if (_contexts.ContainsKey(id)) + var pages = _pages[ctxId].ToArray(); + for (var i = 0; i < pages.Length; i++) { - await GetPage(id).CloseAsync(); + var page = _pages[ctxId].FirstOrDefault(); + if (page != null) + { + await page.CloseAsync(); + } } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs index 4e505997d..4fb916600 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -1,3 +1,5 @@ +using System.Linq; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver @@ -9,7 +11,7 @@ public async Task GoToPage(MessageInfo message, PageActionA try { // Check if the page is already open - if (!args.OpenNewTab && context.Pages.Count > 0) + /*if (!args.OpenNewTab && context.Pages.Count > 0) { foreach (var p in context.Pages) { @@ -18,14 +20,15 @@ public async Task GoToPage(MessageInfo message, PageActionA // Disable this due to performance issue, some page is too large // result.Body = await p.ContentAsync(); result.IsSuccess = true; - await p.BringToFrontAsync(); + // await p.BringToFrontAsync(); return result; } } - } + }*/ - var page = args.OpenNewTab ? await _instance.NewPage(message.ContextId) : + var page = args.OpenNewTab ? await _instance.NewPage(message.ContextId, fetched: args.OnDataFetched) : _instance.GetPage(message.ContextId); + var response = await page.GotoAsync(args.Url); await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); await page.WaitForLoadStateAsync(LoadState.NetworkIdle); @@ -50,4 +53,14 @@ public async Task GoToPage(MessageInfo message, PageActionA return result; } + + private void Page_Response1(object sender, IResponse e) + { + throw new NotImplementedException(); + } + + private void Page_Response(object sender, IResponse e) + { + throw new NotImplementedException(); + } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index c5f7d48cc..4bbd84aca 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -1,4 +1,4 @@ -using System.Xml.Linq; +using System.Web; namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; @@ -88,7 +88,8 @@ public async Task LocateElement(MessageInfo message, Elemen }*/ var html = await locator.InnerHTMLAsync(); - result.Body = html; + // fix if html has & + result.Body = HttpUtility.HtmlDecode(html); result.IsSuccess = true; } else if (count > 1) @@ -129,7 +130,7 @@ public async Task LocateElement(MessageInfo message, Elemen await page.EvaluateAsync($@" (element) => {{ - element.style.outline = '2px solid red'; + element.style.outline = '2px solid {location.HighlightColor}'; }}", handle); result.IsHighlighted = true;