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.
This commit is contained in:
jim
2026-04-03 13:23:38 -05:00
parent eb86bc8f66
commit 390e1284cb
3 changed files with 85 additions and 7 deletions
+28 -7
View File
@@ -767,7 +767,7 @@ namespace URLNotesGrabberCORE
if (!string.IsNullOrEmpty(specificBlog)) if (!string.IsNullOrEmpty(specificBlog))
sql = "SELECT BlogName, COALESCE(LikesPulled, 0), COALESCE(LikesCursor, 0) FROM Blogs WHERE BlogName = @blog"; sql = "SELECT BlogName, COALESCE(LikesPulled, 0), COALESCE(LikesCursor, 0) FROM Blogs WHERE BlogName = @blog";
else 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)) using (SQLiteCommand command = new SQLiteCommand(sql, connection))
{ {
@@ -1280,6 +1280,7 @@ namespace URLNotesGrabberCORE
{ {
public static string Blog { get; set; } = string.Empty; public static string Blog { get; set; } = string.Empty;
private static IConfiguration Configuration { get; set; } = null!; private static IConfiguration Configuration { get; set; } = null!;
private static string ApiConfigSection { get; set; } = "TumblrApi";
static APIAccess() static APIAccess()
{ {
@@ -1288,17 +1289,37 @@ namespace URLNotesGrabberCORE
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build(); .Build();
if (Configuration["TumblrApi:ConsumerKey"] == null) ValidateApiSection(ApiConfigSection);
throw new InvalidOperationException("TumblrApi configuration is missing in appsettings.json");
} }
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"); 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"); 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"); 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"); throw new InvalidOperationException("OAuthTokenSecret is not configured");
private static int GetRetryDelaySecondsFromHeaders(IEnumerable<HeaderParameter>? headers) private static int GetRetryDelaySecondsFromHeaders(IEnumerable<HeaderParameter>? headers)
+45
View File
@@ -21,6 +21,45 @@ namespace URLNotesGrabberCORE
.Build(); .Build();
var settings = config.GetSection("appSettings"); var settings = config.GetSection("appSettings");
string apiSectionName = "TumblrApi";
List<string> filteredArgs = new List<string>();
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 // Setup Dual Logging
string logPath = "console_output.log"; string logPath = "console_output.log";
if (File.Exists(logPath)) 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("-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; break;
case "-parse": case "-parse":
+12
View File
@@ -15,5 +15,17 @@
"ConsumerSecret": "sA8BwNVTVKqBRRJmHbAD6NuyKPJ3bb9cei2bYhMgqT8cLX8tSG", "ConsumerSecret": "sA8BwNVTVKqBRRJmHbAD6NuyKPJ3bb9cei2bYhMgqT8cLX8tSG",
"OAuthToken": "HPJI6IijHoKN6WzBumG7KjS7g01iCu07jQsulpueKbWN1ZJ35J", "OAuthToken": "HPJI6IijHoKN6WzBumG7KjS7g01iCu07jQsulpueKbWN1ZJ35J",
"OAuthTokenSecret": "ajkN0Z1kKrsJaIXZEDI8zLsjRSQxFkfgO1i5k78FuQYCQjSY7w" "OAuthTokenSecret": "ajkN0Z1kKrsJaIXZEDI8zLsjRSQxFkfgO1i5k78FuQYCQjSY7w"
},
"TumblrApi3": {
"ConsumerKey": "Jmoh13AS9hKBYsf939uENQsiWUuZJLFT3do4YK9P0bpspVuxkH",
"ConsumerSecret": "8LcHjuqpOS9gZZPaML1joT330w5NqyGdreSqSBazdAWCADzcms",
"OAuthToken": "Gm2esFTeb5LKsb4A2YJGrF2Udycfc0AF8afof2qNGyzNSq1qWM",
"OAuthTokenSecret": "Kfokor80s5fVue91OmNHidCdILZ9AnHtRplrct2P6Q75jgNTLa"
},
"TumblrApi4": {
"ConsumerKey": "gOuydIEENkRmEvvuf57R69yPRb39FC0Egb9p6ntxWuCuFzlsV3",
"ConsumerSecret": "0my913slXAHgra4mEEYV98stJ3wXGjPTWJeviiZXwJ2AqzYtIF ",
"OAuthToken": "JHk08YufSrsetTR1Ekuh10cPOzC6rjqU5WwVu3uV4vvLzp0w8w",
"OAuthTokenSecret": "1oryr4WMBxuDrt4vL4sJQs5qNagkH3KxwIf61M8SfvklTrhHSb"
} }
} }