feat: remote await events from gateway

This commit is contained in:
alyssa 2025-03-29 17:55:01 +00:00
parent 64ff69723c
commit 15c992c572
17 changed files with 439 additions and 30 deletions

View file

@ -26,6 +26,7 @@ public class BotConfig
public string? HttpListenerAddr { get; set; }
public bool DisableGateway { get; set; } = false;
public string? EventAwaiterTarget { get; set; }
public string? DiscordBaseUrl { get; set; }
public string? AvatarServiceUrl { get; set; }

View file

@ -28,7 +28,7 @@ public abstract class BaseInteractive
ButtonStyle style = ButtonStyle.Secondary, bool disabled = false)
{
var dispatch = _ctx.Services.Resolve<InteractionDispatchService>();
var customId = dispatch.Register(handler, Timeout);
var customId = dispatch.Register(_ctx.ShardId, handler, Timeout);
var button = new Button
{
@ -89,7 +89,7 @@ public abstract class BaseInteractive
{
var dispatch = ctx.Services.Resolve<InteractionDispatchService>();
foreach (var button in _buttons)
button.CustomId = dispatch.Register(button.Handler, Timeout);
button.CustomId = dispatch.Register(_ctx.ShardId, button.Handler, Timeout);
}
public abstract Task Start();

View file

@ -1,5 +1,6 @@
using Autofac;
using Myriad.Cache;
using Myriad.Gateway;
using Myriad.Rest.Types;
using Myriad.Types;
@ -69,6 +70,9 @@ public class YesNoPrompt: BaseInteractive
return true;
}
// no need to reawait message
// gateway will already have sent us only matching messages
return false;
}
@ -88,6 +92,17 @@ public class YesNoPrompt: BaseInteractive
{
try
{
// check if http gateway and set listener
// todo: this one needs to handle options for message
if (_ctx.Cache is HttpDiscordCache)
await (_ctx.Cache as HttpDiscordCache).AwaitMessage(
_ctx.Guild?.Id ?? 0,
_ctx.Channel.Id,
_ctx.Author.Id,
Timeout,
options: new[] { "yes", "y", "no", "n" }
);
await queue.WaitFor(MessagePredicate, Timeout, cts.Token);
}
catch (TimeoutException e)

View file

@ -49,8 +49,15 @@ public class BotModule: Module
if (botConfig.HttpCacheUrl != null)
{
var cache = new HttpDiscordCache(c.Resolve<ILogger>(),
c.Resolve<HttpClient>(), botConfig.HttpCacheUrl, botConfig.Cluster?.TotalShards ?? 1, botConfig.ClientId, botConfig.HttpUseInnerCache);
var cache = new HttpDiscordCache(
c.Resolve<ILogger>(),
c.Resolve<HttpClient>(),
botConfig.HttpCacheUrl,
botConfig.EventAwaiterTarget,
botConfig.Cluster?.TotalShards ?? 1,
botConfig.ClientId,
botConfig.HttpUseInnerCache
);
var metrics = c.Resolve<IMetrics>();

View file

@ -1,5 +1,7 @@
using System.Collections.Concurrent;
using Myriad.Cache;
using NodaTime;
using Serilog;
@ -16,9 +18,12 @@ public class InteractionDispatchService: IDisposable
private readonly ConcurrentDictionary<Guid, RegisteredInteraction> _handlers = new();
private readonly ILogger _logger;
public InteractionDispatchService(IClock clock, ILogger logger)
private readonly IDiscordCache _cache;
public InteractionDispatchService(IClock clock, ILogger logger, IDiscordCache cache)
{
_clock = clock;
_cache = cache;
_logger = logger.ForContext<InteractionDispatchService>();
_cleanupWorker = CleanupLoop(_cts.Token);
@ -50,9 +55,15 @@ public class InteractionDispatchService: IDisposable
_handlers.TryRemove(customIdGuid, out _);
}
public string Register(Func<InteractionContext, Task> callback, Duration? expiry = null)
public string Register(int shardId, Func<InteractionContext, Task> callback, Duration? expiry = null)
{
var key = Guid.NewGuid();
// if http_cache, return RegisterRemote
// not awaited here, it's probably fine
if (_cache is HttpDiscordCache)
(_cache as HttpDiscordCache).AwaitInteraction(shardId, key.ToString(), expiry);
var handler = new RegisteredInteraction
{
Callback = callback,

View file

@ -1,6 +1,7 @@
using Autofac;
using Myriad.Builders;
using Myriad.Cache;
using Myriad.Gateway;
using Myriad.Rest.Exceptions;
using Myriad.Rest.Types.Requests;
@ -40,8 +41,12 @@ public static class ContextUtils
}
public static async Task<MessageReactionAddEvent> AwaitReaction(this Context ctx, Message message,
User user = null, Func<MessageReactionAddEvent, bool> predicate = null, Duration? timeout = null)
User user, Func<MessageReactionAddEvent, bool> predicate = null, Duration? timeout = null)
{
// check if http gateway and set listener
if (ctx.Cache is HttpDiscordCache)
await (ctx.Cache as HttpDiscordCache).AwaitReaction(ctx.Guild?.Id ?? 0, message.Id, user!.Id, timeout);
bool ReactionPredicate(MessageReactionAddEvent evt)
{
if (message.Id != evt.MessageId) return false; // Ignore reactions for different messages
@ -57,11 +62,17 @@ public static class ContextUtils
public static async Task<bool> ConfirmWithReply(this Context ctx, string expectedReply, bool treatAsHid = false)
{
var timeout = Duration.FromMinutes(1);
// check if http gateway and set listener
if (ctx.Cache is HttpDiscordCache)
await (ctx.Cache as HttpDiscordCache).AwaitMessage(ctx.Guild?.Id ?? 0, ctx.Channel.Id, ctx.Author.Id, timeout);
bool Predicate(MessageCreateEvent e) =>
e.Author.Id == ctx.Author.Id && e.ChannelId == ctx.Channel.Id;
var msg = await ctx.Services.Resolve<HandlerQueue<MessageCreateEvent>>()
.WaitFor(Predicate, Duration.FromMinutes(1));
.WaitFor(Predicate, timeout);
var content = msg.Content;
if (treatAsHid)
@ -96,11 +107,17 @@ public static class ContextUtils
async Task<int> PromptPageNumber()
{
var timeout = Duration.FromMinutes(0.5);
// check if http gateway and set listener
if (ctx.Cache is HttpDiscordCache)
await (ctx.Cache as HttpDiscordCache).AwaitMessage(ctx.Guild?.Id ?? 0, ctx.Channel.Id, ctx.Author.Id, timeout);
bool Predicate(MessageCreateEvent e) =>
e.Author.Id == ctx.Author.Id && e.ChannelId == ctx.Channel.Id;
var msg = await ctx.Services.Resolve<HandlerQueue<MessageCreateEvent>>()
.WaitFor(Predicate, Duration.FromMinutes(0.5));
.WaitFor(Predicate, timeout);
int.TryParse(msg.Content, out var num);

View file

@ -764,6 +764,8 @@
"myriad": {
"type": "Project",
"dependencies": {
"NodaTime": "[3.2.0, )",
"NodaTime.Serialization.JsonNet": "[3.1.0, )",
"Polly": "[8.5.0, )",
"Polly.Contrib.WaitAndRetry": "[1.1.1, )",
"Serilog": "[4.2.0, )",