From 390e1284cbcfc4d2f4b3256090a74fcd9915cde9 Mon Sep 17 00:00:00 2001 From: jim Date: Fri, 3 Apr 2026 13:23:38 -0500 Subject: [PATCH] Add support for selecting Tumblr API credentials via CLI Allows switching between multiple Tumblr API credential sets at runtime using new command-line arguments (-api3, -api4, -api [section]). API keys are now read from the selected section in appsettings.json, with validation for required keys. Also updates the SQL query for selecting blogs needing likes to use more precise filtering and ordering. Usage/help output is updated to reflect new options. --- URLNotesGrabberCORE/DataAccess.cs | 35 +++++++++++++++++----- URLNotesGrabberCORE/Program.cs | 45 ++++++++++++++++++++++++++++ URLNotesGrabberCORE/appsettings.json | 12 ++++++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/URLNotesGrabberCORE/DataAccess.cs b/URLNotesGrabberCORE/DataAccess.cs index 01759a8..61b6032 100644 --- a/URLNotesGrabberCORE/DataAccess.cs +++ b/URLNotesGrabberCORE/DataAccess.cs @@ -767,7 +767,7 @@ namespace URLNotesGrabberCORE if (!string.IsNullOrEmpty(specificBlog)) sql = "SELECT BlogName, COALESCE(LikesPulled, 0), COALESCE(LikesCursor, 0) FROM Blogs WHERE BlogName = @blog"; else - sql = "SELECT BlogName, COALESCE(LikesPulled, 0), COALESCE(LikesCursor, 0) FROM Blogs WHERE COALESCE(LikesPulled, 0) = 0"; + sql = "SELECT B.BlogName, COALESCE(LikesPulled, 0), COALESCE(LikesCursor, 0) FROM Blogs B INNER JOIN Notes N ON N.NoteBlogName = B.BlogName WHERE B.LikesPulled = 0 AND N.TimeStamp >= 1535778000 AND N.rootBlogName = B.BlogName AND EXISTS (SELECT 1 FROM Posts P WHERE P.BlogName = B.BlogName) GROUP BY B.BlogName ORDER BY MIN(N.Timestamp);"; using (SQLiteCommand command = new SQLiteCommand(sql, connection)) { @@ -1280,6 +1280,7 @@ namespace URLNotesGrabberCORE { public static string Blog { get; set; } = string.Empty; private static IConfiguration Configuration { get; set; } = null!; + private static string ApiConfigSection { get; set; } = "TumblrApi"; static APIAccess() { @@ -1288,17 +1289,37 @@ namespace URLNotesGrabberCORE .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); - if (Configuration["TumblrApi:ConsumerKey"] == null) - throw new InvalidOperationException("TumblrApi configuration is missing in appsettings.json"); + ValidateApiSection(ApiConfigSection); } - private static string ConsumerKey => Configuration["TumblrApi:ConsumerKey"] ?? + public static void SetApiConfigSection(string sectionName) + { + if (string.IsNullOrWhiteSpace(sectionName)) + sectionName = "TumblrApi"; + + ValidateApiSection(sectionName); + ApiConfigSection = sectionName; + Console.WriteLine($"Using API settings section: {ApiConfigSection}"); + } + + private static void ValidateApiSection(string sectionName) + { + if (Configuration[$"{sectionName}:ConsumerKey"] == null || + Configuration[$"{sectionName}:ConsumerSecret"] == null || + Configuration[$"{sectionName}:OAuthToken"] == null || + Configuration[$"{sectionName}:OAuthTokenSecret"] == null) + { + throw new InvalidOperationException($"{sectionName} configuration is missing required keys in appsettings.json"); + } + } + + private static string ConsumerKey => Configuration[$"{ApiConfigSection}:ConsumerKey"] ?? throw new InvalidOperationException("ConsumerKey is not configured"); - private static string ConsumerSecret => Configuration["TumblrApi:ConsumerSecret"] ?? + private static string ConsumerSecret => Configuration[$"{ApiConfigSection}:ConsumerSecret"] ?? throw new InvalidOperationException("ConsumerSecret is not configured"); - private static string OAuthToken => Configuration["TumblrApi:OAuthToken"] ?? + private static string OAuthToken => Configuration[$"{ApiConfigSection}:OAuthToken"] ?? throw new InvalidOperationException("OAuthToken is not configured"); - private static string OAuthTokenSecret => Configuration["TumblrApi:OAuthTokenSecret"] ?? + private static string OAuthTokenSecret => Configuration[$"{ApiConfigSection}:OAuthTokenSecret"] ?? throw new InvalidOperationException("OAuthTokenSecret is not configured"); private static int GetRetryDelaySecondsFromHeaders(IEnumerable? headers) diff --git a/URLNotesGrabberCORE/Program.cs b/URLNotesGrabberCORE/Program.cs index 07f14f9..bbfa6c3 100644 --- a/URLNotesGrabberCORE/Program.cs +++ b/URLNotesGrabberCORE/Program.cs @@ -21,6 +21,45 @@ namespace URLNotesGrabberCORE .Build(); var settings = config.GetSection("appSettings"); + string apiSectionName = "TumblrApi"; + List filteredArgs = new List(); + for (int i = 0; i < args.Length; i++) + { + if (string.Equals(args[i], "-api3", StringComparison.OrdinalIgnoreCase) || + string.Equals(args[i], "--api3", StringComparison.OrdinalIgnoreCase)) + { + apiSectionName = "TumblrApi3"; + continue; + } + + if (string.Equals(args[i], "-api4", StringComparison.OrdinalIgnoreCase) || + string.Equals(args[i], "--api4", StringComparison.OrdinalIgnoreCase)) + { + apiSectionName = "TumblrApi4"; + continue; + } + + if (string.Equals(args[i], "-api", StringComparison.OrdinalIgnoreCase) || + string.Equals(args[i], "--api", StringComparison.OrdinalIgnoreCase)) + { + if (i + 1 < args.Length && !string.IsNullOrWhiteSpace(args[i + 1])) + { + apiSectionName = args[i + 1].Trim(); + i++; + } + else + { + Console.WriteLine("--Missing API section after -api/--api. Using default TumblrApi.--"); + } + continue; + } + + filteredArgs.Add(args[i]); + } + + args = filteredArgs.ToArray(); + APIAccess.SetApiConfigSection(apiSectionName); + // Setup Dual Logging string logPath = "console_output.log"; if (File.Exists(logPath)) @@ -77,6 +116,12 @@ namespace URLNotesGrabberCORE Console.WriteLine("-likes\t Fetch likes for all blogs needing it (LikesPulled=0), or a specific blog via param"); + Console.WriteLine("-api3\t Use TumblrApi3 settings from appsettings.json"); + + Console.WriteLine("-api4\t Use TumblrApi4 settings from appsettings.json"); + + Console.WriteLine("-api [section]\t Use a specific API settings section from appsettings.json (e.g. TumblrApi3)"); + break; case "-parse": diff --git a/URLNotesGrabberCORE/appsettings.json b/URLNotesGrabberCORE/appsettings.json index 69ff7c5..69358fd 100644 --- a/URLNotesGrabberCORE/appsettings.json +++ b/URLNotesGrabberCORE/appsettings.json @@ -15,5 +15,17 @@ "ConsumerSecret": "sA8BwNVTVKqBRRJmHbAD6NuyKPJ3bb9cei2bYhMgqT8cLX8tSG", "OAuthToken": "HPJI6IijHoKN6WzBumG7KjS7g01iCu07jQsulpueKbWN1ZJ35J", "OAuthTokenSecret": "ajkN0Z1kKrsJaIXZEDI8zLsjRSQxFkfgO1i5k78FuQYCQjSY7w" + }, + "TumblrApi3": { + "ConsumerKey": "Jmoh13AS9hKBYsf939uENQsiWUuZJLFT3do4YK9P0bpspVuxkH", + "ConsumerSecret": "8LcHjuqpOS9gZZPaML1joT330w5NqyGdreSqSBazdAWCADzcms", + "OAuthToken": "Gm2esFTeb5LKsb4A2YJGrF2Udycfc0AF8afof2qNGyzNSq1qWM", + "OAuthTokenSecret": "Kfokor80s5fVue91OmNHidCdILZ9AnHtRplrct2P6Q75jgNTLa" + }, + "TumblrApi4": { + "ConsumerKey": "gOuydIEENkRmEvvuf57R69yPRb39FC0Egb9p6ntxWuCuFzlsV3", + "ConsumerSecret": "0my913slXAHgra4mEEYV98stJ3wXGjPTWJeviiZXwJ2AqzYtIF ", + "OAuthToken": "JHk08YufSrsetTR1Ekuh10cPOzC6rjqU5WwVu3uV4vvLzp0w8w", + "OAuthTokenSecret": "1oryr4WMBxuDrt4vL4sJQs5qNagkH3KxwIf61M8SfvklTrhHSb" } } \ No newline at end of file