start member add responder
This commit is contained in:
parent
633ba8f600
commit
f0cb5a9d03
3 changed files with 105 additions and 7 deletions
|
|
@ -0,0 +1,94 @@
|
||||||
|
using Catalogger.Backend.Cache;
|
||||||
|
using Catalogger.Backend.Database;
|
||||||
|
using Catalogger.Backend.Database.Queries;
|
||||||
|
using Catalogger.Backend.Extensions;
|
||||||
|
using Catalogger.Backend.Services;
|
||||||
|
using Humanizer;
|
||||||
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
|
using Remora.Discord.API.Objects;
|
||||||
|
using Remora.Discord.Extensions.Embeds;
|
||||||
|
using Remora.Discord.Gateway.Responders;
|
||||||
|
using Remora.Results;
|
||||||
|
|
||||||
|
namespace Catalogger.Backend.Bot.Responders.Guilds;
|
||||||
|
|
||||||
|
public class GuildMemberAddResponder(
|
||||||
|
ILogger logger,
|
||||||
|
DatabaseContext db,
|
||||||
|
IMemberCache memberCache,
|
||||||
|
WebhookExecutorService webhookExecutor,
|
||||||
|
IDiscordRestGuildAPI guildApi,
|
||||||
|
PluralkitApiService pluralkitApi) : IResponder<IGuildMemberAdd>
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger = logger.ForContext<GuildMemberAddResponder>();
|
||||||
|
private static readonly TimeSpan NewAccountThreshold = 7.Days();
|
||||||
|
|
||||||
|
public async Task<Result> RespondAsync(IGuildMemberAdd member, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
await memberCache.SetAsync(member.GuildID, member);
|
||||||
|
|
||||||
|
var user = member.User.GetOrThrow();
|
||||||
|
|
||||||
|
var builder = new EmbedBuilder()
|
||||||
|
.WithTitle("Member joined")
|
||||||
|
.WithColour(DiscordUtils.Green)
|
||||||
|
.WithAuthor(user.Tag(), null, user.AvatarUrl())
|
||||||
|
.WithDescription($"<@{user.ID}>")
|
||||||
|
.WithCurrentTimestamp()
|
||||||
|
.WithFooter($"ID: {user.ID}");
|
||||||
|
|
||||||
|
var guildConfig = await db.GetGuildAsync(member.GuildID, ct);
|
||||||
|
var guildRes = await guildApi.GetGuildAsync(member.GuildID, withCounts: true, ct);
|
||||||
|
if (guildRes.IsSuccess)
|
||||||
|
builder.Description += $"\n{guildRes.Entity.ApproximateMemberCount.Value.Ordinalize()} to join";
|
||||||
|
|
||||||
|
builder.Description += $"\ncreated <t:{user.ID.Timestamp.ToUnixTimeSeconds()}>";
|
||||||
|
|
||||||
|
var pkSystem = await pluralkitApi.GetPluralKitSystemAsync(user.ID.Value, ct);
|
||||||
|
if (pkSystem != null)
|
||||||
|
{
|
||||||
|
var createdAt = pkSystem.Created != null
|
||||||
|
? $"<t:{pkSystem.Created.Value.ToUnixTimeSeconds()}>"
|
||||||
|
: "*(unknown)*";
|
||||||
|
builder.AddField("PluralKit system", $"""
|
||||||
|
**ID:** {pkSystem.Id} (`{pkSystem.Uuid}`)
|
||||||
|
**Name:** {pkSystem.Name ?? "*(none)*"}
|
||||||
|
**Tag:** {pkSystem.Tag ?? "*(none)*"}
|
||||||
|
**Created:** {createdAt}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: find used invite
|
||||||
|
|
||||||
|
List<Embed> embeds = [builder.Build().GetOrThrow()];
|
||||||
|
|
||||||
|
if (user.ID.Timestamp > DateTimeOffset.Now - NewAccountThreshold)
|
||||||
|
{
|
||||||
|
embeds.Add(new EmbedBuilder().WithTitle("New account")
|
||||||
|
.WithDescription($"\u26a0\ufe0f Created <t:{user.ID.Timestamp.ToUnixTimeSeconds()}:R>").Build()
|
||||||
|
.GetOrThrow());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkSystem != null)
|
||||||
|
{
|
||||||
|
if (guildConfig.BannedSystems.Contains(pkSystem.Id) ||
|
||||||
|
guildConfig.BannedSystems.Contains(pkSystem.Uuid.ToString()))
|
||||||
|
{
|
||||||
|
embeds.Add(new EmbedBuilder().WithTitle("Banned system")
|
||||||
|
.WithDescription(
|
||||||
|
"\u26a0\ufe0f The system associated with this account has been banned from the server.")
|
||||||
|
.WithColour(DiscordUtils.Red)
|
||||||
|
.WithFooter($"ID: {pkSystem.Id}")
|
||||||
|
.Build()
|
||||||
|
.GetOrThrow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (embeds.Count > 1)
|
||||||
|
await webhookExecutor.SendLogWithAttachmentsAsync(guildConfig.Channels.GuildMemberAdd, embeds, []);
|
||||||
|
else await webhookExecutor.QueueLogAsync(guildConfig.Channels.GuildMemberAdd, embeds[0]);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@ using System.Threading.RateLimiting;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Polly;
|
using Polly;
|
||||||
using Remora.Rest.Json.Policies;
|
|
||||||
|
|
||||||
namespace Catalogger.Backend.Services;
|
namespace Catalogger.Backend.Services;
|
||||||
|
|
||||||
|
|
@ -33,7 +32,7 @@ public class PluralkitApiService(ILogger logger)
|
||||||
|
|
||||||
_logger.Debug("Requesting {Path} from PluralKit API", path);
|
_logger.Debug("Requesting {Path} from PluralKit API", path);
|
||||||
|
|
||||||
var resp = await _client.SendAsync(req, ct);
|
var resp = await _pipeline.ExecuteAsync(async ct2 => await _client.SendAsync(req, ct2), ct);
|
||||||
if (resp.StatusCode == HttpStatusCode.NotFound && allowNotFound)
|
if (resp.StatusCode == HttpStatusCode.NotFound && allowNotFound)
|
||||||
{
|
{
|
||||||
_logger.Debug("PluralKit API path {Path} returned 404 but 404 response is valid", path);
|
_logger.Debug("PluralKit API path {Path} returned 404 but 404 response is valid", path);
|
||||||
|
|
@ -48,15 +47,15 @@ public class PluralkitApiService(ILogger logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await resp.Content.ReadFromJsonAsync<T>(new JsonSerializerOptions
|
return await resp.Content.ReadFromJsonAsync<T>(new JsonSerializerOptions
|
||||||
{ PropertyNamingPolicy = new SnakeCaseNamingPolicy() }, ct) ??
|
{ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }, ct) ??
|
||||||
throw new CataloggerError("JSON response from PluralKit API was null");
|
throw new CataloggerError("JSON response from PluralKit API was null");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PkMessage?> GetPluralKitMessageAsync(ulong id, CancellationToken ct = default) =>
|
public async Task<PkMessage?> GetPluralKitMessageAsync(ulong id, CancellationToken ct = default) =>
|
||||||
await DoRequestAsync<PkMessage>($"/messages/{id}", allowNotFound: true, ct);
|
await DoRequestAsync<PkMessage>($"/messages/{id}", allowNotFound: true, ct);
|
||||||
|
|
||||||
public async Task<PkSystem> GetPluralKitSystemAsync(ulong id, CancellationToken ct = default) =>
|
public async Task<PkSystem?> GetPluralKitSystemAsync(ulong id, CancellationToken ct = default) =>
|
||||||
(await DoRequestAsync<PkSystem>($"/systems/{id}", allowNotFound: false, ct))!;
|
await DoRequestAsync<PkSystem>($"/systems/{id}", allowNotFound: true, ct);
|
||||||
|
|
||||||
public record PkMessage(
|
public record PkMessage(
|
||||||
ulong Id,
|
ulong Id,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Catalogger.Backend.Extensions;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
|
using Remora.Discord.API.Objects;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
using Guild = Catalogger.Backend.Database.Models.Guild;
|
using Guild = Catalogger.Backend.Database.Models.Guild;
|
||||||
|
|
||||||
|
|
@ -54,6 +55,8 @@ public class WebhookExecutorService(
|
||||||
|
|
||||||
public async Task QueueLogAsync(ulong channelId, IEmbed embed)
|
public async Task QueueLogAsync(ulong channelId, IEmbed embed)
|
||||||
{
|
{
|
||||||
|
if (channelId == 0) return;
|
||||||
|
|
||||||
var queue = _cache.GetOrAdd(channelId, []);
|
var queue = _cache.GetOrAdd(channelId, []);
|
||||||
queue.Enqueue(embed);
|
queue.Enqueue(embed);
|
||||||
_cache[channelId] = queue;
|
_cache[channelId] = queue;
|
||||||
|
|
@ -88,15 +91,17 @@ public class WebhookExecutorService(
|
||||||
embeds: embeds, username: _selfUser!.Username, avatarUrl: _selfUser.AvatarUrl());
|
embeds: embeds, username: _selfUser!.Username, avatarUrl: _selfUser.AvatarUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendLogWithAttachmentsAsync(ulong channelId, IEmbed embed, IEnumerable<FileData> files)
|
public async Task SendLogWithAttachmentsAsync(ulong channelId, List<Embed> embeds, IEnumerable<FileData> files)
|
||||||
{
|
{
|
||||||
|
if (channelId == 0) return;
|
||||||
|
|
||||||
var attachments = files
|
var attachments = files
|
||||||
.Select<FileData, OneOf.OneOf<FileData, IPartialAttachment>>(f => f)
|
.Select<FileData, OneOf.OneOf<FileData, IPartialAttachment>>(f => f)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var webhook = await webhookCache.GetOrFetchWebhookAsync(channelId, id => FetchWebhookAsync(id));
|
var webhook = await webhookCache.GetOrFetchWebhookAsync(channelId, id => FetchWebhookAsync(id));
|
||||||
await webhookApi.ExecuteWebhookAsync(DiscordSnowflake.New(webhook.Id), webhook.Token, shouldWait: false,
|
await webhookApi.ExecuteWebhookAsync(DiscordSnowflake.New(webhook.Id), webhook.Token, shouldWait: false,
|
||||||
embeds: new List<IEmbed>([embed]), attachments: attachments, username: _selfUser!.Username,
|
embeds: embeds, attachments: attachments, username: _selfUser!.Username,
|
||||||
avatarUrl: _selfUser.AvatarUrl());
|
avatarUrl: _selfUser.AvatarUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue