From e18845e4aeebcc803803212327df81b5bb72c8bd Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Thu, 25 Apr 2024 15:43:40 +0800 Subject: [PATCH 01/11] hdong:hard code page size to 10 for Qtoss project currently. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index d11752f64..df0c247bf 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Utilities; public class Pagination { private int _page; - private int _size; + private int _size => 10; public int Page { @@ -20,10 +20,10 @@ public int Size return _size; } - set - { - _size = value; - } + //set + //{ + // _size = value; + //} } public int Offset From 8e2a97e39ffdc981475b02b1bc98a3c2eb2ab69a Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 11 May 2024 22:22:14 -0500 Subject: [PATCH 02/11] Add UserLanguage to IUserIdentity --- .../BotSharp.Abstraction/Users/IUserIdentity.cs | 1 + .../BotSharp.Core/Users/Services/UserIdentity.cs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs index a0998117f..4fecb48c7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs @@ -8,4 +8,5 @@ public interface IUserIdentity string FirstName { get; } string LastName { get; } string FullName { get; } + string? UserLanguage { get; } } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs index b8acdeeac..a8eaac5e7 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs @@ -56,4 +56,14 @@ public string FullName return $"{FirstName} {LastName}".Trim(); } } + + [JsonPropertyName("user_language")] + public string? UserLanguage + { + get + { + _contextAccessor.HttpContext.Request.Headers.TryGetValue("User-Language", out var languages); + return languages.FirstOrDefault(); + } + } } From 8eee1fd1643273e2c4c673367e55e4efc066e705 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 17 May 2024 18:45:18 -0500 Subject: [PATCH 03/11] Change SendHttpRequest args --- .../Browsing/IWebBrowser.cs | 2 +- .../Browsing/Models/ElementActionArgs.cs | 2 ++ .../Browsing/Models/HttpRequestParams.cs | 2 +- .../PlaywrightDriver/PlaywrightInstance.cs | 5 ++-- .../PlaywrightWebDriver.DoAction.cs | 5 ++++ .../PlaywrightWebDriver.HttpRequest.cs | 4 ++-- .../PlaywrightWebDriver.LocateElement.cs | 23 +++++++++++++++++-- .../SeleniumWebDriver.HttpRequest.cs | 4 ++-- .../Functions/HttpRequestFn.cs | 7 +++++- 9 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index 80dd69258..fc9073512 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -24,6 +24,6 @@ public interface IWebBrowser Task EvaluateScript(string contextId, string script); Task CloseBrowser(string contextId); Task CloseCurrentPage(string contextId); - Task SendHttpRequest(string contextId, HttpRequestParams actionParams); + Task SendHttpRequest(MessageInfo message, HttpRequestParams actionParams); Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs index 8b644b316..0d44066a2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs @@ -10,6 +10,8 @@ public class ElementActionArgs public ElementPosition? Position { get; set; } + public string? PressKey { get; set; } + /// /// Required for deserialization /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs index 2b8cc0525..e6d24a8e8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs @@ -18,7 +18,7 @@ public class HttpRequestParams public HttpRequestParams(string url, HttpMethod method, string? payload = null) { - Method = HttpMethod.Get; + Method = method; Url = url; Payload = payload; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs index a801400bd..e7fd59084 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs @@ -40,11 +40,12 @@ public async Task InitContext(string id) Channel = "chrome", IgnoreDefaultArgs = new[] { - "--disable-infobars" + "--enable-automation", }, Args = new[] { "--disable-infobars", + "--no-sandbox", // "--start-maximized" } }); @@ -104,6 +105,6 @@ public async Task CloseCurrentPage(string id) public void Dispose() { _contexts.Clear(); - _playwright.Dispose(); + _playwright?.Dispose(); } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs index dd4600c9c..91a99aa80 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs @@ -28,6 +28,11 @@ await locator.ClickAsync(new LocatorClickOptions else if (action.Action == BroswerActionEnum.InputText) { await locator.FillAsync(action.Content); + + if (action.PressKey != null) + { + await locator.PressAsync(action.PressKey); + } } } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index a5a593341..4a0177a00 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -4,7 +4,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task SendHttpRequest(string contextId, HttpRequestParams args) + public async Task SendHttpRequest(MessageInfo message, HttpRequestParams args) { var result = new BrowserActionResult(); @@ -27,7 +27,7 @@ public async Task SendHttpRequest(string contextId, HttpReq try { - var response = await EvaluateScript(contextId, script); + var response = await EvaluateScript(message.ContextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); } 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 78fa00a3b..13b0464c2 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -1,3 +1,5 @@ +using System.Xml.Linq; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver @@ -18,12 +20,12 @@ public async Task LocateElement(MessageInfo message, Elemen // check if selector is specified if (location.Selector != null) { - locator = page.Locator(location.Selector); + locator = locator.Locator(location.Selector); count = await locator.CountAsync(); } // try attribute - if (count == 0 && !string.IsNullOrEmpty(location.AttributeName)) + if (!string.IsNullOrEmpty(location.AttributeName)) { locator = locator.Locator($"[{location.AttributeName}='{location.AttributeValue}']"); count = await locator.CountAsync(); @@ -65,12 +67,29 @@ public async Task LocateElement(MessageInfo message, Elemen else if (count == 1) { result.Selector = locator.ToString().Split('@').Last(); + + // Make sure the element is visible + await locator.EvaluateAsync("element => element.style.height = ''"); + await locator.EvaluateAsync("element => element.style.width = ''"); + await locator.EvaluateAsync("element => element.style.opacity = ''"); + var text = await locator.InnerTextAsync(); result.Body = text; result.IsSuccess = true; } else if (count > 1) { + // Make sure the element is visible + foreach (var element in await locator.AllAsync()) + { + if (!await element.IsVisibleAsync()) + { + await element.EvaluateAsync("element => element.style.height = '10px'"); + await element.EvaluateAsync("element => element.style.width = '10px'"); + await element.EvaluateAsync("element => element.style.opacity = '1.0'"); + } + } + if (location.FailIfMultiple) { result.Message = $"Multiple elements are found by {locator}"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs index b88a5d96a..c9940318b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs @@ -4,7 +4,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; public partial class SeleniumWebDriver { - public async Task SendHttpRequest(string contextId, HttpRequestParams args) + public async Task SendHttpRequest(MessageInfo message, HttpRequestParams args) { var result = new BrowserActionResult(); @@ -27,7 +27,7 @@ public async Task SendHttpRequest(string contextId, HttpReq try { - var response = await EvaluateScript(contextId, script); + var response = await EvaluateScript(message.ContextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs index c16da29cc..fff2c2401 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs @@ -21,7 +21,12 @@ public async Task Execute(RoleDialogModel message) var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - var result = await _browser.SendHttpRequest(convService.ConversationId, args); + var result = await _browser.SendHttpRequest(new MessageInfo + { + AgentId = agent.Id, + MessageId = message.MessageId, + ContextId = convService.ConversationId + }, args); message.Content = result.IsSuccess ? result.Body : From 018d889ad33429fb756a8b6b4bce3776eff46c36 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 21 May 2024 05:51:38 -0500 Subject: [PATCH 04/11] Add UserCreated hook. --- .../BotSharp.Abstraction/Users/IAuthenticationHook.cs | 1 + .../BotSharp.Core/Users/Services/UserService.cs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs index 33b8086f5..0de103280 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs @@ -8,4 +8,5 @@ public interface IAuthenticationHook Task Authenticate(string id, string password); void AddClaims(List claims); void BeforeSending(Token token); + Task UserCreated(User user); } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 6e856b9f6..d51b6f4ab 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Users.Models; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; @@ -57,6 +56,12 @@ record = user; _logger.LogWarning($"Created new user account: {record.Id} {record.UserName}"); Utilities.ClearCache(); + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + await hook.UserCreated(record); + } + return record; } From f584c3190fc723c4c4d0e95b19d71b72b04e09a5 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 23 May 2024 07:05:36 -0500 Subject: [PATCH 05/11] Fix UserService. --- .../Browsing/Models/BrowserActionResult.cs | 5 +++++ .../Browsing/Models/ElementLocatingArgs.cs | 5 +++++ .../Users/Services/UserService.cs | 2 +- .../PlaywrightWebDriver.LocateElement.cs | 20 ++++++++++++++++--- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs index a634cccd3..b3576b90c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs @@ -8,4 +8,9 @@ public class BrowserActionResult public string Selector { get; set; } public string Body { get; set; } public bool IsHighlighted { get; set; } + + public override string ToString() + { + return $"{IsSuccess} - {Selector}"; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs index 6d6411d87..3b96c7604 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs @@ -5,6 +5,9 @@ public class ElementLocatingArgs [JsonPropertyName("match_rule")] public string MatchRule { get; set; } = string.Empty; + [JsonPropertyName("tag")] + public string? Tag { get; set; } = null!; + [JsonPropertyName("text")] public string? Text { get; set; } @@ -20,6 +23,8 @@ public class ElementLocatingArgs [JsonPropertyName("selector")] public string? Selector { get; set; } + public bool Parent { get; set; } + public bool FailIfMultiple { get; set; } /// diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index de4c12c8d..4324b6f7e 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -77,7 +77,7 @@ record = user; record = db.GetUserByUserName(id); } - User? user = null; + User? user = record; var hooks = _services.GetServices(); if (record == null || record.Source != "internal") { 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 13b0464c2..df6e0a7d0 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -24,6 +24,12 @@ public async Task LocateElement(MessageInfo message, Elemen count = await locator.CountAsync(); } + if (location.Tag != null) + { + locator = page.Locator(location.Tag); + count = await locator.CountAsync(); + } + // try attribute if (!string.IsNullOrEmpty(location.AttributeName)) { @@ -66,12 +72,20 @@ public async Task LocateElement(MessageInfo message, Elemen } else if (count == 1) { + if (location.Parent) + { + locator = locator.Locator(".."); + } + result.Selector = locator.ToString().Split('@').Last(); // Make sure the element is visible - await locator.EvaluateAsync("element => element.style.height = ''"); - await locator.EvaluateAsync("element => element.style.width = ''"); - await locator.EvaluateAsync("element => element.style.opacity = ''"); + /*if (!await locator.IsVisibleAsync()) + { + await locator.EvaluateAsync("element => element.style.height = '15px'"); + await locator.EvaluateAsync("element => element.style.width = '15px'"); + await locator.EvaluateAsync("element => element.style.opacity = '1.0'"); + }*/ var text = await locator.InnerTextAsync(); result.Body = text; From e8656a99c01d88fc4a76456cabc189d5986c7483 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 25 May 2024 20:34:29 -0500 Subject: [PATCH 06/11] Add new user verification code. --- .../Repositories/IBotSharpRepository.cs | 9 +-- .../Users/IUserService.cs | 2 + .../BotSharp.Abstraction/Users/Models/User.cs | 2 + .../Users/Models/UserActivationModel.cs | 7 +++ .../Users/Settings/AccountSetting.cs | 9 +++ .../BotSharp.Core/BotSharpCoreExtensions.cs | 5 ++ .../Repository/BotSharpDbContext.cs | 15 ----- .../FileRepository/FileRepository.User.cs | 9 +++ .../Users/Services/UserService.cs | 59 ++++++++++++++++++- .../Controllers/UserController.cs | 12 ++++ .../Collections/UserDocument.cs | 7 ++- .../Repository/MongoRepository.User.cs | 10 ++++ .../PlaywrightWebDriver.HttpRequest.cs | 1 + 13 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index a451071e7..6adcc8ad8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -17,10 +17,11 @@ public interface IBotSharpRepository #endregion #region User - User? GetUserByEmail(string email); - User? GetUserById(string id); - User? GetUserByUserName(string userName); - void CreateUser(User user); + User? GetUserByEmail(string email) => throw new NotImplementedException(); + User? GetUserById(string id) => throw new NotImplementedException(); + User? GetUserByUserName(string userName) => throw new NotImplementedException(); + void CreateUser(User user) => throw new NotImplementedException(); + void UpdateUserVerified(string userId) => throw new NotImplementedException(); #endregion #region Agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index debf68f4d..8f314ffa5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Users.Models; +using BotSharp.OpenAPI.ViewModels.Users; namespace BotSharp.Abstraction.Users; @@ -6,6 +7,7 @@ public interface IUserService { Task GetUser(string id); Task CreateUser(User user); + Task ActiveUser(UserActivationModel model); Task GetToken(string authorization); Task GetMyProfile(); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs index 7ca441fe0..4e6ca265a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs @@ -14,6 +14,8 @@ public class User public string Source { get; set; } = "internal"; public string? ExternalId { get; set; } public string Role { get; set; } = UserRole.Client; + public string? VerificationCode { get; set; } + public bool Verified { get; set; } public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs new file mode 100644 index 000000000..904bd9360 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs @@ -0,0 +1,7 @@ +namespace BotSharp.OpenAPI.ViewModels.Users; + +public class UserActivationModel +{ + public string UserName { get; set; } + public string VerificationCode { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs new file mode 100644 index 000000000..06176ce62 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Users.Settings; + +public class AccountSetting +{ + /// + /// Whether to enable verification code to verify the authenticity of new users + /// + public bool NewUserVerification { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index 51f3eb1de..2dd2dc7f9 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -5,6 +5,7 @@ using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Messaging.JsonConverters; +using BotSharp.Abstraction.Users.Settings; namespace BotSharp.Core; @@ -84,6 +85,10 @@ public static void RegisterPlugins(IServiceCollection services, IConfiguration c return settingService.Bind("PluginLoader"); }); + var accountSettings = new AccountSetting(); + config.Bind("Account", accountSettings); + services.AddScoped(x => accountSettings); + var loader = new PluginLoader(services, config, pluginSettings); loader.Load(assembly => { diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 28aac06bb..6bba6c2a2 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -1,6 +1,5 @@ using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Plugins.Models; -using BotSharp.Abstraction.Repositories.Models; using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Users.Models; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -176,20 +175,6 @@ public IEnumerable TruncateConversation(string conversationId, string me => throw new NotImplementedException(); #endregion - #region User - public User? GetUserByEmail(string email) - => throw new NotImplementedException(); - - public User? GetUserById(string id) - => throw new NotImplementedException(); - - public User? GetUserByUserName(string userName) - => throw new NotImplementedException(); - - public void CreateUser(User user) - => throw new NotImplementedException(); - #endregion - #region Execution Log public void AddExecutionLogs(string conversationId, List logs) { diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index bc992a082..36f17b854 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -32,4 +32,13 @@ public void CreateUser(User user) var path = Path.Combine(dir, "user.json"); File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); } + + public void UpdateUserVerified(string userId) + { + var user = GetUserById(userId); + user.Verified = true; + var dir = Path.Combine(_dbSettings.FileRepository, "users", user.Id); + var path = Path.Combine(dir, "user.json"); + File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); + } } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 4324b6f7e..ae5220b97 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -1,4 +1,6 @@ using BotSharp.Abstraction.Users.Models; +using BotSharp.Abstraction.Users.Settings; +using BotSharp.OpenAPI.ViewModels.Users; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using NanoidDotNet; @@ -12,12 +14,17 @@ public class UserService : IUserService private readonly IServiceProvider _services; private readonly IUserIdentity _user; private readonly ILogger _logger; + private readonly AccountSetting _setting; - public UserService(IServiceProvider services, IUserIdentity user, ILogger logger) + public UserService(IServiceProvider services, + IUserIdentity user, + ILogger logger, + AccountSetting setting) { _services = services; _user = user; _logger = logger; + _setting = setting; } public async Task CreateUser(User user) @@ -51,6 +58,12 @@ record = user; record.Salt = Guid.NewGuid().ToString("N"); record.Password = Utilities.HashText(user.Password, record.Salt); + if (_setting.NewUserVerification) + { + record.VerificationCode = Nanoid.Generate(alphabet: "0123456789", size: 6); + record.Verified = false; + } + db.CreateUser(record); _logger.LogWarning($"Created new user account: {record.Id} {record.UserName}"); @@ -120,6 +133,11 @@ record = db.GetUserByUserName(id); return default; } + if (_setting.NewUserVerification && !record.Verified) + { + return default; + } + #if !DEBUG if (Utilities.HashText(password, record.Salt) != record.Password) { @@ -206,4 +224,43 @@ public async Task GetUser(string id) var user = db.GetUserById(id); return user; } + + public async Task ActiveUser(UserActivationModel model) + { + var id = model.UserName; + var db = _services.GetRequiredService(); + var record = id.Contains("@") ? db.GetUserByEmail(id) : db.GetUserByUserName(id); + if (record == null) + { + record = db.GetUserByUserName(id); + } + + if (record == null) + { + return default; + } + + if (record.VerificationCode != model.VerificationCode) + { + return default; + } + + if (record.Verified) + { + return default; + } + + db.UpdateUserVerified(record.Id); + + var accessToken = GenerateJwtToken(record); + var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); + var token = new Token + { + AccessToken = accessToken, + ExpireTime = jwt.Payload.Exp.Value, + TokenType = "Bearer", + Scope = "api" + }; + return token; + } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index e4201aa8c..317d1fc9e 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -61,6 +61,18 @@ public async Task CreateUser(UserCreationModel user) return UserViewModel.FromUser(createdUser); } + [AllowAnonymous] + [HttpPost("/user/activate")] + public async Task> ActivateUser(UserActivationModel model) + { + var token = await _userService.ActiveUser(model); + if (token == null) + { + return Unauthorized(); + } + return Ok(token); + } + [HttpGet("/user/me")] public async Task GetMyUserProfile() { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index c21a6e08c..c277b9f3e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -13,7 +13,8 @@ public class UserDocument : MongoBase public string Source { get; set; } = "internal"; public string? ExternalId { get; set; } public string Role { get; set; } - + public string? VerificationCode { get; set; } + public bool Verified { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } @@ -30,7 +31,9 @@ public User ToUser() Salt = Salt, Source = Source, ExternalId = ExternalId, - Role = Role + Role = Role, + VerificationCode = VerificationCode, + Verified = Verified, }; } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index 1b6b8b772..1a5211f4a 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -40,10 +40,20 @@ public void CreateUser(User user) Source = user.Source, ExternalId = user.ExternalId, Role = user.Role, + VerificationCode = user.VerificationCode, + Verified = user.Verified, CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow }; _dc.Users.InsertOne(userCollection); } + + public void UpdateUserVerified(string userId) + { + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Verified, true) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + _dc.Users.UpdateOne(filter, update); + } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index 4a0177a00..a769332b2 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -27,6 +27,7 @@ public async Task SendHttpRequest(MessageInfo message, Http try { + _logger.LogInformation($"SendHttpRequest: {args.Url}"); var response = await EvaluateScript(message.ContextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); From 0ffabd35aa09364697536979618b2ed87d669a15 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Tue, 28 May 2024 19:47:48 +0800 Subject: [PATCH 07/11] hdong: #65. --- .../Users/IUserService.cs | 2 ++ .../Users/Services/UserService.cs | 26 +++++++++++++++++++ .../Controllers/UserController.cs | 14 ++++++++++ 3 files changed, 42 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 8f314ffa5..4071790c7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -10,4 +10,6 @@ public interface IUserService Task ActiveUser(UserActivationModel model); Task GetToken(string authorization); Task GetMyProfile(); + Task VerifyUserUnique(string userName); + Task VerifyEmailUnique(string email); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index ae5220b97..19242e398 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -263,4 +263,30 @@ record = db.GetUserByUserName(id); }; return token; } + + public async Task VerifyUserUnique(string userName) + { + if (string.IsNullOrEmpty(userName)) + return false; + + var db = _services.GetRequiredService(); + var user = db.GetUserByUserName(userName); + if (user == null) + return true; + + return false; + } + + public async Task VerifyEmailUnique(string email) + { + if (string.IsNullOrEmpty(email)) + return false; + + var db = _services.GetRequiredService(); + var emailName = db.GetUserByEmail(email); + if (emailName == null) + return true; + + return false; + } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index 317d1fc9e..8228e594d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -94,4 +94,18 @@ public async Task GetMyUserProfile() } return UserViewModel.FromUser(user); } + + [AllowAnonymous] + [HttpPost("/user/unique/{username}")] + public async Task VerifyUserUnique([FromRoute] string userName) + { + return await _userService.VerifyUserUnique(userName); + } + + [AllowAnonymous] + [HttpPost("/email/unique/{email}")] + public async Task VerifyEmailUnique([FromRoute]string email) + { + return await _userService.VerifyEmailUnique(email); + } } From c9428cd965f472edb291282a2a0a1fdf4ddfd42c Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Wed, 29 May 2024 10:20:38 +0800 Subject: [PATCH 08/11] hdong: clean up code for Pagination size. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 660c8e262..382bd55dc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Utilities; public class Pagination { private int _page; - private int _size => 10; + private int _size; public int Page { @@ -19,11 +19,11 @@ public int Size if (_size > 100) return 100; return _size; - } - //set - //{ - // _size = value; - //} + } + set + { + _size = value; + } } /// From 8a9b6f319bde02552990c266da84fcb817c887f0 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Wed, 29 May 2024 10:36:27 +0800 Subject: [PATCH 09/11] hdong: remove the limit. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 382bd55dc..512d27c4c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -13,11 +13,8 @@ public int Page public int Size { - get + get { - if (_size <= 0) return 20; - if (_size > 100) return 100; - return _size; } set From 9fc7f1c3b0a2cbd3d34d6f8ae2ac08b661e9d2f5 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Wed, 29 May 2024 10:50:41 +0800 Subject: [PATCH 10/11] hdong:change request method and API name. --- .../BotSharp.OpenAPI/Controllers/UserController.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index 8228e594d..e413724bc 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -95,16 +95,14 @@ public async Task GetMyUserProfile() return UserViewModel.FromUser(user); } - [AllowAnonymous] - [HttpPost("/user/unique/{username}")] - public async Task VerifyUserUnique([FromRoute] string userName) + [HttpGet("/user/name/existing")] + public async Task VerifyUserUnique([FromQuery] string userName) { return await _userService.VerifyUserUnique(userName); } - [AllowAnonymous] - [HttpPost("/email/unique/{email}")] - public async Task VerifyEmailUnique([FromRoute]string email) + [HttpGet("/user/email/existing")] + public async Task VerifyEmailUnique([FromQuery] string email) { return await _userService.VerifyEmailUnique(email); } From e9ae471a6571ab622fe9fee4df6226e82b86568c Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 28 May 2024 22:44:49 -0500 Subject: [PATCH 11/11] Remove openIfNotExist from LaunchBrowser --- .../Browsing/IWebBrowser.cs | 2 +- .../PlaywrightWebDriver.GoToPage.cs | 18 +++++++++++++++++- .../PlaywrightWebDriver.LaunchBrowser.cs | 2 +- .../PlaywrightWebDriver.LocateElement.cs | 4 ++-- .../SeleniumWebDriver.LaunchBrowser.cs | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index fc9073512..e2581ba9b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -4,7 +4,7 @@ namespace BotSharp.Abstraction.Browsing; public interface IWebBrowser { - Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true); + Task LaunchBrowser(string contextId, string? url); Task ScreenshotAsync(string contextId, string path); Task ScrollPageAsync(BrowserActionParams actionParams); 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 5513e89f7..f830bc9f5 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -5,13 +5,29 @@ public partial class PlaywrightWebDriver public async Task GoToPage(string contextId, string url, bool openNewTab = false) { var result = new BrowserActionResult(); + var context = await _instance.InitInstance(contextId); try { + // Check if the page is already open + foreach (var p in context.Pages) + { + if (p.Url == url) + { + result.Body = await p.ContentAsync(); + result.IsSuccess = true; + await p.BringToFrontAsync(); + return result; + } + } + var page = openNewTab ? await _instance.NewPage(contextId) : _instance.GetPage(contextId); var response = await page.GotoAsync(url); await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); - await page.WaitForLoadStateAsync(LoadState.NetworkIdle); + await page.WaitForLoadStateAsync(LoadState.NetworkIdle, new PageWaitForLoadStateOptions + { + Timeout = 1000 * 60 * 5 + }); if (response.Status == 200) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs index b3afd940e..df8448286 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true) + public async Task LaunchBrowser(string contextId, string? url) { var result = new BrowserActionResult() { 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 df6e0a7d0..60e0ce951 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -87,8 +87,8 @@ public async Task LocateElement(MessageInfo message, Elemen await locator.EvaluateAsync("element => element.style.opacity = '1.0'"); }*/ - var text = await locator.InnerTextAsync(); - result.Body = text; + var html = await locator.InnerHTMLAsync(); + result.Body = html; result.IsSuccess = true; } else if (count > 1) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs index 3203b617a..73cfa9eee 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; public partial class SeleniumWebDriver { - public async Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true) + public async Task LaunchBrowser(string contextId, string? url) { var result = new BrowserActionResult() {