Fix various issues with proxying and webhook caching
This commit is contained in:
parent
8940226385
commit
ca56fd419b
@ -51,7 +51,7 @@ namespace PluralKit.Bot
|
||||
.AddTransient(_ => _config.GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig())
|
||||
.AddTransient(_ => _config.GetSection("PluralKit").GetSection("Bot").Get<BotConfig>() ?? new BotConfig())
|
||||
|
||||
.AddScoped<IDbConnection>(svc =>
|
||||
.AddTransient<IDbConnection>(svc =>
|
||||
{
|
||||
|
||||
var conn = new NpgsqlConnection(svc.GetRequiredService<CoreConfig>().Database);
|
||||
|
@ -46,7 +46,7 @@ namespace PluralKit.Bot {
|
||||
return new EmbedBuilder()
|
||||
.WithAuthor($"#{message.Channel.Name}: {member.Name}", member.AvatarUrl)
|
||||
.WithDescription(message.Content)
|
||||
.WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: ${message.Id}")
|
||||
.WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: {message.Id}")
|
||||
.WithTimestamp(message.Timestamp)
|
||||
.Build();
|
||||
}
|
||||
|
@ -77,12 +77,19 @@ namespace PluralKit.Bot
|
||||
}
|
||||
|
||||
public async Task HandleMessageAsync(IMessage message) {
|
||||
var results = await _connection.QueryAsync<PKMember, PKSystem, ProxyDatabaseResult>("select members.*, systems.* from members, systems, accounts where members.system = systems.id and accounts.system = systems.id and accounts.uid = @Uid", (member, system) => new ProxyDatabaseResult { Member = member, System = system }, new { Uid = message.Author.Id });
|
||||
var results = await _connection.QueryAsync<PKMember, PKSystem, ProxyDatabaseResult>(
|
||||
"select members.*, systems.* from members, systems, accounts where members.system = systems.id and accounts.system = systems.id and accounts.uid = @Uid",
|
||||
(member, system) =>
|
||||
new ProxyDatabaseResult { Member = member, System = system }, new { Uid = message.Author.Id });
|
||||
|
||||
// Find a member with proxy tags matching the message
|
||||
var match = GetProxyTagMatch(message.Content, results);
|
||||
if (match == null) return;
|
||||
|
||||
// We know message.Channel can only be ITextChannel as PK doesn't work in DMs/groups
|
||||
// Afterwards we ensure the bot has the right permissions, otherwise bail early
|
||||
if (!await EnsureBotPermissions(message.Channel as ITextChannel)) return;
|
||||
|
||||
// Fetch a webhook for this channel, and send the proxied message
|
||||
var webhook = await _webhookCache.GetWebhook(message.Channel as ITextChannel);
|
||||
var hookMessage = await ExecuteWebhook(webhook, match.InnerText, match.ProxyName, match.Member.AvatarUrl, message.Attachments.FirstOrDefault());
|
||||
@ -96,8 +103,42 @@ namespace PluralKit.Bot
|
||||
await message.DeleteAsync();
|
||||
}
|
||||
|
||||
private async Task<bool> EnsureBotPermissions(ITextChannel channel)
|
||||
{
|
||||
var guildUser = await channel.Guild.GetCurrentUserAsync();
|
||||
var permissions = guildUser.GetPermissions(channel);
|
||||
|
||||
if (!permissions.ManageWebhooks)
|
||||
{
|
||||
await channel.SendMessageAsync(
|
||||
$"{Emojis.Error} PluralKit does not have the *Manage Webhooks* permission in this channel, and thus cannot proxy messages. Please contact a server administrator to remedy this.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!permissions.ManageMessages)
|
||||
{
|
||||
await channel.SendMessageAsync(
|
||||
$"{Emojis.Error} PluralKit does not have the *Manage Messages* permission in this channel, and thus cannot delete the original trigger message. Please contact a server administrator to remedy this.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<IMessage> ExecuteWebhook(IWebhook webhook, string text, string username, string avatarUrl, IAttachment attachment) {
|
||||
var client = new DiscordWebhookClient(webhook);
|
||||
// TODO: DiscordWebhookClient's ctor does a call to GetWebhook that may be unnecessary, see if there's a way to do this The Hard Way :tm:
|
||||
// TODO: this will probably crash if there are multiple consecutive failures, perhaps have a loop instead?
|
||||
DiscordWebhookClient client;
|
||||
try
|
||||
{
|
||||
client = new DiscordWebhookClient(webhook);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// webhook was deleted or invalid
|
||||
webhook = await _webhookCache.InvalidateAndRefreshWebhook(webhook);
|
||||
client = new DiscordWebhookClient(webhook);
|
||||
}
|
||||
|
||||
ulong messageId;
|
||||
if (attachment != null) {
|
||||
@ -108,6 +149,8 @@ namespace PluralKit.Bot
|
||||
} else {
|
||||
messageId = await client.SendMessageAsync(text, username: username, avatarUrl: avatarUrl);
|
||||
}
|
||||
|
||||
// TODO: SendMessageAsync should return a full object(??), see if there's a way to avoid the extra server call here
|
||||
return await webhook.Channel.GetMessageAsync(messageId);
|
||||
}
|
||||
|
||||
|
@ -32,13 +32,24 @@ namespace PluralKit.Bot
|
||||
// We cache the webhook through a Lazy<Task<T>>, this way we make sure to only create one webhook per channel
|
||||
// If the webhook is requested twice before it's actually been found, the Lazy<T> wrapper will stop the
|
||||
// webhook from being created twice.
|
||||
var lazyWebhookValue =
|
||||
var lazyWebhookValue =
|
||||
_webhooks.GetOrAdd(channel.Id, new Lazy<Task<IWebhook>>(() => GetOrCreateWebhook(channel)));
|
||||
return await lazyWebhookValue.Value;
|
||||
|
||||
// It's possible to "move" a webhook to a different channel after creation
|
||||
// Here, we ensure it's actually still pointing towards the proper channel, and if not, wipe and refetch one.
|
||||
var webhook = await lazyWebhookValue.Value;
|
||||
if (webhook.Channel.Id != channel.Id) return await InvalidateAndRefreshWebhook(webhook);
|
||||
return webhook;
|
||||
}
|
||||
|
||||
public async Task<IWebhook> InvalidateAndRefreshWebhook(IWebhook webhook)
|
||||
{
|
||||
_webhooks.TryRemove(webhook.Channel.Id, out _);
|
||||
return await GetWebhook(webhook.Channel.Id);
|
||||
}
|
||||
|
||||
private async Task<IWebhook> GetOrCreateWebhook(ITextChannel channel) =>
|
||||
await FindExistingWebhook(channel) ?? await GetOrCreateWebhook(channel);
|
||||
await FindExistingWebhook(channel) ?? await DoCreateWebhook(channel);
|
||||
|
||||
private async Task<IWebhook> FindExistingWebhook(ITextChannel channel) => (await channel.GetWebhooksAsync()).FirstOrDefault(IsWebhookMine);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user