-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from bobbahbrown/tgstation
Version 1.3.6 - Add support for /tg/station
- Loading branch information
Showing
10 changed files
with
273 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |