public async Task CreateTagAsync(ulong guildId, ulong creatorId, string name, string content) { _authorizationService.RequireClaims(AuthorizationClaim.CreateTag); if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("The tag name cannot be blank or whitespace.", nameof(name)); } if (string.IsNullOrWhiteSpace(content)) { throw new ArgumentException("The tag content cannot be blank or whitespace.", nameof(content)); } if (!_tagNameRegex.IsMatch(name)) { throw new ArgumentException("The tag name cannot have punctuation at the end.", nameof(name)); } name = name.Trim().ToLower(); if (await _modixContext.Set <TagEntity>().Where(x => x.GuildId == guildId).Where(x => x.DeleteActionId == null).AnyAsync(x => x.Name == name)) { throw new InvalidOperationException($"A tag with the name '{name}' already exists."); } var tag = new TagEntity { GuildId = guildId, OwnerUserId = creatorId, Name = name, Content = content, }; var createAction = new TagActionEntity() { GuildId = guildId, Created = DateTimeOffset.Now, Type = TagActionType.TagCreated, CreatedById = creatorId, }; tag.CreateAction = createAction; _modixContext.Set <TagEntity>().Add(tag); await _modixContext.SaveChangesAsync(); }
/// <inheritdoc /> public async Task <PromotionActionSummary?> TryCloseAsync(long campaignId, ulong closedById, PromotionCampaignOutcome outcome) { var entity = await ModixContext.Set <PromotionCampaignEntity>() .Where(x => x.Id == campaignId) .FirstOrDefaultAsync(); if ((entity == null) || (entity.CloseActionId != null)) { return(null); } entity.Outcome = outcome; entity.CloseAction = new PromotionActionEntity() { GuildId = entity.GuildId, Type = PromotionActionType.CampaignClosed, Created = DateTimeOffset.Now, CreatedById = closedById, CampaignId = entity.Id }; await ModixContext.SaveChangesAsync(); var action = await ModixContext.Set <PromotionActionEntity>().AsNoTracking() .Where(x => x.Id == entity.CloseActionId) .AsExpandable() .Select(PromotionActionSummary.FromEntityProjection) .FirstAsync(); return(action); }
public async Task <bool> TryUpdateAync(long infractionId, string newReason, ulong updatedById) { var entity = await ModixContext.Set <InfractionEntity>() .Where(x => x.Id == infractionId) .FirstOrDefaultAsync(); if (entity == null) { return(false); } var originalReason = entity.Reason; entity.Reason = newReason; entity.UpdateAction = new ModerationActionEntity() { GuildId = entity.GuildId, Type = ModerationActionType.InfractionUpdated, Created = DateTimeOffset.Now, CreatedById = updatedById, InfractionId = entity.Id, OriginalInfractionReason = originalReason, }; await ModixContext.SaveChangesAsync(); await RaiseModerationActionCreatedAsync(entity.UpdateAction); return(true); }
/// <inheritdoc /> public async Task <bool> TryUpdateAsync(ulong userId, ulong guildId, Action <GuildUserMutationData> updateAction) { if (updateAction == null) { throw new ArgumentNullException(nameof(updateAction)); } var entity = await ModixContext.Set <GuildUserEntity>() .Where(x => x.UserId == userId) .Where(x => x.GuildId == guildId) .Include(x => x.User) .FirstOrDefaultAsync(); if (entity == null) { return(false); } var data = GuildUserMutationData.FromEntity(entity); updateAction.Invoke(data); data.ApplyTo(entity); ModixContext.UpdateProperty(entity.User, x => x.Username); ModixContext.UpdateProperty(entity.User, x => x.Discriminator); ModixContext.UpdateProperty(entity, x => x.Nickname); ModixContext.UpdateProperty(entity, x => x.LastSeen); await ModixContext.SaveChangesAsync(); return(true); }
/// <inheritdoc /> public async Task CreateAsync(DeletedMessageBatchCreationData data) { if (data is null) { throw new ArgumentNullException(nameof(data)); } var entity = data.ToEntity(); await ModixContext.Set <DeletedMessageBatchEntity>().AddAsync(entity); await ModixContext.SaveChangesAsync(); entity.CreateAction.DeletedMessageBatchId = entity.Id; await ModixContext.SaveChangesAsync(); var deletedMessageEntities = data.Data.Select(x => { x.BatchId = entity.Id; return(x.ToBatchEntity()); }); await ModixContext.Set <DeletedMessageEntity>().AddRangeAsync(deletedMessageEntities); await ModixContext.SaveChangesAsync(); await RaiseModerationActionCreatedAsync(entity.CreateAction); }
/// <inheritdoc /> public async Task <bool> TryDeleteAsync(long infractionId, ulong deletedById) { var entity = await ModixContext.Set <InfractionEntity>() .Where(x => x.Id == infractionId) .FirstOrDefaultAsync(); if ((entity == null) || (entity.DeleteActionId != null)) { return(false); } entity.DeleteAction = new ModerationActionEntity() { GuildId = entity.GuildId, Type = ModerationActionType.InfractionDeleted, Created = DateTimeOffset.Now, CreatedById = deletedById, InfractionId = entity.Id }; await ModixContext.SaveChangesAsync(); await RaiseModerationActionCreatedAsync(entity.DeleteAction); return(true); }
/// <inheritdoc /> public async Task <IReadOnlyCollection <InfractionSummary> > SearchSummariesAsync(InfractionSearchCriteria searchCriteria, IEnumerable <SortingCriteria>?sortingCriteria = null) => await ModixContext.Set <InfractionEntity>().AsNoTracking() .FilterBy(searchCriteria) .AsExpandable() .Select(InfractionSummary.FromEntityProjection) .SortBy(sortingCriteria, InfractionSummary.SortablePropertyMap) .ToArrayAsync();
/// <inheritdoc /> public async Task <IReadOnlyList <MessageCountByDate> > GetGuildUserMessageCountByDate(ulong guildId, ulong userId, TimeSpan timespan) { var earliestDateTime = DateTimeOffset.UtcNow - timespan; var results = await ModixContext.Set <MessageCountByDate>() .FromSqlRaw( @"select date(""Timestamp""::timestamp AT TIME ZONE 'UTC') as ""Date"", count(""Id"") as ""MessageCount"" from ""Messages"" where ""GuildId"" = :GuildId and ""AuthorId"" = :UserId and ""Timestamp"" > :StartTimestamp group by date(""Timestamp""::timestamp AT TIME ZONE 'UTC')", new NpgsqlParameter(":GuildId", NpgsqlDbType.Bigint) { Value = unchecked ((long)guildId) }, new NpgsqlParameter(":UserId", NpgsqlDbType.Bigint) { Value = unchecked ((long)userId) }, new NpgsqlParameter(":StartTimestamp", NpgsqlDbType.TimestampTz) { Value = earliestDateTime }) .ToArrayAsync(); return(results); }
/// <inheritdoc /> public async Task CreateAsync( GuildUserCreationData data, CancellationToken cancellationToken) { if (data == null) { throw new ArgumentNullException(nameof(data)); } var guildDataEntity = data.ToGuildDataEntity(); guildDataEntity.User = await ModixContext .Set <UserEntity>() .AsQueryable() .FirstOrDefaultAsync(x => x.Id == data.UserId, cancellationToken) ?? data.ToUserEntity(); await ModixContext.Set <GuildUserEntity>().AddAsync(guildDataEntity, cancellationToken); if ((guildDataEntity.User.Username != data.Username) && !(data.Username is null)) { guildDataEntity.User.Username = data.Username; } if ((guildDataEntity.User.Discriminator != data.Discriminator) && !(data.Discriminator is null)) { guildDataEntity.User.Discriminator = data.Discriminator; } await ModixContext.SaveChangesAsync(cancellationToken); }
/// <inheritdoc /> public async Task CreateAsync(MessageCreationData data) { var entity = data.ToEntity(); await ModixContext.Set <MessageEntity>().AddAsync(entity); await ModixContext.SaveChangesAsync(); }
/// <inheritdoc /> public async Task <IReadOnlyDictionary <EphemeralEmoji, int> > GetCountsAsync(EmojiSearchCriteria criteria) { if (criteria is null) { throw new ArgumentNullException(nameof(criteria)); } var emoji = await ModixContext.Set <EmojiEntity>().AsNoTracking() .FilterBy(criteria) .GroupBy(x => new { x.EmojiId, EmojiName = x.EmojiId == null ? x.EmojiName : null }, x => new { x.EmojiId, x.EmojiName, x.IsAnimated, x.Timestamp }) .ToArrayAsync(); var counts = emoji.ToDictionary( x => { var mostRecentEmoji = x.OrderByDescending(y => y.Timestamp).First(); return(EphemeralEmoji.FromRawData(mostRecentEmoji.EmojiName, mostRecentEmoji.EmojiId, mostRecentEmoji.IsAnimated)); }, x => x.Count()); return(counts); }
/// <inheritdoc /> public async Task <bool> TryUpdateAsync(ulong roleId, Action <GuildRoleMutationData> updateAction) { if (updateAction == null) { throw new ArgumentNullException(nameof(updateAction)); } var entity = await ModixContext.Set <GuildRoleEntity>() .Where(x => x.RoleId == roleId) .FirstOrDefaultAsync(); if (entity == null) { return(false); } var data = GuildRoleMutationData.FromEntity(entity); updateAction.Invoke(data); data.ApplyTo(entity); ModixContext.UpdateProperty(entity, x => x.Name); ModixContext.UpdateProperty(entity, x => x.Position); await ModixContext.SaveChangesAsync(); return(true); }
/// <inheritdoc /> public Task <DateTimeOffset?> ReadExpiresFirstOrDefaultAsync(InfractionSearchCriteria searchCriteria, IEnumerable <SortingCriteria>?sortingCriteria = null) => ModixContext.Set <InfractionEntity>().AsNoTracking() .FilterBy(searchCriteria) .AsExpandable() .Select(InfractionSummary.FromEntityProjection) .SortBy(sortingCriteria, InfractionSummary.SortablePropertyMap) .Select(x => x.Expires) .FirstOrDefaultAsync();
/// <inheritdoc /> public async Task <MessageBrief> GetMessage(ulong messageId) { return(await ModixContext.Set <MessageEntity>() .AsNoTracking() .Where(x => x.Id == messageId) .Select(MessageBrief.FromEntityProjection) .FirstOrDefaultAsync()); }
/// <inheritdoc /> public async Task <IReadOnlyCollection <ModerationActionSummary> > SearchSummariesAsync(ModerationActionSearchCriteria searchCriteria) { return(await ModixContext.Set <ModerationActionEntity>().AsNoTracking() .FilterBy(searchCriteria) .AsExpandable() .Select(ModerationActionSummary.FromEntityProjection) .ToArrayAsync()); }
public async Task <List <MessageContentPatternDto> > GetPatterns(ulong guildId) { var key = GetKeyForCache(guildId); if (!_memoryCache.TryGetValue(key, out List <MessageContentPatternDto> patterns)) { patterns = await _db .Set <MessageContentPatternEntity>() .Where(x => x.GuildId == guildId) .Select(x => new MessageContentPatternDto(x.Pattern, x.PatternType)) .ToListAsync(); _memoryCache.Set(key, patterns, _patternCacheEntryOptions); } return(patterns); }
/// <inheritdoc /> public async Task <IReadOnlyCollection <PromotionCampaignSummary> > GetPromotionsForUserAsync(ulong guildId, ulong userId) => await ModixContext.Set <PromotionCampaignEntity>().AsNoTracking() .Where(x => x.GuildId == guildId && x.SubjectId == userId && x.Outcome == PromotionCampaignOutcome.Accepted) .AsExpandable() .Select(PromotionCampaignSummary.FromEntityProjection) .ToArrayAsync();
/// <inheritdoc /> public async Task <int> GetTotalMessageCountAsync(ulong guildId, TimeSpan timespan) { var earliestDateTime = DateTimeOffset.UtcNow - timespan; return(await ModixContext.Set <MessageEntity>().AsNoTracking() .Where(x => x.GuildId == guildId && x.Timestamp >= earliestDateTime) .CountAsync()); }
/// <inheritdoc /> public Task <GuildUserSummary> ReadSummaryAsync(ulong userId, ulong guildId) { return(ModixContext.Set <GuildUserEntity>() .AsNoTracking() .Where(x => x.UserId == userId) .Where(x => x.GuildId == guildId) .AsExpandable() .Select(GuildUserSummary.FromEntityProjection) .FirstOrDefaultAsync()); }
/// <inheritdoc /> public async Task UpdateStarboardColumn(ulong messageId, ulong?starboardEntryId) { var entity = await ModixContext.Set <MessageEntity>() .Where(x => x.Id == messageId) .FirstOrDefaultAsync(); entity.StarboardEntryId = starboardEntryId; ModixContext.Set <MessageEntity>().Update(entity); await ModixContext.SaveChangesAsync(); }
/// <inheritdoc /> public async Task DeleteAsync(EmojiSearchCriteria criteria) { if (criteria is null) { throw new ArgumentNullException(nameof(criteria)); } var entities = ModixContext.Set <EmojiEntity>().FilterBy(criteria); ModixContext.RemoveRange(entities); await ModixContext.SaveChangesAsync(); }
/// <inheritdoc /> public async Task DeleteAsync(ulong messageId) { var entity = await ModixContext.Set <MessageEntity>() .Where(x => x.Id == messageId) .FirstOrDefaultAsync(); if (entity is MessageEntity) { ModixContext.Set <MessageEntity>().Remove(entity); await ModixContext.SaveChangesAsync(); } }
/// <inheritdoc /> public async Task CreateAsync(GuildRoleCreationData data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } var entity = data.ToEntity(); await ModixContext.Set <GuildRoleEntity>().AddAsync(entity); await ModixContext.SaveChangesAsync(); }
/// <inheritdoc /> public async Task CreateAsync(GuildChannelCreationData data, CancellationToken cancellationToken = default) { if (data == null) { throw new ArgumentNullException(nameof(data)); } var entity = data.ToEntity(); await ModixContext.Set <GuildChannelEntity>().AddAsync(entity, cancellationToken); await ModixContext.SaveChangesAsync(cancellationToken); }
/// <inheritdoc /> public async Task <PromotionActionSummary> TryUpdateAsync(long commentId, ulong userId, Action <PromotionCommentMutationData> updateAction) { if (updateAction is null) { throw new ArgumentNullException(nameof(updateAction)); } var oldComment = await ModixContext.Set <PromotionCommentEntity>() .Include(x => x.Campaign) .SingleAsync(x => x.Id == commentId); var modifyAction = new PromotionActionEntity { CampaignId = oldComment.CampaignId, Created = DateTimeOffset.Now, CreatedById = userId, GuildId = oldComment.Campaign.GuildId, Type = PromotionActionType.CommentModified, }; await ModixContext.Set <PromotionActionEntity>().AddAsync(modifyAction); await ModixContext.SaveChangesAsync(); var data = PromotionCommentMutationData.FromEntity(oldComment); updateAction(data); var newComment = data.ToEntity(); newComment.CreateActionId = modifyAction.Id; await ModixContext.Set <PromotionCommentEntity>().AddAsync(newComment); await ModixContext.SaveChangesAsync(); modifyAction.OldCommentId = oldComment.Id; modifyAction.NewCommentId = newComment.Id; oldComment.ModifyActionId = modifyAction.Id; newComment.CreateActionId = modifyAction.Id; await ModixContext.SaveChangesAsync(); var actionSummary = await ModixContext.Set <PromotionActionEntity>().AsNoTracking() .Where(x => x.Id == modifyAction.Id) .AsExpandable() .Select(PromotionActionSummary.FromEntityProjection) .FirstAsync(); return(actionSummary); }
/// <inheritdoc /> public async Task <IReadOnlyDictionary <ulong, int> > GetTotalMessageCountByChannelAsync(ulong guildId, TimeSpan timespan) { var earliestDateTime = DateTimeOffset.UtcNow - timespan; var messages = await ModixContext.Set <MessageEntity>() .AsNoTracking() .Where(x => x.GuildId == guildId && x.Timestamp >= earliestDateTime) .Select(x => x.ChannelId) .ToListAsync(); return(messages .GroupBy(channelId => channelId) .ToDictionary(x => x.Key, x => x.Count())); }
/// <inheritdoc /> public async Task <long> CreateAsync(EmojiCreationData data) { if (data is null) { throw new ArgumentNullException(nameof(data)); } var entity = data.ToEntity(); await ModixContext.Set <EmojiEntity>().AddAsync(entity); await ModixContext.SaveChangesAsync(); return(entity.Id); }
/// <inheritdoc /> public async Task <SingleEmojiUsageStatistics> GetEmojiStatsAsync(ulong guildId, EphemeralEmoji emoji, TimeSpan?dateFilter = null) { var query = GetQuery(); var parameters = GetParameters(); var stats = await ModixContext .Set <SingleEmojiStatsDto>() .FromSqlRaw(query, parameters) .AsNoTracking() .FirstOrDefaultAsync(); return(SingleEmojiUsageStatistics.FromDto(stats ?? new SingleEmojiStatsDto())); NpgsqlParameter[] GetParameters() => new[]
/// <inheritdoc /> public async Task <IReadOnlyCollection <EmojiSummary> > SearchSummariesAsync(EmojiSearchCriteria criteria) { if (criteria is null) { throw new ArgumentNullException(nameof(criteria)); } var emoji = await ModixContext.Set <EmojiEntity>().AsNoTracking() .FilterBy(criteria) .AsExpandable() .Select(EmojiSummary.FromEntityProjection) .ToArrayAsync(); return(emoji); }
/// <inheritdoc /> public async Task <int> DeleteAsync(DesignatedChannelMappingSearchCriteria criteria, ulong deletedById) { var entities = await ModixContext.Set <DesignatedChannelMappingEntity>() .Where(x => x.DeleteActionId == null) .FilterBy(criteria) .ToArrayAsync(); foreach (var entity in entities) { DoEntityDelete(entity, deletedById); } await ModixContext.SaveChangesAsync(); return(entities.Length); }