/// <summary> /// Retrieves the event monitoring channel. /// </summary> /// <param name="guild">The guild to grab the channel from.</param> /// <returns>A retrieval result which may or may not have succeeded.</returns> private async Task <RetrieveEntityResult <ITextChannel> > GetMonitoringChannelAsync(IGuild guild) { var getSettings = await _moderation.GetOrCreateServerSettingsAsync(guild); if (!getSettings.IsSuccess) { return(RetrieveEntityResult <ITextChannel> .FromError(getSettings)); } var settings = getSettings.Entity; if (settings.MonitoringChannel is null) { return(RetrieveEntityResult <ITextChannel> .FromError("No configured channel.")); } var channel = await guild.GetChannelAsync((ulong)settings.MonitoringChannel); if (channel is null) { return(RetrieveEntityResult <ITextChannel> .FromError("Channel not found. Deleted?")); } if (!(channel is ITextChannel textChannel)) { return(RetrieveEntityResult <ITextChannel> .FromError("The configured channel isn't a text channel.")); } return(RetrieveEntityResult <ITextChannel> .FromSuccess(textChannel)); }
public async Task <RetrieveEntityResult <Character> > GetCharacterByNameAsync ( Server server, string name ) { server = _database.NormalizeReference(server); if (await GetCharacters(server).CountAsync(ch => string.Equals(ch.Name.ToLower(), name.ToLower())) > 1) { return(RetrieveEntityResult <Character> .FromError ( "There's more than one character with that name. Please specify which user it belongs to." )); } var character = GetCharacters(server).FirstOrDefault ( ch => string.Equals(ch.Name.ToLower(), name.ToLower()) ); if (character is null) { return(RetrieveEntityResult <Character> .FromError("No character with that name found.")); } return(RetrieveEntityResult <Character> .FromSuccess(character)); }
/// <inheritdoc/> public override Task <RetrieveEntityResult <bool> > IsConditionFulfilledForUser(IServiceProvider services, IGuildUser discordUser) { return(Task.FromResult ( RetrieveEntityResult <bool> .FromSuccess(discordUser.RoleIds.Contains((ulong)this.RoleID)) )); }
/// <summary> /// Loads the bot token from disk. /// </summary> /// <exception cref="FileNotFoundException">Thrown if the bot token file can't be found.</exception> /// <exception cref="InvalidDataException">Thrown if no token exists in the file.</exception> /// <returns>A retrieval result which may or may not have succeeded.</returns> public async Task <RetrieveEntityResult <string> > GetBotTokenAsync() { var tokenPath = UPath.Combine(UPath.Root, "Discord", "bot.token"); if (!this.FileSystem.FileExists(tokenPath)) { return(RetrieveEntityResult <string> .FromError("The token file could not be found.")); } var getTokenStream = OpenLocalStream(tokenPath); if (!getTokenStream.IsSuccess) { return(RetrieveEntityResult <string> .FromError("The token file could not be opened.")); } await using var tokenStream = getTokenStream.Entity; var token = await AsyncIO.ReadAllTextAsync(tokenStream); if (string.IsNullOrEmpty(token)) { return(RetrieveEntityResult <string> .FromError("The token file did not contain a valid token.")); } return(RetrieveEntityResult <string> .FromSuccess(token)); }
public async Task <RetrieveEntityResult <string> > GetSassAsync(bool includeNSFW = false) { if (!_isSassLoaded) { var loadSassAsync = await LoadSassAsync(); if (!loadSassAsync.IsSuccess) { return(RetrieveEntityResult <string> .FromError(loadSassAsync)); } } List <string> availableSass; if (includeNSFW) { availableSass = _sass.Union(_sassNSFW).ToList(); } else { availableSass = _sass; } if (availableSass.Count == 0) { return(RetrieveEntityResult <string> .FromError ( "There's no available sass. You'll just have to provide your own." )); } return(RetrieveEntityResult <string> .FromSuccess(availableSass.PickRandom())); }
public async Task <RetrieveEntityResult <IEnumerable <UserKink> > > GetUserKinksByCategoryAsync ( [NotNull] IUser user, KinkCategory category ) { var getUserKinksResult = await GetUserKinksAsync(user); if (!getUserKinksResult.IsSuccess) { return(RetrieveEntityResult <IEnumerable <UserKink> > .FromError(getUserKinksResult)); } var userKinks = getUserKinksResult.Entity; var groups = userKinks.GroupBy(k => k.Kink.Category).ToList(); var first = groups.FirstOrDefault(g => g.Key == category); if (first is null) { return(RetrieveEntityResult <IEnumerable <UserKink> > .FromSuccess(new UserKink[] { })); } return(RetrieveEntityResult <IEnumerable <UserKink> > .FromSuccess(first.OrderBy(uk => uk.Kink.ToString()))); }
public async Task <RetrieveEntityResult <Character> > GetUserCharacterByNameAsync ( [NotNull] ICommandContext context, [NotNull] User characterOwner, [NotNull] string characterName ) { var character = await GetUserCharacters(characterOwner, context.Guild) .FirstOrDefaultAsync ( ch => string.Equals(ch.Name, characterName, StringComparison.OrdinalIgnoreCase) ); if (character is null) { var isCurrentUser = context.Message.Author.Id == (ulong)characterOwner.DiscordID; var errorMessage = isCurrentUser ? "You don't own a character with that name." : "The user doesn't own a character with that name."; return(RetrieveEntityResult <Character> .FromError(errorMessage)); } return(RetrieveEntityResult <Character> .FromSuccess(character)); }
public static async Task <RetrieveEntityResult <string> > BestLevenshteinMatchAsync ( [NotNull, ItemNotNull] this IQueryable <string> @this, [NotNull] string search, double tolerance = 0.25 ) { var candidates = from candidate in @this let distance = LevenshteinDistance.Compute ( candidate.ToLowerInvariant(), search.ToLowerInvariant() ) let maxDistance = Math.Max(candidate.Length, search.Length) let percentile = distance / (float)maxDistance select new Tuple <string, int, double>(candidate, distance, percentile); var hasAnyPassing = await candidates.Where(c => c.Item3 <= tolerance).AnyAsync(); if (!hasAnyPassing) { return(RetrieveEntityResult <string> .FromError("No sufficiently close match found.")); } var best = await candidates.OrderBy(x => x.Item2).FirstAsync(); return(RetrieveEntityResult <string> .FromSuccess(best.Item1)); }
private RetrieveEntityResult <TUser> FindBestMatchingUserBy <TUser> ( IEnumerable <TUser> users, Func <TUser, string> selector, string value ) where TUser : class, IUser { var values = users.Select(u => (User: u, Value: selector(u))).ToImmutableList(); var literalMatch = values.FirstOrDefault(uv => uv.Value == value).User; if (!(literalMatch is null)) { return(RetrieveEntityResult <TUser> .FromSuccess(literalMatch)); } var partialMatch = values.FirstOrDefault ( uv => string.Equals(uv.Value, value, StringComparison.OrdinalIgnoreCase) ).User; if (!(partialMatch is null)) { return(RetrieveEntityResult <TUser> .FromSuccess(partialMatch)); } return(RetrieveEntityResult <TUser> .FromError("No matching user found.")); }
/// <summary> /// Loads and retrieves the bundled transformation messages. /// </summary> /// <param name="this">The content service.</param> /// <returns>A retrieval result which may or may not have succeeded.</returns> public static RetrieveEntityResult <TransformationText> GetTransformationMessages ( [NotNull] this ContentService @this ) { if ([email protected](TransformationMessagesPath)) { return(RetrieveEntityResult <TransformationText> .FromError("Transformation messages not found.")); } using ( var reader = new StreamReader ( @this.FileSystem.OpenFile ( TransformationMessagesPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) ) { var content = reader.ReadToEnd(); if (!TransformationText.TryDeserialize(content, out var text)) { return(RetrieveEntityResult <TransformationText> .FromError("Failed to parse the messages.")); } return(RetrieveEntityResult <TransformationText> .FromSuccess(text)); } }
/// <summary> /// Gets or creates the global transformation protection data for the given user. /// </summary> /// <param name="discordUser">The user.</param> /// <returns>Global protection data for the given user.</returns> public async Task <RetrieveEntityResult <GlobalUserProtection> > GetOrCreateGlobalUserProtectionAsync ( IUser discordUser ) { var protection = await _database.GlobalUserProtections.AsQueryable() .FirstOrDefaultAsync(p => p.User.DiscordID == (long)discordUser.Id); if (!(protection is null)) { return(RetrieveEntityResult <GlobalUserProtection> .FromSuccess(protection)); } var getUserResult = await _users.GetOrRegisterUserAsync(discordUser); if (!getUserResult.IsSuccess) { return(RetrieveEntityResult <GlobalUserProtection> .FromError(getUserResult)); } var user = getUserResult.Entity; protection = new GlobalUserProtection(user); _database.GlobalUserProtections.Update(protection); await _database.SaveChangesAsync(); return(RetrieveEntityResult <GlobalUserProtection> .FromSuccess(protection)); }
public async Task <RetrieveEntityResult <Roleplay> > GetUserRoleplayByNameAsync ( [NotNull] ICommandContext context, [NotNull] IUser roleplayOwner, [NotNull] string roleplayName ) { var roleplay = await GetRoleplays(context.Guild) .FirstOrDefaultAsync ( rp => rp.Name.Equals(roleplayName, StringComparison.OrdinalIgnoreCase) && rp.Owner.DiscordID == (long)roleplayOwner.Id ); if (roleplay is null) { var isCurrentUser = context.Message.Author.Id == roleplayOwner.Id; var errorMessage = isCurrentUser ? "You don't own a roleplay with that name." : "The user doesn't own a roleplay with that name."; return(RetrieveEntityResult <Roleplay> .FromError(errorMessage)); } return(RetrieveEntityResult <Roleplay> .FromSuccess(roleplay)); }
public async Task <RetrieveEntityResult <IReadOnlyList <Transformation> > > GetTransformationsByPartAndSpeciesAsync ( Bodypart bodypart, Species species ) { var bodyparts = new List <Bodypart>(); if (bodypart.IsComposite()) { bodyparts.AddRange(bodypart.GetComposingParts()); } else { bodyparts.Add(bodypart); } var transformations = await _database.Transformations.AsQueryable() .Where(tf => bodyparts.Contains(tf.Part) && tf.Species.Name.ToLower().Equals(species.Name.ToLower())) .ToListAsync(); if (!transformations.Any()) { return(RetrieveEntityResult <IReadOnlyList <Transformation> > .FromError ( "No transformation found for that combination." )); } return(RetrieveEntityResult <IReadOnlyList <Transformation> > .FromSuccess(transformations)); }
public async Task <RetrieveEntityResult <Character> > GetNamedCharacterAsync ( [NotNull] string characterName, [NotNull] IGuild guild ) { var guildCharacters = _database.Characters.Where(ch => ch.ServerID == (long)guild.Id); if (await guildCharacters.CountAsync(ch => string.Equals(ch.Name, characterName, StringComparison.OrdinalIgnoreCase)) > 1) { return(RetrieveEntityResult <Character> .FromError ( "There's more than one character with that name. Please specify which user it belongs to." )); } var character = GetCharacters(guild).FirstOrDefault(ch => string.Equals(ch.Name, characterName, StringComparison.OrdinalIgnoreCase)); if (character is null) { return(RetrieveEntityResult <Character> .FromError("No character with that name found.")); } return(RetrieveEntityResult <Character> .FromSuccess(character)); }
public async Task <RetrieveEntityResult <IGuildChannel> > GetDedicatedRoleplayChannelAsync ( [NotNull] IGuild guild, [NotNull] Roleplay roleplay ) { if (!(roleplay.DedicatedChannelID is null)) { var guildChannel = await guild.GetChannelAsync((ulong)roleplay.DedicatedChannelID.Value); if (!(guildChannel is null)) { return(RetrieveEntityResult <IGuildChannel> .FromSuccess(guildChannel)); } return(RetrieveEntityResult <IGuildChannel> .FromError ( "Attempted to delete a channel, but it appears to have been deleted." )); } return(RetrieveEntityResult <IGuildChannel> .FromError ( "The roleplay doesn't have a dedicated channel." )); }
public async Task <RetrieveEntityResult <Roleplay> > GetUserRoleplayByNameAsync ( Server server, User roleplayOwner, string roleplayName ) { roleplayOwner = _database.NormalizeReference(roleplayOwner); server = _database.NormalizeReference(server); var roleplay = await GetRoleplays(server) .FirstOrDefaultAsync ( rp => rp.Name.ToLower().Equals(roleplayName.ToLower()) && rp.Owner == roleplayOwner ); if (roleplay is null) { return(RetrieveEntityResult <Roleplay> .FromError("You don't own a roleplay with that name.")); } return(RetrieveEntityResult <Roleplay> .FromSuccess(roleplay)); }
/// <summary> /// Gets a roleplay by its given name. /// </summary> /// <param name="roleplayName">The name of the roleplay.</param> /// <param name="server">The server that the search is scoped to.</param> /// <returns>A retrieval result which may or may not have succeeded.</returns> public async Task <RetrieveEntityResult <Roleplay> > GetNamedRoleplayAsync(string roleplayName, Server server) { server = _database.NormalizeReference(server); if ( await _database.Roleplays.AsQueryable() .CountAsync(rp => string.Equals(rp.Name.ToLower(), roleplayName.ToLower())) > 1 ) { return(RetrieveEntityResult <Roleplay> .FromError ( "There's more than one roleplay with that name. Please specify which user it belongs to." )); } var roleplay = GetRoleplays(server) .FirstOrDefault(rp => string.Equals(rp.Name.ToLower(), roleplayName.ToLower())); if (roleplay is null) { return(RetrieveEntityResult <Roleplay> .FromError("No roleplay with that name found.")); } return(RetrieveEntityResult <Roleplay> .FromSuccess(roleplay)); }
public async Task <RetrieveEntityResult <Kink> > GetNextKinkByCurrentFListIDAsync ( int precedingFListID, CancellationToken ct = default ) { var getKinkResult = await GetKinkByFListIDAsync(precedingFListID, ct); if (!getKinkResult.IsSuccess) { return(getKinkResult); } var currentKink = getKinkResult.Entity; var getKinksResult = await GetKinksByCategoryAsync(currentKink.Category, ct); if (!getKinksResult.IsSuccess) { return(RetrieveEntityResult <Kink> .FromError(getKinksResult)); } var group = getKinksResult.Entity; var nextKink = group.SkipUntil(k => k.FListID == precedingFListID).FirstOrDefault(); if (nextKink is null) { return(RetrieveEntityResult <Kink> .FromError("The current kink was the last one in the category.")); } return(RetrieveEntityResult <Kink> .FromSuccess(nextKink)); }
public async Task <RetrieveEntityResult <IEnumerable <UserKink> > > GetUserKinksByCategoryAsync ( IUser user, KinkCategory category, CancellationToken ct = default ) { var getUserKinksResult = await GetUserKinksAsync(user, ct : ct); if (!getUserKinksResult.IsSuccess) { return(RetrieveEntityResult <IEnumerable <UserKink> > .FromError(getUserKinksResult)); } var userKinks = getUserKinksResult.Entity; var group = userKinks.Where(k => k.Kink.Category == category).ToList(); if (!group.Any()) { return(RetrieveEntityResult <IEnumerable <UserKink> > .FromSuccess(new UserKink[] { })); } return(RetrieveEntityResult <IEnumerable <UserKink> > .FromSuccess(group.OrderBy(uk => uk.Kink.ToString()))); }
/// <summary> /// Gets a queryable list of characters belonging to the given user on their guild. /// </summary> /// <param name="guildUser">The user.</param> /// <param name="ct">The cancellation token in use.</param> /// <returns>A retrieval result which may or may not have succeeded.</returns> public async Task <RetrieveEntityResult <IEnumerable <Character> > > GetUserCharactersAsync ( IGuildUser guildUser, CancellationToken ct = default ) { var getUser = await _users.GetOrRegisterUserAsync(guildUser, ct); if (!getUser.IsSuccess) { return(RetrieveEntityResult <IEnumerable <Character> > .FromError(getUser)); } var getServer = await _servers.GetOrRegisterServerAsync(guildUser.Guild, ct); if (!getServer.IsSuccess) { return(RetrieveEntityResult <IEnumerable <Character> > .FromError(getServer)); } var user = getUser.Entity; var server = getServer.Entity; return(RetrieveEntityResult <IEnumerable <Character> > .FromSuccess ( await _characters.GetUserCharactersAsync(server, user, ct) )); }
public async Task <RetrieveEntityResult <Character> > GetCurrentCharacterAsync ( [NotNull] ICommandContext context, [NotNull] User discordUser ) { if (!await HasActiveCharacterOnServerAsync(discordUser, context.Guild)) { var isCurrentUser = context.Message.Author.Id == (ulong)discordUser.DiscordID; var errorMessage = isCurrentUser ? "You haven't assumed a character." : "The user hasn't assumed a character."; return(RetrieveEntityResult <Character> .FromError(errorMessage)); } var currentCharacter = await GetUserCharacters(discordUser, context.Guild) .FirstOrDefaultAsync ( ch => ch.IsCurrent ); if (currentCharacter is null) { return(RetrieveEntityResult <Character> .FromError("Failed to retrieve a current character.")); } return(RetrieveEntityResult <Character> .FromSuccess(currentCharacter)); }
public static async Task <RetrieveEntityResult <TResult> > SelectFromBestLevenshteinMatchAsync <TSource, TResult> ( this IQueryable <TSource> @this, Func <TSource, TResult> selector, Expression <Func <TSource, string> > stringSelector, string search, double tolerance = 0.25 ) where TResult : class where TSource : class { var matchResult = (await @this.Select(stringSelector).ToListAsync()).BestLevenshteinMatch(search, tolerance); if (!matchResult.IsSuccess) { return(RetrieveEntityResult <TResult> .FromError(matchResult)); } var selectedString = matchResult.Entity; var selectorFunc = stringSelector.Compile(); var selectedObject = await @this.FirstOrDefaultAsync(i => selectorFunc(i) == selectedString); if (selectedObject is null) { return(RetrieveEntityResult <TResult> .FromError("No matching object for the selector found.")); } var result = selector(selectedObject); return(RetrieveEntityResult <TResult> .FromSuccess(result)); }
public static RetrieveEntityResult <string> BestLevenshteinMatch ( this IEnumerable <string> @this, string search, double tolerance = 0.25 ) { var candidates = @this.Select ( s => { var distance = LevenshteinDistance.Compute ( s.ToLowerInvariant(), search.ToLowerInvariant() ); var maxDistance = Math.Max(s.Length, search.Length); var percentile = distance / (float)maxDistance; return(value: s, distance, percentile); } ); var passing = candidates.Where(c => c.percentile <= tolerance).ToList(); if (!passing.Any()) { return(RetrieveEntityResult <string> .FromError("No sufficiently close match found.")); } var best = passing.MinBy(c => c.distance).First(); return(RetrieveEntityResult <string> .FromSuccess(best.value)); }
public static RetrieveEntityResult <TResult> SelectFromBestLevenshteinMatch <TSource, TResult> ( this IEnumerable <TSource> @this, Func <TSource, TResult> selector, Func <TSource, string> stringSelector, string search, double tolerance = 0.25 ) where TResult : class where TSource : class { var enumerable = @this as IList <TSource> ?? @this.ToList(); var matchResult = enumerable.Select(stringSelector).BestLevenshteinMatch(search, tolerance); if (!matchResult.IsSuccess) { return(RetrieveEntityResult <TResult> .FromError(matchResult)); } var selectedString = matchResult.Entity; var selectedObject = enumerable.FirstOrDefault(i => stringSelector(i) == selectedString); if (selectedObject is null) { return(RetrieveEntityResult <TResult> .FromError("No matching object for the selector found.")); } var result = selector(selectedObject); return(RetrieveEntityResult <TResult> .FromSuccess(result)); }
/// <summary> /// Retrieves the permission instance of the given type from the registry. /// </summary> /// <param name="permissionType">The permission type.</param> /// <returns>A retrieval result which may or may not have succeeded.</returns> public RetrieveEntityResult <IPermission> GetPermission(Type permissionType) { if (!_registeredPermissions.TryGetValue(permissionType, out var permission)) { return(RetrieveEntityResult <IPermission> .FromError("No permission of that type has been registered.")); } return(RetrieveEntityResult <IPermission> .FromSuccess(permission)); }
/// <inheritdoc /> public override ValueTask <RetrieveEntityResult <Snowflake> > TryParse(string value, CancellationToken ct) { return(new ValueTask <RetrieveEntityResult <Snowflake> > ( !Snowflake.TryParse(value, out var snowflake) ? RetrieveEntityResult <Snowflake> .FromError($"Failed to parse \"{value}\" as a Snowflake.") : RetrieveEntityResult <Snowflake> .FromSuccess(snowflake.Value) )); }
public RetrieveEntityResult <string> GetJoinMessage(Server server) { if (server.JoinMessage.IsNullOrWhitespace()) { return(RetrieveEntityResult <string> .FromError("No join message set.")); } return(RetrieveEntityResult <string> .FromSuccess(server.JoinMessage)); }
public RetrieveEntityResult <string> GetDescription(Server server) { if (server.Description.IsNullOrWhitespace()) { return(RetrieveEntityResult <string> .FromError("No description set.")); } return(RetrieveEntityResult <string> .FromSuccess(server.Description)); }
public static async Task <RetrieveEntityResult <IReadOnlyList <Species> > > DiscoverBundledSpeciesAsync ( [NotNull] this ContentService @this ) { const string speciesFilename = "Species.yml"; var deser = new DeserializerBuilder() .WithNodeDeserializer(i => new ValidatingNodeDeserializer(i), s => s.InsteadOf <ObjectNodeDeserializer>()) .WithNamingConvention(new UnderscoredNamingConvention()) .Build(); var species = new List <Species>(); var speciesFolders = @this.FileSystem.EnumerateDirectories ( BaseTransformationSpeciesPath ); foreach (var directory in speciesFolders) { var speciesFilePath = UPath.Combine(directory, speciesFilename); if ([email protected](speciesFilePath)) { continue; } var openStreamResult = @this.OpenLocalStream(speciesFilePath); if (!openStreamResult.IsSuccess) { return(RetrieveEntityResult <IReadOnlyList <Species> > .FromError(openStreamResult)); } using (var speciesFile = openStreamResult.Entity) { var content = await AsyncIO.ReadAllTextAsync(speciesFile, Encoding.UTF8); try { species.Add(deser.Deserialize <Species>(content)); } catch (YamlException yex) { if (yex.InnerException is SerializationException sex) { return(RetrieveEntityResult <IReadOnlyList <Species> > .FromError(sex)); } return(RetrieveEntityResult <IReadOnlyList <Species> > .FromError(yex)); } } } return(RetrieveEntityResult <IReadOnlyList <Species> > .FromSuccess(species)); }
public async Task <RetrieveEntityResult <Server> > GetServerAsync(IGuild discordServer) { var server = await _database.Servers.FirstOrDefaultAsync(u => u.DiscordID == (long)discordServer.Id); if (server is null) { return(RetrieveEntityResult <Server> .FromError("That server has not been registered in the database.")); } return(RetrieveEntityResult <Server> .FromSuccess(server)); }