#region References using Server.Accounting; using Server.Commands; using Server.Gumps; using Server.Misc; using Server.Mobiles; using Server.Network; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Mail; #endregion namespace Server.Engines.Help { public enum PageType { Bug, Stuck, Account, Question, Suggestion, Other, VerbalHarassment, PhysicalHarassment } public class PageEntry { // What page types should have a speech log as attachment? public static readonly PageType[] SpeechLogAttachment = new[] { PageType.VerbalHarassment }; private readonly Mobile m_Sender; private Mobile m_Handler; private DateTime m_Sent; private string m_Message; private PageType m_Type; private Point3D m_PageLocation; private Map m_PageMap; private readonly List m_SpeechLog; public static readonly string SupportEmail = Config.Get("General.SupportEmail", default(string)); public static readonly string SupportWebsite = Config.Get("General.SupportWebsite", default(string)); public Mobile Sender => m_Sender; public Mobile Handler { get { return m_Handler; } set { PageQueue.OnHandlerChanged(m_Handler, value, this); m_Handler = value; } } public DateTime Sent { get { return m_Sent; } set { m_Sent = value; } } public string Message { get { return m_Message; } set { m_Message = value; } } public PageType Type { get { return m_Type; } set { m_Type = value; } } public Point3D PageLocation { get { return m_PageLocation; } set { m_PageLocation = value; } } public Map PageMap { get { return m_PageMap; } set { m_PageMap = value; } } public List SpeechLog => m_SpeechLog; private Timer m_Timer; public void Stop() { if (m_Timer != null) { m_Timer.Stop(); } m_Timer = null; } public PageEntry(Mobile sender, string message, PageType type) { m_Sender = sender; m_Sent = DateTime.UtcNow; m_Message = Utility.FixHtml(message); m_Type = type; m_PageLocation = sender.Location; m_PageMap = sender.Map; PlayerMobile pm = sender as PlayerMobile; if (pm != null && pm.SpeechLog != null && Array.IndexOf(SpeechLogAttachment, type) >= 0) { m_SpeechLog = new List(pm.SpeechLog); } m_Timer = new InternalTimer(this); m_Timer.Start(); } private class InternalTimer : Timer { private static readonly TimeSpan StatusDelay = TimeSpan.FromMinutes(2.0); private readonly PageEntry m_Entry; public InternalTimer(PageEntry entry) : base(TimeSpan.FromSeconds(1.0), StatusDelay) { m_Entry = entry; } protected override void OnTick() { int index = PageQueue.IndexOf(m_Entry); if (m_Entry.Sender.NetState != null && index != -1) { m_Entry.Sender.SendLocalizedMessage(1008077, true, (index + 1).ToString()); // Thank you for paging. Queue status : if (SupportEmail == null || SupportWebsite == null) { m_Entry.Sender.SendLocalizedMessage(1008084); // You can reference our website at www.uo.com or contact us at support@uo.com. To cancel your page, please select the help button again and select cancel. } else { m_Entry.Sender.SendMessage("You can reference our website at " + SupportWebsite + " or contact us at " + SupportEmail + ". To cancel your page, please select the help button again and select cancel."); } if (m_Entry.Handler != null && m_Entry.Handler.NetState == null) { m_Entry.Handler = null; } } else { PageQueue.Remove(m_Entry); } } } } public class ResponseEntry { public static readonly TimeSpan ExpirationPeriod = TimeSpan.FromDays(7); public static List Entries { get; set; } public static void Configure() { Entries = new List(); EventSink.Login += Login; EventSink.BeforeWorldSave += BeforeSave; } public static void Login(LoginEventArgs args) { Mobile m = args.Mobile; Timer.DelayCall(TimeSpan.FromSeconds(2.0), () => { List entries = Entries.Where(e => e.Sender == m).ToList(); foreach (ResponseEntry entry in entries) { entry.SendGump(); } ColUtility.Free(entries); }); } public static void BeforeSave(BeforeWorldSaveEventArgs args) { List list = Entries.Where(e => e.Expired).ToList(); foreach (ResponseEntry entry in list) { Entries.Remove(entry); } } public static void AddEntry(ResponseEntry entry) { if (!Entries.Contains(entry)) { Entries.Add(entry); } } public Mobile Sender { get; set; } public Mobile Handler { get; set; } public string Message { get; set; } public DateTime Expires { get; set; } public bool Expired => Expires < DateTime.UtcNow; public ResponseEntry(Mobile sender, Mobile handler, string message) { Sender = sender; Handler = handler; Message = message; Expires = DateTime.UtcNow + ExpirationPeriod; AddEntry(this); } public void SendGump() { if (Sender.NetState != null) { Sender.SendGump(new MessageSentGump(Sender, Handler != null ? Handler.Name : "Staff", Message)); Entries.Remove(this); } } public ResponseEntry(GenericReader reader) { int version = reader.ReadInt(); Sender = reader.ReadMobile(); Handler = reader.ReadMobile(); Message = reader.ReadString(); Expires = reader.ReadDateTime(); if (Sender != null && !Expired) { AddEntry(this); } } public void Serialize(GenericWriter writer) { writer.Write(0); writer.Write(Sender); writer.Write(Handler); writer.Write(Message); writer.Write(Expires); } } public class PageQueue { private static readonly ArrayList m_List = new ArrayList(); private static readonly Hashtable m_KeyedByHandler = new Hashtable(); private static readonly Hashtable m_KeyedBySender = new Hashtable(); public static readonly bool ShowStaffOffline = Config.Get("General.ShowStaffOffline", true); public static void Initialize() { CommandSystem.Register("Pages", AccessLevel.Counselor, Pages_OnCommand); } public static bool CheckAllowedToPage(Mobile from) { PlayerMobile pm = from as PlayerMobile; if (pm == null) { return true; } if (pm.DesignContext != null) { from.SendLocalizedMessage(500182); // You cannot request help while customizing a house or transferring a character. return false; } else if (pm.PagingSquelched) { from.SendMessage("You cannot request help, sorry."); return false; } return true; } public static string GetPageTypeName(PageType type) { if (type == PageType.VerbalHarassment) { return "Verbal Harassment"; } else if (type == PageType.PhysicalHarassment) { return "Physical Harassment"; } else { return type.ToString(); } } public static void OnHandlerChanged(Mobile old, Mobile value, PageEntry entry) { if (old != null) { m_KeyedByHandler.Remove(old); } if (value != null) { m_KeyedByHandler[value] = entry; } } [Usage("Pages")] [Description("Opens the page queue menu.")] private static void Pages_OnCommand(CommandEventArgs e) { PageEntry entry = (PageEntry)m_KeyedByHandler[e.Mobile]; if (entry != null) { e.Mobile.SendGump(new PageEntryGump(e.Mobile, entry)); } else if (m_List.Count > 0) { e.Mobile.SendGump(new PageQueueGump(e.Mobile)); } else { e.Mobile.SendMessage("The page queue is empty."); } } #region Page In Queue Gump public static void Pages_OnCalled(Mobile from) { PageEntry entry = (PageEntry)m_KeyedByHandler[from]; if (entry != null) { from.SendGump(new PageEntryGump(from, entry)); } else if (m_List.Count > 0) { from.SendGump(new PageQueueGump(from)); } else { from.SendMessage("The page queue is empty."); } } #endregion public static bool IsHandling(Mobile check) { return m_KeyedByHandler.ContainsKey(check); } public static bool Contains(Mobile sender) { return m_KeyedBySender.ContainsKey(sender); } public static int IndexOf(PageEntry e) { return m_List.IndexOf(e); } public static void Cancel(Mobile sender) { Remove((PageEntry)m_KeyedBySender[sender]); } public static void Remove(PageEntry e) { if (e == null) { return; } e.Stop(); m_List.Remove(e); m_KeyedBySender.Remove(e.Sender); if (e.Handler != null) { m_KeyedByHandler.Remove(e.Handler); } } public static PageEntry GetEntry(Mobile sender) { return (PageEntry)m_KeyedBySender[sender]; } public static void Remove(Mobile sender) { Remove(GetEntry(sender)); } public static ArrayList List => m_List; public static void Enqueue(PageEntry entry) { m_List.Add(entry); m_KeyedBySender[entry.Sender] = entry; bool isStaffOnline = false; foreach (NetState ns in NetState.Instances) { Mobile m = ns.Mobile; #region Page In Queue Gump if (m != null && m.IsStaff() && m.AutoPageNotify && !IsHandling(m)) { m.CloseGump(typeof(PageInQueueGump)); m.SendGump(new PageInQueueGump(m)); //m.SendMessage( "A new page has been placed in the queue." ); } #endregion if (m != null && m.IsStaff() && m.AutoPageNotify && m.LastMoveTime - Core.TickCount < 600000) { isStaffOnline = true; break; } } if (!isStaffOnline && ShowStaffOffline) { 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); } } private static void SendEmail(PageEntry entry) { Mobile sender = entry.Sender; DateTime time = DateTime.UtcNow; MailMessage mail = new MailMessage(Email.FromAddress, Email.SpeechLogPageAddresses) { Subject = "ServUO Speech Log Page Forwarding" }; using (StringWriter writer = new StringWriter()) { writer.WriteLine("ServUO 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); } } public class PageInQueueGump : Gump { public PageInQueueGump(Mobile owner) : base(180, 50) { Closable = false; Disposable = true; Dragable = true; Resizable = false; AddPage(0); AddBackground(0, 0, 241, 93, 2620); AddHtml(7, 10, 227, 25, @"
There Is A Page In Queue", true, false); AddHtml(7, 58, 227, 25, @"
" + DateTime.UtcNow, true, false); AddButton(90, 35, 247, 248, 1, GumpButtonType.Reply, 0); AddImage(18, 39, 57); AddImage(193, 39, 59); } public override void OnResponse(NetState state, RelayInfo info) { Mobile from = state.Mobile; switch (info.ButtonID) { case 0: break; case 1: from.SendGump(new PageQueueGump(from)); break; } } } }