Skip to content

Commit

Permalink
Merge pull request #36 from bobbahbrown/tgstation
Browse files Browse the repository at this point in the history
Version 1.3.6 - Add support for /tg/station
  • Loading branch information
bobbah authored Apr 24, 2021
2 parents 398fd4e + 9f8f29d commit ba00a75
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 10 deletions.
6 changes: 3 additions & 3 deletions CentCom.API/CentCom.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>1.3.5</Version>
<AssemblyVersion>1.3.5.0</AssemblyVersion>
<FileVersion>1.3.5.0</FileVersion>
<Version>1.3.6</Version>
<AssemblyVersion>1.3.6.0</AssemblyVersion>
<FileVersion>1.3.6.0</FileVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
2 changes: 1 addition & 1 deletion CentCom.API/Views/Viewer/viewbans.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
{
<div class="ban-container @(b.Active ? "active-ban" : (b.UnbannedBy != null ? "lifted-ban" : "expired-ban"))">
<div class="ban-title">
<span>@(b.Type) Ban / @b.SourceName / @(b.Active ? "Active" : (b.UnbannedBy != null ? "Unbanned" : "Expired"))</span>
<span>@(b.Type) Ban | @b.SourceName | @(b.Active ? "Active" : (b.UnbannedBy != null ? "Unbanned" : "Expired"))</span>
</div>
<div class="ban-contents">
<div class="ban-reason">@b.Reason</div>
Expand Down
6 changes: 3 additions & 3 deletions CentCom.Common/CentCom.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>1.3.5</Version>
<AssemblyVersion>1.3.5.0</AssemblyVersion>
<FileVersion>1.3.5.0</FileVersion>
<Version>1.3.6</Version>
<AssemblyVersion>1.3.6.0</AssemblyVersion>
<FileVersion>1.3.6.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
51 changes: 51 additions & 0 deletions CentCom.Server/BanSources/TgBanParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using CentCom.Common.Data;
using CentCom.Common.Models;
using CentCom.Server.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace CentCom.Server.BanSources
{
public class TgBanParser : BanParser
{
public override Dictionary<string, BanSource> Sources => new Dictionary<string, BanSource>()
{
{ "tgstation", new BanSource()
{
Display = "/tg/station",
Name = "tgstation",
RoleplayLevel = RoleplayLevel.Low
} }
};
public override bool SourceSupportsBanIDs => true;
private readonly TgBanService _banService;

public TgBanParser(DatabaseContext dbContext, TgBanService banService, ILogger<TgBanParser> logger) : base(dbContext, logger)
{
_banService = banService;
}

public override async Task<IEnumerable<Ban>> FetchAllBansAsync()
{
_logger.LogInformation("Fetching all bans for /tg/station...");
return await _banService.GetBansBatchedAsync();
}

public override async Task<IEnumerable<Ban>> FetchNewBansAsync()
{
_logger.LogInformation("Fetching new bans for /tg/station...");
var recent = await _dbContext.Bans
.Where(x => Sources.Keys.Contains(x.SourceNavigation.Name))
.OrderByDescending(x => x.BannedOn)
.Take(5)
.Include(x => x.JobBans)
.Include(x => x.SourceNavigation)
.ToListAsync();
return await _banService.GetBansBatchedAsync(searchFor: recent.Select(x => x.Id));
}
}
}
7 changes: 4 additions & 3 deletions CentCom.Server/CentCom.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>1.3.5.0</AssemblyVersion>
<FileVersion>1.3.5.0</FileVersion>
<Version>1.3.5</Version>
<AssemblyVersion>1.3.6.0</AssemblyVersion>
<FileVersion>1.3.6.0</FileVersion>
<Version>1.3.6</Version>
</PropertyGroup>

<ItemGroup>
Expand All @@ -28,6 +28,7 @@
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.1" />
<PackageReference Include="Quartz" Version="3.2.3" />
<PackageReference Include="RestSharp" Version="106.11.7" />
<PackageReference Include="RestSharp.Serializers.SystemTextJson" Version="106.11.7" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
Expand Down
10 changes: 10 additions & 0 deletions CentCom.Server/External/Raw/IRawBan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using CentCom.Common.Models;

namespace CentCom.Server.External.Raw
{
interface IRawBan
{
BanType GetBanType();
Ban AsBan(BanSource source);
}
}
95 changes: 95 additions & 0 deletions CentCom.Server/External/Raw/TgRawBan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using CentCom.Common.Extensions;
using CentCom.Common.Models;
using System;
using System.Globalization;
using System.Text.Json.Serialization;

namespace CentCom.Server.External.Raw
{
public class TgRawBan : IRawBan
{
[JsonPropertyName("id")]
public int Id { get; set; }
private string _expirationTime;
[JsonPropertyName("expiration_time")]
public string ExpirationTimeRaw
{
get => _expirationTime; set
{
_expirationTime = value;
ExpirationTime = ParseTgDateTime(value);
}
}
public DateTime? ExpirationTime { get; private set; }
[JsonPropertyName("role")]
public string Role { get; set; }
[JsonPropertyName("ckey")]
public string CKey { get; set; }
[JsonPropertyName("a_ckey")]
public string AdminCKey { get; set; }
[JsonPropertyName("reason")]
public string Reason { get; set; }
private string _bannedAt;
[JsonPropertyName("bantime")]
public string BannedAtRaw
{
get => _bannedAt; set
{
_bannedAt = value;
BannedAt = ParseTgDateTime(value).Value;
}
}
public DateTime BannedAt { get; private set; }
private string _unbannedAt;
[JsonPropertyName("unbanned_datetime")]
public string UnbannedAtRaw
{
get => _unbannedAt; set
{
_unbannedAt = value;
UnbannedAt = ParseTgDateTime(value);
}
}
public DateTime? UnbannedAt { get; private set; }
[JsonPropertyName("unbanned_Ckey")]
public string UnbannedBy { get; set; }

public BanType GetBanType() => Role.ToLower() == "server" ? BanType.Server : BanType.Job;

public Ban AsBan(BanSource source)
{
var toReturn = new Ban()
{
BanID = Id.ToString(),
BannedBy = AdminCKey,
BannedOn = BannedAt,
BanType = GetBanType(),
CKey = CKey,
UnbannedBy = UnbannedBy,
Reason = Reason,
Expires = ExpirationTime,
SourceNavigation = source
};

if (toReturn.BanType == BanType.Job)
{
toReturn.AddJob(Role);
}

return toReturn;
}

private static DateTime? ParseTgDateTime(string value)
{
if (DateTime.TryParse(value,
CultureInfo.InvariantCulture.DateTimeFormat,
DateTimeStyles.AllowWhiteSpaces,
out var expiration))
{
return DateTime.SpecifyKind(expiration, DateTimeKind.Utc);
}

return null;
}
}
}
14 changes: 14 additions & 0 deletions CentCom.Server/External/TgApiResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using CentCom.Server.External.Raw;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace CentCom.Server.External
{
public class TgApiResponse
{
[JsonPropertyName("beforeid")]
public int BeforeId { get; set; }
[JsonPropertyName("bans")]
public IEnumerable<TgRawBan> Bans { get; set; }
}
}
1 change: 1 addition & 0 deletions CentCom.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public static void RegisterServices()
services.AddSingleton<YogBanService>();
services.AddSingleton<FulpBanService>();
services.AddSingleton<TGMCBanService>();
services.AddSingleton<TgBanService>();

// Add ban parsers
var parsers = AppDomain.CurrentDomain.GetAssemblies().Aggregate(new List<Type>(), (curr, next) =>
Expand Down
91 changes: 91 additions & 0 deletions CentCom.Server/Services/TgBanService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using CentCom.Common.Extensions;
using CentCom.Common.Models;
using CentCom.Server.Exceptions;
using CentCom.Server.External;
using CentCom.Server.External.Raw;
using Microsoft.Extensions.Logging;
using RestSharp;
using RestSharp.Serializers.SystemTextJson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace CentCom.Server.Services
{
public class TgBanService
{
private readonly IRestClient _client;
private readonly ILogger _logger;
private const string BASE_URL = "https://tgstation13.org/";
private readonly static BanSource _banSource = new BanSource { Name = "tgstation" };

public TgBanService(ILogger<TgBanService> logger)
{
_logger = logger;
_client = new RestClient(BASE_URL);
_client.UseSystemTextJson(new System.Text.Json.JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
Converters = { new JsonStringEnumConverter() }
});
}

private async Task<List<TgRawBan>> GetBansAsync(int? startingId = null)
{
var request = new RestRequest($"tgdb/publicbans.php", Method.GET, DataFormat.Json)
.AddQueryParameter("format", "json");
if (startingId.HasValue)
request.AddQueryParameter("beforeid", startingId.ToString());
var response = await _client.ExecuteAsync<TgApiResponse>(request);

if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
var errorMessage = $"tgdb returned a non-200 HTTP response code: { response.StatusCode}, aborting parse.";
_logger.LogError(errorMessage);
throw new BanSourceUnavailableException(errorMessage);
}

return response.Data.Bans.ToList();
}

public async Task<IEnumerable<Ban>> GetBansBatchedAsync(int? startingId = null, IEnumerable<int> searchFor = null)
{
// Get bans, must use a sequential approach here due to the last ban
// ID only being available once we finish a page
var dirtyBans = new List<TgRawBan>();
int? lastRequested = startingId;
List<TgRawBan> lastResponse = null;
do
{
lastResponse = await GetBansAsync(lastRequested);
if (!lastResponse.Any())
break;

// If the last ban on the page is a job ban, get the next page to ensure we have the full ban
if (lastResponse[^1].GetBanType() == BanType.Job)
{
var nextPage = await GetBansAsync(lastResponse[^1].Id);
lastResponse.AddRange(nextPage.Where(x => x.CKey == lastResponse[^1].CKey && x.BannedAt == lastResponse[^1].BannedAt));
}

lastRequested = lastResponse.Min(x => x.Id);
dirtyBans.AddRange(lastResponse);
}
while (lastResponse.Any() && (searchFor == null || lastResponse.Any(x => searchFor.Contains(x.Id))));

// Flatten any jobbans
var intermediateBans = dirtyBans.Select(x => x.AsBan(_banSource));
var cleanBans = intermediateBans.Where(x => x.BanType == BanType.Server).ToList();
foreach (var group in intermediateBans.Where(x => x.BanType == BanType.Job).GroupBy(x => new { x.CKey, x.BannedOn }))
{
var firstBan = group.OrderBy(x => x.BanID).First();
firstBan.AddJobRange(group.SelectMany(x => x.JobBans).Select(x => x.Job));
cleanBans.Add(firstBan);
}

return cleanBans;
}
}
}

0 comments on commit ba00a75

Please sign in to comment.