C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
/*
* Different message support between FacePunch Forced wipes & Manually wiped
* Includes Automation systems
* Includes test console command
*/
namespace Oxide.Plugins {
[Info("Discord Wipe Embed", "Khan", "1.0.0")]
[Description("Posts an embed to Discord upon wipe.")]
public class DiscordWipeEmbed : RustPlugin {
#region License Agreement (EULA) of Discord Wipe Embed
/*
End-User License Agreement (EULA) of Discord Wipe Embed
This End-User License Agreement ("EULA") is a legal agreement between you and Kyle.This EULA agreement
governs your acquisition and use of our Discord Wipe Embed plugin ("Software") directly from Kyle.Please read this EULA agreement carefully before downloading and using the Discord Wipe Embed plugin.It provides a license to use the Discord Wipe Embed and contains warranty information and liability disclaimers.If you are entering into this EULA agreement on behalf of a company or other legal entity, you represent that you have the authority
to bind such entity and its affiliates to these terms and conditions.If you do not have such authority or if you do not agree with the
terms and conditions of this EULA agreement, DO NOT purchase or download the Software.This EULA agreement shall apply only to the Software supplied by Kyle Farris regardless of whether other software is referred
to or described herein.The terms also apply to any David updates, supplements, Internet-based services, and support services for the Software,
unless other terms accompany those items on delivery.If so, those terms apply.License Grant
Kyle hereby grants you a personal, non-transferable, non-exclusive license to use the Discord Wipe Embed software on your devices in
accordance with the terms of this EULA agreement.You are permitted to load the Discord Wipe Embed on your personal server owned by you.You are not permitted to:
Edit, alter, modify, adapt, translate or otherwise change the whole or any part of the Software nor permit the whole or any part
of the Software to be combined with or become incorporated in any other software, nor decompile, disassemble or reverse
engineer the Software or attempt to do any such things.Reproduce, copy, distribute, resell or otherwise use the Software for any commercial purpose
Allow any third party to use the Software on behalf of or for the benefit of any third party
Use the Software in any way which breaches any applicable local, national or international law
use the Software for any purpose that Kyle considers is a breach of this EULA agreement
Intellectual Property and Ownership
Kyle shall at all times retain ownership of the Software as originally downloaded by you and all subsequent downloads of the Software by you.The Software (and the copyright, and other intellectual property rights of whatever nature in the Software, including any modifications made thereto) are and shall remain the property of Kyle.Termination
This EULA agreement is effective from the date you first use the Software and shall continue until terminated.You may terminate it at any time upon written notice to Kyle.It will also terminate immediately if you fail to comply with any term of this EULA agreement.Upon such termination, the licenses granted by this EULA agreement will immediately terminate and you agree to stop all access and use of the Software.The provisions that by their nature continue and survive will survive any termination of this EULA agreement.*/
#endregion
#region Declarations
readonly Dictionary<string, string> _headers = new Dictionary<string, string> {
{
"Content-Type", "application/json"
}
}
;
private PluginConfig _cfg;
private bool _onnewsave;
private bool _forceWipe = DateTime.Today.DayOfWeek == DayOfWeek.Thursday && DateTime.Today.Day <= 7;
#endregion
#region Configuration
internal class PluginConfig : SerializableConfiguration {
[JsonProperty("wipediscord")]
public string ConsoleCommand = "sendwipe";
[JsonProperty("Disable Auto Send (Requires you to manually use console command to send embed message)")]
public bool DisableAuto = false;
public ConnectionInfo ConnectionInfo = new ConnectionInfo();
public Discord ForceWipe = new Discord() {
AtEveryone = true, EmbedHexColour = "#ffc348", EmbedImageUrl = "https://gspics.org/images/2023/04/21/0Pti87.png",
}
;
public Discord Manually = new Discord() {
AtEveryone = true, EmbedHexColour = "#ffc348", EmbedImageUrl = "https://gspics.org/images/2023/04/21/0Pti87.png",
}
;
[JsonProperty("Do not touch.")]
public WipedDates WipedDates = new WipedDates();
}
public class ConnectionInfo {
public string DiscordWebhookURL = "https://discord.com/api/webhooks/1088427781307768933/OuuTdLO8uBTqo0QfQvN6qPXXr82EUtHHVEeFZshEqQLzKUnDKY_Kok-0dh7II_Kj7hwP";
public string ServerName = "[UA] UKRAINIAN | RUST | CLASSIC | WIPE 28.04";
public string RustServerIp = "162.55.97.229:20700";
public string DonationURL = "ukrainianrust.gamestores.app";
}
public class Discord {
public bool AtEveryone;
public string EmbedHexColour;
[JsonProperty("https://imgur.com/gallery/wFdW8Nd")]
public string EmbedImageUrl;
}
public class WipedDates {
public string WipedTime = DateTime.Now.ToString("Понедельник, 27 март 2023 19:00");
public string LastWipe = DateTime.Now.ToString("Понедельник, 27 март 2023 19:00");
}
#region Updater
internal class SerializableConfiguration {
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonHelper.Deserialize(ToJson()) as Dictionary<string, object>;
}
private static class JsonHelper {
public static object Deserialize(string json) => ToObject(JToken.Parse(json));
private static object ToObject(JToken token) {
switch (token.Type) {
case JTokenType.Object : return token.Children<JProperty>().ToDictionary(prop => prop.Name, prop => ToObject(prop.Value));
case JTokenType.Array : return token.Select(ToObject).ToList();
default : return ((JValue) token).Value;
}
}
}
private bool MaybeUpdateConfig(SerializableConfiguration config) {
var currentWithDefaults = config.ToDictionary();
var currentRaw = Config.ToDictionary(x => x.Key, x => x.Value);
return MaybeUpdateConfigDict(currentWithDefaults, currentRaw);
}
private bool MaybeUpdateConfigDict(Dictionary<string, object> currentWithDefaults, Dictionary<string, object> currentRaw) {
bool changed = false;
foreach (var key in currentWithDefaults.Keys) {
object currentRawValue;
if (currentRaw.TryGetValue(key, out currentRawValue)) {
var defaultDictValue = currentWithDefaults[key] as Dictionary<string, object>;
var currentDictValue = currentRawValue as Dictionary<string, object>;
if (defaultDictValue != null) {
if (currentDictValue == null) {
currentRaw[key] = currentWithDefaults[key];
changed = true;
}
else if (MaybeUpdateConfigDict(defaultDictValue, currentDictValue)) changed = true;
}
}
else {
currentRaw[key] = currentWithDefaults[key];
changed = true;
}
}
return changed;
}
#endregion
protected override void LoadDefaultConfig() => _cfg = new PluginConfig();
protected override void LoadConfig() {
base.LoadConfig();
try {
_cfg = Config.ReadObject<PluginConfig>();
if (_cfg == null) {
LoadDefaultConfig();
PrintWarning($"Generating Config File for {Title}");
throw new JsonException();
}
if (MaybeUpdateConfig(_cfg)) {
PrintWarning($"{Title} Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch (Exception ex) {
PrintWarning($"{Title} Failed to load config file (Please Verify Formatting) (" + ex.Message + ")");
}
}
protected override void SaveConfig() => Config.WriteObject(_cfg, true);
#endregion
#region Hooks
private void Init() {
if (_cfg.DisableAuto) Unsubscribe(nameof(OnNewSave));
}
[UsedImplicitly]
private void OnNewSave(string filename) => _onnewsave = true;
private void OnServerInitialized() {
cmd.AddConsoleCommand(_cfg.ConsoleCommand, this, nameof(ConsoleCmdManual));
if (!_onnewsave) return;
_cfg.WipedDates.LastWipe = _cfg.WipedDates.WipedTime;
_cfg.WipedDates.WipedTime = DateTime.Now.ToString("f");
OnNewSaveDelay();
}
#endregion
#region Console Command
private void ConsoleCmdManual(ConsoleSystem.Arg arg) {
if (!arg.IsRcon) return;
OnNewSaveDelay();
}
#endregion
#region Helper
private void OnNewSaveDelay() {
var seed = ConVar.Server.seed.ToString();
var worldSize = ConVar.Server.worldsize.ToString();
var color = _forceWipe ? _cfg.ForceWipe.EmbedHexColour : _cfg.Manually.EmbedHexColour;
var fault = $"https://rustmaps.com/monuments/{worldSize}_{seed}";
var icon = _forceWipe ? string.IsNullOrEmpty(_cfg.ForceWipe.EmbedImageUrl) ? fault : _cfg.ForceWipe.EmbedImageUrl : string.IsNullOrEmpty(_cfg.Manually.EmbedImageUrl) ? fault : _cfg.Manually.EmbedImageUrl;
Embed embed = BuildMessage();
if (!string.IsNullOrEmpty(_cfg.ConnectionInfo.DonationURL)) embed.AddTitle("Server").AddDescription($"{_cfg.ConnectionInfo.ServerName}").AddField("Seed", $"{seed}", true).AddField("World Size", $"{worldSize}", true).AddField("Protocol", $"{Rust.Protocol.network}", true).AddField("Direct Connect", $"`connect {_cfg.ConnectionInfo.RustServerIp}`", true).AddField("Donation Link", $"{_cfg.ConnectionInfo.DonationURL}").AddImage(new Image(icon)).AddColor(color);
else embed.AddTitle("Server").AddDescription($"{_cfg.ConnectionInfo.ServerName}").AddField("Seed", $"{seed}", true).AddField("World Size", $"{worldSize}", true).AddField("Protocol", $"{Rust.Protocol.network}", true).AddField("Direct Connect", $"`connect {_cfg.ConnectionInfo.RustServerIp}`", true).AddImage(new Image(icon)).AddColor(color);
SendWebhook(_cfg.ConnectionInfo.DiscordWebhookURL, new List<Embed> {
embed
}
);
}
#endregion
#region Discord
private void SendWebhook(string url, List<Embed> embeds) {
Message message = new Message();
if (_forceWipe) if (_cfg.ForceWipe.AtEveryone) message.AddContent("@everyone");
else if (_cfg.Manually.AtEveryone) message.AddContent("@everyone");
message.Embeds = embeds;
var json = JsonConvert.SerializeObject(message);
webrequest.Enqueue(url, json, PostCallBack, this, Core.Libraries.RequestMethod.POST, _headers);
}
private void PostCallBack(int code, string response) {
if (code != 200 && code != 204) PrintWarning($"Host responded with {code.ToString()}: {response}");
}
#endregion
#region Discord
private class Message {
[JsonProperty("content")]
public string Content;
[JsonProperty("embeds")]
public List<Embed> Embeds;
public Message() {
Embeds = new List<Embed>();
}
public Message(string content) {
Embeds = new List<Embed>();
Content = content;
}
public Message AddContent(string content) {
Content = content;
return this;
}
public Message AddEmbed(Embed embed) {
Embeds.Add(embed);
return this;
}
public string ToJson() => JsonConvert.SerializeObject(this);
}
private class Embed {
[JsonProperty("title")]
public string Title;
[JsonProperty("description")]
public string Description;
[JsonProperty("color")]
public int Color;
[JsonProperty("image")]
public Image Image;
[JsonProperty("fields")]
public List<Field> Fields = new List<Field>();
public Embed AddTitle(string title) {
Title = title;
return this;
}
public Embed AddDescription(string description) {
Description = description;
return this;
}
public Embed AddColor(string color) {
Color = int.Parse(color.TrimStart('#'), NumberStyles.AllowHexSpecifier);
return this;
}
public Embed AddField(string name, string value, bool inline = false) {
Fields.Add(new Field(name, value, inline));
return this;
}
public Embed AddImage(Image image) {
Image = image;
return this;
}
}
private class Field {
public Field(string name, string value, bool inline) {
Name = name;
Value = value;
Inline = inline;
}
[JsonProperty("name")]
public string Name;
[JsonProperty("value")]
public string Value;
[JsonProperty("inline")]
public bool Inline;
}
private class Image {
[JsonProperty("url")]
public string Url;
public Image(string url) {
Url = url;
}
}
private Embed BuildMessage() {
Embed embed = new Embed();
return embed;
}
#endregion
}
}