From 29972c610b76779a6457cf89f1021dbfe27a51c0 Mon Sep 17 00:00:00 2001 From: PoisnFang Date: Mon, 5 Jun 2023 13:09:51 -0700 Subject: [PATCH] csv export for messages --- .github/FUNDING.yaml | 1 + Commands/BasicCommandsModule.cs | 7 +- Commands/CopyChannelCommand.cs | 96 +++++++++++++- Images/snapshot-bmc-button.png | Bin 12332 -> 0 bytes MessageExport.cs | 9 ++ PoisnCopy.csproj | 1 + Program.cs | 223 ++++++++++++++++---------------- README.md | 15 ++- 8 files changed, 219 insertions(+), 133 deletions(-) create mode 100644 .github/FUNDING.yaml delete mode 100644 Images/snapshot-bmc-button.png create mode 100644 MessageExport.cs diff --git a/.github/FUNDING.yaml b/.github/FUNDING.yaml new file mode 100644 index 0000000..7b8e4d8 --- /dev/null +++ b/.github/FUNDING.yaml @@ -0,0 +1 @@ +github: [Poisnfang] diff --git a/Commands/BasicCommandsModule.cs b/Commands/BasicCommandsModule.cs index cda7366..3d27946 100644 --- a/Commands/BasicCommandsModule.cs +++ b/Commands/BasicCommandsModule.cs @@ -1,11 +1,6 @@ -using DSharpPlus; -using DSharpPlus.CommandsNext; +using DSharpPlus.CommandsNext; using DSharpPlus.CommandsNext.Attributes; -using DSharpPlus.Entities; -using DSharpPlus.Interactivity; using DSharpPlus.Interactivity.Extensions; -using System; -using System.Threading.Tasks; namespace PoisnCopy.Commands; diff --git a/Commands/CopyChannelCommand.cs b/Commands/CopyChannelCommand.cs index 6f68144..2c388d1 100644 --- a/Commands/CopyChannelCommand.cs +++ b/Commands/CopyChannelCommand.cs @@ -1,13 +1,11 @@ -using DSharpPlus; +using CsvHelper; +using DSharpPlus; using DSharpPlus.CommandsNext; using DSharpPlus.CommandsNext.Attributes; using DSharpPlus.Entities; -using DSharpPlus.Interactivity; using DSharpPlus.Interactivity.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Globalization; +using System.IO; using static DSharpPlus.Entities.DiscordEmbedBuilder; namespace PoisnCopy.Commands; @@ -78,6 +76,9 @@ public async Task CopyChannel(CommandContext ctx) await ctx.RespondAsync( $"Copy command: `pc.loadchannel {selectedChannel.Value.GuildId} {selectedChannel.Value.Id}`" ); + await ctx.RespondAsync( + $"Export command: `pc.exportchannel {selectedChannel.Value.GuildId} {selectedChannel.Value.Id}`" + ); } [Command("loadchannel")] @@ -182,4 +183,85 @@ await ctx.Channel.SendMessageAsync( await ctx.RespondAsync($"{newChan.Name} copy complete!"); } -} \ No newline at end of file + + [Command("exportchannel")] + [Description("Export a copied channel")] + public async Task ExportChannel(CommandContext ctx, ulong guildId, ulong channelId) + { + var guild = await ctx.Client.GetGuildAsync(guildId); + var selectedChannel = guild.GetChannel(channelId); + + await ctx.Channel.SendMessageAsync("Starting export..."); + + await ctx.Channel.SendMessageAsync("Collecting messages..."); + + var messag = await selectedChannel.GetMessagesAsync(); + + var messCopy = messag.ToList(); + var more = await selectedChannel.GetMessagesAsync(100); + + while (more.Count > 0) + { + messCopy.AddRange(more); + more = await selectedChannel.GetMessagesBeforeAsync(more.LastOrDefault().Id, 100); + } + + await ctx.Channel.SendMessageAsync("Organizing messages..."); + + messCopy.Reverse(); + + var messageExports = new List(); + + await ctx.Channel.SendMessageAsync( + $"Exporting {messCopy.Count} messages... (this could take awhile)" + ); + + foreach (var mes in messCopy) + { + if (!string.IsNullOrEmpty(mes.Content)) + { + var textMessage = new MessageExport + { + AuthorName = mes.Author.Username, + IconUrl = mes.Author.AvatarUrl, + MessageConent = mes.Content, + Timestamp = mes.Timestamp.ToString("o") + }; + messageExports.Add(textMessage); + } + + if (mes.Attachments.Count > 0) + { + foreach (var att in mes.Attachments) + { + var imageMessage = new MessageExport + { + AuthorName = mes.Author.Username, + IconUrl = mes.Author.AvatarUrl, + MessageConent = att.Url, + Timestamp = mes.Timestamp.ToString("o") + }; + messageExports.Add(imageMessage); + } + } + } + + try + { + using var memStream = new MemoryStream(); + using var writer = new StreamWriter(memStream); + using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); + csv.WriteRecords(messageExports); + await writer.FlushAsync(); + memStream.Position = 0; + var fileMessage = new DiscordMessageBuilder() { Content = "Messages exported" }; + fileMessage.AddFile($"{selectedChannel.Name}-export.csv", memStream, true); + + await ctx.Channel.SendMessageAsync(fileMessage); + } + catch (Exception e) + { + await ctx.Channel.SendMessageAsync(e.Message); + } + } +} diff --git a/Images/snapshot-bmc-button.png b/Images/snapshot-bmc-button.png deleted file mode 100644 index 71ae4b9a3ae5aeb39474cf52ea5fd49b3f3cb864..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12332 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 + diff --git a/Program.cs b/Program.cs index 2688c54..42dd428 100644 --- a/Program.cs +++ b/Program.cs @@ -1,141 +1,134 @@ -using System; -using System.Linq; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using DSharpPlus; +using DSharpPlus; using DSharpPlus.CommandsNext; using DSharpPlus.Interactivity; -using Microsoft.Extensions.Configuration; -using DSharpPlus.Interactivity.Extensions; using DSharpPlus.Interactivity.Enums; -using System.Collections.Generic; +using DSharpPlus.Interactivity.Extensions; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace PoisnCopy +namespace PoisnCopy; + +internal class Program { - internal class Program - { - /* This is the cancellation token we'll use to end the bot if needed(used for most async stuff). */ - private CancellationTokenSource _cts { get; set; } + /* This is the cancellation token we'll use to end the bot if needed(used for most async stuff). */ + private CancellationTokenSource _cts { get; set; } - /* We'll load the app config into this when we create it a little later. */ - private IConfigurationRoot _config; + /* We'll load the app config into this when we create it a little later. */ + private IConfigurationRoot _config; - /* These are the discord library's main classes */ - private DiscordClient _discord; - private CommandsNextExtension _commands; - private InteractivityExtension _interactivity; + /* These are the discord library's main classes */ + private DiscordClient _discord; + private CommandsNextExtension _commands; + private InteractivityExtension _interactivity; - /* Use the async main to create an instance of the class and await it(async main is only available in C# 7.1 onwards). */ + /* Use the async main to create an instance of the class and await it(async main is only available in C# 7.1 onwards). */ - private static async Task Main(string[] args) => await new Program().InitBot(args); + private static async Task Main(string[] args) => await new Program().InitBot(args); - private async Task InitBot(string[] args) + private async Task InitBot(string[] args) + { + try { - try - { - Console.WriteLine("[info] Welcome to my bot!"); - _cts = new CancellationTokenSource(); - - // Load the config file(we'll create this shortly) - Console.WriteLine("[info] Loading config file.."); - _config = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("config.json", optional: false, reloadOnChange: true) - .Build(); - - // Create the DSharpPlus client - Console.WriteLine("[info] Creating discord client.."); - _discord = new DiscordClient( - new DiscordConfiguration - { - Token = _config.GetValue("discord:token"), - TokenType = TokenType.Bot, - Intents = DiscordIntents.AllUnprivileged | DiscordIntents.MessageContents - } - ); - - // Create the interactivity module(I'll show you how to use this later on) - _interactivity = _discord.UseInteractivity( - new InteractivityConfiguration() - { - PaginationBehaviour = PaginationBehaviour.WrapAround, // What to do when a pagination request times out - PaginationDeletion = PaginationDeletion.DeleteMessage, // How long to wait before timing out - Timeout = TimeSpan.FromSeconds(30) // Default time to wait for interactive commands like waiting for a message or a reaction - } - ); - - // Build dependancies and then create the commands module. - var services = BuildServices(); - _commands = _discord.UseCommandsNext( - new CommandsNextConfiguration - { - StringPrefixes = new List - { - _config.GetValue("discord:CommandPrefix") - }, // Load the command prefix(what comes before the command, eg "!" or "/") from our config file - Services = services, - EnableDms = false - } - ); - - Console.WriteLine("[info] Loading command modules.."); - - var type = typeof(IModule); // Get the type of our interface - var types = AppDomain.CurrentDomain - .GetAssemblies() // Get the assemblies associated with our project - .SelectMany(s => s.GetTypes()) // Get all the types - .Where(p => type.IsAssignableFrom(p) && !p.IsInterface); // Filter to find any type that can be assigned to an IModule - - var typeList = types as Type[] ?? types.ToArray(); // Convert to an array - foreach (var t in typeList) + Console.WriteLine("[info] Welcome to my bot!"); + _cts = new CancellationTokenSource(); + + // Load the config file(we'll create this shortly) + Console.WriteLine("[info] Loading config file.."); + _config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("config.json", optional: false, reloadOnChange: true) + .Build(); + + // Create the DSharpPlus client + Console.WriteLine("[info] Creating discord client.."); + _discord = new DiscordClient( + new DiscordConfiguration { - _commands.RegisterCommands(t); + Token = _config.GetValue("discord:token"), + TokenType = TokenType.Bot, + Intents = DiscordIntents.AllUnprivileged | DiscordIntents.MessageContents } + ); - Console.WriteLine($"[info] Loaded {typeList.Count()} modules."); - await RunAsync(args); - } - catch (Exception ex) - { - // This will catch any exceptions that occur during the operation/setup of your bot. + // Create the interactivity module(I'll show you how to use this later on) + _interactivity = _discord.UseInteractivity( + new InteractivityConfiguration() + { + PaginationBehaviour = PaginationBehaviour.WrapAround, // What to do when a pagination request times out + PaginationDeletion = PaginationDeletion.DeleteMessage, // How long to wait before timing out + Timeout = TimeSpan.FromSeconds(30) // Default time to wait for interactive commands like waiting for a message or a reaction + } + ); + + // Build dependancies and then create the commands module. + var services = BuildServices(); + _commands = _discord.UseCommandsNext( + new CommandsNextConfiguration + { + StringPrefixes = new List + { + _config.GetValue("discord:CommandPrefix") + }, // Load the command prefix(what comes before the command, eg "!" or "/") from our config file + Services = services, + EnableDms = false + } + ); + + Console.WriteLine("[info] Loading command modules.."); - // Feel free to replace this with what ever logging solution you'd like to use. - // I may do a guide later on the basic logger I implemented in my most recent bot. - Console.Error.WriteLine(ex.ToString()); + var type = typeof(IModule); // Get the type of our interface + var types = AppDomain.CurrentDomain + .GetAssemblies() // Get the assemblies associated with our project + .SelectMany(s => s.GetTypes()) // Get all the types + .Where(p => type.IsAssignableFrom(p) && !p.IsInterface); // Filter to find any type that can be assigned to an IModule + + var typeList = types as Type[] ?? types.ToArray(); // Convert to an array + foreach (var t in typeList) + { + _commands.RegisterCommands(t); } - } - private async Task RunAsync(string[] args) + Console.WriteLine($"[info] Loaded {typeList.Count()} modules."); + await RunAsync(args); + } + catch (Exception ex) { - // Connect to discord's service - Console.WriteLine("Connecting.."); - await _discord.ConnectAsync(); - Console.WriteLine("Connected!"); - var connections = _discord.Guilds.Count; - Console.WriteLine($"I am running on {connections} servers"); - - // Keep the bot running until the cancellation token requests we stop - while (!_cts.IsCancellationRequested) - await Task.Delay(TimeSpan.FromMinutes(1)); + // This will catch any exceptions that occur during the operation/setup of your bot. + + // Feel free to replace this with what ever logging solution you'd like to use. + // I may do a guide later on the basic logger I implemented in my most recent bot. + Console.Error.WriteLine(ex.ToString()); } + } + + private async Task RunAsync(string[] args) + { + // Connect to discord's service + Console.WriteLine("Connecting.."); + await _discord.ConnectAsync(); + Console.WriteLine("Connected!"); + var connections = _discord.Guilds.Count; + Console.WriteLine($"I am running on {connections} servers"); + + // Keep the bot running until the cancellation token requests we stop + while (!_cts.IsCancellationRequested) + await Task.Delay(TimeSpan.FromMinutes(1)); + } - /* - DSharpPlus has dependancy injection for commands, this builds a list of dependancies. - We can then access these in our command modules. - */ + /* + DSharpPlus has dependancy injection for commands, this builds a list of dependancies. + We can then access these in our command modules. + */ - private ServiceProvider BuildServices() - { - var deps = new ServiceCollection(); + private ServiceProvider BuildServices() + { + var deps = new ServiceCollection(); - deps.AddSingleton(_interactivity) // Add interactivity - .AddSingleton(_cts) // Add the cancellation token - .AddSingleton(_config) // Add our config - .AddSingleton(_discord); // Add the discord client + deps.AddSingleton(_interactivity) // Add interactivity + .AddSingleton(_cts) // Add the cancellation token + .AddSingleton(_config) // Add our config + .AddSingleton(_discord); // Add the discord client - return deps.BuildServiceProvider(); - } + return deps.BuildServiceProvider(); } } \ No newline at end of file diff --git a/README.md b/README.md index db29ede..a572931 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # ALERT -PoisnCopy is now on 100 servers which is the capacity based on [this post from Discord](https://support.discord.com/hc/en-us/articles/4410940809111). I do not have any plans to Verify my bot. this decision is not an easy one as I know this bot is very useful to many people as Discord does not offer this service themselves. I frankly do not have time to spend getting this bot into compliance. Should demand change for this, I might reconsider. -Therefore, _**if you try to add the bot to your servers it won't work.**_ Feel free to contact me directly with question: posinfang@poisnfang.com -### Show Some Support -Buy Me A Coffee +PoisnCopy is now on 100 servers which is the capacity based on [this post from Discord](https://support.discord.com/hc/en-us/articles/4410940809111). I hope to verify this bot at some point, but it might be take a while. + +Therefore, _**if you try to add the bot to your servers it won't work.**_ Contact me directly with questions: posinfang@poisnfang.com + +**_I can assist in setting this bot up for personal use. Please reach out if you need help!_** + +### Show Some Support - _Sponsor this project on Github_ + # PoisnCopy + Discord channel copy bot Add the bot to BOTH of your servers using this link. (NOTE: You must be the OWNER (Not just an admin) of BOTH servers) @@ -18,7 +23,7 @@ The bot will list the channels in the server that you are typing in. You must en **Make sure your bot can see which channel you are typing in (check left hand side)** ![image](https://user-images.githubusercontent.com/60050783/107395699-49d3a300-6aba-11eb-8b1c-d4e4b41cd6f3.png) -*Note: The `pc.copychannel` command is just to list the IDs, once you have them, you can just use the `pc.loadchannel`command. +\*Note: The `pc.copychannel` command is just to list the IDs, once you have them, you can just use the `pc.loadchannel`command. i.e.