Иконка ресурса

FREE PrivateMessages : 1.3.2 1.3.2

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.IO;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("PrivateMessages", "SeesAll", "1.3.2")]
    [Description("Allows users to send private messages to each other")]
    class PrivateMessages : CovalencePlugin
    {
        private const string AllowPermission = "privatemessages.allow";
        private const int MaxHistoryEntries = 5;
        private const int UiPlayersPerPage = 10;
        private const string AuditLogDirectoryName = "PrivateMessages";
        private const string UiPickerPanel = "PrivateMessages.UI.Picker";
        private const string UiComposePanel = "PrivateMessages.UI.Compose";
        private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);

        private readonly Dictionary<string, string> pmHistory = new Dictionary<string, string>();
        private readonly Dictionary<string, double> cooldowns = new Dictionary<string, double>();
        private readonly Dictionary<string, ConversationHistory> conversationHistory = new Dictionary<string, ConversationHistory>();
        private readonly Dictionary<string, string> selectedPmTargets = new Dictionary<string, string>();

        [PluginReference] private Plugin Ignore, UFilter, BetterChatMute;

        private ConfigData configData;
        private string auditLogDirectoryPath;
        private double nextHistoryPruneAt;

        private class ConversationHistory
        {
            public readonly List<string> Messages = new List<string>();
            public double LastUpdated;
        }

        #region Localization

        private string Lang(string key, string id = null, params object[] args)
        {
            return string.Format(lang.GetMessage(key, this, id), args);
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                {"PMTo", "[#00FFFF]PM to {0}[/#]: {1}"},
                {"PMFrom", "[#00FFFF]PM from {0}[/#]: {1}"},
                {"PlayerNotOnline", "{0} is not online."},
                {"NotOnlineAnymore", "The last person you was talking to is not online anymore."},
                {"NotMessaged", "You haven't messaged anyone or they haven't messaged you."},
                {"IgnoreYou", "[#FF0000]{0} is ignoring you and cant receive your PMs[/#]"},
                {"SelfPM", "You can not send messages to yourself."},
                {"SyntaxR", "Incorrect Syntax use: /r <msg>"},
                {"HistorySyntax", "Incorrect Syntax use: /pmhistory <name>"},
                {"SyntaxPM", "Incorrect Syntax use: /{0} <name> <msg>"},
                {"PmUiUsage", "Open /{0} to choose a player and send a private message. You can also still use /{0} <name> <message>."},
                {"PmUiSelected", "Selected [#00FFFF]{0}[/#] for private messages. Now type [#00FFFF]/{1} <message>[/#]."},
                {"PmUiSelectedOffline", "Your selected PM target is no longer online. Open /{0} again to pick another player."},
                {"PmUiNoPlayers", "No other online players are available to message."},
                {"PmUiTitle", "Private Message"},
                {"PmUiClose", "Close"},
                {"PmUiBack", "Back"},
                {"PmUiPrev", "< Prev"},
                {"PmUiNext", "Next >"},
                {"PmUiInstructions", "Click a player to open the PM composer"},
                {"PmUiComposeTitle", "Private Message Composer"},
                {"PmUiComposeTo", "To: {0}"},
                {"PmUiComposeHint", "Type your message below and press Enter to send."},
                {"PmUiComposeOpened", "Opened the PM composer for [#00FFFF]{0}[/#]."},
                {"PmUiEmptyMessage", "Type a message before sending."},
                {"NotAllowedToChat", "You are not allowed to chat here"},
                {"History", "Your History:\n{0}"},
                {"CooldownMessage", "You will be able to send a private message in {0} seconds"},
                {"NoHistory", "There is not any saved pm history with this player."},
                {"CannotFindUser", "Cannot find this user"},
                {"MultiplePlayersFound", "Multiple players matched '{0}': {1}. Please be more specific."},
                {"CommandDisabled", "This command has been disabled"},
                {"IsMuted", "You are currently muted & cannot send private messages"},
                {"TargetMuted", "This person is muted & cannot receive your private message"},
                {"NoPermission", "You don't have the correct permissions to run this command"},
                {"HistoryPM", "[#00FFFF]{0}[/#]: {1}"},
                {"Logging", "[PM]{0}->{1}:{2}"}
            }, this);
        }

        #endregion

        #region Hooks

        private void Init()
        {
            LoadVariables();

            AddCovalenceCommand(string.IsNullOrEmpty(configData.PmCommand) ? "pm" : configData.PmCommand, nameof(cmdPm));
            AddCovalenceCommand("r", nameof(cmdPmReply));
            AddCovalenceCommand("pmhistory", nameof(cmdPmHistory));
            permission.RegisterPermission(AllowPermission, this);


            auditLogDirectoryPath = Path.Combine("oxide", "logs", AuditLogDirectoryName);
            Directory.CreateDirectory(auditLogDirectoryPath);
        }

        private void OnUserDisconnected(IPlayer player)
        {
            if (player == null)
            {
                return;
            }

            pmHistory.Remove(player.Id);
            cooldowns.Remove(player.Id);
            selectedPmTargets.Remove(player.Id);
            RemoveStaleReplyTargets(player.Id);
            RemovePlayerConversationHistory(player.Id);
            RemovePlayerSelections(player.Id);
            DestroyPmUi(player);
        }

        private void Unload()
        {
            foreach (var player in players.Connected)
            {
                DestroyPmUi(player);
            }
        }

        private void OnNewSave(string filename)
        {
            if (!configData.ClearAuditLogsOnNewSave)
            {
                return;
            }

            ClearAuditLogs();
        }

        #endregion

        #region Commands

        private void cmdPm(IPlayer player, string command, string[] args)
        {
            if (!HasPmPermission(player))
            {
                return;
            }

            if (args == null || args.Length == 0)
            {
                OpenPmPickerUi(player, 0);
                return;
            }

            string selectedTargetId;
            if (selectedPmTargets.TryGetValue(player.Id, out selectedTargetId))
            {
                string selectedTargetError;
                var selectedTarget = FindConnectedPlayer(selectedTargetId, out selectedTargetError);
                if (selectedTarget == null)
                {
                    selectedPmTargets.Remove(player.Id);
                    player.Reply(Lang("PmUiSelectedOffline", player.Id, configData.PmCommand));
                    OpenPmPickerUi(player, 0);
                    return;
                }

                HandlePm(player, selectedTarget, BuildMessage(args, 0));
                return;
            }

            if (args.Length < 2)
            {
                player.Reply(Lang("SyntaxPM", player.Id, configData.PmCommand));
                player.Reply(Lang("PmUiUsage", player.Id, configData.PmCommand));
                return;
            }

            string lookupError;
            var target = FindConnectedPlayer(args[0], out lookupError);
            if (target == null)
            {
                player.Reply(string.IsNullOrEmpty(lookupError) ? Lang("PlayerNotOnline", player.Id, args[0]) : lookupError);
                return;
            }

            if (target.Id == player.Id)
            {
                player.Reply(Lang("SelfPM", player.Id));
                return;
            }

            HandlePm(player, target, BuildMessage(args, 1));
        }

        private void cmdPmReply(IPlayer player, string command, string[] args)
        {
            if (!HasPmPermission(player))
            {
                return;
            }

            if (args == null || args.Length == 0)
            {
                player.Reply(Lang("SyntaxR", player.Id));
                return;
            }

            string targetId;
            if (!pmHistory.TryGetValue(player.Id, out targetId))
            {
                player.Reply(Lang("NotMessaged", player.Id));
                return;
            }

            string lookupError;
            var target = FindConnectedPlayer(targetId, out lookupError);
            if (target == null)
            {
                pmHistory.Remove(player.Id);
                player.Reply(Lang("NotOnlineAnymore", player.Id));
                return;
            }

            HandlePm(player, target, BuildMessage(args, 0));
        }

        private void cmdPmHistory(IPlayer player, string command, string[] args)
        {
            if (!configData.EnableHistory)
            {
                player.Reply(Lang("CommandDisabled", player.Id));
                return;
            }

            PruneInactiveConversationHistoryIfNeeded();

            if (args == null || args.Length != 1)
            {
                player.Reply(Lang("HistorySyntax", player.Id));
                return;
            }

            var target = covalence.Players.FindPlayer(args[0]);
            if (target == null)
            {
                player.Reply(Lang("CannotFindUser", player.Id));
                return;
            }

            var history = GetConversationHistory(player.Id, target.Id);
            if (history == null || history.Messages.Count == 0)
            {
                player.Reply(Lang("NoHistory", player.Id));
                return;
            }

            player.Reply(Lang("History", player.Id, string.Join(Environment.NewLine, history.Messages)));
        }


        [ConsoleCommand("privatemessages.ui.close")]
        private void ConsolePmUiClose(ConsoleSystem.Arg arg)
        {
            var basePlayer = arg?.Player();
            if (basePlayer == null)
            {
                return;
            }

            DestroyPmUi(basePlayer.IPlayer);
        }

        [ConsoleCommand("privatemessages.ui.page")]
        private void ConsolePmUiPage(ConsoleSystem.Arg arg)
        {
            var basePlayer = arg?.Player();
            if (basePlayer == null)
            {
                return;
            }

            var player = basePlayer.IPlayer;
            if (!HasPmPermission(player))
            {
                DestroyPmUi(player);
                return;
            }

            int page;
            if (!HasConsoleArgs(arg) || !int.TryParse(GetConsoleArg(arg, 0), out page))
            {
                page = 0;
            }

            OpenPmPickerUi(player, page);
        }

        [ConsoleCommand("privatemessages.ui.select")]
        private void ConsolePmUiSelect(ConsoleSystem.Arg arg)
        {
            var basePlayer = arg?.Player();
            if (basePlayer == null)
            {
                return;
            }

            var player = basePlayer.IPlayer;
            if (!HasPmPermission(player))
            {
                DestroyPmUi(player);
                return;
            }

            if (!HasConsoleArgs(arg))
            {
                OpenPmPickerUi(player, 0);
                return;
            }

            var targetArg = GetConsoleArg(arg, 0);
            string lookupError;
            var target = FindConnectedPlayer(targetArg, out lookupError);
            if (target == null)
            {
                player.Reply(string.IsNullOrEmpty(lookupError) ? Lang("PlayerNotOnline", player.Id, targetArg) : lookupError);
                OpenPmPickerUi(player, 0);
                return;
            }

            if (target.Id == player.Id)
            {
                player.Reply(Lang("SelfPM", player.Id));
                OpenPmPickerUi(player, 0);
                return;
            }

            selectedPmTargets[player.Id] = target.Id;
            OpenPmComposerUi(player, target);
            player.Reply(Lang("PmUiComposeOpened", player.Id, target.Name));
        }

        [ConsoleCommand("privatemessages.ui.back")]
        private void ConsolePmUiBack(ConsoleSystem.Arg arg)
        {
            var basePlayer = arg?.Player();
            if (basePlayer == null)
            {
                return;
            }

            var player = basePlayer.IPlayer;
            if (!HasPmPermission(player))
            {
                DestroyPmUi(player);
                return;
            }

            OpenPmPickerUi(player, 0);
        }

        [ConsoleCommand("privatemessages.ui.send")]
        private void ConsolePmUiSend(ConsoleSystem.Arg arg)
        {
            var basePlayer = arg?.Player();
            if (basePlayer == null)
            {
                return;
            }

            var player = basePlayer.IPlayer;
            if (!HasPmPermission(player))
            {
                DestroyPmUi(player);
                return;
            }

            if (!HasConsoleArgs(arg))
            {
                OpenPmPickerUi(player, 0);
                return;
            }

            var targetArg = GetConsoleArg(arg, 0);
            string lookupError;
            var target = FindConnectedPlayer(targetArg, out lookupError);
            if (target == null)
            {
                selectedPmTargets.Remove(player.Id);
                player.Reply(string.IsNullOrEmpty(lookupError) ? Lang("PlayerNotOnline", player.Id, targetArg) : lookupError);
                OpenPmPickerUi(player, 0);
                return;
            }

            var message = BuildConsoleArgMessage(arg, 1);
            if (string.IsNullOrWhiteSpace(message))
            {
                player.Reply(Lang("PmUiEmptyMessage", player.Id));
                OpenPmComposerUi(player, target);
                return;
            }

            DestroyPmUi(player);
            HandlePm(player, target, message);
        }

        #endregion

        #region PM Processing

        private void HandlePm(IPlayer sender, IPlayer target, string rawMessage)
        {
            if (!(bool)(Interface.Oxide.CallHook("CanChat", sender) ?? true))
            {
                sender.Reply(Lang("NotAllowedToChat", sender.Id));
                return;
            }

            if (configData.UseBetterChatMute && BetterChatMute != null && CheckMuteStatus(sender, target))
            {
                return;
            }

            if (IsOnCooldown(sender))
            {
                return;
            }

            if (IsIgnored(sender, target))
            {
                return;
            }

            var message = ProcessMessage(rawMessage);
            if (string.IsNullOrWhiteSpace(message))
            {
                return;
            }

            if (Interface.Oxide.CallHook("OnPMProcessed", sender, target, message) != null)
            {
                return;
            }

            AddPmHistory(sender.Id, target.Id);
            selectedPmTargets[sender.Id] = target.Id;

            sender.Reply(Lang("PMTo", sender.Id, target.Name, message));
            target.Reply(Lang("PMFrom", target.Id, sender.Name, message));

            AddHistoryAndLogging(sender, target, message);
        }

        private bool HasPmPermission(IPlayer player)
        {
            if (!configData.UsePermission)
            {
                return true;
            }

            if (player.HasPermission(AllowPermission))
            {
                return true;
            }

            player.Reply(Lang("NoPermission", player.Id));
            return false;
        }

        private void AddHistoryAndLogging(IPlayer initiator, IPlayer target, string message)
        {
            PruneInactiveConversationHistoryIfNeeded();

            if (configData.EnableHistory)
            {
                AddToHistory(initiator.Id, target.Id, Lang("HistoryPM", null, initiator.Name, message));
            }

            if (configData.EnableConsoleLogging)
            {
                Puts(Lang("Logging", null, initiator.Name, target.Name, message));
            }

            if (configData.EnableFileAuditLogging)
            {
                WriteAuditLog(initiator, target, message);
            }
        }

        private void AddPmHistory(string initiatorId, string targetId)
        {
            pmHistory[initiatorId] = targetId;
            pmHistory[targetId] = initiatorId;
        }

        private bool CheckMuteStatus(IPlayer player, IPlayer target)
        {
            var playerMuted = BetterChatMute.CallHook("API_IsMuted", player);
            if (playerMuted is bool && (bool)playerMuted)
            {
                player.Reply(Lang("IsMuted", player.Id));
                return true;
            }

            var targetMuted = BetterChatMute.CallHook("API_IsMuted", target);
            if (targetMuted is bool && (bool)targetMuted)
            {
                player.Reply(Lang("TargetMuted", player.Id));
                return true;
            }

            return false;
        }

        private bool IsIgnored(IPlayer sender, IPlayer target)
        {
            if (!configData.UseIgnore)
            {
                return false;
            }

            var ignored = Ignore?.CallHook("HasIgnored", target.Id, sender.Id);
            if (!(ignored is bool) || !(bool)ignored)
            {
                return false;
            }

            sender.Reply(Lang("IgnoreYou", sender.Id, target.Name));
            return true;
        }

        private bool IsOnCooldown(IPlayer player)
        {
            if (!configData.UseCooldown)
            {
                return false;
            }

            var now = GetTimeStamp();
            double expiresAt;
            if (cooldowns.TryGetValue(player.Id, out expiresAt))
            {
                if (expiresAt > now)
                {
                    player.Reply(Lang("CooldownMessage", player.Id, Math.Round(expiresAt - now, 2)));
                    return true;
                }

                cooldowns.Remove(player.Id);
            }

            cooldowns[player.Id] = now + configData.CooldownTime;
            return false;
        }

        private string ProcessMessage(string message)
        {
            if (configData.UseUFilter && UFilter != null)
            {
                var filtered = UFilter.Call("ProcessText", message);
                if (filtered != null)
                {
                    message = filtered.ToString();
                }
            }

            return RemoveRichText(message);
        }

        private static string BuildMessage(string[] args, int startIndex)
        {
            if (args == null || args.Length <= startIndex)
            {
                return string.Empty;
            }

            return string.Join(" ", args, startIndex, args.Length - startIndex);
        }

        private static bool HasConsoleArgs(ConsoleSystem.Arg arg)
        {
            return arg?.Args != null && arg.Args.Length > 0;
        }

        private static string GetConsoleArg(ConsoleSystem.Arg arg, int index)
        {
            if (arg?.Args == null || index < 0 || index >= arg.Args.Length)
            {
                return string.Empty;
            }

            return arg.GetString(index) ?? string.Empty;
        }

        private static string BuildConsoleArgMessage(ConsoleSystem.Arg arg, int startIndex)
        {
            if (arg?.Args == null || arg.Args.Length <= startIndex)
            {
                return string.Empty;
            }

            var parts = new string[arg.Args.Length - startIndex];
            for (var i = startIndex; i < arg.Args.Length; i++)
            {
                parts[i - startIndex] = arg.GetString(i) ?? string.Empty;
            }

            return string.Join(" ", parts);
        }

        #endregion

        #region Player Lookup

        private IPlayer FindConnectedPlayer(string nameOrIdOrIp, out string errorMessage)
        {
            errorMessage = null;

            if (string.IsNullOrEmpty(nameOrIdOrIp))
            {
                return null;
            }

            IPlayer partialMatch = null;
            var multipleMatches = new List<string>();
            var search = nameOrIdOrIp.Trim();

            foreach (var activePlayer in covalence.Players.Connected)
            {
                if (activePlayer == null)
                {
                    continue;
                }

                if (string.Equals(activePlayer.Id, search, StringComparison.Ordinal) ||
                    string.Equals(activePlayer.Address, search, StringComparison.Ordinal) ||
                    string.Equals(activePlayer.Name, search, StringComparison.OrdinalIgnoreCase))
                {
                    return activePlayer;
                }

                if (string.IsNullOrEmpty(activePlayer.Name) ||
                    activePlayer.Name.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0)
                {
                    continue;
                }

                if (partialMatch == null)
                {
                    partialMatch = activePlayer;
                    multipleMatches.Add(activePlayer.Name);
                    continue;
                }

                multipleMatches.Add(activePlayer.Name);
            }

            if (multipleMatches.Count > 1)
            {
                errorMessage = Lang("MultiplePlayersFound", null, search, string.Join(", ", multipleMatches));
                return null;
            }

            return partialMatch;
        }

        private List<IPlayer> GetPmPickerCandidates(IPlayer requester)
        {
            var availablePlayers = new List<IPlayer>();

            foreach (var activePlayer in covalence.Players.Connected)
            {
                if (activePlayer == null || activePlayer.Id == requester.Id || string.IsNullOrEmpty(activePlayer.Name))
                {
                    continue;
                }

                availablePlayers.Add(activePlayer);
            }

            availablePlayers.Sort((left, right) => string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
            return availablePlayers;
        }

        #endregion


        #region UI

        private void OpenPmPickerUi(IPlayer player, int page)
        {
            var basePlayer = player.Object as BasePlayer;
            if (basePlayer == null)
            {
                player.Reply(Lang("PmUiUsage", player.Id, configData.PmCommand));
                return;
            }

            var availablePlayers = GetPmPickerCandidates(player);
            if (availablePlayers.Count == 0)
            {
                DestroyPmUi(player);
                player.Reply(Lang("PmUiNoPlayers", player.Id));
                return;
            }

            var maxPage = Math.Max(0, (availablePlayers.Count - 1) / UiPlayersPerPage);
            if (page < 0)
            {
                page = 0;
            }
            else if (page > maxPage)
            {
                page = maxPage;
            }

            DestroyPmUi(player);

            var elements = new CuiElementContainer();
            elements.Add(new CuiPanel
            {
                Image = { Color = "0.10 0.10 0.10 0.96" },
                RectTransform = { AnchorMin = "0.34 0.24", AnchorMax = "0.66 0.76" },
                CursorEnabled = true
            }, "Overlay", UiPickerPanel);

            elements.Add(new CuiLabel
            {
                Text =
                {
                    Text = Lang("PmUiTitle", player.Id),
                    FontSize = 18,
                    Align = TextAnchor.MiddleCenter,
                    Color = "1 1 1 1"
                },
                RectTransform = { AnchorMin = "0.05 0.90", AnchorMax = "0.95 0.98" }
            }, UiPickerPanel);

            elements.Add(new CuiLabel
            {
                Text =
                {
                    Text = Lang("PmUiInstructions", player.Id, configData.PmCommand),
                    FontSize = 12,
                    Align = TextAnchor.MiddleCenter,
                    Color = "0.85 0.85 0.85 1"
                },
                RectTransform = { AnchorMin = "0.05 0.83", AnchorMax = "0.95 0.90" }
            }, UiPickerPanel);

            elements.Add(new CuiButton
            {
                Button = { Color = "0.60 0.20 0.20 1.0", Command = "privatemessages.ui.close" },
                RectTransform = { AnchorMin = "0.74 0.05", AnchorMax = "0.95 0.12" },
                Text = { Text = Lang("PmUiClose", player.Id), FontSize = 13, Align = TextAnchor.MiddleCenter }
            }, UiPickerPanel);

            if (page > 0)
            {
                elements.Add(new CuiButton
                {
                    Button = { Color = "0.20 0.35 0.50 1.0", Command = string.Format("privatemessages.ui.page {0}", page - 1) },
                    RectTransform = { AnchorMin = "0.05 0.05", AnchorMax = "0.22 0.12" },
                    Text = { Text = Lang("PmUiPrev", player.Id), FontSize = 13, Align = TextAnchor.MiddleCenter }
                }, UiPickerPanel);
            }

            if (page < maxPage)
            {
                elements.Add(new CuiButton
                {
                    Button = { Color = "0.20 0.35 0.50 1.0", Command = string.Format("privatemessages.ui.page {0}", page + 1) },
                    RectTransform = { AnchorMin = "0.24 0.05", AnchorMax = "0.41 0.12" },
                    Text = { Text = Lang("PmUiNext", player.Id), FontSize = 13, Align = TextAnchor.MiddleCenter }
                }, UiPickerPanel);
            }

            elements.Add(new CuiLabel
            {
                Text =
                {
                    Text = string.Format("{0}/{1}", page + 1, maxPage + 1),
                    FontSize = 12,
                    Align = TextAnchor.MiddleCenter,
                    Color = "0.85 0.85 0.85 1"
                },
                RectTransform = { AnchorMin = "0.44 0.05", AnchorMax = "0.56 0.12" }
            }, UiPickerPanel);

            var startIndex = page * UiPlayersPerPage;
            var endIndex = Math.Min(startIndex + UiPlayersPerPage, availablePlayers.Count);
            var rowHeight = 0.068f;
            var top = 0.78f;

            for (var index = startIndex; index < endIndex; index++)
            {
                var row = index - startIndex;
                var rowTop = top - (row * rowHeight);
                var rowBottom = rowTop - 0.052f;
                var target = availablePlayers[index];
                var isSelected = selectedPmTargets.ContainsKey(player.Id) && selectedPmTargets[player.Id] == target.Id;
                var buttonColor = isSelected ? "0.18 0.45 0.18 1.0" : "0.18 0.18 0.18 1.0";

                elements.Add(new CuiButton
                {
                    Button = { Color = buttonColor, Command = string.Format("privatemessages.ui.select {0}", target.Id) },
                    RectTransform = { AnchorMin = string.Format("0.07 {0:0.000}", rowBottom), AnchorMax = string.Format("0.93 {0:0.000}", rowTop) },
                    Text = { Text = SanitizeUiText(target.Name), FontSize = 13, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1" }
                }, UiPickerPanel);
            }

            CuiHelper.AddUi(basePlayer, elements);
        }

        private void OpenPmComposerUi(IPlayer player, IPlayer target)
        {
            var basePlayer = player.Object as BasePlayer;
            if (basePlayer == null || target == null)
            {
                OpenPmPickerUi(player, 0);
                return;
            }

            DestroyPmUi(player);

            var elements = new CuiElementContainer();
            elements.Add(new CuiPanel
            {
                Image = { Color = "0.10 0.10 0.10 0.96" },
                RectTransform = { AnchorMin = "0.30 0.30", AnchorMax = "0.70 0.68" },
                CursorEnabled = true
            }, "Overlay", UiComposePanel);

            elements.Add(new CuiElement
            {
                Parent = UiComposePanel,
                Components =
                {
                    new CuiNeedsCursorComponent(),
                    new CuiNeedsKeyboardComponent()
                }
            });

            elements.Add(new CuiLabel
            {
                Text =
                {
                    Text = Lang("PmUiComposeTitle", player.Id),
                    FontSize = 18,
                    Align = TextAnchor.MiddleCenter,
                    Color = "1 1 1 1"
                },
                RectTransform = { AnchorMin = "0.05 0.84", AnchorMax = "0.95 0.95" }
            }, UiComposePanel);

            elements.Add(new CuiLabel
            {
                Text =
                {
                    Text = Lang("PmUiComposeTo", player.Id, SanitizeUiText(target.Name)),
                    FontSize = 14,
                    Align = TextAnchor.MiddleCenter,
                    Color = "0.90 0.90 0.90 1"
                },
                RectTransform = { AnchorMin = "0.05 0.72", AnchorMax = "0.95 0.82" }
            }, UiComposePanel);

            elements.Add(new CuiLabel
            {
                Text =
                {
                    Text = Lang("PmUiComposeHint", player.Id),
                    FontSize = 12,
                    Align = TextAnchor.MiddleCenter,
                    Color = "0.85 0.85 0.85 1"
                },
                RectTransform = { AnchorMin = "0.05 0.60", AnchorMax = "0.95 0.70" }
            }, UiComposePanel);

            elements.Add(new CuiPanel
            {
                Image = { Color = "0.16 0.16 0.16 1.0" },
                RectTransform = { AnchorMin = "0.07 0.35", AnchorMax = "0.93 0.54" }
            }, UiComposePanel, UiComposePanel + ".InputBg");

            elements.Add(new CuiElement
            {
                Name = UiComposePanel + ".Input",
                Parent = UiComposePanel + ".InputBg",
                Components =
                {
                    new CuiInputFieldComponent
                    {
                        Align = TextAnchor.MiddleLeft,
                        CharsLimit = 180,
                        Command = string.Format("privatemessages.ui.send {0}", target.Id),
                        FontSize = 14,
                        IsPassword = false,
                        Text = string.Empty,
                        Color = "1 1 1 1"
                    },
                    new CuiRectTransformComponent { AnchorMin = "0.02 0.10", AnchorMax = "0.98 0.90" }
                }
            });

            elements.Add(new CuiButton
            {
                Button = { Color = "0.25 0.35 0.50 1.0", Command = "privatemessages.ui.back" },
                RectTransform = { AnchorMin = "0.07 0.10", AnchorMax = "0.26 0.20" },
                Text = { Text = Lang("PmUiBack", player.Id), FontSize = 13, Align = TextAnchor.MiddleCenter }
            }, UiComposePanel);

            elements.Add(new CuiButton
            {
                Button = { Color = "0.60 0.20 0.20 1.0", Command = "privatemessages.ui.close" },
                RectTransform = { AnchorMin = "0.74 0.10", AnchorMax = "0.93 0.20" },
                Text = { Text = Lang("PmUiClose", player.Id), FontSize = 13, Align = TextAnchor.MiddleCenter }
            }, UiComposePanel);

            CuiHelper.AddUi(basePlayer, elements);
        }

        private void DestroyPmUi(IPlayer player)
        {
            var basePlayer = player?.Object as BasePlayer;
            if (basePlayer == null)
            {
                return;
            }

            CuiHelper.DestroyUi(basePlayer, UiPickerPanel);
            CuiHelper.DestroyUi(basePlayer, UiComposePanel);
        }

        private static string SanitizeUiText(string text)
        {
            return string.IsNullOrEmpty(text) ? string.Empty : text.Replace("\n", " ").Replace("\r", " ");
        }

        #endregion

        #region History

        private void AddToHistory(string senderId, string targetId, string message)
        {
            var history = GetOrCreateConversationHistory(senderId, targetId);
            history.LastUpdated = GetTimeStamp();
            history.Messages.Add(message);

            if (history.Messages.Count > MaxHistoryEntries)
            {
                history.Messages.RemoveAt(0);
            }
        }

        private ConversationHistory GetConversationHistory(string senderId, string targetId)
        {
            ConversationHistory history;
            return conversationHistory.TryGetValue(GetConversationKey(senderId, targetId), out history) ? history : null;
        }

        private ConversationHistory GetOrCreateConversationHistory(string senderId, string targetId)
        {
            var key = GetConversationKey(senderId, targetId);

            ConversationHistory history;
            if (!conversationHistory.TryGetValue(key, out history))
            {
                history = new ConversationHistory
                {
                    LastUpdated = GetTimeStamp()
                };
                conversationHistory[key] = history;
            }

            return history;
        }

        private void PruneInactiveConversationHistoryIfNeeded()
        {
            if (!configData.EnableHistory || configData.HistoryPruneMinutes <= 0 || conversationHistory.Count == 0)
            {
                return;
            }

            var now = GetTimeStamp();
            if (now < nextHistoryPruneAt)
            {
                return;
            }

            nextHistoryPruneAt = now + Math.Min(configData.HistoryPruneMinutes * 60.0, 300.0);
            PruneInactiveConversationHistory(now);
        }

        private void PruneInactiveConversationHistory(double now)
        {
            var expirationAge = configData.HistoryPruneMinutes * 60.0;
            var keysToRemove = new List<string>();

            foreach (var entry in conversationHistory)
            {
                if (now - entry.Value.LastUpdated >= expirationAge)
                {
                    keysToRemove.Add(entry.Key);
                }
            }

            foreach (var key in keysToRemove)
            {
                conversationHistory.Remove(key);
            }
        }

        private void RemovePlayerConversationHistory(string playerId)
        {
            if (conversationHistory.Count == 0 || string.IsNullOrEmpty(playerId))
            {
                return;
            }

            var keysToRemove = new List<string>();
            foreach (var entry in conversationHistory)
            {
                if (ConversationKeyContainsPlayer(entry.Key, playerId))
                {
                    keysToRemove.Add(entry.Key);
                }
            }

            foreach (var key in keysToRemove)
            {
                conversationHistory.Remove(key);
            }
        }

        private static bool ConversationKeyContainsPlayer(string conversationKey, string playerId)
        {
            if (string.IsNullOrEmpty(conversationKey) || string.IsNullOrEmpty(playerId))
            {
                return false;
            }

            var separatorIndex = conversationKey.IndexOf('|');
            if (separatorIndex < 0)
            {
                return false;
            }

            return string.CompareOrdinal(conversationKey, 0, playerId, 0, playerId.Length) == 0 &&
                   (conversationKey.Length == playerId.Length || separatorIndex == playerId.Length)
                   ||
                   separatorIndex + 1 + playerId.Length == conversationKey.Length &&
                   string.CompareOrdinal(conversationKey, separatorIndex + 1, playerId, 0, playerId.Length) == 0;
        }

        private static string GetConversationKey(string playerA, string playerB)
        {
            return string.CompareOrdinal(playerA, playerB) < 0
                ? playerA + "|" + playerB
                : playerB + "|" + playerA;
        }

        #endregion

        #region Audit Logging

        private void WriteAuditLog(IPlayer initiator, IPlayer target, string message)
        {
            try
            {
                var timestamp = DateTime.Now;
                var filePath = Path.Combine(auditLogDirectoryPath, string.Format("privatemessages_{0:yyyy-MM-dd}.log", timestamp));
                var line = string.Format(
                    "[{0:yyyy-MM-dd HH:mm:ss}] {1} ({2}) -> {3} ({4}): {5}",
                    timestamp,
                    SanitizeLogValue(initiator?.Name),
                    SanitizeLogValue(initiator?.Id),
                    SanitizeLogValue(target?.Name),
                    SanitizeLogValue(target?.Id),
                    SanitizeLogValue(message));

                File.AppendAllText(filePath, line + Environment.NewLine);
            }
            catch (Exception ex)
            {
                PrintWarning("Failed to write PM audit log: {0}", ex.Message);
            }
        }

        private void ClearAuditLogs()
        {
            try
            {
                if (!Directory.Exists(auditLogDirectoryPath))
                {
                    return;
                }

                foreach (var filePath in Directory.GetFiles(auditLogDirectoryPath, "*.log", SearchOption.TopDirectoryOnly))
                {
                    File.Delete(filePath);
                }
            }
            catch (Exception ex)
            {
                PrintWarning("Failed to clear PM audit logs: {0}", ex.Message);
            }
        }

        private void RemoveStaleReplyTargets(string playerId)
        {
            var staleReplyTargets = new List<string>();
            foreach (var entry in pmHistory)
            {
                if (entry.Value == playerId)
                {
                    staleReplyTargets.Add(entry.Key);
                }
            }

            foreach (var stalePlayerId in staleReplyTargets)
            {
                pmHistory.Remove(stalePlayerId);
            }
        }

        private void RemovePlayerSelections(string playerId)
        {
            var staleSelections = new List<string>();
            foreach (var entry in selectedPmTargets)
            {
                if (entry.Value == playerId)
                {
                    staleSelections.Add(entry.Key);
                }
            }

            foreach (var stalePlayerId in staleSelections)
            {
                selectedPmTargets.Remove(stalePlayerId);
            }
        }

        private static string SanitizeLogValue(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return string.Empty;
            }

            return value.Replace("\r", " ").Replace("\n", " ").Trim();
        }

        #endregion

        #region Utilities

        private static string RemoveRichText(string message)
        {
            if (string.IsNullOrEmpty(message))
            {
                return string.Empty;
            }

            var output = new char[message.Length];
            var outputIndex = 0;
            var insideTag = false;

            for (var i = 0; i < message.Length; i++)
            {
                var character = message[i];
                if (character == '<')
                {
                    insideTag = true;
                    continue;
                }

                if (character == '>')
                {
                    insideTag = false;
                    continue;
                }

                if (!insideTag)
                {
                    output[outputIndex++] = character;
                }
            }

            return new string(output, 0, outputIndex);
        }

        private static double GetTimeStamp()
        {
            return (DateTime.UtcNow - UnixEpoch).TotalSeconds;
        }

        #endregion

        #region Configuration

        private class ConfigData
        {
            public bool UseUFilter = false;
            public bool UseIgnore = false;
            public bool UseCooldown = false;
            public bool UseBetterChatMute = false;
            public bool EnableConsoleLogging = false;
            public bool EnableFileAuditLogging = true;
            public bool ClearAuditLogsOnNewSave = false;
            public bool EnableHistory = false;
            public bool UsePermission = false;
            public int CooldownTime = 3;
            public int HistoryPruneMinutes = 60;
            public string PmCommand = "pm";
        }

        private void LoadVariables()
        {
            var configChanged = false;
            configData = new ConfigData
            {
                UseUFilter = GetConfigValue(nameof(ConfigData.UseUFilter), false, ref configChanged),
                UseIgnore = GetConfigValue(nameof(ConfigData.UseIgnore), false, ref configChanged),
                UseCooldown = GetConfigValue(nameof(ConfigData.UseCooldown), false, ref configChanged),
                UseBetterChatMute = GetConfigValue(nameof(ConfigData.UseBetterChatMute), false, ref configChanged),
                EnableConsoleLogging = GetConfigValue(nameof(ConfigData.EnableConsoleLogging), false, ref configChanged),
                EnableFileAuditLogging = GetConfigValue(nameof(ConfigData.EnableFileAuditLogging), true, ref configChanged),
                ClearAuditLogsOnNewSave = GetConfigValue(nameof(ConfigData.ClearAuditLogsOnNewSave), false, ref configChanged),
                EnableHistory = GetConfigValue(nameof(ConfigData.EnableHistory), false, ref configChanged),
                UsePermission = GetConfigValue(nameof(ConfigData.UsePermission), false, ref configChanged),
                CooldownTime = GetConfigValue(nameof(ConfigData.CooldownTime), 3, ref configChanged),
                HistoryPruneMinutes = GetConfigValue(nameof(ConfigData.HistoryPruneMinutes), 60, ref configChanged),
                PmCommand = GetConfigValue(nameof(ConfigData.PmCommand), "pm", ref configChanged)
            };

            if (configData.CooldownTime < 0)
            {
                configData.CooldownTime = 0;
                configChanged = true;
            }

            if (configData.HistoryPruneMinutes < 0)
            {
                configData.HistoryPruneMinutes = 0;
                configChanged = true;
            }

            if (string.IsNullOrEmpty(configData.PmCommand))
            {
                configData.PmCommand = "pm";
                configChanged = true;
            }

            if (configChanged)
            {
                SaveConfig(configData);
            }
        }

        protected override void LoadDefaultConfig()
        {
            SaveConfig(new ConfigData());
        }

        private T GetConfigValue<T>(string key, T defaultValue, ref bool configChanged)
        {
            if (Config[key] == null)
            {
                Config[key] = defaultValue;
                configChanged = true;
                return defaultValue;
            }

            return (T)Convert.ChangeType(Config[key], typeof(T));
        }

        private void SaveConfig(ConfigData config)
        {
            Config.WriteObject(config, true);
        }

        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху