Files
2026-07-23 18:21:43 -07:00

129 lines
4.5 KiB
C#

using DeluxeBackend.Models;
using DeluxeBackend.Services;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using System.Security.Cryptography;
using static DeluxeBackend.Enums;
namespace DeluxeBackend.Discord.Commands
{
public class LinkCode : InteractionModuleBase<SocketInteractionContext>
{
private readonly ILiteDbService _db;
private readonly INotificationService _ws;
private static readonly HashSet<ulong> GameRoles = new()
{
1502718783700078767, 1495587327593021490, 1495580320525844581,
1495580213529280563, 1495909230643908729, 1500323661251477635,
1495580027453046804, 1501711512996282369, 1495580006712213674,
1495580717134905457
};
public LinkCode(ILiteDbService db, INotificationService ws)
{
_db = db;
_ws = ws;
}
[SlashCommand("ping", "Check bot latency")]
public async Task Ping() => await RespondAsync($"{Context.Client.Latency}ms");
[SlashCommand("link", "Generate a unique code to link your Deluxe account")]
public async Task LinkAccount()
{
await DeferAsync(ephemeral: true);
if (Context.User is not SocketGuildUser guildUser)
{
await FollowupAsync("This command must be used within a server.");
return;
}
if (!guildUser.Roles.Any(r => GameRoles.Contains(r.Id)))
{
await FollowupAsync("You do not have the required permissions.", ephemeral: true);
return;
}
Account? account = _db.Accounts.FindOne(x => x.DiscordId == guildUser.Id);
if (account != null)
{
var linkedEmbed = new EmbedBuilder()
.WithDescription("Your Discord profile is **already linked** to a Deluxe account.")
.WithColor(Color.LighterGrey)
.Build();
await FollowupAsync(embed: linkedEmbed, ephemeral: true);
return;
}
var pendingLink = _db.ActionLinks.FindOne(x =>
x.ExtraData!.DiscordUserId == guildUser.Id &&
x.ExpiresAt > DateTime.UtcNow &&
x.Uses < x.MaxUses);
if (pendingLink != null)
{
long unixTime = ((DateTimeOffset)pendingLink.ExpiresAt).ToUnixTimeSeconds();
var existingEmbed = new EmbedBuilder()
.WithDescription($"You already have an active code:\n\n**`{pendingLink.Code}`**\n\nIt expires **<t:{unixTime}:R>** (<t:{unixTime}:f>).")
.WithColor(Color.LighterGrey)
.Build();
await FollowupAsync(embed: existingEmbed, ephemeral: true);
return;
}
string code;
while (true)
{
code = GenerateSecureCode(8);
var existing = _db.ActionLinks.FindOne(x => x.Code == code);
if (existing == null) break;
if (!existing.IsValid)
{
_db.ActionLinks.Delete(existing.Id);
break;
}
}
DateTime expiry = DateTime.UtcNow.AddMinutes(10);
long expiryUnix = ((DateTimeOffset)expiry).ToUnixTimeSeconds();
var actionLink = new ActionLink
{
Code = code,
CreatorPlayerId = 1,
Description = $"Discord Link: {guildUser.Username}",
ExpiresAt = expiry,
Type = ActionLinkType.DiscordLink,
ExtraData = new ActionLinkExtraData { DiscordUserId = guildUser.Id },
MaxUses = 1
};
_db.ActionLinks.Insert(actionLink);
var embed = new EmbedBuilder()
.WithDescription($"Use the code below to link your account:\n\n**`{code}`**\n\nExpires **<t:{expiryUnix}:R>**.")
.WithColor(Color.LighterGrey)
.Build();
await FollowupAsync(embed: embed, ephemeral: true);
}
private static string GenerateSecureCode(int length)
{
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
return string.Create(length, chars, (buffer, symbols) =>
{
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = symbols[RandomNumberGenerator.GetInt32(symbols.Length)];
}
});
}
}
}