Switch to Json (broken)

This commit is contained in:
Tony Bark 2025-03-16 11:33:45 -04:00
parent 5cccb9a4b2
commit ac1d3862c6
8 changed files with 213 additions and 37 deletions

128
.editorconfig Normal file
View file

@ -0,0 +1,128 @@
# editorconfig.org
# top-most EditorConfig file
root = true
# Default settings:
# A newline ending every file
# Use 4 spaces as indentation
[*]
charset = utf-8
end_of_line = crlf
indent_style = tab
indent_size = 4
insert_final_newline = false
trim_trailing_whitespace = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
# C# files
[*.cs]
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# avoid this. unless absolutely necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# only use var when it's obvious what the variable type is
csharp_style_var_for_built_in_types = true:none
csharp_style_var_when_type_is_apparent = true:none
csharp_style_var_elsewhere = true:suggestion
# use language keywords instead of BCL types
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Use UPPER_CASE for private or internal constant fields
dotnet_naming_rule.constants_should_be_upper_case.severity = suggestion
dotnet_naming_rule.constants_should_be_upper_case.symbols = constants
dotnet_naming_rule.constants_should_be_upper_case.style = constant_style
dotnet_naming_symbols.constants.applicable_kinds = field, local
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.constant_style.capitalization = all_upper
# Comment this group and uncomment out the next group if you don't want _ prefixed fields.
# internal and private fields should be _camel_case
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults
dotnet_sort_system_directives_first = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
# Null checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = when_multiline:silent

1
.gitignore vendored
View file

@ -546,3 +546,4 @@ FodyWeavers.xsd
.idea/**
*.txt
*.toml
*.json

View file

@ -10,15 +10,15 @@ public class Config
/// <summary>
/// Gets or sets the name of the schedule file.
/// </summary>
public string? File { get; set; }
public string File { get; set; } = "schedule.txt";
/// <summary>
/// Gets or sets the directory path where the schedule file is stored.
/// </summary>
public string? Path { get; set; }
public string Path { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
/// <summary>
/// Gets or sets the list of available topics from the configuration file.
/// </summary>
public TomlArray? Topics { get; set; }
public IList<string> Topics { get; set; } = new[] { "Games", "Politics", "Research", "Technology" };
}

View file

@ -1,3 +1,5 @@
global using Tomlyn;
global using Tomlyn.Model;
global using PublishTimes;
global using System.Text.Json;
global using System.Text.Json.Serialization;

View file

@ -1,5 +1,27 @@
// 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;
}
return new Config();
}
/// <summary>
/// Prompts the user with a yes/no question and returns their choice as a boolean value.
/// </summary>
@ -90,7 +112,7 @@ string SelectTopics(List<string> topics)
}
var selection = string.Join(", ", userChoices.ToArray());
Console.WriteLine($"{Environment.NewLine}Select a Topic (Choose a Number){Environment.NewLine}{selection}");
Console.WriteLine($"{Environment.NewLine}Select a Number{Environment.NewLine}{selection}");
var input = Console.ReadLine();
// Attempt to parse a number.
@ -114,7 +136,7 @@ string NewTopic(List<string> topics)
if (UserChoice("Choose a Topic?"))
newTopic = SelectTopics(topics);
else
newTopic = "===";
newTopic = "Any";
return newTopic;
}
@ -130,24 +152,24 @@ void ExportSchedule(List<String> storeSchedule)
var appDir = AppDomain.CurrentDomain.BaseDirectory;
// File directory is used for file location set in config
var outputDir = Directory.GetCurrentDirectory();
var defaultTopics = new[] { "Games", "Politics", "Research", "Technology" };
var outputFile = "schedule.txt";
var cfgFile = "config.toml";
var topics = new List<string>();
var cfgPath = Path.Combine(appDir, cfgFile);
var filePath = Path.Combine(outputDir, outputFile);
var appendSchedule = false;
var topic = "";
var config = GetConfig(cfgPath);
var outputFile = config.File;
var filePath = Path.Combine(outputDir, outputFile!);
var chosenTopic = "";
// 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 model = Toml.ToModel<Config>(toml);
var usrDir = model.Path;
var usrFileName = model.File;
var tomlList = string.Join(", ", model.Topics);
var usrList = tomlList.Split(',');
var usrDir = config.Path;
var usrFileName = config.File;
// Convert list into array
var usrTopics = config.Topics;
if (!string.IsNullOrEmpty(usrDir))
outputDir = usrDir;
@ -155,33 +177,42 @@ void ExportSchedule(List<String> storeSchedule)
if (!string.IsNullOrEmpty(usrFileName))
outputFile = usrFileName;
if (usrList.Length > 0)
defaultTopics = usrList;
// 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);
filePath = Path.Combine(outputDir, outputFile!);
}
topic = NewTopic(defaultTopics.ToList());
// Set new topic
topics = config.Topics.ToList();
chosenTopic = NewTopic(topics);
// If the file already exists, assume a previous schedule was written
if (File.Exists(filePath))
// Write to file.
JsonWriterOptions options = new()
{
if (UserChoice("Add to existing file?"))
appendSchedule = true;
Indented = true
};
// Write to file.
using (var outputFile = new StreamWriter(filePath, appendSchedule))
{
outputFile.WriteLine($" === {topic} ===");
foreach (var line in storeSchedule)
outputFile.WriteLine(line);
}
}
var jsonList = JsonSerializer.Deserialize<List<Schedule>>(filePath)
?? new List<Schedule>();
// Clear list from memory before exit
jsonList.Add(new Schedule()
{
Topic = chosenTopic,
Times = storeSchedule,
});
var jsonString = JsonSerializer.Serialize(jsonList);
Console.WriteLine(jsonString);
// File.WriteAllText(filePath, jsonString);
Console.WriteLine($"{Environment.NewLine}Written to: {filePath}");
// Clear list from memory
storeSchedule.Clear();
}
/// <summary>
@ -193,13 +224,14 @@ void PrintSchedule(bool isRestart = false)
var storeSchedule = new List<String>();
var scheduledTimes = GenerateSchedule();
// Clear the screen on restart
if (isRestart)
Console.Clear();
Console.WriteLine("=== Publish Times ===");
foreach (var time in scheduledTimes)
{
var articleTime = $"Article {scheduledTimes.IndexOf(time) + 1} Scheduled at: {ConvertTo12Hour(time)}";
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

View file

@ -1,12 +1,14 @@
# Publish Times
This is a very simple console application that generates a list of times to publish news articles within a randomized 2-3 hour delay while avoiding time conflicts within a 30-minute window.
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.
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.
## 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.
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.
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.
@ -14,6 +16,8 @@ An optional ``config.toml`` file allows for further customization of file name,
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.
This tool was originally intended with a simple purpose: suggest some publishing times. However, when customable topics were added, it became more versatile. It can be used with anything that can be scheduled to publish, from news, art, or anything else.
## License
I hereby waive this project under the public domain - see [UNLICENSE](UNLICENSE) for details.

9
Schedule.cs Normal file
View file

@ -0,0 +1,9 @@
public class Schedule
{
[JsonPropertyName("topic")]
public string Topic { get; set; } = "";
[JsonPropertyName("times")]
public IList<string> Times { get; set; } = new List<string>();
}

View file

@ -1,5 +1,5 @@
path = "/home/tonytins/Documents/"
file = "newscycle.txt"
file = "newscycle.json"
topics = [
"Games",
"News",