Skip to content

Commit c27acab

Browse files
authored
Merge pull request #536 from visagang/features/vguruparan
Add new email handler utility
2 parents 6dc518b + 149de9a commit c27acab

File tree

11 files changed

+313
-0
lines changed

11 files changed

+313
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace BotSharp.Abstraction.Email.Settings;
2+
3+
public class EmailHandlerSettings
4+
{
5+
public string EmailAddress { get; set; } = string.Empty;
6+
public string Name { get; set; } = string.Empty;
7+
public string Username { get; set; } = string.Empty;
8+
public string Password { get; set; } = string.Empty;
9+
public string SMTPServer { get; set; } = string.Empty;
10+
public int SMTPPort { get; set; }
11+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(TargetFramework)</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<LangVersion>$(LangVersion)</LangVersion>
7+
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
8+
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
9+
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
10+
<OutputPath>$(SolutionDir)packages</OutputPath>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\handle_email_request.json" />
15+
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\handle_email_request.fn.liquid" />
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\handle_email_request.json">
20+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
21+
</Content>
22+
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\handle_email_request.fn.liquid">
23+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
24+
</Content>
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<PackageReference Include="MailKit" Version="4.7.0" />
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
33+
</ItemGroup>
34+
35+
</Project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using BotSharp.Abstraction.Agents;
2+
using BotSharp.Abstraction.Email.Settings;
3+
using BotSharp.Abstraction.Settings;
4+
using BotSharp.Plugin.EmailHandler.Hooks;
5+
using Microsoft.Extensions.Configuration;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace BotSharp.Plugin.EmailHandler
13+
{
14+
public class EmailHandlerPlugin : IBotSharpPlugin
15+
{
16+
public string Id => "a8e217de-e413-47a8-bbf1-af9207392a63";
17+
public string Name => "Email Handler";
18+
public string Description => "Empower agent to handle sending out emails";
19+
public string IconUrl => "https://cdn-icons-png.freepik.com/512/6711/6711567.png";
20+
21+
public void RegisterDI(IServiceCollection services, IConfiguration config)
22+
{
23+
services.AddScoped(provider =>
24+
{
25+
var settingService = provider.GetRequiredService<ISettingService>();
26+
return settingService.Bind<EmailHandlerSettings>("EmailHandler");
27+
});
28+
29+
services.AddScoped<IAgentHook, EmailHandlerHook>();
30+
services.AddScoped<IAgentUtilityHook, EmailHandlerUtilityHook>();
31+
}
32+
}
33+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace BotSharp.Plugin.EmailHandler.Enums
8+
{
9+
public class Utility
10+
{
11+
public const string EmailHandler = "email-handler";
12+
}
13+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using BotSharp.Abstraction.Email.Settings;
2+
using BotSharp.Plugin.EmailHandler.LlmContexts;
3+
using MailKit;
4+
using MailKit.Net.Smtp;
5+
using MailKit.Security;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.Extensions.Logging;
8+
using MimeKit;
9+
using System.Net.Http;
10+
11+
namespace BotSharp.Plugin.EmailHandler.Functions;
12+
13+
public class HandleEmailRequestFn : IFunctionCallback
14+
{
15+
public string Name => "handle_email_request";
16+
public string Indication => "Handling email request";
17+
18+
private readonly IServiceProvider _services;
19+
private readonly ILogger<HandleEmailRequestFn> _logger;
20+
private readonly IHttpClientFactory _httpClientFactory;
21+
private readonly IHttpContextAccessor _context;
22+
private readonly BotSharpOptions _options;
23+
private readonly EmailHandlerSettings _emailSettings;
24+
25+
public HandleEmailRequestFn(IServiceProvider services,
26+
ILogger<HandleEmailRequestFn> logger,
27+
IHttpClientFactory httpClientFactory,
28+
IHttpContextAccessor context,
29+
BotSharpOptions options,
30+
EmailHandlerSettings emailPluginSettings)
31+
{
32+
_services = services;
33+
_logger = logger;
34+
_httpClientFactory = httpClientFactory;
35+
_context = context;
36+
_options = options;
37+
_emailSettings = emailPluginSettings;
38+
}
39+
public async Task<bool> Execute(RoleDialogModel message)
40+
{
41+
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs, _options.JsonSerializerOptions);
42+
var recipient = args?.ToAddress;
43+
var body = args?.Content;
44+
var subject = args?.Subject;
45+
46+
try
47+
{
48+
var mailMessage = new MimeMessage();
49+
mailMessage.From.Add(new MailboxAddress(_emailSettings.Name, _emailSettings.EmailAddress));
50+
mailMessage.To.Add(new MailboxAddress("", recipient));
51+
mailMessage.Subject = subject;
52+
mailMessage.Body = new TextPart("plain")
53+
{
54+
Text = body
55+
};
56+
var response = await HandleSendEmailBySMTP(mailMessage);
57+
_logger.LogWarning($"Email successfully send over to {recipient}. Email Subject: {subject} [{response}]");
58+
message.Content = response;
59+
return true;
60+
}
61+
catch (Exception ex)
62+
{
63+
var msg = $"Failed to send the email. {ex.Message}";
64+
_logger.LogError($"{msg}\n(Error: {ex.Message})");
65+
message.Content = msg;
66+
return false;
67+
}
68+
}
69+
70+
public async Task<string> HandleSendEmailBySMTP(MimeMessage mailMessage)
71+
{
72+
using var smtpClient = new SmtpClient();
73+
await smtpClient.ConnectAsync(_emailSettings.SMTPServer, _emailSettings.SMTPPort, SecureSocketOptions.StartTls);
74+
await smtpClient.AuthenticateAsync(_emailSettings.EmailAddress, _emailSettings.Password);
75+
var response = await smtpClient.SendAsync(mailMessage);
76+
return response;
77+
}
78+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using BotSharp.Abstraction.Agents;
2+
using BotSharp.Abstraction.Agents.Enums;
3+
using BotSharp.Abstraction.Agents.Settings;
4+
using BotSharp.Abstraction.Functions.Models;
5+
using BotSharp.Abstraction.Repositories;
6+
using BotSharp.Plugin.EmailHandler.Enums;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
13+
namespace BotSharp.Plugin.EmailHandler.Hooks;
14+
15+
public class EmailHandlerHook : AgentHookBase
16+
{
17+
private static string FUNCTION_NAME = "handle_email_request";
18+
19+
public override string SelfId => string.Empty;
20+
21+
public EmailHandlerHook(IServiceProvider services, AgentSettings settings)
22+
: base(services, settings)
23+
{
24+
}
25+
public override void OnAgentLoaded(Agent agent)
26+
{
27+
var conv = _services.GetRequiredService<IConversationService>();
28+
var isConvMode = conv.IsConversationMode();
29+
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.EmailHandler);
30+
31+
if (isConvMode && isEnabled)
32+
{
33+
var (prompt, fn) = GetPromptAndFunction();
34+
if (fn != null)
35+
{
36+
if (!string.IsNullOrWhiteSpace(prompt))
37+
{
38+
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
39+
}
40+
41+
if (agent.Functions == null)
42+
{
43+
agent.Functions = new List<FunctionDef> { fn };
44+
}
45+
else
46+
{
47+
agent.Functions.Add(fn);
48+
}
49+
}
50+
}
51+
52+
base.OnAgentLoaded(agent);
53+
}
54+
55+
private (string, FunctionDef?) GetPromptAndFunction()
56+
{
57+
var db = _services.GetRequiredService<IBotSharpRepository>();
58+
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
59+
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty;
60+
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME));
61+
return (prompt, loadAttachmentFn);
62+
}
63+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using BotSharp.Abstraction.Agents;
2+
using BotSharp.Plugin.EmailHandler.Enums;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace BotSharp.Plugin.EmailHandler.Hooks
10+
{
11+
public class EmailHandlerUtilityHook : IAgentUtilityHook
12+
{
13+
public void AddUtilities(List<string> utilities)
14+
{
15+
utilities.Add(Utility.EmailHandler);
16+
}
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Text.Json.Serialization;
6+
using System.Threading.Tasks;
7+
8+
namespace BotSharp.Plugin.EmailHandler.LlmContexts
9+
{
10+
public class LlmContextIn
11+
{
12+
[JsonPropertyName("to_address")]
13+
public string? ToAddress { get; set; }
14+
15+
[JsonPropertyName("email_content")]
16+
public string? Content { get; set; }
17+
[JsonPropertyName("subject")]
18+
public string? Subject { get; set; }
19+
}
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
global using System;
2+
global using System.Collections.Generic;
3+
global using System.Text;
4+
global using BotSharp.Abstraction.Conversations;
5+
global using BotSharp.Abstraction.Plugins;
6+
global using System.Text.Json;
7+
global using BotSharp.Abstraction.Conversations.Models;
8+
global using System.Threading.Tasks;
9+
global using BotSharp.Abstraction.Functions;
10+
global using BotSharp.Abstraction.Agents.Models;
11+
global using BotSharp.Abstraction.Templating;
12+
global using Microsoft.Extensions.DependencyInjection;
13+
global using System.Linq;
14+
global using BotSharp.Abstraction.Utilities;
15+
global using BotSharp.Abstraction.Messaging;
16+
global using BotSharp.Abstraction.Messaging.Models.RichContent;
17+
global using BotSharp.Abstraction.Options;
18+
global using BotSharp.Abstraction.Http.Settings;
19+
global using BotSharp.Abstraction.Messaging.Enums;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "handle_email_request",
3+
"description": "If the user requests to send an email, you need to capture the email content and the recipient email address. If the user explicitly enter email subject use the same if not intelligently capture the email subject from the content. Then call this function to send out email.",
4+
"parameters": {
5+
"type": "object",
6+
"properties": {
7+
"to_address": {
8+
"type": "string",
9+
"description": "The email address to which the email should be sent to. It needs to be a valid email address in the correct string format."
10+
},
11+
"email_content": {
12+
"type": "string",
13+
"description": "The content of the email which needs to be send over. It can be plain string or a raw html."
14+
},
15+
"subject": {
16+
"type": "string",
17+
"description": "The subject of the email which needs to be send over."
18+
}
19+
},
20+
"required": [ "to_address", "email_content", "subject" ]
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Please call handle_email_request if user wants to send out an email.

0 commit comments

Comments
 (0)