/// <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()); }
public static string HtmlizeTags(string[] tags, MetaDocs meta) { StringBuilder tagOutput = new StringBuilder(); foreach (string tag in tags) { string[] parts = tag.Split(' ', 2); string properName = $"<code>{ScriptHighlighter.ColorArgument(Util.EscapeForHTML(parts[0]), false)}</code>"; if (parts.Length == 2) { tagOutput.Append(properName).Append(' ').Append(ParseAndEscape(parts[1])); } else { MetaTag actualTag = meta.FindTag(parts[0]); if (actualTag == null) { string nameLow = parts[0].ToLowerFast(); tagOutput.Append(Util.EscapeForHTML(parts[0])).Append((nameLow == "none" || nameLow == "todo") ? "" : " ERROR: TAG INVALID"); } else { string desc = ParseAndEscape(actualTag.Description); if (desc.Contains('\n')) { desc = desc.Before('\n') + $" <a href=\"/Docs/Tags/{actualTag.CleanedName}\">(...)</a>"; } tagOutput.Append($"<a href=\"/Docs/Tags/{actualTag.CleanedName}\">").Append(properName).Append("</a> ").Append(desc); } } tagOutput.Append("\n<br>"); } return(tagOutput.ToString()); }
/// <summary><see cref="MetaObject.AddTo(MetaDocs)"/></summary> public override void AddTo(MetaDocs docs) { docs.Tags.Add(CleanName, this); docs.TagBases.Add(CleanName.BeforeAndAfter('.', out string otherBits)); foreach (string bit in otherBits.Split('.')) { docs.TagParts.Add(bit); } }
/// <summary> /// Post-check handler to require specific values be set (not-null). /// </summary> /// <param name="docs">The relevant docs object.</param> /// <param name="requiredValues">The values that are required.</param> public void Require(MetaDocs docs, params object[] requiredValues) { foreach (object obj in requiredValues) { if (obj == null) { docs.LoadErrors.Add($"{Type.Name} '{Name}' is missing a required meta key."); } } }
/// <summary><see cref="MetaObject.PostCheck(MetaDocs)"/></summary> public override void PostCheck(MetaDocs docs) { Require(docs, Actions[0], Triggers); PostCheckLinkableText(docs, Triggers); foreach (string context in Context) { PostCheckLinkableText(docs, context); } foreach (string determine in Determinations) { PostCheckLinkableText(docs, determine); } }
public override void PostCheck(MetaDocs docs) { Require(docs, MechObject, MechName, Input, Description); PostCheckTags(docs, Tags); PostCheckLinkableText(docs, Description); if (Tags.IsEmpty()) { if (docs.Tags.ContainsKey(CleanName)) { docs.LoadErrors.Add($"Mechanism '{Name}' has no Tags link, but has the same name as an existing tag. A link should be added."); } } }
/// <summary> /// Post-check handler for tags, used in <see cref="MetaCommand"/> and <see cref="MetaMechanism"/>. /// </summary> /// <param name="docs">The relevant docs object.</param> /// <param name="tags">The relevant tags list.</param> public void PostCheckTags(MetaDocs docs, string[] tags) { foreach (string tag in tags) { if (tag.EndsWith(">")) { MetaTag realTag = docs.FindTag(tag); if (realTag == null) { docs.LoadErrors.Add($"{Type.Name} '{Name}' references tag '{tag}', which doesn't exist."); } } PostCheckLinkableText(docs, tag); } }
/// <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><see cref="MetaObject.PostCheck(MetaDocs)"/></summary> public override void PostCheck(MetaDocs docs) { Require(docs, TagFull, Returns, Description); if (!string.IsNullOrWhiteSpace(Mechanism)) { if (!docs.Mechanisms.ContainsKey(Mechanism.ToLowerFast())) { docs.LoadErrors.Add($"Tag '{Name}' references mechanism '{Mechanism}', which doesn't exist."); } PostCheckLinkableText(docs, Mechanism); } else { if (docs.Mechanisms.ContainsKey(CleanedName)) { docs.LoadErrors.Add($"Tag '{Name}' has no mechanism link, but has the same name as an existing mechanism. A link should be added."); } } PostCheckLinkableText(docs, Description); }
/// <summary> /// Post-check handler for linkable text, to find bad links. /// </summary> /// <param name="docs">The relevant docs object.</param> /// <param name="linkedtext">The relevant linkable list.</param> public void PostCheckLinkableText(MetaDocs docs, string linkedtext) { if (string.IsNullOrWhiteSpace(linkedtext)) { return; } int nextLinkIndex = linkedtext.IndexOf("<@link"); if (nextLinkIndex < 0) { return; } while (nextLinkIndex >= 0) { int endIndex = FindClosingTagMark(linkedtext, nextLinkIndex + 1); if (endIndex < 0) { return; } int startOfMetaCommand = nextLinkIndex + "<@link ".Length; string metaCommand = linkedtext[startOfMetaCommand..endIndex];
/// <summary><see cref="MetaObject.AddTo(MetaDocs)"/></summary> public override void AddTo(MetaDocs docs) { docs.Actions.Add(CleanName, this); }
/// <summary><see cref="MetaObject.PostCheck(MetaDocs)"/></summary> public override void PostCheck(MetaDocs docs) { Require(docs, Short, Description, Syntax, CommandName); PostCheckTags(docs, Tags); PostCheckLinkableText(docs, Description); }
/// <summary><see cref="MetaObject.AddTo(MetaDocs)"/></summary> public override void AddTo(MetaDocs docs) { docs.Commands.Add(CleanName, this); }
public override void PostCheck(MetaDocs docs) { Require(docs, LangName, Description); PostCheckLinkableText(docs, Description); }
/// <summary><see cref="MetaObject.AddTo(MetaDocs)"/></summary> public override void AddTo(MetaDocs docs) { docs.GuidePages.Add(CleanName, this); }
/// <summary> /// Checks the object for validity, after all loading is done. /// </summary> /// <param name="docs">The relevant docs object.</param> public virtual void PostCheck(MetaDocs docs) { }
/// <summary> /// Adds the object to the meta docs set. /// </summary> /// <param name="docs">The docs set.</param> public abstract void AddTo(MetaDocs docs);
public static void ReloadMeta() { lock (ReloadTimeLock) { DateTimeOffset now = DateTimeOffset.UtcNow; if (now.Subtract(LastReload).TotalSeconds < 15) { Console.WriteLine("Ignoring too-fast reload..."); return; } LastReload = now; } lock (ReloadLock) { Console.WriteLine("Reloading meta..."); MetaDocs docs = MetaDocsLoader.DownloadAll(); Console.WriteLine("Meta loaded, HTMLizing..."); List <WebsiteMetaCommand> _commands = new List <WebsiteMetaCommand>(); List <WebsiteMetaTag> _tags = new List <WebsiteMetaTag>(); List <WebsiteMetaObjectType> _objectTypes = new List <WebsiteMetaObjectType>(); List <WebsiteMetaEvent> _events = new List <WebsiteMetaEvent>(); List <WebsiteMetaAction> _actions = new List <WebsiteMetaAction>(); List <WebsiteMetaLanguage> _languages = new List <WebsiteMetaLanguage>(); List <WebsiteMetaMechanism> _mechanisms = new List <WebsiteMetaMechanism>(); List <WebsiteMetaObject> _allObjects = new List <WebsiteMetaObject>(); void procSet <T, T2>(ref List <T> webObjs, ICollection <T2> origObjs) where T : WebsiteMetaObject <T2>, new() where T2 : MetaObject { foreach (T2 obj in origObjs) { T webObj = new T() { Object = obj }; webObjs.Add(webObj); } webObjs = webObjs.OrderBy(o => string.IsNullOrWhiteSpace(o.Object.Plugin) ? 0 : 1).ThenBy(o => o.Object.Warnings.Count).ThenBy(o => o.Object.Group).ThenBy(o => o.Object.CleanName).ToList(); _allObjects.AddRange(webObjs); } procSet(ref _commands, docs.Commands.Values); procSet(ref _tags, docs.Tags.Values); procSet(ref _objectTypes, docs.ObjectTypes.Values); procSet(ref _events, docs.Events.Values); procSet(ref _actions, docs.Actions.Values); procSet(ref _languages, docs.Languages.Values); procSet(ref _mechanisms, docs.Mechanisms.Values); foreach (WebsiteMetaObject obj in _allObjects) { obj.Docs = docs; obj.LoadHTML(); obj.AllSearchableText = obj.ObjectGeneric.GetAllSearchableText().ToLowerFast(); } Commands = _commands; Tags = _tags; ObjectTypes = _objectTypes; Events = _events; Actions = _actions; Languages = _languages; Mechanisms = _mechanisms; AllObjects = _allObjects; MetaDocs.CurrentMeta = docs; Console.WriteLine("Meta loaded and ready!"); } }
/// <summary> /// Post-check handler for linkable text, to find bad links. /// </summary> /// <param name="docs">The relevant docs object.</param> /// <param name="linkedtext">The relevant linkable list.</param> public void PostCheckLinkableText(MetaDocs docs, string linkedtext) { if (string.IsNullOrWhiteSpace(linkedtext)) { return; } int nextLinkIndex = linkedtext.IndexOf("<@link"); if (nextLinkIndex < 0) { return; } while (nextLinkIndex >= 0) { int endIndex = FindClosingTagMark(linkedtext, nextLinkIndex + 1); if (endIndex < 0) { return; } int startOfMetaCommand = nextLinkIndex + "<@link ".Length; string metaCommand = linkedtext.Substring(startOfMetaCommand, endIndex - startOfMetaCommand); if (!metaCommand.StartsWith("url")) { int firstSpace = metaCommand.IndexOf(' '); if (firstSpace < 0) { docs.LoadErrors.Add($"{Type.Name} '{Name}' contains text link '{metaCommand}', which is formatted incorrectly."); return; } string type = metaCommand.Substring(0, firstSpace).ToLowerFast(); string searchText = metaCommand.Substring(firstSpace + 1).ToLowerFast(); bool exists; if (type.Equals("command")) { exists = docs.Commands.ContainsKey(searchText); } else if (type.Equals("tag")) { exists = docs.FindTag(searchText) != null; } else if (type.Equals("mechanism")) { exists = docs.Mechanisms.ContainsKey(searchText); } else if (type.Equals("event")) { if (searchText.StartsWith("on ")) { searchText = searchText.Substring("on ".Length); } exists = docs.Events.Values.Any(e => e.CleanEvents.Any(s => s.Contains(searchText))); if (!exists) { exists = docs.Events.Values.Any(e => e.RegexMatcher.IsMatch(searchText)); } } else if (type.Equals("action")) { if (searchText.StartsWith("on ")) { searchText = searchText.Substring("on ".Length); } exists = docs.Actions.Values.Any(a => a.CleanActions.Any(s => s.Contains(searchText))); } else if (type.Equals("language")) { exists = docs.Languages.Keys.Any(s => s.Contains(searchText)); } else { docs.LoadErrors.Add($"{Type.Name} '{Name}' contains text link '{metaCommand}', which refers to an unknown meta type."); return; } if (!exists) { docs.LoadErrors.Add($"{Type.Name} '{Name}' contains text link '{metaCommand}', which does not exist."); return; } } nextLinkIndex = linkedtext.IndexOf("<@link", endIndex + 1); } }
public override void AddTo(MetaDocs docs) { docs.Mechanisms.Add(CleanName, this); }