using Discord; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; namespace RegexBot.Common; /// /// Miscellaneous utility methods useful for the bot and modules. /// public static class Utilities { /// /// Gets a compiled regex that matches a channel tag and pulls its snowflake value. /// public static Regex ChannelMention { get; } = new(@"<#(?\d+)>", RegexOptions.Compiled); /// /// Gets a compiled regex that matches a custom emoji and pulls its name and ID. /// public static Regex CustomEmoji { get; } = new(@"<:(?[A-Za-z0-9_]{2,}):(?\d+)>", RegexOptions.Compiled); /// /// Gets a compiled regex that matches a fully formed Discord handle, extracting the name and discriminator. /// public static Regex DiscriminatorSearch { get; } = new(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled); /// /// Gets a compiled regex that matches a user tag and pulls its snowflake value. /// public static Regex UserMention { get; } = new(@"<@!?(?\d+)>", RegexOptions.Compiled); /// /// Performs common checks on the specified message to see if it fits all the criteria of a /// typical, ordinary message sent by an ordinary guild user. /// public static bool IsValidUserMessage(SocketMessage msg, [NotNullWhen(true)] out SocketTextChannel? channel) { channel = default; if (msg.Channel is not SocketTextChannel ch) return false; if (msg.Author.IsBot || msg.Author.IsWebhook) return false; if (((IMessage)msg).Type != MessageType.Default) return false; if (msg is SocketSystemMessage) return false; channel = ch; return true; } /// /// Given a JToken, gets all string-based values out of it if the token may be a string /// or an array of strings. /// /// The JSON token to analyze and retrieve strings from. /// Thrown if the given token is not a string or array containing all strings. /// Thrown if the given token is null. public static List LoadStringOrStringArray(JToken? token) { const string ExNotString = "This token contains a non-string element."; if (token == null) throw new ArgumentNullException(nameof(token), "The provided token is null."); var results = new List(); if (token.Type == JTokenType.String) { results.Add(token.Value()!); } else if (token.Type == JTokenType.Array) { foreach (var entry in token.Values()) { if (entry.Type != JTokenType.String) throw new ArgumentException(ExNotString, nameof(token)); results.Add(entry.Value()!); } } else { throw new ArgumentException(ExNotString, nameof(token)); } return results; } /// /// Returns a representation of this entity that can be parsed by the constructor. /// public static string AsEntityNameString(this IUser entity) => $"@{entity.Id}::{entity.Username}"; /// /// If given string is in an EntityName format, returns a displayable representation of it based on /// a cache query. Otherwise, returns the input string as-is. /// [return: NotNullIfNotNull("input")] public static string? TryFromEntityNameString(string? input, RegexbotClient bot) { string? result = null; try { var entityTry = new EntityName(input!, EntityType.User); var issueq = bot.EcQueryUser(entityTry.Id!.Value.ToString()); if (issueq != null) result = $"<@{issueq.UserId}> - {issueq.Username}#{issueq.Discriminator} `{issueq.UserId}`"; else result = $"Unknown user with ID `{entityTry.Id!.Value}`"; } catch (Exception) { } return result ?? input; } /// /// Given an input string, replaces certain special character combinations with information /// from the specified message. /// public static string ProcessTextTokens(string input, SocketMessage m) { // TODO elaborate on this // For now, replaces all instances of @_ with the message sender. return input .Replace("@_", m.Author.Mention) .Replace("@\\_", "@_"); } }