129 lines
4.5 KiB
C#
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)];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
} |