From d021d153897b45812d69e4d4e2059318a57a2433 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:17:05 +0200 Subject: [PATCH 01/11] Add a comand to force the image send --- Program.cs | 57 ++++++++++-- Recuerdense-Bot.csproj | 1 + obj/Debug/net7.0/Recuerdense-Bot.assets.cache | Bin 13056 -> 13927 bytes ...erdense-Bot.csproj.AssemblyReference.cache | Bin 82866 -> 83996 bytes obj/Recuerdense-Bot.csproj.nuget.dgspec.json | 4 + obj/project.assets.json | 85 +++++++++++++++--- obj/project.nuget.cache | 5 +- 7 files changed, 131 insertions(+), 21 deletions(-) diff --git a/Program.cs b/Program.cs index f0d04c5..afe5ab2 100644 --- a/Program.cs +++ b/Program.cs @@ -5,11 +5,13 @@ using System.Linq; using System.Threading.Tasks; using Discord; +using Discord.Commands; using Discord.WebSocket; using CsvHelper; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; using Google.Apis.Services; +using Microsoft.Extensions.DependencyInjection; namespace DiscordBotExample { @@ -18,6 +20,8 @@ class Program private static List _imageUrls; private static Random _random = new Random(); private static DiscordSocketClient _client; + private static CommandService _commands; + private static IServiceProvider _services; private static ulong _channelId; private static string _fileId; private static string _credentialsPath; @@ -37,11 +41,6 @@ static async Task Main(string[] args) if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(channelIdStr) || string.IsNullOrEmpty(_fileId) || string.IsNullOrEmpty(_credentialsPath) || string.IsNullOrEmpty(postTimeStr)) { Console.WriteLine("Environment variables are not set correctly."); - Console.WriteLine($"DISCORD_BOT_TOKEN: {(string.IsNullOrEmpty(token) ? "Not set" : "Set")}"); - Console.WriteLine($"DISCORD_CHANNEL_ID: {(string.IsNullOrEmpty(channelIdStr) ? "Not set" : "Set")}"); - Console.WriteLine($"GOOGLE_DRIVE_FILE_ID: {(string.IsNullOrEmpty(_fileId) ? "Not set" : "Set")}"); - Console.WriteLine($"GOOGLE_CREDENTIALS_PATH: {(string.IsNullOrEmpty(_credentialsPath) ? "Not set" : "Set")}"); - Console.WriteLine($"POST_TIME: {(string.IsNullOrEmpty(postTimeStr) ? "Not set" : "Set")}"); return; } @@ -59,10 +58,17 @@ static async Task Main(string[] args) return; } - // Initialize the Discord client + // Initialize the Discord client and command service _client = new DiscordSocketClient(); + _commands = new CommandService(); + _services = new ServiceCollection() + .AddSingleton(_client) + .AddSingleton(_commands) + .BuildServiceProvider(); + _client.Log += Log; _client.Ready += OnReady; + _client.MessageReceived += HandleCommandAsync; // Start the bot await _client.LoginAsync(TokenType.Bot, token); @@ -82,6 +88,9 @@ private static async Task OnReady() { Console.WriteLine("Bot is connected."); + // Initialize command handling + await _commands.AddModulesAsync(typeof(Program).Assembly, _services); + // Download and process the CSV file from Google Drive var csvData = await DownloadCsvFromGoogleDrive(); @@ -119,6 +128,27 @@ private static async Task OnReady() await ScheduleNextPost(); } + private static async Task HandleCommandAsync(SocketMessage messageParam) + { + var message = messageParam as SocketUserMessage; + var context = new SocketCommandContext(_client, message); + + if (message == null || message.Author.IsBot) + return; + + int argPos = 0; + + if (message.HasCharPrefix('/', ref argPos)) + { + var result = await _commands.ExecuteAsync(context, argPos, _services); + + if (!result.IsSuccess) + { + Console.WriteLine(result.ErrorReason); + } + } + } + private static async Task ScheduleNextPost() { var nowUtc = DateTime.UtcNow; @@ -149,7 +179,7 @@ private static async Task ScheduleNextPost() await ScheduleNextPost(); } - private static async Task DownloadCsvFromGoogleDrive() + public static async Task DownloadCsvFromGoogleDrive() { try { @@ -189,7 +219,7 @@ private static async Task DownloadCsvFromGoogleDrive() } } - private static async Task PostRandomImageUrl() + public static async Task PostRandomImageUrl() { var channel = _client.GetChannel(_channelId) as IMessageChannel; @@ -212,4 +242,15 @@ public class YourRecordClass public string image_url { get; set; } public string has_spoilers { get; set; } } + + // Command Module for handling commands + public class CommandModule : ModuleBase + { + [Command("send")] + public async Task SendRandomImage() + { + await Program.PostRandomImageUrl(); + await ReplyAsync("Random image sent!"); + } + } } diff --git a/Recuerdense-Bot.csproj b/Recuerdense-Bot.csproj index 0b0da34..923565f 100644 --- a/Recuerdense-Bot.csproj +++ b/Recuerdense-Bot.csproj @@ -12,6 +12,7 @@ + diff --git a/obj/Debug/net7.0/Recuerdense-Bot.assets.cache b/obj/Debug/net7.0/Recuerdense-Bot.assets.cache index 64aa4407012815b19e2c09cb9d09221124ae68d5..6c3d48188e64fcec4fc7484d2e550ce0b567589f 100644 GIT binary patch delta 2162 zcmah~;cpXV9PYcVYnz*+Wt4Pn8P`(AU0d#2OW8UmcCax9S|-j3tlMO3nZs`2KqW*) zjggR$u=)F9LP+2Pd?6(0B^pEGAD~$bd@&{_!Y4!gs^3iXdCPm`a=Xq;p4{F0KF{y> z-tV6G_kMEfk6K~$@bEyJ%jK&4IllNr8N7V^7vuWA@0E{!2>rI|{ch*%x9#!!_g32W zevkr5Tp`>?h&9XPkE&mPR&ld0=wUcUzsHpgp9&Yzn-vsBSa)$@8z%jUJ5DL;T|F82UMz%C!=KS8F)!A*3-?^k)oFu)}dpa|F{ z2QjAc@6h$SKg?7z#mH^XttVQzn%+%xJU4GQ`9hLU5s_4w_Y7SPNi0QQg)Y*i zPA|3i()7Tq`7Md-_9W(;w64bLOw*BhmZ$OXB9$Xv)(e|mV|_Fk`8a6x4^q1}1Locy z>O73MSAbW65^w|<1*Wn4(`m&L*7KKLwT9iB+(?>T@%TxXA`_r(yPsY6`Rblez>u-GKcT@7#2E zyQHpZ*$osY(X{(1(ygfWc0SWUF)P}9Hg6W(x~X`Lc)NG1Q3d1Xrb|+3hor7K1{x@< zy!-F6{9~u?@09Ws^(pTS*%vt1AkEttp9f9@?*KKxycUeVlqXHkj*X4(db{nxQLgi& xT+8U_!YVyV4YY`tf-#ydH?tB2;z@cM@A5o5{rklU6*QjeBaEfF%+RwB*?(K3QtAKz delta 1850 zcmZ`(U2IEX81C6jPuG@KY;^6qcAM&1+tYT(O09mznEBZ{?7up;b~9?&#&#hHiMa5S zc|s(FxN#woSQ=MC=87N@_Yx!$R}#r?Tp-?W&N&&*`I0B+%lp00^S$T$p6~sVJ3AOp z?r3VT7z~DJ_O|VG+U}kixX?Cz@y^F9kG-GYysQv!&1nETeBTxI)i# z_vIoSSl+cLCPu_aDmUDp8p)2w@NHqE)k8@I1UF#;Y>pn>GMep`awuN# zao@O<8^LquN+ee{ISOfw4YsSQLURGr{HVP~jt1zfqqEle*0>9Ss|@KsQj=i zlglZU$N%B4&4(ZuN7R5o7uDi#xb8tbE(%v6I`fxntSUuq4q*MvEv|Pv8g% z0@GYaSWnnM*hpv~Y$7aa93C4uI+_t%vg2V%jMj;gLf=fwjBX@o(Hjj0blH6?*j8n7 zu2Uy>3x3(FLoAd17*k~jqoul);;e8FbBKv12GuW9-M;*@B}N_3#IHHbf7zy;DPY)G zi$e~xNwp46JJQw_q|1!qBmA+At*jOA9Xy80#!RX~WMha(IL#*292Cu%R3nf9+AWhu za!ihmTDV;!i{{_IdheZZl$&wM#UofgQ8NFE8L@;g_b#To1#oVcqlp6BSdg%tu!FFZ z&__QOoO&(cy4zKvm9z`V(rQFXENJ2a@K$I`wUE>hlfo0O9#1Q@wOUoJ4Fv%-d$hHZ zq_vN5#g9{!6`H&3#iPmz z&6Rc>wPtazN}Jb#mZDQ=tJdDP6Y1r>|97bb9U33td_FopRA2PmE)WC??WA%Zdv<|hN63aBfu=VF)=gK!yb`TB6GkV_z1p2$~8 Q%^VlUbFnV}cGXSpH+D)Ae*gdg diff --git a/obj/Debug/net7.0/Recuerdense-Bot.csproj.AssemblyReference.cache b/obj/Debug/net7.0/Recuerdense-Bot.csproj.AssemblyReference.cache index 24050724bbeee3977c20462185f4c5f8c0c143e7..08db8f7cd07a39df94e41db77850eca2d6d293fd 100644 GIT binary patch delta 186 zcmdng&N^oTD+e3n4h9AW#@NXlc|02fw+1pMIx{o2F-{j$XH=M8qsOShA7i0spl1-1 zlbIBgms(;z{eT{$jz~&Q4kIH_Bg27waiv?gIT#rkHKz0GGiop~a!fYl_nO|P&)CP9 z4Hn|oQ3y*dD$dN$v$dFPD4;RD)qqiDdXyex1Rti+PHdCy#DFGF=g?=gMwoSu5w2Ak E04IwwsQ>@~ delta 26 icmbO;fpt?mD+e3nHU= 33.0.1", "Discord.Net >= 3.15.3", "Google.Apis.Drive.v3 >= 1.68.0.3508", + "Microsoft.Extensions.DependencyInjection >= 8.0.0", "System.Threading.Tasks >= 4.3.0" ] }, @@ -1231,6 +1290,10 @@ "target": "Package", "version": "[1.68.0.3508, )" }, + "Microsoft.Extensions.DependencyInjection": { + "target": "Package", + "version": "[8.0.0, )" + }, "System.Threading.Tasks": { "target": "Package", "version": "[4.3.0, )" diff --git a/obj/project.nuget.cache b/obj/project.nuget.cache index 0c797bd..49f21e8 100644 --- a/obj/project.nuget.cache +++ b/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "8L4McY806g6gaWyJ3N7rwakc1O/2wbVwtmjPlu8A5GWQBuYsZdNi3tecXUHRcjJgzaDu7Yn+CZcgGsaN4aqxeg==", + "dgSpecHash": "OqRsFgHRl7cY56bSI1IQL7iCdqXvdVoikKRr6yUyu6ZULVMINRj33//CN4Vf3HCgadJw8B+pv+bzRlgGvMU/YA==", "success": true, "projectFilePath": "D:\\Winnie\\Documentos\\recuerdate\\Recuerdense-Bot.csproj", "expectedPackageFiles": [ @@ -17,7 +17,8 @@ "C:\\Users\\Winnie\\.nuget\\packages\\google.apis.core\\1.68.0\\google.apis.core.1.68.0.nupkg.sha512", "C:\\Users\\Winnie\\.nuget\\packages\\google.apis.drive.v3\\1.68.0.3508\\google.apis.drive.v3.1.68.0.3508.nupkg.sha512", "C:\\Users\\Winnie\\.nuget\\packages\\microsoft.bcl.asyncinterfaces\\6.0.0\\microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512", - "C:\\Users\\Winnie\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\6.0.0\\microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512", + "C:\\Users\\Winnie\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\8.0.0\\microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512", + "C:\\Users\\Winnie\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\8.0.0\\microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512", "C:\\Users\\Winnie\\.nuget\\packages\\microsoft.netcore.platforms\\1.1.0\\microsoft.netcore.platforms.1.1.0.nupkg.sha512", "C:\\Users\\Winnie\\.nuget\\packages\\microsoft.netcore.targets\\1.1.0\\microsoft.netcore.targets.1.1.0.nupkg.sha512", "C:\\Users\\Winnie\\.nuget\\packages\\newtonsoft.json\\13.0.3\\newtonsoft.json.13.0.3.nupkg.sha512", From ffe50341bf2f2bea2ddb0c17389c0fa8d95f1b8e Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:22:51 +0200 Subject: [PATCH 02/11] Optimized coded, and fixed sync commands --- Program.cs | 101 ++++++++++++++++++++--------------------------------- 1 file changed, 37 insertions(+), 64 deletions(-) diff --git a/Program.cs b/Program.cs index afe5ab2..e48eca9 100644 --- a/Program.cs +++ b/Program.cs @@ -17,8 +17,6 @@ namespace DiscordBotExample { class Program { - private static List _imageUrls; - private static Random _random = new Random(); private static DiscordSocketClient _client; private static CommandService _commands; private static IServiceProvider _services; @@ -30,35 +28,30 @@ class Program static async Task Main(string[] args) { - // Read environment variables var token = Environment.GetEnvironmentVariable("DISCORD_BOT_TOKEN"); var channelIdStr = Environment.GetEnvironmentVariable("DISCORD_CHANNEL_ID"); _fileId = Environment.GetEnvironmentVariable("GOOGLE_DRIVE_FILE_ID"); _credentialsPath = Environment.GetEnvironmentVariable("GOOGLE_CREDENTIALS_PATH"); var postTimeStr = Environment.GetEnvironmentVariable("POST_TIME"); - // Check if token, channelId, fileId, credentialsPath, or postTime is null or empty if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(channelIdStr) || string.IsNullOrEmpty(_fileId) || string.IsNullOrEmpty(_credentialsPath) || string.IsNullOrEmpty(postTimeStr)) { Console.WriteLine("Environment variables are not set correctly."); return; } - // Parse channel ID if (!ulong.TryParse(channelIdStr, out _channelId)) { Console.WriteLine("Invalid DISCORD_CHANNEL_ID format."); return; } - // Parse post time if (!TimeSpan.TryParse(postTimeStr, out _postTimeSpain)) { Console.WriteLine("Invalid POST_TIME format. It must be in the format HH:mm:ss."); return; } - // Initialize the Discord client and command service _client = new DiscordSocketClient(); _commands = new CommandService(); _services = new ServiceCollection() @@ -70,11 +63,9 @@ static async Task Main(string[] args) _client.Ready += OnReady; _client.MessageReceived += HandleCommandAsync; - // Start the bot await _client.LoginAsync(TokenType.Bot, token); await _client.StartAsync(); - // Block the application until it is closed await Task.Delay(-1); } @@ -88,46 +79,18 @@ private static async Task OnReady() { Console.WriteLine("Bot is connected."); - // Initialize command handling - await _commands.AddModulesAsync(typeof(Program).Assembly, _services); - - // Download and process the CSV file from Google Drive - var csvData = await DownloadCsvFromGoogleDrive(); - - if (csvData != null) - { - using (var reader = new StringReader(csvData)) - using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) - { - _imageUrls = csv.GetRecords() - .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") - .Select(record => record.image_url.Trim()) - .ToList(); - } - - Console.WriteLine("Filtered URLs read from CSV:"); - foreach (var url in _imageUrls) - { - Console.WriteLine(url); - } - } - else - { - Console.WriteLine("Failed to download or read the CSV file. Exiting..."); - return; - } - - // Check if imageUrls is empty - if (_imageUrls.Count == 0) - { - Console.WriteLine("No valid URLs available. Exiting..."); - return; - } + await RegisterCommandsAsync(); // Register command modules // Schedule the first post await ScheduleNextPost(); } + private static async Task RegisterCommandsAsync() + { + // Register commands from the assembly + await _commands.AddModulesAsync(typeof(Program).Assembly, _services); + } + private static async Task HandleCommandAsync(SocketMessage messageParam) { var message = messageParam as SocketUserMessage; @@ -153,29 +116,21 @@ private static async Task ScheduleNextPost() { var nowUtc = DateTime.UtcNow; var spainTime = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, _spainTimeZone); - - // Specify that nextPostTimeSpain is unspecified in terms of kind because we will convert it to a specific time zone var nextPostTimeSpain = DateTime.SpecifyKind(DateTime.Today.Add(_postTimeSpain), DateTimeKind.Unspecified); if (nextPostTimeSpain <= spainTime) { - // If the time has already passed for today, schedule for tomorrow nextPostTimeSpain = nextPostTimeSpain.AddDays(1); } - // Convert the unspecified time to Spain time zone and then to UTC nextPostTimeSpain = TimeZoneInfo.ConvertTimeToUtc(nextPostTimeSpain, _spainTimeZone); - - // Calculate the delay var delay = nextPostTimeSpain - nowUtc; Console.WriteLine($"Scheduling next post in {delay.TotalMinutes} minutes."); - await Task.Delay(delay); await PostRandomImageUrl(); - // Schedule the next post await ScheduleNextPost(); } @@ -183,7 +138,6 @@ public static async Task DownloadCsvFromGoogleDrive() { try { - // Set up Google Drive API service var credential = GoogleCredential.FromFile(_credentialsPath) .CreateScoped(DriveService.Scope.DriveReadonly); @@ -193,9 +147,9 @@ public static async Task DownloadCsvFromGoogleDrive() ApplicationName = "DiscordBotExample", }); - // Download the file var request = service.Files.Get(_fileId); var stream = new MemoryStream(); + request.MediaDownloader.ProgressChanged += progress => { if (progress.Status == Google.Apis.Download.DownloadStatus.Completed) @@ -223,27 +177,46 @@ public static async Task PostRandomImageUrl() { var channel = _client.GetChannel(_channelId) as IMessageChannel; - if (channel != null && _imageUrls.Count > 0) - { - int index = _random.Next(_imageUrls.Count); - string randomUrl = _imageUrls[index]; - await channel.SendMessageAsync(randomUrl); - } - else + if (channel != null) { - Console.WriteLine("No URLs available."); + string csvData = await DownloadCsvFromGoogleDrive(); + + if (!string.IsNullOrEmpty(csvData)) + { + using (var reader = new StringReader(csvData)) + using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) + { + var imageUrls = csv.GetRecords() + .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") + .Select(record => record.image_url.Trim()) + .ToArray(); + + if (imageUrls.Length > 0) + { + int index = new Random().Next(imageUrls.Length); + string randomUrl = imageUrls[index]; + await channel.SendMessageAsync(randomUrl); + } + else + { + Console.WriteLine("No valid URLs available."); + } + } + } + else + { + Console.WriteLine("Failed to download or read the CSV file."); + } } } } - // Define a class that matches the CSV structure public class YourRecordClass { public string image_url { get; set; } public string has_spoilers { get; set; } } - // Command Module for handling commands public class CommandModule : ModuleBase { [Command("send")] From 4f8a4aa2021e14b0f92401abddf2073a51ba18e7 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:26:53 +0200 Subject: [PATCH 03/11] Fixes --- Program.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Program.cs b/Program.cs index e48eca9..2553c6f 100644 --- a/Program.cs +++ b/Program.cs @@ -78,17 +78,14 @@ private static Task Log(LogMessage log) private static async Task OnReady() { Console.WriteLine("Bot is connected."); - - await RegisterCommandsAsync(); // Register command modules - - // Schedule the first post + await RegisterCommandsAsync(); await ScheduleNextPost(); } private static async Task RegisterCommandsAsync() { - // Register commands from the assembly - await _commands.AddModulesAsync(typeof(Program).Assembly, _services); + // Register the command module + await _commands.AddModuleAsync(_services); } private static async Task HandleCommandAsync(SocketMessage messageParam) @@ -101,13 +98,14 @@ private static async Task HandleCommandAsync(SocketMessage messageParam) int argPos = 0; - if (message.HasCharPrefix('/', ref argPos)) + if (message.HasStringPrefix("/", ref argPos)) { var result = await _commands.ExecuteAsync(context, argPos, _services); if (!result.IsSuccess) { Console.WriteLine(result.ErrorReason); + await context.Channel.SendMessageAsync($"Error: {result.ErrorReason}"); } } } @@ -130,7 +128,6 @@ private static async Task ScheduleNextPost() await Task.Delay(delay); await PostRandomImageUrl(); - await ScheduleNextPost(); } From e6450b2551c6919e9cac8ec7be5c2a6ca7fb5ae6 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:29:37 +0200 Subject: [PATCH 04/11] Added logs --- Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Program.cs b/Program.cs index 2553c6f..8adfe42 100644 --- a/Program.cs +++ b/Program.cs @@ -104,7 +104,7 @@ private static async Task HandleCommandAsync(SocketMessage messageParam) if (!result.IsSuccess) { - Console.WriteLine(result.ErrorReason); + Console.WriteLine($"Command failed: {result.ErrorReason}"); await context.Channel.SendMessageAsync($"Error: {result.ErrorReason}"); } } From 141da058f0dfb8eb71049ea00bbc6910591afd31 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:32:20 +0200 Subject: [PATCH 05/11] more fixes --- Program.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Program.cs b/Program.cs index 8adfe42..0d5df62 100644 --- a/Program.cs +++ b/Program.cs @@ -66,6 +66,7 @@ static async Task Main(string[] args) await _client.LoginAsync(TokenType.Bot, token); await _client.StartAsync(); + // Prevent application from closing await Task.Delay(-1); } @@ -78,13 +79,14 @@ private static Task Log(LogMessage log) private static async Task OnReady() { Console.WriteLine("Bot is connected."); - await RegisterCommandsAsync(); - await ScheduleNextPost(); + + // Call asynchronous tasks without blocking + await Task.Run(() => RegisterCommandsAsync()).ConfigureAwait(false); + await Task.Run(() => ScheduleNextPost()).ConfigureAwait(false); } private static async Task RegisterCommandsAsync() { - // Register the command module await _commands.AddModuleAsync(_services); } From 4abecfa1dde23b9ca7b1ec74e1bfeef6b70a62c5 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:37:26 +0200 Subject: [PATCH 06/11] fixes+ --- Program.cs | 153 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 59 deletions(-) diff --git a/Program.cs b/Program.cs index 0d5df62..9cb2091 100644 --- a/Program.cs +++ b/Program.cs @@ -5,18 +5,19 @@ using System.Linq; using System.Threading.Tasks; using Discord; -using Discord.Commands; using Discord.WebSocket; +using Discord.Commands; using CsvHelper; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; using Google.Apis.Services; -using Microsoft.Extensions.DependencyInjection; namespace DiscordBotExample { class Program { + private static List _imageUrls; + private static Random _random = new Random(); private static DiscordSocketClient _client; private static CommandService _commands; private static IServiceProvider _services; @@ -28,45 +29,51 @@ class Program static async Task Main(string[] args) { + // Read environment variables var token = Environment.GetEnvironmentVariable("DISCORD_BOT_TOKEN"); var channelIdStr = Environment.GetEnvironmentVariable("DISCORD_CHANNEL_ID"); _fileId = Environment.GetEnvironmentVariable("GOOGLE_DRIVE_FILE_ID"); _credentialsPath = Environment.GetEnvironmentVariable("GOOGLE_CREDENTIALS_PATH"); var postTimeStr = Environment.GetEnvironmentVariable("POST_TIME"); + // Check if token, channelId, fileId, credentialsPath, or postTime is null or empty if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(channelIdStr) || string.IsNullOrEmpty(_fileId) || string.IsNullOrEmpty(_credentialsPath) || string.IsNullOrEmpty(postTimeStr)) { Console.WriteLine("Environment variables are not set correctly."); + Console.WriteLine($"DISCORD_BOT_TOKEN: {(string.IsNullOrEmpty(token) ? "Not set" : "Set")}"); + Console.WriteLine($"DISCORD_CHANNEL_ID: {(string.IsNullOrEmpty(channelIdStr) ? "Not set" : "Set")}"); + Console.WriteLine($"GOOGLE_DRIVE_FILE_ID: {(string.IsNullOrEmpty(_fileId) ? "Not set" : "Set")}"); + Console.WriteLine($"GOOGLE_CREDENTIALS_PATH: {(string.IsNullOrEmpty(_credentialsPath) ? "Not set" : "Set")}"); + Console.WriteLine($"POST_TIME: {(string.IsNullOrEmpty(postTimeStr) ? "Not set" : "Set")}"); return; } + // Parse channel ID if (!ulong.TryParse(channelIdStr, out _channelId)) { Console.WriteLine("Invalid DISCORD_CHANNEL_ID format."); return; } + // Parse post time if (!TimeSpan.TryParse(postTimeStr, out _postTimeSpain)) { Console.WriteLine("Invalid POST_TIME format. It must be in the format HH:mm:ss."); return; } + // Initialize the Discord client and command service _client = new DiscordSocketClient(); _commands = new CommandService(); - _services = new ServiceCollection() - .AddSingleton(_client) - .AddSingleton(_commands) - .BuildServiceProvider(); - _client.Log += Log; _client.Ready += OnReady; _client.MessageReceived += HandleCommandAsync; + // Start the bot await _client.LoginAsync(TokenType.Bot, token); await _client.StartAsync(); - // Prevent application from closing + // Block the application until it is closed await Task.Delay(-1); } @@ -80,35 +87,64 @@ private static async Task OnReady() { Console.WriteLine("Bot is connected."); - // Call asynchronous tasks without blocking - await Task.Run(() => RegisterCommandsAsync()).ConfigureAwait(false); - await Task.Run(() => ScheduleNextPost()).ConfigureAwait(false); + // Download and process the CSV file from Google Drive + var csvData = await DownloadCsvFromGoogleDrive(); + + if (csvData != null) + { + using (var reader = new StringReader(csvData)) + using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) + { + _imageUrls = csv.GetRecords() + .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") + .Select(record => record.image_url.Trim()) + .ToList(); + } + + Console.WriteLine("Filtered URLs read from CSV:"); + foreach (var url in _imageUrls) + { + Console.WriteLine(url); + } + } + else + { + Console.WriteLine("Failed to download or read the CSV file. Exiting..."); + return; + } + + // Check if imageUrls is empty + if (_imageUrls.Count == 0) + { + Console.WriteLine("No valid URLs available. Exiting..."); + return; + } + + // Register commands + await RegisterCommandsAsync(); + + // Schedule the first post + await ScheduleNextPost(); } private static async Task RegisterCommandsAsync() { - await _commands.AddModuleAsync(_services); + // Add commands to the CommandService + await _commands.AddModulesAsync(typeof(Program).Assembly, _services); } - private static async Task HandleCommandAsync(SocketMessage messageParam) + private static async Task HandleCommandAsync(SocketMessage arg) { - var message = messageParam as SocketUserMessage; + var message = arg as SocketUserMessage; var context = new SocketCommandContext(_client, message); - if (message == null || message.Author.IsBot) - return; + if (message.Author.IsBot) return; int argPos = 0; - if (message.HasStringPrefix("/", ref argPos)) { var result = await _commands.ExecuteAsync(context, argPos, _services); - - if (!result.IsSuccess) - { - Console.WriteLine($"Command failed: {result.ErrorReason}"); - await context.Channel.SendMessageAsync($"Error: {result.ErrorReason}"); - } + if (!result.IsSuccess) Console.WriteLine(result.ErrorReason); } } @@ -116,27 +152,37 @@ private static async Task ScheduleNextPost() { var nowUtc = DateTime.UtcNow; var spainTime = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, _spainTimeZone); + + // Specify that nextPostTimeSpain is unspecified in terms of kind because we will convert it to a specific time zone var nextPostTimeSpain = DateTime.SpecifyKind(DateTime.Today.Add(_postTimeSpain), DateTimeKind.Unspecified); if (nextPostTimeSpain <= spainTime) { + // If the time has already passed for today, schedule for tomorrow nextPostTimeSpain = nextPostTimeSpain.AddDays(1); } + // Convert the unspecified time to Spain time zone and then to UTC nextPostTimeSpain = TimeZoneInfo.ConvertTimeToUtc(nextPostTimeSpain, _spainTimeZone); + + // Calculate the delay var delay = nextPostTimeSpain - nowUtc; Console.WriteLine($"Scheduling next post in {delay.TotalMinutes} minutes."); + await Task.Delay(delay); await PostRandomImageUrl(); + + // Schedule the next post await ScheduleNextPost(); } - public static async Task DownloadCsvFromGoogleDrive() + private static async Task DownloadCsvFromGoogleDrive() { try { + // Set up Google Drive API service var credential = GoogleCredential.FromFile(_credentialsPath) .CreateScoped(DriveService.Scope.DriveReadonly); @@ -146,9 +192,9 @@ public static async Task DownloadCsvFromGoogleDrive() ApplicationName = "DiscordBotExample", }); + // Download the file var request = service.Files.Get(_fileId); var stream = new MemoryStream(); - request.MediaDownloader.ProgressChanged += progress => { if (progress.Status == Google.Apis.Download.DownloadStatus.Completed) @@ -172,57 +218,46 @@ public static async Task DownloadCsvFromGoogleDrive() } } - public static async Task PostRandomImageUrl() + private static async Task PostRandomImageUrl() { var channel = _client.GetChannel(_channelId) as IMessageChannel; - if (channel != null) + if (channel != null && _imageUrls.Count > 0) { - string csvData = await DownloadCsvFromGoogleDrive(); + int index = _random.Next(_imageUrls.Count); + string randomUrl = _imageUrls[index]; + await channel.SendMessageAsync(randomUrl); + } + else + { + Console.WriteLine("No URLs available."); + } + } - if (!string.IsNullOrEmpty(csvData)) + [Group("image")] + public class ImageCommands : ModuleBase + { + [Command("send")] + public async Task SendRandomImage() + { + if (Program._imageUrls.Count > 0) { - using (var reader = new StringReader(csvData)) - using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) - { - var imageUrls = csv.GetRecords() - .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") - .Select(record => record.image_url.Trim()) - .ToArray(); - - if (imageUrls.Length > 0) - { - int index = new Random().Next(imageUrls.Length); - string randomUrl = imageUrls[index]; - await channel.SendMessageAsync(randomUrl); - } - else - { - Console.WriteLine("No valid URLs available."); - } - } + int index = Program._random.Next(Program._imageUrls.Count); + string randomUrl = Program._imageUrls[index]; + await Context.Channel.SendMessageAsync(randomUrl); } else { - Console.WriteLine("Failed to download or read the CSV file."); + await Context.Channel.SendMessageAsync("No URLs available."); } } } } + // Define a class that matches the CSV structure public class YourRecordClass { public string image_url { get; set; } public string has_spoilers { get; set; } } - - public class CommandModule : ModuleBase - { - [Command("send")] - public async Task SendRandomImage() - { - await Program.PostRandomImageUrl(); - await ReplyAsync("Random image sent!"); - } - } } From eb749224bf5ac52fd9ef01d86e648d45a9d1862c Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:41:55 +0200 Subject: [PATCH 07/11] change how commands are handled --- Program.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Program.cs b/Program.cs index 9cb2091..6d8c205 100644 --- a/Program.cs +++ b/Program.cs @@ -40,11 +40,6 @@ static async Task Main(string[] args) if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(channelIdStr) || string.IsNullOrEmpty(_fileId) || string.IsNullOrEmpty(_credentialsPath) || string.IsNullOrEmpty(postTimeStr)) { Console.WriteLine("Environment variables are not set correctly."); - Console.WriteLine($"DISCORD_BOT_TOKEN: {(string.IsNullOrEmpty(token) ? "Not set" : "Set")}"); - Console.WriteLine($"DISCORD_CHANNEL_ID: {(string.IsNullOrEmpty(channelIdStr) ? "Not set" : "Set")}"); - Console.WriteLine($"GOOGLE_DRIVE_FILE_ID: {(string.IsNullOrEmpty(_fileId) ? "Not set" : "Set")}"); - Console.WriteLine($"GOOGLE_CREDENTIALS_PATH: {(string.IsNullOrEmpty(_credentialsPath) ? "Not set" : "Set")}"); - Console.WriteLine($"POST_TIME: {(string.IsNullOrEmpty(postTimeStr) ? "Not set" : "Set")}"); return; } @@ -130,7 +125,7 @@ private static async Task OnReady() private static async Task RegisterCommandsAsync() { // Add commands to the CommandService - await _commands.AddModulesAsync(typeof(Program).Assembly, _services); + await _commands.AddModuleAsync(null); } private static async Task HandleCommandAsync(SocketMessage arg) @@ -234,7 +229,6 @@ private static async Task PostRandomImageUrl() } } - [Group("image")] public class ImageCommands : ModuleBase { [Command("send")] From ab6db9acaa124b4459bfc3b4a5f5f23600c22414 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:46:35 +0200 Subject: [PATCH 08/11] Commands should be fixed now --- Program.cs | 76 +++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Program.cs b/Program.cs index 6d8c205..01ae3e6 100644 --- a/Program.cs +++ b/Program.cs @@ -7,6 +7,7 @@ using Discord; using Discord.WebSocket; using Discord.Commands; +using Discord.Rest; using CsvHelper; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; @@ -19,8 +20,6 @@ class Program private static List _imageUrls; private static Random _random = new Random(); private static DiscordSocketClient _client; - private static CommandService _commands; - private static IServiceProvider _services; private static ulong _channelId; private static string _fileId; private static string _credentialsPath; @@ -57,12 +56,11 @@ static async Task Main(string[] args) return; } - // Initialize the Discord client and command service + // Initialize the Discord client _client = new DiscordSocketClient(); - _commands = new CommandService(); _client.Log += Log; _client.Ready += OnReady; - _client.MessageReceived += HandleCommandAsync; + _client.InteractionCreated += HandleInteractionAsync; // Start the bot await _client.LoginAsync(TokenType.Bot, token); @@ -124,22 +122,43 @@ private static async Task OnReady() private static async Task RegisterCommandsAsync() { - // Add commands to the CommandService - await _commands.AddModuleAsync(null); + var sendCommand = new SlashCommandBuilder() + .WithName("send") + .WithDescription("Send a random image from the list"); + + // Replace 'your_guild_id_here' with your actual guild ID + var guildId = ulong.Parse(Environment.GetEnvironmentVariable("GUILD_ID")); // Example: 123456789012345678 + var guild = _client.GetGuild(guildId); + + await guild.DeleteApplicationCommandsAsync(); // Clear existing commands in the guild + await _client.Rest.DeleteAllGlobalCommandsAsync(); // Optionally clear global commands + await guild.CreateApplicationCommandAsync(sendCommand.Build()); + + Console.WriteLine("Slash command /send registered for guild"); } - private static async Task HandleCommandAsync(SocketMessage arg) + private static async Task HandleInteractionAsync(SocketInteraction interaction) { - var message = arg as SocketUserMessage; - var context = new SocketCommandContext(_client, message); - - if (message.Author.IsBot) return; + if (interaction is SocketSlashCommand command) + { + if (command.Data.Name == "send") + { + await HandleSendCommandAsync(command); + } + } + } - int argPos = 0; - if (message.HasStringPrefix("/", ref argPos)) + private static async Task HandleSendCommandAsync(SocketSlashCommand command) + { + if (_imageUrls.Count > 0) + { + int index = _random.Next(_imageUrls.Count); + string randomUrl = _imageUrls[index]; + await command.RespondAsync(randomUrl); + } + else { - var result = await _commands.ExecuteAsync(context, argPos, _services); - if (!result.IsSuccess) Console.WriteLine(result.ErrorReason); + await command.RespondAsync("No URLs available."); } } @@ -229,29 +248,10 @@ private static async Task PostRandomImageUrl() } } - public class ImageCommands : ModuleBase + public class YourRecordClass { - [Command("send")] - public async Task SendRandomImage() - { - if (Program._imageUrls.Count > 0) - { - int index = Program._random.Next(Program._imageUrls.Count); - string randomUrl = Program._imageUrls[index]; - await Context.Channel.SendMessageAsync(randomUrl); - } - else - { - await Context.Channel.SendMessageAsync("No URLs available."); - } - } + public string image_url { get; set; } + public string has_spoilers { get; set; } } } - - // Define a class that matches the CSV structure - public class YourRecordClass - { - public string image_url { get; set; } - public string has_spoilers { get; set; } - } } From 1f49185997a6e4f3f16a8ae6fdcf6ead61884ed2 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 21:49:50 +0200 Subject: [PATCH 09/11] Failsafe added in case someone use "/send" before downloading and loading the csv --- Program.cs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Program.cs b/Program.cs index 01ae3e6..819cc34 100644 --- a/Program.cs +++ b/Program.cs @@ -25,6 +25,7 @@ class Program private static string _credentialsPath; private static TimeSpan _postTimeSpain; private static TimeZoneInfo _spainTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); + private static bool _isImageUrlsLoaded = false; // Flag to track if image URLs are loaded static async Task Main(string[] args) { @@ -92,6 +93,8 @@ private static async Task OnReady() .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") .Select(record => record.image_url.Trim()) .ToList(); + + _isImageUrlsLoaded = true; // Set flag to true when URLs are loaded } Console.WriteLine("Filtered URLs read from CSV:"); @@ -106,13 +109,6 @@ private static async Task OnReady() return; } - // Check if imageUrls is empty - if (_imageUrls.Count == 0) - { - Console.WriteLine("No valid URLs available. Exiting..."); - return; - } - // Register commands await RegisterCommandsAsync(); @@ -150,15 +146,22 @@ private static async Task HandleInteractionAsync(SocketInteraction interaction) private static async Task HandleSendCommandAsync(SocketSlashCommand command) { - if (_imageUrls.Count > 0) + if (_isImageUrlsLoaded) { - int index = _random.Next(_imageUrls.Count); - string randomUrl = _imageUrls[index]; - await command.RespondAsync(randomUrl); + if (_imageUrls.Count > 0) + { + int index = _random.Next(_imageUrls.Count); + string randomUrl = _imageUrls[index]; + await command.RespondAsync(randomUrl); + } + else + { + await command.RespondAsync("No URLs available."); + } } else { - await command.RespondAsync("No URLs available."); + await command.RespondAsync("The bot is still loading data. Please try again later."); } } From 7821ec97c24fa8aa6c7c94046fd1eb3d7bd3e177 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 22:00:26 +0200 Subject: [PATCH 10/11] Code optimizations --- Program.cs | 152 ++++++++++++++++++++++++++--------------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/Program.cs b/Program.cs index 819cc34..7b1233c 100644 --- a/Program.cs +++ b/Program.cs @@ -6,8 +6,6 @@ using System.Threading.Tasks; using Discord; using Discord.WebSocket; -using Discord.Commands; -using Discord.Rest; using CsvHelper; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; @@ -17,7 +15,7 @@ namespace DiscordBotExample { class Program { - private static List _imageUrls; + private static List _imageUrls = new List(); private static Random _random = new Random(); private static DiscordSocketClient _client; private static ulong _channelId; @@ -25,7 +23,7 @@ class Program private static string _credentialsPath; private static TimeSpan _postTimeSpain; private static TimeZoneInfo _spainTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); - private static bool _isImageUrlsLoaded = false; // Flag to track if image URLs are loaded + private static bool _isImageUrlsLoaded = false; static async Task Main(string[] args) { @@ -36,7 +34,7 @@ static async Task Main(string[] args) _credentialsPath = Environment.GetEnvironmentVariable("GOOGLE_CREDENTIALS_PATH"); var postTimeStr = Environment.GetEnvironmentVariable("POST_TIME"); - // Check if token, channelId, fileId, credentialsPath, or postTime is null or empty + // Validate environment variables if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(channelIdStr) || string.IsNullOrEmpty(_fileId) || string.IsNullOrEmpty(_credentialsPath) || string.IsNullOrEmpty(postTimeStr)) { Console.WriteLine("Environment variables are not set correctly."); @@ -81,34 +79,6 @@ private static async Task OnReady() { Console.WriteLine("Bot is connected."); - // Download and process the CSV file from Google Drive - var csvData = await DownloadCsvFromGoogleDrive(); - - if (csvData != null) - { - using (var reader = new StringReader(csvData)) - using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) - { - _imageUrls = csv.GetRecords() - .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") - .Select(record => record.image_url.Trim()) - .ToList(); - - _isImageUrlsLoaded = true; // Set flag to true when URLs are loaded - } - - Console.WriteLine("Filtered URLs read from CSV:"); - foreach (var url in _imageUrls) - { - Console.WriteLine(url); - } - } - else - { - Console.WriteLine("Failed to download or read the CSV file. Exiting..."); - return; - } - // Register commands await RegisterCommandsAsync(); @@ -122,7 +92,6 @@ private static async Task RegisterCommandsAsync() .WithName("send") .WithDescription("Send a random image from the list"); - // Replace 'your_guild_id_here' with your actual guild ID var guildId = ulong.Parse(Environment.GetEnvironmentVariable("GUILD_ID")); // Example: 123456789012345678 var guild = _client.GetGuild(guildId); @@ -135,33 +104,36 @@ private static async Task RegisterCommandsAsync() private static async Task HandleInteractionAsync(SocketInteraction interaction) { - if (interaction is SocketSlashCommand command) + if (interaction is SocketSlashCommand command && command.Data.Name == "send") { - if (command.Data.Name == "send") - { - await HandleSendCommandAsync(command); - } + await HandleSendCommandAsync(command); } } private static async Task HandleSendCommandAsync(SocketSlashCommand command) { - if (_isImageUrlsLoaded) + if (!_isImageUrlsLoaded) { - if (_imageUrls.Count > 0) - { - int index = _random.Next(_imageUrls.Count); - string randomUrl = _imageUrls[index]; - await command.RespondAsync(randomUrl); - } - else - { - await command.RespondAsync("No URLs available."); - } + await command.RespondAsync("The bot is still loading data. Please try again later."); + return; + } + + if (_imageUrls.Count > 0) + { + string randomUrl = GetRandomImageUrl(); + await command.RespondAsync(randomUrl); } else { - await command.RespondAsync("The bot is still loading data. Please try again later."); + await command.RespondAsync("No URLs available."); + } + } + + private static string GetRandomImageUrl() + { + lock (_random) // Ensure thread safety if accessing from multiple threads + { + return _imageUrls[_random.Next(_imageUrls.Count)]; } } @@ -170,19 +142,14 @@ private static async Task ScheduleNextPost() var nowUtc = DateTime.UtcNow; var spainTime = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, _spainTimeZone); - // Specify that nextPostTimeSpain is unspecified in terms of kind because we will convert it to a specific time zone var nextPostTimeSpain = DateTime.SpecifyKind(DateTime.Today.Add(_postTimeSpain), DateTimeKind.Unspecified); if (nextPostTimeSpain <= spainTime) { - // If the time has already passed for today, schedule for tomorrow nextPostTimeSpain = nextPostTimeSpain.AddDays(1); } - // Convert the unspecified time to Spain time zone and then to UTC nextPostTimeSpain = TimeZoneInfo.ConvertTimeToUtc(nextPostTimeSpain, _spainTimeZone); - - // Calculate the delay var delay = nextPostTimeSpain - nowUtc; Console.WriteLine($"Scheduling next post in {delay.TotalMinutes} minutes."); @@ -190,16 +157,66 @@ private static async Task ScheduleNextPost() await Task.Delay(delay); await PostRandomImageUrl(); - - // Schedule the next post await ScheduleNextPost(); } + private static async Task PostRandomImageUrl() + { + var channel = _client.GetChannel(_channelId) as IMessageChannel; + + if (channel != null) + { + if (!_isImageUrlsLoaded) + { + await channel.SendMessageAsync("The bot is still loading data. Please try again later."); + return; + } + + if (_imageUrls.Count > 0) + { + string randomUrl = GetRandomImageUrl(); + await channel.SendMessageAsync(randomUrl); + } + else + { + await channel.SendMessageAsync("No URLs available."); + } + } + } + + private static async Task LoadImageUrls() + { + var csvData = await DownloadCsvFromGoogleDrive(); + + if (csvData != null) + { + using (var reader = new StringReader(csvData)) + using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) + { + _imageUrls = csv.GetRecords() + .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") + .Select(record => record.image_url.Trim()) + .ToList(); + + _isImageUrlsLoaded = true; // Set flag to true when URLs are loaded + } + + Console.WriteLine("Filtered URLs read from CSV:"); + foreach (var url in _imageUrls) + { + Console.WriteLine(url); + } + } + else + { + Console.WriteLine("Failed to download or read the CSV file."); + } + } + private static async Task DownloadCsvFromGoogleDrive() { try { - // Set up Google Drive API service var credential = GoogleCredential.FromFile(_credentialsPath) .CreateScoped(DriveService.Scope.DriveReadonly); @@ -209,7 +226,6 @@ private static async Task DownloadCsvFromGoogleDrive() ApplicationName = "DiscordBotExample", }); - // Download the file var request = service.Files.Get(_fileId); var stream = new MemoryStream(); request.MediaDownloader.ProgressChanged += progress => @@ -235,22 +251,6 @@ private static async Task DownloadCsvFromGoogleDrive() } } - private static async Task PostRandomImageUrl() - { - var channel = _client.GetChannel(_channelId) as IMessageChannel; - - if (channel != null && _imageUrls.Count > 0) - { - int index = _random.Next(_imageUrls.Count); - string randomUrl = _imageUrls[index]; - await channel.SendMessageAsync(randomUrl); - } - else - { - Console.WriteLine("No URLs available."); - } - } - public class YourRecordClass { public string image_url { get; set; } From 690337496b40c6327f53cc431852c814c66a5582 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Sep 2024 22:03:12 +0200 Subject: [PATCH 11/11] Reverted --- Program.cs | 152 ++++++++++++++++++++++++++--------------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/Program.cs b/Program.cs index 7b1233c..819cc34 100644 --- a/Program.cs +++ b/Program.cs @@ -6,6 +6,8 @@ using System.Threading.Tasks; using Discord; using Discord.WebSocket; +using Discord.Commands; +using Discord.Rest; using CsvHelper; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; @@ -15,7 +17,7 @@ namespace DiscordBotExample { class Program { - private static List _imageUrls = new List(); + private static List _imageUrls; private static Random _random = new Random(); private static DiscordSocketClient _client; private static ulong _channelId; @@ -23,7 +25,7 @@ class Program private static string _credentialsPath; private static TimeSpan _postTimeSpain; private static TimeZoneInfo _spainTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); - private static bool _isImageUrlsLoaded = false; + private static bool _isImageUrlsLoaded = false; // Flag to track if image URLs are loaded static async Task Main(string[] args) { @@ -34,7 +36,7 @@ static async Task Main(string[] args) _credentialsPath = Environment.GetEnvironmentVariable("GOOGLE_CREDENTIALS_PATH"); var postTimeStr = Environment.GetEnvironmentVariable("POST_TIME"); - // Validate environment variables + // Check if token, channelId, fileId, credentialsPath, or postTime is null or empty if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(channelIdStr) || string.IsNullOrEmpty(_fileId) || string.IsNullOrEmpty(_credentialsPath) || string.IsNullOrEmpty(postTimeStr)) { Console.WriteLine("Environment variables are not set correctly."); @@ -79,6 +81,34 @@ private static async Task OnReady() { Console.WriteLine("Bot is connected."); + // Download and process the CSV file from Google Drive + var csvData = await DownloadCsvFromGoogleDrive(); + + if (csvData != null) + { + using (var reader = new StringReader(csvData)) + using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) + { + _imageUrls = csv.GetRecords() + .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") + .Select(record => record.image_url.Trim()) + .ToList(); + + _isImageUrlsLoaded = true; // Set flag to true when URLs are loaded + } + + Console.WriteLine("Filtered URLs read from CSV:"); + foreach (var url in _imageUrls) + { + Console.WriteLine(url); + } + } + else + { + Console.WriteLine("Failed to download or read the CSV file. Exiting..."); + return; + } + // Register commands await RegisterCommandsAsync(); @@ -92,6 +122,7 @@ private static async Task RegisterCommandsAsync() .WithName("send") .WithDescription("Send a random image from the list"); + // Replace 'your_guild_id_here' with your actual guild ID var guildId = ulong.Parse(Environment.GetEnvironmentVariable("GUILD_ID")); // Example: 123456789012345678 var guild = _client.GetGuild(guildId); @@ -104,36 +135,33 @@ private static async Task RegisterCommandsAsync() private static async Task HandleInteractionAsync(SocketInteraction interaction) { - if (interaction is SocketSlashCommand command && command.Data.Name == "send") + if (interaction is SocketSlashCommand command) { - await HandleSendCommandAsync(command); + if (command.Data.Name == "send") + { + await HandleSendCommandAsync(command); + } } } private static async Task HandleSendCommandAsync(SocketSlashCommand command) { - if (!_isImageUrlsLoaded) - { - await command.RespondAsync("The bot is still loading data. Please try again later."); - return; - } - - if (_imageUrls.Count > 0) + if (_isImageUrlsLoaded) { - string randomUrl = GetRandomImageUrl(); - await command.RespondAsync(randomUrl); + if (_imageUrls.Count > 0) + { + int index = _random.Next(_imageUrls.Count); + string randomUrl = _imageUrls[index]; + await command.RespondAsync(randomUrl); + } + else + { + await command.RespondAsync("No URLs available."); + } } else { - await command.RespondAsync("No URLs available."); - } - } - - private static string GetRandomImageUrl() - { - lock (_random) // Ensure thread safety if accessing from multiple threads - { - return _imageUrls[_random.Next(_imageUrls.Count)]; + await command.RespondAsync("The bot is still loading data. Please try again later."); } } @@ -142,14 +170,19 @@ private static async Task ScheduleNextPost() var nowUtc = DateTime.UtcNow; var spainTime = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, _spainTimeZone); + // Specify that nextPostTimeSpain is unspecified in terms of kind because we will convert it to a specific time zone var nextPostTimeSpain = DateTime.SpecifyKind(DateTime.Today.Add(_postTimeSpain), DateTimeKind.Unspecified); if (nextPostTimeSpain <= spainTime) { + // If the time has already passed for today, schedule for tomorrow nextPostTimeSpain = nextPostTimeSpain.AddDays(1); } + // Convert the unspecified time to Spain time zone and then to UTC nextPostTimeSpain = TimeZoneInfo.ConvertTimeToUtc(nextPostTimeSpain, _spainTimeZone); + + // Calculate the delay var delay = nextPostTimeSpain - nowUtc; Console.WriteLine($"Scheduling next post in {delay.TotalMinutes} minutes."); @@ -157,66 +190,16 @@ private static async Task ScheduleNextPost() await Task.Delay(delay); await PostRandomImageUrl(); - await ScheduleNextPost(); - } - - private static async Task PostRandomImageUrl() - { - var channel = _client.GetChannel(_channelId) as IMessageChannel; - - if (channel != null) - { - if (!_isImageUrlsLoaded) - { - await channel.SendMessageAsync("The bot is still loading data. Please try again later."); - return; - } - - if (_imageUrls.Count > 0) - { - string randomUrl = GetRandomImageUrl(); - await channel.SendMessageAsync(randomUrl); - } - else - { - await channel.SendMessageAsync("No URLs available."); - } - } - } - - private static async Task LoadImageUrls() - { - var csvData = await DownloadCsvFromGoogleDrive(); - if (csvData != null) - { - using (var reader = new StringReader(csvData)) - using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture))) - { - _imageUrls = csv.GetRecords() - .Where(record => !string.IsNullOrWhiteSpace(record.image_url) && record.has_spoilers != "yes") - .Select(record => record.image_url.Trim()) - .ToList(); - - _isImageUrlsLoaded = true; // Set flag to true when URLs are loaded - } - - Console.WriteLine("Filtered URLs read from CSV:"); - foreach (var url in _imageUrls) - { - Console.WriteLine(url); - } - } - else - { - Console.WriteLine("Failed to download or read the CSV file."); - } + // Schedule the next post + await ScheduleNextPost(); } private static async Task DownloadCsvFromGoogleDrive() { try { + // Set up Google Drive API service var credential = GoogleCredential.FromFile(_credentialsPath) .CreateScoped(DriveService.Scope.DriveReadonly); @@ -226,6 +209,7 @@ private static async Task DownloadCsvFromGoogleDrive() ApplicationName = "DiscordBotExample", }); + // Download the file var request = service.Files.Get(_fileId); var stream = new MemoryStream(); request.MediaDownloader.ProgressChanged += progress => @@ -251,6 +235,22 @@ private static async Task DownloadCsvFromGoogleDrive() } } + private static async Task PostRandomImageUrl() + { + var channel = _client.GetChannel(_channelId) as IMessageChannel; + + if (channel != null && _imageUrls.Count > 0) + { + int index = _random.Next(_imageUrls.Count); + string randomUrl = _imageUrls[index]; + await channel.SendMessageAsync(randomUrl); + } + else + { + Console.WriteLine("No URLs available."); + } + } + public class YourRecordClass { public string image_url { get; set; }