Adds MailKit (#69)
This commit is contained in:
parent
d6bba58b59
commit
e11d1f3ad9
5 changed files with 151 additions and 153 deletions
|
|
@ -1,8 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Mail;
|
||||
using Server.Accounting;
|
||||
using Server.Misc;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
|
|
@ -49,7 +46,7 @@ namespace Server.Engines.Help
|
|||
m_Timer.Start();
|
||||
}
|
||||
|
||||
public Mobile Sender{ get; }
|
||||
public Mobile Sender { get; }
|
||||
|
||||
public Mobile Handler
|
||||
{
|
||||
|
|
@ -61,17 +58,17 @@ namespace Server.Engines.Help
|
|||
}
|
||||
}
|
||||
|
||||
public DateTime Sent{ get; }
|
||||
public DateTime Sent { get; }
|
||||
|
||||
public string Message{ get; }
|
||||
public string Message { get; }
|
||||
|
||||
public PageType Type{ get; }
|
||||
public PageType Type { get; }
|
||||
|
||||
public Point3D PageLocation{ get; }
|
||||
public Point3D PageLocation { get; }
|
||||
|
||||
public Map PageMap{ get; }
|
||||
public Map PageMap { get; }
|
||||
|
||||
public List<SpeechLogEntry> SpeechLog{ get; }
|
||||
public List<SpeechLogEntry> SpeechLog { get; }
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
|
|
@ -118,7 +115,7 @@ namespace Server.Engines.Help
|
|||
private static Dictionary<Mobile, PageEntry> m_KeyedByHandler = new Dictionary<Mobile, PageEntry>();
|
||||
private static Dictionary<Mobile, PageEntry> m_KeyedBySender = new Dictionary<Mobile, PageEntry>();
|
||||
|
||||
public static List<PageEntry> List{ get; } = new List<PageEntry>();
|
||||
public static List<PageEntry> List { get; } = new List<PageEntry>();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
|
|
@ -207,6 +204,7 @@ namespace Server.Engines.Help
|
|||
Remove(GetEntry(sender));
|
||||
}
|
||||
|
||||
|
||||
public static void Enqueue(PageEntry entry)
|
||||
{
|
||||
List.Add(entry);
|
||||
|
|
@ -230,55 +228,9 @@ namespace Server.Engines.Help
|
|||
entry.Sender.SendMessage(
|
||||
"We are sorry, but no staff members are currently available to assist you. Your page will remain in the queue until one becomes available, or until you cancel it manually.");
|
||||
|
||||
if (Email.FromAddress != null && Email.SpeechLogPageAddresses != null && entry.SpeechLog != null)
|
||||
SendEmail(entry);
|
||||
if (Email.FROM_ADDRESS != null && Email.SPEECH_LOG_PAGE_ADDRESS != null && entry.SpeechLog != null)
|
||||
Email.SendQueueEmail(entry, GetPageTypeName(entry.Type));
|
||||
}
|
||||
|
||||
private static void SendEmail(PageEntry entry)
|
||||
{
|
||||
Mobile sender = entry.Sender;
|
||||
DateTime time = DateTime.UtcNow;
|
||||
|
||||
MailMessage mail = new MailMessage(Email.FromAddress, Email.SpeechLogPageAddresses)
|
||||
{
|
||||
Subject = "RunUO Speech Log Page Forwarding"
|
||||
};
|
||||
|
||||
using (StringWriter writer = new StringWriter())
|
||||
{
|
||||
writer.WriteLine("RunUO Speech Log Page - {0}", GetPageTypeName(entry.Type));
|
||||
writer.WriteLine();
|
||||
|
||||
writer.WriteLine("From: '{0}', Account: '{1}'", sender.RawName,
|
||||
sender.Account is Account ? sender.Account.Username : "???");
|
||||
writer.WriteLine("Location: {0} [{1}]", sender.Location, sender.Map);
|
||||
writer.WriteLine("Sent on: {0}/{1:00}/{2:00} {3}:{4:00}:{5:00}", time.Year, time.Month, time.Day, time.Hour,
|
||||
time.Minute, time.Second);
|
||||
writer.WriteLine();
|
||||
|
||||
writer.WriteLine("Message:");
|
||||
writer.WriteLine("'{0}'", entry.Message);
|
||||
writer.WriteLine();
|
||||
|
||||
writer.WriteLine("Speech Log");
|
||||
writer.WriteLine("==========");
|
||||
|
||||
foreach (SpeechLogEntry logEntry in entry.SpeechLog)
|
||||
{
|
||||
Mobile from = logEntry.From;
|
||||
string fromName = from.RawName;
|
||||
string fromAccount = from.Account is Account ? from.Account.Username : "???";
|
||||
DateTime created = logEntry.Created;
|
||||
string speech = logEntry.Speech;
|
||||
|
||||
writer.WriteLine("{0}:{1:00}:{2:00} - {3} ({4}): '{5}'", created.Hour, created.Minute, created.Second,
|
||||
fromName, fromAccount, speech);
|
||||
}
|
||||
|
||||
mail.Body = writer.ToString();
|
||||
}
|
||||
|
||||
Email.AsyncSend(mail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Mail;
|
||||
using Server.Accounting;
|
||||
using Server.Network;
|
||||
|
||||
|
|
@ -43,18 +42,9 @@ namespace Server.Misc
|
|||
{
|
||||
Console.Write("Crash: Sending email...");
|
||||
|
||||
MailMessage message = new MailMessage(Email.FromAddress, Email.CrashAddresses);
|
||||
if (Email.FROM_ADDRESS != null && Email.CRASH_ADDRESS != null)
|
||||
Email.SendCrashEmail(filePath);
|
||||
|
||||
message.Subject = "Automated RunUO Crash Report";
|
||||
|
||||
message.Body = "Automated RunUO Crash Report. See attachment for details.";
|
||||
|
||||
message.Attachments.Add(new Attachment(filePath));
|
||||
|
||||
if (Email.Send(message))
|
||||
Console.WriteLine("done");
|
||||
else
|
||||
Console.WriteLine("failed");
|
||||
}
|
||||
|
||||
private static string GetRoot()
|
||||
|
|
@ -69,13 +59,7 @@ namespace Server.Misc
|
|||
}
|
||||
}
|
||||
|
||||
private static string Combine(string path1, string path2)
|
||||
{
|
||||
if (path1.Length == 0)
|
||||
return path2;
|
||||
|
||||
return Path.Combine(path1, path2);
|
||||
}
|
||||
private static string Combine(string path1, string path2) => path1.Length == 0 ? path2 : Path.Combine(path1, path2);
|
||||
|
||||
private static void Restart(CrashedEventArgs e)
|
||||
{
|
||||
|
|
@ -247,7 +231,7 @@ namespace Server.Misc
|
|||
|
||||
Console.WriteLine("done");
|
||||
|
||||
if (Email.FromAddress != null && Email.CrashAddresses != null)
|
||||
if (Email.FROM_ADDRESS != null && Email.CRASH_ADDRESS != null)
|
||||
SendEmail(filePath);
|
||||
}
|
||||
catch
|
||||
|
|
@ -263,4 +247,4 @@ namespace Server.Misc
|
|||
return $"{now.Day}-{now.Month}-{now.Year}-{now.Hour}-{now.Minute}-{now.Second}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,98 +1,131 @@
|
|||
using MailKit.Net.Smtp;
|
||||
using MimeKit;
|
||||
using Server.Accounting;
|
||||
using Server.Engines.Help;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Misc
|
||||
{
|
||||
public class Email
|
||||
public static class Email
|
||||
{
|
||||
/* In order to support emailing, fill in EmailServer and FromAddress:
|
||||
* Example:
|
||||
* public static readonly string EmailServer = "mail.domain.com";
|
||||
* public static readonly string FromAddress = "runuo@domain.com";
|
||||
*
|
||||
* If you want to add crash reporting emailing, fill in CrashAddresses:
|
||||
* Example:
|
||||
* public static readonly string CrashAddresses = "first@email.here,second@email.here,third@email.here";
|
||||
*
|
||||
* If you want to add speech log page emailing, fill in SpeechLogPageAddresses:
|
||||
* Example:
|
||||
* public static readonly string SpeechLogPageAddresses = "first@email.here,second@email.here,third@email.here";
|
||||
*/
|
||||
public static readonly MailboxAddress FROM_ADDRESS = new MailboxAddress(Configuration.Instance.emailSettings.FromName, Configuration.Instance.emailSettings.FromAddress);
|
||||
public static readonly MailboxAddress CRASH_ADDRESS = new MailboxAddress(Configuration.Instance.emailSettings.crashName, Configuration.Instance.emailSettings.crashAddress);
|
||||
public static readonly MailboxAddress SPEECH_LOG_PAGE_ADDRESS = new MailboxAddress(Configuration.Instance.emailSettings.speechLogPageName, Configuration.Instance.emailSettings.speechLogPageAddress);
|
||||
public static readonly string EMAIL_SERVER = Configuration.Instance.emailSettings.emailServer;
|
||||
public static readonly int EMAIL_PORT = Configuration.Instance.emailSettings.emailPort;
|
||||
public static readonly string EMAIL_SERVER_USERNAME = Configuration.Instance.emailSettings.emailUsername;
|
||||
public static readonly string EMAIL_SERVER_PASSWORD = Configuration.Instance.emailSettings.emailPassword;
|
||||
public static readonly int RETRY_SEND_EMAIL_COUNT = 5;
|
||||
public static readonly int SEND_DELAY_SECONDS = 2;
|
||||
|
||||
public static readonly string EmailServer = null;
|
||||
public static readonly int EmailPort = 25;
|
||||
|
||||
public static readonly string FromAddress = null;
|
||||
public static readonly string EmailUsername = null;
|
||||
public static readonly string EmailPassword = null;
|
||||
|
||||
|
||||
public static readonly string CrashAddresses = null;
|
||||
public static readonly string SpeechLogPageAddresses = null;
|
||||
|
||||
private static Regex _pattern = new Regex(@"^[a-z0-9.+_-]+@([a-z0-9-]+\.)+[a-z]+$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static SmtpClient _Client;
|
||||
|
||||
public static bool IsValid(string address)
|
||||
/// <summary>
|
||||
/// Sends Queue-Page request using Email
|
||||
/// </summary>
|
||||
/// <param name="entry"></param>
|
||||
/// <param name="pageType"></param>
|
||||
public static void SendQueueEmail(PageEntry entry, string pageType)
|
||||
{
|
||||
if (address == null || address.Length > 320)
|
||||
return false;
|
||||
Mobile sender = entry.Sender;
|
||||
DateTime time = DateTime.UtcNow;
|
||||
|
||||
return _pattern.IsMatch(address);
|
||||
}
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(FROM_ADDRESS);
|
||||
message.To.Add(SPEECH_LOG_PAGE_ADDRESS);
|
||||
message.Subject = "ModernUO Speech Log Page Forwarding";
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
if (EmailServer != null)
|
||||
using (StringWriter writer = new StringWriter())
|
||||
{
|
||||
_Client = new SmtpClient(EmailServer, EmailPort);
|
||||
if (EmailUsername != null) _Client.Credentials = new NetworkCredential(EmailUsername, EmailPassword);
|
||||
}
|
||||
}
|
||||
writer.WriteLine(@$"
|
||||
ModernUO Speech Log Page - {pageType}
|
||||
|
||||
public static bool Send(MailMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
// .NET relies on the MTA to generate Message-ID header. Not all MTAs will add this header.
|
||||
From: '{sender.RawName}', Account: '{((sender.Account is Account accSend) ? accSend.Username : " ??? ")}'
|
||||
|
||||
DateTime now = DateTime.UtcNow;
|
||||
string messageID = $"<{now.ToString("yyyyMMdd")}.{now.ToString("HHmmssff")}@{EmailServer}>";
|
||||
message.Headers.Add("Message-ID", messageID);
|
||||
Location: {sender.Location} [{sender.Map}]
|
||||
Sent on: {time.Year}/{time.Month:00}/{time.Day:00} {time.Hour}:{time.Minute:00}:{time.Second:00}
|
||||
|
||||
message.Headers.Add("X-Mailer", "RunUO");
|
||||
Message:
|
||||
'{entry.Message}'
|
||||
|
||||
lock (_Client)
|
||||
Speech Log
|
||||
==========
|
||||
");
|
||||
|
||||
foreach (SpeechLogEntry logEntry in entry.SpeechLog)
|
||||
{
|
||||
_Client.Send(message);
|
||||
Mobile from = logEntry.From;
|
||||
string fromName = from.RawName;
|
||||
string fromAccount = from.Account is Account accFrom ? accFrom.Username : "???";
|
||||
DateTime created = logEntry.Created;
|
||||
string speech = logEntry.Speech;
|
||||
writer.WriteLine(@$"{created.Hour}:{created.Minute:00}:{created.Second:00} - {fromName} ({fromAccount}): '{speech}'");
|
||||
}
|
||||
|
||||
message.Body = new BodyBuilder
|
||||
{
|
||||
TextBody = writer.ToString(),
|
||||
HtmlBody = null
|
||||
}.ToMessageBody();
|
||||
|
||||
}
|
||||
catch
|
||||
SendAsync(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends crash email
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
public static void SendCrashEmail(string filePath)
|
||||
{
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(FROM_ADDRESS);
|
||||
message.To.Add(CRASH_ADDRESS);
|
||||
message.Subject = "Automated ModernUO Crash Report";
|
||||
var builder = new BodyBuilder
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
TextBody = "Automated ModernUO Crash Report. See attachment for details.",
|
||||
HtmlBody = null
|
||||
};
|
||||
builder.Attachments.Add(filePath);
|
||||
message.Body = builder.ToMessageBody();
|
||||
}
|
||||
|
||||
public static void AsyncSend(MailMessage message)
|
||||
/// <summary>
|
||||
/// Sends emails async
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
private static async void SendAsync(MimeMessage message)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(SendCallback, message);
|
||||
}
|
||||
DateTime now = DateTime.UtcNow;
|
||||
string messageID = $"<{now:yyyyMMdd}.{now:HHmmssff}@{EMAIL_SERVER}>";
|
||||
message.Headers.Add("Message-ID", messageID);
|
||||
message.From.Add(FROM_ADDRESS);
|
||||
|
||||
private static void SendCallback(object state)
|
||||
{
|
||||
MailMessage message = (MailMessage)state;
|
||||
int delay = SEND_DELAY_SECONDS;
|
||||
|
||||
if (Send(message))
|
||||
Console.WriteLine("Sent e-mail '{0}' to '{1}'.", message.Subject, message.To);
|
||||
else
|
||||
Console.WriteLine("Failure sending e-mail '{0}' to '{1}'.", message.Subject, message.To);
|
||||
for (int i = 0; i < RETRY_SEND_EMAIL_COUNT; i++)
|
||||
try
|
||||
{
|
||||
using SmtpClient client = new SmtpClient();
|
||||
await client.ConnectAsync(EMAIL_SERVER, EMAIL_PORT, true);
|
||||
await client.AuthenticateAsync(EMAIL_SERVER_USERNAME, EMAIL_SERVER_PASSWORD);
|
||||
await client.SendAsync(message);
|
||||
await client.DisconnectAsync(true);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
}
|
||||
|
||||
delay *= delay;
|
||||
|
||||
await Task.Delay(delay * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ namespace Server
|
|||
[JsonPropertyName("dataDirectories")]
|
||||
public List<string> DataDirectories { get; set; } = new List<string>();
|
||||
|
||||
[JsonPropertyName("emailSettings")]
|
||||
public EmailSettings emailSettings { get; set; } = new EmailSettings();
|
||||
|
||||
private static string FilePath => Path.Join(Core.BaseDirectory, "modernuo.json");
|
||||
|
||||
private static void PromptDataDirectories(Configuration config)
|
||||
|
|
@ -73,4 +76,29 @@ namespace Server
|
|||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make configuration pluggable. Move this to scripts
|
||||
public class EmailSettings
|
||||
{
|
||||
[JsonPropertyName("fromAddress")]
|
||||
public string FromAddress { get; set; }
|
||||
[JsonPropertyName("fromName")]
|
||||
public string FromName { get; set; }
|
||||
[JsonPropertyName("crashAddress")]
|
||||
public string crashAddress { get; set; }
|
||||
[JsonPropertyName("crashName")]
|
||||
public string crashName { get; set; }
|
||||
[JsonPropertyName("speechLogPageAddress")]
|
||||
public string speechLogPageAddress { get; set; }
|
||||
[JsonPropertyName("speechLogPageName")]
|
||||
public string speechLogPageName { get; set; }
|
||||
[JsonPropertyName("emailServer")]
|
||||
public string emailServer { get; set; }
|
||||
[JsonPropertyName("emailPort")]
|
||||
public int emailPort { get; set; }
|
||||
[JsonPropertyName("emailUsername")]
|
||||
public string emailUsername { get; set; }
|
||||
[JsonPropertyName("emailPassword")]
|
||||
public string emailPassword { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue