added meta property and service for clients

started workign on a new profile page for clients
This commit is contained in:
RaidMax 2018-02-14 13:01:26 -06:00
parent d2ead61061
commit c599d8ef20
14 changed files with 594 additions and 22 deletions

View File

@ -127,6 +127,7 @@
<HintPath>lib\Kayak.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
@ -164,6 +165,9 @@
<Content Include="Webfront\images\minimap_mp_terminal.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Webfront\profile.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Webfront\scripts\wordcloud2.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@ -58,7 +58,11 @@ namespace IW4MAdmin
bool binaryContent = requestedPage.BinaryContent != null;
if (requestedPage.content != null && requestedPage.content.GetType() != typeof(string))
#if !DEBUG
requestedPage.content = Newtonsoft.Json.JsonConvert.SerializeObject(requestedPage.content);
#else
requestedPage.content = Newtonsoft.Json.JsonConvert.SerializeObject(requestedPage.content, Newtonsoft.Json.Formatting.Indented);
#endif
string maxAge = requestedPage.contentType == "application/json" ? "0" : "31536000";
var headers = new HttpResponseHead()

View File

@ -23,6 +23,7 @@ namespace IW4MAdmin
{
return Math.Abs($"{IP}:{Port.ToString()}".GetHashCode());
}
override public async Task<bool> AddPlayer(Player polledPlayer)
{
if (polledPlayer.Ping == 999 || polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) || polledPlayer.ClientNumber < 0)

View File

@ -14,6 +14,8 @@ using System.Threading.Tasks;
using SharedLibrary.Services;
using System.Linq.Expressions;
using SharedLibrary.Database.Models;
using System.Collections.Specialized;
using SharedLibrary.Dtos;
namespace IW4MAdmin
{
@ -41,8 +43,10 @@ namespace IW4MAdmin
SharedLibrary.WebService.PageList.Add(new PubbansJSON());
SharedLibrary.WebService.PageList.Add(new AdminsJSON());
SharedLibrary.WebService.PageList.Add(new Admins());
SharedLibrary.WebService.PageList.Add(new Profile());
SchedulerThread = new Thread(() => {
SchedulerThread = new Thread(() =>
{
ScheduleThreadStart(WebScheduler, WebServer);
})
{
@ -698,9 +702,8 @@ namespace IW4MAdmin
return "/pages";
}
public async Task<HttpResponse> GetPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
public async Task<HttpResponse> GetPage(NameValueCollection querySet, IDictionary<string, string> headers)
{
var pages = SharedLibrary.WebService.PageList.Select(p => new
{
pagePath = p.GetPath(),
@ -745,7 +748,7 @@ namespace IW4MAdmin
return "GetPlayer";
}
public async Task<HttpResponse> GetPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
public async Task<HttpResponse> GetPage(NameValueCollection querySet, IDictionary<string, string> headers)
{
List<PlayerInfo> pInfo = new List<PlayerInfo>();
IList<EFClient> matchedPlayers = new List<EFClient>();
@ -788,12 +791,22 @@ namespace IW4MAdmin
recent = true;
}
bool isProfile = querySet["profile"] != null;
if (matchedPlayers != null && matchedPlayers.Count > 0)
{
foreach (var pp in matchedPlayers)
{
if (pp == null) continue;
List<ProfileMeta> meta = new List<ProfileMeta>();
if (isProfile)
{
meta.AddRange(await ApplicationManager.GetInstance().GetPenaltyService().ReadGetClientPenaltiesAsync(pp.ClientId));
meta.AddRange(await ApplicationManager.GetInstance().GetPenaltyService().ReadGetClientPenaltiesAsync(pp.ClientId, false));
meta.AddRange(await MetaService.GetMeta(pp.ClientId));
}
PlayerInfo eachPlayer = new PlayerInfo()
{
playerIP = pp.IPAddressString,
@ -801,16 +814,19 @@ namespace IW4MAdmin
playerLevel = pp.Level.ToString(),
playerName = pp.Name,
playernpID = pp.NetworkId.ToString(),
forumID = -1,
authed = authed,
showV2Features = false,
playerAliases = new List<string>(),
playerIPs = new List<string>()
playerIPs = new List<string>(),
Meta = meta.OrderByDescending(m => m.When).ToList(),
FirstSeen = Utilities.GetTimePassed(pp.FirstConnection, false),
TimePlayed = Math.Round(pp.TotalConnectionTime / 3600.0, 1).ToString("#,##0"),
LastSeen = Utilities.GetTimePassed(pp.LastConnection, false)
};
if (!recent)
{
eachPlayer.playerAliases = pp.AliasLink.Children
.Where(a => a.Name != eachPlayer.playerName)
.OrderBy(a => a.Name)
.Select(a => a.Name)
.Distinct()
@ -827,12 +843,11 @@ namespace IW4MAdmin
// eachPlayer.playerIPs = eachPlayer.playerIPs.Distinct().ToList();
eachPlayer.playerConnections = pp.Connections;
eachPlayer.lastSeen = Utilities.GetTimePassed(pp.LastConnection);
pInfo.Add(eachPlayer);
}
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(pInfo);
resp.content = pInfo;
return resp;
}
@ -846,6 +861,21 @@ namespace IW4MAdmin
}
}
class Profile : HTMLPage
{
public override string GetPath() => "/profile";
public override string GetContent(NameValueCollection querySet, IDictionary<string, string> headers)
{
IFile admins = new IFile("webfront\\profile.html");
string content = admins.GetText();
admins.Close();
return content;
}
public override string GetName() => "Client Profile";
}
[Serializable]
struct ServerInfo
{
@ -876,13 +906,14 @@ namespace IW4MAdmin
public string playerLevel;
public string playerIP;
public string playernpID;
public Int64 forumID;
public List<string> playerAliases;
public List<string> playerIPs;
public int playerConnections;
public string lastSeen;
public bool showV2Features;
public string LastSeen;
public string FirstSeen;
public string TimePlayed;
public bool authed;
public List<ProfileMeta> Meta;
}
[Serializable]

322
Admin/Webfront/profile.html Normal file
View File

@ -0,0 +1,322 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>IW4MAdmin by RaidMax</title>
<meta name="description" content="Administration tool for IW4M servers. IW4MAdmin Created by RaidMax">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="http://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<style>
#profile_avatar {
height: 200px;
width: 200px;
}
.profile-shortcode {
font-size: 150pt;
line-height: 175px;
color: white;
}
.text-highlight {
color: rgb(0, 122, 204) !important;
}
#profile_info {
color: white;
}
#profile_name {
font-size: 4em;
line-height: 1em;
}
#profile_info > .text-muted {
font-size: 1.5em;
line-height: 1.25em;
}
#content,
.container-fluid, #profile_aliases {
background-color: rgb(34, 34, 34);
}
#profile_level > span.level {
color: rgba(236, 130, 222, 0.69);
font-weight: bold;
font-size: 1.5em;
line-height: 1.4em;
}
#profile_wrapper {
border-bottom: 2px rgb(0, 122, 204) solid;
}
.level-color-user, .level-color-guest {
color: rgba(255, 255, 255, 0.85);
}
.level-bgcolor-user, .level-bgcolor-guest {
background-color: rgba(255, 255, 255, 0.85);
}
.level-color-trusted, .level-color-user {
color: rgba(116,147,99,1);
}
.level-bgcolor-trusted, .level-bgcolor-user {
background-color: rgba(116,147,99,1);
}
.level-color-flagged {
color: rgba(253, 139, 22, 0.85);
}
.level-bgcolor-flagged {
background-color: rgba(253, 139, 22, 0.85);
}
.level-color-banned {
color: rgba(255, 69, 69, 0.85);
}
.level-bgcolor-banned {
background-color: rgba(255, 69, 69, 0.85);
}
.level-color-moderator {
color: rgba(235, 211, 101, 0.75);
}
.level-bgcolor-moderator {
background-color: rgba(235, 211, 101, 0.75);
}
.level-color-administrator {
color: rgba(236, 130, 222, 0.69);
}
.level-bgcolor-administrator {
background-color: rgba(236, 130, 222, 0.69);
}
.level-color-senioradmin {
color: rgba(50, 177, 185, 0.85);
}
.level-bgcolor-senioradmin {
background-color: rgba(50, 177, 185, 0.85);
}
.level-color-owner {
color: rgb(0, 122, 204);
}
.level-bgcolor-owner {
background-color: rgb(0, 122, 204);
}
.profile-meta-title {
color: white;
}
.profile-meta-entry {
font-size: 1.5em;
}
.penalties-color-kick {
color: rgba(116, 147, 99, 1);
}
.penalties-color-report {
color: rgba(116, 147, 99, 1);
}
.penalties-color-warning {
color: rgba(235, 211, 101, 0.75);
}
.penalties-color-tempban {
color: rgba(253, 139, 22, 0.85);
}
.penalties-color-flag {
color: rgba(253, 139, 22, 0.85);
}
.penalties-color-ban {
color: rgba(255, 69, 69, 0.85);
}
#profile_aliases_btn {
font-size: 0.5em;
color: rgb(0, 122, 204);
cursor: pointer;
}
#profile_aliases_btn:hover {
color: white;
cursor: pointer;
}
#profile_aliases {
position: relative;
display: none;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function parseGet(val) {
var result = "undefined",
tmp = [];
location.search
.substr(1)
.split("&")
.forEach(function (item) {
tmp = item.split("=");
if (tmp[0] === val) result = decodeURIComponent(tmp[1]);
});
return result;
}
function penaltyToName(penaltyName) {
switch (penaltyName) {
case "Flag":
return "Flagged"
case "Warning":
return "Warned";
case "Report":
return "Reported";
case "Ban":
return "Banned";
case "Kick":
return "Kicked";
case "TempBan":
return "Temp Banned";
}
}
let playerInfo = undefined;
function loadMeta(meta) {
let eventString = '';
// it's a penalty
if (meta.Class.includes("Penalty")) {
if (meta.Value.PunisherId != playerInfo.playerID) {
eventString = `<div><span class="penalties-color-${meta.Value.Type.toLowerCase()}">${penaltyToName(meta.Value.Type)}</span> by <span class="text-highlight"> <a href="/profile?id=${meta.Value.PunisherId}">${meta.Value.PunisherName}</a></span > for <span style="color: white; ">${meta.Value.Offense}</span> ${meta.WhenString} ago </div>`;
}
else {
eventString = `<div><span class="penalties-color-${meta.Value.Type.toLowerCase()}">${penaltyToName(meta.Value.Type)} </span> <span class="text-highlight"><a href="/profile?id=${meta.Value.OffenderId}"> ${meta.Value.OffenderName}</a></span > for <span style="color: white; ">${meta.Value.Offense}</span> ${meta.WhenString} ago </div>`;
}
}
// it's a message
else if (meta.Key.includes("Event")) {
eventString = `<div><span style="color:white;">></span><span class="text-muted"> ${meta.Value}</span></div>`;
}
$('#profile_events').append(eventString);
}
let count = 1;
$(window).scroll(function () {
if ($(window).scrollTop() == $(document).height() - $(window).height() || $(document).height() == $(window).height()) {
while (count % 40 != 0 && count < playerInfo.Meta.length) {
loadMeta(playerInfo.Meta[count - 1]);
count++;
}
count++;
}
});
$(document).ready(function () {
$("#profile_aliases_btn").click(function (e) {
if ($("#profile_aliases").text() != '') {
$("#profile_aliases").slideToggle(50);
}
});
jQuery.getJSON("/getplayer?profile=1&id=" + parseGet("id"), function (result) {
playerInfo = result[0];
let firstLetter = playerInfo.playerName.match(/[a-zA-Z]/);
if (firstLetter == undefined)
firstLetter = '?';
else
firstLetter = firstLetter.pop().toUpperCase();
$('#profile_avatar').addClass('level-bgcolor-' + playerInfo.playerLevel.toLowerCase());
$('#profile_avatar > .profile-shortcode').text(firstLetter);
$('#profile_name > .client-name').prepend(playerInfo.playerName);
$.each(playerInfo.playerAliases, function (index, alias) {
$("#profile_aliases").append(alias + "<br/>");
});
$('#profile_time_played > .text-highlight').text(playerInfo.TimePlayed);
$('#profile_level > span').text(playerInfo.playerLevel).addClass('level-color-' + playerInfo.playerLevel.toLowerCase());
$('#profile_first_seen > .text-highlight').text(playerInfo.FirstSeen);
$('#profile_last_seen > .text-highlight').text(playerInfo.LastSeen);
$.each(playerInfo.Meta, function (index, meta) {
if (!meta.Key.includes("Event")) {
let metaString = `<div class="profile-meta-entry"><span class="profile-meta-value text-highlight">${meta.Value}</span><span class="profile-meta-title"> ${meta.Key}</span></div>`;
$("#profile_meta").append(metaString);
}
});
$.each(playerInfo.Meta, function (index, meta) {
loadMeta(meta);
if (count % 40 == 0) {
count++;
return false;
}
count++
});
});
});
</script>
</head>
<body>
<div class="container-fluid">
<div id="content" class="p-4">
<div id="profile_wrapper" class="row d-flex d-sm-inline-flex justify-content-center justify-content-left pb-4">
<div class="mr-auto ml-auto ml-sm-0 mr-sm-0">
<div id="profile_avatar" class="text-center">
<span class="profile-shortcode">_</span>
</div>
</div>
<div id="profile_info" class="text-center text-sm-left pr-4 pl-4">
<div id="profile_name">
<span class="client-name"><span id="profile_aliases_btn" class="oi oi-caret-bottom pl-2"></span></span>
<div id="profile_aliases" class="pr-4 pb-2 pt-2 text-muted"></div>
</div>
<div id="profile_level" class="text-muted">
<span>_</span>
</div>
<div id="profile_time_played" class="text-muted">
Played <span class="text-highlight">_</span> hours
</div>
<div id="profile_first_seen" class="text-muted">
First seen <span class="text-highlight">_</span> ago
</div>
<div id="profile_last_seen" class="text-muted">
Last seen <span class="text-highlight">_</span> ago
</div>
</div>
<div id="profile_meta" class="text-center text-sm-right pt-2">
</div>
</div>
<div class="row d-md-flex pt-4">
<div id="profile_events" class="text-muted text-left ml-sm-0">
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

View File

@ -50,7 +50,7 @@
var p = "";
p +=
"<div class=\"playerInfo table alternate_" + i % 2 + "\"> \
<div class=\"tableCell\"><a href=\"/players?id=" + player['playerID'] + "\">" + escapeHtml(player['playerName']) + "</a></div> \
<div class=\"tableCell\"><a href=\"/profile?id=" + player['playerID'] + "\">" + escapeHtml(player['playerName']) + "</a></div> \
<div class=\"tableCell\">" + formatHidden(player['playerAliases'], player.authed) + "</div> \
<div class=\"tableCell\">" + formatHidden(player['playerIPs'], player.authed) + "</div> \
<div class=\"tableCell\">" + getColorForLevel(player['playerLevel'], player['playerLevel']) + "</div> \
@ -61,7 +61,7 @@
"<div class=\"tableCell\" style='width: 2em;'><a href=\"/chat?clientid=" + player.playerID + "\"><i class=\"fa fa-comments\" aria-hidden=\"true\"></i></a></div>"
p +=
"<div class=\"tableCell alignRight\">" + checkJustNow(player['lastSeen']) + "</div> \
"<div class=\"tableCell alignRight\">" + checkJustNow(player['LastSeen']) + "</div> \
</div>";
$("#playersTable").append(p);

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using SharedLibrary;
using SharedLibrary.Dtos;
using SharedLibrary.Helpers;
using SharedLibrary.Interfaces;
using SharedLibrary.Services;
@ -81,7 +82,65 @@ namespace StatsPlugin
public Task OnLoadAsync(IManager manager)
{
// todo: is this fast?
// meta data info
async Task<List<ProfileMeta>> getStats(int clientId)
{
var statsSvc = new GenericRepository<EFClientStatistics>();
var clientStats = await statsSvc.FindAsync(c => c.ClientId == clientId);
int kills = clientStats.Sum(c => c.Kills);
int deaths = clientStats.Sum(c => c.Deaths);
double kdr = Math.Round(clientStats.Sum(c => c.KDR) / clientStats.Count, 2);
double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Count, 2);
return new List<ProfileMeta>()
{
new ProfileMeta()
{
Key = "Kills",
Value = kills
},
new ProfileMeta()
{
Key = "Deaths",
Value = deaths
},
new ProfileMeta()
{
Key = "KDR",
Value = kdr
},
new ProfileMeta()
{
Key = "Skill",
Value = skill
}
};
}
async Task<List<ProfileMeta>> getMessages(int clientId)
{
var messageSvc = new GenericRepository<EFClientMessage>();
var messages = await messageSvc.FindAsync(m => m.ClientId == clientId);
var messageMeta = messages.Select(m => new ProfileMeta()
{
Key = "EventMessage",
Value = m.Message,
When = m.TimeSent
}).ToList();
messageMeta.Add(new ProfileMeta()
{
Key = "Messages",
Value = messages.Count
});
return messageMeta;
}
MetaService.AddMeta(getStats);
MetaService.AddMeta(getMessages);
// todo: is this fast? make async?
string totalKills()
{
var serverStats = new GenericRepository<EFServerStatistics>();

View File

@ -0,0 +1,18 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Dtos
{
public class ProfileMeta
{
public DateTime When { get; set; }
public string WhenString => Utilities.GetTimePassed(When, false);
public string Key { get; set; }
public dynamic Value { get; set; }
public virtual string Class => Value.GetType().ToString();
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Dtos
{
public class ProfilePenalty
{
public string OffenderName { get; set; }
public int OffenderId { get; set; }
public string PunisherName { get; set; }
public int PunisherId { get; set; }
public string Offense { get; set; }
public string Type { get; set; }
}
}

View File

@ -75,6 +75,8 @@ namespace SharedLibrary.Objects
public Server CurrentServer { get; set; }
[NotMapped]
public int Score { get; set; }
[NotMapped]
public IList<Dtos.ProfileMeta> Meta { get; set; }
private int _ipaddress;
public override int IPAddress

View File

@ -0,0 +1,27 @@
using SharedLibrary.Dtos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Services
{
public class MetaService
{
private static List<Func<int, Task<List<ProfileMeta>>>> MetaActions = new List<Func<int, Task<List<ProfileMeta>>>>();
public static void AddMeta(Func<int, Task<List<ProfileMeta>>> metaAction)
{
MetaActions.Add(metaAction);
}
public static async Task<List<ProfileMeta>> GetMeta(int clientId)
{
var meta = new List<ProfileMeta>();
foreach (var action in MetaActions)
meta.AddRange(await action(clientId));
return meta;
}
}
}

View File

@ -8,6 +8,7 @@ using System.Data.Entity;
using SharedLibrary.Database;
using SharedLibrary.Database.Models;
using System.Linq.Expressions;
using SharedLibrary.Dtos;
namespace SharedLibrary.Services
{
@ -56,6 +57,8 @@ namespace SharedLibrary.Services
public async Task<IList<EFPenalty>> Find(Func<EFPenalty, bool> expression)
{
return await Task.FromResult(new List<EFPenalty>());
// fixme: this is so slow!
return await Task.Run(() =>
{
using (var context = new DatabaseContext())
@ -109,6 +112,86 @@ namespace SharedLibrary.Services
.ToListAsync();
}
/// <summary>
/// Get a read-only copy of client penalties
/// </summary>
/// <param name="clientI"></param>
/// <param name="victim">Retreive penalties for clients receiving penalties, other wise given</param>
/// <returns></returns>
public async Task<List<ProfileMeta>> ReadGetClientPenaltiesAsync(int clientId, bool victim = true)
{
using (var context = new DatabaseContext())
{
context.Configuration.LazyLoadingEnabled = false;
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
if (victim)
{
var iqPenalties = from penalty in context.Penalties.AsNoTracking()
where penalty.OffenderId == clientId
join victimClient in context.Clients.AsNoTracking()
on penalty.OffenderId equals victimClient.ClientId
join victimAlias in context.Aliases
on victimClient.CurrentAliasId equals victimAlias.AliasId
join punisherClient in context.Clients
on penalty.PunisherId equals punisherClient.ClientId
join punisherAlias in context.Aliases
on punisherClient.CurrentAliasId equals punisherAlias.AliasId
//orderby penalty.When descending
select new ProfileMeta()
{
Key = "Event.Penalty",
Value = new ProfilePenalty
{
OffenderName = victimAlias.Name,
OffenderId = victimClient.ClientId,
PunisherName = punisherAlias.Name,
PunisherId = penalty.PunisherId,
Offense = penalty.Offense,
Type = penalty.Type.ToString()
},
When = penalty.When
};
// fixme: is this good and fast?
return await iqPenalties.ToListAsync();
}
else
{
var iqPenalties = from penalty in context.Penalties.AsNoTracking()
where penalty.PunisherId == clientId
join victimClient in context.Clients.AsNoTracking()
on penalty.OffenderId equals victimClient.ClientId
join victimAlias in context.Aliases
on victimClient.CurrentAliasId equals victimAlias.AliasId
join punisherClient in context.Clients
on penalty.PunisherId equals punisherClient.ClientId
join punisherAlias in context.Aliases
on punisherClient.CurrentAliasId equals punisherAlias.AliasId
//orderby penalty.When descending
select new ProfileMeta()
{
Key = "Event.Penalty",
Value = new ProfilePenalty
{
OffenderName = victimAlias.Name,
OffenderId = victimClient.ClientId,
PunisherName = punisherAlias.Name,
PunisherId = penalty.PunisherId,
Offense = penalty.Offense,
Type = penalty.Type.ToString()
},
When = penalty.When
};
// fixme: is this good and fast?
return await iqPenalties.ToListAsync();
}
}
}
public async Task RemoveActivePenalties(int aliasLinkId)
{
using (var context = new DatabaseContext())

View File

@ -103,6 +103,8 @@
<Compile Include="Database\Models\EFClient.cs" />
<Compile Include="Database\Models\EFPenalty.cs" />
<Compile Include="Database\Models\SharedEntity.cs" />
<Compile Include="Dtos\ProfileMeta.cs" />
<Compile Include="Dtos\ProfilePenalty.cs" />
<Compile Include="Exceptions\DatabaseException.cs" />
<Compile Include="Helpers\AsyncStatus.cs" />
<Compile Include="Commands\NativeCommands.cs" />
@ -139,6 +141,7 @@
<Compile Include="Services\AliasService.cs" />
<Compile Include="Services\ClientService.cs" />
<Compile Include="Services\GenericRepository.cs" />
<Compile Include="Services\MetaService.cs" />
<Compile Include="Services\PenaltyService.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="WebService.cs" />