C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
//Reference: System.Drawing
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Ext.Discord.Attributes;
using Oxide.Ext.Discord.Builders;
using Oxide.Ext.Discord.Cache;
using Oxide.Ext.Discord.Clients;
using Oxide.Ext.Discord.Connections;
using Oxide.Ext.Discord.Constants;
using Oxide.Ext.Discord.Entities;
using Oxide.Ext.Discord.Extensions;
using Oxide.Ext.Discord.Interfaces;
using Oxide.Ext.Discord.Libraries;
using Oxide.Ext.Discord.Logging;
using Oxide.Ext.Discord.Types;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using UnityEngine;
using Color = System.Drawing.Color;
using Graphics = System.Drawing.Graphics;
using Star = ProtoBuf.PatternFirework.Star;
//DiscordSignLogger created with PluginMerge v(1.0.9.0) by MJSU @ https://github.com/dassjosh/Plugin.Merge
namespace Oxide.Plugins {
[Info("Discord Sign Logger", "MJSU", "3.0.0")]
[Description("Logs Sign / Firework Changes To Discord")]
public partial class DiscordSignLogger : RustPlugin, IDiscordPlugin, IDiscordPool {
#region Plugins\DiscordSignLogger.Fields.cs
#pragma warning disable CS0649
// ReSharper disable InconsistentNaming
[PluginReference]
private Plugin RustTranslationAPI, SignArtist;
// ReSharper restore InconsistentNaming
#pragma warning restore CS0649
#pragma warning disable CS0649
public DiscordClient Client {
get;
set;
}
#pragma warning restore CS0649
private PluginConfig _pluginConfig;
private PluginData _pluginData;
private const string CommandPrefix = "DSL_CMD";
private const string ActionPrefix = "DSL_ACTION";
private const string ModalPrefix = "DSL_MODAL";
private const string PlayerMessage = "PLAYER_MESSAGE";
private const string ServerMessage = "SERVER_MESSAGE";
private const string AccentColor = "#de8732";
private readonly MessageCreate _actionMessage = new() {
AllowedMentions = AllowedMentions.None
}
;
public DiscordPluginPool Pool {
get;
set;
}
private readonly StringBuilder _sb = new();
public readonly Hash<UnityEngine.Color, Brush> FireworkBrushes = new();
private readonly Hash<NetworkableId, SignageUpdate> _updates = new();
private readonly Hash<uint, string> _prefabNameCache = new();
private readonly Hash<int, string> _itemNameCache = new();
private readonly Hash<TemplateKey, SignMessage> _signMessages = new();
private readonly Hash<ButtonId, ImageButton> _imageButtons = new();
private DiscordChannel _actionChannel;
private readonly DiscordPlaceholders _placeholders = GetLibrary<DiscordPlaceholders>();
private readonly DiscordMessageTemplates _templates = GetLibrary<DiscordMessageTemplates>();
private readonly DiscordButtonTemplates _buttonTemplates = GetLibrary<DiscordButtonTemplates>();
private readonly DiscordCommandLocalizations _local = GetLibrary<DiscordCommandLocalizations>();
public int FireworkImageSize;
public int FireworkHalfImageSize;
public int FireworkCircleSize;
private readonly object _true = true;
private readonly object _false = false;
public static DiscordSignLogger Instance;
#endregion
#region Plugins\DiscordSignLogger.Setup.cs
private void Init() {
Instance = this;
UnsubscribeAll();
_pluginConfig.ReplaceImage.TextColor = _pluginConfig.ReplaceImage.TextColor.Replace("#", "");
_pluginConfig.ReplaceImage.BodyColor = _pluginConfig.ReplaceImage.BodyColor.Replace("#", "");
HashSet<string> ids = new();
foreach (SignMessage message in _pluginConfig.SignMessages) {
if (ids.Add(message.MessageId.Name)) {
_signMessages[message.MessageId] = message;
}
else {
PrintWarning($"Duplicate Sign Message ID: '{message.MessageId.Name}'.Please check your config and correct the duplicate Sign Message ID's");
}
}
ids.Clear();
foreach (ImageButton button in _pluginConfig.Buttons) {
if (ids.Add(button.ButtonId.Id)) {
_imageButtons[button.ButtonId] = button;
}
else {
PrintWarning($"Duplicate Button ID: '{button.ButtonId.Id}'.Please check your config and correct the duplicate Image Button ID's");
}
}
_pluginData = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Name);
RegisterPlaceholders();
RegisterTemplates();
}
protected override void LoadDefaultConfig() {
PrintWarning("Loading Default Config");
}
protected override void LoadConfig() {
base.LoadConfig();
Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
_pluginConfig = AdditionalConfig(Config.ReadObject<PluginConfig>());
Config.WriteObject(_pluginConfig);
}
private PluginConfig AdditionalConfig(PluginConfig config) {
config.FireworkSettings = new FireworkSettings(config.FireworkSettings);
config.ReplaceImage = new ReplaceImageSettings(config.ReplaceImage);
config.SignMessages ??= new List<SignMessage>();
config.PluginSettings = new PluginSettings(config.PluginSettings);
if (config.SignMessages.Count == 0) {
config.SignMessages.Add(new SignMessage(null));
}
else {
for (int index = 0; index < config.SignMessages.Count; index++) {
config.SignMessages[index] = new SignMessage(config.SignMessages[index]);
}
}
config.Buttons ??= new List<ImageButton> {
new() {
ButtonId = new ButtonId("ERASE"), DisplayName = "Erase", Style = ButtonStyle.Primary, Commands = new List<string> {
$"dsl.erase {PlaceholderKeys.EntityId} {PlaceholderKeys.TextureIndex}"
}
, PlayerMessage = "An admin erased your sign for being inappropriate", ServerMessage = string.Empty, RequirePermissions = false, ConfirmModal = false, AllowedRoles = new List<Snowflake>(), AllowedGroups = new List<string>()
}
, new() {
ButtonId = new ButtonId("SIGN_BLOCK_24_HOURS"), DisplayName = "Sign Block (24 Hours)", Style = ButtonStyle.Primary, Commands = new List<string> {
"dsl.signblock {player.id} 86400"
}
, PlayerMessage = "You have been banned from updating signs for 24 hours.", ServerMessage = string.Empty, RequirePermissions = true, ConfirmModal = false, AllowedRoles = new List<Snowflake>(), AllowedGroups = new List<string>()
}
, new() {
ButtonId = new ButtonId("KILL_ENTITY"), DisplayName = "Kill Entity", Style = ButtonStyle.Secondary, Commands = new List<string> {
$"entid kill {PlaceholderKeys.EntityId}"
}
, PlayerMessage = "An admin killed your sign for being inappropriate", ServerMessage = string.Empty, RequirePermissions = true, ConfirmModal = false, AllowedRoles = new List<Snowflake>(), AllowedGroups = new List<string>()
}
, new() {
ButtonId = new ButtonId("KICK_PLAYER"), DisplayName = "Kick Player", Style = ButtonStyle.Danger, Commands = new List<string> {
$"kick {DefaultKeys.Player.Id} \"{PlaceholderKeys.PlayerMessage}\"", $"dsl.erase {PlaceholderKeys.EntityId} {PlaceholderKeys.TextureIndex}"
}
, PlayerMessage = string.Empty, ServerMessage = string.Empty, RequirePermissions = true, ConfirmModal = true, AllowedRoles = new List<Snowflake>(), AllowedGroups = new List<string>()
}
, new() {
ButtonId = new ButtonId("BAN_PLAYER"), DisplayName = "Ban Player", Style = ButtonStyle.Danger, Commands = new List<string> {
$"ban {DefaultKeys.Player.Id} \"{PlaceholderKeys.PlayerMessage}\"", $"dsl.erase {PlaceholderKeys.EntityId} {PlaceholderKeys.TextureIndex}"
}
, PlayerMessage = string.Empty, ServerMessage = string.Empty, RequirePermissions = true, ConfirmModal = true, AllowedRoles = new List<Snowflake>(), AllowedGroups = new List<string>()
}
}
;
for (int index = 0; index < config.Buttons.Count; index++) {
config.Buttons[index] = new ImageButton(config.Buttons[index]);
}
return config;
}
private void OnServerInitialized() {
FireworkCircleSize = _pluginConfig.FireworkSettings.CircleSize;
FireworkImageSize = _pluginConfig.FireworkSettings.ImageSize + FireworkCircleSize;
FireworkHalfImageSize = _pluginConfig.FireworkSettings.ImageSize / 2;
if (string.IsNullOrEmpty(_pluginConfig.DiscordApiKey)) {
PrintWarning("Please set the Discord Bot Token and reload the plugin");
return;
}
if (SignArtist is {
IsLoaded : true
}
) {
if (SignArtist.Version < new VersionNumber(1, 4, 0)) {
PrintWarning("Sign Artist version is outdated and may not function correctly.Please update SignArtist @ https://umod.org/plugins/sign-artist to version 1.4.0 or higher");
}
}
else {
Unsubscribe(nameof(OnPlayerCommand));
}
Client.Connect(new BotConnection {
Intents = GatewayIntents.Guilds, ApiToken = _pluginConfig.DiscordApiKey, LogLevel = _pluginConfig.ExtensionDebugging
}
);
}
private void Unload() {
SaveData();
Instance = null;
}
#endregion
#region Plugins\DiscordSignLogger.CoreHooks.cs
private void OnImagePost(BasePlayer player, string url, bool raw, ISignage signage, uint textureIndex) {
bool ignore = player == null || !_pluginConfig.PluginSettings.SignArtist.ShouldLog(url);
_updates[signage.NetworkID] = new SignageUpdate(player, signage, (byte)textureIndex, ignore, url);
}
private void OnSignUpdated(ISignage signage, BasePlayer player, int textureIndex = 0) {
if (player == null) {
_updates.Remove(signage.NetworkID);
return;
}
if (signage.GetTextureCRCs()[textureIndex] == 0) {
return;
}
SignageUpdate update = _updates[signage.NetworkID] ?? new SignageUpdate(player, signage, (byte)textureIndex, player == null);
_updates.Remove(signage.NetworkID);
if (update.IgnoreMessage) {
return;
}
SendDiscordMessage(update);
}
private void OnItemPainted(PaintedItemStorageEntity entity, Item item, BasePlayer player, byte[] image) {
if (entity._currentImageCrc != 0) {
PaintedItemUpdate update = new(player, entity, item, image, false);
SendDiscordMessage(update);
}
}
private void OnFireworkDesignChanged(PatternFirework firework, ProtoBuf.PatternFirework.Design design, BasePlayer player) {
if (design?.stars != null && design.stars.Count != 0) {
SendDiscordMessage(new FireworkUpdate(player, firework));
}
}
private void OnCopyInfoToSign(SignContent content, ISignage sign, IUGCBrowserEntity browser) {
BaseEntity entity = (BaseEntity)sign;
BasePlayer player = BasePlayer.FindByID(entity.OwnerID);
SignageUpdate update = new(player, sign, 0);
SendDiscordMessage(update);
}
private object CanUpdateSign(BasePlayer player, BaseEntity entity) {
if (!_pluginData.IsSignBanned(player)) {
return null;
}
PlaceholderData data = GetPlaceholderData();
data.AddTimeSpan(_pluginData.GetRemainingBan(player));
Chat(player, LangKeys.BlockedMessage, data);
//Client side the sign will still be updated if we block it here.We destroy the entity client side to force a redraw of the image.NextTick(() =>
{
entity.DestroyOnClient(player.Connection);
entity.SendNetworkUpdate();
}
);
return _false;
}
private object OnFireworkDesignChange(PatternFirework firework, ProtoBuf.PatternFirework.Design design, BasePlayer player) {
if (!_pluginData.IsSignBanned(player)) {
return null;
}
PlaceholderData data = GetPlaceholderData();
data.AddTimeSpan(_pluginData.GetRemainingBan(player));
Chat(player, LangKeys.BlockedMessage, data);
return _true;
}
private object OnPlayerCommand(BasePlayer player, string cmd, string[] args) {
if (!cmd.StartsWith("sil", StringComparison.OrdinalIgnoreCase)) {
return null;
}
if (!_pluginData.IsSignBanned(player)) {
return null;
}
PlaceholderData data = GetPlaceholderData();
data.AddTimeSpan(_pluginData.GetRemainingBan(player));
Chat(player, LangKeys.BlockedMessage, data);
return _true;
}
private void UnsubscribeAll() {
Unsubscribe(nameof(OnImagePost));
Unsubscribe(nameof(OnSignUpdated));
Unsubscribe(nameof(OnFireworkDesignChanged));
Unsubscribe(nameof(CanUpdateSign));
Unsubscribe(nameof(OnFireworkDesignChange));
Unsubscribe(nameof(OnPlayerCommand));
Unsubscribe(nameof(OnCopyInfoToSign));
}
private void SubscribeAll() {
Subscribe(nameof(OnSignUpdated));
Subscribe(nameof(OnFireworkDesignChanged));
Subscribe(nameof(CanUpdateSign));
Subscribe(nameof(OnFireworkDesignChange));
Subscribe(nameof(OnCopyInfoToSign));
if (SignArtist is {
IsLoaded : true
}
) {
Subscribe(nameof(OnPlayerCommand));
Subscribe(nameof(OnImagePost));
}
}
#endregion
#region Plugins\DiscordSignLogger.DiscordHooks.cs
[HookMethod(DiscordExtHooks.OnDiscordGuildCreated)]
private void OnDiscordGuildCreated(DiscordGuild guild) {
bool subscribe = false;
foreach (SignMessage message in _pluginConfig.SignMessages) {
if (message.MessageChannel == null && message.ChannelId.IsValid()) {
DiscordChannel channel = guild.GetChannel(message.ChannelId);
if (channel != null) {
message.MessageChannel = channel;
subscribe = true;
}
}
}
if (_pluginConfig.ActionLogChannel.IsValid()) {
DiscordChannel channel = guild.GetChannel(_pluginConfig.ActionLogChannel);
if (channel != null) {
_actionChannel = channel;
}
}
if (subscribe) {
SubscribeAll();
Puts($"{Title} Ready");
RegisterApplicationCommands();
}
}
#endregion
#region Plugins\DiscordSignLogger.DiscordHelpers.cs
public void RunCommand(DiscordInteraction interaction, SignUpdateState state, ImageButton button, string playerMessage, string serverMessage) {
using PlaceholderData data = GetPlaceholderData(state, interaction).AddGuild(Client, interaction.GuildId).Add(PlaceholderDataKeys.PlayerMessage, playerMessage).Add(PlaceholderDataKeys.ServerMessage, serverMessage);
data.ManualPool();
_sb.Clear();
foreach (string buttonCommand in button.Commands) {
string command = _placeholders.ProcessPlaceholders(buttonCommand, data);
covalence.Server.Command(command);
if (_actionChannel != null) {
_sb.AppendLine(command);
}
}
if (_actionChannel != null) {
string command = _sb.ToString();
data.Add(PlaceholderDataKeys.Command, command);
_actionChannel.CreateGlobalTemplateMessage(Client, TemplateKeys.Action.Message, null, data);
}
if (!string.IsNullOrEmpty(playerMessage)) {
BasePlayer player = state.Player.Object as BasePlayer;
if (player != null && player.IsConnected) {
string message = _placeholders.ProcessPlaceholders(playerMessage, data);
Chat(player, message);
}
}
if (!string.IsNullOrEmpty(serverMessage)) {
string message = _placeholders.ProcessPlaceholders(serverMessage, data);
covalence.Server.Broadcast(message);
}
if (_pluginConfig.DisableDiscordButton) {
DisableButton(interaction.Message, interaction.Data.CustomId);
}
interaction.CreateResponse(Client, new InteractionResponse {
Type = InteractionResponseType.UpdateMessage, Data = new InteractionCallbackData {
Components = interaction.Message.Components
}
}
);
}
public void ShowConfirmationModal(DiscordInteraction interaction, SignUpdateState state, ImageButton button, TemplateKey messageId, ButtonId buttonId) {
InteractionModalBuilder builder = new(interaction);
builder.AddModalCustomId(BuildCustomId(ModalPrefix, messageId, buttonId, state.Serialize()));
builder.AddModalTitle(button.DisplayName);
builder.AddInputText(PlayerMessage, "Player Message", InputTextStyles.Paragraph, button.PlayerMessage, false);
builder.AddInputText(ServerMessage, "Server Message", InputTextStyles.Paragraph, button.ServerMessage, false);
interaction.CreateResponse(Client, builder);
}
public bool UserHasButtonPermission(DiscordInteraction interaction, ImageButton button) {
for (int index = 0; index < button.AllowedRoles.Count; index++) {
Snowflake role = button.AllowedRoles[index];
if (interaction.Member.HasRole(role)) {
return true;
}
}
IPlayer player = interaction.Member.User.Player;
if (player != null) {
for (int index = 0; index < button.AllowedGroups.Count; index++) {
string group = button.AllowedGroups[index];
if (permission.UserHasGroup(player.Id, group)) {
return true;
}
}
}
return false;
}
public bool TryParseCommand(string command, out TemplateKey messageId, out ButtonId buttonId, out SignUpdateState state) {
messageId = default;
buttonId = default;
state = null;
ReadOnlySpan<char> span = command.AsSpan();
ReadOnlySpan<char> token = " ";
//Command Prefix can be ignored
if (!span.TryParseNextString(token, out span, out ReadOnlySpan<char> _)) return false;
if (!span.TryParseNextString(token, out span, out ReadOnlySpan<char> messageIdString)) return false;
if (!span.TryParseNextString(token, out span, out ReadOnlySpan<char> buttonIdString)) return false;
if (!span.TryParseNextString(token, out span, out ReadOnlySpan<char> stateString)) return false;
messageId = new TemplateKey(messageIdString.ToString());
buttonId = new ButtonId(buttonIdString.ToString());
state = SignUpdateState.Deserialize(stateString);
return true;
}
public void DisableButton(DiscordMessage message, string id) {
for (int index = 0; index < message.Components.Count; index++) {
ActionRowComponent row = message.Components[index];
for (int i = 0; i < row.Components.Count; i++) {
BaseComponent component = row.Components[i];
if (component is ButtonComponent button && button.CustomId == id) {
button.Disabled = true;
return;
}
}
}
}
public void DisableAllButtons(DiscordMessage message) {
for (int index = 0; index < message.Components.Count; index++) {
ActionRowComponent row = message.Components[index];
for (int i = 0; i < row.Components.Count; i++) {
BaseComponent component = row.Components[i];
if (component is ButtonComponent button) {
button.Disabled = true;
}
}
}
}
public void SendErrorResponse(DiscordInteraction interaction, TemplateKey template, PlaceholderData data) {
DisableAllButtons(interaction.Message);
SendComponentUpdateResponse(interaction);
SendFollowupResponse(interaction, template, data);
}
public void SendComponentUpdateResponse(DiscordInteraction interaction) {
interaction.CreateResponse(Client, new InteractionResponse {
Type = InteractionResponseType.UpdateMessage, Data = new InteractionCallbackData {
Components = interaction.Message.Components
}
}
);
}
public void SendTemplateResponse(DiscordInteraction interaction, TemplateKey templateName, PlaceholderData data) {
interaction.CreateTemplateResponse(Client, InteractionResponseType.ChannelMessageWithSource, templateName, null, data);
}
public void SendFollowupResponse(DiscordInteraction interaction, TemplateKey templateName, PlaceholderData data) {
interaction.CreateFollowUpTemplateResponse(Client, templateName, null, data);
}
public IEnumerable<IPlayer> GetBannedPlayers() {
foreach (ulong key in _pluginData.SignBannedUsers.Keys) {
IPlayer player = FindPlayerById(StringCache<ulong>.Instance.ToString(key));
if (player != null) {
yield return player;
}
}
}
#endregion
#region Plugins\DiscordSignLogger.DiscordMethods.cs
public void SendDiscordMessage(BaseImageUpdate update) {
SignUpdateState state = new(update);
StateKey encodedState = state.Serialize();
using PlaceholderData data = GetPlaceholderData(state);
data.ManualPool();
data.AddPlayer(state.Player).Add(PlaceholderDataKeys.State, state).Add(PlaceholderDataKeys.Owner, state.Owner).Add(PlaceholderDataKeys.MessageState, encodedState);
if (update is SignageUpdate signage) {
data.Add(PlaceholderDataKeys.SignArtistUrl, signage.Url);
}
for (int index = 0; index < _pluginConfig.SignMessages.Count; index++) {
SignMessage signMessage = _pluginConfig.SignMessages[index];
DiscordMessageTemplate message = _templates.GetGlobalTemplate(this, signMessage.MessageId);
MessageCreate create = message.ToMessage<MessageCreate>(data);
data.Add(PlaceholderDataKeys.MessageId, signMessage.MessageId);
create.AddAttachment("image.png", update.GetImage(), "image/png", $"{update.DisplayName} Updated {update.Entity.ShortPrefabName} @{update.Entity.transform.position} On {DateTime.Now:f}");
if (signMessage.Buttons.Count != 0) {
if (signMessage.UseActionButton) {
create.Components = new List<ActionRowComponent> {
new() {
Components = {
_buttonTemplates.GetGlobalTemplate(this, TemplateKeys.Action.Button).ToComponent(data)
}
}
}
;
}
else {
create.Components = CreateButtons(signMessage, data, encodedState);
}
}
signMessage.MessageChannel?.CreateMessage(Client, create);
}
}
private List<ActionRowComponent> CreateButtons(SignMessage signMessage, PlaceholderData data, StateKey encodedState) {
MessageComponentBuilder builder = new();
for (int i = 0; i < signMessage.Buttons.Count; i++) {
ButtonId buttonId = signMessage.Buttons[i];
ImageButton command = _imageButtons[buttonId];
if (command.Commands.Count == 0) {
continue;
}
if (command.Style == ButtonStyle.Link) {
builder.AddLinkButton(command.DisplayName, _placeholders.ProcessPlaceholders(command.Commands[0], data));
}
else {
builder.AddActionButton(command.Style, command.DisplayName, BuildCustomId(CommandPrefix, signMessage.MessageId, buttonId, encodedState));
}
}
return builder.Build();
}
private string BuildCustomId(string command, IDiscordKey messageId, ButtonId? buttonId, IDiscordKey encodedState) {
return $"{command} {messageId.ToString()} {(buttonId.HasValue ? buttonId.Value.Id : "_")} {encodedState}";
}
#endregion
#region Plugins\DiscordSignLogger.Commands.cs
[ConsoleCommand("dsl.erase")]
private void EraseCommand(ConsoleSystem.Arg arg) {
if (!arg.IsAdmin) {
return;
}
NetworkableId id = arg.GetEntityID(0);
uint index = arg.GetUInt(1);
BaseEntity entity = BaseNetworkable.serverEntities.Find(id) as BaseEntity;
if (!entity) {
return;
}
switch (entity) {
case ISignage signage : {
uint[] textures = signage.GetTextureCRCs();
uint crc = textures[index];
if (crc != 0) {
FileStorage.server.RemoveExact(crc, FileStorage.Type.png, signage.NetworkID, index);
textures[index] = 0;
entity.SendNetworkUpdate();
HandleReplaceImage(signage, index);
}
break;
}
case PaintedItemStorageEntity item : {
if (item._currentImageCrc != 0) {
FileStorage.server.RemoveExact(item._currentImageCrc, FileStorage.Type.png, item.net.ID, 0);
item._currentImageCrc = 0;
item.SendNetworkUpdate();
}
break;
}
case PatternFirework firework : firework.Design?.Dispose();
firework.Design = null;
firework.SendNetworkUpdateImmediate();
break;
}
}
[ConsoleCommand("dsl.signblock")]
private void BanCommand(ConsoleSystem.Arg arg) {
if (!arg.IsAdmin) {
return;
}
ulong playerId = arg.GetULong(0);
float duration = arg.GetFloat(1);
_pluginData.AddSignBan(playerId, duration);
using PlaceholderData data = GetPlaceholderData();
data.ManualPool();
data.AddTimeSpan(TimeSpan.FromSeconds(duration));
if (duration <= 0) {
arg.ReplyWith($"{playerId} has been sign blocked permanently");
}
else {
arg.ReplyWith(_placeholders.ProcessPlaceholders($"{playerId} has been sign blocked for {DefaultKeys.Timespan.Formatted}", data));
}
SaveData();
}
[ConsoleCommand("dsl.signunblock")]
private void UnbanCommand(ConsoleSystem.Arg arg) {
if (!arg.IsAdmin) {
return;
}
ulong playerId = arg.GetULong(0);
_pluginData.RemoveSignBan(playerId);
SaveData();
arg.ReplyWith($"{playerId} has been unbanned");
}
private void HandleReplaceImage(ISignage signage, uint index) {
if (_pluginConfig.ReplaceImage.Mode == EraseMode.None || SignArtist is not {
IsLoaded : true
}
) {
return;
}
ReplaceImageSettings image = _pluginConfig.ReplaceImage;
if (signage is Signage) {
if (image.Mode == EraseMode.Text) {
SignArtist.Call("API_SignText", null, signage, image.Message, image.FontSize, image.TextColor, image.BodyColor, index);
}
else if (!string.IsNullOrEmpty(image.Url)) {
SignArtist.Call("API_SkinSign", null, signage, image.Url, _false, index);
}
}
else if (signage is PhotoFrame) {
if (!string.IsNullOrEmpty(image.Url)) {
SignArtist.Call("API_SkinPhotoFrame", null, signage, image.Url);
}
}
else if (signage is CarvablePumpkin) {
if (!string.IsNullOrEmpty(image.Url)) {
SignArtist.Call("API_SkinPumpkin", null, signage, image.Url);
}
}
}
#endregion
#region Plugins\DiscordSignLogger.Helpers.cs
public IPlayer FindPlayerById(string id) => covalence.Players.FindPlayerById(id);
public void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, _pluginData);
public void Puts(string format) => base.Puts(format);
#endregion
#region Plugins\DiscordSignLogger.Lang.cs
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
[LangKeys.Chat]
= $"<color=#bebebe>[<color={AccentColor}>{Title}</color>] {{0}}</color>", [LangKeys.NoPermission] = "You do not have permission to perform this action", [LangKeys.KickReason] = "Inappropriate sign/firework image", [LangKeys.BanReason] = "Inappropriate sign/firework image", [LangKeys.BlockedMessage] = $"You're not allowed to update this sign/firework because you have been blocked.Your block will expire in {DefaultKeys.Timespan.Formatted}.",
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
[LangKeys.Chat]
= $"<color=#bebebe>[<color={AccentColor}>{Title}</color>] {{0}}</color>", [LangKeys.NoPermission] = "У вас нет разрешения на выполнение этого действия", [LangKeys.KickReason] = "Недопустимое изображение знака/фейерверка", [LangKeys.BanReason] = "Недопустимое изображение знака/фейерверка", [LangKeys.BlockedMessage] = $"Возможность использовать изображения на знаке/феерверке для вас заблокирована.Разблокировка через {DefaultKeys.Timespan.Formatted}.",
}
, this, "ru");
}
public string Lang(string key, BasePlayer player = null) {
return lang.GetMessage(key, this, player ? player.UserIDString : null);
}
public string Lang(string key, BasePlayer player, PlaceholderData data) {
return _placeholders.ProcessPlaceholders(Lang(key, player), data);
}
public string Lang(string key, BasePlayer player = null, params object[] args) {
try {
return string.Format(Lang(key, player), args);
}
catch (Exception ex) {
PrintError($"Lang Key '{key}' threw exception\n:{ex}");
throw;
}
}
public void Chat(BasePlayer player, string key) => PrintToChat(player, Lang(LangKeys.Chat, player, Lang(key, player)));
public void Chat(BasePlayer player, string key, PlaceholderData data) => PrintToChat(player, Lang(LangKeys.Chat, player, Lang(key, player, data)));
#endregion
#region Plugins\DiscordSignLogger.Placeholders.cs
public void RegisterPlaceholders() {
_placeholders.RegisterPlaceholder<SignUpdateState, ulong>(this, PlaceholderKeys.EntityId, PlaceholderDataKeys.State, state => state.EntityId);
_placeholders.RegisterPlaceholder<SignUpdateState, string>(this, PlaceholderKeys.EntityName, PlaceholderDataKeys.State, state => GetEntityName(state.Entity));
_placeholders.RegisterPlaceholder<SignUpdateState, string>(this, PlaceholderKeys.ItemName, PlaceholderDataKeys.State, state => GetItemName(state.ItemId));
_placeholders.RegisterPlaceholder<string>(this, PlaceholderKeys.PlayerMessage, PlaceholderDataKeys.PlayerMessage);
_placeholders.RegisterPlaceholder<string>(this, PlaceholderKeys.ServerMessage, PlaceholderDataKeys.ServerMessage);
_placeholders.RegisterPlaceholder<string>(this, PlaceholderKeys.SignArtistUrl, PlaceholderDataKeys.SignArtistUrl);
_placeholders.RegisterPlaceholder<string>(this, PlaceholderKeys.Command, PlaceholderDataKeys.Command);
_placeholders.RegisterPlaceholder<string>(this, PlaceholderKeys.ButtonId, PlaceholderDataKeys.ButtonId);
_placeholders.RegisterPlaceholder<string>(this, PlaceholderKeys.PlayerId, PlaceholderDataKeys.PlayerId);
_placeholders.RegisterPlaceholder<SignUpdateState, bool>(this, PlaceholderKeys.IsOutside, PlaceholderDataKeys.State, state => state.Entity && state.Entity.IsOutside());
_placeholders.RegisterPlaceholder<TemplateKey>(this, PlaceholderKeys.MessageId, PlaceholderDataKeys.MessageId);
_placeholders.RegisterPlaceholder<StateKey, string>(this, PlaceholderKeys.MessageState, PlaceholderDataKeys.MessageState, state => state.State);
_placeholders.RegisterPlaceholder<SignUpdateState, string>(this, PlaceholderKeys.TextureIndex, PlaceholderDataKeys.State, state => {
if (state.Entity is ISignage signage && signage.GetTextureCRCs().Length <= 1) {
return null;
}
return StringCache<byte>.Instance.ToString(state.TextureIndex);
}
);
_placeholders.RegisterPlaceholder<SignUpdateState, GenericPosition>(this, PlaceholderKeys.Position, PlaceholderDataKeys.State, state => {
BaseEntity entity = state.Entity;
Vector3 pos = entity ? entity.transform.position : Vector3.zero;
return new GenericPosition(pos.x, pos.y, pos.z);
}
);
PlayerPlaceholders.RegisterPlaceholders(this, PlaceholderKeys.OwnerKeys, PlaceholderDataKeys.Owner);
}
public PlaceholderData GetPlaceholderData(SignUpdateState state, DiscordInteraction interaction) => GetPlaceholderData(state).AddInteraction(interaction);
public PlaceholderData GetPlaceholderData(SignUpdateState state) {
return GetPlaceholderData().AddPlayer(state.Player).Add(PlaceholderDataKeys.State, state).Add(PlaceholderDataKeys.Owner, state.Owner);
}
public PlaceholderData GetPlaceholderData(DiscordInteraction interaction) => GetPlaceholderData().AddInteraction(interaction);
public PlaceholderData GetPlaceholderData() {
return _placeholders.CreateData(this);
}
#endregion
#region Plugins\DiscordSignLogger.Templates.cs
public void RegisterTemplates() {
HashSet<string> messages = new();
foreach (SignMessage message in _pluginConfig.SignMessages) {
if (messages.Add(message.MessageId.Name)) {
_templates.RegisterGlobalTemplateAsync(this, message.MessageId, CreateDefaultTemplate(), new TemplateVersion(1, 0, 2), new TemplateVersion(1, 0, 2));
}
else {
PrintWarning($"Duplicate Message ID: '{message.MessageId.Name}'.Please check your config and correct the duplicate Sign Message ID's");
}
}
_templates.RegisterGlobalTemplateAsync(this, TemplateKeys.Action.Message, CreateActionMessage($"{DefaultKeys.User.Mention} ran command \"{PlaceholderKeys.Command}\"", DiscordColor.Blurple), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_buttonTemplates.RegisterGlobalTemplateAsync(this, TemplateKeys.Action.Button, new ButtonTemplate("Actions", ButtonStyle.Primary, BuildCustomId(ActionPrefix, PlaceholderKeys.MessageId, null, PlaceholderKeys.MessageState)), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
RegisterEn();
RegisterRu();
}
public void RegisterEn() {
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.NoPermission, CreateMessage("You do not have permission to perform this action", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Errors.FailedToParse, CreateMessage("An error occurred parsing button data", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Errors.ButtonIdNotFound, CreateMessage($"Failed to find button with id: {PlaceholderKeys.ButtonId}.Please validate the button exists in the config.", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Block.Success, CreateMessage($"{DefaultKeys.Player.Name} ({DefaultKeys.Player.Id}) has been sign blocked for {DefaultKeys.Timespan.Formatted}.", DiscordColor.Success), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Block.Errors.PlayerNotFound, CreateMessage($"Failed to find player with id: {PlaceholderKeys.PlayerId}", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Block.Errors.IsAlreadyBanned, CreateMessage($"{DefaultKeys.Player.Name} ({DefaultKeys.Player.Id}) is already banned", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Unblock.Success, CreateMessage($"You have removed {DefaultKeys.Player.Name} ({DefaultKeys.Player.Id}) sign block.", DiscordColor.Success), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Unblock.Errors.PlayerNotFound, CreateMessage($"Failed to find player with id: {PlaceholderKeys.PlayerId}", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Unblock.Errors.NotBanned, CreateMessage($"{DefaultKeys.Player.Name} ({DefaultKeys.Player.Id}) is not sign blocked.", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
}
public void RegisterRu() {
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.NoPermission, CreateMessage("У вас нет разрешения на выполнение этого действия", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0), "ru");
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Errors.FailedToParse, CreateMessage("Произошла ошибка при анализе данных кнопки.", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0), "ru");
_templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Errors.ButtonIdNotFound, CreateMessage($"Не удалось найти кнопку с идентификатором: {PlaceholderKeys.ButtonId}.Пожалуйста, проверьте наличие кнопки в файле конфигурации.", DiscordColor.Danger), new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0), "ru");
}
public DiscordMessageTemplate CreateMessage(string description, DiscordColor color) {
return new DiscordMessageTemplate {
Embeds = new List<DiscordEmbedTemplate> {
CreateEmbedTemplate(description, color)
}
}
;
}
public DiscordMessageTemplate CreateActionMessage(string description, DiscordColor color) {
return new DiscordMessageTemplate {
Embeds = new List<DiscordEmbedTemplate> {
CreateEmbedTemplate(description, color)
}
, Components = new List<BaseComponentTemplate> {
new ButtonTemplate("Image Message", ButtonStyle.Link, "discord://-/channels/{guild.id}/{channel.id}/{message.id}")
}
}
;
}
public DiscordEmbedTemplate CreateEmbedTemplate(string description, DiscordColor color) {
return new() {
Description = $"[{Title}] {description}", Color = color.ToHex(), Footer = GetFooterTemplate()
}
;
}
public DiscordMessageTemplate CreateDefaultTemplate() {
return new DiscordMessageTemplate {
Embeds = new List<DiscordEmbedTemplate> {
new() {
Title = $"{DefaultKeys.Server.Name}", Color = "#AC7061", ImageUrl = "attachment://image.png", TimeStamp = true, Fields = new List<DiscordEmbedFieldTemplate> {
new() {
Name = "Player:", Value = $"{DefaultKeys.Player.Name} ([{DefaultKeys.Player.Id}]({DefaultKeys.Player.SteamProfile}))", Inline = true
}
, new() {
Name = "Owner:", Value = $"{PlaceholderKeys.OwnerKeys.Name} ([{PlaceholderKeys.OwnerKeys.Id}]({PlaceholderKeys.OwnerKeys.SteamProfile}))", Inline = true
}
, new() {
Name = "Position:", Value = $"{PlaceholderKeys.Position}", Inline = true
}
, new() {
Name = "Item:", Value = $"{PlaceholderKeys.EntityName}", Inline = true
}
, new() {
Name = "Is Outside:", Value = $"{PlaceholderKeys.IsOutside}", Inline = true
}
, new() {
Name = "Texture Index", Value = $"{PlaceholderKeys.TextureIndex}", Inline = true, HideIfEmpty = true
}
, new() {
Name = "Sign Artist URL", Value = $"{PlaceholderKeys.SignArtistUrl}", Inline = true, HideIfEmpty = true
}
}
, Footer = GetFooterTemplate()
}
}
}
;
}
public EmbedFooterTemplate GetFooterTemplate() {
return new EmbedFooterTemplate {
Enabled = true, Text = $"{DefaultKeys.Plugin.Name} V{DefaultKeys.Plugin.Version} by {DefaultKeys.Plugin.Author}", IconUrl = "https://assets.umod.org/images/icons/plugin/61f1b7f6da7b6.png"
}
;
}
#endregion
#region Plugins\DiscordSignLogger.RustTranslationApi.cs
public string GetEntityName(BaseEntity entity) {
if (!entity.IsValid()) {
return string.Empty;
}
if (_prefabNameCache.TryGetValue(entity.prefabID, out string name)) {
return name;
}
if (RustTranslationAPI is {
IsLoaded : true
}
) {
name = RustTranslationAPI.Call<string>("GetDeployableTranslation", lang.GetServerLanguage(), entity.ShortPrefabName);
if (!string.IsNullOrEmpty(name)) {
_prefabNameCache[entity.prefabID] = name;
return name;
}
}
_prefabNameCache[entity.prefabID] = entity.ShortPrefabName;
return entity.ShortPrefabName;
}
public string GetItemName(int itemId) {
if (itemId == 0) {
return string.Empty;
}
if (_itemNameCache.TryGetValue(itemId, out string name)) {
return name;
}
if (RustTranslationAPI is {
IsLoaded : true
}
) {
name = RustTranslationAPI.Call<string>("GetItemTranslationByID", lang.GetServerLanguage(), itemId);
if (!string.IsNullOrEmpty(name)) {
_itemNameCache[itemId] = name;
return name;
}
}
name = ItemManager.FindItemDefinition(itemId).displayName.translated;
_itemNameCache[itemId] = name;
return name;
}
#endregion
#region Plugins\DiscordSignLogger.AppCommands.cs
public void RegisterApplicationCommands() {
ApplicationCommandBuilder builder = new ApplicationCommandBuilder(AppCommand.Command, "Discord Sign Logger Commands", ApplicationCommandType.ChatInput).AddDefaultPermissions(PermissionFlags.None);
AddBlockCommand(builder);
AddUnblockCommand(builder);
CommandCreate build = builder.Build();
DiscordCommandLocalization localization = builder.BuildCommandLocalization();
TemplateKey template = new("Command");
_local.RegisterCommandLocalizationAsync(this, template, localization, new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0)).Then(_ => {
_local.ApplyCommandLocalizationsAsync(this, build, template).Then(() => {
// ... [Code preview truncated due to length. Full source in attached file] ...