diff --git a/Catalogger.Backend/Config.cs b/Catalogger.Backend/Config.cs
index 2a60d6a..612b91e 100644
--- a/Catalogger.Backend/Config.cs
+++ b/Catalogger.Backend/Config.cs
@@ -60,6 +60,9 @@ public class Config
// If enabled, nothing will be logged.
public bool TestMode { get; init; } = false;
+
+ // Token for discord.bots.gg stats
+ public string? BotsGgToken { get; init; }
}
public class WebConfig
diff --git a/Catalogger.Backend/Services/StatusUpdateService.cs b/Catalogger.Backend/Services/StatusUpdateService.cs
index 6c0dff7..1d858aa 100644
--- a/Catalogger.Backend/Services/StatusUpdateService.cs
+++ b/Catalogger.Backend/Services/StatusUpdateService.cs
@@ -13,6 +13,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
+using System.Net.Http.Headers;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using Catalogger.Backend.Bot;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Gateway.Commands;
@@ -24,15 +27,16 @@ public class StatusUpdateService(ILogger logger, ShardedGatewayClient shardedCli
: BackgroundService
{
private readonly ILogger _logger = logger.ForContext();
+ private readonly HttpClient _client = new();
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(3));
while (await timer.WaitForNextTickAsync(ct))
- UpdateShardStatuses(ct);
+ await UpdateShardStatuses(ct);
}
- private void UpdateShardStatuses(CancellationToken ct = default)
+ private async Task UpdateShardStatuses(CancellationToken ct = default)
{
_logger.Information(
"Updating status for {TotalShards} shards. Guild count is {GuildCount}",
@@ -59,6 +63,8 @@ public class StatusUpdateService(ILogger logger, ShardedGatewayClient shardedCli
client.SubmitCommand(PresenceFor(shardId));
}
+
+ await ReportStatsAsync();
}
private UpdatePresence PresenceFor(int shardId)
@@ -75,4 +81,43 @@ public class StatusUpdateService(ILogger logger, ShardedGatewayClient shardedCli
Activities: [new Activity(Name: "Beep", Type: ActivityType.Custom, State: status)]
);
}
+
+ private async Task ReportStatsAsync()
+ {
+ if (config.Discord.BotsGgToken == null)
+ return;
+
+ _logger.Debug("Posting stats to discord.bots.gg");
+
+ var req = new HttpRequestMessage(
+ HttpMethod.Post,
+ $"https://discord.bots.gg/api/v1/bots/{config.Discord.ApplicationId}/stats"
+ );
+ req.Headers.Add("Authorization", config.Discord.BotsGgToken);
+ req.Content = new StringContent(
+ JsonSerializer.Serialize(
+ new BotsGgStats(
+ (int)CataloggerMetrics.GuildsCached.Value,
+ shardedClient.TotalShards
+ )
+ ),
+ new MediaTypeHeaderValue("application/json", "utf-8")
+ );
+
+ var resp = await _client.SendAsync(req);
+ if (!resp.IsSuccessStatusCode)
+ {
+ var content = await resp.Content.ReadAsStringAsync();
+ _logger.Error(
+ "Error updating stats for discord.bots.gg: {StatusCode}, {Content}",
+ (int)resp.StatusCode,
+ content
+ );
+ }
+ }
+
+ private record BotsGgStats(
+ [property: JsonPropertyName("guildCount")] int GuildCount,
+ [property: JsonPropertyName("shardCount")] int ShardCount
+ );
}