C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using ConVar;
using Newtonsoft.Json;
using System.Globalization;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using UnityEngine.UI;
using Oxide.Core.Libraries;
using Oxide.Plugins.AutomatedMessagesMethods;
/*2.1.0
* Added "Send as game tip" option for replies + ability to preview game tip in /am.edit
* Added "Send in chat" option (true by default)
* Fix for inaccurate grid {hacklocation}
* Added support for Toxic Village when using {hacklocation}
*/
namespace Oxide.Plugins {
[Info("AutomatedMessages", "beee", "2.1.0")]
[Description("Automated chat messages based on triggers or repeating interval.")]
class AutomatedMessages : RustPlugin {
#region Fields
private static AutomatedMessages _plugin;
private PluginConfig _config;
private List<Timer> _timers;
public const string PREFIX_SHORT = "am";
public const string PREFIX_LONG = "automatedmessages";
public const string PERM_ADMIN = $"{PREFIX_LONG}.admin";
private Dictionary<Regex, PluginConfig.ConfigAction> _autoReplyPatterns;
[PluginReference]
private Plugin PlaceholderAPI;
#endregion
#region Load/Unload Hooks
private void Init() {
UnsubscribeHooks();
}
void OnServerInitialized() {
pData = PlayersData.Load();
ProcessConfig();
RegisterCommand(_config.ToggleCommand, nameof(TipsToggleChatCMD));
RegisterPermissions();
InitTimers();
InitAutoReply();
SubscribeHooks();
RegisterConfigCommands();
SetUnsetCountries();
RegisterCommand($"{PREFIX_SHORT}.ui.texteditor", nameof(LongInputCMD));
}
void Loaded() {
_plugin = this;
_timers = new();
}
void Unload() {
DestroyTimers();
SaveData();
foreach (var player in BasePlayer.activePlayerList) {
CuiHelper.DestroyUi(player, uimodal);
CuiHelper.DestroyUi(player, chatpreview);
CuiHelper.DestroyUi(player, gametippreview);
}
_plugin = null;
_timers = null;
}
#endregion
#region Functions
void RegisterPermissions() {
permission.RegisterPermission(PERM_ADMIN, this);
}
private void InitTimers() {
DestroyTimers();
_timers = new();
int count = 0;
foreach (var message in _config.Actions.FindAll(s => s.IsEnabled() && s.Type == "Timed")) {
Lang_Action defaultLangAction = message.CachedLangActions[_config.DefaultLang];
if (defaultLangAction.Replies == null || defaultLangAction.Replies.Count == 0) continue;
if (message.Interval <= 0) continue;
Timer timer = _plugin.timer.Repeat(message.Interval * 60, 0, () => {
ProcessReply(message);
}
);
_timers.Add(timer);
count++;
}
PrintWarning($"Started {count} chat timer{((count == 1) ? "" : "s")}.");
}
private void InitAutoReply() {
_autoReplyPatterns = new();
int count = 0;
foreach (var message in _config.Actions.FindAll(s => s.IsEnabled() && s.Type == "AutoReply")) {
Lang_Action defaultLangAction = message.CachedLangActions[_config.DefaultLang];
if (defaultLangAction.Replies == null || defaultLangAction.Replies.Count == 0) continue;
if (string.IsNullOrEmpty(message.Target)) continue;
foreach (var set in message.Target.Split('|')) {
string regexPattern = "";
foreach (var keyword in set.Split(',')) regexPattern += $"(?=.*{(keyword.StartsWith("!") ? "^!" : "")}\\b{keyword.TrimStart('!').Trim()}\\b)";
if (regexPattern == "") continue;
regexPattern = $"^{regexPattern}.*$";
_autoReplyPatterns.Add(new (regexPattern, RegexOptions.IgnoreCase), message);
count++;
}
}
PrintWarning($"Cached {count} auto reply keyword pattern{((count == 1) ? "" : "s")}.");
}
List<string> _registeredConfigCommands;
private void RegisterConfigCommands() {
_registeredConfigCommands = new();
int count = 0;
foreach (var message in _config.Actions.FindAll(s => s.IsEnabled() && s.Type == "ChatCommand")) {
Lang_Action defaultLangAction = message.CachedLangActions[_config.DefaultLang];
if (defaultLangAction.Replies == null || defaultLangAction.Replies.Count == 0) continue;
if (string.IsNullOrEmpty(message.Target)) continue;
string command = message.Target.Replace("/", "").Trim().ToLower();
foreach (string com in command.Split(',').Select(s => s.Trim())) {
if (string.IsNullOrEmpty(com)) continue;
RegisterCommand(com, nameof(CustomChatCMD));
_registeredConfigCommands.Add(com);
count++;
}
}
PrintWarning($"Registered {count} chat command{((count == 1) ? "" : "s")}.");
}
private void UnregisterCachedCommands() {
if (_registeredConfigCommands == null) return;
Covalence library = GetLibrary<Covalence>();
foreach (var command in _registeredConfigCommands) library.UnregisterCommand(command, this);
_registeredConfigCommands.Clear();
}
private void RegisterCommand(string command, string callback, string perm = null) {
if (!string.IsNullOrEmpty(command) && !command.Equals("null", StringComparison.OrdinalIgnoreCase)) {
if (string.IsNullOrEmpty(perm)) AddCovalenceCommand(command, callback);
else AddCovalenceCommand(command, callback, perm);
}
}
private void SetUnsetCountries() {
foreach (var player in BasePlayer.activePlayerList) {
PlayerInfo pinfo;
bool isNew = !pData.Players.TryGetValue(player.userID, out pinfo);
if (isNew) pData.Players.Add(player.userID, pinfo = new PlayerInfo() {
TipsActive = true, isNew = true
}
);
if (string.IsNullOrEmpty(pinfo.Country)) SetPlayerCountry(player, pinfo);
}
}
private void DestroyTimers() {
foreach (Timer timer in _timers) if (timer != null) timer.Destroy();
}
private void ProcessReplies(BasePlayer player, List<string> types, string target = "", bool onlySendToTeam = false) {
if (!player.userID.IsSteamId()) return;
PlayerInfo pinfo;
bool isNew = !pData.Players.TryGetValue(player.userID, out pinfo);
if (isNew) pData.Players.Add(player.userID, pinfo = new PlayerInfo() {
TipsActive = true, isNew = true
}
);
foreach (var action in _config.Actions.FindAll(s => s.IsEnabled() && types.Contains(s.Type))) {
Lang_Action defaultLangAction = action.CachedLangActions[_config.DefaultLang];
if (defaultLangAction.Replies == null || defaultLangAction.Replies.Count == 0) continue;
if ((action.PlayerCanDisable && !pinfo.TipsActive) || !MessageIsEligible(action, player.UserIDString)) continue;
bool toSend = false;
if(AMTriggers.TryGetValue(action.Type, out AMTrigger amTrigger)) {
if (amTrigger.RequiresTarget) {
if (string.IsNullOrEmpty(target)) break;
switch (amTrigger.Key) {
case "ChatCommand" : if (action.Target.Replace("/", "").ToLower().Split(',').Any(x => x.Trim() == target)) toSend = true;
break;
default : if(action.Target == target) toSend = true;
break;
}
}
else {
switch (amTrigger.Key) {
case "NewPlayerJoined" : if (pinfo.isNew) toSend = true;
break;
case "PlayerConnected" : if (pinfo.justJoined) toSend = true;
break;
case "PlayerDead" : if (pinfo.wasDead && ((target == "" && !action.IsGlobalBroadcast) || (target == "FromEntityDeath" && action.IsGlobalBroadcast))) {
pinfo.wasDead = false;
toSend = true;
}
break;
default : toSend = true;
break;
}
}
if (amTrigger.UsesGenericCooldown && action.OnCooldownAll) toSend = false;
if (amTrigger.UsesPlayerCooldown && action.OnCooldown != null && action.OnCooldown.Contains(player.userID)) toSend = false;
}
if (toSend) {
if (action.IsGlobalBroadcast) ProcessReply(action, player, onlySendToTeam : onlySendToTeam);
else {
string playerLanguage = "";
Lang_Action langAction = null;
string reply = string.Empty;
if (Lang_TryGetAction(player, action.Type, action.Id, out playerLanguage, out langAction) && langAction.Replies.Count > 0) {
if (langAction.ReplyIndex >= langAction.Replies.Count) langAction.ReplyIndex = 0;
reply = langAction.Replies[langAction.ReplyIndex];
IncrementReplyIndex(ref langAction);
}
else {
langAction = action.CachedLangActions[_config.DefaultLang];
if (langAction.ReplyIndex >= langAction.Replies.Count) langAction.ReplyIndex = 0;
reply = langAction.Replies[langAction.ReplyIndex];
IncrementReplyIndex(ref langAction);
}
reply = FormatMessage(player, reply);
timer.Once(0.5f, () => {
SendMessage(player, reply, action.SendInChat, action.SendAsGameTip);
}
);
}
switch(action.Type) {
case "AutoReply" : action.RunCooldownAll(_config.AutoReplyCooldown);
break;
case "ChatCommand" : action.RunCooldownAll(_config.ChatCommandCooldown);
break;
case "EnteredZone" : action.RunCooldownPlayer(player, _config.ZoneManagerCooldown);
break;
case "LeftZone" : action.RunCooldownPlayer(player, _config.ZoneManagerCooldown);
break;
case "EnteredMonument" : action.RunCooldownPlayer(player, _config.MonumentWatcherCooldown);
break;
case "LeftMonument" : action.RunCooldownPlayer(player, _config.MonumentWatcherCooldown);
break;
}
}
}
pinfo.isNew = false;
pinfo.justJoined = false;
}
private void ProcessReply(PluginConfig.ConfigAction action, BasePlayer triggerOwner = null, Lang_Action defaultLangAction = null, bool onlySendToTeam = false) {
if(defaultLangAction == null) defaultLangAction = action.CachedLangActions[_config.DefaultLang];
if (defaultLangAction.Replies == null || defaultLangAction.Replies.Count == 0) return;
if (triggerOwner != null && triggerOwner.IsAdmin && action.DontTriggerAdmin) return;
List<string> incrementedLangIndex = Facepunch.Pool.Get<List<string>>();
foreach (var player in BasePlayer.activePlayerList) {
if(onlySendToTeam && triggerOwner.currentTeam != 0 && triggerOwner.currentTeam != player.currentTeam) continue;
string playerLanguage = "";
PlayerInfo pinfo;
if (!pData.Players.TryGetValue(player.userID, out pinfo)) pData.Players.Add(player.userID, pinfo = new PlayerInfo() {
TipsActive = true, isNew = true
}
);
if ((action.PlayerCanDisable && !pinfo.TipsActive) || !MessageIsEligible(action, player.UserIDString)) continue;
string reply = string.Empty;
if (Lang_TryGetAction(player, action.Type, action.Id, out playerLanguage, out Lang_Action playerlangAction) && playerlangAction.Replies.Count > 0) {
if (playerlangAction.ReplyIndex >= playerlangAction.Replies.Count) playerlangAction.ReplyIndex = 0;
reply = playerlangAction.Replies[playerlangAction.ReplyIndex];
if (!incrementedLangIndex.Contains(playerLanguage)) {
IncrementReplyIndex(ref playerlangAction);
incrementedLangIndex.Add(playerLanguage);
}
}
else {
if (defaultLangAction.ReplyIndex >= defaultLangAction.Replies.Count) defaultLangAction.ReplyIndex = 0;
reply = defaultLangAction.Replies[defaultLangAction.ReplyIndex];
if (!incrementedLangIndex.Contains(_config.DefaultLang)) {
IncrementReplyIndex(ref defaultLangAction);
incrementedLangIndex.Add(_config.DefaultLang);
}
}
reply = FormatMessage((triggerOwner == null) ? player : triggerOwner, reply);
timer.Once(0.5f, () => {
SendMessage(player, reply, action.SendInChat, action.SendAsGameTip);
}
);
}
Facepunch.Pool.FreeUnmanaged<string>(ref incrementedLangIndex);
}
private void IncrementReplyIndex(ref Lang_Action lang_Action) {
lang_Action.ReplyIndex++;
if (lang_Action.ReplyIndex >= lang_Action.Replies.Count || lang_Action.ReplyIndex < 0) lang_Action.ReplyIndex = 0;
}
List<string> availableVariables = new List<string>() {
"{playername}", "{playerid}", "{playercountry}", "{hacklocation}", "{wipetimeremaining}", "{online}", "{sleeping}", "{joining}"
}
;
private string FormatMessage(BasePlayer player, string message) {
StringBuilder builder = new StringBuilder(message);
builder.Replace("{playername}", player.displayName).Replace("{playerid}", player.UserIDString).Replace("{online}", $"{BasePlayer.activePlayerList.Count}").Replace("{sleeping}", $"{BasePlayer.sleepingPlayerList.Count}").Replace("{joining}", $"{ServerMgr.Instance.connectionQueue.Joining + ServerMgr.Instance.connectionQueue.Queued}");
if (message.Contains("{playercountry}")) {
PlayerInfo pinfo;
bool found = pData.Players.TryGetValue(player.userID, out pinfo);
if (found && !string.IsNullOrEmpty(pinfo.Country)) builder.Replace("{playercountry}", pinfo.Country);
else builder.Replace("{playercountry}", "");
}
if (message.Contains("{wipetimeremaining}") && WipeTimer.serverinstance != null) {
DateTimeOffset nowTime = DateTimeOffset.UtcNow;
nowTime = nowTime.AddDays((double)WipeTimer.daysToAddTest);
nowTime = nowTime.AddHours((double)WipeTimer.hoursToAddTest);
DateTimeOffset wipeTime = WipeTimer.serverinstance.GetWipeTime(nowTime);
TimeSpan timeSpan = wipeTime - nowTime;
builder.Replace("{wipetimeremaining}", FormatTime(timeSpan, m : false, s : false));
}
if (message.Contains("{hacklocation}")) {
PlayerInfo pinfo;
bool found = pData.Players.TryGetValue(player.userID, out pinfo);
if (found && !string.IsNullOrEmpty(pinfo.HackLocation)) builder.Replace("{hacklocation}", pinfo.HackLocation);
else builder.Replace("{hacklocation}", GetGrid(player.transform.position));
}
if (PlaceholderAPI) {
PlaceholderAPI?.CallHook("ProcessPlaceholders", player.IPlayer, builder);
}
return builder.ToString();
}
//Credit @Substrata
string GetGrid(Vector3 pos) {
float worldSize = TerrainMeta.Size.x;
int cellCount = Mathf.FloorToInt((worldSize * 7) / 1024);
float cellSize = worldSize / cellCount;
int x = Mathf.FloorToInt((pos.x + (worldSize / 2)) / cellSize);
int z = Mathf.FloorToInt((pos.z + (worldSize / 2)) / cellSize);
char zChar = (char)('A' + (x % 26));
string columnLabel = x < 26 ? zChar.ToString() : $"{(char)('A' + x / 26 - 1)}{zChar}";
int row = cellCount - 1 - z;
return $"{columnLabel}{row}";
}
#endregion
#region Hooks
void UnsubscribeHooks() {
Unsubscribe(nameof(OnPlayerChat));
Unsubscribe(nameof(OnPlayerDisconnected));
Unsubscribe(nameof(CanHackCrate));
Unsubscribe(nameof(OnEntityDeath));
Unsubscribe(nameof(OnPlayerSleepEnded));
Unsubscribe(nameof(OnUserPermissionGranted));
Unsubscribe(nameof(OnUserPermissionRevoked));
Unsubscribe(nameof(OnUserGroupAdded));
Unsubscribe(nameof(OnUserGroupRemoved));
Unsubscribe(nameof(OnEnterZone));
Unsubscribe(nameof(OnExitZone));
Unsubscribe(nameof(OnPlayerEnteredMonument));
Unsubscribe(nameof(OnPlayerExitedMonument));
}
void SubscribeHooks() {
foreach (var trigger in AMTriggers) {
if (string.IsNullOrEmpty(trigger.Value.Hooks)) continue;
if (_config.Actions.Any(s => s.Type == trigger.Key && s.IsEnabled())) foreach (string hook in trigger.Value.Hooks.Split(',')) Subscribe(hook);
}
}
private void OnPlayerChat(BasePlayer player, string message, Chat.ChatChannel chatchannel) {
if (chatchannel != Chat.ChatChannel.Global && chatchannel != Chat.ChatChannel.Team) return;
foreach (var keywords in _autoReplyPatterns.Where(regex => regex.Key.Match(message).Success)) {
NextTick(() => {
ProcessReplies(player, new List<string>() {
"AutoReply"
}
, (keywords.Value as PluginConfig.ConfigAction).Target, chatchannel == Chat.ChatChannel.Team && _config.BroadcastToTeamOnly);
}
);
}
}
private void OnPlayerConnected(BasePlayer player) {
if (!player.userID.IsSteamId()) return;
PlayerInfo pinfo;
if (pData.Players.TryGetValue(player.userID, out pinfo)) {
pinfo.justJoined = true;
}
else {
pData.Players.Add(player.userID, pinfo = new PlayerInfo() {
TipsActive = true, isNew = true, justJoined = !HasActiveNewPlayerJoinedAction
}
);
}
if (string.IsNullOrEmpty(pinfo.Country)) SetPlayerCountry(player, pinfo);
}
private void OnPlayerDisconnected(BasePlayer player) {
if (player != null) ProcessReplies(player, new List<string>() {
"PlayerDisconnected"
}
);
}
private void CanHackCrate(BasePlayer player, HackableLockedCrate crate) {
if (player != null && player.userID.IsSteamId()) {
string Location = GetGrid(crate.transform.position);
if (OnCargoShip(crate)) Location = "Cargo Ship";
else {
MonumentRef monument = GetMonumentFromPosition(crate.transform.position);
if (monument != null) Location = $"{monument.DisplayName} ({Location})";
}
PlayerInfo pinfo;
if (pData.Players.TryGetValue(player.userID, out pinfo)) {
pinfo.HackLocation = Location;
}
else {
pData.Players.Add(player.userID, pinfo = new PlayerInfo() {
TipsActive = true, HackLocation = Location
}
);
}
ProcessReplies(player, new List<string>() {
"CrateHacked"
}
);
}
}
private void OnEntityDeath(BasePlayer player, HitInfo hitInfo) {
if (player == null || !player.userID.IsSteamId()) return;
PlayerInfo pinfo;
if (pData.Players.TryGetValue(player.userID, out pinfo)) {
pinfo.wasDead = true;
ProcessReplies(player, new List<string>() {
"PlayerDead"
}
, "FromEntityDeath");
}
}
private void OnPlayerSleepEnded(BasePlayer player) {
if (player.IsDead() || !player.IsConnected) return;
ProcessReplies(player, new List<string>() {
"NewPlayerJoined", "PlayerConnected", "PlayerDead"
}
);
}
private void OnUserPermissionGranted(string id, string permName) {
var player = BasePlayer.FindByID(Convert.ToUInt64(id));
if (player != null) ProcessReplies(player, new List<string>() {
"PermissionGranted"
}
, permName);
}
private void OnUserPermissionRevoked(string id, string permName) {
var player = BasePlayer.FindByID(Convert.ToUInt64(id));
if (player != null) ProcessReplies(player, new List<string>() {
"PermissionRevoked"
}
, permName);
}
private void OnUserGroupAdded(string userId, string groupName) {
var player = BasePlayer.FindByID(Convert.ToUInt64(userId));
if (player != null) ProcessReplies(player, new List<string>() {
"AddedToGroup"
}
, groupName);
}
private void OnUserGroupRemoved(string userId, string groupName) {
var player = BasePlayer.FindByID(Convert.ToUInt64(userId));
if (player != null) ProcessReplies(player, new List<string>() {
"RemovedFromGroup"
}
, groupName);
}
private void OnEnterZone(string ZoneID, BasePlayer player) {
if (player != null) ProcessReplies(player, new List<string>() {
"EnteredZone"
}
, ZoneID);
}
private void OnExitZone(string ZoneID, BasePlayer player) {
if (player != null) ProcessReplies(player, new List<string>() {
"LeftZone"
}
, ZoneID);
}
private void OnPlayerEnteredMonument(string monumentID, BasePlayer player, string type, string oldMonumentID) {
if (player != null) ProcessReplies(player, new List<string>() {
"EnteredMonument"
}
, monumentID);
}
private void OnPlayerExitedMonument(string monumentID, BasePlayer player, string type, string reason, string newMonumentID) {
if (player != null) ProcessReplies(player, new List<string>() {
"LeftMonument"
}
, monumentID);
}
#endregion
#region Commands
private void TipsToggleChatCMD(IPlayer user, string command, string[] args) {
var player = user.Object as BasePlayer;
if (player == null) return;
bool isActive = pData.ToggleTipsActive(player.userID);
if (isActive) SendMessage(player, lang.GetMessage("toggle_enabled", this, player.UserIDString));
else SendMessage(player, lang.GetMessage("toggle_disabled", this, player.UserIDString));
}
private void CustomChatCMD(IPlayer user, string command, string[] args) {
var player = user.Object as BasePlayer;
if (player == null) return;
ProcessReplies(player, new List<string>() {
"ChatCommand"
}
, command);
}
#endregion
#region AMTrigger
Dictionary<string, AMTrigger> AMTriggers;
private class AMTrigger {
public string Key {
get;
set;
}
public string Hooks {
get;
set;
}
public bool RequiresTarget {
get;
set;
}
public bool UsesIsGlobalBroadcast {
get;
set;
}
public bool UsesDontTriggerAdmin {
get;
set;
}
public bool UsesGenericCooldown {
get;
set;
}
public bool UsesPlayerCooldown {
get;
set;
}
}
private void InitAMTriggers() {
AMTriggers = new();
AMTriggers.Add("Timed", new AMTrigger() {
Key = "Timed", RequiresTarget = false
}
);
AMTriggers.Add("ChatCommand", new AMTrigger() {
Key = "ChatCommand", Hooks = "OnPlayerChat", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true, UsesGenericCooldown = true
}
);
AMTriggers.Add("AutoReply", new AMTrigger() {
Key = "AutoReply", Hooks = "OnPlayerChat", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true, UsesGenericCooldown = true
}
);
AMTriggers.Add("NewPlayerJoined", new AMTrigger() {
Key = "NewPlayerJoined", Hooks = "OnPlayerSleepEnded", RequiresTarget = false, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("PlayerConnected", new AMTrigger() {
Key = "PlayerConnected", Hooks = "OnPlayerSleepEnded", RequiresTarget = false, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("PlayerDisconnected", new AMTrigger() {
Key = "PlayerDisconnected", Hooks = "OnPlayerDisconnected", RequiresTarget = false, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("PermissionGranted", new AMTrigger() {
Key = "PermissionGranted", Hooks = "OnUserPermissionGranted", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("PermissionRevoked", new AMTrigger() {
Key = "PermissionRevoked", Hooks = "OnUserPermissionRevoked", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("AddedToGroup", new AMTrigger() {
Key = "AddedToGroup", Hooks = "OnUserGroupAdded", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("RemovedFromGroup", new AMTrigger() {
Key = "RemovedFromGroup", Hooks = "OnUserGroupRemoved", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("PlayerDead", new AMTrigger() {
Key = "PlayerDead", Hooks = "OnEntityDeath,OnPlayerSleepEnded", RequiresTarget = false, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("CrateHacked", new AMTrigger() {
Key = "CrateHacked", Hooks = "CanHackCrate", RequiresTarget = false, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true
}
);
AMTriggers.Add("EnteredZone", new AMTrigger() {
Key = "EnteredZone", Hooks = "OnEnterZone", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true, UsesPlayerCooldown = true
}
);
AMTriggers.Add("LeftZone", new AMTrigger() {
Key = "LeftZone", Hooks = "OnExitZone", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true, UsesPlayerCooldown = true
}
);
AMTriggers.Add("EnteredMonument", new AMTrigger() {
Key = "EnteredMonument", Hooks = "OnPlayerEnteredMonument", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true, UsesPlayerCooldown = true
}
);
AMTriggers.Add("LeftMonument", new AMTrigger() {
Key = "LeftMonument", Hooks = "OnPlayerExitedMonument", RequiresTarget = true, UsesIsGlobalBroadcast = true, UsesDontTriggerAdmin = true, UsesPlayerCooldown = true
}
);
}
#endregion
#region Config
bool HasActiveNewPlayerJoinedAction = false;
void ProcessConfig() {
HasActiveNewPlayerJoinedAction = _config.Actions.Any(s => s.Type == "NewPlayerJoined" && s.IsEnabled());
Lang_CacheActions();
}
private class PluginConfig {
[JsonProperty(Order = 1000)]
public Oxide.Core.VersionNumber Version;
[JsonProperty(Order = 1, PropertyName = "Chat Icon (Steam Id)")]
public string IconSteamId {
get;
set;
}
[JsonProperty(Order = 2, PropertyName = "Toggle Chat Command")]
public string ToggleCommand {
get;
set;
}
[JsonProperty(Order = 3, PropertyName = "AutoReply Cooldown (in seconds)")]
public int AutoReplyCooldown {
get;
set;
}
[JsonProperty(Order = 4, PropertyName = "ChatCommand Cooldown (in seconds)")]
public int ChatCommandCooldown {
get;
set;
}
[JsonProperty(Order = 5, PropertyName = "ZoneManager Cooldown (in seconds)")]
public int ZoneManagerCooldown {
get;
set;
}
[JsonProperty(Order = 6, PropertyName = "MonumentWatcher Cooldown (in seconds)")]
public int MonumentWatcherCooldown {
get;
set;
}
[JsonProperty(Order = 7, PropertyName = "Sample Types for Reference (Do Not Edit)")]
public string SampleTypes {
get;
set;
}
[JsonProperty(Order = 8, PropertyName = "Replies Server Languages (Creates lang file for each in data/AutomatedMessages/lang)")]
public List<string> ServerLangs {
get;
set;
}
[JsonProperty(Order = 9, PropertyName = "Default Server Language")]
public string DefaultLang {
get;
set;
}
[JsonProperty(Order = 10, PropertyName = "AutoReply `Broadcast to all` option to broadcast to team only if keywords sent from team chat")]
public bool BroadcastToTeamOnly {
get;
set;
}
[JsonProperty(Order = 11, PropertyName = "Actions")]
public List<ConfigAction> Actions {
get;
set;
}
[JsonProperty("Chat Icon (SteamId)")]
public ulong _ObsIconSteamId {
get;
set;
}
[JsonProperty("Messages")]
public List<ConfigAction> _ObsMessages = null;
public bool ShouldSerialize_ObsMessages() => _ObsMessages != null;
internal class ConfigAction {
[JsonProperty("Id")]
public string Id = GenerateId();
[JsonProperty(Order = 0, PropertyName = "Enabled")]
public bool Enabled {
get;
set;
}
[JsonProperty("Type (Check Sample Types above for Reference)")]
public string Type = "";
[JsonProperty("Broadcast to all?")]
public bool IsGlobalBroadcast {
get;
set;
}
[JsonProperty("Don't trigger for admins")]
public bool DontTriggerAdmin {
get;
set;
}
[JsonProperty("Send in chat")]
public bool SendInChat = true;
[JsonProperty("Send as game tip")]
public bool SendAsGameTip {
get;
set;
}
[JsonProperty("Interval between messages in minutes (if Type = Timed)")]
public int Interval = 0;
[JsonProperty("Target")]
public string Target = "";
[JsonProperty("Permissions")]
public List<string> Permissions = new();
[JsonProperty("Groups")]
public List<string> Groups = new();
[JsonProperty("Blacklisted Permissions")]
public List<string> BlacklistedPerms = new();
[JsonProperty("Blacklisted Groups")]
public List<string> BlacklistedGroups = new();
[JsonProperty("Player Can Disable?")]
public bool PlayerCanDisable {
get;
set;
}
[JsonIgnore]
public bool OnCooldownAll = false;
[JsonIgnore]
public List<ulong> OnCooldown {
get;
set;
}
[JsonIgnore]
public Dictionary<string, Lang_Action> CachedLangActions {
get;
set;
}
[JsonProperty("Messages (Random if more than one)")]
public List<string> _ObsMessages = null;
[JsonProperty("Replies")]
public List<string> _ObsReplies = null;
public void RunCooldownAll(int cooldown) {
if (cooldown <= 0) return;
OnCooldownAll = true;
_plugin.timer.Once(cooldown, () => {
OnCooldownAll = false;
}
);
}
public void RunCooldownPlayer(BasePlayer player, int cooldown) {
OnCooldown ??= new();
if (cooldown <= 0 || OnCooldown.Contains(player.userID)) return;
OnCooldown.Add(player.userID);
_plugin.timer.Once(cooldown, () => {
OnCooldown.Remove(player.userID);
}
);
}
public ConfigAction Clone(bool newGuid = false) {
return new ConfigAction() {
Id = newGuid ? GenerateId() : Id, Enabled = Enabled, Type = Type, IsGlobalBroadcast = IsGlobalBroadcast, DontTriggerAdmin = DontTriggerAdmin, SendInChat = SendInChat, SendAsGameTip = SendAsGameTip, Interval = Interval, Target = Target, Permissions = new (Permissions), Groups = new (Groups), BlacklistedPerms = new (BlacklistedPerms), BlacklistedGroups = new (BlacklistedGroups), PlayerCanDisable = PlayerCanDisable, CachedLangActions = null
}
;
}
public bool ShouldSerialize_ObsMessages() => _ObsMessages != null;
public bool ShouldSerialize_ObsReplies() => _ObsReplies != null;
public static string GenerateId() {
ulong timestamp = (ulong)DateTime.UtcNow.Ticks;
uint randomPart = (uint)UnityEngine.Random.Range(0, int.MaxValue);
return ((timestamp << 32) | randomPart).ToString();
}
public bool IsEnabled() => Enabled && (SendInChat || SendAsGameTip) ? true : false;
}
public bool ShouldSerialize_ObsIconSteamId() => _ObsIconSteamId != 0;
}
private PluginConfig GetDefaultConfig() {
PluginConfig result = new PluginConfig {
IconSteamId = "0", Version = Version, ToggleCommand = "tips", AutoReplyCooldown = 30, ChatCommandCooldown = 30, ZoneManagerCooldown = 0, MonumentWatcherCooldown = 15, SampleTypes = string.Join(" | ", AMTriggers.Keys), ServerLangs = new List<string>() {
"en", "es", "it", "ru", "zh"
}
, DefaultLang = lang.GetServerLanguage(), BroadcastToTeamOnly = true, Actions = new List<PluginConfig.ConfigAction>()
}
;
return result;
}
private void CheckForConfigUpdates() {
bool changes = false;
if (_config == null) {
PluginConfig tmpDefaultConfig = GetDefaultConfig();
_config = tmpDefaultConfig;
changes = true;
}
string sampleTypes = string.Join(" | ", AMTriggers.Keys);
if(_config.SampleTypes != sampleTypes) {
_config.SampleTypes = sampleTypes;
changes = true;
}
//1.0.8 update
if (_config.Version == null || _config.Version < new VersionNumber(1, 0, 8)) {
PluginConfig tmpDefaultConfig = GetDefaultConfig();
_config.ToggleCommand = tmpDefaultConfig.ToggleCommand;
changes = true;
}
//1.0.16 update
if (_config.Version < new VersionNumber(1, 0, 16)) {
PluginConfig tmpDefaultConfig = GetDefaultConfig();
_config.AutoReplyCooldown = tmpDefaultConfig.AutoReplyCooldown;
changes = true;
}
//2.0.0 update
if (_config.Version < new VersionNumber(2, 0, 0)) {
if (_config._ObsMessages != null) _config.Actions = new List<PluginConfig.ConfigAction>(_config._ObsMessages);
else _config.Actions = new();
_config._ObsMessages = null;
foreach (var action in _config.Actions) {
if (action._ObsMessages != null) action._ObsReplies = new List<string>(action._ObsMessages);
else action._ObsReplies = new();
action._ObsMessages = null;
}
changes = true;
}
if(string.IsNullOrEmpty(_config.DefaultLang) || !AvailableLangs.ContainsKey(_config.DefaultLang)) {
_config.DefaultLang = lang.GetServerLanguage();
changes = true;
}
//2.0.6 update
if (_config.Version < new VersionNumber(2, 0, 6)) {
PluginConfig tmpDefaultConfig = GetDefaultConfig();
_config.ServerLangs = _config.ServerLangs;
_config.IconSteamId = _config._ObsIconSteamId.ToString();
_config._ObsIconSteamId = 0;
_config.ChatCommandCooldown = tmpDefaultConfig.ChatCommandCooldown;
_config.ZoneManagerCooldown = tmpDefaultConfig.ZoneManagerCooldown;
_config.MonumentWatcherCooldown = tmpDefaultConfig.MonumentWatcherCooldown;
_config.ServerLangs = new List<string>() {
_config.DefaultLang
}
;
RepliesLangData = new();
foreach (string serverLang in _config.ServerLangs) {
if (!AvailableLangs.ContainsKey(serverLang)) continue;
if (RepliesLangData.ContainsKey(serverLang)) continue;
Lang_Root langRoot = new();
foreach (var amTrigger in AMTriggers) if (!langRoot.Triggers.ContainsKey(amTrigger.Key)) {
langRoot.Triggers.Add(amTrigger.Key, new());
changes = true;
}
RepliesLangData.Add(serverLang, langRoot);
}
foreach (var action in _config.Actions) {
if (action._ObsReplies != null) {
foreach (var langRoot in RepliesLangData) {
if(langRoot.Value.Triggers.TryGetValue(action.Type, out Lang_Trigger langTrigger)) {
langTrigger.Actions ??= new();
if(langRoot.Key == _config.DefaultLang) langTrigger.Actions.Add(action.Id, new() {
Replies = action._ObsReplies
}
);
else langTrigger.Actions.Add(action.Id, new());
}
}
action._ObsReplies = null;
}
}
Lang_SaveRepliesData();
changes = true;
}
else Lang_LoadRepliesData();
if (_config.ServerLangs == null || _config.ServerLangs.Count == 0) {
_config.ServerLangs = new List<string>() {
lang.GetServerLanguage()
}
;
changes = true;
}
var langsToRemove = _config.ServerLangs.Where(s => !AvailableLangs.ContainsKey(s));
if (langsToRemove.Length > 0) {
PrintWarning($"Language code{(langsToRemove.Length > 1 ? "s" : "")} \"{string.Join(", ", langsToRemove)}\" {(langsToRemove.Length > 1 ? "are" : "is")} not available or not in correct code (removed from config), the following are the available options:\n{string.Join(", ", AvailableLangs.Keys)}.");
for (int i = 0; i < langsToRemove.Length; i++) _config.ServerLangs.Remove(langsToRemove[i]);
changes = true;
}
//2.0.9 update
if (_config.Version < new VersionNumber(2, 0, 9)) {
_config.BroadcastToTeamOnly = true;
changes = true;
}
if (_config.Version != Version) changes = true;
if (changes) {
_config.Version = Version;
PrintWarning("Config updated.");
SaveConfig();
}
}
protected override void LoadDefaultConfig() => _config = GetDefaultConfig();
protected override void LoadConfig() {
InitAMTriggers();
base.LoadConfig();
_config = Config.ReadObject<PluginConfig>();
CheckForConfigUpdates();
}
protected override void SaveConfig() {
Config.WriteObject(_config);
}
#endregion
#region Data
public PlayersData pData;
private void OnServerSave() => timer.Once(UnityEngine.Random.Range(5f, 10f), SaveData);
private bool wiped {
get;
set;
}
private void OnNewSave(string filename) => wiped = true;
private void SaveData() {
if (pData != null) pData.Save();
}
public class PlayersData {
public Dictionary<ulong, PlayerInfo> Players = new();
public static PlayersData Load() {
var data = Interface.Oxide.DataFileSystem.ReadObject<PlayersData>($"{nameof(AutomatedMessages)}/PlayerData");
if (data == null || _plugin.wiped) {
_plugin.PrintWarning("No player data found! Creating a new data file.");
data = new();
data.Save();
}
return data;
}
public void Save() {
Interface.Oxide.DataFileSystem.WriteObject($"{nameof(AutomatedMessages)}/PlayerData", this);
}
public bool CheckIfNew(ulong playerid) {
return !Players.ContainsKey(playerid);
}
public void EnableTips(ulong playerid, bool active) {
PlayerInfo playerInfo;
if (!Players.TryGetValue(playerid, out playerInfo)) Players.Add(playerid, playerInfo = new PlayerInfo() {
TipsActive = active, isNew = true
}
);
else playerInfo.TipsActive = active;
}
public bool ToggleTipsActive(ulong playerid) {
PlayerInfo playerInfo;
if (!Players.TryGetValue(playerid, out playerInfo)) Players.Add(playerid, playerInfo = new PlayerInfo() {
TipsActive = true, isNew = true
}
);
else playerInfo.TipsActive = !playerInfo.TipsActive;
return playerInfo.TipsActive;
}
public bool IsActive(ulong playerid) {
PlayerInfo playerInfo;
if (!Players.TryGetValue(playerid, out playerInfo)) {
return true;
}
else {
return playerInfo.TipsActive;
}
}
}
public class PlayerInfo {
public string Country = "";
public bool TipsActive = true;
[JsonIgnore]
public bool wasDead = false;
[JsonIgnore]
public bool justJoined = false;
[JsonIgnore]
public bool isNew = false;
[JsonIgnore]
public string HackLocation = "";
}
#endregion
#region Helpers
private void SendMessage(BasePlayer player, string message, bool SendInChat = true, bool SendAsGameTip = false) {
if (player == null) return;
if (SendInChat) {
ulong iconSteamId = 0;
ul
// ... [Code preview truncated due to length. Full source in attached file] ...