using System.Collections; namespace RegexBot.Common; /// /// Represents a commonly-used configuration structure: an array of strings consisting of values. /// public class EntityList : IEnumerable { private readonly IReadOnlyCollection _innerList; /// Gets an enumerable collection of all role names defined in this list. public IEnumerable Roles => _innerList.Where(n => n.Type == EntityType.Role); /// Gets an enumerable collection of all channel names defined in this list. public IEnumerable Channels => _innerList.Where(n => n.Type == EntityType.Channel); /// Gets an enumerable collection of all user names defined in this list. public IEnumerable Users => _innerList.Where(n => n.Type == EntityType.User); /// /// Creates a new EntityList instance with no data. /// public EntityList() : this(null, false) { } /// /// Creates a new EntityList instance using the given JSON token as input. /// /// JSON array to be used for input. For ease of use, null values are also accepted. /// Specifies if all entities defined in configuration must have their type specified. /// The input is not a JSON array. /// /// Unintiutively, this exception is thrown if a user-provided configuration value is blank. /// /// /// When enforceTypes is set, this is thrown if an EntityName results in having its Type be Unspecified. /// public EntityList(JToken? input, bool enforceTypes) { if (input == null) { _innerList = new List().AsReadOnly(); return; } if (input.Type != JTokenType.Array) throw new ArgumentException("JToken input must be a JSON array."); var inputArray = (JArray)input; var list = new List(); foreach (var item in inputArray.Values()) { if (string.IsNullOrWhiteSpace(item)) continue; var itemName = new EntityName(item); if (enforceTypes && itemName.Type == EntityType.Unspecified) throw new FormatException($"The following value is not prefixed: {item}"); list.Add(itemName); } _innerList = list.AsReadOnly(); } /// /// Checks if the parameters of the given matches with /// any entity specified in this list. /// /// The incoming message object with which to scan for a match. /// /// Specifies if EntityName instances within this list should have their internal ID value /// updated if found during the matching process. /// /// /// True if the message author exists in this list, or if the message's channel exists in this list, /// or if the message author contains a role that exists in this list. /// public bool IsListMatch(SocketMessage msg, bool keepId) { var author = (SocketGuildUser)msg.Author; var authorRoles = author.Roles; var channel = msg.Channel; foreach (var entry in this) { if (entry.Type == EntityType.Role) { if (entry.Id.HasValue) { return authorRoles.Any(r => r.Id == entry.Id.Value); } else { foreach (var r in authorRoles) { if (!string.Equals(r.Name, entry.Name, StringComparison.OrdinalIgnoreCase)) break; if (keepId) entry.SetId(r.Id); return true; } } } else if (entry.Type == EntityType.Channel) { if (entry.Id.HasValue) { return entry.Id.Value == channel.Id; } else { if (!string.Equals(channel.Name, entry.Name, StringComparison.OrdinalIgnoreCase)) break; if (keepId) entry.SetId(channel.Id); return true; } } else // User { if (entry.Id.HasValue) { return entry.Id.Value == author.Id; } else { if (!string.Equals(author.Username, entry.Name, StringComparison.OrdinalIgnoreCase)) break; if (keepId) entry.SetId(author.Id); return true; } } } return false; } /// /// Determines if this list contains no entries. /// public bool IsEmpty() => _innerList.Count == 0; /// public override string ToString() => $"Entity list contains {_innerList.Count} item(s)."; /// public IEnumerator GetEnumerator() => _innerList.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }