C#:
// Requires: BetterChat
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Better Chat Mentions", "Death", "1.3.1")]
[Description("Adds Discord-like mention capability for @playername and @everyone")]
class BetterChatMentions : RustPlugin
{
#region Fields
const string PermEveryone = "betterchatmentions.everyone";
const string PermDisallow = "betterchatmentions.disallow";
const string PermExclude = "betterchatmentions.exclude";
[PluginReference] Plugin BetterChat;
Dictionary<ulong, double> LastAlert = new Dictionary<ulong, double>();
#endregion
#region Config
ConfigFile config;
class ConfigFile
{
[JsonProperty("Group Color To Use (Title/Username/Message)")]
public string GroupColor = "Username";
[JsonProperty("Alert to play")]
public string Alert = "assets/prefabs/locks/keypad/effects/lock.code.unlock.prefab";
[JsonProperty("Delay Between Alert Sounds (Seconds)")]
public double AlertSoundDelay = 0;
[JsonProperty("Everyone Ping Color")]
public string EveryonePingColor = "#55aaff";
[JsonProperty("Ping Audio Offset")]
public Vector3 PingSoundOffset = new Vector3(0f, 2f, 0f);
}
protected override void LoadConfig()
{
base.LoadConfig();
config = Config.ReadObject<ConfigFile>();
Config.WriteObject(config, true);
}
protected override void LoadDefaultConfig()
{
config = new ConfigFile();
}
protected override void SaveConfig()
{
Config.WriteObject(config, true);
}
#endregion
#region Hooks
void Init()
{
permission.RegisterPermission(PermEveryone, this);
permission.RegisterPermission(PermDisallow, this);
permission.RegisterPermission(PermExclude, this);
}
Dictionary<string, object> OnBetterChat(Dictionary<string, object> data)
{
var player = data["Player"] as IPlayer;
if (player.HasPermission(PermDisallow))
{
return null;
}
var text = data["Message"].ToString();
var substringIncrease = 0;
var soundPlayers = Facepunch.Pool.Get<List<BasePlayer>>();
var indexes = AllIndexesOf('@', text);
foreach (int listIndex in indexes)
{
var index = listIndex + substringIncrease;
var playerName = text.Substring(index);
var spaceIndex = playerName.IndexOf(' ');
if (spaceIndex != -1)
{
playerName = playerName.Substring(0, spaceIndex);
}
var nameOnly = playerName.TrimStart('@');
var pingEveryone = player.HasPermission(PermEveryone) && string.Equals(nameOnly, "everyone", StringComparison.OrdinalIgnoreCase);
var highlightColour = (pingEveryone) ? config.EveryonePingColor : string.Empty;
var highlightText = pingEveryone ? "everyone" : nameOnly;
if (!pingEveryone)
{
var target = FindPlayer(nameOnly);
if (target == null || ShouldExclude(target))
{
continue;
}
highlightText = target.displayName;
if (!soundPlayers.Contains(target))
{
soundPlayers.Add(target);
}
highlightColour = GetGroupColour(target);
if (string.IsNullOrEmpty(highlightColour))
{
continue;
}
}
var newText = $"@<color={highlightColour}>{highlightText}</color>";
var sb = new StringBuilder(text);
sb.Remove(index, playerName.Length);
sb.Insert(index, newText);
text = sb.ToString();
substringIncrease += newText.Length - playerName.Length;
if (pingEveryone)
{
soundPlayers.AddRange(BasePlayer.activePlayerList);
break;
}
}
Facepunch.Pool.FreeUnmanaged(ref indexes);
foreach (var target in soundPlayers)
{
if (!ShouldPlaySound(target))
{
continue;
}
PlaySound(target, config.Alert);
}
data["Message"] = text;
Facepunch.Pool.FreeUnmanaged(ref soundPlayers);
return data;
}
#endregion
#region Functions
string GetGroupColour(BasePlayer player)
{
var groups = BetterChat.Call<List<JObject>>("API_GetUserGroups", player.IPlayer);
if (groups == null || groups.Count == 0)
{
return string.Empty;
}
var primaryGroup = (JObject)default;
var currentPriority = int.MaxValue;
foreach (var group in groups)
{
var priority = (int)group["Priority"];
if (priority < currentPriority)
{
currentPriority = priority;
primaryGroup = group;
}
}
if (primaryGroup == null)
{
return string.Empty;
}
var colorToken = primaryGroup[config.GroupColor]?["Color"];
var groupColour = colorToken?.ToString();
if (string.IsNullOrEmpty(groupColour))
{
return string.Empty;
}
if (!groupColour.StartsWith("#"))
{
groupColour = "#" + groupColour;
}
return groupColour;
}
void PlaySound(BasePlayer player, string path)
{
var effect = new Effect(path, player, 0, Vector3.zero, config.PingSoundOffset);
EffectNetwork.Send(effect, player.net.connection);
RecordAlertSoundPlayed(player);
}
bool ShouldPlaySound(BasePlayer player)
{
if (config.AlertSoundDelay <= 0 || !LastAlert.TryGetValue(player.userID, out var lastTime))
{
return true;
}
return ((TimeSinceEpoch() - lastTime) > config.AlertSoundDelay);
}
void RecordAlertSoundPlayed(BasePlayer player)
{
if (config.AlertSoundDelay <= 0)
{
return;
}
LastAlert[player.userID] = TimeSinceEpoch();
}
#endregion
#region Helpers
List<int> AllIndexesOf(char character, string input)
{
var indexes = Facepunch.Pool.Get<List<int>>();
for (int i = 0; i < input.Length; i++)
{
if (input[i] == character)
{
indexes.Add(i);
}
}
return indexes;
}
BasePlayer FindPlayer(string name)
{
if (string.IsNullOrEmpty(name))
{
return null;
}
foreach (var player in BasePlayer.activePlayerList)
{
if (player.displayName.ToLower().Contains(name.ToLower()))
{
return player;
}
}
return null;
}
double TimeSinceEpoch()
{
return (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
}
bool ShouldExclude(BasePlayer player)
{
return permission.UserHasPermission(player.UserIDString, PermExclude);
}
#endregion
}
}