Tickets with assets smaller than 25MiB include them in the transcript

This commit is contained in:
Toastie 2024-12-27 16:58:26 +13:00
parent 7dfa3ad5e4
commit 84ad8cbca4
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
4 changed files with 239 additions and 48 deletions

View file

@ -13,6 +13,7 @@ namespace SupportChild.Commands;
public class CloseCommand public class CloseCommand
{ {
// TODO: Refactor this class a whole lot
private static Dictionary<ulong, string> closeReasons = new Dictionary<ulong, string>(); private static Dictionary<ulong, string> closeReasons = new Dictionary<ulong, string>();
[RequireGuild] [RequireGuild]
@ -38,7 +39,7 @@ public class CloseCommand
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(DiscordButtonStyle.Danger, "supportboi_closeconfirm", "Confirm")); .AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Danger, "supportchild_closeconfirm", "Confirm"));
await command.RespondAsync(confirmation); await command.RespondAsync(confirmation);
closeReasons.Add(command.Channel.Id, reason); closeReasons.Add(command.Channel.Id, reason);
@ -81,11 +82,52 @@ public class CloseCommand
closeReason = "\nReason: " + cachedReason + "\n"; closeReason = "\nReason: " + cachedReason + "\n";
} }
string fileName = Transcriber.GetZipFilename(ticket.id);
string filePath = Transcriber.GetZipPath(ticket.id);
long zipSize = 0;
// If the zip transcript doesn't exist, use the html file.
try
{
FileInfo fi = new FileInfo(filePath);
if (!fi.Exists || fi.Length >= 26214400)
{
fileName = Transcriber.GetHTMLFilename(ticket.id);
filePath = Transcriber.GetHtmlPath(ticket.id);
}
zipSize = fi.Length;
}
catch (Exception e)
{
await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = "ERROR: Could not find transcript file. Aborting..."
}));
Logger.Error("Failed to access transcript file:", e);
return;
}
// Check if the chosen file path works.
if (!File.Exists(filePath))
{
await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = "ERROR: Could not find transcript file. Aborting..."
}));
Logger.Error("Transcript file does not exist: \"" + filePath + "\"");
return;
}
try try
{ {
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = await SupportChild.client.GetChannelAsync(Config.logChannel); DiscordChannel logChannel = await SupportChild.client.GetChannelAsync(Config.logChannel);
DiscordEmbed embed = new DiscordEmbedBuilder
await using FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read);
DiscordMessageBuilder message = new DiscordMessageBuilder();
message.AddEmbed(new DiscordEmbedBuilder
{ {
Color = DiscordColor.Green, Color = DiscordColor.Green,
Description = "Ticket " + ticket.id.ToString("00000") + " closed by " + Description = "Ticket " + ticket.id.ToString("00000") + " closed by " +
@ -94,12 +136,8 @@ public class CloseCommand
{ {
Text = "Ticket: " + ticket.id.ToString("00000") Text = "Ticket: " + ticket.id.ToString("00000")
} }
}; });
message.AddFiles(new Dictionary<string, Stream> { { fileName, file } });
await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read);
DiscordMessageBuilder message = new DiscordMessageBuilder();
message.AddEmbed(embed);
message.AddFiles(new Dictionary<string, Stream> { { Transcriber.GetFilename(ticket.id), file } });
await logChannel.SendMessageAsync(message); await logChannel.SendMessageAsync(message);
} }
@ -110,25 +148,41 @@ public class CloseCommand
if (Config.closingNotifications) if (Config.closingNotifications)
{ {
DiscordEmbed embed = new DiscordEmbedBuilder
{
Color = DiscordColor.Green,
Description = "Ticket " + ticket.id.ToString("00000") + " which you opened has now been closed, " +
"check the transcript for more info.\n" + closeReason,
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = "Ticket: " + ticket.id.ToString("00000")
}
};
try try
{ {
DiscordUser staffMember = await SupportChild.client.GetUserAsync(ticket.creatorID); DiscordUser staffMember = await SupportChild.client.GetUserAsync(ticket.creatorID);
await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read); await using FileStream file = new(filePath, FileMode.Open, FileAccess.Read);
DiscordMessageBuilder message = new DiscordMessageBuilder(); DiscordMessageBuilder message = new();
message.AddEmbed(embed);
message.AddFiles(new Dictionary<string, Stream> { { Transcriber.GetFilename(ticket.id), file } }); if (zipSize >= 26214400)
{
message.AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Orange,
Description = "Ticket " + ticket.id.ToString("00000") + " which you opened has now been closed, check the transcript for more info.\n\n" +
"The zip file is too large, sending only the HTML file. Ask an administrator for the zip if you need it.\"\n" + closeReason,
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = "Ticket: " + ticket.id.ToString("00000")
}
});
}
else
{
message.AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Green,
Description = "Ticket " + ticket.id.ToString("00000") + " which you opened has now been closed, " + "check the transcript for more info.\n" + closeReason,
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = "Ticket: " + ticket.id.ToString("00000")
}
});
}
message.AddFiles(new Dictionary<string, Stream> { { fileName, file } });
await staffMember.SendMessageAsync(message); await staffMember.SendMessageAsync(message);
} }

View file

@ -13,6 +13,7 @@ namespace SupportChild.Commands;
public class TranscriptCommand public class TranscriptCommand
{ {
// TODO: Refactor the hell out of this
[RequireGuild] [RequireGuild]
[Command("transcript")] [Command("transcript")]
[Description("Creates a transcript of a ticket.")] [Description("Creates a transcript of a ticket.")]
@ -80,11 +81,49 @@ public class TranscriptCommand
} }
} }
string fileName = Transcriber.GetZipFilename(ticket.id);
string filePath = Transcriber.GetZipPath(ticket.id);
long zipSize = 0;
// If the zip transcript doesn't exist, use the html file.
try
{
FileInfo fi = new FileInfo(filePath);
if (!fi.Exists || fi.Length >= 26214400)
{
fileName = Transcriber.GetHTMLFilename(ticket.id);
filePath = Transcriber.GetHtmlPath(ticket.id);
}
zipSize = fi.Length;
}
catch (Exception e)
{
await command.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = "ERROR: Could not find transcript file. Aborting..."
}));
Logger.Error("Failed to access transcript file:", e);
return;
}
// Check if the chosen file path works.
if (!File.Exists(filePath))
{
await command.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Red,
Description = "ERROR: Could not find transcript file. Aborting..."
}));
Logger.Error("Transcript file does not exist: \"" + filePath + "\"");
return;
}
try try
{ {
// Log it if the log channel exists // Log it if the log channel exists
DiscordChannel logChannel = await SupportChild.client.GetChannelAsync(Config.logChannel); DiscordChannel logChannel = await SupportChild.client.GetChannelAsync(Config.logChannel);
await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read); await using FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read);
DiscordMessageBuilder message = new DiscordMessageBuilder(); DiscordMessageBuilder message = new DiscordMessageBuilder();
message.AddEmbed(new DiscordEmbedBuilder message.AddEmbed(new DiscordEmbedBuilder
@ -96,7 +135,7 @@ public class TranscriptCommand
Text = "Ticket: " + ticket.id.ToString("00000") Text = "Ticket: " + ticket.id.ToString("00000")
} }
}); });
message.AddFiles(new Dictionary<string, Stream> { { Transcriber.GetFilename(ticket.id), file } }); message.AddFiles(new Dictionary<string, Stream> { { fileName, file } });
await logChannel.SendMessageAsync(message); await logChannel.SendMessageAsync(message);
} }
@ -105,20 +144,54 @@ public class TranscriptCommand
Logger.Error("Could not send message in log channel."); Logger.Error("Could not send message in log channel.");
} }
if (await SendDirectMessage(command, fileName, filePath, zipSize, ticket.id))
{
await command.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Green,
Description = "Transcript sent!\n"
}));
}
}
private static async Task<bool> SendDirectMessage(SlashCommandContext command, string fileName, string filePath, long zipSize, uint ticketID)
{
try try
{ {
// Send transcript in a direct message // Send transcript in a direct message
await using FileStream file = new FileStream(Transcriber.GetPath(ticket.id), FileMode.Open, FileAccess.Read); await using FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read);
DiscordMessageBuilder directMessage = new DiscordMessageBuilder(); DiscordMessageBuilder directMessage = new DiscordMessageBuilder();
directMessage.AddEmbed(new DiscordEmbedBuilder
if (zipSize >= 26214400)
{ {
Color = DiscordColor.Green, directMessage.AddEmbed(new DiscordEmbedBuilder
Description = "Transcript generated!\n" {
}); Color = DiscordColor.Orange,
directMessage.AddFiles(new Dictionary<string, Stream> { { Transcriber.GetFilename(ticket.id), file } }); Description = "Transcript generated.\n\nThe zip file is too large, sending only the HTML file. Ask an administrator for the zip if you need it.",
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = "Ticket: " + ticketID.ToString("00000")
}
});
}
else
{
directMessage.AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Green,
Description = "Transcript generated!\n",
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = "Ticket: " + ticketID.ToString("00000")
}
});
}
directMessage.AddFiles(new Dictionary<string, Stream> { { fileName, file } });
await command.Member.SendMessageAsync(directMessage); await command.Member.SendMessageAsync(directMessage);
return true;
} }
catch (UnauthorizedException) catch (UnauthorizedException)
{ {
@ -127,13 +200,7 @@ public class TranscriptCommand
Color = DiscordColor.Red, Color = DiscordColor.Red,
Description = "Not allowed to send direct message to you, please check your privacy settings.\n" Description = "Not allowed to send direct message to you, please check your privacy settings.\n"
})); }));
return; return false;
} }
await command.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder
{
Color = DiscordColor.Green,
Description = "Transcript sent!\n"
}));
} }
} }

View file

@ -30,6 +30,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="7.1.4" />
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="DSharpPlus" Version="5.0.0-nightly-02397" /> <PackageReference Include="DSharpPlus" Version="5.0.0-nightly-02397" />
<PackageReference Include="DSharpPlus.Commands" Version="5.0.0-nightly-02397" /> <PackageReference Include="DSharpPlus.Commands" Version="5.0.0-nightly-02397" />

View file

@ -1,4 +1,6 @@
using System.IO; using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord;
@ -11,7 +13,7 @@ namespace SupportChild;
internal static class Transcriber internal static class Transcriber
{ {
private static string transcriptDir = "./transcripts"; private static string transcriptDir = "./transcripts"; // TODO: Should be local variable (or come from the config class) when added to config to avoid race conditions when reloading
internal static async Task ExecuteAsync(ulong channelID, uint ticketID) internal static async Task ExecuteAsync(ulong channelID, uint ticketID)
{ {
@ -28,36 +30,103 @@ internal static class Transcriber
Directory.CreateDirectory(transcriptDir); Directory.CreateDirectory(transcriptDir);
} }
string htmlPath = GetHtmlPath(ticketID);
string zipPath = GetZipPath(ticketID);
string assetDirPath = GetAssetDirPath(ticketID);
string assetDirName = GetAssetDirName(ticketID);
string htmlFilename = GetHTMLFilename(ticketID);
// TODO: Delete existing files
if (File.Exists(htmlPath))
{
File.Delete(htmlPath);
}
if (File.Exists(zipPath))
{
File.Delete(zipPath);
}
if (Directory.Exists(assetDirPath))
{
Directory.Delete(assetDirPath, true);
}
Channel channel = await discordClient.GetChannelAsync(new Snowflake(channelID)); Channel channel = await discordClient.GetChannelAsync(new Snowflake(channelID));
Guild guild = await discordClient.GetGuildAsync(channel.GuildId); Guild guild = await discordClient.GetGuildAsync(channel.GuildId);
ExportRequest request = new( ExportRequest request = new(
guild, guild,
channel, channel,
GetPath(ticketID), htmlPath,
null, assetDirPath,
ExportFormat.HtmlDark, ExportFormat.HtmlDark,
null, null,
null, null,
PartitionLimit.Null, PartitionLimit.Null,
MessageFilter.Null, MessageFilter.Null,
true, true,
false, true,
false, true,
"en-US", "en-SE",
true true
); );
await exporter.ExportChannelAsync(request); await exporter.ExportChannelAsync(request);
string[] assetFiles;
try
{
assetFiles = Directory.GetFiles(assetDirPath);
}
catch (Exception)
{
assetFiles = [];
}
if (assetFiles.Length > 0)
{
using (ZipArchive zip = ZipFile.Open(zipPath, ZipArchiveMode.Create))
{
zip.CreateEntryFromFile(htmlPath, htmlFilename);
foreach (string assetFile in assetFiles)
{
zip.CreateEntryFromFile(assetFile, assetDirName + "/" + Path.GetFileName(assetFile));
}
}
Directory.Delete(assetDirPath, true);
}
} }
internal static string GetPath(uint ticketNumber) internal static string GetHtmlPath(uint ticketNumber)
{ {
return transcriptDir + "/" + GetFilename(ticketNumber); return transcriptDir + "/" + GetHTMLFilename(ticketNumber);
} }
internal static string GetFilename(uint ticketNumber) internal static string GetZipPath(uint ticketNumber)
{
return transcriptDir + "/" + GetZipFilename(ticketNumber);
}
internal static string GetAssetDirPath(uint ticketNumber)
{
return transcriptDir + "/" + GetAssetDirName(ticketNumber);
}
internal static string GetAssetDirName(uint ticketNumber)
{
return "ticket-" + ticketNumber.ToString("00000") + "-assets";
}
internal static string GetHTMLFilename(uint ticketNumber)
{ {
return "ticket-" + ticketNumber.ToString("00000") + ".html"; return "ticket-" + ticketNumber.ToString("00000") + ".html";
} }
internal static string GetZipFilename(uint ticketNumber)
{
return "ticket-" + ticketNumber.ToString("00000") + ".zip";
}
} }