IW4M-Admin/SharedLibraryCore/Services/PenaltyService.cs
RaidMax 9e74dac5ed fix stat issue with concurrent threads
fix potential lost penalty if server does not response to kick request
make sure that broadcast only shows one custom say name
add unit tests
2020-04-21 17:34:00 -05:00

203 lines
8.0 KiB
C#

using Microsoft.EntityFrameworkCore;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace SharedLibraryCore.Services
{
public class PenaltyService : Interfaces.IEntityService<EFPenalty>
{
public virtual async Task<EFPenalty> Create(EFPenalty newEntity)
{
using (var context = new DatabaseContext())
{
var penalty = new EFPenalty()
{
Active = true,
OffenderId = newEntity.Offender.ClientId,
PunisherId = newEntity.Punisher.ClientId,
LinkId = newEntity.Link.AliasLinkId,
Type = newEntity.Type,
Expires = newEntity.Expires,
Offense = newEntity.Offense,
When = DateTime.UtcNow,
AutomatedOffense = newEntity.AutomatedOffense ?? newEntity.Punisher.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense,
IsEvadedOffense = newEntity.IsEvadedOffense
};
context.Penalties.Add(penalty);
await context.SaveChangesAsync();
}
return newEntity;
}
public Task<EFPenalty> Delete(EFPenalty entity)
{
throw new NotImplementedException();
}
public async Task<IList<EFPenalty>> Find(Func<EFPenalty, bool> expression)
{
throw await Task.FromResult(new Exception());
}
public Task<EFPenalty> Get(int entityID)
{
throw new NotImplementedException();
}
public Task<EFPenalty> GetUnique(long entityProperty)
{
throw new NotImplementedException();
}
public Task<EFPenalty> Update(EFPenalty entity)
{
throw new NotImplementedException();
}
public async Task<IList<PenaltyInfo>> GetRecentPenalties(int count, int offset, EFPenalty.PenaltyType showOnly = EFPenalty.PenaltyType.Any, bool ignoreAutomated = true)
{
using (var context = new DatabaseContext(true))
{
var iqPenalties = context.Penalties
.Where(p => showOnly == EFPenalty.PenaltyType.Any ? p.Type != EFPenalty.PenaltyType.Any : p.Type == showOnly)
.Where(_penalty => ignoreAutomated ? _penalty.PunisherId != 1 : true)
.OrderByDescending(p => p.When)
.Skip(offset)
.Take(count)
.Select(_penalty => new PenaltyInfo()
{
Id = _penalty.PenaltyId,
Offense = _penalty.Offense,
AutomatedOffense = _penalty.AutomatedOffense,
OffenderId = _penalty.OffenderId,
OffenderName = _penalty.Offender.CurrentAlias.Name,
PunisherId = _penalty.PunisherId,
PunisherName = _penalty.Punisher.CurrentAlias.Name,
PunisherLevel = _penalty.Punisher.Level,
PenaltyType = _penalty.Type,
Expires = _penalty.Expires,
TimePunished = _penalty.When,
IsEvade = _penalty.IsEvadedOffense
});
#if DEBUG == true
var querySql = iqPenalties.ToSql();
#endif
return await iqPenalties.ToListAsync();
}
}
/// <summary>
/// retrieves penalty information for meta service
/// </summary>
/// <param name="clientId">database id of the client</param>
/// <param name="count">how many items to retrieve</param>
/// <param name="offset">not used</param>
/// <param name="startAt">retreive penalties older than this</param>
/// <returns></returns>
public async Task<IList<PenaltyInfo>> GetClientPenaltyForMetaAsync(int clientId, int count, int offset, DateTime? startAt)
{
var linkedPenaltyType = Utilities.LinkedPenaltyTypes();
using (var ctx = new DatabaseContext(true))
{
var linkId = await ctx.Clients.AsNoTracking()
.Where(_penalty => _penalty.ClientId == clientId)
.Select(_penalty => _penalty.AliasLinkId)
.FirstOrDefaultAsync();
var iqPenalties = ctx.Penalties.AsNoTracking()
.Where(_penalty => _penalty.OffenderId == clientId || _penalty.PunisherId == clientId || (linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId))
.Where(_penalty => _penalty.When <= startAt)
.OrderByDescending(_penalty => _penalty.When)
.Skip(offset)
.Take(count)
.Select(_penalty => new PenaltyInfo()
{
Id = _penalty.PenaltyId,
Offense = _penalty.Offense,
AutomatedOffense = _penalty.AutomatedOffense,
OffenderId = _penalty.OffenderId,
OffenderName = _penalty.Offender.CurrentAlias.Name,
PunisherId = _penalty.PunisherId,
PunisherName = _penalty.Punisher.CurrentAlias.Name,
PunisherLevel = _penalty.Punisher.Level,
PenaltyType = _penalty.Type,
Expires = _penalty.Expires,
TimePunished = _penalty.When,
IsEvade = _penalty.IsEvadedOffense
});
#if DEBUG == true
var querySql = iqPenalties.ToSql();
#endif
return await iqPenalties.Distinct().ToListAsync();
}
}
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int? ip = null, bool includePunisherName = false)
{
var now = DateTime.UtcNow;
Expression<Func<EFPenalty, bool>> filter = (p) => (new EFPenalty.PenaltyType[]
{
EFPenalty.PenaltyType.TempBan,
EFPenalty.PenaltyType.Ban,
EFPenalty.PenaltyType.Flag
}.Contains(p.Type) &&
p.Active &&
(p.Expires == null || p.Expires > now));
using (var context = new DatabaseContext(true))
{
var iqLinkPenalties = context.Penalties
.Where(p => p.LinkId == linkId)
.Where(filter);
var iqIPPenalties = context.Aliases
.Where(a => a.IPAddress != null && a.IPAddress == ip)
.SelectMany(a => a.Link.ReceivedPenalties)
.Where(filter);
#if DEBUG == true
var penaltiesSql = iqLinkPenalties.ToSql();
var ipPenaltiesSql = iqIPPenalties.ToSql();
#endif
var activePenalties = (await iqLinkPenalties.ToListAsync())
.Union(await iqIPPenalties.ToListAsync())
.Distinct();
// this is a bit more performant in memory (ordering)
return activePenalties.OrderByDescending(p => p.When).ToList();
}
}
public virtual async Task RemoveActivePenalties(int aliasLinkId)
{
using (var context = new DatabaseContext())
{
var now = DateTime.UtcNow;
await context.Penalties
.Where(p => p.LinkId == aliasLinkId)
.Where(p => p.Expires > now || p.Expires == null)
.ForEachAsync(p =>
{
p.Active = false;
p.Expires = now;
});
await context.SaveChangesAsync();
}
}
}
}