I just set up the cmd feature in rustcord. I've used rustcord for 7 years or so on my server? and it's awesome. But one shortcoming I've recentlly found, is console output isn't returned to a discord channel when using a command. for instance, when adding a user to a oxide group, while discord said "success", the console said "player not found"
So, I am not a coder, but ai-fixed it, and offering it here for review and hopeful addition as a feature.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core.Plugins;
using UnityEngine;
using Facepunch;
using Oxide.Core.Libraries.Covalence;
using Oxide.Ext.Discord.Builders;
using Oxide.Ext.Discord.Clients;
using Oxide.Ext.Discord.Connections;
using Oxide.Ext.Discord.Constants;
using Oxide.Ext.Discord.Entities;
using Oxide.Ext.Discord.Interfaces;
using Oxide.Ext.Discord.Logging;
namespace Oxide.Plugins
{
[Info("Rustcord", "Kirollos & OuTSMoKE", "3.4.1")]
[Description("Complete game server monitoring through discord.")]
internal class Rustcord : RustPlugin, IDiscordPlugin
{
// --- BEGIN ADDED FIELDS FOR CONSOLE CAPTURE ---
private bool _captureActive = false;
private List<string> _captureBuffer = new List<string>();
private Snowflake _captureChannel;
// --- END ADDED FIELDS ---
[PluginReference] Plugin PrivateMessages, BetterChatMute, Clans, AdminChat, DiscordAuth, AdminHammer, AdminRadar, Kits, Vanish, RaidableBases, DangerousTreasures, NoGiveNotices, Give, AirEvent, HarborEvent, JunkyardEvent, PowerPlantEvent;
public DiscordClient Client { get; set; }
#region Back End Shit
private Settings _settings;
private int? _channelCount;
private Snowflake _botId;
private UpdatePresenceCommand DiscordPresence = new UpdatePresenceCommand
{
Activities = new List<DiscordActivity>
{
new DiscordActivity
{
Type = ActivityType.Game,
Name = "Rustcord Initializing..."
}
}
};
private Timer StatusTimer = null;
private object FindUserByID(DiscordUser user)
{
throw new NotImplementedException();
}
private static string FormatTime(TimeSpan time)
{
var values = new List<string>();
if (time.Days != 0)
values.Add($"{time.Days} day(s)");
if (time.Hours != 0)
values.Add($"{time.Hours} hour(s)");
if (time.Minutes != 0)
values.Add($"{time.Minutes} minute(s)");
if (time.Seconds != 0)
values.Add($"{time.Seconds} second(s)");
return values.ToSentence();
}
private string GetPlayerFormattedField(IPlayer player)
{
return $"{player.Name} ([{player.Id}](https://steamcommunity.com/profiles/{player.Id}))";
}
private string GetFormattedSteamID(string id)
{
return $"[{id}](https://steamcommunity.com/profiles/{id})";
}
private string FindGridPosition(Vector3 position) => MapHelper.GridToString(MapHelper.PositionToGrid(position));
enum CacheType
{
OnPlayerChat = 0,
OnPlayerConnected = 1,
OnPlayerDisconnected = 2,
OnPlayerJoin = 3
}
// --- BEGIN ADDED: run a server command and send its output back to Discord (no Unity dependency) ---
// --- BEGIN ADDED: run a server command and send its console output back to Discord using capture window ---
private void ExecuteCommandAndReturn(string cmd, string[] args, Snowflake channelId, float captureSeconds = 5.0f)
{
string fullCmd = cmd + ((args != null && args.Length > 0) ? " " + string.Join(" ", args) : "");
// Prepare capture
_captureBuffer.Clear();
_captureChannel = channelId;
_captureActive = true;
Puts($"[Rustcord] Capture START for: {fullCmd} (window={captureSeconds}s)");
try
{
if (args != null && args.Length > 0)
this.Server.Command(cmd, args);
else
this.Server.Command(cmd);
}
catch (Exception e)
{
_captureBuffer.Add($"[Rustcord] Exception while executing command: {e.Message}");
}
// Stop capture shortly after to collect emitted console lines
timer.Once(captureSeconds, () =>
{
_captureActive = false;
Puts($"[Rustcord] Capture END for: {fullCmd}. Lines captured: {_captureBuffer.Count}");
string output = _captureBuffer.Count > 0 ? string.Join("\n", _captureBuffer) : "(no console output)";
if (output.Length > 1900)
output = output.Substring(0, 1900) + "\n... (truncated)";
string msg = $"↩️ **Console output for:** `{fullCmd}`\n```{output}```";
GetChannel(Client, _captureChannel, chan =>
{
chan.CreateMessage(Client, msg);
});
});
}
// --- END ADDED ---
Dictionary<CacheType, Dictionary<BasePlayer, Dictionary<string, string>>> cache = new Dictionary<CacheType, Dictionary<BasePlayer, Dictionary<string, string>>>();
private string rbdiff;
Dictionary<string, string> GetPlayerCache(BasePlayer player, string message, CacheType type)
{
switch (type)
{
case CacheType.OnPlayerChat:
{
Dictionary<string, string> dict;
if (!cache[CacheType.OnPlayerChat].TryGetValue(player, out dict))
{
cache[CacheType.OnPlayerChat].Add(player, dict = new Dictionary<string, string>
{
["playername"] = player.displayName,
["message"] = message,
["playersteamid"] = player.UserIDString,
["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt")
});
}
dict["playername"] = player.displayName;
dict["message"] = message;
dict["playersteamid"] = player.UserIDString;
dict["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt");
return dict;
}
case CacheType.OnPlayerConnected:
{
Dictionary<string, string> dict;
if (!cache[CacheType.OnPlayerConnected].TryGetValue(player, out dict))
{
cache[CacheType.OnPlayerConnected].Add(player, dict = new Dictionary<string, string>
{
["playername"] = player.displayName,
["playerip"] = message.Substring(0, message.IndexOf(":")),
["playersteamid"] = player.UserIDString,
["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt")
});
}
dict["playername"] = player.displayName;
dict["playerip"] = message.Substring(0, message.IndexOf(":"));
dict["playersteamid"] = player.UserIDString;
dict["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt");
return dict;
}
case CacheType.OnPlayerJoin:
{
Dictionary<string, string> dict;
if (!cache[CacheType.OnPlayerJoin].TryGetValue(player, out dict))
{
cache[CacheType.OnPlayerDisconnected].Add(player, dict = new Dictionary<string, string>
{
["playername"] = player.displayName,
["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt")
});
}
dict["playername"] = player.displayName;
dict["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt");
return dict;
}
case CacheType.OnPlayerDisconnected:
default:
{
Dictionary<string, string> dict;
if (!cache[CacheType.OnPlayerDisconnected].TryGetValue(player, out dict))
{
cache[CacheType.OnPlayerDisconnected].Add(player, dict = new Dictionary<string, string>
{
["playername"] = player.displayName,
["reason"] = message,
["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt")
});
}
dict["playername"] = player.displayName;
dict["reason"] = message;
dict["time"] = DateTimeOffset.UtcNow.DateTime.ToLocalTime().ToString("hh:mm tt");
return dict;
}
}
}
private void OnDiscordClientCreated()
{
if (string.IsNullOrEmpty(_settings.General.Apikey) || _settings.General.Apikey == null || _settings.General.Apikey == "BotToken")
{
PrintError("API key is empty or invalid!");
return;
}
bool flag = true;
try
{
BotConnection settings = new BotConnection
{
ApiToken = _settings.General.Apikey,
Intents = GatewayIntents.Guilds | GatewayIntents.GuildMembers | GatewayIntents.GuildMessages | GatewayIntents.DirectMessages | GatewayIntents.MessageContent,
LogLevel = _settings.General.ExtensionDebugging
};
Client.Connect(settings);
}
catch (Exception e)
{
flag = false;
PrintError($"Rustcord failed to create client! Exception message: {e}");
}
if (flag)
{
cmd.AddChatCommand(_settings.General.ReportCommand, this, "cmdReport");
cmd.AddChatCommand("bug", this, "cmdBug");
SubscribeHooks();
}
UnityEngine.Application.logMessageReceived += ConsoleLog;
}
// --- BEGIN ADDED: Hook to capture server console output while active ---
private void OnServerMessage(string message)
{
if (!_captureActive) return;
if (string.IsNullOrEmpty(message)) return;
// Filter out our own Discord echo to avoid loops
if (message.StartsWith("[DISCORD]") || message.StartsWith("[Discord]"))
return;
_captureBuffer.Add(message);
}
// --- END ADDED ---
private void Reload()
{
rust.RunServerCommand("oxide.reload Rustcord");
}
void OnDiscordGatewayReady(GatewayReadyEvent rdy)
{
_botId = rdy.User.Id;
SubscribeHooks();
_channelCount = _settings?.Channels.Count;
if (_settings.General.EnableBotStatus)
{
NextFrame(() =>
{
if (StatusTimer != null && !StatusTimer.Destroyed)
{
StatusTimer.Destroy();
}
StatusTimer = timer.Every(6f, () =>
{
var text = new Dictionary<string, string>
{
["playercount"] = Convert.ToString(BasePlayer.activePlayerList.Count),
["maxplayers"] = Convert.ToString(ConVar.Server.maxplayers),
["sleepercount"] = Convert.ToString(BasePlayer.sleepingPlayerList.Count)
};
var msg = Translate("Discord_Status", text);
DiscordPresence.Activities[0].Name = string.IsNullOrEmpty(msg) ? "Rustcord initializing...." : msg;
Client.UpdateStatus(DiscordPresence);
});
});
}
}
void OnDiscordGuildCreated(DiscordGuild newguild)
{
for (int i = 0; i < _channelCount; i++)
{
if (_settings.Channels[i].perms.Contains("msg_plugininit"))
{
GetChannel(Client, _settings.Channels[i].Channelid, c => {
c.CreateMessage(Client, "Rustcord Initialized!");
}, newguild.Id);
}
}
}
private void Unload()
{
if (StatusTimer != null && !StatusTimer.Destroyed)
{
StatusTimer.Destroy();
}
UnityEngine.Application.logMessageReceived -= ConsoleLog;
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMessageCreated)]
private void OnDiscordGuildMessageCreated(DiscordMessage message)
{
//Puts($"{nameof(OnDiscordGuildMessageCreated)} New Message: {message.Author.FullUserName}: {message.Content}.");
if ((message.Content?.Length ?? 0) == 0)
{
//Puts($"{nameof(OnDiscordGuildMessageCreated)} Skipping Message {message.Author.FullUserName}: {message.Content}. Content Length is 0");
return;
}
Settings.Channel channelidx = FindChannelById(message.ChannelId);
if (channelidx == null)
{
//Puts($"{nameof(OnDiscordGuildMessageCreated)} Skipping Message {message.Author.FullUserName}: {message.Content}. Channel Not Found");
return;
}
if (message.Author.Id == _botId)
{
return;
}
if (message.Content[0] == _settings.DiscordSide.Commandprefix[0])
{
if (!channelidx.perms.Contains("cmd_allow"))
return;
string cmd;
string msg;
try
{
cmd = message.Content.Split(' ')[0].ToLower();
if (string.IsNullOrEmpty(cmd.Trim()))
cmd = message.Content.Trim().ToLower();
}
catch
{
cmd = message.Content.Trim().ToLower();
}
cmd = cmd.Remove(0, 1);
msg = message.Content.Remove(0, 1 + cmd.Length).Trim();
cmd = cmd.Trim();
cmd = cmd.ToLower();
if (!channelidx.perms.Contains("cmd_" + cmd))
return;
if (!_settings.Commandroles.ContainsKey(cmd))
{
DiscordToGameCmd(cmd, msg, message.Author, message.ChannelId);
return;
}
var roles = _settings.Commandroles[cmd];
if (roles.Count == 0)
{
DiscordToGameCmd(cmd, msg, message.Author, message.ChannelId);
return;
}
foreach (var roleid in message.Member.Roles)
{
var rolename = GetRoleNameById(roleid);
if (roles.Contains(rolename))
{
DiscordToGameCmd(cmd, msg, message.Author, message.ChannelId);
break;
}
}
}
else
{
var chattag = _settings.DiscordSide.GameChatTag;
var chattagcolor = _settings.DiscordSide.GameChatTagColor;
var chatnamecolor = _settings.DiscordSide.GameChatNameColor;
var chattextcolor = _settings.DiscordSide.GameChatTextColor;
if (!channelidx.perms.Contains("msg_chat")) return;
string nickname = message.Member?.Nickname ?? "";
if (nickname.Length == 0)
nickname = message.Author.Username;
//PrintToChat("<color=" + chattagcolor + ">" + chattag + "</color> " + "<color=" + chatnamecolor + ">" + nickname + ":</color> " + "<color=" + chattextcolor + ">" + message.content + "</color>");
string text = $"<color={chattagcolor}>{chattag}</color> <color={chatnamecolor}>{nickname}:</color> <color={chattextcolor}>{message.Content}</color>";
foreach (var player in BasePlayer.activePlayerList) Player.Message(player, text, _settings.DiscordSide.GameChatIconSteamID);
Puts("[DISCORD] " + nickname + ": " + message.Content);
}
}
private string Translate(string msg, Dictionary<string, string> parameters = null)
{
if (string.IsNullOrEmpty(msg))
return string.Empty;
msg = lang.GetMessage(msg, this);
if (parameters != null)
{
foreach (var lekey in parameters)
{
if (msg.Contains("{" + lekey.Key + "}"))
msg = msg.Replace("{" + lekey.Key + "}", lekey.Value);
}
}
return msg;
}
private Settings.Channel FindChannelById(Snowflake id)
{
for (int i = 0; i < _channelCount; i++)
{
if (_settings.Channels[i].Channelid == id)
return _settings.Channels[i];
}
return null;
}
private void GetChannel(DiscordClient c, Snowflake chan_id, Action<DiscordChannel> cb, Snowflake guildid = default(Snowflake))
{
//Guild g = guildid == null ? c.DiscordServers.FirstOrDefault(x => x.channels.FirstOrDefault(y => y.id == chan_id) != null) : c.GetGuild(guildid);
DiscordGuild g = null;
DiscordChannel foundchan = null;
if (guildid.IsValid())
g = c.Bot.GetGuild(guildid);
else
foreach (var G in c.Bot.Servers.Values)
{
foundchan = G.Channels[chan_id];
if (foundchan != null)
{
g = G;
break;
}
}
if (g == null)
{
PrintWarning($"Rustcord failed to fetch channel! (chan_id={chan_id}). Guild is invalid.");
return;
}
if (g.Unavailable ?? false == true)
{
PrintWarning($"Rustcord failed to fetch channel! (chan_id={chan_id}). Guild is possibly invalid or not available yet.");
return;
}
//Channel foundchan = g?.channels?.FirstOrDefault(z => z.id == chan_id);
if (foundchan == null)
{
if (guildid.IsValid()) return; // Ignore printing error
PrintWarning($"Rustcord failed to fetch channel! (chan_id={chan_id}).");
return;
}
if (foundchan.Id != chan_id) return;
cb?.Invoke(foundchan);
}
private string GetRoleNameById(Snowflake id)
{
//var role = _client.DiscordServers.FirstOrDefault(x => x.roles.FirstOrDefault(y => y.id == id) != null)?.roles.FirstOrDefault(z => z.id == id);
//return role?.name ?? "";
foreach (var r in Client.Bot.Servers.Values)
{
var role = r.Roles[id];
if (role != null)
{
return role.Name;
}
}
return string.Empty;
}
private IPlayer FindPlayer(string nameorId)
{
foreach (var player in covalence.Players.Connected)
{
if (player.Id == nameorId)
return player;
if (player.Name == nameorId)
return player;
}
return null;
}
private DiscordUser FindUserByID(Snowflake id)
{
foreach (DiscordGuild guild in Client.Bot.Servers.Values)
{
var member = guild.Members[id];
if (member != null)
{
return member.User;
}
}
return null;
}
private BasePlayer FindPlayerByID(string Id)
{
foreach (var player in BasePlayer.activePlayerList)
{
if (player.UserIDString == Id)
return player;
}
return null;
}
private IPlayer GetPlayer(string id)
{
return covalence.Players.FindPlayerById(id);
}
private IPlayer GetPlayer(ulong id)
{
return GetPlayer(id.ToString());
}
#endregion
#region Config Layout
private class Settings
{
[JsonProperty(PropertyName = "General Settings")]
public GeneralSettings General { get; set; }
[JsonProperty(PropertyName = "Discord to Game Settings")]
public DiscordSideSettings DiscordSide { get; set; }
[JsonProperty(PropertyName = "Rust Logging Settings")]
public GameLogSettings GameLog { get; set; }
[JsonProperty(PropertyName = "Plugin Logging Settings")]
public PluginLogSettings PluginLog { get; set; }
[JsonProperty(PropertyName = "Premium Plugin Logging Settings")]
public PremiumPluginLogSettings PremiumPluginLog { get; set; }
[JsonProperty(PropertyName = "Discord Output Formatting")]
public OutputSettings OutputFormat { get; set; }
[JsonProperty(PropertyName = "Logging Exclusions")]
public ExcludedSettings Excluded { get; set; }
[JsonProperty(PropertyName = "Filter Settings")]
public FilterSettings Filters { get; set; }
[JsonProperty(PropertyName = "Discord Logging Channels")]
public List<Channel> Channels { get; set; }
[JsonProperty(PropertyName = "Discord Command Role Assignment (Empty = All roles can use command.)")]
public Dictionary<string, List<string>> Commandroles { get; set; }
public class Channel
{
[JsonProperty(PropertyName = "Discord Channel ID #")]
public Snowflake Channelid { get; set; }
[JsonProperty(PropertyName = "Channel Flags")]
public List<string> perms { get; set; }
[JsonProperty(PropertyName = "Custom: Words/Phrases to Log")]
public List<string> CustomFilter { get; set; }
[JsonIgnore]
public readonly StringBuilder FilterBuilder = new StringBuilder();
[JsonIgnore]
public Timer Timer;
}
}
public class GeneralSettings
{
[JsonProperty(PropertyName = "API Key (Bot Token)")]
public string Apikey { get; set; }
[JsonProperty(PropertyName = "Auto Reload Plugin")]
public bool AutoReloadPlugin { get; set; }
[JsonProperty(PropertyName = "Auto Reload Time (Seconds)")]
public int AutoReloadTime { get; set; }
[JsonProperty(PropertyName = "Enable Bot Status")]
public bool EnableBotStatus { get; set; }
[JsonProperty(PropertyName = "In-Game Report Command")]
public string ReportCommand { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
[DefaultValue(DiscordLogLevel.Info)]
[JsonProperty(PropertyName = "Discord Extension Log Level (Verbose/Debug/Info/Warning/Error/Exception/Off)")]
public DiscordLogLevel ExtensionDebugging { get; set; } = DiscordLogLevel.Info;
}
public class DiscordSideSettings
{
[JsonProperty(PropertyName = "Discord Command Prefix")]
public string Commandprefix { get; set; }
[JsonProperty(PropertyName = "Discord to Game Chat: Icon (Steam ID)")]
public ulong GameChatIconSteamID { get; set; }
[JsonProperty(PropertyName = "Discord to Game Chat: Tag")]
public string GameChatTag { get; set; }
[JsonProperty(PropertyName = "Discord to Game Chat: Tag Color (Hex)")]
public string GameChatTagColor { get; set; }
[JsonProperty(PropertyName = "Discord to Game Chat: Player Name Color (Hex)")]
public string GameChatNameColor { get; set; }
[JsonProperty(PropertyName = "Discord to Game Chat: Message Color (Hex)")]
public string GameChatTextColor { get; set; }
}
public class GameLogSettings
{
[JsonProperty(PropertyName = "Enable Logging: Player Chat")]
public bool LogChat { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Joins & Quits")]
public bool LogJoinQuits { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Deaths")]
public bool LogDeaths { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Vehicle Spawns (Heli/APC/Plane/Ship)")]
public bool LogVehicleSpawns { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Crate Drops (Hackable/Supply)")]
public bool LogCrateDrops { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Usergroup Changes")]
public bool LogUserGroups { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Permission Changes")]
public bool LogPermissions { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Kicks & Bans")]
public bool LogKickBans { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Player Name Changes")]
public bool LogNameChanges { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Server Commands (Gestures/Note Edits)")]
public bool LogServerCommands { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Server Messages (Give/Item Spawns)")]
public bool LogServerMessages { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Player F7 Reports")]
public bool LogF7Reports { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Team Changes")]
public bool LogTeams { get; set; }
[JsonProperty(PropertyName = "Enable Logging: RCON Connections")]
public bool LogRCON { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Spectates")]
public bool LogSpectates { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Server Wipe")]
public bool LogServerWipe { get; set; }
[JsonProperty(PropertyName = "Enable Custom Logging")]
public bool EnableCustomLogging { get; set; }
}
public class PluginLogSettings
{
[JsonProperty(PropertyName = "Enable Logging: AdminHammer")]
public bool LogPluginAdminHammer { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Admin Radar")]
public bool LogPluginAdminRadar { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Better Chat Mute")]
public bool LogPluginBetterChatMute { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Clans")]
public bool LogPluginClans { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Dangerous Treasures")]
public bool LogPluginDangerousTreasures { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Discord Auth")]
public bool LogPluginDiscordAuth { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Godmode")]
public bool LogPluginGodmode { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Kits")]
public bool LogPluginKits { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Private Messages")]
public bool LogPluginPrivateMessages { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Raidable Bases")]
public bool LogPluginRaidableBases { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Sign Artist")]
public bool LogPluginSignArtist { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Vanish")]
public bool LogPluginVanish { get; set; }
}
public class PremiumPluginLogSettings
{
[JsonProperty(PropertyName = "Enable Logging: Air Event")]
public bool LogPluginAirEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Armored Train Event")]
public bool LogPluginArmoredTrainEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Cargo Train Event")]
public bool LogPluginCargoTrainEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Convoy Event")]
public bool LogPluginConvoyEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Harbor Event")]
public bool LogPluginHarborEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Junkyard Event")]
public bool LogPluginJunkyardEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Power Plant Event")]
public bool LogPluginPowerPlantEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Satellite Dish Event")]
public bool LogPluginSatDishEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Sputnik Event")]
public bool LogPluginSputnikEvent { get; set; }
[JsonProperty(PropertyName = "Enable Logging: Water Event")]
public bool LogPluginWaterEvent { get; set; }
}
public class OutputSettings
{
[JsonProperty(PropertyName = "Output Type: Bans (Simple/Embed)")]
public string OutputTypeBans { get; set; }
[JsonProperty(PropertyName = "Output Type: Bug Report (Simple/Embed)")]
public string OutputTypeBugs { get; set; }
[JsonProperty(PropertyName = "Output Type: Deaths (Simple/Embed/DeathNotes)")]
public string OutputTypeDeaths { get; set; }
[JsonProperty(PropertyName = "Output Type: F7 Reports (Simple/Embed)")]
public string OutputTypeF7Report { get; set; }
[JsonProperty(PropertyName = "Output Type: Join/Quit (Simple/Embed)")]
public string OutputTypeJoinQuit { get; set; }
[JsonProperty(PropertyName = "Output Type: Join Player Info (Admin Channel) (Simple/Embed)")]
public string OutputTypeJoinAdminChan { get; set; }
[JsonProperty(PropertyName = "Output Type: Kicks (Simple/Embed)")]
public string OutputTypeKicks { get; set; }
[JsonProperty(PropertyName = "Output Type: Note Logging (Simple/Embed)")]
public string OutputTypeNoteLog { get; set; }
[JsonProperty(PropertyName = "Output Type: Player Name Change (Simple/Embed)")]
public string OutputTypeNameChange { get; set; }
[JsonProperty(PropertyName = "Output Type: /Report (Simple/Embed)")]
public string OutputTypeReports { get; set; }
[JsonProperty(PropertyName = "Output Type: Server Wipe (Simple/Embed)")]
public string OutputTypeServerWipe { get; set; }
[JsonProperty(PropertyName = "Output Type: Teams (Simple/Embed)")]
public string OutputTypeTeams { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Admin Hammer (Simple/Embed)")]
public string OutputTypeAdminHammer { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Admin Radar (Simple/Embed)")]
public string OutputTypeAdminRadar { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Better Chat Mute (Simple/Embed)")]
public string OutputTypeBetterChatMute { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Clans (Simple/Embed)")]
public string OutputTypeClans { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Dangerous Treasures (Simple/Embed)")]
public string OutputTypeDangerousTreasures { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Discord Auth (Simple/Embed)")]
public string OutputTypeDiscordAuth { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Godmode (Simple/Embed)")]
public string OutputTypeGodmode { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Kits (Simple/Embed)")]
public string OutputTypeKits { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Private Messages (Simple/Embed)")]
public string OutputTypePMs { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Raidable Bases (Simple/Embed)")]
public string OutputTypeRaidableBases { get; set; }
[JsonProperty(PropertyName = "Output Type (Plugin): Vanish (Simple/Embed)")]
public string OutputTypeVanish { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): AirEvent (Simple/Embed)")]
public string OutputTypeAirEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): ArmoredTrainEvent (Simple/Embed)")]
public string OutputTypeArmoredTrainEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): CargoTrainEvent (Simple/Embed)")]
public string OutputTypeCargoTrainEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): ConvoyEvent (Simple/Embed)")]
public string OutputTypeConvoyEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): HarborEvent (Simple/Embed)")]
public string OutputTypeHarborEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): JunkyardEvent (Simple/Embed)")]
public string OutputTypeJunkyardEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): PowerPlantEvent (Simple/Embed)")]
public string OutputTypePowerPlantEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): SatDishEvent (Simple/Embed)")]
public string OutputTypeSatDishEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): SputnikEvent (Simple/Embed)")]
public string OutputTypeSputnikEvent { get; set; }
[JsonProperty(PropertyName = "Output Type (Premium Plugin): WaterEvent (Simple/Embed)")]
public string OutputTypeWaterEvent { get; set; }
}
public class ExcludedSettings
{
[JsonProperty(PropertyName = "Exclude Listed Groups From log_groups")]
public List<string> LogExcludeGroups { get; set; }
[JsonProperty(PropertyName = "Exclude Listed Permissions From log_perms")]
public List<string> LogExcludePerms { get; set; }
}
public class FilterSettings
{
[JsonProperty(PropertyName = "Chat Filter: Replacement Word")]
public string FilteredWord { get; set; }
[JsonProperty(PropertyName = "Chat Filter: Words to Filter")]
public List<string> FilterWords { get; set; }
}
#endregion
#region Default Config
private Settings GetDefaultSettings()
{
return new Settings
{
General = new GeneralSettings
{
Apikey = "BotToken",
AutoReloadPlugin = false,
AutoReloadTime = 901,
EnableBotStatus = false,
ReportCommand = "report",
ExtensionDebugging = DiscordLogLevel.Info
},
DiscordSide = new DiscordSideSettings
{
Commandprefix = "!",
GameChatIconSteamID = 76561############,
GameChatTag = "[RUSTCORD]",
GameChatTagColor = "#7289DA",
GameChatNameColor = "#55aaff",
GameChatTextColor = "#ffffff",
},
GameLog = new GameLogSettings
{
LogChat = true,
LogJoinQuits = true,
LogDeaths = false,
LogVehicleSpawns = false,
LogCrateDrops = false,
LogUserGroups = false,
LogPermissions = false,
LogKickBans = true,
LogNameChanges = false,
LogServerCommands = false,
LogServerMessages = false,
LogF7Reports = false,
LogTeams = false,
LogRCON = false,
LogSpectates = false,
LogServerWipe = false,
EnableCustomLogging = false
},
PluginLog = new PluginLogSettings
{
LogPluginAdminHammer = false,
LogPluginAdminRadar = false,
LogPluginBetterChatMute = false,
LogPluginClans = false,
LogPluginDangerousTreasures = false,
LogPluginDiscordAuth = false,
LogPluginGodmode = false,
LogPluginKits = false,
LogPluginPrivateMessages = false,
LogPluginRaidableBases = false,
LogPluginSignArtist = false,
LogPluginVanish = false
},
PremiumPluginLog = new PremiumPluginLogSettings
{
LogPluginAirEvent = false,
LogPluginArmoredTrainEvent = false,
LogPluginCargoTrainEvent = false,
LogPluginConvoyEvent = false,
LogPluginHarborEvent = false,
LogPluginJunkyardEvent = false,
LogPluginPowerPlantEvent = false,
LogPluginSputnikEvent = false,
LogPluginSatDishEvent = false,
LogPluginWaterEvent = false
},
OutputFormat = new OutputSettings
{
OutputTypeBans = "Simple",
OutputTypeBugs = "Simple",
OutputTypeDeaths = "Simple",
OutputTypeF7Report = "Simple",
OutputTypeJoinQuit = "Simple",
OutputTypeJoinAdminChan = "Simple",
OutputTypeKicks = "Simple",
OutputTypeNameChange = "Simple",
OutputTypeNoteLog = "Simple",
OutputTypeReports = "Simple",
OutputTypeServerWipe = "Simple",
OutputTypeTeams = "Simple",
OutputTypeAdminHammer = "Simple",
OutputTypeAdminRadar = "Simple",
OutputTypeBetterChatMute = "Simple",
OutputTypeClans = "Simple",
OutputTypeDangerousTreasures = "Simple",
OutputTypeDiscordAuth = "Simple",
OutputTypeGodmode = "Simple",
OutputTypeKits = "Simple",
OutputTypePMs = "Simple",
OutputTypeRaidableBases = "Simple",
OutputTypeVanish = "Simple",
OutputTypeAirEvent = "Simple",
OutputTypeArmoredTrainEvent = "Simple",
OutputTypeCargoTrainEvent = "Simple",
OutputTypeConvoyEvent = "Simple",
OutputTypeHarborEvent = "Simple",
OutputTypeJunkyardEvent = "Simple",
OutputTypePowerPlantEvent = "Simple",
OutputTypeSputnikEvent = "Simple",
OutputTypeSatDishEvent = "Simple",
OutputTypeWaterEvent = "Simple"
},
Channels = new List<Settings.Channel>
{
new Settings.Channel
{
perms = new List<string>
{
"cmd_allow",
"cmd_players",
"cmd_kick",
"cmd_com",
"cmd_mute",
"cmd_unmute",
"msg_join",
"msg_quit",
"death_pvp",
"msg_chat",
"game_bug",
"msg_serverinit"
},
CustomFilter = new List<string>
{
"keyword1",
"keyword2"
}
},
new Settings.Channel
{
perms = new List<string>
{
"msg_joinlog",
"game_report",
"msg_teamchat",
"game_bug"
},
CustomFilter = new List<string>
{
"keyword1",
"keyword2"
}
}
},
Commandroles = new Dictionary<string, List<string>>
{
{
"players", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"mute", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"unmute", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"kick", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"ban", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"timeban", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"unban", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
},
{
"com", new List<string>()
{
"DiscordRoleName",
"DiscordRoleName2"
}
}
},
Filters = new FilterSettings
{
FilterWords = new List<string>
{
"badword1",
"badword2"
},
FilteredWord = "<censored>",
},
Excluded = new ExcludedSettings
{
LogExcludeGroups = new List<string>
{
"example-group1",
"example-group2"
},
LogExcludePerms = new List<string>
{
"example.permission1",
"example.permission2"
}
},
};
}
protected override void LoadDefaultConfig()
{
PrintWarning("Attempting to create default config...");
Config.Clear();
Config.WriteObject(GetDefaultSettings(), true);
Config.Save();
}
#endregion
#region Hooks
void SubscribeHooks()
{
if (_settings.GameLog.LogChat) Subscribe(nameof(OnPlayerChat));
if (_settings.GameLog.LogJoinQuits)
{
Subscribe(nameof(OnPlayerConnected));
Subscribe(nameof(OnPlayerDisconnected));
}
if (_settings.GameLog.LogDeaths)
{
if (_settings.OutputFormat.OutputTypeDeaths == "DeathNotes") Subscribe(nameof(OnDeathNotice));
if ((_settings.OutputFormat.OutputTypeDeaths == "Simple") || (_settings.OutputFormat.OutputTypeDeaths == "Embed")) Subscribe(nameof(OnPlayerDeath));
}
if (_settings.GameLog.LogVehicleSpawns) Subscribe(nameof(OnEntitySpawned));
if (_settings.GameLog.LogCrateDrops)
{
Subscribe(nameof(OnCrateDropped));
Subscribe(nameof(OnSupplyDropLanded));
}
if (_settings.GameLog.LogUserGroups)
{
Subscribe(nameof(OnGroupCreated));
Subscribe(nameof(OnGroupDeleted));
Subscribe(nameof(OnUserGroupAdded));
Subscribe(nameof(OnUserGroupRemoved));
}
if (_settings.GameLog.LogPermissions)
{
Subscribe(nameof(OnUserPermissionGranted));
Subscribe(nameof(OnGroupPermissionGranted));
Subscribe(nameof(OnUserPermissionRevoked));
Subscribe(nameof(OnGroupPermissionRevoked));
}
if (_settings.GameLog.LogKickBans)
{
Subscribe(nameof(OnUserKicked));
Subscribe(nameof(OnUserBanned));
Subscribe(nameof(OnUserUnbanned));
}
if (_settings.GameLog.LogNameChanges) Subscribe(nameof(OnUserNameUpdated));
if (_settings.GameLog.LogServerMessages) Subscribe(nameof(OnServerMessage));
if (_settings.GameLog.LogServerCommands) Subscribe(nameof(OnServerCommand));
if (_settings.GameLog.LogF7Reports) Subscribe(nameof(OnPlayerReported));
if (_settings.GameLog.LogServerWipe) Subscribe(nameof(OnNewSave));
if (_settings.GameLog.LogTeams)
{
Subscribe(nameof(OnTeamCreated));
Subscribe(nameof(OnTeamAcceptInvite));
Subscribe(nameof(OnTeamLeave));
Subscribe(nameof(OnTeamKick));
Subscribe(nameof(OnTeamDisbanded));
}
if (_settings.GameLog.LogRCON)
{
Subscribe(nameof(OnRconConnection));
}
if (_settings.GameLog.LogSpectates)
{
Subscribe(nameof(OnPlayerSpectate));
Subscribe(nameof(OnPlayerSpectateEnd));
}
if (_settings.PluginLog.LogPluginAdminHammer)
{
Subscribe(nameof(OnAdminHammerEnabled));
Subscribe(nameof(OnAdminHammerDisabled));
}
if (_settings.PluginLog.LogPluginAdminRadar)
{
Subscribe(nameof(OnRadarActivated));
Subscribe(nameof(OnRadarDeactivated));
}
if (_settings.PluginLog.LogPluginBetterChatMute)
{
Subscribe(nameof(OnBetterChatMuted));
Subscribe(nameof(OnBetterChatTimeMuted));
Subscribe(nameof(OnBetterChatUnmuted));
Subscribe(nameof(OnBetterChatMuteExpired));
}
if (_settings.PluginLog.LogPluginClans)
{
Subscribe(nameof(OnClanCreate));
Subscribe(nameof(OnClanDisbanded));
Subscribe(nameof(OnClanChat));
}
if (_settings.PluginLog.LogPluginDangerousTreasures)
{
Subscribe(nameof(OnDangerousEventStarted));
Subscribe(nameof(OnDangerousEventEnded));
}
if (_settings.PluginLog.LogPluginGodmode)
{
Subscribe(nameof(OnGodmodeToggled));
}
if (_settings.PluginLog.LogPluginKits)
{
Subscribe(nameof(OnKitRedeemed));
}
if (_settings.PluginLog.LogPluginPrivateMessages) Subscribe(nameof(OnPMProcessed));
if (_settings.PluginLog.LogPluginRaidableBases)
{
Subscribe(nameof(OnRaidableBaseStarted));
Subscribe(nameof(OnRaidableBaseEnded));
}
if (_settings.PluginLog.LogPluginSignArtist) Subscribe(nameof(OnImagePost));
if (_settings.PluginLog.LogPluginDiscordAuth)
{
Subscribe(nameof(OnDiscordPlayerLinked));
Subscribe(nameof(OnDiscordPlayerUnlinked));
}
if (_settings.PluginLog.LogPluginVanish)
{
Subscribe(nameof(OnVanishDisappear));
Subscribe(nameof(OnVanishReappear));
}
if (_settings.PremiumPluginLog.LogPluginAirEvent)
{
Subscribe(nameof(OnAirEventStart));
Subscribe(nameof(OnAirEventEnd));
}
if (_settings.PremiumPluginLog.LogPluginArmoredTrainEvent)
{
Subscribe(nameof(OnArmoredTrainEventStart));
Subscribe(nameof(OnArmoredTrainEventStop));
}
if (_settings.PremiumPluginLog.LogPluginCargoTrainEvent)
{
Subscribe(nameof(OnTrainEventStarted));
Subscribe(nameof(OnTrainEventEnded));
}
if (_settings.PremiumPluginLog.LogPluginConvoyEvent)
{
Subscribe(nameof(OnConvoyStart));
Subscribe(nameof(OnConvoyStop));
}
if (_settings.PremiumPluginLog.LogPluginHarborEvent)
{
Subscribe(nameof(OnHarborEventStart));
Subscribe(nameof(OnHarborEventEnd));
}
if (_settings.PremiumPluginLog.LogPluginJunkyardEvent)
{
Subscribe(nameof(OnJunkyardEventStart));
Subscribe(nameof(OnJunkyardEventEnd));
}
if (_settings.PremiumPluginLog.LogPluginPowerPlantEvent)
{
Subscribe(nameof(OnPowerPlantEventStart));
Subscribe(nameof(OnPowerPlantEventEnd));
}
if (_settings.PremiumPluginLog.LogPluginSputnikEvent)
{
Subscribe(nameof(OnSputnikEventStart));
Subscribe(nameof(OnSputnikEventStop));
}
if (_settings.PremiumPluginLog.LogPluginSatDishEvent)
{
Subscribe(nameof(OnSatDishEventStart));
Subscribe(nameof(OnSatDishEventEnd));
}
if (_settings.PremiumPluginLog.LogPluginWaterEvent)
{
Subscribe(nameof(OnWaterEventStart));
Subscribe(nameof(OnWaterEventEnd));
}
}
private void Init()
{
cache[CacheType.OnPlayerChat] = new Dictionary<BasePlayer, Dictionary<string, string>>();
cache[CacheType.OnPlayerConnected] = new Dictionary<BasePlayer, Dictionary<string, string>>();
cache[CacheType.OnPlayerDisconnected] = new Dictionary<BasePlayer, Dictionary<string, string>>();
cache[CacheType.OnPlayerJoin] = new Dictionary<BasePlayer, Dictionary<string, string>>();
UnsubscribeHooks();
}
void UnsubscribeHooks()
{
Unsubscribe(nameof(OnPlayerChat));
Unsubscribe(nameof(OnPlayerConnected));
Unsubscribe(nameof(OnPlayerDisconnected));
Unsubscribe(nameof(OnDeathNotice));
Unsubscribe(nameof(OnPlayerDeath));
Unsubscribe(nameof(OnEntitySpawned));
Unsubscribe(nameof(OnCrateDropped));
Unsubscribe(nameof(OnSupplyDropLanded));
Unsubscribe(nameof(OnGroupCreated));
Unsubscribe(nameof(OnGroupDeleted));
Unsubscribe(nameof(OnUserGroupAdded));
Unsubscribe(nameof(OnUserGroupRemoved));
Unsubscribe(nameof(OnUserPermissionGranted));
Unsubscribe(nameof(OnGroupPermissionGranted));
Unsubscribe(nameof(OnUserPermissionRevoked));
Unsubscribe(nameof(OnGroupPermissionRevoked));
Unsubscribe(nameof(OnUserKicked));
Unsubscribe(nameof(OnUserBanned));
Unsubscribe(nameof(OnUserUnbanned));
Unsubscribe(nameof(OnUserNameUpdated));
Unsubscribe(nameof(OnServerMessage));
Unsubscribe(nameof(OnServerCommand));
Unsubscribe(nameof(OnPlayerReported));
Unsubscribe(nameof(OnTeamCreated));
Unsubscribe(nameof(OnTeamAcceptInvite));
Unsubscribe(nameof(OnTeamLeave));
Unsubscribe(nameof(OnTeamKick));
Unsubscribe(nameof(OnTeamDisbanded));
Unsubscribe(nameof(OnRconConnection));
Unsubscribe(nameof(OnPlayerSpectate));
Unsubscribe(nameof(OnPlayerSpectateEnd));
Unsubscribe(nameof(OnAdminHammerEnabled));
Unsubscribe(nameof(OnAdminHammerDisabled));
Unsubscribe(nameof(OnRadarActivated));
Unsubscribe(nameof(OnRadarDeactivated));
Unsubscribe(nameof(OnBetterChatTimeMuted));
Unsubscribe(nameof(OnBetterChatMuted));
Unsubscribe(nameof(OnBetterChatTimeMuted));
Unsubscribe(nameof(OnBetterChatUnmuted));
Unsubscribe(nameof(OnBetterChatMuteExpired));
Unsubscribe(nameof(OnClanCreate));
Unsubscribe(nameof(OnClanDisbanded));
Unsubscribe(nameof(OnClanChat));
Unsubscribe(nameof(OnPMProcessed));
Unsubscribe(nameof(OnImagePost));
Unsubscribe(nameof(OnDiscordPlayerLinked));
Unsubscribe(nameof(OnDiscordPlayerUnlinked));
Unsubscribe(nameof(OnRaidableBaseStarted));
Unsubscribe(nameof(OnRaidableBaseEnded));
Unsubscribe(nameof(OnGodmodeToggled));
Unsubscribe(nameof(OnKitRedeemed));
Unsubscribe(nameof(OnDangerousEventStarted));
Unsubscribe(nameof(OnDangerousEventEnded));
Unsubscribe(nameof(OnAirEventStart));
Unsubscribe(nameof(OnAirEventEnd));
Unsubscribe(nameof(OnArmoredTrainEventStart));
Unsubscribe(nameof(OnArmoredTrainEventStop));
Unsubscribe(nameof(OnTrainEventStarted));
Unsubscribe(nameof(OnTrainEventEnded));
Unsubscribe(nameof(OnConvoyStart));
Unsubscribe(nameof(OnConvoyStop));
Unsubscribe(nameof(OnHarborEventStart));
Unsubscribe(nameof(OnHarborEventEnd));
Unsubscribe(nameof(OnJunkyardEventStart));
Unsubscribe(nameof(OnJunkyardEventEnd));
Unsubscribe(nameof(OnPowerPlantEventStart));
Unsubscribe(nameof(OnPowerPlantEventEnd));
Unsubscribe(nameof(OnSatDishEventStart));
Unsubscribe(nameof(OnSatDishEventEnd));
Unsubscribe(nameof(OnSputnikEventStart));
Unsubscribe(nameof(OnSputnikEventStop));
Unsubscribe(nameof(OnWaterEventStart));
Unsubscribe(nameof(OnWaterEventEnd));
}
#endregion
#region Null Config Check
private void Loaded()
{
_settings = Config.ReadObject<Settings>();
// Make sure objects are not taken off the config, otherwise some parts of code will release NRE.
if (_settings.OutputFormat.OutputTypeBans == null)
_settings.OutputFormat.OutputTypeBans = "Simple";
if (_settings.OutputFormat.OutputTypeBugs == null)
_settings.OutputFormat.OutputTypeBugs = "Simple";
if (_settings.OutputFormat.OutputTypeDeaths == null)
_settings.OutputFormat.OutputTypeDeaths = "Simple";
if (_settings.OutputFormat.OutputTypeF7Report == null)
_settings.OutputFormat.OutputTypeF7Report = "Simple";
if (_settings.OutputFormat.OutputTypeJoinQuit == null)
_settings.OutputFormat.OutputTypeJoinQuit = "Simple";
if (_settings.OutputFormat.OutputTypeJoinAdminChan == null)
_settings.OutputFormat.OutputTypeJoinAdminChan = "Simple";
if (_settings.OutputFormat.OutputTypeKicks == null)
_settings.OutputFormat.OutputTypeKicks = "Simple";
if (_settings.OutputFormat.OutputTypeNameChange == null)
_settings.OutputFormat.OutputTypeNameChange = "Simple";
if (_settings.OutputFormat.OutputTypeNoteLog == null)
_settings.OutputFormat.OutputTypeNoteLog = "Simple";
if (_settings.OutputFormat.OutputTypeReports == null)
_settings.OutputFormat.OutputTypeReports = "Simple";
if (_settings.OutputFormat.OutputTypeServerWipe == null)
_settings.OutputFormat.OutputTypeServerWipe = "Simple";
if (_settings.OutputFormat.OutputTypeTeams == null)
_settings.OutputFormat.OutputTypeTeams = "Simple";
if (_settings.OutputFormat.OutputTypeAdminHammer == null)
_settings.OutputFormat.OutputTypeAdminHammer = "Simple";
if (_settings.OutputFormat.OutputTypeAdminRadar == null)
_settings.OutputFormat.OutputTypeAdminRadar = "Simple";
if (_settings.OutputFormat.OutputTypeBetterChatMute == null)
_settings.OutputFormat.OutputTypeBetterChatMute = "Simple";
if (_settings.OutputFormat.OutputTypeClans == null)
_settings.OutputFormat.OutputTypeClans = "Simple";
if (_settings.OutputFormat.OutputTypeDangerousTreasures == null)
_settings.OutputFormat.OutputTypeDangerousTreasures = "Simple";
if (_settings.OutputFormat.OutputTypeDiscordAuth == null)
_settings.OutputFormat.OutputTypeDiscordAuth = "Simple";
if (_settings.OutputFormat.OutputTypeKits == null)
_settings.OutputFormat.OutputTypeKits = "Simple";
if (_settings.OutputFormat.OutputTypePMs == null)
_settings.OutputFormat.OutputTypePMs = "Simple";
if (_settings.OutputFormat.OutputTypeRaidableBases == null)
_settings.OutputFormat.OutputTypeRaidableBases = "Simple";
if (_settings.OutputFormat.OutputTypeVanish == null)
_settings.OutputFormat.OutputTypeVanish = "Simple";
if (_settings.OutputFormat.OutputTypeAirEvent == null)
_settings.OutputFormat.OutputTypeAirEvent = "Simple";
if (_settings.OutputFormat.OutputTypeArmoredTrainEvent == null)
_settings.OutputFormat.OutputTypeArmoredTrainEvent = "Simple";
if (_settings.OutputFormat.OutputTypeCargoTrainEvent == null)
_settings.OutputFormat.OutputTypeCargoTrainEvent = "Simple";
if (_settings.OutputFormat.OutputTypeConvoyEvent == null)
_settings.OutputFormat.OutputTypeConvoyEvent = "Simple";
if (_settings.OutputFormat.OutputTypeHarborEvent == null)
_settings.OutputFormat.OutputTypeHarborEvent = "Simple";
if (_settings.OutputFormat.OutputTypeJunkyardEvent == null)
_settings.OutputFormat.OutputTypeJunkyardEvent = "Simple";
if (_settings.OutputFormat.OutputTypePowerPlantEvent == null)
_settings.OutputFormat.OutputTypePowerPlantEvent = "Simple";
if (_settings.OutputFormat.OutputTypeSatDishEvent == null)
_settings.OutputFormat.OutputTypeSatDishEvent = "Simple";
if (_settings.OutputFormat.OutputTypeSputnikEvent == null)
_settings.OutputFormat.OutputTypeSputnikEvent = "Simple";
if (_settings.OutputFormat.OutputTypeWaterEvent == null)
_settings.OutputFormat.OutputTypeWaterEvent = "Simple";
foreach (var channel in _settings.Channels)
{
if (channel.CustomFilter == null)
{
channel.CustomFilter = new List<string>();
}
}
if (_settings.General.ReportCommand == null)
_settings.General.ReportCommand = "report";