mirror of
https://github.com/tonytins/staggerpost.git
synced 2025-03-25 02:39:12 +00:00
Huge Refactor
- Cleaned up Program.cs by moving a lot of functions to their own classes - Renamed topics to community - Choosing a community is no longer optional with the switch to JSON - Added csharpier tool and reformttered all code
This commit is contained in:
parent
d91ff3352b
commit
188318c724
12 changed files with 396 additions and 382 deletions
13
.config/dotnet-tools.json
Normal file
13
.config/dotnet-tools.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "0.30.6",
|
||||
"commands": [
|
||||
"dotnet-csharpier"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -546,4 +546,4 @@ FodyWeavers.xsd
|
|||
.idea/**
|
||||
*.txt
|
||||
*.toml
|
||||
*.json
|
||||
schedule.json
|
25
Config.cs
25
Config.cs
|
@ -7,19 +7,18 @@ namespace StaggerPost;
|
|||
/// </summary>
|
||||
public class Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the schedule file.
|
||||
/// </summary>
|
||||
public string? File { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the schedule file.
|
||||
/// </summary>
|
||||
public string? File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the directory path where the schedule file is stored.
|
||||
/// </summary>
|
||||
public string? Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of available topics from the configuration file.
|
||||
/// </summary>
|
||||
public List<string> Topics { get; set; } = new List<string>();
|
||||
/// <summary>
|
||||
/// Gets or sets the directory path where the schedule file is stored.
|
||||
/// </summary>
|
||||
public string? Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of available topics from the configuration file.
|
||||
/// </summary>
|
||||
public List<string> Communities { get; set; } = new List<string>();
|
||||
}
|
||||
|
|
128
Export.cs
Normal file
128
Export.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
// I hereby waive this project under the public domain - see UNLICENSE for details.
|
||||
namespace StaggerPost;
|
||||
|
||||
internal static class Export
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves configuration settings from a TOML file if it exists; otherwise, returns a default configuration.
|
||||
/// </summary>
|
||||
/// <param name="file">The name of the configuration file (defaults to "config.toml").</param>
|
||||
/// <returns>A Config object populated with values from the file, or a default Config instance if the file is not found.</returns>
|
||||
static Config GetConfig(string file)
|
||||
{
|
||||
var cfgPath = Path.Combine(Tracer.AppDirectory, file);
|
||||
|
||||
if (!File.Exists(cfgPath))
|
||||
{
|
||||
Tracer.LogLine("Config file not found. Switching to defaults.");
|
||||
var defaultList = new[]
|
||||
{
|
||||
"games@lemmy.world",
|
||||
"politics@lemmy.world",
|
||||
"science@lemmy.world",
|
||||
"technology@lemmy.world",
|
||||
};
|
||||
|
||||
var config = new Config()
|
||||
{
|
||||
File = "schedule.json",
|
||||
Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
|
||||
Communities = defaultList.ToList(),
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
Tracer.LogLine($"Discovered config file: {cfgPath}");
|
||||
var toml = File.ReadAllText(cfgPath);
|
||||
var model = Toml.ToModel<Config>(toml);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports the scheduled articles to a file, allowing the user to modify
|
||||
/// the directory, filename, and list of topics based on
|
||||
/// a configuration file if available.
|
||||
/// </summary>
|
||||
public static void ToJSON(List<String> storeTimes, string cfgPath)
|
||||
{
|
||||
// File directory is used for file location set in config
|
||||
var outputDir = Directory.GetCurrentDirectory();
|
||||
var topics = new List<string>();
|
||||
var config = GetConfig(cfgPath);
|
||||
var outputFile = config.File;
|
||||
var filePath = Path.Combine(outputDir, outputFile!);
|
||||
var chosenTopic = "";
|
||||
var times = new List<string>();
|
||||
|
||||
// If the config file exists, read from that but don't assume anything is filled
|
||||
if (File.Exists(cfgPath))
|
||||
{
|
||||
var toml = File.ReadAllText(cfgPath);
|
||||
var usrDir = config.Path;
|
||||
var usrFileName = config.File;
|
||||
// Convert list into array
|
||||
var list = config.Communities;
|
||||
var tomlList = string.Join(", ", list);
|
||||
var usrTopics = tomlList.Split(',');
|
||||
|
||||
if (string.IsNullOrEmpty(usrDir))
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
// Set new file Path
|
||||
filePath = Path.Combine(outputDir, outputFile!);
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
File.WriteAllText(filePath, "[]");
|
||||
|
||||
foreach (var time in storeTimes)
|
||||
times.Add(time.Trim());
|
||||
|
||||
// Set new topic
|
||||
topics = config.Communities.ToList();
|
||||
Console.Clear();
|
||||
chosenTopic = Interactive.SelectTopics(topics);
|
||||
|
||||
var date = Interactive.SelectDate();
|
||||
|
||||
// Write to file.
|
||||
var jsonFile = File.ReadAllText(filePath);
|
||||
var jsonList = string.IsNullOrWhiteSpace(jsonFile)
|
||||
? new List<Schedule>()
|
||||
: JsonSerializer.Deserialize<List<Schedule>>(jsonFile) ?? new List<Schedule>();
|
||||
|
||||
jsonList.Add(
|
||||
new Schedule()
|
||||
{
|
||||
Community = chosenTopic.Trim(),
|
||||
Date = date.Trim(),
|
||||
Times = times,
|
||||
}
|
||||
);
|
||||
|
||||
var jsonOptions = new JsonSerializerOptions() { WriteIndented = true };
|
||||
|
||||
var json = JsonSerializer.Serialize(jsonList, jsonOptions);
|
||||
File.WriteAllText(filePath, json);
|
||||
Tracer.LogLine($"{json}{Environment.NewLine}Written to: {filePath}");
|
||||
|
||||
// Clear list from memory
|
||||
storeTimes.Clear();
|
||||
}
|
||||
}
|
60
Generator.cs
Normal file
60
Generator.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// I hereby waive this project under the public domain - see UNLICENSE for details.
|
||||
namespace StaggerPost;
|
||||
|
||||
internal static class Generator
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a schedule of article publishing times, ensuring a randomized
|
||||
/// delay between each while avoiding time conflicts within a 30-minute window.
|
||||
/// </summary>
|
||||
/// <returns>A list of TimeSpan objects representing scheduled article times.</returns>
|
||||
public static List<TimeSpan> GenerateTimes()
|
||||
{
|
||||
var numberOfArticles = 5; // Define how many articles to schedule
|
||||
var startTime = new TimeSpan(9, 0, 0); // Starting time at 9:00 AM
|
||||
var rng = new Random();
|
||||
var scheduledTimes = new List<TimeSpan>();
|
||||
|
||||
for (int i = 0; i < numberOfArticles; i++)
|
||||
{
|
||||
var baseDelayHours = rng.Next(2, 4); // Randomly choose between 2-3 hours delay
|
||||
var minutesToAdd = rng.Next(0, 60); // Randomly choose minutes (0-59)
|
||||
|
||||
// Calculate new time by adding base delay and random minutes
|
||||
var nextTime = startTime.Add(new TimeSpan(baseDelayHours, minutesToAdd, 0));
|
||||
|
||||
// Check if the new time is within 30 minutes of any existing time
|
||||
while (
|
||||
scheduledTimes.Exists(previousTime =>
|
||||
Math.Abs((nextTime - previousTime).TotalMinutes) < 30
|
||||
)
|
||||
)
|
||||
{
|
||||
// If the new time is within 30 minutes of an existing time, adjust it
|
||||
nextTime = nextTime.Add(new TimeSpan(0, 30, 0));
|
||||
}
|
||||
|
||||
scheduledTimes.Add(nextTime);
|
||||
startTime = nextTime; // Update start time for the next article
|
||||
}
|
||||
|
||||
return scheduledTimes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a TimeSpan into a 12-hour AM/PM formatted time string.
|
||||
/// </summary>
|
||||
/// <param name="time">The TimeSpan representing the time of day.</param>
|
||||
/// <returns>A formatted string representing the time in AM/PM format.</returns>
|
||||
public static string ConvertTo12Hour(TimeSpan time)
|
||||
{
|
||||
var minutes = time.TotalMinutes;
|
||||
var hours12 = time.Hours % 12;
|
||||
|
||||
if (hours12 == 0)
|
||||
hours12 = 1;
|
||||
|
||||
var period = time.Hours >= 12 ? "PM" : "AM";
|
||||
return $"{hours12}:{time.Minutes:D2} {period}";
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
global using System.Diagnostics;
|
||||
global using System.Text;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using StaggerPost;
|
||||
global using Tomlyn;
|
||||
global using Tomlyn.Model;
|
||||
global using StaggerPost;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
95
Interactive.cs
Normal file
95
Interactive.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
// I hereby waive this project under the public domain - see UNLICENSE for details.
|
||||
namespace StaggerPost;
|
||||
|
||||
internal static class Interactive
|
||||
{
|
||||
/// <summary>
|
||||
/// Prompts the user with a yes/no question and returns their choice as a boolean value.
|
||||
/// </summary>
|
||||
/// <param name="choice">The message to display to the user.</param>
|
||||
/// <returns>True if the user selects 'Y' or presses Enter, otherwise false.</returns>
|
||||
public static bool UserChoice(string choice)
|
||||
{
|
||||
Console.WriteLine($"{Environment.NewLine}{choice} Y/N");
|
||||
var input = Console.ReadKey().Key;
|
||||
if (input == ConsoleKey.Y || input == ConsoleKey.Enter)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prompts the user to select a topic from a given list
|
||||
/// and returns the chosen topic.
|
||||
/// </summary>
|
||||
/// <param name="communities">An array of available topics.</param>
|
||||
/// <returns>The selected topic as a string.</returns>
|
||||
public static string SelectTopics(List<string> communities)
|
||||
{
|
||||
var topicChoice = "";
|
||||
var topicNum = 0;
|
||||
var userChoices = new List<string>();
|
||||
var numOfTopics = 0;
|
||||
var topicDict = new Dictionary<int, string>();
|
||||
|
||||
foreach (var community in communities)
|
||||
{
|
||||
numOfTopics++;
|
||||
var title = community.Trim();
|
||||
topicDict.Add(numOfTopics, title);
|
||||
userChoices.Add(
|
||||
$"{Environment.NewLine}{numOfTopics} {title.TrimEnd(new char[] { ',' })}"
|
||||
);
|
||||
}
|
||||
|
||||
var topicSelect = string.Join(", ", userChoices.ToArray());
|
||||
Console.WriteLine($"{Environment.NewLine}Choose a Topic{Environment.NewLine}{topicSelect}");
|
||||
var input = Console.ReadLine();
|
||||
|
||||
// Attempt to parse a number.
|
||||
if (int.TryParse(input, out topicNum) == true)
|
||||
topicChoice = topicDict[topicNum];
|
||||
else
|
||||
SelectTopics(communities);
|
||||
|
||||
return topicChoice;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prompts the user to select a date (either today or tomorrow) and returns the selected date as a formatted string.
|
||||
/// </summary>
|
||||
/// <returns>A string representing the selected date in a short date format.</returns>
|
||||
public static string SelectDate()
|
||||
{
|
||||
var dtChoices = new[] { "Today", "Tomorrow" };
|
||||
var dtDict = new Dictionary<int, string>();
|
||||
var dtSelection = new List<string>();
|
||||
var dtChoice = 0;
|
||||
var dtNum = 0;
|
||||
|
||||
foreach (var days in dtChoices)
|
||||
{
|
||||
dtNum++;
|
||||
var day = days.Trim();
|
||||
dtDict.Add(dtNum, day);
|
||||
dtSelection.Add($"{dtNum} {day}");
|
||||
}
|
||||
|
||||
var topicSelect = string.Join(", ", dtSelection.ToArray());
|
||||
Console.WriteLine($"{Environment.NewLine}Choose a Date{Environment.NewLine}{topicSelect}");
|
||||
var input = Console.ReadLine();
|
||||
|
||||
// Attempt to parse a number.
|
||||
if (int.TryParse(input, out dtNum) == true)
|
||||
dtChoice = dtNum;
|
||||
|
||||
// Any choice above 2 tomorrow
|
||||
if (dtChoice >= 2)
|
||||
{
|
||||
var dt = DateTime.Now.AddDays(1);
|
||||
return dt.ToString("d");
|
||||
}
|
||||
|
||||
return DateTime.Today.ToString("d");
|
||||
}
|
||||
}
|
333
Program.cs
333
Program.cs
|
@ -1,322 +1,41 @@
|
|||
// I hereby waive this project under the public domain - see UNLICENSE for details.
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves configuration settings from a TOML file if it exists; otherwise, returns a default configuration.
|
||||
/// </summary>
|
||||
/// <param name="file">The name of the configuration file (defaults to "config.toml").</param>
|
||||
/// <returns>A Config object populated with values from the file, or a default Config instance if the file is not found.</returns>
|
||||
Config GetConfig(string file = "config.toml")
|
||||
{
|
||||
// App directory is used for config file
|
||||
var appDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
var cfgPath = Path.Combine(appDir, file);
|
||||
|
||||
if (File.Exists(cfgPath))
|
||||
{
|
||||
var toml = File.ReadAllText(cfgPath);
|
||||
var model = Toml.ToModel<Config>(toml);
|
||||
|
||||
return model;
|
||||
}
|
||||
var defaultList = new[] { "Games", "Politics", "Research", "Technology" };
|
||||
return new Config()
|
||||
{
|
||||
File = "schedule.json",
|
||||
Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
|
||||
Topics = defaultList.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prompts the user with a yes/no question and returns their choice as a boolean value.
|
||||
/// </summary>
|
||||
/// <param name="choice">The message to display to the user.</param>
|
||||
/// <returns>True if the user selects 'Y' or presses Enter, otherwise false.</returns>
|
||||
bool UserChoice(string choice)
|
||||
{
|
||||
Console.WriteLine($"{Environment.NewLine}{choice} Y/N");
|
||||
var input = Console.ReadKey().Key;
|
||||
if (input == ConsoleKey.Y || input == ConsoleKey.Enter)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a schedule of article publishing times, ensuring a randomized
|
||||
/// delay between each while avoiding time conflicts within a 30-minute window.
|
||||
/// </summary>
|
||||
/// <returns>A list of TimeSpan objects representing scheduled article times.</returns>
|
||||
List<TimeSpan> GenerateTimes()
|
||||
{
|
||||
var numberOfArticles = 5; // Define how many articles to schedule
|
||||
var startTime = new TimeSpan(9, 0, 0); // Starting time at 9:00 AM
|
||||
var rng = new Random();
|
||||
var scheduledTimes = new List<TimeSpan>();
|
||||
|
||||
for (int i = 0; i < numberOfArticles; i++)
|
||||
{
|
||||
var baseDelayHours = rng.Next(2, 4); // Randomly choose between 2-3 hours delay
|
||||
var minutesToAdd = rng.Next(0, 60); // Randomly choose minutes (0-59)
|
||||
|
||||
// Calculate new time by adding base delay and random minutes
|
||||
var nextTime = startTime.Add(new TimeSpan(baseDelayHours, minutesToAdd, 0));
|
||||
|
||||
// Check if the new time is within 30 minutes of any existing time
|
||||
while (scheduledTimes.Exists(previousTime => Math.Abs((nextTime - previousTime).TotalMinutes) < 30))
|
||||
{
|
||||
// If the new time is within 30 minutes of an existing time, adjust it
|
||||
nextTime = nextTime.Add(new TimeSpan(0, 30, 0));
|
||||
}
|
||||
|
||||
scheduledTimes.Add(nextTime);
|
||||
startTime = nextTime; // Update start time for the next article
|
||||
}
|
||||
|
||||
return scheduledTimes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a TimeSpan into a 12-hour AM/PM formatted time string.
|
||||
/// </summary>
|
||||
/// <param name="time">The TimeSpan representing the time of day.</param>
|
||||
/// <returns>A formatted string representing the time in AM/PM format.</returns>
|
||||
string ConvertTo12Hour(TimeSpan time)
|
||||
{
|
||||
var minutes = time.TotalMinutes;
|
||||
var hours12 = time.Hours % 12;
|
||||
|
||||
if (hours12 == 0)
|
||||
hours12 = 1;
|
||||
|
||||
var period = time.Hours >= 12 ? "PM" : "AM";
|
||||
return $"{hours12}:{time.Minutes:D2} {period}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prompts the user to select a topic from a given list
|
||||
/// and returns the chosen topic.
|
||||
/// </summary>
|
||||
/// <param name="topics">An array of available topics.</param>
|
||||
/// <returns>The selected topic as a string.</returns>
|
||||
string SelectTopics(List<string> topics)
|
||||
{
|
||||
var topicChoice = "";
|
||||
var topicNum = 0;
|
||||
var userChoices = new List<string>();
|
||||
var numOfTopics = 0;
|
||||
var topicDict = new Dictionary<int,
|
||||
string>();
|
||||
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
numOfTopics++;
|
||||
var title = topic.Trim();
|
||||
topicDict.Add(numOfTopics, title);
|
||||
userChoices.Add($"{numOfTopics} {title}");
|
||||
}
|
||||
|
||||
var topicSelect = string.Join(", ", userChoices.ToArray());
|
||||
Console.WriteLine($"{Environment.NewLine}Choose a Topic{Environment.NewLine}{topicSelect}");
|
||||
var input = Console.ReadLine();
|
||||
|
||||
// Attempt to parse a number.
|
||||
if (int.TryParse(input, out topicNum) == true)
|
||||
topicChoice = topicDict[topicNum];
|
||||
else
|
||||
NewTopic(topics);
|
||||
|
||||
return topicChoice;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prompts the user to select a date (either today or tomorrow) and returns the selected date as a formatted string.
|
||||
/// </summary>
|
||||
/// <returns>A string representing the selected date in a short date format.</returns>
|
||||
string SelectDate()
|
||||
{
|
||||
var dtChoices = new[] { "Today", "Tomorrow" };
|
||||
var dtDict = new Dictionary<int,
|
||||
string>();
|
||||
var dtSelection = new List<string>();
|
||||
var dtChoice = 0;
|
||||
var dtNum = 0;
|
||||
|
||||
foreach (var days in dtChoices)
|
||||
{
|
||||
dtNum++;
|
||||
var day = days.Trim();
|
||||
dtDict.Add(dtNum, day);
|
||||
dtSelection.Add($"{dtNum} {day}");
|
||||
}
|
||||
|
||||
var topicSelect = string.Join(", ", dtSelection.ToArray());
|
||||
Console.WriteLine($"{Environment.NewLine}Choose a Date{Environment.NewLine}{topicSelect}");
|
||||
var input = Console.ReadLine();
|
||||
|
||||
// Attempt to parse a number.
|
||||
if (int.TryParse(input, out dtNum) == true)
|
||||
dtChoice = dtNum;
|
||||
|
||||
// Any choice above 2 tomorrow
|
||||
if (dtChoice >= 2)
|
||||
{
|
||||
var dt = DateTime.Now.AddDays(1);
|
||||
return dt.ToString("d");
|
||||
}
|
||||
|
||||
return DateTime.Today.ToString("d");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the user to choose a new topic from a given list or default to placeholder if no selection is made.
|
||||
/// </summary>
|
||||
/// <param name="topics">A list of available topics.</param>
|
||||
/// <returns>The selected topic or a default placeholder if none is chosen.</returns>
|
||||
string NewTopic(List<string> topics)
|
||||
{
|
||||
var newTopic = "";
|
||||
|
||||
if (UserChoice("Choose a Topic?"))
|
||||
newTopic = SelectTopics(topics);
|
||||
else
|
||||
newTopic = "Any";
|
||||
|
||||
return newTopic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports the scheduled articles to a file, allowing the user to modify
|
||||
/// the directory, filename, and list of topics based on
|
||||
/// a configuration file if available.
|
||||
/// </summary>
|
||||
void ExportSchedule(List<String> storeTimes)
|
||||
{
|
||||
// App directory is used for config file
|
||||
var appDir = Tracer.AppDirectory;
|
||||
// File directory is used for file location set in config
|
||||
var outputDir = Directory.GetCurrentDirectory();
|
||||
var cfgFile = "config.toml";
|
||||
|
||||
var topics = new List<string>();
|
||||
var cfgPath = Path.Combine(appDir, cfgFile);
|
||||
var config = GetConfig(cfgPath);
|
||||
var outputFile = config.File;
|
||||
var filePath = Path.Combine(outputDir, outputFile!);
|
||||
var chosenTopic = "";
|
||||
var times = new List<string>();
|
||||
|
||||
|
||||
// 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 list = config.Topics;
|
||||
var tomlList = string.Join(", ", list);
|
||||
var usrTopics = tomlList.Split(',');
|
||||
|
||||
if (string.IsNullOrEmpty(usrDir))
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
// Set new file Path
|
||||
filePath = Path.Combine(outputDir, outputFile!);
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
File.WriteAllText(filePath, "[]");
|
||||
|
||||
foreach (var time in storeTimes)
|
||||
times.Add(time.Trim());
|
||||
|
||||
// Set new topic
|
||||
topics = config.Topics.ToList();
|
||||
chosenTopic = NewTopic(topics);
|
||||
|
||||
var date = SelectDate();
|
||||
|
||||
// Write to file.
|
||||
var jsonContent = File.ReadAllText(filePath);
|
||||
var jsonList = string.IsNullOrWhiteSpace(jsonContent) ? new List<Schedule>()
|
||||
: JsonSerializer.Deserialize<List<Schedule>>(jsonContent) ?? new List<Schedule>();
|
||||
|
||||
|
||||
jsonList.Add(new Schedule()
|
||||
{
|
||||
Topic = chosenTopic.Trim(),
|
||||
Date = date.Trim(),
|
||||
Times = times,
|
||||
});
|
||||
|
||||
var jsonOptions = new JsonSerializerOptions()
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
var jsonString = JsonSerializer.Serialize(jsonList, jsonOptions);
|
||||
File.WriteAllText(filePath, jsonString);
|
||||
Tracer.WriteLine($"{jsonString}{Environment.NewLine}Written to: {filePath}");
|
||||
|
||||
// Clear list from memory
|
||||
storeTimes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the scheduled article times in a formatted manner and provides
|
||||
/// options to export the schedule or restart the scheduling process.
|
||||
/// </summary>
|
||||
void PrintTimes(bool isRestart = false)
|
||||
void PrintTimes()
|
||||
{
|
||||
var storeSchedule = new List<String>();
|
||||
var scheduledTimes = GenerateTimes();
|
||||
var storeSchedule = new List<String>();
|
||||
var scheduledTimes = Generator.GenerateTimes();
|
||||
|
||||
// Clear the screen on restart
|
||||
if (isRestart)
|
||||
Console.Clear();
|
||||
// Clear the screen on restart
|
||||
Console.Clear();
|
||||
|
||||
Console.WriteLine("=== Publish Times ===");
|
||||
foreach (var time in scheduledTimes)
|
||||
{
|
||||
var articleTime = $"{ConvertTo12Hour(time)}";
|
||||
// Correct format string to display time in 12-hour format with AM/PM
|
||||
Console.WriteLine(articleTime);
|
||||
// Store the schedule to memory for option export
|
||||
storeSchedule.Add(articleTime);
|
||||
}
|
||||
Console.WriteLine("=== Publish Times ===");
|
||||
foreach (var time in scheduledTimes)
|
||||
{
|
||||
var articleTime = $"{Generator.ConvertTo12Hour(time)}";
|
||||
// Correct format string to display time in 12-hour format with AM/PM
|
||||
Console.WriteLine(articleTime);
|
||||
// Store the schedule to memory for option export
|
||||
storeSchedule.Add(articleTime);
|
||||
}
|
||||
|
||||
// Give the user an option to export the schedule
|
||||
if (UserChoice("Retry?"))
|
||||
PrintTimes(true);
|
||||
// Give the user an option to export the schedule
|
||||
if (Interactive.UserChoice("Retry?"))
|
||||
PrintTimes();
|
||||
|
||||
// Give the user an option to export the schedule
|
||||
if (UserChoice("Export?"))
|
||||
ExportSchedule(storeSchedule);
|
||||
// Give the user an option to export the schedule
|
||||
Export.ToJSON(storeSchedule, "config.toml");
|
||||
|
||||
if (UserChoice("Generate A New Batch?"))
|
||||
PrintTimes(true);
|
||||
else
|
||||
{
|
||||
Console.Clear();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
}
|
||||
if (Interactive.UserChoice("Generate A New Batch?"))
|
||||
PrintTimes();
|
||||
else
|
||||
{
|
||||
Console.Clear();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Start the loop
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# StaggerPost
|
||||
|
||||
This is a very simple console application that generates a list of times to publish news articles within a randomized 2-3 hour delay within a 30-minute window to avoid conflicts. This keeps thing flowing at an organic and slow pace.
|
||||
This is a very simple console application that suggests a list of times to post news articles within a randomized 2-3 hour and 30-minute delay to avoid conflicts. This keeps thing flowing at an organic and slow pace.
|
||||
|
||||
It is not recommended for use with hot topics. Instead, you should focus on overlooked foreign affairs, local news or op-eds. Of course, this is just covering general news.
|
||||
|
||||
|
|
13
Schedule.cs
13
Schedule.cs
|
@ -1,12 +1,11 @@
|
|||
|
||||
public class Schedule
|
||||
{
|
||||
[JsonPropertyName("topic")]
|
||||
public string Topic { get; set; } = "";
|
||||
[JsonPropertyName("community")]
|
||||
public string Community { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("date")]
|
||||
public string Date { get; set; } = "";
|
||||
[JsonPropertyName("date")]
|
||||
public string Date { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("times")]
|
||||
public IList<string> Times { get; set; } = new List<string>();
|
||||
[JsonPropertyName("times")]
|
||||
public IList<string> Times { get; set; } = new List<string>();
|
||||
}
|
||||
|
|
98
Tracer.cs
98
Tracer.cs
|
@ -7,60 +7,60 @@ namespace StaggerPost;
|
|||
/// </summary>
|
||||
internal static class Tracer
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a line of text to the console, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="content">The text to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void WriteLine(string content) =>
|
||||
Console.WriteLine(content);
|
||||
const string LOG = "[LOG]:";
|
||||
|
||||
/// <summary>
|
||||
/// Writes text to the console without a newline, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="content">The text to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void Write(string content) =>
|
||||
Console.Write(content);
|
||||
/// <summary>
|
||||
/// Writes a line of text to the console, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="content">The text to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogLine(string content) => Console.WriteLine($"{LOG} {content}");
|
||||
|
||||
/// <summary>
|
||||
/// Writes multiple lines of text to the console, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="contents">A collection of text lines to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void WriteLine(IEnumerable<string> contents)
|
||||
{
|
||||
foreach (var content in contents)
|
||||
{
|
||||
Console.WriteLine(content);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Writes text to the console without a newline, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="content">The text to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void Log(string content) => Console.Write($"{LOG} {content}");
|
||||
|
||||
/// <summary>
|
||||
/// Writes multiple text entries to the console without newlines, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="contents">A collection of text entries to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void Write(IEnumerable<string> contents)
|
||||
{
|
||||
foreach (var content in contents)
|
||||
{
|
||||
Console.Write(content);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Writes multiple lines of text to the console, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="contents">A collection of text lines to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogLine(IEnumerable<string> contents)
|
||||
{
|
||||
foreach (var content in contents)
|
||||
{
|
||||
Console.WriteLine($"{LOG} {content}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current working directory in DEBUG mode or the application's base directory in release mode.
|
||||
/// </summary>
|
||||
internal static string AppDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes multiple text entries to the console without newlines, but only when in DEBUG mode.
|
||||
/// </summary>
|
||||
/// <param name="contents">A collection of text entries to write to the console.</param>
|
||||
[Conditional("DEBUG")]
|
||||
internal static void Log(IEnumerable<string> contents)
|
||||
{
|
||||
foreach (var content in contents)
|
||||
{
|
||||
Console.Write($"{LOG} {content}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current working directory in DEBUG mode or the application's base directory in release mode.
|
||||
/// </summary>
|
||||
internal static string AppDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG
|
||||
return Directory.GetCurrentDirectory();
|
||||
return Directory.GetCurrentDirectory();
|
||||
#else
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
path = "/home/tonytins/Documents/"
|
||||
file = "newscycle.json"
|
||||
topics = [
|
||||
communities = [
|
||||
"Games",
|
||||
"News",
|
||||
"Science",
|
||||
|
|
Loading…
Add table
Reference in a new issue