Updated to latest 5.0.0 nightly build of DSharpPlus

Still needs a new logger implementation and some new exception handling.
This commit is contained in:
Toastie 2024-12-26 18:36:20 +13:00
parent f89d50c982
commit 5c75e1fa7d
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
33 changed files with 629 additions and 531 deletions

View file

@ -1,19 +1,24 @@
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class AddCategoryCommand : ApplicationCommandModule public class AddCategoryCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("addcategory", "Adds a category to the ticket bot letting users open tickets in them.")] [Command("addcategory")]
public async Task OnExecute(InteractionContext command, [Option("Title", "The name to display on buttons and in selection boxes.")] string title, [Option("Category", "The category to add.")] DiscordChannel category) [Description("Adds a category to the ticket bot letting users open tickets in them.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("title")][Description("The name to display on buttons and in selection boxes.")] string title,
[Parameter("category")][Description("The category to add.")] DiscordChannel category)
{ {
if (!category.IsCategory) if (!category.IsCategory)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "That channel is not a category." Description = "That channel is not a category."
@ -23,7 +28,7 @@ public class AddCategoryCommand : ApplicationCommandModule
if (string.IsNullOrWhiteSpace(title)) if (string.IsNullOrWhiteSpace(title))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Invalid category title specified." Description = "Invalid category title specified."
@ -33,7 +38,7 @@ public class AddCategoryCommand : ApplicationCommandModule
if (Database.TryGetCategory(category.Id, out Database.Category _)) if (Database.TryGetCategory(category.Id, out Database.Category _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "That category is already registered." Description = "That category is already registered."
@ -43,7 +48,7 @@ public class AddCategoryCommand : ApplicationCommandModule
if (Database.TryGetCategory(title, out Database.Category _)) if (Database.TryGetCategory(title, out Database.Category _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "There is already a category with that title." Description = "There is already a category with that title."
@ -53,7 +58,7 @@ public class AddCategoryCommand : ApplicationCommandModule
if (Database.AddCategory(title, category.Id)) if (Database.AddCategory(title, category.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Category added." Description = "Category added."
@ -61,7 +66,7 @@ public class AddCategoryCommand : ApplicationCommandModule
} }
else else
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed adding the category to the database." Description = "Error: Failed adding the category to the database."

View file

@ -1,22 +1,25 @@
using System; using System;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus; using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class AddCommand : ApplicationCommandModule public class AddCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("add", "Adds a user to a ticket")] [Command("add")]
public async Task OnExecute(InteractionContext command, [Option("User", "User to add to ticket.")] DiscordUser user) [Description("Adds a user to this ticket.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("User to add to ticket.")] DiscordUser user)
{ {
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.IsOpenTicket(command.Channel.Id)) if (!Database.IsOpenTicket(command.Channel.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
@ -27,11 +30,12 @@ public class AddCommand : ApplicationCommandModule
DiscordMember member; DiscordMember member;
try try
{ {
// TODO: This throws an exception instead of returning null now
member = (user == null ? command.Member : await command.Guild.GetMemberAsync(user.Id)); member = (user == null ? command.Member : await command.Guild.GetMemberAsync(user.Id));
if (member == null) if (member == null)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not find that user in this server." Description = "Could not find that user in this server."
@ -41,7 +45,7 @@ public class AddCommand : ApplicationCommandModule
} }
catch (Exception) catch (Exception)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not find that user in this server." Description = "Could not find that user in this server."
@ -51,28 +55,30 @@ public class AddCommand : ApplicationCommandModule
try try
{ {
await command.Channel.AddOverwriteAsync(member, Permissions.AccessChannels); await command.Channel.AddOverwriteAsync(member, DiscordPermissions.AccessChannels);
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Added " + member.Mention + " to ticket." Description = "Added " + member.Mention + " to ticket."
}); });
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = member.Mention + " was added to " + command.Channel.Mention + Description = member.Mention + " was added to " + command.Channel.Mention +
" by " + command.Member.Mention + "." " by " + command.Member?.Mention + "."
}); });
} }
} }
catch (Exception) catch (Exception)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not add " + member.Mention + " to ticket, unknown error occured." Description = "Could not add " + member.Mention + " to ticket, unknown error occured."

View file

@ -1,21 +1,24 @@
using DSharpPlus.Entities; using System.ComponentModel;
using DSharpPlus.Entities;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.SlashCommands; using DSharpPlus.Commands;
using DSharpPlus.SlashCommands.Attributes; using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class AddMessageCommand : ApplicationCommandModule public class AddMessageCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("addmessage", "Adds a new message for the 'say' command.")] [Command("addmessage")]
public async Task OnExecute(InteractionContext command, [Description("Adds a new message for the 'say' command.")]
[Option("Identifier", "The identifier word used in the /say command.")] string identifier, public async Task OnExecute(SlashCommandContext command,
[Option("Message", "The message the /say command will return.")] string message) [Parameter("identifier")][Description("The identifier word used in the /say command.")] string identifier,
[Parameter("message")][Description("The message the /say command will return.")] string message)
{ {
if (string.IsNullOrEmpty(message)) if (string.IsNullOrEmpty(message))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "No message specified." Description = "No message specified."
@ -25,7 +28,7 @@ public class AddMessageCommand : ApplicationCommandModule
if (Database.TryGetMessage(identifier.ToLower(), out Database.Message _)) if (Database.TryGetMessage(identifier.ToLower(), out Database.Message _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "There is already a message with that identifier." Description = "There is already a message with that identifier."
@ -35,7 +38,7 @@ public class AddMessageCommand : ApplicationCommandModule
if (Database.AddMessage(identifier, command.Member.Id, message)) if (Database.AddMessage(identifier, command.Member.Id, message))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Message added." Description = "Message added."
@ -43,7 +46,7 @@ public class AddMessageCommand : ApplicationCommandModule
} }
else else
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed adding the message to the database." Description = "Error: Failed adding the message to the database."

View file

@ -1,17 +1,21 @@
using System; using System;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
using MySqlConnector; using MySqlConnector;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class AddStaffCommand : ApplicationCommandModule public class AddStaffCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("addstaff", "Adds a new staff member.")] [Command("addstaff")]
public async Task OnExecute(InteractionContext command, [Option("User", "User to add to staff.")] DiscordUser user) [Description("Adds a new staff member.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("User to add to staff.")] DiscordUser user)
{ {
DiscordMember staffMember = null; DiscordMember staffMember = null;
try try
@ -20,7 +24,7 @@ public class AddStaffCommand : ApplicationCommandModule
if (staffMember == null) if (staffMember == null)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not find that user in this server." Description = "Could not find that user in this server."
@ -30,7 +34,7 @@ public class AddStaffCommand : ApplicationCommandModule
} }
catch (Exception) catch (Exception)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not find that user in this server." Description = "Could not find that user in this server."
@ -47,14 +51,16 @@ public class AddStaffCommand : ApplicationCommandModule
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
cmd.Dispose(); cmd.Dispose();
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = staffMember.Mention + " was added to staff." Description = staffMember.Mention + " was added to staff."
}); });
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder

View file

@ -1,25 +1,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
[SlashCommandGroup("admin", "Administrative commands.")] [Command("admin")]
public class AdminCommands : ApplicationCommandModule [Description("Administrative commands.")]
public class AdminCommands
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("listinvalid", "List tickets which channels have been deleted. Use /admin unsetticket <id> to remove them.")] [Command("listinvalid")]
public async Task ListInvalid(InteractionContext command) [Description("List tickets which channels have been deleted. Use /admin unsetticket <id> to remove them.")]
public async Task ListInvalid(SlashCommandContext command)
{ {
if (!Database.TryGetOpenTickets(out List<Database.Ticket> openTickets)) if (!Database.TryGetOpenTickets(out List<Database.Ticket> openTickets))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not get any open tickets from database." Description = "Could not get any open tickets from database."
@ -28,7 +32,7 @@ public class AdminCommands : ApplicationCommandModule
// Get all channels in all guilds the bot is part of // Get all channels in all guilds the bot is part of
List<DiscordChannel> allChannels = new List<DiscordChannel>(); List<DiscordChannel> allChannels = new List<DiscordChannel>();
foreach (KeyValuePair<ulong, DiscordGuild> guild in SupportChild.discordClient.Guilds) foreach (KeyValuePair<ulong, DiscordGuild> guild in SupportChild.client.Guilds)
{ {
try try
{ {
@ -49,7 +53,7 @@ public class AdminCommands : ApplicationCommandModule
if (listItems.Count == 0) if (listItems.Count == 0)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "All tickets are valid!" Description = "All tickets are valid!"
@ -86,14 +90,16 @@ public class AdminCommands : ApplicationCommandModule
await command.Interaction.SendPaginatedResponseAsync(true, command.User, listPages); await command.Interaction.SendPaginatedResponseAsync(true, command.User, listPages);
} }
[SlashRequireGuild] [RequireGuild]
[SlashCommand("setticket", "Turns a channel into a ticket WARNING: Anyone will be able to delete the channel using /close.")] [Command("setticket")]
public async Task SetTicket(InteractionContext command, [Option("User", "(Optional) The owner of the ticket.")] DiscordUser user = null) [Description("Turns a channel into a ticket. WARNING: Anyone will be able to delete the channel using /close.")]
public async Task SetTicket(SlashCommandContext command,
[Parameter("user")][Description("(Optional) The owner of the ticket.")] DiscordUser user = null)
{ {
// Check if ticket exists in the database // Check if ticket exists in the database
if (Database.IsOpenTicket(command.Channel.Id)) if (Database.IsOpenTicket(command.Channel.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is already a ticket." Description = "This channel is already a ticket."
@ -105,14 +111,16 @@ public class AdminCommands : ApplicationCommandModule
long id = Database.NewTicket(ticketUser.Id, 0, command.Channel.Id); long id = Database.NewTicket(ticketUser.Id, 0, command.Channel.Id);
string ticketID = id.ToString("00000"); string ticketID = id.ToString("00000");
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Channel has been designated ticket " + ticketID + "." Description = "Channel has been designated ticket " + ticketID + "."
}); });
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder
@ -123,9 +131,11 @@ public class AdminCommands : ApplicationCommandModule
} }
} }
[SlashRequireGuild] [RequireGuild]
[SlashCommand("unsetticket", "Deletes a ticket from the ticket system without deleting the channel.")] [Command("unsetticket")]
public async Task UnsetTicket(InteractionContext command, [Option("TicketID", "(Optional) Ticket to unset. Uses the channel you are in by default.")] long ticketID = 0) [Description("Deletes a ticket from the ticket system without deleting the channel.")]
public async Task UnsetTicket(SlashCommandContext command,
[Parameter("ticket-id")][Description("(Optional) Ticket to unset. Uses the channel you are in by default.")] long ticketID = 0)
{ {
Database.Ticket ticket; Database.Ticket ticket;
@ -134,7 +144,7 @@ public class AdminCommands : ApplicationCommandModule
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out ticket)) if (!Database.TryGetOpenTicket(command.Channel.Id, out ticket))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket!" Description = "This channel is not a ticket!"
@ -147,7 +157,7 @@ public class AdminCommands : ApplicationCommandModule
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicketByID((uint)ticketID, out ticket)) if (!Database.TryGetOpenTicketByID((uint)ticketID, out ticket))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "There is no ticket with this ticket ID." Description = "There is no ticket with this ticket ID."
@ -159,14 +169,16 @@ public class AdminCommands : ApplicationCommandModule
if (Database.DeleteOpenTicket(ticket.id)) if (Database.DeleteOpenTicket(ticket.id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Channel has been undesignated as a ticket." Description = "Channel has been undesignated as a ticket."
}); });
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder
@ -178,7 +190,7 @@ public class AdminCommands : ApplicationCommandModule
} }
else else
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed removing ticket from database." Description = "Error: Failed removing ticket from database."
@ -186,10 +198,11 @@ public class AdminCommands : ApplicationCommandModule
} }
} }
[SlashCommand("reload", "Reloads the bot config.")] [Command("reload")]
public async Task Reload(InteractionContext command) [Description("Reloads the bot config.")]
public async Task Reload(SlashCommandContext command)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Reloading bot application..." Description = "Reloading bot application..."

View file

@ -1,17 +1,21 @@
using System; using System;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class AssignCommand : ApplicationCommandModule public class AssignCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("assign", "Assigns a staff member to this ticket.")] [Command("assign")]
public async Task OnExecute(InteractionContext command, [Option("User", "(Optional) User to assign to this ticket.")] DiscordUser user = null) [Description("Assigns a staff member to this ticket.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("(Optional) User to assign to this ticket.")] DiscordUser user = null)
{ {
DiscordMember member = null; DiscordMember member = null;
try try
@ -20,7 +24,7 @@ public class AssignCommand : ApplicationCommandModule
if (member == null) if (member == null)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not find that user in this server." Description = "Could not find that user in this server."
@ -30,7 +34,7 @@ public class AssignCommand : ApplicationCommandModule
} }
catch (Exception) catch (Exception)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not find that user in this server." Description = "Could not find that user in this server."
@ -41,7 +45,7 @@ public class AssignCommand : ApplicationCommandModule
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
@ -51,7 +55,7 @@ public class AssignCommand : ApplicationCommandModule
if (!Database.IsStaff(member.Id)) if (!Database.IsStaff(member.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: User is not registered as staff." Description = "Error: User is not registered as staff."
@ -61,7 +65,7 @@ public class AssignCommand : ApplicationCommandModule
if (!Database.AssignStaff(ticket, member.Id)) if (!Database.AssignStaff(ticket, member.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed to assign " + member.Mention + " to ticket." Description = "Error: Failed to assign " + member.Mention + " to ticket."
@ -69,7 +73,7 @@ public class AssignCommand : ApplicationCommandModule
return; return;
} }
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Assigned " + member.Mention + " to ticket." Description = "Assigned " + member.Mention + " to ticket."
@ -88,8 +92,9 @@ public class AssignCommand : ApplicationCommandModule
catch (UnauthorizedException) { } catch (UnauthorizedException) { }
} }
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder

View file

@ -1,22 +1,26 @@
using System; using System;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class BlacklistCommand : ApplicationCommandModule public class BlacklistCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("blacklist", "Blacklists a user from opening tickets.")] [Command("blacklist")]
public async Task OnExecute(InteractionContext command, [Option("User", "User to blacklist.")] DiscordUser user) [Description("Blacklists a user from opening tickets.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("User to blacklist.")] DiscordUser user)
{ {
try try
{ {
if (!Database.Blacklist(user.Id, command.User.Id)) if (!Database.Blacklist(user.Id, command.User.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = user.Mention + " is already blacklisted." Description = user.Mention + " is already blacklisted."
@ -24,14 +28,15 @@ public class BlacklistCommand : ApplicationCommandModule
return; return;
} }
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Blacklisted " + user.Mention + "." Description = "Blacklisted " + user.Mention + "."
}, true); }, true);
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder
@ -43,7 +48,7 @@ public class BlacklistCommand : ApplicationCommandModule
} }
catch (Exception) catch (Exception)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error occured while blacklisting " + user.Mention + "." Description = "Error occured while blacklisting " + user.Mention + "."

View file

@ -1,27 +1,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus; using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class CloseCommand : ApplicationCommandModule public class CloseCommand
{ {
private static Dictionary<ulong, string> closeReasons = new Dictionary<ulong, string>(); private static Dictionary<ulong, string> closeReasons = new Dictionary<ulong, string>();
[SlashRequireGuild] [RequireGuild]
[SlashCommand("close", "Closes a ticket.")] [Command("close")]
public async Task OnExecute(InteractionContext command, [Option("Reason", "(Optional) Reason for closing the ticket.")] string reason = "") [Description("Closes this ticket.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("reason")][Description("(Optional) The reason for closing this ticket.")] string reason = "")
{ {
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket _)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
@ -35,15 +38,15 @@ public class CloseCommand : ApplicationCommandModule
Color = DiscordColor.Cyan, Color = DiscordColor.Cyan,
Description = "Are you sure you wish to close this ticket? You cannot re-open it again later." Description = "Are you sure you wish to close this ticket? You cannot re-open it again later."
}) })
.AddComponents(new DiscordButtonComponent(ButtonStyle.Danger, "supportchild_closeconfirm", "Confirm")); .AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Danger, "supportboi_closeconfirm", "Confirm"));
await command.CreateResponseAsync(confirmation); await command.RespondAsync(confirmation);
closeReasons.Add(command.Channel.Id, reason); closeReasons.Add(command.Channel.Id, reason);
} }
public static async Task OnConfirmed(DiscordInteraction interaction) public static async Task OnConfirmed(DiscordInteraction interaction)
{ {
await interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
ulong channelID = interaction.Channel.Id; ulong channelID = interaction.Channel.Id;
string channelName = interaction.Channel.Name; string channelName = interaction.Channel.Name;
@ -80,8 +83,10 @@ public class CloseCommand : ApplicationCommandModule
closeReason = "\nReason: " + cachedReason + "\n"; closeReason = "\nReason: " + cachedReason + "\n";
} }
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = interaction.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await interaction.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
DiscordEmbed embed = new DiscordEmbedBuilder DiscordEmbed embed = new DiscordEmbedBuilder
@ -112,6 +117,7 @@ public class CloseCommand : ApplicationCommandModule
try try
{ {
// TODO: This throws an exception instead of returning null now
DiscordMember staffMember = await interaction.Guild.GetMemberAsync(ticket.creatorID); DiscordMember staffMember = await interaction.Guild.GetMemberAsync(ticket.creatorID);
await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read); await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read);

View file

@ -1,28 +1,30 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus; using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class CreateButtonPanelCommand : ApplicationCommandModule public class CreateButtonPanelCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("createbuttonpanel", "Creates a series of buttons which users can use to open new tickets in specific categories.")] [Command("createbuttonpanel")]
public async Task OnExecute(InteractionContext command) [Description("Creates a series of buttons which users can use to open new tickets in specific categories.")]
public async Task OnExecute(SlashCommandContext command)
{ {
DiscordMessageBuilder builder = new DiscordMessageBuilder().WithContent(" "); DiscordMessageBuilder builder = new DiscordMessageBuilder().WithContent(" ");
List<Database.Category> verifiedCategories = await Utilities.GetVerifiedChannels(); List<Database.Category> verifiedCategories = await Utilities.GetVerifiedChannels();
if (verifiedCategories.Count == 0) if (verifiedCategories.Count == 0)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: No registered categories found." Description = "Error: No registered categories found. You have to use /addcategory first."
}, true); }, true);
return; return;
} }
@ -36,13 +38,13 @@ public class CreateButtonPanelCommand : ApplicationCommandModule
for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < verifiedCategories.Count; nrOfButtons++) for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < verifiedCategories.Count; nrOfButtons++)
{ {
buttonRow.Add(new DiscordButtonComponent(ButtonStyle.Primary, "supportchild_newticketbutton " + verifiedCategories[nrOfButtons].id, verifiedCategories[nrOfButtons].name)); buttonRow.Add(new DiscordButtonComponent(DiscordButtonStyle.Primary, "supportchild_newticketbutton " + verifiedCategories[nrOfButtons].id, verifiedCategories[nrOfButtons].name));
} }
builder.AddComponents(buttonRow); builder.AddComponents(buttonRow);
} }
await command.Channel.SendMessageAsync(builder); await command.Channel.SendMessageAsync(builder);
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Successfully created message, make sure to run this command again if you add new categories to the bot." Description = "Successfully created message, make sure to run this command again if you add new categories to the bot."
@ -51,7 +53,7 @@ public class CreateButtonPanelCommand : ApplicationCommandModule
public static async Task OnButtonUsed(DiscordInteraction interaction) public static async Task OnButtonUsed(DiscordInteraction interaction)
{ {
await interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral());
if (!ulong.TryParse(interaction.Data.CustomId.Replace("supportchild_newticketbutton ", ""), out ulong categoryID) || categoryID == 0) if (!ulong.TryParse(interaction.Data.CustomId.Replace("supportchild_newticketbutton ", ""), out ulong categoryID) || categoryID == 0)
{ {

View file

@ -1,32 +1,35 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus; using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class CreateSelectionBoxPanelCommand : ApplicationCommandModule public class CreateSelectionBoxPanelCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("createselectionboxpanel", "Creates a selection box which users can use to open new tickets in specific categories.")] [Command("createselectionboxpanel")]
public async Task OnExecute(InteractionContext command, [Option("Message", "(Optional) The message to show in the selection box.")] string message = null) [Description("Creates a selection box which users can use to open new tickets in specific categories.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("message")][Description("(Optional) The message to show in the selection box.")] string message = null)
{ {
DiscordMessageBuilder builder = new DiscordMessageBuilder() DiscordMessageBuilder builder = new DiscordMessageBuilder()
.WithContent(" ") .WithContent(" ")
.AddComponents(await GetSelectComponents(command, message ?? "Open new ticket...")); .AddComponents(await GetSelectComponents(command, message ?? "Open new ticket..."));
await command.Channel.SendMessageAsync(builder); await command.Channel.SendMessageAsync(builder);
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Successfully created message, make sure to run this command again if you add new categories to the bot." Description = "Successfully created message, make sure to run this command again if you add new categories to the bot."
}, true); }, true);
} }
public static async Task<List<DiscordSelectComponent>> GetSelectComponents(InteractionContext command, string placeholder) public static async Task<List<DiscordSelectComponent>> GetSelectComponents(SlashCommandContext command, string placeholder)
{ {
List<Database.Category> verifiedCategories = await Utilities.GetVerifiedChannels(); List<Database.Category> verifiedCategories = await Utilities.GetVerifiedChannels();
@ -65,7 +68,7 @@ public class CreateSelectionBoxPanelCommand : ApplicationCommandModule
return; return;
} }
await interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral());
(bool success, string message) = await NewCommand.OpenNewTicket(interaction.User.Id, interaction.ChannelId, categoryID); (bool success, string message) = await NewCommand.OpenNewTicket(interaction.User.Id, interaction.ChannelId, categoryID);

View file

@ -1,24 +1,28 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class ListAssignedCommand : ApplicationCommandModule public class ListAssignedCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("listassigned", "Lists tickets assigned to a user.")] [Command("listassigned")]
public async Task OnExecute(InteractionContext command, [Option("User", "(Optional) User to list tickets for.")] DiscordUser user = null) [Description("Lists tickets assigned to a user.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("(Optional) User to list tickets for.")] DiscordUser user = null)
{ {
DiscordUser listUser = user == null ? command.User : user; DiscordUser listUser = user == null ? command.User : user;
if (!Database.TryGetAssignedTickets(listUser.Id, out List<Database.Ticket> assignedTickets)) if (!Database.TryGetAssignedTickets(listUser.Id, out List<Database.Ticket> assignedTickets))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "User does not have any assigned tickets." Description = "User does not have any assigned tickets."

View file

@ -1,18 +1,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class ListCommand : ApplicationCommandModule public class ListCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("list", "Lists tickets opened by a user.")] [Command("list")]
public async Task OnExecute(InteractionContext command, [Option("User", "(Optional) The user to get tickets by.")] DiscordUser user = null) [Description("Lists tickets opened by a user.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("(Optional) The user to get tickets by.")] DiscordUser user = null)
{ {
DiscordUser listUser = user == null ? command.User : user; DiscordUser listUser = user == null ? command.User : user;
@ -80,7 +84,7 @@ public class ListCommand : ApplicationCommandModule
if (embeds.Count == 0) if (embeds.Count == 0)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Cyan, Color = DiscordColor.Cyan,
Description = "User does not have any open or closed tickets." Description = "User does not have any open or closed tickets."

View file

@ -1,22 +1,25 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class ListOpen : ApplicationCommandModule public class ListOpen
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("listopen", "Lists all open tickets, oldest first.")] [Command("listopen")]
public async Task OnExecute(InteractionContext command) [Description("Lists all open tickets, oldest first.")]
public async Task OnExecute(SlashCommandContext command)
{ {
if (!Database.TryGetOpenTickets(out List<Database.Ticket> openTickets)) if (!Database.TryGetOpenTickets(out List<Database.Ticket> openTickets))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Could not fetch any open tickets." Description = "Could not fetch any open tickets."

View file

@ -1,22 +1,25 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class ListUnassignedCommand : ApplicationCommandModule public class ListUnassignedCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("listunassigned", "Lists unassigned tickets.")] [Command("listunassigned")]
public async Task OnExecute(InteractionContext command) [Description("Lists unassigned tickets.")]
public async Task OnExecute(SlashCommandContext command)
{ {
if (!Database.TryGetAssignedTickets(0, out List<Database.Ticket> unassignedTickets)) if (!Database.TryGetAssignedTickets(0, out List<Database.Ticket> unassignedTickets))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "There are no unassigned tickets." Description = "There are no unassigned tickets."

View file

@ -1,24 +1,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class MoveCommand : ApplicationCommandModule public class MoveCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("move", "Moves a ticket to another category.")] [Command("move")]
public async Task OnExecute(InteractionContext command, [Option("Category", "The category to move the ticket to. Only has to be the beginning of the name.")] string category) [Description("Moves a ticket to another category.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("category")][Description("The category to move the ticket to. Only has to be the beginning of the name.")] string category)
{ {
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket _)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
@ -28,7 +32,7 @@ public class MoveCommand : ApplicationCommandModule
if (string.IsNullOrEmpty(category)) if (string.IsNullOrEmpty(category))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: No category provided." Description = "Error: No category provided."
@ -42,7 +46,7 @@ public class MoveCommand : ApplicationCommandModule
if (categoryChannel == null) if (categoryChannel == null)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Could not find a category by that name." Description = "Error: Could not find a category by that name."
@ -52,7 +56,7 @@ public class MoveCommand : ApplicationCommandModule
if (command.Channel.Id == categoryChannel.Id) if (command.Channel.Id == categoryChannel.Id)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: The ticket is already in that category." Description = "Error: The ticket is already in that category."
@ -66,7 +70,7 @@ public class MoveCommand : ApplicationCommandModule
} }
catch (UnauthorizedException) catch (UnauthorizedException)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Not authorized to move this ticket to that category." Description = "Error: Not authorized to move this ticket to that category."
@ -74,7 +78,7 @@ public class MoveCommand : ApplicationCommandModule
return; return;
} }
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Ticket was moved to " + categoryChannel.Mention Description = "Ticket was moved to " + categoryChannel.Mention

View file

@ -1,50 +1,52 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus; using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class NewCommand : ApplicationCommandModule public class NewCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("new", "Opens a new ticket.")] [Command("new")]
public async Task OnExecute(InteractionContext command) [Description("Opens a new ticket.")]
public async Task OnExecute(SlashCommandContext command)
{ {
List<Database.Category> verifiedCategories = await Utilities.GetVerifiedChannels(); List<Database.Category> verifiedCategories = await Utilities.GetVerifiedChannels();
switch (verifiedCategories.Count) switch (verifiedCategories.Count)
{ {
case 0: case 0:
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: No registered categories found." Description = "Error: No registered categories found."
}, true); }, true);
return; return;
case 1: case 1:
await command.DeferAsync(true); await command.DeferResponseAsync(true);
(bool success, string message) = await OpenNewTicket(command.User.Id, command.Channel.Id, verifiedCategories[0].id); (bool success, string message) = await OpenNewTicket(command.User.Id, command.Channel.Id, verifiedCategories[0].id);
if (success) if (success)
{ {
await command.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(new DiscordEmbedBuilder await command.FollowupAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = message Description = message
}).AsEphemeral()); }, true);
} }
else else
{ {
await command.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(new DiscordEmbedBuilder await command.FollowupAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = message Description = message
}).AsEphemeral()); }, true);
} }
return; return;
default: default:
@ -60,7 +62,7 @@ public class NewCommand : ApplicationCommandModule
} }
} }
public static async Task CreateButtons(InteractionContext command, List<Database.Category> verifiedCategories) public static async Task CreateButtons(SlashCommandContext command, List<Database.Category> verifiedCategories)
{ {
DiscordInteractionResponseBuilder builder = new DiscordInteractionResponseBuilder().WithContent(" "); DiscordInteractionResponseBuilder builder = new DiscordInteractionResponseBuilder().WithContent(" ");
@ -71,15 +73,15 @@ public class NewCommand : ApplicationCommandModule
for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < verifiedCategories.Count; nrOfButtons++) for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < verifiedCategories.Count; nrOfButtons++)
{ {
buttonRow.Add(new DiscordButtonComponent(ButtonStyle.Primary, "supportchild_newcommandbutton " + verifiedCategories[nrOfButtons].id, verifiedCategories[nrOfButtons].name)); buttonRow.Add(new DiscordButtonComponent(DiscordButtonStyle.Primary, "supportchild_newcommandbutton " + verifiedCategories[nrOfButtons].id, verifiedCategories[nrOfButtons].name));
} }
builder.AddComponents(buttonRow); builder.AddComponents(buttonRow);
} }
await command.CreateResponseAsync(builder.AsEphemeral()); await command.RespondAsync(builder.AsEphemeral());
} }
public static async Task CreateSelector(InteractionContext command, List<Database.Category> verifiedCategories) public static async Task CreateSelector(SlashCommandContext command, List<Database.Category> verifiedCategories)
{ {
verifiedCategories = verifiedCategories.OrderBy(x => x.name).ToList(); verifiedCategories = verifiedCategories.OrderBy(x => x.name).ToList();
List<DiscordSelectComponent> selectionComponents = new List<DiscordSelectComponent>(); List<DiscordSelectComponent> selectionComponents = new List<DiscordSelectComponent>();
@ -96,7 +98,7 @@ public class NewCommand : ApplicationCommandModule
selectionComponents.Add(new DiscordSelectComponent("supportchild_newcommandselector" + selectionBoxes, "Open new ticket...", categoryOptions, false, 0, 1)); selectionComponents.Add(new DiscordSelectComponent("supportchild_newcommandselector" + selectionBoxes, "Open new ticket...", categoryOptions, false, 0, 1));
} }
await command.CreateResponseAsync(new DiscordInteractionResponseBuilder().AddComponents(selectionComponents).AsEphemeral()); await command.RespondAsync(new DiscordInteractionResponseBuilder().AddComponents(selectionComponents).AsEphemeral());
} }
public static async Task OnCategorySelection(DiscordInteraction interaction) public static async Task OnCategorySelection(DiscordInteraction interaction)
@ -104,10 +106,10 @@ public class NewCommand : ApplicationCommandModule
string stringID; string stringID;
switch (interaction.Data.ComponentType) switch (interaction.Data.ComponentType)
{ {
case ComponentType.Button: case DiscordComponentType.Button:
stringID = interaction.Data.CustomId.Replace("supportchild_newcommandbutton ", ""); stringID = interaction.Data.CustomId.Replace("supportchild_newcommandbutton ", "");
break; break;
case ComponentType.StringSelect: case DiscordComponentType.StringSelect:
if (interaction.Data.Values == null || interaction.Data.Values.Length <= 0) if (interaction.Data.Values == null || interaction.Data.Values.Length <= 0)
{ {
return; return;
@ -115,8 +117,8 @@ public class NewCommand : ApplicationCommandModule
stringID = interaction.Data.Values[0]; stringID = interaction.Data.Values[0];
break; break;
case ComponentType.ActionRow: case DiscordComponentType.ActionRow:
case ComponentType.FormInput: case DiscordComponentType.FormInput:
default: default:
return; return;
} }
@ -126,7 +128,7 @@ public class NewCommand : ApplicationCommandModule
return; return;
} }
await interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate, new DiscordInteractionResponseBuilder().AsEphemeral()); await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate, new DiscordInteractionResponseBuilder().AsEphemeral());
(bool success, string message) = await OpenNewTicket(interaction.User.Id, interaction.ChannelId, categoryID); (bool success, string message) = await OpenNewTicket(interaction.User.Id, interaction.ChannelId, categoryID);
@ -161,9 +163,7 @@ public class NewCommand : ApplicationCommandModule
return (false, "You cannot use this command in a ticket channel."); return (false, "You cannot use this command in a ticket channel.");
} }
if (!Database.IsStaff(userID) if (!Database.IsStaff(userID) && Database.TryGetOpenTickets(userID, out List<Database.Ticket> ownTickets) && ownTickets.Count >= Config.ticketLimit)
&& Database.TryGetOpenTickets(userID, out List<Database.Ticket> ownTickets)
&& (ownTickets.Count >= Config.ticketLimit && Config.ticketLimit != 0))
{ {
return (false, "You have reached the limit for maximum open tickets."); return (false, "You have reached the limit for maximum open tickets.");
} }
@ -171,7 +171,7 @@ public class NewCommand : ApplicationCommandModule
DiscordChannel category = null; DiscordChannel category = null;
try try
{ {
category = await SupportChild.discordClient.GetChannelAsync(categoryID); category = await SupportChild.client.GetChannelAsync(categoryID);
} }
catch (Exception) { /*ignored*/ } catch (Exception) { /*ignored*/ }
@ -196,7 +196,7 @@ public class NewCommand : ApplicationCommandModule
try try
{ {
ticketChannel = await category.Guild.CreateChannelAsync("ticket", ChannelType.Text, category); ticketChannel = await category.Guild.CreateChannelAsync("ticket", DiscordChannelType.Text, category);
} }
catch (Exception) { /* ignored */ } catch (Exception) { /* ignored */ }
@ -227,7 +227,7 @@ public class NewCommand : ApplicationCommandModule
try try
{ {
await ticketChannel.AddOverwriteAsync(member, Permissions.AccessChannels); await ticketChannel.AddOverwriteAsync(member, DiscordPermissions.AccessChannels);
} }
catch (DiscordException e) catch (DiscordException e)
{ {
@ -238,12 +238,7 @@ public class NewCommand : ApplicationCommandModule
await ticketChannel.SendMessageAsync("Hello, " + member.Mention + "!\n" + Config.welcomeMessage); await ticketChannel.SendMessageAsync("Hello, " + member.Mention + "!\n" + Config.welcomeMessage);
// Refreshes the channel as changes were made to it above // Refreshes the channel as changes were made to it above
ticketChannel = await SupportChild.discordClient.GetChannelAsync(ticketChannel.Id); ticketChannel = await SupportChild.client.GetChannelAsync(ticketChannel.Id);
if (ticketChannel.Parent?.IsCategory ?? false)
{
Interviewer.StartInterview(ticketChannel);
}
if (staffID != 0) if (staffID != 0)
{ {
@ -273,8 +268,10 @@ public class NewCommand : ApplicationCommandModule
} }
} }
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = category.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await category.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
DiscordEmbed logMessage = new DiscordEmbedBuilder DiscordEmbed logMessage = new DiscordEmbedBuilder

View file

@ -1,25 +1,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class RandomAssignCommand : ApplicationCommandModule public class RandomAssignCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("rassign", "Randomly assigns a staff member to a ticket.")] [Command("rassign")]
public async Task OnExecute(InteractionContext command, [Option("Role", "(Optional) Limit the random assignment to a specific role.")] DiscordRole role = null) [Description("Randomly assigns a staff member to a ticket.")]
public async Task OnExecute(SlashCommandContext command, [Parameter("role")][Description("(Optional) Limit the random assignment to a specific role.")] DiscordRole role = null)
{ {
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: This channel is not a ticket." Description = "Error: This channel is not a ticket."
@ -37,7 +40,7 @@ public class RandomAssignCommand : ApplicationCommandModule
// Attempt to assign the staff member to the ticket // Attempt to assign the staff member to the ticket
if (!Database.AssignStaff(ticket, staffMember.Id)) if (!Database.AssignStaff(ticket, staffMember.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed to assign " + staffMember.Mention + " to ticket." Description = "Error: Failed to assign " + staffMember.Mention + " to ticket."
@ -46,7 +49,7 @@ public class RandomAssignCommand : ApplicationCommandModule
} }
// Respond that the command was successful // Respond that the command was successful
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Randomly assigned " + staffMember.Mention + " to ticket." Description = "Randomly assigned " + staffMember.Mention + " to ticket."
@ -66,8 +69,9 @@ public class RandomAssignCommand : ApplicationCommandModule
catch (UnauthorizedException) { } catch (UnauthorizedException) { }
} }
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder
@ -78,7 +82,7 @@ public class RandomAssignCommand : ApplicationCommandModule
} }
} }
private static async Task<DiscordMember> GetRandomVerifiedStaffMember(InteractionContext command, DiscordRole targetRole, Database.Ticket ticket) private static async Task<DiscordMember> GetRandomVerifiedStaffMember(SlashCommandContext command, DiscordRole targetRole, Database.Ticket ticket)
{ {
if (targetRole != null) // A role was provided if (targetRole != null) // A role was provided
{ {
@ -112,7 +116,7 @@ public class RandomAssignCommand : ApplicationCommandModule
Database.StaffMember staffEntry = Database.GetRandomActiveStaff(ticket.assignedStaffID, ticket.creatorID); Database.StaffMember staffEntry = Database.GetRandomActiveStaff(ticket.assignedStaffID, ticket.creatorID);
if (staffEntry == null) if (staffEntry == null)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: There are no other staff members to choose from." Description = "Error: There are no other staff members to choose from."
@ -129,7 +133,7 @@ public class RandomAssignCommand : ApplicationCommandModule
} }
// Send a more generic error if we get to this point and still haven't found the staff member // Send a more generic error if we get to this point and still haven't found the staff member
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Could not find an applicable staff member." Description = "Error: Could not find an applicable staff member."

View file

@ -1,19 +1,23 @@
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class RemoveCategoryCommand : ApplicationCommandModule public class RemoveCategoryCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("removecategory", "Removes the ability for users to open tickets in a specific category.")] [Command("removecategory")]
public async Task OnExecute(InteractionContext command, [Option("Category", "The category to remove.")] DiscordChannel channel) [Description("Removes the ability for users to open tickets in a specific category.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("category")][Description("The category to remove.")] DiscordChannel channel)
{ {
if (!Database.TryGetCategory(channel.Id, out Database.Category _)) if (!Database.TryGetCategory(channel.Id, out Database.Category _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "That category is not registered." Description = "That category is not registered."
@ -23,7 +27,7 @@ public class RemoveCategoryCommand : ApplicationCommandModule
if (Database.RemoveCategory(channel.Id)) if (Database.RemoveCategory(channel.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Category removed." Description = "Category removed."
@ -31,7 +35,7 @@ public class RemoveCategoryCommand : ApplicationCommandModule
} }
else else
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed removing the category from the database." Description = "Error: Failed removing the category from the database."

View file

@ -1,19 +1,23 @@
using DSharpPlus.Entities; using System.ComponentModel;
using DSharpPlus.Entities;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.SlashCommands; using DSharpPlus.Commands;
using DSharpPlus.SlashCommands.Attributes; using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class RemoveMessageCommand : ApplicationCommandModule public class RemoveMessageCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("removemessage", "Removes a message from the 'say' command.")] [Command("removemessage")]
public async Task OnExecute(InteractionContext command, [Option("Identifier", "The identifier word used in the /say command.")] string identifier) [Description("Removes a message from the 'say' command.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("identifier")][Description("The identifier word used in the /say command.")] string identifier)
{ {
if (!Database.TryGetMessage(identifier.ToLower(), out Database.Message _)) if (!Database.TryGetMessage(identifier.ToLower(), out Database.Message _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "There is no message with that identifier." Description = "There is no message with that identifier."
@ -23,7 +27,7 @@ public class RemoveMessageCommand : ApplicationCommandModule
if (Database.RemoveMessage(identifier)) if (Database.RemoveMessage(identifier))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Message removed." Description = "Message removed."
@ -31,7 +35,7 @@ public class RemoveMessageCommand : ApplicationCommandModule
} }
else else
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed removing the message from the database." Description = "Error: Failed removing the message from the database."

View file

@ -1,20 +1,24 @@
using System.Threading.Tasks; using System.ComponentModel;
using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
using MySqlConnector; using MySqlConnector;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class RemoveStaffCommand : ApplicationCommandModule public class RemoveStaffCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("removestaff", "Removes a staff member.")] [Command("removestaff")]
public async Task OnExecute(InteractionContext command, [Option("User", "User to remove from staff.")] DiscordUser user) [Description("Removes a staff member.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("user")][Description("User to remove from staff.")] DiscordUser user)
{ {
if (!Database.IsStaff(user.Id)) if (!Database.IsStaff(user.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "User is already not registered as staff." Description = "User is already not registered as staff."
@ -26,17 +30,18 @@ public class RemoveStaffCommand : ApplicationCommandModule
c.Open(); c.Open();
MySqlCommand deletion = new MySqlCommand(@"DELETE FROM staff WHERE user_id=@user_id", c); MySqlCommand deletion = new MySqlCommand(@"DELETE FROM staff WHERE user_id=@user_id", c);
deletion.Parameters.AddWithValue("@user_id", user.Id); deletion.Parameters.AddWithValue("@user_id", user.Id);
deletion.Prepare(); await deletion.PrepareAsync();
deletion.ExecuteNonQuery(); deletion.ExecuteNonQuery();
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "User was removed from staff." Description = "User was removed from staff."
}, true); }, true);
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder

View file

@ -1,18 +1,22 @@
using DSharpPlus.Entities; using DSharpPlus.Entities;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class SayCommand : ApplicationCommandModule public class SayCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("say", "Prints a message with information from staff. Use without identifier to list all identifiers.")] [Command("say")]
public async Task OnExecute(InteractionContext command, [Option("Identifier", "(Optional) The identifier word to summon a message.")] string identifier = null) [Description("Prints a message with information from staff. Use without identifier to list all identifiers.")]
public async Task OnExecute(SlashCommandContext command,
[Parameter("Identifier")][Description("(Optional) The identifier word to summon a message.")] string identifier = null)
{ {
// Print list of all messages if no identifier is provided // Print list of all messages if no identifier is provided
if (identifier == null) if (identifier == null)
@ -23,7 +27,7 @@ public class SayCommand : ApplicationCommandModule
if (!Database.TryGetMessage(identifier.ToLower(), out Database.Message message)) if (!Database.TryGetMessage(identifier.ToLower(), out Database.Message message))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "There is no message with that identifier." Description = "There is no message with that identifier."
@ -31,19 +35,19 @@ public class SayCommand : ApplicationCommandModule
return; return;
} }
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Cyan, Color = DiscordColor.Cyan,
Description = message.message.Replace("\\n", "\n") Description = message.message.Replace("\\n", "\n")
}); });
} }
private static async void SendMessageList(InteractionContext command) private static async void SendMessageList(SlashCommandContext command)
{ {
List<Database.Message> messages = Database.GetAllMessages(); List<Database.Message> messages = Database.GetAllMessages();
if (messages.Count == 0) if (messages.Count == 0)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "There are no messages registered." Description = "There are no messages registered."

View file

@ -1,22 +1,25 @@
using System.Threading.Tasks; using System.ComponentModel;
using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
using MySqlConnector; using MySqlConnector;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class SetSummaryCommand : ApplicationCommandModule public class SetSummaryCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("setsummary", "Sets a ticket's summary for the summary command.")] [Command("setsummary")]
public async Task OnExecute(InteractionContext command, [Option("Summary", "The ticket summary text.")] string summary) [Description("Sets a ticket's summary for the summary command.")]
public async Task OnExecute(SlashCommandContext command, [Parameter("Summary")][Description("The ticket summary text.")] string summary)
{ {
ulong channelID = command.Channel.Id; ulong channelID = command.Channel.Id;
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket _)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket _))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
@ -29,11 +32,11 @@ public class SetSummaryCommand : ApplicationCommandModule
MySqlCommand update = new MySqlCommand(@"UPDATE tickets SET summary = @summary WHERE channel_id = @channel_id", c); MySqlCommand update = new MySqlCommand(@"UPDATE tickets SET summary = @summary WHERE channel_id = @channel_id", c);
update.Parameters.AddWithValue("@summary", summary); update.Parameters.AddWithValue("@summary", summary);
update.Parameters.AddWithValue("@channel_id", channelID); update.Parameters.AddWithValue("@channel_id", channelID);
update.Prepare(); await update.PrepareAsync();
update.ExecuteNonQuery(); update.ExecuteNonQuery();
update.Dispose(); update.Dispose();
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Summary set." Description = "Summary set."

View file

@ -1,15 +1,18 @@
using System.Threading.Tasks; using System.ComponentModel;
using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class StatusCommand : ApplicationCommandModule public class StatusCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("status", "Shows bot status and information.")] [Command("status")]
public async Task OnExecute(InteractionContext command) [Description("Shows bot status and information.")]
public async Task OnExecute(SlashCommandContext command)
{ {
long openTickets = Database.GetNumberOfTickets(); long openTickets = Database.GetNumberOfTickets();
long closedTickets = Database.GetNumberOfClosedTickets(); long closedTickets = Database.GetNumberOfClosedTickets();
@ -23,6 +26,6 @@ public class StatusCommand : ApplicationCommandModule
.AddField("Closed tickets:", closedTickets + " ", true) .AddField("Closed tickets:", closedTickets + " ", true)
.AddField("Report bugs:", "[Toastielab Issues](https://toastielab.dev/Emotions-stuff/SupportChild/issues)", true) .AddField("Report bugs:", "[Toastielab Issues](https://toastielab.dev/Emotions-stuff/SupportChild/issues)", true)
.AddField("Commands:", "[Toastielab Repository](https://toastielab.dev/Emotions-stuff/SupportChild)", true); .AddField("Commands:", "[Toastielab Repository](https://toastielab.dev/Emotions-stuff/SupportChild)", true);
await command.CreateResponseAsync(botInfo); await command.RespondAsync(botInfo);
} }
} }

View file

@ -1,35 +1,37 @@
using System.Threading.Tasks; using System.ComponentModel;
using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class SummaryCommand : ApplicationCommandModule public class SummaryCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("summary", "Lists tickets assigned to a user.")] [Command("summary")]
public async Task OnExecute(InteractionContext command) [Description("Lists tickets assigned to a user.")]
public async Task OnExecute(SlashCommandContext command)
{ {
if (Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket))
{ {
DiscordEmbed channelInfo = new DiscordEmbedBuilder() await command.RespondAsync(new DiscordEmbedBuilder
.WithTitle("Channel information")
.WithColor(DiscordColor.Cyan)
.AddField("Ticket number:", ticket.id.ToString("00000"), true)
.AddField("Ticket creator:", $"<@{ticket.creatorID}>", true)
.AddField("Assigned staff:", ticket.assignedStaffID == 0 ? "Unassigned." : $"<@{ticket.assignedStaffID}>", true)
.AddField("Creation time:", ticket.DiscordRelativeTime(), true)
.AddField("Summary:", string.IsNullOrEmpty(ticket.summary) ? "No summary." : ticket.summary.Replace("\\n", "\n"));
await command.CreateResponseAsync(channelInfo);
}
else
{
await command.CreateResponseAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
}, true); }, true);
return;
} }
DiscordEmbed channelInfo = new DiscordEmbedBuilder()
.WithTitle("Channel information")
.WithColor(DiscordColor.Cyan)
.AddField("Ticket number:", ticket.id.ToString("00000"), true)
.AddField("Ticket creator:", $"<@{ticket.creatorID}>", true)
.AddField("Assigned staff:", ticket.assignedStaffID == 0 ? "Unassigned." : $"<@{ticket.assignedStaffID}>", true)
.AddField("Creation time:", ticket.DiscordRelativeTime(), true)
.AddField("Summary:", string.IsNullOrEmpty(ticket.summary) ? "No summary." : ticket.summary.Replace("\\n", "\n"));
await command.RespondAsync(channelInfo);
} }
} }

View file

@ -1,22 +1,25 @@
using System.Threading.Tasks; using System.ComponentModel;
using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class ToggleActiveCommand : ApplicationCommandModule public class ToggleActiveCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("toggleactive", "Toggles active status for a staff member.")] [Command("toggleactive")]
public async Task OnExecute(InteractionContext command, [Option("User", "(Optional) Staff member to toggle activity for.")] DiscordUser user = null) [Description("Toggles active status for a staff member.")]
public async Task OnExecute(SlashCommandContext command, [Parameter("user")][Description("(Optional) Staff member to toggle activity for.")] DiscordUser user = null)
{ {
DiscordUser staffUser = user == null ? command.User : user; DiscordUser staffUser = user == null ? command.User : user;
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetStaff(staffUser.Id, out Database.StaffMember staffMember)) if (!Database.TryGetStaff(staffUser.Id, out Database.StaffMember staffMember))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = user == null ? "You have not been registered as staff." : "The user is not registered as staff." Description = user == null ? "You have not been registered as staff." : "The user is not registered as staff."
@ -26,7 +29,7 @@ public class ToggleActiveCommand : ApplicationCommandModule
if (Database.SetStaffActive(staffUser.Id, !staffMember.active)) if (Database.SetStaffActive(staffUser.Id, !staffMember.active))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = staffMember.active ? "Staff member is now set as inactive and will no longer be randomly assigned any support tickets." : "Staff member is now set as active and will be randomly assigned support tickets again." Description = staffMember.active ? "Staff member is now set as inactive and will no longer be randomly assigned any support tickets." : "Staff member is now set as active and will be randomly assigned support tickets again."
@ -34,7 +37,7 @@ public class ToggleActiveCommand : ApplicationCommandModule
} }
else else
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Unable to update active status in database." Description = "Error: Unable to update active status in database."

View file

@ -1,21 +1,24 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class TranscriptCommand : ApplicationCommandModule public class TranscriptCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("transcript", "Creates a transcript of a ticket.")] [Command("transcript")]
public async Task OnExecute(InteractionContext command, [Option("Ticket", "(Optional) Ticket number to get transcript of.")] long ticketID = 0) [Description("Creates a transcript of a ticket.")]
public async Task OnExecute(SlashCommandContext command, [Parameter("ticket-id")][Description("(Optional) Ticket number to get transcript of.")] long ticketID = 0)
{ {
await command.DeferAsync(true); await command.DeferResponseAsync(true);
Database.Ticket ticket; Database.Ticket ticket;
if (ticketID == 0) // If there are no arguments use current channel if (ticketID == 0) // If there are no arguments use current channel
{ {
@ -76,9 +79,10 @@ public class TranscriptCommand : ApplicationCommandModule
return; return;
} }
} }
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read); await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read);

View file

@ -1,20 +1,23 @@
using System.Threading.Tasks; using System.ComponentModel;
using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class UnassignCommand : ApplicationCommandModule public class UnassignCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("unassign", "Unassigns a staff member from a ticket.")] [Command("unassign")]
public async Task OnExecute(InteractionContext command) [Description("Unassigns a staff member from a ticket.")]
public async Task OnExecute(SlashCommandContext command)
{ {
// Check if ticket exists in the database // Check if ticket exists in the database
if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket)) if (!Database.TryGetOpenTicket(command.Channel.Id, out Database.Ticket ticket))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "This channel is not a ticket." Description = "This channel is not a ticket."
@ -24,7 +27,7 @@ public class UnassignCommand : ApplicationCommandModule
if (!Database.UnassignStaff(ticket)) if (!Database.UnassignStaff(ticket))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error: Failed to unassign staff member from ticket." Description = "Error: Failed to unassign staff member from ticket."
@ -32,14 +35,15 @@ public class UnassignCommand : ApplicationCommandModule
return; return;
} }
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Unassigned staff member from ticket." Description = "Unassigned staff member from ticket."
}); });
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder

View file

@ -1,22 +1,25 @@
using System; using System;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
namespace SupportChild.Commands; namespace SupportChild.Commands;
public class UnblacklistCommand : ApplicationCommandModule public class UnblacklistCommand
{ {
[SlashRequireGuild] [RequireGuild]
[SlashCommand("unblacklist", "Unblacklists a user from opening tickets.")] [Command("unblacklist")]
public async Task OnExecute(InteractionContext command, [Option("User", "User to remove from blacklist.")] DiscordUser user) [Description("Unblacklists a user from opening tickets.")]
public async Task OnExecute(SlashCommandContext command, [Parameter("user")][Description("User to remove from blacklist.")] DiscordUser user)
{ {
try try
{ {
if (!Database.Unblacklist(user.Id)) if (!Database.Unblacklist(user.Id))
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = user.Mention + " is not blacklisted." Description = user.Mention + " is not blacklisted."
@ -24,14 +27,15 @@ public class UnblacklistCommand : ApplicationCommandModule
return; return;
} }
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Removed " + user.Mention + " from blacklist." Description = "Removed " + user.Mention + " from blacklist."
}, true); }, true);
// TODO: This throws an exception instead of returning null now
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = command.Guild.GetChannel(Config.logChannel); DiscordChannel logChannel = await command.Guild.GetChannelAsync(Config.logChannel);
if (logChannel != null) if (logChannel != null)
{ {
await logChannel.SendMessageAsync(new DiscordEmbedBuilder await logChannel.SendMessageAsync(new DiscordEmbedBuilder
@ -43,7 +47,7 @@ public class UnblacklistCommand : ApplicationCommandModule
} }
catch (Exception) catch (Exception)
{ {
await command.CreateResponseAsync(new DiscordEmbedBuilder await command.RespondAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Error occured while removing " + user.Mention + " from blacklist." Description = "Error occured while removing " + user.Mention + " from blacklist."

View file

@ -2,35 +2,37 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DiscordChatExporter.Core.Utils.Extensions;
using DSharpPlus; using DSharpPlus;
using DSharpPlus.Commands;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.EventArgs;
using DSharpPlus.Commands.Exceptions;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using DSharpPlus.EventArgs; using DSharpPlus.EventArgs;
using DSharpPlus.Exceptions; using DSharpPlus.Exceptions;
using DSharpPlus.SlashCommands;
using DSharpPlus.SlashCommands.Attributes;
using DSharpPlus.SlashCommands.EventArgs;
using SupportChild.Commands; using SupportChild.Commands;
namespace SupportChild; namespace SupportChild;
internal static class EventHandler public static class EventHandler
{ {
internal static Task OnReady(DiscordClient client, ReadyEventArgs e) public static Task OnReady(DiscordClient client, GuildDownloadCompletedEventArgs e)
{ {
Logger.Log("Client is ready to process events."); Logger.Log("Connected to Discord.");
// Checking activity type // Checking activity type
if (!Enum.TryParse(Config.presenceType, true, out ActivityType activityType)) if (!Enum.TryParse(Config.presenceType, true, out DiscordActivityType activityType))
{ {
Logger.Log("Presence type '" + Config.presenceType + "' invalid, using 'Playing' instead."); Logger.Log("Presence type '" + Config.presenceType + "' invalid, using 'Playing' instead.");
activityType = ActivityType.Playing; activityType = DiscordActivityType.Playing;
} }
client.UpdateStatusAsync(new DiscordActivity(Config.presenceText, activityType), UserStatus.Online); client.UpdateStatusAsync(new DiscordActivity(Config.presenceText, activityType), DiscordUserStatus.Online);
return Task.CompletedTask; return Task.CompletedTask;
} }
internal static async Task OnGuildAvailable(DiscordClient discordClient, GuildCreateEventArgs e) public static async Task OnGuildAvailable(DiscordClient discordClient, GuildAvailableEventArgs e)
{ {
Logger.Log("Found Discord server: " + e.Guild.Name + " (" + e.Guild.Id + ")"); Logger.Log("Found Discord server: " + e.Guild.Name + " (" + e.Guild.Id + ")");
@ -49,21 +51,7 @@ internal static class EventHandler
} }
} }
internal static Task OnClientError(DiscordClient _, ClientErrorEventArgs e) public static async Task OnMessageCreated(DiscordClient client, MessageCreatedEventArgs e)
{
Logger.Error("Client exception occured:\n" + e.Exception);
switch (e.Exception)
{
case BadRequestException ex:
Logger.Error("JSON Message: " + ex.JsonMessage);
break;
default:
break;
}
return Task.CompletedTask;
}
internal static async Task OnMessageCreated(DiscordClient client, MessageCreateEventArgs e)
{ {
if (e.Author.IsBot) if (e.Author.IsBot)
{ {
@ -76,15 +64,9 @@ internal static class EventHandler
return; return;
} }
// Ignore staff messages // Sends a DM to the assigned staff member if at least a day has gone by since the last message and the user sending the message isn't staff
if (Database.IsStaff(e.Author.Id))
{
return;
}
// Sends a DM to the assigned staff member if at least a day has gone by since the last message
IReadOnlyList<DiscordMessage> messages = await e.Channel.GetMessagesAsync(2); IReadOnlyList<DiscordMessage> messages = await e.Channel.GetMessagesAsync(2);
if (messages.Count > 1 && messages[1].Timestamp < DateTimeOffset.UtcNow.AddDays(Config.ticketUpdatedNotificationDelay * -1)) if (messages.Count > 1 && messages[1].Timestamp < DateTimeOffset.UtcNow.AddDays(Config.ticketUpdatedNotificationDelay * -1) && !Database.IsStaff(e.Author.Id))
{ {
try try
{ {
@ -100,42 +82,7 @@ internal static class EventHandler
} }
} }
internal static async Task OnCommandError(SlashCommandsExtension commandSystem, SlashCommandErrorEventArgs e) public static async Task OnMemberAdded(DiscordClient client, GuildMemberAddedEventArgs e)
{
switch (e.Exception)
{
case SlashExecutionChecksFailedException checksFailedException:
{
foreach (SlashCheckBaseAttribute attr in checksFailedException.FailedChecks)
{
await e.Context.Channel.SendMessageAsync(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = ParseFailedCheck(attr)
});
}
return;
}
case BadRequestException ex:
Logger.Error("Command exception occured:\n" + e.Exception);
Logger.Error("JSON Message: " + ex.JsonMessage);
return;
default:
{
Logger.Error("Exception occured: " + e.Exception.GetType() + ": " + e.Exception);
await e.Context.Channel.SendMessageAsync(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = "Internal error occured, please report this to the developer."
});
return;
}
}
}
internal static async Task OnMemberAdded(DiscordClient client, GuildMemberAddEventArgs e)
{ {
if (!Database.TryGetOpenTickets(e.Member.Id, out List<Database.Ticket> ownTickets)) if (!Database.TryGetOpenTickets(e.Member.Id, out List<Database.Ticket> ownTickets))
{ {
@ -151,7 +98,7 @@ internal static class EventHandler
{ {
try try
{ {
await channel.AddOverwriteAsync(e.Member, Permissions.AccessChannels); await channel.AddOverwriteAsync(e.Member, DiscordPermissions.AccessChannels);
await channel.SendMessageAsync(new DiscordEmbedBuilder await channel.SendMessageAsync(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
@ -170,7 +117,7 @@ internal static class EventHandler
} }
} }
internal static async Task OnMemberRemoved(DiscordClient client, GuildMemberRemoveEventArgs e) public static async Task OnMemberRemoved(DiscordClient client, GuildMemberRemovedEventArgs e)
{ {
if (Database.TryGetOpenTickets(e.Member.Id, out List<Database.Ticket> ownTickets)) if (Database.TryGetOpenTickets(e.Member.Id, out List<Database.Ticket> ownTickets))
{ {
@ -217,22 +164,22 @@ internal static class EventHandler
} }
} }
internal static async Task OnComponentInteractionCreated(DiscordClient client, ComponentInteractionCreateEventArgs e) public static async Task OnComponentInteractionCreated(DiscordClient client, ComponentInteractionCreatedEventArgs e)
{ {
try try
{ {
switch (e.Interaction.Data.ComponentType) switch (e.Interaction.Data.ComponentType)
{ {
case ComponentType.Button: case DiscordComponentType.Button:
switch (e.Id) switch (e.Id)
{ {
case "supportchild_closeconfirm": case "supportchild_closeconfirm":
await CloseCommand.OnConfirmed(e.Interaction); await CloseCommand.OnConfirmed(e.Interaction);
return; return;
case { } when e.Id.StartsWith("supportchild_newcommandbutton"): case not null when e.Id.StartsWith("supportchild_newcommandbutton"):
await NewCommand.OnCategorySelection(e.Interaction); await NewCommand.OnCategorySelection(e.Interaction);
return; return;
case { } when e.Id.StartsWith("supportchild_newticketbutton"): case not null when e.Id.StartsWith("supportchild_newticketbutton"):
await CreateButtonPanelCommand.OnButtonUsed(e.Interaction); await CreateButtonPanelCommand.OnButtonUsed(e.Interaction);
return; return;
case "right": case "right":
@ -249,23 +196,23 @@ internal static class EventHandler
Logger.Warn("Unknown button press received! '" + e.Id + "'"); Logger.Warn("Unknown button press received! '" + e.Id + "'");
return; return;
} }
case ComponentType.StringSelect: case DiscordComponentType.StringSelect:
switch (e.Id) switch (e.Id)
{ {
case { } when e.Id.StartsWith("supportchild_newcommandselector"): case not null when e.Id.StartsWith("supportchild_newcommandselector"):
await NewCommand.OnCategorySelection(e.Interaction); await NewCommand.OnCategorySelection(e.Interaction);
return; return;
case { } when e.Id.StartsWith("supportchild_newticketselector"): case not null when e.Id.StartsWith("supportchild_newticketselector"):
await CreateSelectionBoxPanelCommand.OnSelectionMenuUsed(e.Interaction); await CreateSelectionBoxPanelCommand.OnSelectionMenuUsed(e.Interaction);
return; return;
default: default:
Logger.Warn("Unknown selection box option received! '" + e.Id + "'"); Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
return; return;
} }
case ComponentType.ActionRow: case DiscordComponentType.ActionRow:
Logger.Warn("Unknown action row received! '" + e.Id + "'"); Logger.Warn("Unknown action row received! '" + e.Id + "'");
return; return;
case ComponentType.FormInput: case DiscordComponentType.FormInput:
Logger.Warn("Unknown form input received! '" + e.Id + "'"); Logger.Warn("Unknown form input received! '" + e.Id + "'");
return; return;
default: default:
@ -283,18 +230,38 @@ internal static class EventHandler
Logger.Error("Interaction Exception occured: " + ex.GetType() + ": " + ex); Logger.Error("Interaction Exception occured: " + ex.GetType() + ": " + ex);
} }
} }
public static async Task OnCommandError(CommandsExtension commandSystem, CommandErroredEventArgs e)
private static string ParseFailedCheck(SlashCheckBaseAttribute attr)
{ {
return attr switch switch (e.Exception)
{ {
SlashRequireDirectMessageAttribute => "This command can only be used in direct messages!", case ChecksFailedException checksFailedException:
SlashRequireOwnerAttribute => "Only the server owner can use that command!", {
SlashRequirePermissionsAttribute => "You don't have permission to do that!", foreach (ContextCheckFailedData error in checksFailedException.Errors)
SlashRequireBotPermissionsAttribute => "The bot doesn't have the required permissions to do that!", {
SlashRequireUserPermissionsAttribute => "You don't have permission to do that!", await e.Context.Channel.SendMessageAsync(new DiscordEmbedBuilder
SlashRequireGuildAttribute => "This command has to be used in a Discord server!", {
_ => "Unknown Discord API error occured, please try again later." Color = DiscordColor.Red,
}; Description = error.ErrorMessage
});
}
return;
}
case BadRequestException ex:
Logger.Error("Command exception occured:\n" + e.Exception);
Logger.Error("JSON Message: " + ex.JsonMessage);
return;
default:
{
Logger.Error("Exception occured: " + e.Exception.GetType() + ": " + e.Exception);
await e.Context.Channel.SendMessageAsync(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = "Internal error occured, please report this to the developer."
});
return;
}
}
} }
} }

View file

@ -10,7 +10,7 @@ public static class Logger
{ {
try try
{ {
SupportChild.discordClient.Logger.Log(LogLevel.Debug, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message); SupportChild.client.Logger.Log(LogLevel.Debug, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -22,7 +22,7 @@ public static class Logger
{ {
try try
{ {
SupportChild.discordClient.Logger.Log(LogLevel.Information, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message); SupportChild.client.Logger.Log(LogLevel.Information, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -34,7 +34,7 @@ public static class Logger
{ {
try try
{ {
SupportChild.discordClient.Logger.Log(LogLevel.Warning, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message); SupportChild.client.Logger.Log(LogLevel.Warning, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -46,7 +46,7 @@ public static class Logger
{ {
try try
{ {
SupportChild.discordClient.Logger.Log(LogLevel.Error, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message); SupportChild.client.Logger.Log(LogLevel.Error, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -58,7 +58,7 @@ public static class Logger
{ {
try try
{ {
SupportChild.discordClient.Logger.Log(LogLevel.Critical, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message); SupportChild.client.Logger.Log(LogLevel.Critical, new EventId(420, Assembly.GetEntryAssembly()?.GetName().Name), message);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {

View file

@ -8,31 +8,24 @@ using DSharpPlus;
using DSharpPlus.Interactivity; using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Enums; using DSharpPlus.Interactivity.Enums;
using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.SlashCommands; using DSharpPlus.Commands;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SupportChild.Commands; using SupportChild.Commands;
using CommandLine; using CommandLine;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.EventArgs;
using DSharpPlus.Commands.Exceptions;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Commands.Processors.TextCommands.Parsing;
using DSharpPlus.Entities;
using DSharpPlus.Exceptions;
using Microsoft.Extensions.DependencyInjection;
namespace SupportChild; namespace SupportChild;
internal static class SupportChild internal static class SupportChild
{ {
// Sets up a dummy client to use for logging internal static DiscordClient client = null;
private static readonly DiscordConfiguration config = new()
{
Token = "DUMMY_TOKEN",
TokenType = TokenType.Bot,
MinimumLogLevel = LogLevel.Debug,
AutoReconnect = true,
Intents = DiscordIntents.All,
LogTimestampFormat = "yyyy-MM-dd HH:mm:ss",
LogUnknownEvents = false
};
public static DiscordClient discordClient { get; private set; } = new(config);
private static SlashCommandsExtension commands = null;
public class CommandLineArguments public class CommandLineArguments
{ {
[CommandLine.Option('c', [CommandLine.Option('c',
@ -124,10 +117,10 @@ internal static class SupportChild
public static async void Reload() public static async void Reload()
{ {
if (discordClient != null) if (client != null)
{ {
await discordClient.DisconnectAsync(); await client.DisconnectAsync();
discordClient.Dispose(); client.Dispose();
} }
Config.LoadConfig(); Config.LoadConfig();
@ -152,78 +145,103 @@ internal static class SupportChild
throw; throw;
} }
try
{
Logger.Log("Connecting to database... (" + Config.hostName + ":" + Config.port + ")");
Interviewer.ParseConfig(Config.interviews);
Interviewer.LoadActiveInterviews();
}
catch (Exception e)
{
Logger.Fatal("Could not set up database tables, please confirm connection settings, status of the server and permissions of MySQL user. Error: " + e);
throw;
}
Logger.Log("Setting up Discord client..."); Logger.Log("Setting up Discord client...");
DiscordClientBuilder clientBuilder = DiscordClientBuilder.CreateDefault(Config.token, DiscordIntents.All)
.SetReconnectOnFatalGatewayErrors()
.SetLogLevel(Config.logLevel)
.ConfigureServices(configure =>
{
configure.AddSingleton<IClientErrorHandler>(new ErrorHandler());
})
.ConfigureEventHandlers(builder =>
{
builder.HandleGuildDownloadCompleted(EventHandler.OnReady);
builder.HandleGuildAvailable(EventHandler.OnGuildAvailable);
builder.HandleMessageCreated(EventHandler.OnMessageCreated);
builder.HandleGuildMemberAdded(EventHandler.OnMemberAdded);
builder.HandleGuildMemberRemoved(EventHandler.OnMemberRemoved);
builder.HandleComponentInteractionCreated(EventHandler.OnComponentInteractionCreated);
})
.UseInteractivity(new InteractivityConfiguration
{
PaginationBehaviour = PaginationBehaviour.Ignore,
PaginationDeletion = PaginationDeletion.DeleteMessage,
Timeout = TimeSpan.FromMinutes(15)
})
.UseCommands((_, extension) =>
{
extension.AddCommands(
[
typeof(AddCategoryCommand),
typeof(AddCommand),
typeof(AddMessageCommand),
typeof(AddStaffCommand),
typeof(AssignCommand),
typeof(BlacklistCommand),
typeof(CloseCommand),
typeof(CreateButtonPanelCommand),
typeof(CreateSelectionBoxPanelCommand),
typeof(ListAssignedCommand),
typeof(ListCommand),
typeof(ListOpen),
typeof(ListUnassignedCommand),
typeof(MoveCommand),
typeof(NewCommand),
typeof(RandomAssignCommand),
typeof(RemoveCategoryCommand),
typeof(RemoveMessageCommand),
typeof(RemoveStaffCommand),
typeof(SayCommand),
typeof(SetSummaryCommand),
typeof(StatusCommand),
typeof(SummaryCommand),
typeof(ToggleActiveCommand),
typeof(TranscriptCommand),
typeof(UnassignCommand),
typeof(UnblacklistCommand),
typeof(AdminCommands)
]);
extension.AddProcessor(new SlashCommandProcessor());
extension.CommandErrored += EventHandler.OnCommandError;
}, new CommandsConfiguration()
{
RegisterDefaultCommandProcessors = false,
UseDefaultCommandErrorHandler = false
})
.ConfigureExtraFeatures(clientConfig =>
{
clientConfig.LogUnknownEvents = false;
clientConfig.LogUnknownAuditlogs = false;
});
// Setting up client configuration client = clientBuilder.Build();
config.Token = Config.token;
config.MinimumLogLevel = Config.logLevel;
discordClient = new DiscordClient(config);
Logger.Log("Hooking events...");
discordClient.Ready += EventHandler.OnReady;
discordClient.GuildAvailable += EventHandler.OnGuildAvailable;
discordClient.ClientErrored += EventHandler.OnClientError;
discordClient.MessageCreated += EventHandler.OnMessageCreated;
discordClient.GuildMemberAdded += EventHandler.OnMemberAdded;
discordClient.GuildMemberRemoved += EventHandler.OnMemberRemoved;
discordClient.ComponentInteractionCreated += EventHandler.OnComponentInteractionCreated;
discordClient.UseInteractivity(new InteractivityConfiguration
{
PaginationBehaviour = PaginationBehaviour.Ignore,
PaginationDeletion = PaginationDeletion.DeleteMessage,
Timeout = TimeSpan.FromMinutes(15)
});
Logger.Log("Registering commands...");
commands = discordClient.UseSlashCommands();
commands.RegisterCommands<AddCategoryCommand>();
commands.RegisterCommands<AddCommand>();
commands.RegisterCommands<AddMessageCommand>();
commands.RegisterCommands<AddStaffCommand>();
commands.RegisterCommands<AssignCommand>();
commands.RegisterCommands<BlacklistCommand>();
commands.RegisterCommands<CloseCommand>();
commands.RegisterCommands<CreateButtonPanelCommand>();
commands.RegisterCommands<CreateSelectionBoxPanelCommand>();
commands.RegisterCommands<ListAssignedCommand>();
commands.RegisterCommands<ListCommand>();
commands.RegisterCommands<ListOpen>();
commands.RegisterCommands<ListUnassignedCommand>();
commands.RegisterCommands<MoveCommand>();
commands.RegisterCommands<NewCommand>();
commands.RegisterCommands<RandomAssignCommand>();
commands.RegisterCommands<RemoveCategoryCommand>();
commands.RegisterCommands<RemoveMessageCommand>();
commands.RegisterCommands<RemoveStaffCommand>();
commands.RegisterCommands<SayCommand>();
commands.RegisterCommands<SetSummaryCommand>();
commands.RegisterCommands<StatusCommand>();
commands.RegisterCommands<SummaryCommand>();
commands.RegisterCommands<ToggleActiveCommand>();
commands.RegisterCommands<TranscriptCommand>();
commands.RegisterCommands<UnassignCommand>();
commands.RegisterCommands<UnblacklistCommand>();
commands.RegisterCommands<AdminCommands>();
Logger.Log("Hooking command events...");
commands.SlashCommandErrored += EventHandler.OnCommandError;
Logger.Log("Connecting to Discord..."); Logger.Log("Connecting to Discord...");
await discordClient.ConnectAsync(); await client.ConnectAsync();
}
}
internal class ErrorHandler : IClientErrorHandler
{
public ValueTask HandleEventHandlerError(string name, Exception exception, Delegate invokedDelegate, object sender, object args)
{
Logger.Error("Client exception occured:\n" + exception);
switch (exception)
{
case BadRequestException ex:
Logger.Error("JSON Message: " + ex.JsonMessage);
break;
default:
break;
}
return ValueTask.FromException(exception);
}
public ValueTask HandleGatewayError(Exception exception)
{
Logger.Error("A gateway error occured:\n" + exception);
return ValueTask.FromException(exception);
} }
} }

View file

@ -31,9 +31,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="DSharpPlus" Version="4.5.0" /> <PackageReference Include="DSharpPlus" Version="5.0.0-nightly-02397" />
<PackageReference Include="DSharpPlus.Interactivity" Version="4.5.0" /> <PackageReference Include="DSharpPlus.Commands" Version="5.0.0-nightly-02397" />
<PackageReference Include="DSharpPlus.SlashCommands" Version="4.5.0" /> <PackageReference Include="DSharpPlus.Interactivity" Version="5.0.0-nightly-02397" />
<PackageReference Include="EmbeddedBuildTime" Version="1.0.3" /> <PackageReference Include="EmbeddedBuildTime" Version="1.0.3" />
<PackageReference Include="GitInfo" Version="3.3.5"> <PackageReference Include="GitInfo" Version="3.3.5">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View file

@ -50,7 +50,7 @@ public static class Utilities
DiscordChannel channel = null; DiscordChannel channel = null;
try try
{ {
channel = await SupportChild.discordClient.GetChannelAsync(category.id); channel = await SupportChild.client.GetChannelAsync(category.id);
} }
catch (Exception) { /*ignored*/ } catch (Exception) { /*ignored*/ }