Skip to content

Commit

Permalink
Add Support for /tg/station (RENEWED) (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
bobbah authored Jan 31, 2025
1 parent e86f3e7 commit 4483407
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 125 deletions.
20 changes: 19 additions & 1 deletion CentCom.Server/BanSources/TgBanParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ public override async Task<IEnumerable<Ban>> FetchNewBansAsync()
.Include(x => x.JobBans)
.Include(x => x.SourceNavigation)
.ToListAsync();
return await _banService.GetBansBatchedAsync(searchFor: recent.Select(x => x.Id));

var foundBans = new List<Ban>();
var page = 1;
while (true)
{
var bans = await _banService.GetBansAsync(page);
if (bans.Count == 0)
break;

foundBans.AddRange(bans.Select(x => x.AsBan(Sources["tgstation"])));

// Check for existing bans
if (foundBans.Any(x => recent.Any(y => y.BanID == x.BanID)))
break;

page++;
}

return foundBans.DistinctBy(x => x.BanID);
}
}
164 changes: 85 additions & 79 deletions CentCom.Server/External/Raw/TgRawBan.cs
Original file line number Diff line number Diff line change
@@ -1,106 +1,112 @@
using System;
using System.Globalization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using CentCom.Common.Extensions;
using CentCom.Common.Models;

namespace CentCom.Server.External.Raw;

public class TgRawBan : IRawBan
public class TgUser
{
private string _bannedAt;
private string _expirationTime;
private string _unbannedAt;

[JsonPropertyName("id")]
public int Id { get; set; }

[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("userIdentifier")]
public string UserIdentifier { get; set; }
}

[JsonPropertyName("a_ckey")]
public string AdminCKey { get; set; }
public class TgServer
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("identifier")]
public string Identifier { get; set; }

[JsonPropertyName("port")]
public int Port { get; set; }

[JsonPropertyName("address")]
public string Address { get; set; }

[JsonPropertyName("url")]
public string Url { get; set; }

[JsonPropertyName("publicLogs")]
public string PublicLogsUrl { get; set; }

[JsonPropertyName("rawLogs")]
public string RawLogsUrl { get; set; }

[JsonPropertyName("round")]
public int Round { get; set; }
}

public class TgRawBan : IRawBan
{
[JsonPropertyName("id")]
public int Id { get; set; }

[JsonPropertyName("admin")]
public TgUser Admin { get; set; }

[JsonPropertyName("target")]
public TgUser Target { get; set; }

[JsonPropertyName("unbanner")]
public TgUser? Unbanner { get; set; }

Check warning on line 58 in CentCom.Server/External/Raw/TgRawBan.cs

View workflow job for this annotation

GitHub Actions / ubuntu

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

[JsonPropertyName("roles")]
public List<string> Roles { get; set; }

[JsonPropertyName("bantime")]
public DateTimeOffset BanTime { get; set; }

[JsonPropertyName("unbannedTime")]
public DateTimeOffset? UnbannedTime { get; set; }

[JsonPropertyName("round")]
public int Round { get; set; }

[JsonPropertyName("status")]
public string Status { get; set; }

[JsonPropertyName("reason")]
public string Reason { get; set; }

[JsonPropertyName("bantime")]
public string BannedAtRaw
{
get => _bannedAt; set
{
_bannedAt = value;
BannedAt = ParseTgDateTime(value).Value;
}
}

public DateTime BannedAt { get; private set; }

[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;

[JsonPropertyName("server")]
public TgServer Server { get; set; }

[JsonPropertyName("expiration")]
public DateTimeOffset? Expiration { get; set; }

[JsonPropertyName("banIds")]
public List<int> BanIds { get; set; }

public BanType GetBanType() => Roles.Count == 1 && Roles[0] == "Server" ? BanType.Server : BanType.Job;

public Ban AsBan(BanSource source)
{
var toReturn = new Ban
{
BanID = Id.ToString(),
BannedBy = AdminCKey,
BannedOn = BannedAt,
SourceNavigation = source,
BanType = GetBanType(),
CKey = CKey,
UnbannedBy = UnbannedBy,
CKey = Target.CKey,
BannedOn = BanTime.UtcDateTime,
BannedBy = Admin.CKey,
Reason = Reason,
Expires = UnbannedAt ?? ExpirationTime,
SourceNavigation = source
Expires = Expiration?.UtcDateTime,
UnbannedBy = Unbanner?.CKey,
BanID = string.Join(";", BanIds),
JobBans = null,
BanAttributes = (BanAttribute)0
};

// Add job bans if relevant
if (toReturn.BanType == BanType.Job)
{
toReturn.AddJob(Role);
}
toReturn.AddJobRange(Roles);

return toReturn;
}

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

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

namespace CentCom.Server.External;

public class TgApiResponse
public class TgApiPagination
{
[JsonPropertyName("beforeid")]
public int BeforeId { get; set; }
[JsonPropertyName("items")]
public int Items { get; set; }

[JsonPropertyName("page")]
public int Page { get; set; }

[JsonPropertyName("per_page")]
public int PerPage { get; set; }
}

[JsonPropertyName("bans")]
public IEnumerable<TgRawBan> Bans { get; set; }
public class TgApiResponse
{
[JsonPropertyName("data")]
public IEnumerable<TgRawBan> Data { get; set; }

[JsonPropertyName("pagination")]
public TgApiPagination Pagination { get; set; }
}
54 changes: 15 additions & 39 deletions CentCom.Server/Services/TgBanService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using CentCom.Common.Extensions;
using CentCom.Common.Models;
using CentCom.Server.External;
using CentCom.Server.External.Raw;
Expand All @@ -28,57 +27,34 @@ public TgBanService(ILogger<TgBanService> logger) : base(logger)
}));
}

protected override string BaseUrl => "https://tgstation13.org/";
protected override string BaseUrl => "https://statbus.space/";

private async Task<List<TgRawBan>> GetBansAsync(int? startingId = null)
public async Task<List<TgRawBan>> GetBansAsync(int? page = null)
{
var request = new RestRequest("tgdb/publicbans.php")
.AddQueryParameter("format", "json");
if (startingId.HasValue)
request.AddQueryParameter("beforeid", startingId.ToString());
var request = new RestRequest($"bans/public/v1/{page}")
.AddQueryParameter("json", "true");
var response = await Client.ExecuteAsync<TgApiResponse>(request);

if (response.StatusCode != HttpStatusCode.OK)
FailedRequest(response);

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

public async Task<IEnumerable<Ban>> GetBansBatchedAsync(int? startingId = null, IEnumerable<int> searchFor = null)
public async Task<IEnumerable<Ban>> GetBansBatchedAsync()
{
// 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>();
var lastRequested = startingId;
List<TgRawBan> lastResponse;
do
var allBans = new List<TgRawBan>();
var page = 1;
while (true)
{
lastResponse = await GetBansAsync(lastRequested);
if (!lastResponse.Any())
var bans = await GetBansAsync(page);
if (bans.Count == 0)
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);

allBans.AddRange(bans);
page++;
}

return cleanBans;
return allBans.Select(x => x.AsBan(BanSource)).DistinctBy(x => x.BanID);
}
}

0 comments on commit 4483407

Please sign in to comment.