diff --git a/Tools/BiblioTech/BaseCommand.cs b/Tools/BiblioTech/BaseCommand.cs index c68aaeb..45f6ea6 100644 --- a/Tools/BiblioTech/BaseCommand.cs +++ b/Tools/BiblioTech/BaseCommand.cs @@ -29,7 +29,15 @@ namespace BiblioTech } catch (Exception ex) { - await command.FollowupAsync("Something failed while trying to do that...", ephemeral: true); + if (IsInAdminChannel(command)) + { + var msg = "Failed with exception: " + ex; + await command.FollowupAsync(msg.Substring(0, Math.Min(1900, msg.Length))); + } + else + { + await command.FollowupAsync("Something failed while trying to do that...", ephemeral: true); + } Console.WriteLine(ex); } } diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 4ad3a3c..9def408 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -109,7 +109,7 @@ namespace BiblioTech.Commands return; } - var report = string.Join(Environment.NewLine, Program.UserRepo.GetInteractionReport(user)); + var report = Program.UserRepo.GetInteractionReport(user); await context.Followup(report); } } @@ -134,7 +134,7 @@ namespace BiblioTech.Commands } var nl = Environment.NewLine; - await context.Followup($"Deployments:{nl}{string.Join(nl, deployments.Select(FormatDeployment))}"); + await context.Followup(deployments.Select(FormatDeployment).ToArray()); } private string FormatDeployment(CodexDeployment deployment) @@ -249,23 +249,31 @@ namespace BiblioTech.Commands this.ci = ci; } - protected async Task OnDeployment(CommandContext context, Func action) + protected async Task OnDeployment(CommandContext context, Func action) { var deployment = Program.DeploymentFilesMonitor.GetDeployments().SingleOrDefault(); if (deployment == null) { await context.Followup("No deployment found."); - return; + return default; } try { var group = ci.WrapCodexContainers(deployment.CodexInstances.Select(i => i.Containers).ToArray()); - await action(group, deployment.Metadata.Name); + var result = action(group, deployment.Metadata.Name); + return result; } catch (Exception ex) { - await context.Followup("Failed to wrap nodes with exception: " + ex); + var message = new[] + { + "Failed to wrap nodes with exception: " + }; + var exceptionMessage = ex.ToString().Split(Environment.NewLine); + + await context.Followup(message.Concat(exceptionMessage).ToArray()); + return default; } } } @@ -280,32 +288,40 @@ namespace BiblioTech.Commands protected override async Task onSubCommand(CommandContext context) { - await OnDeployment(context, async (group, name) => + var report = await OnDeployment(context, CreateNetInfoReport); + if (report != null && report.Any()) { - var nl = Environment.NewLine; - var content = new List - { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", - $"Deployment name: '{name}'" - }; + await context.Followup(report); + } + } - foreach (var node in group) + private string[] CreateNetInfoReport(ICodexNodeGroup group, string name) + { + var content = new List + { + $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", + $"Deployment name: '{name}'." + }; + + foreach (var node in group) + { + try { - try - { - var info = node.GetDebugInfo(); - var json = JsonConvert.SerializeObject(info, Formatting.Indented); - var jsonInsert = $"{nl}```{nl}{json}{nl}```{nl}"; - content.Add($"Node '{node.GetName()}' responded with {jsonInsert}"); - } - catch (Exception ex) - { - content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); - } + var info = node.GetDebugInfo(); + var json = JsonConvert.SerializeObject(info, Formatting.Indented); + var jsonLines = json.Split(Environment.NewLine); + content.Add($"Node '{node.GetName()}' responded with:"); + content.Add("---"); + content.AddRange(jsonLines); + content.Add("---"); } + catch (Exception ex) + { + content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); + } + } - await context.Followup(string.Join(nl, content)); - }); + return content.ToArray(); } } @@ -326,33 +342,41 @@ namespace BiblioTech.Commands var peerId = await peerIdOption.Parse(context); if (string.IsNullOrEmpty(peerId)) return; - await OnDeployment(context, async (group, name) => + var report = await OnDeployment(context, (group, name) => CreateDebugPeerReport(group, name, peerId)); + if (report != null && report.Any()) { - var nl = Environment.NewLine; - var content = new List - { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", - $"Deployment name: '{name}'" - }; + await context.Followup(report); + } + } - content.Add($"Calling debug/peer for '{peerId}' on {group.Count()} Codex nodes."); - foreach (var node in group) + private string[] CreateDebugPeerReport(ICodexNodeGroup group, string name, string peerId) + { + var content = new List + { + $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", + $"Deployment name: '{name}'.", + $"Calling debug/peer for '{peerId}'." + }; + + foreach (var node in group) + { + try { - try - { - var info = node.GetDebugPeer(peerId); - var json = JsonConvert.SerializeObject(info, Formatting.Indented); - var jsonInsert = $"{nl}```{nl}{json}{nl}```{nl}"; - content.Add($"Node '{node.GetName()}' responded with {jsonInsert}"); - } - catch (Exception ex) - { - content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); - } + var info = node.GetDebugPeer(peerId); + var json = JsonConvert.SerializeObject(info, Formatting.Indented); + var jsonLines = json.Split(Environment.NewLine); + content.Add($"Node '{node.GetName()}' responded with:"); + content.Add("---"); + content.AddRange(jsonLines); + content.Add("---"); } + catch (Exception ex) + { + content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); + } + } - await context.Followup(string.Join(nl, content)); - }); + return content.ToArray(); } } } diff --git a/Tools/BiblioTech/Options/CommandContext.cs b/Tools/BiblioTech/Options/CommandContext.cs index d3293e1..42a065a 100644 --- a/Tools/BiblioTech/Options/CommandContext.cs +++ b/Tools/BiblioTech/Options/CommandContext.cs @@ -4,36 +4,99 @@ namespace BiblioTech.Options { public class CommandContext { + private const string AttachmentFolder = "attachments"; + public CommandContext(SocketSlashCommand command, IReadOnlyCollection options) { Command = command; Options = options; + + var attachmentPath = Path.Combine(Program.Config.DataPath, AttachmentFolder); + if (Directory.Exists(attachmentPath)) + { + Directory.CreateDirectory(attachmentPath); + } } public SocketSlashCommand Command { get; } public IReadOnlyCollection Options { get; } - public async Task Followup(string message) + public async Task Followup(string line) { - if (message.Length > 1900) - { - await FollowupWithAttachement("codexdiscordbot_file.txt", message); - return; - } + var array = line.Split(Environment.NewLine); + await Followup(array); + } + + public async Task Followup(string[] lines) + { + var chunker = new LineChunker(lines); + var chunks = chunker.GetChunks(); + if (!chunks.Any()) return; + + // First chunk is a modification of the original message. + // Everything after that, we must create a new message. + var first = chunks.First(); + chunks.RemoveAt(0); await Command.ModifyOriginalResponseAsync(m => { - m.Content = message; + m.Content = FormatChunk(first); }); + + foreach (var remaining in chunks) + { + await Command.FollowupAsync(FormatChunk(remaining)); + } } - private async Task FollowupWithAttachement(string filename, string content) + private string FormatChunk(string[] chunk) { - using var fileStream = new MemoryStream(); - using var streamWriter = new StreamWriter(fileStream); - await streamWriter.WriteAsync(content); + return string.Join(Environment.NewLine, chunk); + } + } - await Command.FollowupWithFileAsync(fileStream, filename); + public class LineChunker + { + private readonly List input; + private readonly int maxCharacters; + + public LineChunker(string[] input, int maxCharacters = 1950) + { + this.input = input.ToList(); + this.maxCharacters = maxCharacters; + } + + public List GetChunks() + { + var result = new List(); + while (input.Any()) + { + result.Add(GetChunk()); + } + + return result; + } + + private string[] GetChunk() + { + var totalLength = 0; + var result = new List(); + + while (input.Any()) + { + var nextLine = input[0]; + var nextLength = totalLength + nextLine.Length; + if (nextLength > maxCharacters) + { + return result.ToArray(); + } + + input.RemoveAt(0); + result.Add(nextLine); + totalLength += nextLine.Length; + } + + return result.ToArray(); } } } diff --git a/Tools/BiblioTech/UserRepo.cs b/Tools/BiblioTech/UserRepo.cs index 1a78ac4..0db6b6f 100644 --- a/Tools/BiblioTech/UserRepo.cs +++ b/Tools/BiblioTech/UserRepo.cs @@ -76,17 +76,17 @@ namespace BiblioTech return result.ToArray(); } - public string GetUserReport(IUser user) + public string[] GetUserReport(IUser user) { var userData = GetUserData(user); - if (userData == null) return "User has not joined the test net."; + if (userData == null) return new[] { "User has not joined the test net." }; return userData.CreateOverview(); } - public string GetUserReport(EthAddress ethAddress) + public string[] GetUserReport(EthAddress ethAddress) { var userData = GetUserDataForAddress(ethAddress); - if (userData == null) return "No user is using this eth address."; + if (userData == null) return new[] { "No user is using this eth address." }; return userData.CreateOverview(); } @@ -196,14 +196,15 @@ namespace BiblioTech public List AssociateEvents { get; } public List MintEvents { get; } - public string CreateOverview() + public string[] CreateOverview() { - var nl = Environment.NewLine; - return - $"name: '{Name}' - id:{DiscordId}{nl}" + - $"joined: {CreatedUtc.ToString("o")}{nl}" + - $"current address: {CurrentAddress}{nl}" + - $"{AssociateEvents.Count + MintEvents.Count} total bot events."; + return new[] + { + $"name: '{Name}' - id:{DiscordId}", + $"joined: {CreatedUtc.ToString("o")}", + $"current address: {CurrentAddress}", + $"{AssociateEvents.Count + MintEvents.Count} total bot events." + }; } }