Merge branch 'feat/webhooks' into main
This commit is contained in:
@@ -18,6 +18,12 @@ namespace PluralKit.Bot
|
||||
throw new PKError("This command can not be run in a DM.");
|
||||
}
|
||||
|
||||
public static Context CheckDMContext(this Context ctx)
|
||||
{
|
||||
if (ctx.Channel.GuildId == null) return ctx;
|
||||
throw new PKError("This command must be run in a DM.");
|
||||
}
|
||||
|
||||
public static Context CheckSystemPrivacy(this Context ctx, PKSystem target, PrivacyLevel level)
|
||||
{
|
||||
if (level.CanAccess(ctx.LookupContextFor(target))) return ctx;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Myriad.Extensions;
|
||||
@@ -9,14 +11,15 @@ using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public class Token
|
||||
public class Api
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
public Token(IDatabase db, ModelRepository repo)
|
||||
private readonly DispatchService _dispatch;
|
||||
private static readonly Regex _webhookRegex = new("https://(?:\\w+.)?discord(?:app)?.com/api(?:/v.*)?/webhooks/(.*)");
|
||||
public Api(ModelRepository repo, DispatchService dispatch)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
|
||||
public async Task GetToken(Context ctx)
|
||||
@@ -91,5 +94,63 @@ namespace PluralKit.Bot
|
||||
await ctx.Reply($"{Emojis.Error} Could not send token in DMs. Are your DMs closed?");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SystemWebhook(Context ctx)
|
||||
{
|
||||
ctx.CheckSystem().CheckDMContext();
|
||||
|
||||
if (!ctx.HasNext(false))
|
||||
{
|
||||
if (ctx.System.WebhookUrl == null)
|
||||
await ctx.Reply("Your system does not have a webhook URL set. Set one with `pk;system webhook <url>`!");
|
||||
else
|
||||
await ctx.Reply($"Your system's webhook URL is <{ctx.System.WebhookUrl}>.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ctx.MatchClear("your system's webhook URL"))
|
||||
{
|
||||
await _repo.UpdateSystem(ctx.System.Id, new()
|
||||
{
|
||||
WebhookUrl = null,
|
||||
WebhookToken = null,
|
||||
});
|
||||
|
||||
await ctx.Reply($"{Emojis.Success} System webhook URL removed.");
|
||||
return;
|
||||
}
|
||||
|
||||
var newUrl = ctx.RemainderOrNull();
|
||||
if (!await DispatchExt.ValidateUri(newUrl))
|
||||
throw new PKError($"The URL {newUrl.AsCode()} is invalid or I cannot access it. Are you sure this is a valid, publicly accessible URL?");
|
||||
|
||||
if (_webhookRegex.IsMatch(newUrl))
|
||||
throw new PKError("PluralKit does not currently support setting a Discord webhook URL as your system's webhook URL.");
|
||||
|
||||
try
|
||||
{
|
||||
await _dispatch.DoPostRequest(ctx.System.Id, newUrl, null, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new PKError($"Could not verify that the new URL is working: {e.Message}");
|
||||
}
|
||||
|
||||
var newToken = StringUtils.GenerateToken();
|
||||
|
||||
await _repo.UpdateSystem(ctx.System.Id, new()
|
||||
{
|
||||
WebhookUrl = newUrl,
|
||||
WebhookToken = newToken,
|
||||
});
|
||||
|
||||
await ctx.Reply($"{Emojis.Success} Successfully the new webhook URL for your system."
|
||||
+ $"\n\n{Emojis.Warn} The following token is used to authenticate requests from PluralKit to you."
|
||||
+ " If it leaks, you should clear and re-set the webhook URL to get a new token."
|
||||
+ "\ntodo: add link to docs or something"
|
||||
);
|
||||
|
||||
await ctx.Reply(newToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,9 +152,9 @@ namespace PluralKit.Bot
|
||||
return ctx.Execute<SystemLink>(Unlink, m => m.UnlinkAccount(ctx));
|
||||
if (ctx.Match("token"))
|
||||
if (ctx.Match("refresh", "renew", "invalidate", "reroll", "regen"))
|
||||
return ctx.Execute<Token>(TokenRefresh, m => m.RefreshToken(ctx));
|
||||
return ctx.Execute<Api>(TokenRefresh, m => m.RefreshToken(ctx));
|
||||
else
|
||||
return ctx.Execute<Token>(TokenGet, m => m.GetToken(ctx));
|
||||
return ctx.Execute<Api>(TokenGet, m => m.GetToken(ctx));
|
||||
if (ctx.Match("import"))
|
||||
return ctx.Execute<ImportExport>(Import, m => m.Import(ctx));
|
||||
if (ctx.Match("export"))
|
||||
@@ -286,6 +286,8 @@ namespace PluralKit.Bot
|
||||
await ctx.Execute<SystemEdit>(SystemAvatar, m => m.Avatar(ctx));
|
||||
else if (ctx.Match("delete", "remove", "destroy", "erase", "yeet"))
|
||||
await ctx.Execute<SystemEdit>(SystemDelete, m => m.Delete(ctx));
|
||||
else if (ctx.Match("webhook", "hook"))
|
||||
await ctx.Execute<Api>(null, m => m.SystemWebhook(ctx));
|
||||
else if (ctx.Match("timezone", "tz"))
|
||||
await ctx.Execute<SystemEdit>(SystemTimezone, m => m.SystemTimezone(ctx));
|
||||
else if (ctx.Match("proxy"))
|
||||
|
||||
@@ -10,6 +10,8 @@ using Dapper;
|
||||
|
||||
using Humanizer;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
using Myriad.Builders;
|
||||
@@ -24,13 +26,15 @@ namespace PluralKit.Bot
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly EmbedService _embeds;
|
||||
private readonly HttpClient _client;
|
||||
private readonly DispatchService _dispatch;
|
||||
|
||||
public Groups(IDatabase db, ModelRepository repo, EmbedService embeds, HttpClient client)
|
||||
public Groups(IDatabase db, ModelRepository repo, EmbedService embeds, HttpClient client, DispatchService dispatch)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
_embeds = embeds;
|
||||
_client = client;
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
|
||||
public async Task CreateGroup(Context ctx)
|
||||
@@ -59,6 +63,12 @@ namespace PluralKit.Bot
|
||||
|
||||
var newGroup = await _repo.CreateGroup(ctx.System.Id, groupName);
|
||||
|
||||
_ = _dispatch.Dispatch(newGroup.Id, new UpdateDispatchData()
|
||||
{
|
||||
Event = DispatchEvent.CREATE_GROUP,
|
||||
EventData = JObject.FromObject(new { name = groupName }),
|
||||
});
|
||||
|
||||
var eb = new EmbedBuilder()
|
||||
.Description($"Your new group, **{groupName}**, has been created, with the group ID **`{newGroup.Hid}`**.\nBelow are a couple of useful commands:")
|
||||
.Field(new("View the group card", $"> pk;group **{newGroup.Reference()}**"))
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace PluralKit.Bot
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly EmbedService _embeds;
|
||||
private readonly HttpClient _client;
|
||||
private readonly DispatchService _dispatch;
|
||||
|
||||
public Member(EmbedService embeds, IDatabase db, ModelRepository repo, HttpClient client)
|
||||
public Member(EmbedService embeds, IDatabase db, ModelRepository repo, HttpClient client, DispatchService dispatch)
|
||||
{
|
||||
_embeds = embeds;
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
_client = client;
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
|
||||
public async Task NewMember(Context ctx)
|
||||
@@ -62,12 +64,20 @@ namespace PluralKit.Bot
|
||||
// Try to match an image attached to the message
|
||||
var avatarArg = ctx.Message.Attachments.FirstOrDefault();
|
||||
Exception imageMatchError = null;
|
||||
bool sentDispatch = false;
|
||||
if (avatarArg != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await AvatarUtils.VerifyAvatarOrThrow(_client, avatarArg.Url);
|
||||
await _repo.UpdateMember(member.Id, new MemberPatch { AvatarUrl = avatarArg.Url });
|
||||
await _db.Execute(conn => _repo.UpdateMember(member.Id, new MemberPatch { AvatarUrl = avatarArg.Url }, conn));
|
||||
|
||||
_ = _dispatch.Dispatch(member.Id, new()
|
||||
{
|
||||
Event = DispatchEvent.CREATE_MEMBER,
|
||||
EventData = JObject.FromObject(new { name = memberName, avatar_url = avatarArg.Url }),
|
||||
});
|
||||
sentDispatch = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -75,6 +85,13 @@ namespace PluralKit.Bot
|
||||
}
|
||||
}
|
||||
|
||||
if (!sentDispatch)
|
||||
_ = _dispatch.Dispatch(member.Id, new()
|
||||
{
|
||||
Event = DispatchEvent.CREATE_MEMBER,
|
||||
EventData = JObject.FromObject(new { name = memberName }),
|
||||
});
|
||||
|
||||
// Send confirmation and space hint
|
||||
await ctx.Reply($"{Emojis.Success} Member \"{memberName}\" (`{member.Hid}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member");
|
||||
// todo: move this to ModelRepository
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace PluralKit.Bot
|
||||
// Commands
|
||||
builder.RegisterType<CommandTree>().AsSelf();
|
||||
builder.RegisterType<Admin>().AsSelf();
|
||||
builder.RegisterType<Api>().AsSelf();
|
||||
builder.RegisterType<Autoproxy>().AsSelf();
|
||||
builder.RegisterType<Checks>().AsSelf();
|
||||
builder.RegisterType<Fun>().AsSelf();
|
||||
@@ -94,7 +95,6 @@ namespace PluralKit.Bot
|
||||
builder.RegisterType<SystemFront>().AsSelf();
|
||||
builder.RegisterType<SystemLink>().AsSelf();
|
||||
builder.RegisterType<SystemList>().AsSelf();
|
||||
builder.RegisterType<Token>().AsSelf();
|
||||
|
||||
// Bot core
|
||||
builder.RegisterType<Bot>().AsSelf().SingleInstance();
|
||||
|
||||
@@ -30,17 +30,19 @@ namespace PluralKit.Bot
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly ILogger _logger;
|
||||
private readonly WebhookExecutorService _webhookExecutor;
|
||||
private readonly DispatchService _dispatch;
|
||||
private readonly ProxyMatcher _matcher;
|
||||
private readonly IMetrics _metrics;
|
||||
private readonly IDiscordCache _cache;
|
||||
private readonly LastMessageCacheService _lastMessage;
|
||||
private readonly DiscordApiClient _rest;
|
||||
|
||||
public ProxyService(LogChannelService logChannel, ILogger logger, WebhookExecutorService webhookExecutor, IDatabase db,
|
||||
public ProxyService(LogChannelService logChannel, ILogger logger, WebhookExecutorService webhookExecutor, DispatchService dispatch, IDatabase db,
|
||||
ProxyMatcher matcher, IMetrics metrics, ModelRepository repo, IDiscordCache cache, DiscordApiClient rest, LastMessageCacheService lastMessage)
|
||||
{
|
||||
_logChannel = logChannel;
|
||||
_webhookExecutor = webhookExecutor;
|
||||
_dispatch = dispatch;
|
||||
_db = db;
|
||||
_matcher = matcher;
|
||||
_metrics = metrics;
|
||||
@@ -297,6 +299,8 @@ namespace PluralKit.Bot
|
||||
|
||||
Task LogMessageToChannel() => _logChannel.LogMessage(ctx, sentMessage, triggerMessage, proxyMessage).AsTask();
|
||||
|
||||
Task DispatchWebhook() => _dispatch.Dispatch(ctx.SystemId.Value, sentMessage);
|
||||
|
||||
async Task DeleteProxyTriggerMessage()
|
||||
{
|
||||
// Wait a second or so before deleting the original message
|
||||
@@ -315,11 +319,11 @@ namespace PluralKit.Bot
|
||||
}
|
||||
|
||||
// Run post-proxy actions (simultaneously; order doesn't matter)
|
||||
// Note that only AddMessage is using our passed-in connection, careful not to pass it elsewhere and run into conflicts
|
||||
await Task.WhenAll(
|
||||
DeleteProxyTriggerMessage(),
|
||||
SaveMessageInDatabase(),
|
||||
LogMessageToChannel()
|
||||
LogMessageToChannel(),
|
||||
DispatchWebhook()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user