-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathServerPairingComponentModule.cs
More file actions
95 lines (85 loc) · 4.13 KB
/
Copy pathServerPairingComponentModule.cs
File metadata and controls
95 lines (85 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using Discord;
using Discord.Interactions;
using Microsoft.Extensions.DependencyInjection;
using RustPlusBot.Features.Pairing.Pairing;
using RustPlusBot.Features.Pairing.Rendering;
namespace RustPlusBot.Features.Pairing.Modules;
/// <summary>Thin handler for the #setup server-pairing prompt buttons. Any guild member.</summary>
/// <param name="scopeFactory">Creates a short-lived DI scope per interaction.</param>
public sealed class ServerPairingComponentModule(IServiceScopeFactory scopeFactory)
: InteractionModuleBase<SocketInteractionContext>
{
private const string InvalidControlMessage = "That control wasn't valid.";
private const string ExpiredMessage = "This pairing expired — press \"Pair\" in-game again.";
/// <summary>Accepts a pending server pairing and triggers the full registration cycle.</summary>
/// <param name="tail">The "{ip}:{port}" custom-id tail.</param>
[ComponentInteraction(ServerPairingComponentIds.AcceptPrefix + "*")]
public async Task AcceptAsync(string tail)
{
if (!ServerPairingComponentIds.TryParseTail(tail, out var ip, out var port) || Context.Guild is null)
{
await RespondAsync(InvalidControlMessage, ephemeral: true).ConfigureAwait(false);
return;
}
await DeferAsync(ephemeral: true).ConfigureAwait(false);
ServerPairingAcceptOutcome outcome;
var scope = scopeFactory.CreateAsyncScope();
await using (scope.ConfigureAwait(false))
{
var coordinator = scope.ServiceProvider.GetRequiredService<IServerPairingCoordinator>();
outcome = await coordinator.TryAcceptAsync(Context.Guild.Id, ip, port, CancellationToken.None)
.ConfigureAwait(false);
}
if (outcome == ServerPairingAcceptOutcome.Expired)
{
// The prompt outlived its pending state (restart/double-click) — clean up the orphaned message.
await DeletePromptMessageSafeAsync().ConfigureAwait(false);
}
await FollowupAsync(outcome switch
{
ServerPairingAcceptOutcome.Added => "Server added.",
ServerPairingAcceptOutcome.AlreadyAdded => "That server was already added.",
_ => ExpiredMessage,
}, ephemeral: true).ConfigureAwait(false);
}
/// <summary>Dismisses a pending server pairing and removes the transient prompt message.</summary>
/// <param name="tail">The "{ip}:{port}" custom-id tail.</param>
[ComponentInteraction(ServerPairingComponentIds.DismissPrefix + "*")]
public async Task DismissAsync(string tail)
{
if (!ServerPairingComponentIds.TryParseTail(tail, out var ip, out var port) || Context.Guild is null)
{
await RespondAsync(InvalidControlMessage, ephemeral: true).ConfigureAwait(false);
return;
}
bool dismissed;
var scope = scopeFactory.CreateAsyncScope();
await using (scope.ConfigureAwait(false))
{
var coordinator = scope.ServiceProvider.GetRequiredService<IServerPairingCoordinator>();
dismissed = coordinator.TryDismiss(Context.Guild.Id, ip, port);
}
// Delete the actual prompt message that hosts this button (the component interaction's source
// message) — not the ephemeral interaction response. Best-effort: a delete failure is non-fatal.
await DeletePromptMessageSafeAsync().ConfigureAwait(false);
await RespondAsync(dismissed ? "Dismissed." : ExpiredMessage, ephemeral: true).ConfigureAwait(false);
}
private async Task DeletePromptMessageSafeAsync()
{
try
{
// The source message of a component interaction is the prompt that carries the button.
if (Context.Interaction is IComponentInteraction component)
{
await component.Message.DeleteAsync().ConfigureAwait(false);
}
}
#pragma warning disable CA1031 // Best-effort prompt cleanup; a delete failure is non-fatal.
catch (Exception ex)
#pragma warning restore CA1031
{
// Ignore: the prompt is transient and harmless if it lingers.
_ = ex;
}
}
}