diff --git a/Config.cs b/Config.cs index b5c236f..14a4dc2 100644 --- a/Config.cs +++ b/Config.cs @@ -10,15 +10,16 @@ public class Config /// /// Gets or sets the name of the schedule file. /// - public string File { get; set; } = "schedule.json"; + public string? File { get; set; } /// /// Gets or sets the directory path where the schedule file is stored. /// - public string Path { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + public string? Path { get; set; } /// /// Gets or sets the list of available topics from the configuration file. /// - public IList Topics { get; set; } = new[] { "Games", "Politics", "Research", "Technology" }; + public List Topics { get; set; } = new List(); + } diff --git a/GlobalUsings.cs b/GlobalUsings.cs index 6e12d2f..5e35567 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -1,3 +1,4 @@ +global using System.Diagnostics; global using Tomlyn; global using Tomlyn.Model; global using PublishTimes; diff --git a/Program.cs b/Program.cs index 631fc3d..c5dd133 100644 --- a/Program.cs +++ b/Program.cs @@ -18,8 +18,13 @@ Config GetConfig(string file = "config.toml") return model; } - - return new Config(); + var defaultList = new[] { "Games", "Politics", "Research", "Technology" }; + return new Config() + { + File = "schedule.json", + Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + Topics = defaultList.ToList() + }; } /// @@ -42,7 +47,7 @@ bool UserChoice(string choice) /// delay between each while avoiding time conflicts within a 30-minute window. /// /// A list of TimeSpan objects representing scheduled article times. -List GenerateSchedule() +List GenerateTimes() { var numberOfArticles = 5; // Define how many articles to schedule var startTime = new TimeSpan(9, 0, 0); // Starting time at 9:00 AM @@ -149,7 +154,7 @@ string NewTopic(List topics) void ExportSchedule(List storeTimes) { // App directory is used for config file - var appDir = AppDomain.CurrentDomain.BaseDirectory; + var appDir = Tracer.AppDirectory; // File directory is used for file location set in config var outputDir = Directory.GetCurrentDirectory(); var cfgFile = "config.toml"; @@ -166,24 +171,32 @@ void ExportSchedule(List storeTimes) // If the config file exists, read from that but don't assume anything is filled if (File.Exists(cfgPath)) { + Tracer.WriteLine(cfgPath); var toml = File.ReadAllText(cfgPath); var usrDir = config.Path; var usrFileName = config.File; // Convert list into array - var usrTopics = config.Topics; + var list = config.Topics; + var tomlList = string.Join(", ", list); + var usrTopics = tomlList.Split(','); - if (!string.IsNullOrEmpty(usrDir)) - outputDir = usrDir; + if (string.IsNullOrEmpty(usrDir)) + return; - if (!string.IsNullOrEmpty(usrFileName)) - outputFile = usrFileName; + outputDir = usrDir; + + if (string.IsNullOrEmpty(usrFileName)) + return; + + outputFile = usrFileName; + + // If array is empty, return; otherwise, apply config + if (usrTopics.Length < 0) + return; + + foreach (var usrTopic in usrTopics) + topics.Add(usrTopic); - // If array is populated, apply config - if (!usrTopics.Any()) - { - foreach (var usrTopic in usrTopics) - topics.Add(usrTopic); - } // Set new file Path filePath = Path.Combine(outputDir, outputFile!); @@ -211,10 +224,14 @@ void ExportSchedule(List storeTimes) Times = times, }); - var jsonString = JsonSerializer.Serialize(jsonList); - Console.WriteLine(jsonList); + var jsonOptions = new JsonSerializerOptions() + { + WriteIndented = true, + }; + + var jsonString = JsonSerializer.Serialize(jsonList, jsonOptions); File.WriteAllText(filePath, jsonString); - Console.WriteLine($"{Environment.NewLine}Written to: {filePath}"); + Tracer.WriteLine($"{jsonString}{Environment.NewLine}Written to: {filePath}"); // Clear list from memory storeTimes.Clear(); @@ -224,10 +241,10 @@ void ExportSchedule(List storeTimes) /// Displays the scheduled article times in a formatted manner and provides /// options to export the schedule or restart the scheduling process. /// -void PrintSchedule(bool isRestart = false) +void PrintTimes(bool isRestart = false) { var storeSchedule = new List(); - var scheduledTimes = GenerateSchedule(); + var scheduledTimes = GenerateTimes(); // Clear the screen on restart if (isRestart) @@ -245,14 +262,14 @@ void PrintSchedule(bool isRestart = false) // Give the user an option to export the schedule if (UserChoice("Retry?")) - PrintSchedule(true); + PrintTimes(true); // Give the user an option to export the schedule if (UserChoice("Export?")) ExportSchedule(storeSchedule); if (UserChoice("Generate A New Batch?")) - PrintSchedule(true); + PrintTimes(true); else { Console.Clear(); @@ -261,4 +278,4 @@ void PrintSchedule(bool isRestart = false) } // Start the loop -PrintSchedule(); +PrintTimes(); diff --git a/PublishTimes.csproj b/PublishTimes.csproj index 109581c..ef9295c 100644 --- a/PublishTimes.csproj +++ b/PublishTimes.csproj @@ -4,6 +4,7 @@ Exe net9.0 enable + 0.2.101 enable diff --git a/README.md b/README.md index 5322e33..191612d 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,29 @@ It is not recommended for use with hot topics. Instead, you should focus on over ## Exporting -Once it generates a list of times, you can retry or export the times. If you export, you're requested to select from a number of topics or default to none if no selection is made. Upon export, you can start over or exit. +Once it generates a list of times, you can retry or export it as a JSON format. If you export, you're requested to select from a number of topics or default to none if no selection is made. Upon export, you can start over or exit. Choice selection is based on the Y/N keys. ``Enter`` works the same as ``Y`` while pressing any other key is the equivalent of ``N``. You can make mistakes, but it expects failure. An optional ``config.toml`` file allows for further customization of file name, directory, and topics. +### Example + +```json +[ + { + "topic": "Games", + "times": [ + "11:41 AM", + "2:05 PM", + "5:05 PM", + "8:18 PM", + "11:02 PM" + ] + } +] +``` + ## Background A while back, I [found a tool](https://schedule.lemmings.world) to schedule articles on Lemmy. I've been posting within a few hours apart at random minutes and I wanted to something decide that for me. I had AI write the base algorithm, everything else is my own touches. diff --git a/Tracer.cs b/Tracer.cs new file mode 100644 index 0000000..c9dc29c --- /dev/null +++ b/Tracer.cs @@ -0,0 +1,66 @@ +// I hereby waive this project under the public domain - see UNLICENSE for details. +namespace PublishTimes; + +/// +/// Provides debug-only console output methods. +/// These methods are only executed when the application is compiled in DEBUG mode. +/// +internal static class Tracer +{ + /// + /// Writes a line of text to the console, but only when in DEBUG mode. + /// + /// The text to write to the console. + [Conditional("DEBUG")] + internal static void WriteLine(string content) => + Console.WriteLine(content); + + /// + /// Writes text to the console without a newline, but only when in DEBUG mode. + /// + /// The text to write to the console. + [Conditional("DEBUG")] + internal static void Write(string content) => + Console.Write(content); + + /// + /// Writes multiple lines of text to the console, but only when in DEBUG mode. + /// + /// A collection of text lines to write to the console. + [Conditional("DEBUG")] + internal static void WriteLine(IEnumerable contents) + { + foreach (var content in contents) + { + Console.WriteLine(content); + } + } + + /// + /// Writes multiple text entries to the console without newlines, but only when in DEBUG mode. + /// + /// A collection of text entries to write to the console. + [Conditional("DEBUG")] + internal static void Write(IEnumerable contents) + { + foreach (var content in contents) + { + Console.Write(content); + } + } + + /// + /// Gets the current working directory in DEBUG mode or the application's base directory in release mode. + /// + internal static string AppDirectory + { + get + { +#if DEBUG + return Directory.GetCurrentDirectory(); +#else + return AppDomain.CurrentDomain.BaseDirectory; +#endif + } + } +}