Decompiled source of DiscordRcon v1.0.1
BepInEx\plugins\DiscordRcon.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers; using System.Buffers.Binary; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Data; using System.Data.SqlTypes; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Sockets; using System.Net.WebSockets; using System.Numerics; using System.Reflection; using System.Reflection.Emit; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using DSharpPlus; using DSharpPlus.AsyncEvents; using DSharpPlus.Entities; using DSharpPlus.EventArgs; using DSharpPlus.Exceptions; using DSharpPlus.Net; using DSharpPlus.Net.Abstractions; using DSharpPlus.Net.Models; using DSharpPlus.Net.Serialization; using DSharpPlus.Net.Udp; using DSharpPlus.Net.WebSocket; using DiscordRcon.Config; using DiscordRcon.Models; using DiscordRcon.Services; using FxResources.Microsoft.Extensions.Logging.Abstractions; using FxResources.System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Bson; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq.JsonPath; using Newtonsoft.Json.Schema; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("DiscordRcon")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+287a44b2b33c54b3377ff5808d24403da1499be1")] [assembly: AssemblyProduct("DiscordRcon")] [assembly: AssemblyTitle("DiscordRcon")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DiscordRcon { internal static class Core { public static ManualLogSource Log => Plugin.LogInstance; public static ConfigService ConfigService { get; private set; } public static RconService RconService { get; private set; } public static DiscordBotService DiscordBotService { get; private set; } public static CommandDiscoveryService CommandDiscoveryService { get; private set; } public static void Initialize() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown ConfigService = new ConfigService(); try { ConfigService.Initialize(); } catch (Exception ex) { ManualLogSource log = Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(52, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Config failed to load: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". DiscordRcon will not start."); } log.LogError(val); return; } RconService = new RconService(); DiscordBotService = new DiscordBotService(); CommandDiscoveryService = new CommandDiscoveryService(); if (ConfigService.IsFirstRun) { LogFirstRunSetup(); return; } DiscordBotService.Initialize(); LogStartupStatus(); } private static void LogFirstRunSetup() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown Log.LogWarning((object)"========================================"); Log.LogWarning((object)"DiscordRcon - First Run Setup Required"); Log.LogWarning((object)"========================================"); Log.LogWarning((object)"This mod needs configuration before it can start."); Log.LogWarning((object)""); Log.LogWarning((object)"1. Edit the BepInEx config file:"); ManualLogSource log = Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(30, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Paths.ConfigPath); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\\io.vrising.DiscordRcon.cfg"); } log.LogWarning(val); Log.LogWarning((object)" - Set Discord.BotToken to your bot token"); Log.LogWarning((object)" - Set Discord.GuildId to your Discord server ID"); Log.LogWarning((object)" - Set RCON.Password to your RCON password"); Log.LogWarning((object)""); Log.LogWarning((object)"2. Edit the role config file:"); ManualLogSource log2 = Log; val = new BepInExWarningLogInterpolatedStringHandler(3, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ConfigService.RoleConfigPath); } log2.LogWarning(val); Log.LogWarning((object)" - Add your Discord role IDs to adminRoles"); Log.LogWarning((object)" - Or add per-command grants in commandRoles"); Log.LogWarning((object)""); Log.LogWarning((object)"3. Optionally edit the custom commands file:"); ManualLogSource log3 = Log; val = new BepInExWarningLogInterpolatedStringHandler(3, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ConfigService.CustomCommandsPath); } log3.LogWarning(val); Log.LogWarning((object)" - Add or remove slash command shortcuts for RCON commands"); Log.LogWarning((object)""); Log.LogWarning((object)"4. Restart the server"); Log.LogWarning((object)"========================================"); } private static void LogStartupStatus() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown ConfigService configService = ConfigService; Log.LogInfo((object)"--- DiscordRcon Startup Status ---"); ManualLogSource log = Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(11, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" Discord: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.IsNullOrEmpty(configService.DiscordBotToken) ? "NOT CONFIGURED" : "Token set"); } log.LogInfo(val); ManualLogSource log2 = Log; val = new BepInExInfoLogInterpolatedStringHandler(17, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" Discord Guild: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>((configService.DiscordGuildId == 0L) ? "NOT SET" : configService.DiscordGuildId.ToString()); } log2.LogInfo(val); ManualLogSource log3 = Log; val = new BepInExInfoLogInterpolatedStringHandler(21, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" RCON: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(configService.RconHost); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(configService.RconPort); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (password "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.IsNullOrEmpty(configService.RconPassword) ? "NOT SET" : "set"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } log3.LogInfo(val); ManualLogSource log4 = Log; val = new BepInExInfoLogInterpolatedStringHandler(43, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" Role Config: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(configService.RoleConfig.AdminRoles.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" admin roles, "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(configService.RoleConfig.CommandRoles.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" command roles"); } log4.LogInfo(val); ManualLogSource log5 = Log; val = new BepInExInfoLogInterpolatedStringHandler(28, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" Custom Commands: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(configService.CustomCommands.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" commands"); } log5.LogInfo(val); Log.LogInfo((object)"-----------------------------------"); } public static void Shutdown() { DiscordBotService?.Shutdown(); RconService?.Shutdown(); } } [BepInPlugin("io.vrising.DiscordRcon", "DiscordRcon", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] internal class Plugin : BasePlugin { internal static Plugin Instance { get; private set; } public static ManualLogSource LogInstance => ((BasePlugin)Instance).Log; public static bool IsServer { get; private set; } public override void Load() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown Instance = this; IsServer = Application.productName == "VRisingServer"; if (!IsServer) { ((BasePlugin)this).Log.LogWarning((object)"DiscordRcon is a server-only plugin. It will not function on the client."); return; } Core.Initialize(); ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(23, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("DiscordRcon"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.0.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded successfully!"); } log.LogInfo(val); } public override bool Unload() { if (IsServer) { Core.Shutdown(); } return true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "io.vrising.DiscordRcon"; public const string PLUGIN_NAME = "DiscordRcon"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace DiscordRcon.Services { public class CommandDiscoveryService { private volatile int _discoveryRunning; private static readonly Regex _ansiRegex = new Regex("\\x1b\\[\\d+(?:;\\d+)*m", RegexOptions.Compiled); public List<DiscoveredCommand> DiscoveredCommands { get; private set; } = new List<DiscoveredCommand>(); public bool IsReady { get; private set; } public void RunDiscovery() { if (Interlocked.CompareExchange(ref _discoveryRunning, 1, 0) == 0) { DiscoverAndRetryAsync(); } } private async Task DiscoverAndRetryAsync() { int attempt = 0; try { bool flag = default(bool); while (true) { if (await DiscoverAndBuildIndexAsync()) { IsReady = true; return; } attempt++; if (attempt >= 20) { break; } ManualLogSource log = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(47, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discovery failed (attempt "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(attempt); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("), retrying in 30s..."); } log.LogInfo(val); await Task.Delay(30000); } Core.Log.LogError((object)"Discovery failed after 20 attempts. Restart the server to retry."); } finally { Interlocked.Exchange(ref _discoveryRunning, 0); } } private async Task<bool> DiscoverAndBuildIndexAsync() { bool flag = default(bool); try { RconResult rconResult = await Core.RconService.SendCommandAsync("help", 30000); if (!rconResult.Success) { ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(18, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discovery failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(rconResult.Message); } log.LogWarning(val); return false; } string helpText = _ansiRegex.Replace(rconResult.Message, ""); List<DiscoveredCommand> list2 = (DiscoveredCommands = ParseHelpOutput(helpText)); IsReady = true; ManualLogSource log2 = Core.Log; BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(37, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Discovery complete: indexed "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(list2.Count); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" commands"); } log2.LogInfo(val2); return true; } catch (Exception ex) { ManualLogSource log3 = Core.Log; BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(17, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Discovery error: "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message); } log3.LogError(val3); return false; } } private List<DiscoveredCommand> ParseHelpOutput(string helpText) { List<DiscoveredCommand> list = new List<DiscoveredCommand>(); string[] array = helpText.Split('\n'); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (string.IsNullOrEmpty(text) || text.StartsWith("Total commands:", StringComparison.OrdinalIgnoreCase) || !text.StartsWith("-")) { continue; } string text2 = text; string text3 = text2.Substring(1, text2.Length - 1).Trim(); int num = text3.IndexOf(':'); if (num < 0) { continue; } string text4 = text3.Substring(0, num).Trim(); if (!string.IsNullOrEmpty(text4)) { text2 = text3; int num2 = num + 1; string text5 = text2.Substring(num2, text2.Length - num2).Trim(); string usage = ""; string description = text5; int num3 = text5.IndexOf(" - "); if (num3 >= 0) { usage = text5.Substring(0, num3).Trim(); text2 = text5; num2 = num3 + 3; description = text2.Substring(num2, text2.Length - num2).Trim(); } list.Add(new DiscoveredCommand(text4, usage, description)); } } return list; } public List<DiscoveredCommand> SearchCommands(string query) { if (string.IsNullOrEmpty(query) || DiscoveredCommands.Count == 0) { return new List<DiscoveredCommand>(); } string query2 = query.ToLowerInvariant(); List<DiscoveredCommand> list = PrefixMatches(query2); if (list.Count > 0) { return list; } List<DiscoveredCommand> list2 = ParentPrefixMatches(query2); if (list2.Count > 0) { return list2; } List<DiscoveredCommand> list3 = SubstringMatches(query2); if (list3.Count > 0) { return list3; } return new List<DiscoveredCommand>(); } private List<DiscoveredCommand> PrefixMatches(string query) { return DiscoveredCommands.Where((DiscoveredCommand c) => c.CommandId.ToLowerInvariant() == query || c.CommandId.ToLowerInvariant().StartsWith(query + ".")).ToList(); } private List<DiscoveredCommand> ParentPrefixMatches(string query) { int num = query.LastIndexOf('.'); while (num > 0) { string text = query.Substring(0, num); List<DiscoveredCommand> list = PrefixMatches(text); if (list.Count > 0) { return list; } num = text.LastIndexOf('.'); } return new List<DiscoveredCommand>(); } private List<DiscoveredCommand> SubstringMatches(string query) { return DiscoveredCommands.Where((DiscoveredCommand c) => c.CommandId.ToLowerInvariant().Contains(query)).Take(10).ToList(); } public DiscoveredCommand FindExact(string commandId) { return DiscoveredCommands.FirstOrDefault((DiscoveredCommand c) => string.Equals(c.CommandId, commandId, StringComparison.OrdinalIgnoreCase)); } } public class DiscordBotService { private DiscordClient _client; private bool _shuttingDown; private Dictionary<string, CustomCommand> _customCommandMap = new Dictionary<string, CustomCommand>(StringComparer.OrdinalIgnoreCase); private static readonly FieldInfo _roleIdField = typeof(DiscordMember).GetField("_role_ids", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly Regex _ansiRegex = new Regex("\\x1b\\[(\\d+(?:;\\d+)*)m", RegexOptions.Compiled); private static readonly Regex _validSlashName = new Regex("^[a-z0-9_-]{1,32}$", RegexOptions.Compiled); private static string AnsiToMarkdown(string input) { StringBuilder stringBuilder = new StringBuilder(input.Length); string text = input; bool flag = false; while (text.Length > 0) { Match match = _ansiRegex.Match(text); if (!match.Success) { stringBuilder.Append(text); break; } stringBuilder.Append(text.Substring(0, match.Index)); string[] array = match.Groups[1].Value.Split(';'); if (array.Contains("0")) { if (flag) { stringBuilder.Append("**"); flag = false; } } else { string[] array2 = array; foreach (string text2 in array2) { if ((text2 == "1" || text2 == "97") && !flag) { stringBuilder.Append("**"); flag = true; } } } string text3 = text; int i = match.Index + match.Length; text = text3.Substring(i, text3.Length - i); } if (flag) { stringBuilder.Append("**"); } return stringBuilder.ToString(); } private static List<ulong> GetMemberRoleIds(DiscordMember member) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown bool flag = default(bool); try { return member.Roles.Select((DiscordRole r) => r.Id).ToList(); } catch (KeyNotFoundException) { if (Core.ConfigService.LogDiscordEvents) { ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(87, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Perm] DiscordMember.Roles threw KeyNotFoundException for "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(member.Username); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(member.Id); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("), falling back to _role_ids"); } log.LogWarning(val); } } if (_roleIdField == null) { Core.Log.LogWarning((object)"[Perm] _role_ids field not found via reflection"); return null; } try { List<ulong> list = (List<ulong>)_roleIdField.GetValue(member); if (Core.ConfigService.LogDiscordEvents) { ManualLogSource log2 = Core.Log; BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(40, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Perm] read "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(list.Count); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" role IDs from _role_ids: ["); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(string.Join(", ", list)); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("]"); } log2.LogInfo(val2); } return list; } catch (Exception ex2) { ManualLogSource log3 = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Perm] failed to read _role_ids: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex2.Message); } log3.LogWarning(val); return null; } } private static string FormatSuggestions(List<DiscoveredCommand> matches) { StringBuilder stringBuilder = new StringBuilder(); foreach (DiscoveredCommand match in matches) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("\n - **"); handler.AppendFormatted(match.CommandId); handler.AppendLiteral("**"); stringBuilder3.Append(ref handler); if (!string.IsNullOrEmpty(match.Usage)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2); handler.AppendLiteral(" `"); handler.AppendFormatted(match.Usage); handler.AppendLiteral("`"); stringBuilder4.Append(ref handler); } if (!string.IsNullOrEmpty(match.Description)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2); handler.AppendLiteral(" - "); handler.AppendFormatted(match.Description); stringBuilder5.Append(ref handler); } } return stringBuilder.ToString(); } private static string FormatFullListing(List<DiscoveredCommand> commands) { StringBuilder stringBuilder = new StringBuilder(); foreach (DiscoveredCommand command in commands) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(6, 1, stringBuilder2); handler.AppendLiteral("- **"); handler.AppendFormatted(command.CommandId); handler.AppendLiteral("**"); stringBuilder3.Append(ref handler); if (!string.IsNullOrEmpty(command.Usage)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2); handler.AppendLiteral(" `"); handler.AppendFormatted(command.Usage); handler.AppendLiteral("`"); stringBuilder4.Append(ref handler); } if (!string.IsNullOrEmpty(command.Description)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2); handler.AppendLiteral(" - "); handler.AppendFormatted(command.Description); stringBuilder5.Append(ref handler); } stringBuilder.Append('\n'); } return stringBuilder.ToString(); } public void Initialize() { ConfigService configService = Core.ConfigService; if (string.IsNullOrEmpty(configService.DiscordBotToken)) { Core.Log.LogWarning((object)"Discord bot token is not configured. Set Discord.BotToken in the config and restart."); return; } if (configService.DiscordGuildId == 0L) { Core.Log.LogWarning((object)"Discord guild ID is not configured. Set Discord.GuildId in the config and restart."); return; } _client = new DiscordClient(new DiscordConfiguration { Token = configService.DiscordBotToken, TokenType = TokenType.Bot, Intents = (DiscordIntents.Guilds | DiscordIntents.GuildMembers) }); _client.InteractionCreated += OnInteractionCreated; _client.SocketErrored += OnSocketError; _client.SocketClosed += OnSocketClosed; _client.Ready += OnReady; _client.GuildAvailable += OnGuildAvailable; ConnectAsync(); } private async Task ConnectAsync() { try { await _client.ConnectAsync(); } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(106, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to connect Discord bot: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Verify your BotToken and that the bot is not already connected elsewhere."); } log.LogError(val); } } private async Task OnReady(DiscordClient sender, ReadyEventArgs e) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discord bot is ready, guilds in cache: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(sender.Guilds.Count); } log.LogInfo(val); if (sender.Guilds.Count == 0) { Core.Log.LogWarning((object)"Discord guild cache is empty. Check that the bot has been added to your server."); } await RegisterSlashCommandsAsync(); Core.Log.LogInfo((object)"Discovery will run in 60s"); Task.Run(async delegate { await Task.Delay(60000); try { Core.CommandDiscoveryService.RunDiscovery(); } catch (Exception ex) { ManualLogSource log2 = Core.Log; bool flag2 = default(bool); BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(24, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Discovery launch error: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); } log2.LogError(val2); } }); } private async Task RegisterSlashCommandsAsync() { bool flag = default(bool); try { ulong discordGuildId = Core.ConfigService.DiscordGuildId; List<DiscordApplicationCommand> commands = new List<DiscordApplicationCommand> { new DiscordApplicationCommand("rcon", "Execute an RCON command", new List<DiscordApplicationCommandOption> { new DiscordApplicationCommandOption("command", "The RCON command to execute", ApplicationCommandOptionType.String, true) }), new DiscordApplicationCommand("help", "Show available RCON commands or details for a specific command", new List<DiscordApplicationCommandOption> { new DiscordApplicationCommandOption("command", "Command name to get help for", ApplicationCommandOptionType.String, false) }) }; _customCommandMap.Clear(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (CustomCommand customCommand in Core.ConfigService.CustomCommands) { if (string.IsNullOrEmpty(customCommand.Name) || string.IsNullOrEmpty(customCommand.RconCommand)) { ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(54, 0, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Skipping custom command with empty name or rconCommand"); } log.LogWarning(val); } else if (customCommand.Name == "rcon" || customCommand.Name == "help") { ManualLogSource log2 = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(65, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Custom command name '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(customCommand.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' conflicts with built-in commands, skipping"); } log2.LogWarning(val); } else if (!_validSlashName.IsMatch(customCommand.Name)) { ManualLogSource log3 = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(107, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Custom command name '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(customCommand.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' is invalid (must be lowercase, 1-32 chars, a-z/0-9/hyphen/underscore only), skipping"); } log3.LogWarning(val); } else if (!hashSet.Add(customCommand.Name)) { ManualLogSource log4 = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(42, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Duplicate custom command name '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(customCommand.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("', skipping"); } log4.LogWarning(val); } else { _customCommandMap[customCommand.Name] = customCommand; commands.Add(new DiscordApplicationCommand(customCommand.Name, customCommand.Description ?? ("Shortcut for " + customCommand.RconCommand), new List<DiscordApplicationCommandOption> { new DiscordApplicationCommandOption("arguments", "Arguments for " + customCommand.RconCommand, ApplicationCommandOptionType.String, false) })); } } await _client.BulkOverwriteGuildApplicationCommandsAsync(discordGuildId, commands); ManualLogSource log5 = Core.Log; BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(50, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Registered "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(commands.Count); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" slash commands (/rcon, /help, "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(_customCommandMap.Count); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" custom)"); } log5.LogInfo(val2); } catch (Exception ex) { ManualLogSource log6 = Core.Log; BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(35, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Failed to register slash commands: "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message); } log6.LogError(val3); } } private Task OnGuildAvailable(DiscordClient sender, GuildCreateEventArgs e) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (Core.ConfigService.LogDiscordEvents) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(27, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discord guild available: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.Guild.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(e.Guild.Id); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } log.LogInfo(val); } return Task.CompletedTask; } private async Task OnInteractionCreated(DiscordClient sender, InteractionCreateEventArgs e) { if (e.Interaction.Type == InteractionType.ApplicationCommand) { DiscordInteraction interaction = e.Interaction; string name = interaction.Data.Name; CustomCommand value; if (name == "rcon") { await HandleRconAsync(interaction); } else if (name == "help") { await HandleHelpAsync(interaction); } else if (_customCommandMap.TryGetValue(name, out value)) { await HandleCustomCommandAsync(interaction, value); } } } private async Task HandleRconAsync(DiscordInteraction interaction) { string commandText = (interaction.Data.Options?.FirstOrDefault((DiscordInteractionDataOption o) => o.Name == "command"))?.Value?.ToString() ?? ""; if (string.IsNullOrWhiteSpace(commandText)) { await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":x: No command provided.")); return; } string commandId = commandText.Split(' ')[0]; ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(10, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[/rcon] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(interaction.User.Username); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(commandText); } log.LogInfo(val); if (!(await HasPermission(interaction.User, interaction.GuildId, commandId))) { await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":x: You don't have permission to use that command.")); return; } await interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); RconResult rconResult = await Core.RconService.SendCommandAsync(commandText); string text = AnsiToMarkdown(rconResult.Message); if (!rconResult.Success) { text = ":x: " + text; } else if (IsUnknownCommand(text) && Core.CommandDiscoveryService.IsReady) { List<DiscoveredCommand> list = Core.CommandDiscoveryService.SearchCommands(commandId); if (list.Count > 0) { text = text + "\n\nDid you mean one of these?" + FormatSuggestions(list); } } await RespondAsync(interaction, text); } private async Task HandleHelpAsync(DiscordInteraction interaction) { string query = (interaction.Data.Options?.FirstOrDefault((DiscordInteractionDataOption o) => o.Name == "command"))?.Value?.ToString() ?? ""; ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(10, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[/help] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(interaction.User.Username); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.IsNullOrEmpty(query) ? "(full listing)" : query); } log.LogInfo(val); if (string.IsNullOrEmpty(query)) { if (!(await HasAnyPermission(interaction.User, interaction.GuildId))) { await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":x: You don't have permission to use that command.")); return; } } else if (!(await HasPermission(interaction.User, interaction.GuildId, query))) { await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":x: You don't have permission to use that command.")); return; } await interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); string text; if (Core.CommandDiscoveryService.IsReady) { if (string.IsNullOrEmpty(query)) { text = FormatFullListing(Core.CommandDiscoveryService.DiscoveredCommands); } else { DiscoveredCommand discoveredCommand = Core.CommandDiscoveryService.FindExact(query); if (discoveredCommand != null) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder2); handler.AppendLiteral("**"); handler.AppendFormatted(discoveredCommand.CommandId); handler.AppendLiteral("**"); stringBuilder3.Append(ref handler); if (!string.IsNullOrEmpty(discoveredCommand.Usage)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(10, 1, stringBuilder2); handler.AppendLiteral("\nUsage: `"); handler.AppendFormatted(discoveredCommand.Usage); handler.AppendLiteral("`"); stringBuilder4.Append(ref handler); } if (!string.IsNullOrEmpty(discoveredCommand.Description)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2); handler.AppendLiteral("\n"); handler.AppendFormatted(discoveredCommand.Description); stringBuilder5.Append(ref handler); } text = stringBuilder.ToString(); } else { List<DiscoveredCommand> list = Core.CommandDiscoveryService.SearchCommands(query); text = ((list.Count <= 0) ? ("No commands found matching \"" + query + "\".") : ("No exact match for \"" + query + "\". Similar commands:" + FormatSuggestions(list))); } } } else { string command = (string.IsNullOrEmpty(query) ? "help" : ("help " + query)); RconResult rconResult = await Core.RconService.SendCommandAsync(command); text = AnsiToMarkdown(rconResult.Message); if (!rconResult.Success) { text = ":x: " + text; } } await RespondAsync(interaction, text); } private static bool IsUnknownCommand(string response) { return response.IndexOf("unknown command", StringComparison.OrdinalIgnoreCase) >= 0; } private async Task HandleCustomCommandAsync(DiscordInteraction interaction, CustomCommand custom) { string text = (interaction.Data.Options?.FirstOrDefault((DiscordInteractionDataOption o) => o.Name == "arguments"))?.Value?.ToString() ?? ""; string commandText = (string.IsNullOrEmpty(text) ? custom.RconCommand : (custom.RconCommand + " " + text)); ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(6, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[/"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(custom.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(interaction.User.Username); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(commandText); } log.LogInfo(val); if (!(await HasPermission(interaction.User, interaction.GuildId, custom.RconCommand))) { await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":x: You don't have permission to use that command.")); return; } await interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); RconResult rconResult = await Core.RconService.SendCommandAsync(commandText); string text2 = AnsiToMarkdown(rconResult.Message); if (!rconResult.Success) { text2 = ":x: " + text2; } else if (IsUnknownCommand(text2) && Core.CommandDiscoveryService.IsReady) { List<DiscoveredCommand> list = Core.CommandDiscoveryService.SearchCommands(custom.RconCommand); if (list.Count > 0) { text2 = text2 + "\n\nDid you mean one of these?" + FormatSuggestions(list); } } await RespondAsync(interaction, text2); } private async Task RespondAsync(DiscordInteraction interaction, string text) { if (text.Length <= 2000) { await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(text)); } else { await RespondLongAsync(interaction, text); } } private async Task RespondLongAsync(DiscordInteraction interaction, string text) { string[] array = text.Split('\n'); string text2 = ""; bool first = true; string[] array2 = array; foreach (string line in array2) { if (text2.Length + line.Length + 1 > 2000) { if (!first) { await interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder().WithContent(text2)); } else { await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(text2)); first = false; } text2 = line; } else { text2 = ((text2.Length == 0) ? line : (text2 + "\n" + line)); } } if (text2.Length > 0) { if (first) { await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(text2)); } else { await interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder().WithContent(text2)); } } } private async Task<bool> HasAnyPermission(DiscordUser user, ulong? guildId) { var (flag, source) = await ResolveUserRoleIds(user, guildId); if (!flag) { return false; } RoleConfig roleConfig = Core.ConfigService.RoleConfig; if (roleConfig.AdminRoles.Count > 0 && source.Any((string id) => roleConfig.AdminRoles.Contains(id))) { return true; } foreach (List<string> entry in roleConfig.CommandRoles.Values) { if (source.Any((string id) => entry.Contains(id))) { return true; } } return false; } private async Task<(bool resolved, List<string> roleIds)> ResolveUserRoleIds(DiscordUser user, ulong? guildId) { if (_client == null) { return (false, null); } if (user == null) { Core.Log.LogWarning((object)"[Perm] user is null, denying"); return (false, null); } List<ulong> roleIds = null; if (user is DiscordMember member) { roleIds = GetMemberRoleIds(member); } else if (guildId.HasValue) { if (_client.Guilds.TryGetValue(guildId.Value, out var value) && value.Members.TryGetValue(user.Id, out var value2)) { roleIds = GetMemberRoleIds(value2); } if (roleIds == null) { try { roleIds = GetMemberRoleIds(await (await _client.GetGuildAsync(guildId.Value)).GetMemberAsync(user.Id)); } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Perm] REST API fallback failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } log.LogWarning(val); } } } if (roleIds == null || roleIds.Count == 0) { Core.Log.LogWarning((object)"[Perm] no role IDs resolved, denying"); return (false, null); } return (true, roleIds.Select((ulong id) => id.ToString()).ToList()); } private async Task<bool> HasPermission(DiscordUser user, ulong? guildId, string commandId) { var (flag, source) = await ResolveUserRoleIds(user, guildId); if (!flag) { return false; } RoleConfig roleConfig = Core.ConfigService.RoleConfig; if (roleConfig.AdminRoles.Count > 0 && source.Any((string id) => roleConfig.AdminRoles.Contains(id))) { return true; } if (roleConfig.CommandRoles.TryGetValue(commandId, out var commandRoleIds) && source.Any((string id) => commandRoleIds.Contains(id))) { return true; } Core.Log.LogWarning((object)"[Perm] no admin or command role match, denying"); return false; } private Task OnSocketError(DiscordClient sender, SocketErrorEventArgs e) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(22, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discord socket error: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.Exception?.Message); } log.LogWarning(val); return Task.CompletedTask; } private Task OnSocketClosed(DiscordClient sender, SocketCloseEventArgs e) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (_shuttingDown) { return Task.CompletedTask; } ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(26, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discord socket closed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(e.CloseCode); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" - "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.CloseMessage); } log.LogWarning(val); ReconnectWithBackoffAsync(); return Task.CompletedTask; } private async Task ReconnectWithBackoffAsync() { int[] delays = new int[7] { 1, 2, 4, 8, 16, 32, 60 }; int attempt = 0; bool flag = default(bool); while (!_shuttingDown && attempt < delays.Length) { await Task.Delay(delays[attempt] * 1000); try { ManualLogSource log = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(29, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Discord reconnect attempt "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(attempt + 1); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("..."); } log.LogInfo(val); await _client.ConnectAsync(); Core.Log.LogInfo((object)"Discord reconnected successfully"); return; } catch (Exception ex) { ManualLogSource log2 = Core.Log; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(26, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Discord reconnect failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); } log2.LogWarning(val2); attempt++; } } Core.Log.LogError((object)"Discord reconnect exhausted all attempts. Manual restart required."); } public void Shutdown() { _shuttingDown = true; if (_client != null) { _client.DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false).GetAwaiter() .GetResult(); _client.Dispose(); } } } public class RconClient : IDisposable { private TcpClient _tcp; private NetworkStream _stream; private int _requestId; private const int SERVERDATA_AUTH = 3; private const int SERVERDATA_AUTH_RESPONSE = 2; private const int SERVERDATA_EXECCOMMAND = 2; private const int SERVERDATA_RESPONSE_VALUE = 0; public async Task ConnectAsync(string host, int port, string password, int timeoutMs = 5000) { _tcp = new TcpClient(); IPAddress[] array = await Dns.GetHostAddressesAsync(host); if (array.Length == 0) { throw new Exception("Could not resolve RCON host: " + host); } using CancellationTokenSource cts = new CancellationTokenSource(timeoutMs); await _tcp.ConnectAsync(array[0], port, cts.Token); _stream = _tcp.GetStream(); SendPacket(NextRequestId(), 3, password); (int, int, string) obj; int num; do { obj = await ReadPacketAsync(cts.Token); (num, _, _) = obj; } while (obj.Item2 != 2); if (num == -1) { throw new Exception("RCON authentication failed"); } } public async Task<string> SendCommandAsync(string command, int timeoutMs = 10000) { SendPacket(NextRequestId(), 2, command); using CancellationTokenSource cts = new CancellationTokenSource(timeoutMs); int item; string item2; do { (int, int, string) obj = await ReadPacketAsync(cts.Token); item = obj.Item2; item2 = obj.Item3; } while (item != 0); return item2; } private int NextRequestId() { return ++_requestId; } private void SendPacket(int id, int type, string body) { byte[] bytes = Encoding.UTF8.GetBytes(body); int num = 8 + bytes.Length + 2; byte[] array = new byte[4 + num]; BitConverter.TryWriteBytes(array.AsSpan(0), num); BitConverter.TryWriteBytes(array.AsSpan(4), id); BitConverter.TryWriteBytes(array.AsSpan(8), type); bytes.CopyTo(array, 12); _stream.Write(array, 0, array.Length); } private async Task<(int id, int type, string body)> ReadPacketAsync(CancellationToken ct = default(CancellationToken)) { byte[] sizeBuf = new byte[4]; await ReadExactAsync(sizeBuf, ct); int size = BitConverter.ToInt32(sizeBuf, 0); if (size < 10 || size > 65536) { throw new Exception($"Invalid RCON packet size: {size}"); } byte[] data = new byte[size]; await ReadExactAsync(data, ct); int item = BitConverter.ToInt32(data, 0); int item2 = BitConverter.ToInt32(data, 4); int num = size - 4 - 4 - 2; string item3 = ((num > 0) ? Encoding.UTF8.GetString(data, 8, num) : ""); return (item, item2, item3); } private async Task ReadExactAsync(byte[] buffer, CancellationToken ct) { int num; for (int offset = 0; offset < buffer.Length; offset += num) { num = await _stream.ReadAsync(buffer, offset, buffer.Length - offset, ct); if (num == 0) { throw new Exception("RCON connection closed"); } } } public void Dispose() { _stream?.Dispose(); _tcp?.Dispose(); } } public class RconService { private readonly SemaphoreSlim _concurrencyLimiter = new SemaphoreSlim(1, 1); public async Task<RconResult> SendCommandAsync(string command, int? timeoutMs = null) { ConfigService cfg = Core.ConfigService; if (string.IsNullOrEmpty(cfg.RconPassword)) { return RconResult.Fail("RCON password not configured"); } await _concurrencyLimiter.WaitAsync(); bool flag = default(bool); try { int timeout = timeoutMs ?? cfg.RconCommandTimeoutMs; using RconClient client = new RconClient(); await client.ConnectAsync(cfg.RconHost, cfg.RconPort, cfg.RconPassword); string text = await client.SendCommandAsync(command, timeout); if (Core.ConfigService.LogRconEvents) { ManualLogSource log = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(11, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("RCON ["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(command); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] -> "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text); } log.LogInfo(val); } return RconResult.Ok(string.IsNullOrEmpty(text) ? "Command executed (no output)" : text); } catch (OperationCanceledException) { return RconResult.Fail("RCON command timed out"); } catch (Exception ex2) { ManualLogSource log2 = Core.Log; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(13, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("RCON failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex2.Message); } log2.LogWarning(val2); return RconResult.Fail("RCON not ready: " + ex2.Message); } finally { _concurrencyLimiter.Release(); } } public void Shutdown() { _concurrencyLimiter.Dispose(); } } public readonly struct RconResult { public bool Success { get; } public string Message { get; } private RconResult(bool success, string message) { Success = success; Message = message; } public static RconResult Ok(string message) { return new RconResult(success: true, message); } public static RconResult Fail(string message) { return new RconResult(success: false, message); } } } namespace DiscordRcon.Models { public class CustomCommand { [JsonPropertyName("name")] public string Name { get; set; } = ""; [JsonPropertyName("description")] public string Description { get; set; } = ""; [JsonPropertyName("rconCommand")] public string RconCommand { get; set; } = ""; } public class DiscoveredCommand { public string CommandId { get; set; } = ""; public string Usage { get; set; } = ""; public string Description { get; set; } = ""; public DiscoveredCommand(string commandId, string usage, string description) { CommandId = commandId; Usage = usage; Description = description; } } public class RoleConfig { [JsonPropertyName("adminRoles")] public List<string> AdminRoles { get; set; } = new List<string>(); [JsonPropertyName("commandRoles")] public Dictionary<string, List<string>> CommandRoles { get; set; } = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); } } namespace DiscordRcon.Config { public class ConfigService { public static readonly string ConfigDir = Path.Combine(Paths.ConfigPath, "DiscordRcon"); public static readonly string RoleConfigPath = Path.Combine(ConfigDir, "discordrcon_roles.json"); public static readonly string CustomCommandsPath = Path.Combine(ConfigDir, "discordrcon_commands.json"); public string DiscordBotToken { get; private set; } = ""; public ulong DiscordGuildId { get; private set; } public string RconHost { get; private set; } = "127.0.0.1"; public int RconPort { get; private set; } = 25575; public string RconPassword { get; private set; } = ""; public int RconCommandTimeoutMs { get; private set; } = 10000; public bool LogDiscordEvents { get; private set; } public bool LogRconEvents { get; private set; } public RoleConfig RoleConfig { get; private set; } = new RoleConfig(); public List<CustomCommand> CustomCommands { get; private set; } = new List<CustomCommand>(); public bool IsFirstRun { get; private set; } public void Initialize() { Directory.CreateDirectory(ConfigDir); BindConfig(); DetectFirstRun(); LoadRoleConfig(); LoadCustomCommands(); } private void DetectFirstRun() { IsFirstRun = string.IsNullOrEmpty(DiscordBotToken) && DiscordGuildId == 0L && string.IsNullOrEmpty(RconPassword); } private void BindConfig() { ConfigFile config = ((BasePlugin)Plugin.Instance).Config; DiscordBotToken = config.Bind<string>("Discord", "BotToken", "", "Discord bot token. Get this from https://discord.com/developers/applications").Value; DiscordGuildId = config.Bind<ulong>("Discord", "GuildId", 0uL, "Discord server (guild) ID for registering slash commands").Value; RconHost = config.Bind<string>("RCON", "Host", "127.0.0.1", "RCON server hostname").Value; RconPort = config.Bind<int>("RCON", "Port", 25575, "RCON server port").Value; RconPassword = config.Bind<string>("RCON", "Password", "", "RCON server password").Value; RconCommandTimeoutMs = config.Bind<int>("RCON", "CommandTimeoutMs", 10000, "Timeout in milliseconds for RCON command responses").Value; LogDiscordEvents = config.Bind<bool>("Debug", "LogDiscordEvents", false, "Verbose logging of Discord gateway events").Value; LogRconEvents = config.Bind<bool>("Debug", "LogRconEvents", false, "Verbose logging of RCON events").Value; } private void LoadRoleConfig() { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown bool flag = default(bool); if (!File.Exists(RoleConfigPath)) { RoleConfig config = new RoleConfig(); SaveRoleConfig(config); ManualLogSource log = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(23, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Created role config at "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(RoleConfigPath); } log.LogInfo(val); } try { string json = File.ReadAllText(RoleConfigPath); RoleConfig = System.Text.Json.JsonSerializer.Deserialize<RoleConfig>(json) ?? new RoleConfig(); ManualLogSource log2 = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(48, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Loaded role config: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(RoleConfig.AdminRoles.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" admin roles, "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(RoleConfig.CommandRoles.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" command roles"); } log2.LogInfo(val); } catch (Exception ex) { ManualLogSource log3 = Core.Log; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(66, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to load role config: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(". Fix or delete the file and restart: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(RoleConfigPath); } log3.LogError(val2); throw; } } private void SaveRoleConfig(RoleConfig config) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown try { string contents = System.Text.Json.JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(RoleConfigPath, contents); } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(28, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to save role config: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } log.LogError(val); } } private void LoadCustomCommands() { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown bool flag = default(bool); if (!File.Exists(CustomCommandsPath)) { List<CustomCommand> commands = new List<CustomCommand> { new CustomCommand { Name = "kick", Description = "Kick a player from the server", RconCommand = "kick" }, new CustomCommand { Name = "ban", Description = "Ban a player from the server", RconCommand = "ban" }, new CustomCommand { Name = "listplayers", Description = "List all online players", RconCommand = "listplayers" } }; SaveCustomCommands(commands); ManualLogSource log = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(34, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Created custom commands config at "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(CustomCommandsPath); } log.LogInfo(val); } try { string json = File.ReadAllText(CustomCommandsPath); CustomCommands = System.Text.Json.JsonSerializer.Deserialize<List<CustomCommand>>(json) ?? new List<CustomCommand>(); ManualLogSource log2 = Core.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Loaded custom commands: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(CustomCommands.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" commands"); } log2.LogInfo(val); } catch (Exception ex) { ManualLogSource log3 = Core.Log; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(70, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to load custom commands: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(". Fix or delete the file and restart: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(CustomCommandsPath); } log3.LogError(val2); throw; } } private void SaveCustomCommands(List<CustomCommand> commands) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown try { string contents = System.Text.Json.JsonSerializer.Serialize(commands, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(CustomCommandsPath, contents); } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(32, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to save custom commands: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } log.LogError(val); } } } } [CompilerGenerated] internal sealed class <cced586a-35fd-45bb-938e-6d9753f8c7da><PrivateImplementationDetails> { [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 28)] internal struct __StaticArrayInitTypeSize=28 { } internal static readonly __StaticArrayInitTypeSize=28 BF7002736ADB60A23C32230A9AF55DF45D5C23AD0F3F03C0AF765A16CDB52EB6/* Not supported: data(01 00 00 00 02 00 00 00 04 00 00 00 08 00 00 00 10 00 00 00 20 00 00 00 3C 00 00 00) */; } namespace Newtonsoft.Json { internal enum ConstructorHandling { Default, AllowNonPublicDefaultConstructor } internal enum DateFormatHandling { IsoDateFormat, MicrosoftDateFormat } internal enum DateParseHandling { None, DateTime, DateTimeOffset } internal enum DateTimeZoneHandling { Local, Utc, Unspecified, RoundtripKind } internal class DefaultJsonNameTable : JsonNameTable { private class Entry { internal readonly string Value; internal readonly int HashCode; internal Entry Next; internal Entry(string value, int hashCode, Entry next) { Value = value; HashCode = hashCode; Next = next; } } private static readonly int HashCodeRandomizer; private int _count; private Entry[] _entries; private int _mask = 31; static DefaultJsonNameTable() { HashCodeRandomizer = Environment.TickCount; } public DefaultJsonNameTable() { _entries = new Entry[_mask + 1]; } public override string? Get(char[] key, int start, int length) { if (length == 0) { return string.Empty; } int num = length + HashCodeRandomizer; num += (num << 7) ^ key[start]; int num2 = start + length; for (int i = start + 1; i < num2; i++) { num += (num << 7) ^ key[i]; } num -= num >> 17; num -= num >> 11; num -= num >> 5; int num3 = Volatile.Read(ref _mask); int num4 = num & num3; for (Entry entry = _entries[num4]; entry != null; entry = entry.Next) { if (entry.HashCode == num && TextEquals(entry.Value, key, start, length)) { return entry.Value; } } return null; } public string Add(string key) { if (key == null) { throw new ArgumentNullException("key"); } int length = key.Length; if (length == 0) { return string.Empty; } int num = length + HashCodeRandomizer; for (int i = 0; i < key.Length; i++) { num += (num << 7) ^ key[i]; } num -= num >> 17; num -= num >> 11; num -= num >> 5; for (Entry entry = _entries[num & _mask]; entry != null; entry = entry.Next) { if (entry.HashCode == num && entry.Value.Equals(key, StringComparison.Ordinal)) { return entry.Value; } } return AddEntry(key, num); } private string AddEntry(string str, int hashCode) { int num = hashCode & _mask; Entry entry = new Entry(str, hashCode, _entries[num]); _entries[num] = entry; if (_count++ == _mask) { Grow(); } return entry.Value; } private void Grow() { Entry[] entries = _entries; int num = _mask * 2 + 1; Entry[] array = new Entry[num + 1]; for (int i = 0; i < entries.Length; i++) { Entry entry = entries[i]; while (entry != null) { int num2 = entry.HashCode & num; Entry next = entry.Next; entry.Next = array[num2]; array[num2] = entry; entry = next; } } _entries = array; Volatile.Write(ref _mask, num); } private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length) { if (str1.Length != str2Length) { return false; } for (int i = 0; i < str1.Length; i++) { if (str1[i] != str2[str2Start + i]) { return false; } } return true; } } [Flags] internal enum DefaultValueHandling { Include = 0, Ignore = 1, Populate = 2, IgnoreAndPopulate = 3 } internal enum FloatFormatHandling { String, Symbol, DefaultValue } internal enum FloatParseHandling { Double, Decimal } internal enum Formatting { None, Indented } internal interface IArrayPool<T> { T[] Rent(int minimumLength); void Return(T[]? array); } internal interface IJsonLineInfo { int LineNumber { get; } int LinePosition { get; } bool HasLineInfo(); } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] internal sealed class JsonArrayAttribute : JsonContainerAttribute { private bool _allowNullItems; public bool AllowNullItems { get { return _allowNullItems; } set { _allowNullItems = value; } } public JsonArrayAttribute() { } public JsonArrayAttribute(bool allowNullItems) { _allowNullItems = allowNullItems; } public JsonArrayAttribute(string id) : base(id) { } } [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)] internal sealed class JsonConstructorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] internal abstract class JsonContainerAttribute : Attribute { internal bool? _isReference; internal bool? _itemIsReference; internal ReferenceLoopHandling? _itemReferenceLoopHandling; internal TypeNameHandling? _itemTypeNameHandling; private Type? _namingStrategyType; private object[]? _namingStrategyParameters; public string? Id { get; set; } public string? Title { get; set; } public string? Description { get; set; } public Type? ItemConverterType { get; set; } public object[]? ItemConverterParameters { get; set; } public Type? NamingStrategyType { get { return _namingStrategyType; } set { _namingStrategyType = value; NamingStrategyInstance = null; } } public object[]? NamingStrategyParameters { get { return _namingStrategyParameters; } set { _namingStrategyParameters = value; NamingStrategyInstance = null; } } internal NamingStrategy? NamingStrategyInstance { get; set; } public bool IsReference { get { return _isReference.GetValueOrDefault(); } set { _isReference = value; } } public bool ItemIsReference { get { return _itemIsReference.GetValueOrDefault(); } set { _itemIsReference = value; } } public ReferenceLoopHandling ItemReferenceLoopHandling { get { return _itemReferenceLoopHandling.GetValueOrDefault(); } set { _itemReferenceLoopHandling = value; } } public TypeNameHandling ItemTypeNameHandling { get { return _itemTypeNameHandling.GetValueOrDefault(); } set { _itemTypeNameHandling = value; } } protected JsonContainerAttribute() { } protected JsonContainerAttribute(string id) { Id = id; } } internal static class JsonConvert { public static readonly string True = "true"; public static readonly string False = "false"; public static readonly string Null = "null"; public static readonly string Undefined = "undefined"; public static readonly string PositiveInfinity = "Infinity"; public static readonly string NegativeInfinity = "-Infinity"; public static readonly string NaN = "NaN"; public static Func<JsonSerializerSettings>? DefaultSettings { get; set; } public static string ToString(DateTime value) { return ToString(value, DateFormatHandling.IsoDateFormat, DateTimeZoneHandling.RoundtripKind); } public static string ToString(DateTime value, DateFormatHandling format, DateTimeZoneHandling timeZoneHandling) { DateTime value2 = DateTimeUtils.EnsureDateTime(value, timeZoneHandling); using StringWriter stringWriter = StringUtils.CreateStringWriter(64); stringWriter.Write('"'); DateTimeUtils.WriteDateTimeString(stringWriter, value2, format, null, CultureInfo.InvariantCulture); stringWriter.Write('"'); return stringWriter.ToString(); } public static string ToString(DateTimeOffset value) { return ToString(value, DateFormatHandling.IsoDateFormat); } public static string ToString(DateTimeOffset value, DateFormatHandling format) { using StringWriter stringWriter = StringUtils.CreateStringWriter(64); stringWriter.Write('"'); DateTimeUtils.WriteDateTimeOffsetString(stringWriter, value, format, null, CultureInfo.InvariantCulture); stringWriter.Write('"'); return stringWriter.ToString(); } public static string ToString(bool value) { if (!value) { return False; } return True; } public static string ToString(char value) { return ToString(char.ToString(value)); } public static string ToString(Enum value) { return value.ToString("D"); } public static string ToString(int value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(short value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(ushort value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(uint value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(long value) { return value.ToString(null, CultureInfo.InvariantCulture); } private static string ToStringInternal(BigInteger value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(ulong value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(float value) { return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); } internal static string ToString(float value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) { return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable); } private static string EnsureFloatFormat(double value, string text, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) { if (floatFormatHandling == FloatFormatHandling.Symbol || (!double.IsInfinity(value) && !double.IsNaN(value))) { return text; } if (floatFormatHandling == FloatFormatHandling.DefaultValue) { if (nullable) { return Null; } return "0.0"; } return quoteChar + text + quoteChar; } public static string ToString(double value) { return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); } internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) { return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable); } private static string EnsureDecimalPlace(double value, string text) { if (double.IsNaN(value) || double.IsInfinity(value) || StringUtils.IndexOf(text, '.') != -1 || StringUtils.IndexOf(text, 'E') != -1 || StringUtils.IndexOf(text, 'e') != -1) { return text; } return text + ".0"; } private static string EnsureDecimalPlace(string text) { if (StringUtils.IndexOf(text, '.') != -1) { return text; } return text + ".0"; } public static string ToString(byte value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(sbyte value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(decimal value) { return EnsureDecimalPlace(value.ToString(null, CultureInfo.InvariantCulture)); } public static string ToString(Guid value) { return ToString(value, '"'); } internal static string ToString(Guid value, char quoteChar) { string text = value.ToString("D", CultureInfo.InvariantCulture); string text2 = quoteChar.ToString(CultureInfo.InvariantCulture); return text2 + text + text2; } public static string ToString(TimeSpan value) { return ToString(value, '"'); } internal static string ToString(TimeSpan value, char quoteChar) { return ToString(value.ToString(), quoteChar); } public static string ToString(Uri? value) { if (value == null) { return Null; } return ToString(value, '"'); } internal static string ToString(Uri value, char quoteChar) { return ToString(value.OriginalString, quoteChar); } public static string ToString(string? value) { return ToString(value, '"'); } public static string ToString(string? value, char delimiter) { return ToString(value, delimiter, StringEscapeHandling.Default); } public static string ToString(string? value, char delimiter, StringEscapeHandling stringEscapeHandling) { if (delimiter != '"' && delimiter != '\'') { throw new ArgumentException("Delimiter must be a single or double quote.", "delimiter"); } return JavaScriptUtils.ToEscapedJavaScriptString(value, delimiter, appendDelimiters: true, stringEscapeHandling); } public static string ToString(object? value) { if (value == null) { return Null; } return ConvertUtils.GetTypeCode(value.GetType()) switch { PrimitiveTypeCode.String => ToString((string)value), PrimitiveTypeCode.Char => ToString((char)value), PrimitiveTypeCode.Boolean => ToString((bool)value), PrimitiveTypeCode.SByte => ToString((sbyte)value), PrimitiveTypeCode.Int16 => ToString((short)value), PrimitiveTypeCode.UInt16 => ToString((ushort)value), PrimitiveTypeCode.Int32 => ToString((int)value), PrimitiveTypeCode.Byte => ToString((byte)value), PrimitiveTypeCode.UInt32 => ToString((uint)value), PrimitiveTypeCode.Int64 => ToString((long)value), PrimitiveTypeCode.UInt64 => ToString((ulong)value), PrimitiveTypeCode.Single => ToString((float)value), PrimitiveTypeCode.Double => ToString((double)value), PrimitiveTypeCode.DateTime => ToString((DateTime)value), PrimitiveTypeCode.Decimal => ToString((decimal)value), PrimitiveTypeCode.DBNull => Null, PrimitiveTypeCode.DateTimeOffset => ToString((DateTimeOffset)value), PrimitiveTypeCode.Guid => ToString((Guid)value), PrimitiveTypeCode.Uri => ToString((Uri)value), PrimitiveTypeCode.TimeSpan => ToString((TimeSpan)value), PrimitiveTypeCode.BigInteger => ToStringInternal((BigInteger)value), _ => throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())), }; } [DebuggerStepThrough] public static string SerializeObject(object? value) { return SerializeObject(value, (Type?)null, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static string SerializeObject(object? value, Formatting formatting) { return SerializeObject(value, formatting, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static string SerializeObject(object? value, params JsonConverter[] converters) { JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings { Converters = converters } : null); return SerializeObject(value, null, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, Formatting formatting, params JsonConverter[] converters) { JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings { Converters = converters } : null); return SerializeObject(value, null, formatting, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, JsonSerializerSettings? settings) { return SerializeObject(value, null, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, Type? type, JsonSerializerSettings? settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); return SerializeObjectInternal(value, type, jsonSerializer); } [DebuggerStepThrough] public static string SerializeObject(object? value, Formatting formatting, JsonSerializerSettings? settings) { return SerializeObject(value, null, formatting, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, Type? type, Formatting formatting, JsonSerializerSettings? settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); jsonSerializer.Formatting = formatting; return SerializeObjectInternal(value, type, jsonSerializer); } private static string SerializeObjectInternal(object? value, Type? type, JsonSerializer jsonSerializer) { StringWriter stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture); using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter)) { jsonTextWriter.Formatting = jsonSerializer.Formatting; jsonSerializer.Serialize(jsonTextWriter, value, type); } return stringWriter.ToString(); } [DebuggerStepThrough] public static object? DeserializeObject(string value) { return DeserializeObject(value, (Type?)null, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static object? DeserializeObject(string value, JsonSerializerSettings settings) { return DeserializeObject(value, null, settings); } [DebuggerStepThrough] public static object? DeserializeObject(string value, Type type) { return DeserializeObject(value, type, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static T? DeserializeObject<T>(string value) { return JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject) { return DeserializeObject<T>(value); } [DebuggerStepThrough] public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject, JsonSerializerSettings settings) { return DeserializeObject<T>(value, settings); } [DebuggerStepThrough] public static T? DeserializeObject<T>(string value, params JsonConverter[] converters) { return (T)DeserializeObject(value, typeof(T), converters); } [DebuggerStepThrough] public static T? DeserializeObject<T>(string value, JsonSerializerSettings? settings) { return (T)DeserializeObject(value, typeof(T), settings); } [DebuggerStepThrough] public static object? DeserializeObject(string value, Type type, params JsonConverter[] converters) { JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings { Converters = converters } : null); return DeserializeObject(value, type, settings); } public static object? DeserializeObject(string value, Type? type, JsonSerializerSettings? settings) { ValidationUtils.ArgumentNotNull(value, "value"); JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); if (!jsonSerializer.IsCheckAdditionalContentSet()) { jsonSerializer.CheckAdditionalContent = true; } using JsonTextReader reader = new JsonTextReader(new StringReader(value)); return jsonSerializer.Deserialize(reader, type); } [DebuggerStepThrough] public static void PopulateObject(string value, object target) { PopulateObject(value, target, null); } public static void PopulateObject(string value, object target, JsonSerializerSettings? settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); using JsonReader jsonReader = new JsonTextReader(new StringReader(value)); jsonSerializer.Populate(jsonReader, target); if (settings == null || !settings.CheckAdditionalContent) { return; } while (jsonReader.Read()) { if (jsonReader.TokenType != JsonToken.Comment) { throw JsonSerializationException.Create(jsonReader, "Additional text found in JSON string after finishing deserializing object."); } } } public static string SerializeXmlNode(XmlNode? node) { return SerializeXmlNode(node, Formatting.None); } public static string SerializeXmlNode(XmlNode? node, Formatting formatting) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter(); return SerializeObject(node, formatting, xmlNodeConverter); } public static string SerializeXmlNode(XmlNode? node, Formatting formatting, bool omitRootObject) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter { OmitRootObject = omitRootObject }; return SerializeObject(node, formatting, xmlNodeConverter); } public static XmlDocument? DeserializeXmlNode(string value) { return DeserializeXmlNode(value, null); } public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName) { return DeserializeXmlNode(value, deserializeRootElementName, writeArrayAttribute: false); } public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName, bool writeArrayAttribute) { return DeserializeXmlNode(value, deserializeRootElementName, writeArrayAttribute, encodeSpecialCharacters: false); } public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName, bool writeArrayAttribute, bool encodeSpecialCharacters) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter(); xmlNodeConverter.DeserializeRootElementName = deserializeRootElementName; xmlNodeConverter.WriteArrayAttribute = writeArrayAttribute; xmlNodeConverter.EncodeSpecialCharacters = encodeSpecialCharacters; return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), xmlNodeConverter); } public static string SerializeXNode(XObject? node) { return SerializeXNode(node, Formatting.None); } public static string SerializeXNode(XObject? node, Formatting formatting) { return SerializeXNode(node, formatting, omitRootObject: false); } public static string SerializeXNode(XObject? node, Formatting formatting, bool omitRootObject) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter { OmitRootObject = omitRootObject }; return SerializeObject(node, formatting, xmlNodeConverter); } public static XDocument? DeserializeXNode(string value) { return DeserializeXNode(value, null); } public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName) { return DeserializeXNode(value, deserializeRootElementName, writeArrayAttribute: false); } public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName, bool writeArrayAttribute) { return DeserializeXNode(value, deserializeRootElementName, writeArrayAttribute, encodeSpecialCharacters: false); } public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName, bool writeArrayAttribute, bool encodeSpecialCharacters) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter(); xmlNodeConverter.DeserializeRootElementName = deserializeRootElementName; xmlNodeConverter.WriteArrayAttribute = writeArrayAttribute; xmlNodeConverter.EncodeSpecialCharacters = encodeSpecialCharacters; return (XDocument)DeserializeObject(value, typeof(XDocument), xmlNodeConverter); } } internal abstract class JsonConverter { public virtual bool CanRead => true; public virtual bool CanWrite => true; public abstract void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer); public abstract object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer); public abstract bool CanConvert(Type objectType); } internal abstract class JsonConverter<T> : JsonConverter { public sealed override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (!((value != null) ? (value is T) : ReflectionUtils.IsNullable(typeof(T)))) { throw new JsonSerializationException("Converter cannot write specified value to JSON. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T))); } WriteJson(writer, (T)value, serializer); } public abstract void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer); public sealed override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { bool flag = existingValue == null; if (!flag && !(existingValue is T)) { throw new JsonSerializationException("Converter cannot read JSON with the specified existing value. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T))); } return ReadJson(reader, objectType, flag ? default(T) : ((T)existingValue), !flag, serializer); } public abstract T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer); public sealed override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false)] internal sealed class JsonConverterAttribute : Attribute { private readonly Type _converterType; public Type ConverterType => _converterType; public object[]? ConverterParameters { get; } public JsonConverterAttribute(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } _converterType = converterType; } public JsonConverterAttribute(Type converterType, params object[] converterParameters) : this(converterType) { ConverterParameters = converterParameters; } } internal class JsonConverterCollection : Collection<JsonConverter> { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] internal sealed class JsonDictionaryAttribute : JsonContainerAttribute { public JsonDictionaryAttribute() { } public JsonDictionaryAttribute(string id) : base(id) { } } [Serializable] internal class JsonException : Exception { public JsonException() { } public JsonException(string message) : base(message) { } public JsonException(string message, Exception? innerException) : base(message, innerException) { } public JsonException(SerializationInfo info, StreamingContext context) : base(info, context) { } internal static JsonException Create(IJsonLineInfo lineInfo, string path, string message) { message = JsonPosition.FormatMessage(lineInfo, path, message); return new JsonException(message); } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal class JsonExtensionDataAttribute : Attribute { public bool WriteData { get; set; } public bool ReadData { get; set; } public JsonExtensionDataAttribute() { WriteData = true; ReadData = true; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class JsonIgnoreAttribute : Attribute { } internal abstract class JsonNameTable { public abstract string? Get(char[] key, int start, int length); } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)] internal sealed class JsonObjectAttribute : JsonContainerAttribute { private MemberSerialization _memberSerialization; internal MissingMemberHandling? _missingMemberHandling; internal Required? _itemRequired; internal NullValueHandling? _itemNullValueHandling; public MemberSerialization MemberSerialization { get { return _memberSerialization; } set { _memberSerialization = value; } } public MissingMemberHandling MissingMemberHandling { get { return _missingMemberHandling.GetValueOrDefault(); } set { _missingMemberHandling = value; } } public NullValueHandling ItemNullValueHandling { get { return _itemNullValueHandling.GetValueOrDefault(); } set { _itemNullValueHandling = value; } } public Required ItemRequired { get { return _itemRequired.GetValueOrDefault(); } set { _itemRequired = value; } } public JsonObjectAttribute() { } public JsonObjectAttribute(MemberSerialization memberSerialization) { MemberSerialization = memberSerialization; } public JsonObjectAttribute(string id) : base(id) { } } internal enum JsonContainerType { None, Object, Array, Constructor } internal struct JsonPosition { private static readonly char[] SpecialCharacters = new char[18] { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' }; internal JsonContainerType Type; internal int Position; internal string? PropertyName; internal bool HasIndex; public JsonPosition(JsonContainerType type) { Type = type; HasIndex = TypeHasIndex(type); Position = -1; PropertyName = null; } internal int CalculateLength() { switch (Type) { case JsonContainerType.Object: return PropertyName.Length + 5; case JsonContainerType.Array: case JsonContainerType.Constructor: return MathUtils.IntLength((ulong)Position) + 2; default: throw new ArgumentOutOfRangeException("Type"); } } internal void WriteTo(StringBuilder sb, ref StringWriter? writer, ref char[]? buffer) { switch (Type) { case JsonContainerType.Object: { string propertyName = PropertyName; if (propertyName.IndexOfAny(SpecialCharacters) != -1) { sb.Append("['"); if (writer == null) { writer = new StringWriter(sb); } JavaScriptUtils.WriteEscapedJavaScriptString(writer, propertyName, '\'', appendDelimiters: false, JavaScriptUtils.SingleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer); sb.Append("']"); } else { if (sb.Length > 0) { sb.Append('.'); } sb.Append(propertyName); } break; } case JsonContainerType.Array: case JsonContainerType.Constructor: sb.Append('['); sb.Append(Position); sb.Append(']'); break; } } internal static bool TypeHasIndex(JsonContainerType type) { if (type != JsonContainerType.Array) { return type == JsonContainerType.Constructor; } return true; } internal static string BuildPath(List<JsonPosition> positions, JsonPosition? currentPosition) { int num = 0; if (positions != null) { for (int i = 0; i < positions.Count; i++) { num += positions[i].CalculateLength(); } } if (currentPosition.HasValue) { num += currentPosition.GetValueOrDefault().CalculateLength(); } StringBuilder stringBuilder = new StringBuilder(num); StringWriter writer = null; char[] buffer = null; if (positions != null) { foreach (JsonPosition position in positions) { position.WriteTo(stringBuilder, ref writer, ref buffer); } } currentPosition?.WriteTo(stringBuilder, ref writer, ref buffer); return stringBuilder.ToString(); } internal static string FormatMessage(IJsonLineInfo? lineInfo, string path, string message) { if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal)) { message = message.Trim(); if (!message.EndsWith('.')) { message += "."; } message += " "; } message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path); if (lineInfo != null && lineInfo.HasLineInfo()) { message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition); } message += "."; return message; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] internal sealed class JsonPropertyAttribute : Attribute { internal NullValueHandling? _nullValueHandling; internal DefaultValueHandling? _defaultValueHandling; internal ReferenceLoopHandling? _referenceLoopHandling; internal ObjectCreationHandling? _objectCreationHandling; internal TypeNameHandling? _typeNameHandling; internal bool? _isReference; internal int? _order; internal Required? _required; internal bool? _itemIsReference; internal ReferenceLoopHandling? _itemReferenceLoopHandling; internal TypeNameHandling? _itemTypeNameHandling; public Type? ItemConverterType { get; set; } public object[]? ItemConverterParameters { get; set; } public Type? NamingStrategyType { get; set; } public object[]? NamingStrategyParameters { get; set; } public NullValueHandling NullValueHandling { get { return _nullValueHandling.GetValueOrDefault(); } set { _nullValueHandling = value; } } public DefaultValueHandling DefaultValueHandling { get { return _defaultValueHandling.GetValueOrDefault(); } set { _defaultValueHandling = value; } } public ReferenceLoopHandling ReferenceLoopHandling { get { return _referenceLoopHandling.GetValueOrDefault(); } set { _referenceLoopHandling = value; } } public ObjectCreationHandling ObjectCreationHandling { get { return _objectCreationHandling.GetValueOrDefault(); } set { _objectCreationHandling = value; } } public TypeNameHandling TypeNameHandling { get { return _typeNameHandling.GetValueOrDefault(); } set { _typeNameHandling = value; } } public bool IsReference { get { return _isReference.GetValueOrDefault(); } set { _isReference = value; } } public int Order { get { return _order.GetValueOrDefault(); } set { _order = value; } } public Required Required { get { return _required.GetValueOrDefault(); } set { _required = value; } } public string? PropertyName { get; set; } public ReferenceLoopHandling ItemReferenceLoopHandling { get { return _itemReferenceLoopHandling.GetValueOrDefault(); } set { _itemReferenceLoopHandling = value; } } public TypeNameHandling ItemTypeNameHandling { get { return _itemTypeNameHandling.GetValueOrDefault(); } set { _itemTypeNameHandling = value; } } public bool ItemIsReference { get { return _itemIsReference.GetValueOrDefault(); } set { _itemIsReference = value; } } public JsonPropertyAttribute() { }