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

FREE DiscordDeath : 2.2.0 2.2.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Discord Death", "MJSU", "2.2.0")]
    [Description("Displays deaths to a discord channel")]
    internal class DiscordDeath : CovalencePlugin
    {
        #region Class Fields
        [PluginReference] private Plugin DeathNotes, PlaceholderAPI;

        private PluginConfig _pluginConfig;
        private DeathNotesConfiguration _deathNotesConfig;

        private const string DefaultUrl = "https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks";
        private const string EmptyField = "";

        private Hash<string, string> _weaponPrefabs;
        
        private enum DebugEnum
        {
            Message,
            None,
            Error,
            Warning,
            Info
        }
        
        public enum CombatEntityType
        {
            Helicopter = 0,
            Bradley = 1,
            Animal = 2,
            Murderer = 3,
            Scientist = 4,
            Player = 5,
            Trap = 6,
            Turret = 7,
            Barricade = 8,
            ExternalWall = 9,
            HeatSource = 10,
            Fire = 11,
            Lock = 12,
            Sentry = 13,
            Other = 14,
            None = 15,
            Scarecrow = 16,
            TunnelDweller = 17,
            UnderwaterDweller = 18,
            ZombieNPC = 19
        }

        private Action<IPlayer, StringBuilder, bool> _replacer;
        private readonly StringBuilder _parser = new StringBuilder();

        private DeathData _deathData;
        #endregion

        #region Setup & Loading
        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                [LangKeys.UnknownOwner] = "Unknown Owner",
            }, this);
        }

        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.DisplayOptions = config.DisplayOptions ?? new List<DisplayOption>
            {
                new DisplayOption
                {
                    WebhookUrl = DefaultUrl,
                    KillerType = "Player",
                    VictimType = "*"
                },
                new DisplayOption
                {
                    WebhookUrl = DefaultUrl,
                    KillerType = "Animal",
                    VictimType = "Player"
                }
            };

            config.DeathMessage = config.DeathMessage ?? new DiscordMessageConfig
            {
                Content = "",
                Embed = new EmbedConfig
                {
                    Color = "#ff6961",
                    Title = "{server.name}",
                    Enabled = true,
                    Url = string.Empty,
                    Image = string.Empty,
                    Thumbnail = string.Empty,
                    Description = ":crossed_swords: {discorddeath.message} :crossed_swords:",
                    Footer = new FooterConfig
                    {
                        IconUrl = string.Empty,
                        Text = string.Empty,
                        Enabled = true
                    },
                    Fields = new List<FieldConfig>
                    {
                        new FieldConfig
                        {
                            Title = "Victim Type",
                            Value = "{discorddeath.victim.type}",
                            Inline = true
                        },
                        new FieldConfig
                        {
                            Title = "Victim",
                            Value = "{discorddeath.victim.name}({discorddeath.victim.id})",
                            Inline = true
                        },      
                        new FieldConfig
                        {
                            Title = "Body Part",
                            Value = "{discorddeath.victim.bodypart}",
                            Inline = true
                        },
                        new FieldConfig
                        {
                            Title = "Attacker Type",
                            Value = "{discorddeath.killer.type}",
                            Inline = true
                        },
                        new FieldConfig
                        {
                            Title = "Attacker",
                            Value = "{discorddeath.killer.name} ({discorddeath.killer.id})",
                            Inline = true
                        }, 
                        new FieldConfig
                        {
                            Title = "Attacker Health",
                            Value = "{discorddeath.killer.health:0.00} HP",
                            Inline = true
                        },
                        new FieldConfig
                        {
                            Title = "Weapon",
                            Value = "{discorddeath.killer.weapon} ({discorddeath.killer.attachments})",
                            Inline = true
                        },  
                        new FieldConfig
                        {
                            Title = "Distance",
                            Value = "{discorddeath.killer.distance:0.00} Meters",
                            Inline = true
                        },  
                        new FieldConfig
                        {
                            Title = "Entity Owner",
                            Value = "{discorddeath.killer.owner}",
                            Inline = true
                        },  
                    }
                }
            };

            return config;
        }
        
        private void OnServerInitialized()
        {
            if (DeathNotes == null || !DeathNotes.IsLoaded)
            {
                PrintError("Missing plugin dependency DeathNotes: https://umod.org/plugins/death-notes");
            }
            else if (DeathNotes.Version < new VersionNumber(6, 3, 6))
            {
                PrintError("DeathNotes plugin must be version 6.3.6 or higher");
            }

            if (!IsPlaceholderApiLoaded())
            {
                PrintError("Missing plugin dependency PlaceholderAPI: https://umod.org/plugins/placeholder-api");
            } 
            else if(PlaceholderAPI.Version < new VersionNumber(2, 2, 0))
            {
                PrintError("Placeholder API plugin must be version 2.2.0 or higher");
            }
            
            foreach (DisplayOption option in _pluginConfig.DisplayOptions)
            {
                if (!string.IsNullOrEmpty(option.WebhookUrl) && option.WebhookUrl != DefaultUrl)
                {
                    option.WebhookUrl = option.WebhookUrl.Replace("/api/webhooks", "/api/v9/webhooks");
                }
            }

            LoadDeathNotesConfig();
            LoadWeaponPrefabs();
        }

        private void OnPluginLoaded(Plugin plugin)
        {
            if (plugin.Name == nameof(DeathNotes))
            {
                LoadDeathNotesConfig();
                LoadWeaponPrefabs();
            }
        }
        #endregion

        #region Death Hook
        private void OnDeathNotice(Dictionary<string, object> data, string message)
        {
            string victim = data["VictimEntityType"].ToString();
            string killer = data["KillerEntityType"].ToString();
            DisplayOption option = null;
            
            foreach (DisplayOption display in _pluginConfig.DisplayOptions)
            {
                if ((display.KillerType == "*" || display.KillerType.Equals(killer, StringComparison.OrdinalIgnoreCase) || (display.KillerType == "-" && killer.Equals("None", StringComparison.OrdinalIgnoreCase)))
                    && (display.VictimType == "*" || display.VictimType.Equals(victim, StringComparison.OrdinalIgnoreCase)  || (display.VictimType == "-" && victim.Equals("None", StringComparison.OrdinalIgnoreCase))))
                {
                    option = display;
                    break;
                }
            }
            
            if (option == null)
            {
                return;
            }

            DeathNotesData notesData = new DeathNotesData(data);
            _deathData = CreateDeathData(notesData, message);

            DiscordMessage discordMessage = ParseMessage(_pluginConfig.DeathMessage);
            
            SendDiscordMessage(option.WebhookUrl, discordMessage);
        }
        #endregion

        #region Death Notes
        public DeathData CreateDeathData(DeathNotesData data, string message)
        {
            DeathData death = new DeathData
            {
                VictimType = data.VictimEntityType.ToString(),
                VictimName = GetEntityName(data.VictimEntity, data.VictimEntityTypeRaw),
                Message = message
            };

            if (data.VictimEntity is BasePlayer && !data.VictimEntity.IsNpc)
            {
                death.VictimId = ((BasePlayer) data.VictimEntity).UserIDString;
            }
            else
            {
                death.VictimId = data.VictimEntity.net.ID.ToString();
            }

            if (data.KillerEntityType != CombatEntityType.None)
            {
                death.KillerType = data.KillerEntityType.ToString();
                death.KillerName = GetEntityName(data.KillerEntity, data.KillerEntityTypeRaw);
                death.BodyPart = GetBodyPart(data.HitInfo);

                if (data.KillerEntity is BasePlayer && !data.KillerEntity.IsNpc)
                {
                    death.KilledId = ((BasePlayer) data.KillerEntity).UserIDString;
                }
                else
                {
                    death.KilledId = data.KillerEntity.net.ID.ToString();
                }
                
                if (data.KillerEntity != null)
                {
                    death.Distance = data.KillerEntity.Distance(data.VictimEntity);
                    death.KillerHealth = data.KillerEntity.Health();
                    
                    if (death.KillerType.Equals("Player", StringComparison.OrdinalIgnoreCase))
                    {
                        death.Weapon = GetCustomizedWeaponName(data.HitInfo, data.DamageType);
                        death.Attachments = GetAttachments(data.HitInfo);
                    }
                    else if(death.KillerType.Equals("Turret", StringComparison.OrdinalIgnoreCase)
                            || death.KillerType.Equals("Lock", StringComparison.OrdinalIgnoreCase)
                            || death.KillerType.Equals("Trap", StringComparison.OrdinalIgnoreCase))
                    {
                        death.Owner = covalence.Players.FindPlayerById(data.KillerEntity.OwnerID.ToString())?.Name ?? Lang(LangKeys.UnknownOwner);
                    }
                }
            }

            death.EnsureNotEmpty();
            
            return death;
        }
        
        public string GetEntityName(BaseEntity entity, object type)
        {
            return DeathNotes?.Call<string>("GetCustomizedEntityName", entity, type);
        }

        public string GetBodyPart(HitInfo info)
        {
            return DeathNotes?.Call<string>("GetCustomizedBodypartName", info);
        }
        
        public string GetAttachments(HitInfo info)
        {
            string[] attachments = DeathNotes?.Call<string[]>("GetCustomizedAttachmentNames", info);
            if (attachments == null)
            {
                return string.Empty;
            }

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

        private string GetCustomizedWeaponName(HitInfo info, DamageType type)
        {
            string name = GetWeaponName(info, type);

            if (string.IsNullOrEmpty(name))
                return null;

            return _deathNotesConfig?.Translations.Weapons[name] ?? name;
        }

        public float GetDistance(float distance)
        {
            if (_deathNotesConfig.UseMetricDistance)
            {
                return distance * 3.28f;
            }

            return distance;
        }

        private string GetWeaponName(HitInfo info, DamageType type)
        {
            if (info == null)
            {
                return null;
            }

            Item item = info.Weapon?.GetItem();
            if (item != null)
            {
                return item.info.displayName.english;
            }

            //TODO: Find way to access flame
            //string prefab = info.Initiator?.GetComponent<Flame>()?.SourceEntity?.ShortPrefabName ?? info.WeaponPrefab?.ShortPrefabName;
            string prefab = info.WeaponPrefab?.ShortPrefabName;
            if (prefab != null)
            {
                if (_weaponPrefabs?.ContainsKey(prefab) ?? false)
                {
                    return _weaponPrefabs[prefab];
                }

                return prefab;
            }
            
            if (type == DamageType.Collision)
            {
                return "Vehicle";
            }

            return null;
        }

        public void LoadWeaponPrefabs()
        {
            if (IsDeathNotesLoaded())
            {
                _weaponPrefabs = Interface.Oxide.DataFileSystem.ReadObject<Hash<string, string>>($"{nameof(DeathNotes)}/WeaponPrefabs");
            }
        }
        
        public void LoadDeathNotesConfig()
        {
            if (IsDeathNotesLoaded())
            {
                string path = $"{Interface.Oxide.ConfigDirectory}/{nameof(DeathNotes)}.json";
                DynamicConfigFile newConfig = new DynamicConfigFile(path);
                if (newConfig.Exists())
                {
                    _deathNotesConfig = newConfig.ReadObject<DeathNotesConfiguration>();
                }
            }
        }
        
        private sealed class DeathNotesConfiguration
        {
            [JsonProperty("Translations")]
            public Translation Translations = new Translation();

            [JsonProperty("Use Metric Distance")]
            public bool UseMetricDistance = true;
            
            public class Translation
            {
                [JsonProperty("Names")]
                public Hash<string, string> Names = new Hash<string, string>();

                [JsonProperty("Bodyparts")]
                public Hash<string, string> Bodyparts = new Hash<string, string>();

                [JsonProperty("Weapons")]
                public Hash<string, string> Weapons = new Hash<string, string>();

                [JsonProperty("Attachments")]
                public Hash<string, string> Attachments = new Hash<string, string>();
            }
        }
        #endregion

        #region PlaceholderAPI
        private string ParseField(string field)
        {
            _parser.Length = 0;
            _parser.Append(field);
            GetReplacer()?.Invoke(null, _parser, false);
            return _parser.ToString();
        }
        
        private void OnPluginUnloaded(Plugin plugin)
        {
            if (plugin?.Name == "PlaceholderAPI")
            {
                _replacer = null;
            }
        }
        
        private void OnPlaceholderAPIReady()
        {
            RegisterPlaceholder("discorddeath.victim.type", (player, s) => _deathData.VictimType, "Displays the victim entity type");
            RegisterPlaceholder("discorddeath.victim.name", (player, s) => _deathData.VictimName, "Displays the victim entity name");
            RegisterPlaceholder("discorddeath.victim.id", (player, s) => _deathData.VictimId, "Displays the victim steam if player else entity id");
            RegisterPlaceholder("discorddeath.victim.bodypart", (player, s) => _deathData.BodyPart, "Displays the victim body part");
            RegisterPlaceholder("discorddeath.killer.type", (player, s) => _deathData.KillerType, "Displays the killer entity type");
            RegisterPlaceholder("discorddeath.killer.id", (player, s) => _deathData.KilledId, "Displays the killer steam if player else entity id");
            RegisterPlaceholder("discorddeath.killer.name", (player, s) => _deathData.KillerName, "Displays the killer entity name");
            RegisterPlaceholder("discorddeath.killer.weapon", (player, s) => _deathData.Weapon, "Displays the killer's weapon");
            RegisterPlaceholder("discorddeath.killer.attachments", (player, s) => _deathData.Attachments, "Displays the killer's attachments");
            RegisterPlaceholder("discorddeath.killer.health", (player, s) => _deathData.KillerHealth, "Displays the killer's health");
            RegisterPlaceholder("discorddeath.killer.owner", (player, s) => _deathData.Owner, "Displays the Owners name of the entity that did the killing");
            RegisterPlaceholder("discorddeath.killer.distance", (player, s) => GetDistance(_deathData.Distance), "Displays the distance to the victim the attacker was");
            RegisterPlaceholder("discorddeath.message", (player, s) => Formatter.ToPlaintext(StripRustTags(_deathData.Message)), "Displays the default death message");
        }

        private void RegisterPlaceholder(string key, Func<IPlayer, string, object> action, string description = null)
        {
            if (IsPlaceholderApiLoaded())
            {
                PlaceholderAPI.Call("AddPlaceholder", this, key, action, description);
            }
        }

        private Action<IPlayer, StringBuilder, bool> GetReplacer()
        {
            if (!IsPlaceholderApiLoaded())
            {
                return _replacer;
            }
            
            return _replacer ?? (_replacer = PlaceholderAPI.Call<Action<IPlayer, StringBuilder, bool>>("GetProcessPlaceholders", 1));
        }

        private bool IsPlaceholderApiLoaded() => PlaceholderAPI != null && PlaceholderAPI.IsLoaded;
        private bool IsDeathNotesLoaded() => DeathNotes != null && DeathNotes.IsLoaded;
        #endregion

        #region Rust Tag Stripping
        private readonly List<Regex> _regexTags = new List<Regex>
        {
            new Regex("<color=.+?>", RegexOptions.Compiled),
            new Regex("<size=.+?>", RegexOptions.Compiled)
        };

        private readonly List<string> _tags = new List<string>
        {
            "</color>",
            "</size>",
            "<i>",
            "</i>",
            "<b>",
            "</b>"
        };

        private string StripRustTags(string original)
        {
            if (string.IsNullOrEmpty(original))
            {
                return string.Empty;
            }

            for (int index = 0; index < _tags.Count; index++)
            {
                string tag = _tags[index];
                original = original.Replace(tag, string.Empty);
            }

            for (int index = 0; index < _regexTags.Count; index++)
            {
                Regex regexTag = _regexTags[index];
                original = regexTag.Replace(original, string.Empty);
            }

            return original;
        }
        #endregion

        #region Helpers
        private void Debug(DebugEnum level, string message)
        {
            if (level <= _pluginConfig.DebugLevel)
            {
                Puts($"{level}: {message}");
            }
        }
        
        public string Lang(string key, IPlayer player = null, params object[] args)
        {
            try
            {
                return string.Format(lang.GetMessage(key, this, player?.Id), args);
            }
            catch(Exception ex)
            {
                PrintError($"Lang Key '{key}' threw exception\n:{ex}");
                throw;
            }
        }
        #endregion

        #region Classes
        private class PluginConfig
        {
            [JsonProperty("Display Options")]
            public List<DisplayOption> DisplayOptions { get; set; }

            [JsonProperty(PropertyName = "Death Message")]
            public DiscordMessageConfig DeathMessage { get; set; }
            
            [JsonConverter(typeof(StringEnumConverter))]
            [DefaultValue(DebugEnum.Warning)]
            [JsonProperty(PropertyName = "Debug Level (None, Error, Warning, Info)")]
            public DebugEnum DebugLevel { get; set; }
        }

        public class DisplayOption
        {
            [JsonProperty(PropertyName = "Webhook url")]
            public string WebhookUrl { get; set; }
            
            [JsonProperty(PropertyName = "Killer Type")]
            public string KillerType { get; set; }
            
            [JsonProperty(PropertyName = "Victim Type")]
            public string VictimType { get; set; }
        }

        public class DeathNotesData
        {
            public object VictimEntityTypeRaw { get; set; }
            public CombatEntityType VictimEntityType { get; set; }
            public BaseEntity VictimEntity { get; set; }
            
            public object KillerEntityTypeRaw { get; set; }
            public CombatEntityType KillerEntityType { get; set; }
            public BaseEntity KillerEntity { get; set; }
            public DamageType DamageType { get; set; }
            
            public HitInfo HitInfo { get; set; }
            
            public DeathNotesData(Dictionary<string,object> data)
            {
                if (data.ContainsKey("VictimEntityType"))
                {
                    VictimEntityTypeRaw = data["VictimEntityType"];
                    VictimEntityType = (CombatEntityType)(int)VictimEntityTypeRaw;
                }
                
                if (data.ContainsKey("VictimEntity"))
                {
                    VictimEntity = data["VictimEntity"] as BaseEntity;
                }
                
                if (data.ContainsKey("KillerEntityType"))
                {
                    KillerEntityTypeRaw = data["KillerEntityType"];
                    KillerEntityType = (CombatEntityType)(int)KillerEntityTypeRaw;
                }
                
                if (data.ContainsKey("KillerEntity"))
                {
                    KillerEntity = data["KillerEntity"] as BaseEntity;
                }
                
                if (data.ContainsKey("DamageType"))
                {
                    DamageType = (DamageType)data["DamageType"];
                }
                
                if (data.ContainsKey("HitInfo"))
                {
                    HitInfo = data["HitInfo"] as HitInfo;
                }
            }
        }
        
        public class DeathData
        {
            public string VictimName { get; set; }
            public string VictimId { get; set; }
            public string VictimType { get; set; }
            public string KillerName { get; set; }
            public string KilledId { get; set; }
            public string KillerType { get; set; }
            public float KillerHealth { get; set; }
            public string Weapon { get; set; }
            public string BodyPart { get; set; }
            public string Attachments { get; set; }
            public float Distance { get; set; }
            public string Owner { get; set; }
            public string Message { get; set; }

            public void EnsureNotEmpty()
            {
                if (string.IsNullOrEmpty(VictimName))
                {
                    VictimName = EmptyField;
                }
                
                if (string.IsNullOrEmpty(VictimId))
                {
                    VictimId = EmptyField;
                }
                
                if (string.IsNullOrEmpty(VictimType))
                {
                    VictimType = EmptyField;
                }

                if (string.IsNullOrEmpty(KillerName))
                {
                    KillerName = EmptyField;
                }
                
                if (string.IsNullOrEmpty(KilledId))
                {
                    KilledId = EmptyField;
                }
                
                if (string.IsNullOrEmpty(KillerType))
                {
                    KillerType = EmptyField;
                }
                
                if (string.IsNullOrEmpty(Weapon))
                {
                    Weapon = EmptyField;
                }

                if (string.IsNullOrEmpty(BodyPart))
                {
                    BodyPart = EmptyField;
                }
                
                if (string.IsNullOrEmpty(Owner))
                {
                    Owner = EmptyField;
                }
                
                if (string.IsNullOrEmpty(Message))
                {
                    Message = EmptyField;
                }
            }
        }
        
        private static class LangKeys
        {
            public const string UnknownOwner = nameof(UnknownOwner);
        }
        #endregion

        #region Discord Embed
        #region Send Embed Methods
        /// <summary>
        /// Headers when sending an embedded message
        /// </summary>
        private readonly Dictionary<string, string> _headers = new Dictionary<string, string>()
        {
            {"Content-Type", "application/json"}
        };

        /// <summary>
        /// Sends the DiscordMessage to the specified webhook url
        /// </summary>
        /// <param name="url">Webhook url</param>
        /// <param name="message">Message being sent</param>
        private void SendDiscordMessage(string url, DiscordMessage message)
        {
            if (string.IsNullOrEmpty(url) || url == DefaultUrl)
            {
                Debug(DebugEnum.Warning, "Webhook URL not set.Please set url in config.");
                return;
            }
            
            string json = message.ToJson();
            if (_pluginConfig.DebugLevel >= DebugEnum.Info)
            {
                Debug(DebugEnum.Info, $"SendDiscordMessage - ToJson \n{json}");
            }

            webrequest.Enqueue(url, json, SendDiscordMessageCallback, this, RequestMethod.POST, _headers);
        }

        /// <summary>
        /// Callback when sending the embed if any errors occured
        /// </summary>
        /// <param name="code">HTTP response code</param>
        /// <param name="message">Response message</param>
        private void SendDiscordMessageCallback(int code, string message)
        {
            if (code != 204)
            {
                PrintError(message);
            }
        }
        #endregion

        #region Helper Methods
        private const string OwnerIcon = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/47/47db946f27bc76d930ac82f1656f7a10707bb67d_full.jpg";

        private void AddPluginInfoFooter(Embed embed)
        {
            embed.AddFooter($"{Title} V{Version} by {Author}", OwnerIcon);
        }
        #endregion

        #region Embed Classes
        private class DiscordMessage
        {
            /// <summary>
            /// The name of the user sending the message changing this will change the webhook bots name
            /// </summary>
            [JsonProperty("username")]
            private string Username { get; set; }

            /// <summary>
            /// The avatar url of the user sending the message changing this will change the webhook bots avatar
            /// </summary>
            [JsonProperty("avatar_url")]
            private string AvatarUrl { get; set; }

            /// <summary>
            /// String only content to be sent
            /// </summary>
            [JsonProperty("content")]
            private string Content { get; set; }

            /// <summary>
            /// Embeds to be sent
            /// </summary>
            [JsonProperty("embeds")]
            private List<Embed> Embeds { get; }

            public DiscordMessage(string username = null, string avatarUrl = null)
            {
                Username = username;
                AvatarUrl = avatarUrl;
                Embeds = new List<Embed>();
            }

            public DiscordMessage(string content, string username = null, string avatarUrl = null)
            {
                Content = content;
                Username = username;
                AvatarUrl = avatarUrl;
                Embeds = new List<Embed>();
            }

            public DiscordMessage(Embed embed, string username = null, string avatarUrl = null)
            {
                Embeds = new List<Embed> {embed};
                Username = username;
                AvatarUrl = avatarUrl;
            }

            /// <summary>
            /// Adds a new embed to the list of embed to send
            /// </summary>
            /// <param name="embed">Embed to add</param>
            /// <returns>This</returns>
            /// <exception cref="IndexOutOfRangeException">Thrown if more than 10 embeds are added in a send as that is the discord limit</exception>
            public DiscordMessage AddEmbed(Embed embed)
            {
                if (Embeds.Count >= 10)
                {
                    throw new IndexOutOfRangeException("Only 10 embed are allowed per message");
                }

                Embeds.Add(embed);
                return this;
            }

            /// <summary>
            /// Adds string content to the message
            /// </summary>
            /// <param name="content"></param>
            /// <returns></returns>
            public DiscordMessage AddContent(string content)
            {
                Content = content;
                return this;
            }

            /// <summary>
            /// Changes the username and avatar image for the bot sending the message
            /// </summary>
            /// <param name="username">username to change</param>
            /// <param name="avatarUrl">avatar img url to change</param>
            /// <returns>This</returns>
            public DiscordMessage AddSender(string username, string avatarUrl)
            {
                Username = username;
                AvatarUrl = avatarUrl;
                return this;
            }

            /// <summary>
            /// Returns message as JSON to be sent in the web request
            /// </summary>
            /// <returns></returns>
            public string ToJson() => JsonConvert.SerializeObject(this, Formatting.None,
                new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
        }

        private class Embed
        {
            /// <summary>
            /// Color of the left side bar of the embed message
            /// </summary>
            [JsonProperty("color")]
            private int Color { get; set; }

            /// <summary>
            /// Fields to be added to the embed message
            /// </summary>
            [JsonProperty("fields")]
            private List<Field> Fields { get; } = new List<Field>();

            /// <summary>
            /// Title of the embed message
            /// </summary>
            [JsonProperty("title")]
            private string Title { get; set; }

            /// <summary>
            /// Description of the embed message
            /// </summary>
            [JsonProperty("description")]
            private string Description { get; set; }

            /// <summary>
            /// Description of the embed message
            /// </summary>
            [JsonProperty("url")]
            private string Url { get; set; }

            /// <summary>
            /// Image to added to the embed message.Appears at the bottom of the message above the footer
            /// </summary>
            [JsonProperty("image")]
            private Image Image { get; set; }

            /// <summary>
            /// Thumbnail image added to the embed message.Appears in the top right corner
            /// </summary>
            [JsonProperty("thumbnail")]
            private Image Thumbnail { get; set; }

            /// <summary>
            /// Video to add to the embed message
            /// </summary>
            [JsonProperty("video")]
            private Video Video { get; set; }

            /// <summary>
            /// Author to add to the embed message.Appears above the title./// </summary>
            [JsonProperty("author")]
            private AuthorInfo Author { get; set; }

            /// <summary>
            /// Footer to add to the embed message.Appears below all content./// </summary>
            [JsonProperty("footer")]
            private Footer Footer { get; set; }

            /// <summary>
            /// Adds a title to the embed message
            /// </summary>
            /// <param name="title">Title to add</param>
            /// <returns>This</returns>
            public Embed AddTitle(string title)
            {
                Title = title;
                return this;
            }

            /// <summary>
            /// Adds a description to the embed message
            /// </summary>
            /// <param name="description">description to add</param>
            /// <returns>This</returns>
            public Embed AddDescription(string description)
            {
                Description = description;
                return this;
            }

            /// <summary>
            /// Adds a url to the embed message
            /// </summary>
            /// <param name="url"></param>
            /// <returns>This</returns>
            public Embed AddUrl(string url)
            {
                Url = url;
                return this;
            }

            /// <summary>
            /// Adds an author to the embed message.The author will appear above the title
            /// </summary>
            /// <param name="name">Name of the author</param>
            /// <param name="iconUrl">Icon Url to use for the author</param>
            /// <param name="url">Url to go to when the authors name is clicked on</param>
            /// <param name="proxyIconUrl">Backup icon url.Can be left null if you only have one icon url</param>
            /// <returns>This</returns>
            public Embed AddAuthor(string name, string iconUrl = null, string url = null, string proxyIconUrl = null)
            {
                Author = new AuthorInfo(name, iconUrl, url, proxyIconUrl);
                return this;
            }

            /// <summary>
            /// Adds a footer to the embed message
            /// </summary>
            /// <param name="text">Text to be added to the footer</param>
            /// <param name="iconUrl">Icon url to add in the footer.Appears to the left of the text</param>
            /// <param name="proxyIconUrl">Backup icon url.Can be left null if you only have one icon url</param>
            /// <returns>This</returns>
            public Embed AddFooter(string text, string iconUrl = null, string proxyIconUrl = null)
            {
                Footer = new Footer(text, iconUrl, proxyIconUrl);

                return this;
            }

            /// <summary>
            /// Adds an int based color to the embed.Color appears as a bar on the left side of the message
            /// </summary>
            /// <param name="color"></param>
            /// <returns></returns>
            public Embed AddColor(int color)
            {
                if (color < 0x0 || color > 0xFFFFFF)
                {
                    throw new Exception($"Color '{color}' is outside the valid color range");
                }

                Color = color;
                return this;
            }

            /// <summary>
            /// Adds a hex based color.Color appears as a bar on the left side of the message
            /// </summary>
            /// <param name="color">Color in string hex format</param>
            /// <returns>This</returns>
            /// <exception cref="Exception">Exception thrown if color is outside of range</exception>
            public Embed AddColor(string color)
            {
                int parsedColor = int.Parse(color.TrimStart('#'), NumberStyles.AllowHexSpecifier);
                if (parsedColor < 0x0 || parsedColor > 0xFFFFFF)
                {
                    throw new Exception($"Color '{color}' is outside the valid color range");
                }

                Color = parsedColor;
                return this;
            }

            /// <summary>
            /// Adds a RGB based color.Color appears as a bar on the left side of the message
            /// </summary>
            /// <param name="red">Red value between 0 - 255</param>
            /// <param name="green">Green value between 0 - 255</param>
            /// <param name="blue">Blue value between 0 - 255</param>
            /// <returns>This</returns>
            /// <exception cref="Exception">Thrown if red, green, or blue is outside of range</exception>
            public Embed AddColor(int red, int green, int blue)
            {
                if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255)
                {
                    throw new Exception($"Color Red:{red} Green:{green} Blue:{blue} is outside the valid color range.Must be between 0 - 255");
                }

                Color = red * 65536 + green * 256 + blue;
                ;
                return this;
            }

            /// <summary>
            /// Adds a blank field./// If inline it will add a blank column./// If not inline will add a blank row
            /// </summary>
            /// <param name="inline">If the field is inline</param>
            /// <returns>This</returns>
            public Embed AddBlankField(bool inline)
            {
                Fields.Add(new Field("", "", inline));
                return this;
            }

            /// <summary>
            /// Adds a new field with the name as the title and value as the value./// If inline will add a new column.If row will add in a new row./// </summary>
            /// <param name="name"></param>
            /// <param name="value"></param>
            /// <param name="inline"></param>
            /// <returns></returns>
            public Embed AddField(string name, string value, bool inline)
            {
                Fields.Add(new Field(name, value, inline));
                return this;
            }

            /// <summary>
            /// Adds an image to the embed.The url should point to the url of the image./// If using attachment image you can make the url: "attachment://{image name}.{image extension}
            /// </summary>
            /// <param name="url">Url for the image</param>
            /// <param name="width">width of the image</param>
            /// <param name="height">height of the image</param>
            /// <param name="proxyUrl">Backup url for the image</param>
            /// <returns></returns>
            public Embed AddImage(string url, int? width = null, int? height = null, string proxyUrl = null)
            {
                Image = new Image(url, width, height, proxyUrl);
                return this;
            }

            /// <summary>
            /// Adds a thumbnail in the top right corner of the embed
            /// If using attachment image you can make the url: "attachment://{image name}.{image extension}
            /// </summary>
            /// <param name="url">Url for the image</param>
            /// <param name="width">width of the image</param>
            /// <param name="height">height of the image</param>
            /// <param name="proxyUrl">Backup url for the image</param>
            /// <returns></returns>
            public Embed AddThumbnail(string url, int? width = null, int? height = null, string proxyUrl = null)
            {
                Thumbnail = new Image(url, width, height, proxyUrl);
                return this;
            }

            /// <summary>
            /// Adds a video to the embed
            /// </summary>
            /// <param name="url">Url for the video</param>
            /// <param name="width">Width of the video</param>
            /// <param name="height">Height of the video</param>
            /// <returns></returns>
            public Embed AddVideo(string url, int? width = null, int? height = null)
            {
                Video = new Video(url, width, height);
                return this;
            }
        }

        /// <summary>
        /// Field for and embed message
        /// </summary>
        private class Field
        {
            /// <summary>
            /// Name of the field
            /// </summary>
            [JsonProperty("name")]
            private string Name { get; }

            /// <summary>
            /// Value for the field
            /// </summary>
            [JsonProperty("value")]
            private string Value { get; }

            /// <summary>
            /// If the field should be in the same row or a new row
            /// </summary>
            [JsonProperty("inline")]
            private bool Inline { get; }

            public Field(string name, string value, bool inline)
            {
                Name = name;
                Value = value;
                Inline = inline;
            }
        }

        /// <summary>
        /// Image for an embed message
        /// </summary>
        private class Image
        {
            /// <summary>
            /// Url for the image
            /// </summary>
            [JsonProperty("url")]
            private string Url { get; }

            /// <summary>
            /// Width for the image
            /// </summary>
            [JsonProperty("width")]
            private int? Width { get; }

            /// <summary>
            /// Height for the image
            /// </summary>
            [JsonProperty("height")]
            private int? Height { get; }

            /// <summary>
            /// Proxy url for the image
            /// </summary>
            [JsonProperty("proxyURL")]
            private string ProxyUrl { get; }

            public Image(string url, int? width, int? height, string proxyUrl)
            {
                Url = url;
                Width = width;
                Height = height;
                ProxyUrl = proxyUrl;
            }
        }

        /// <summary>
        /// Video for an embed message
        /// </summary>
        private class Video
        {
            /// <summary>
            /// Url to the video
            /// </summary>
            [JsonProperty("url")]
            private string Url { get; }

            /// <summary>
            /// Width of the video
            /// </summary>
            [JsonProperty("width")]
            private int? Width { get; }

            /// <summary>
            /// Height of the video
            /// </summary>
            [JsonProperty("height")]
            private int? Height { get; }

            public Video(string url, int? width, int? height)
            {
                Url = url;
                Width = width;
                Height = height;
            }
        }

        /// <summary>
        /// Author of an embed message
        /// </summary>
        private class AuthorInfo
        {
            /// <summary>
            /// Name of the author
            /// </summary>
            [JsonProperty("name")]
            private string Name { get; }

            /// <summary>
            /// Url to go to when clicking on the authors name
            /// </summary>
            [JsonProperty("url")]
            private string Url { get; }

            /// <summary>
            /// Icon url for the author
            /// </summary>
            [JsonProperty("icon_url")]
            private string IconUrl { get; }

            /// <summary>
            /// Proxy icon url for the author
            /// </summary>
            [JsonProperty("proxy_icon_url")]
            private string ProxyIconUrl { get; }

            public AuthorInfo(string name, string iconUrl, string url, string proxyIconUrl)
            {
                Name = name;
                Url = url;
                IconUrl = iconUrl;
                ProxyIconUrl = proxyIconUrl;
            }
        }

        /// <summary>
        /// Footer for an embed message
        /// </summary>
        private class Footer
        {
            /// <summary>
            /// Text for the footer
            /// </summary>
            [JsonProperty("text")]
            private string Text { get; }

            /// <summary>
            /// Icon url for the footer
            /// </summary>
            [JsonProperty("icon_url")]
            private string IconUrl { get; }

            /// <summary>
            /// Proxy icon url for the footer
            /// </summary>
            [JsonProperty("proxy_icon_url")]
            private string ProxyIconUrl { get; }

            public Footer(string text, string iconUrl, string proxyIconUrl)
            {
                Text = text;
                IconUrl = iconUrl;
                ProxyIconUrl = proxyIconUrl;
            }
        }
        #endregion

        #region Attachment Classes
        /// <summary>
        /// Enum for attachment content type
        /// </summary>
        private enum AttachmentContentType
        {
            Png,
            Jpg
        }

        private class Attachment
        {
            /// <summary>
            /// Attachment data
            /// </summary>
            public byte[] Data { get; }

            /// <summary>
            /// File name for the attachment./// Used in the url field of an image
            /// </summary>
            public string Filename { get; }

            /// <summary>
            /// Content type for the attachment
            /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
            /// </summary>
            public string ContentType { get; }

            public Attachment(byte[] data, string filename, AttachmentContentType contentType)
            {
                Data = data;
                Filename = filename;

                switch (contentType)
                {
                    case AttachmentContentType.Jpg:
                        ContentType = "image/jpeg";
                        break;

                    case AttachmentContentType.Png:
                        ContentType = "image/png";
                        break;
                }
            }

            public Attachment(byte[] data, string filename, string contentType)
            {
                Data = data;
                Filename = filename;
                ContentType = contentType;
            }
        }
        #endregion

        #region Config Classes
        private class DiscordMessageConfig
        {
            public string Content { get; set; }
            public EmbedConfig Embed { 
// ... [Code preview truncated due to length. Full source in attached file] ...

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