/// <summary> /// Bot meta reload admin command. /// </summary> public void CMD_Reload(string[] cmds, SocketMessage message) { // NOTE: This implies a one-guild bot. A multi-guild bot probably shouldn't have this "BotCommander" role-based verification. // But under current scale, a true-admin confirmation isn't worth the bother. if (!Bot.IsBotCommander(message.Author as SocketGuildUser)) { SendErrorMessageReply(message, "Authorization Failure", "Nope! That's not for you!"); return; } SendGenericPositiveMessageReply(message, "Reloading", "Yes, boss. Reloading meta documentation now..."); BuildNumberTracker.UpdateAll(); MetaDocs docs = new MetaDocs(); docs.DownloadAll(); Program.CurrentMeta = docs; EmbedBuilder embed = new EmbedBuilder().WithTitle("Reload Complete").WithDescription("Documentation reloaded successfully."); if (docs.LoadErrors.Count > 0) { List <string> errors = docs.LoadErrors.Count > 5 ? docs.LoadErrors.GetRange(0, 5) : docs.LoadErrors; SendErrorMessageReply(message, "Error(s) While Reloading", string.Join("\n", errors)); embed.AddField("Errors", docs.LoadErrors.Count, true); } embed.AddField("Commands", docs.Commands.Count, true); embed.AddField("Mechanisms", docs.Mechanisms.Count, true); embed.AddField("Tags", docs.Tags.Count, true); embed.AddField("Events", docs.Events.Count, true); embed.AddField("Actions", docs.Actions.Count, true); embed.AddField("Languages", docs.Languages.Count, true); SendReply(message, embed.Build()); }
/// <summary>Bot meta reload admin command.</summary> public void CMD_Reload(CommandData command) { // NOTE: This implies a one-guild bot. A multi-guild bot probably shouldn't have this "BotCommander" role-based verification. // But under current scale, a true-admin confirmation isn't worth the bother. if (!DenizenMetaBot.IsBotCommander(command.Message.Author as SocketGuildUser)) { SendErrorMessageReply(command.Message, "Authorization Failure", "Nope! That's not for you!"); return; } SendGenericPositiveMessageReply(command.Message, "Reloading", "Yes, boss. Reloading meta documentation now..."); BuildNumberTracker.UpdateAll(); MetaDocs docs = MetaDocsLoader.DownloadAll(); MetaDocs.CurrentMeta = docs; EmbedBuilder embed = new EmbedBuilder().WithTitle("Reload Complete").WithDescription("Documentation reloaded successfully."); if (docs.LoadErrors.Count > 0) { List <string> errors = docs.LoadErrors.Count > 5 ? docs.LoadErrors.GetRange(0, 5) : docs.LoadErrors; SendErrorMessageReply(command.Message, "Error(s) While Reloading", string.Join("\n", errors)); embed.AddField("Errors", docs.LoadErrors.Count, true); } embed.AddField("Commands", docs.Commands.Count, true); embed.AddField("Mechanisms", docs.Mechanisms.Count, true); embed.AddField("Tags", docs.Tags.Count, true); embed.AddField("Object Types", docs.ObjectTypes.Count, true); embed.AddField("Events", docs.Events.Count, true); embed.AddField("Actions", docs.Actions.Count, true); embed.AddField("Languages", docs.Languages.Count, true); embed.AddField("Guide Pages", docs.GuidePages.Count, true); SendReply(command.Message, embed.Build()); foreach (string url in DenizenMetaBot.ReloadWebooks) { try { Program.ReusableWebClient.PostAsync(url, new ByteArrayContent(Array.Empty <byte>())).Wait(); } catch (Exception ex) { Console.Error.Write($"Failed to ping webhook URL '{url}': {ex}"); } } }
/// <summary> /// Command to check the updatedness of a version string. /// </summary> public void CMD_VersionCheck(string[] cmds, SocketMessage message) { if (cmds.Length == 0) { SendErrorMessageReply(message, "Command Syntax Incorrect", "`!versioncheck <version text>`"); return; } string combined = string.Join(" ", cmds).Trim(); if (combined.ToLowerFast().StartsWith("loading ")) { combined = combined.Substring("loading ".Length).Trim(); } if (combined.IsEmpty()) { SendErrorMessageReply(message, "Bad Input", "Input text doesn't look like a version string (blank input?)."); return; } string projectName = BuildNumberTracker.SplitToNameAndVersion(combined, out string versionText).Replace(":", "").Trim(); versionText = versionText.Trim(); if (projectName.IsEmpty() || versionText.IsEmpty()) { SendErrorMessageReply(message, "Bad Input", "Input text doesn't look like a version string (single word input?)."); return; } if (BuildNumberTracker.TryGetBuildFor(projectName, versionText, out BuildNumberTracker.BuildNumber build, out int buildNum)) { if (build.IsCurrent(buildNum, out int behindBy)) { SendGenericPositiveMessageReply(message, "Running Current Build", $"That version is the current {build.Name} build."); } else { SendGenericNegativeMessageReply(message, "Build Outdated", $"That version is an outdated {build.Name} build.\nThe current {build.Name} build is {build.Value}.\nYou are behind by {behindBy} builds."); } return; } SendErrorMessageReply(message, "Bad Input", $"Input project name (`{EscapeUserInput(projectName)}`) doesn't look like any tracked project (or the version text is formatted incorrectly)."); return; }
/// <summary> /// Fills fields with data from the config file. /// </summary> public void PopulateFromConfig() { ValidChannels.Clear(); Constants.DOCS_URL_BASE = ConfigFile.GetString("url_base"); Constants.COMMAND_PREFIX = ConfigFile.GetString("command_prefix"); foreach (string channel in ConfigFile.GetStringList("valid_channels")) { ValidChannels.Add(ulong.Parse(channel.Trim())); } FDSSection infoSection = ConfigFile.GetSection("info_replies"); foreach (string key in infoSection.GetRootKeys()) { string infoValue = infoSection.GetRootData(key).AsString; string[] keysSplit = key.SplitFast(','); InformationalDataNames.Add(keysSplit[0]); foreach (string name in keysSplit) { InformationalData[name.Trim()] = infoValue; } } FDSSection projectDetailsSection = ConfigFile.GetSection("project_details"); foreach (string key in projectDetailsSection.GetRootKeys()) { FDSSection detailsSection = projectDetailsSection.GetSection(key); ProjectDetails detail = new ProjectDetails { Name = key, Icon = detailsSection.GetString("icon", ""), GitHub = detailsSection.GetString("github", ""), UpdateMessage = detailsSection.GetString("update", "") }; ProjectToDetails.Add(key.ToLowerFast(), detail); } FDSSection channelDetailsSection = ConfigFile.GetSection("channel_details"); foreach (string key in channelDetailsSection.GetRootKeys()) { FDSSection detailsSection = channelDetailsSection.GetSection(key); ChannelDetails detail = new ChannelDetails(); List <ProjectDetails> projects = new List <ProjectDetails>(); foreach (string projName in detailsSection.GetString("updates", "").Split(' ', StringSplitOptions.RemoveEmptyEntries)) { projects.Add(ProjectToDetails[projName]); } detail.Updates = projects.ToArray(); detail.Docs = detailsSection.GetBool("docs", false).Value; ChannelToDetails.Add(ulong.Parse(key), detail); } FDSSection rulesSection = ConfigFile.GetSection("rules"); foreach (string rule in rulesSection.GetRootKeys()) { Rules.Add(rule, rulesSection.GetString(rule)); } FDSSection buildNumbersSection = ConfigFile.GetSection("build_numbers"); foreach (string projectName in buildNumbersSection.GetRootKeys()) { FDSSection project = buildNumbersSection.GetSection(projectName); BuildNumberTracker.AddTracker(project.GetString("name"), project.GetString("regex"), project.GetString("jenkins_job")); } }
/// <summary> /// Fills fields with data from the config file. /// </summary> public void PopulateFromConfig(FDSSection configFile) { ValidChannels.Clear(); InformationalData.Clear(); InformationalDataNames.Clear(); ChannelToDetails.Clear(); ProjectToDetails.Clear(); Rules.Clear(); AcceptableServerVersions.Clear(); BuildNumberTracker.Clear(); DenizenMetaBotConstants.DOCS_URL_BASE = configFile.GetString("url_base"); DenizenMetaBotConstants.COMMAND_PREFIX = configFile.GetString("command_prefix"); DiscordBotBaseHelper.CurrentBot.ClientConfig.CommandPrefix = DenizenMetaBotConstants.COMMAND_PREFIX; if (configFile.HasKey("valid_channels")) { foreach (string channel in configFile.GetStringList("valid_channels")) { ValidChannels.Add(ulong.Parse(channel.Trim())); } } if (configFile.HasKey("info_replies")) { FDSSection infoSection = configFile.GetSection("info_replies"); foreach (string key in infoSection.GetRootKeys()) { string infoValue = infoSection.GetRootData(key).AsString; string[] keysSplit = key.SplitFast(','); InformationalDataNames.Add(keysSplit[0]); foreach (string name in keysSplit) { InformationalData[name.Trim()] = infoValue; } } } if (configFile.HasKey("project_details")) { FDSSection projectDetailsSection = configFile.GetSection("project_details"); foreach (string key in projectDetailsSection.GetRootKeys()) { FDSSection detailsSection = projectDetailsSection.GetSection(key); ProjectDetails detail = new ProjectDetails { Name = key, Icon = detailsSection.GetString("icon", ""), GitHub = detailsSection.GetString("github", ""), UpdateMessage = detailsSection.GetString("update", "") }; ProjectToDetails.Add(key.ToLowerFast(), detail); } } if (configFile.HasKey("channel_details")) { FDSSection channelDetailsSection = configFile.GetSection("channel_details"); foreach (string key in channelDetailsSection.GetRootKeys()) { FDSSection detailsSection = channelDetailsSection.GetSection(key); ChannelDetails detail = new ChannelDetails(); List <ProjectDetails> projects = new List <ProjectDetails>(); foreach (string projName in detailsSection.GetString("updates", "").Split(' ', StringSplitOptions.RemoveEmptyEntries)) { projects.Add(ProjectToDetails[projName]); } detail.Updates = projects.ToArray(); detail.Docs = detailsSection.GetBool("docs", false).Value; ChannelToDetails.Add(ulong.Parse(key), detail); } } if (configFile.HasKey("rules")) { FDSSection rulesSection = configFile.GetSection("rules"); foreach (string rule in rulesSection.GetRootKeys()) { Rules.Add(rule, rulesSection.GetString(rule)); } } if (configFile.HasKey("build_numbers")) { FDSSection buildNumbersSection = configFile.GetSection("build_numbers"); foreach (string projectName in buildNumbersSection.GetRootKeys()) { FDSSection project = buildNumbersSection.GetSection(projectName); BuildNumberTracker.AddTracker(project.GetString("name"), project.GetString("regex"), project.GetString("jenkins_job")); } } if (configFile.HasKey("acceptable_server_versions")) { AcceptableServerVersions = configFile.GetStringList("acceptable_server_versions"); foreach (string version in AcceptableServerVersions) { double versionNumber = double.Parse(version); if (LowestServerVersion <= 0.01 || versionNumber < LowestServerVersion) { LowestServerVersion = versionNumber; } if (versionNumber > HighestServerVersion) { HighestServerVersion = versionNumber; } BuildNumberTracker.AddPaperTracker(version); } } BuildNumberTracker.LoadSpigotData(); if (File.Exists(DiscordBot.CONFIG_FOLDER + "quotes.txt")) { Quotes = File.ReadAllText(DiscordBot.CONFIG_FOLDER + "quotes.txt").Replace("\r", "").Replace('`', '\'').Split("\n\n", StringSplitOptions.RemoveEmptyEntries); QuotesLower = Quotes.Select(s => s.ToLowerFast()).ToArray(); } }
/// <summary> /// Command to check the updatedness of a version string. /// </summary> public void CMD_VersionCheck(string[] cmds, IUserMessage message) { if (cmds.Length == 0) { SendErrorMessageReply(message, "Command Syntax Incorrect", "`!versioncheck <version text>`"); return; } string combined = string.Join(" ", cmds).Trim(); if (combined.ToLowerFast().StartsWith("loading ")) { combined = combined.Substring("loading ".Length).Trim(); } if (combined.IsEmpty()) { SendErrorMessageReply(message, "Bad Input", "Input text doesn't look like a version string (blank input?)."); return; } string projectName = BuildNumberTracker.SplitToNameAndVersion(combined, out string versionText).Replace(":", "").Trim(); versionText = versionText.Trim(); if (projectName.IsEmpty() || versionText.IsEmpty()) { SendErrorMessageReply(message, "Bad Input", "Input text doesn't look like a version string (single word input?)."); return; } string nameLower = projectName.ToLowerFast(); if (nameLower == "paper" || nameLower == "spigot" || nameLower == "craftbukkit") { string output = LogChecker.ServerVersionStatusOutput(combined, out bool isGood); if (string.IsNullOrWhiteSpace(output)) { SendErrorMessageReply(message, "Bad Input", $"Input text looks like a {nameLower} version, but doesn't fit the expected {nameLower} server version format. Should start with '{nameLower} version git-{nameLower}-...'"); return; } if (isGood) { SendGenericPositiveMessageReply(message, "Running Current Build", $"That version is the current {nameLower} build for an acceptable server version."); } else { SendGenericNegativeMessageReply(message, "Build Outdated", $"{output}."); } return; } if (BuildNumberTracker.TryGetBuildFor(projectName, versionText, out BuildNumberTracker.BuildNumber build, out int buildNum)) { if (build.IsCurrent(buildNum, out int behindBy)) { SendGenericPositiveMessageReply(message, "Running Current Build", $"That version is the current {build.Name} build."); } else { SendGenericNegativeMessageReply(message, "Build Outdated", $"That version is an outdated {build.Name} build.\nThe current {build.Name} build is {build.Value}.\nYou are behind by {behindBy} builds."); } return; } SendErrorMessageReply(message, "Bad Input", $"Input project name (`{EscapeUserInput(projectName)}`) doesn't look like any tracked project (or the version text is formatted incorrectly)."); return; }