Task Automation CLI Tool C++

👤 Sharing: AI
```cpp
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <thread>
#include <cstdlib> // For system()
#include <iomanip> // For setw and setfill


// Configuration structure
struct Config {
    std::string task_name;
    std::string command;
    int delay;       // Delay in seconds
    bool run_once;   // Run only once
    bool verbose;    // Verbose output
    bool enabled;    // Enable or disable the task
    std::string log_file; // log file name
    std::string shell_command; //run it through shell or not
};

// Function prototypes
bool loadConfig(const std::string& filename, std::vector<Config>& configs);
void runTask(const Config& config);
void printHelp();
void listTasks(const std::vector<Config>& configs);
void enableTask(std::vector<Config>& configs, const std::string& taskName);
void disableTask(std::vector<Config>& configs, const std::string& taskName);
void saveConfig(const std::string& filename, const std::vector<Config>& configs);
void editTask(std::vector<Config>& configs, const std::string& taskName);
void addTask(std::vector<Config>& configs);
void removeTask(std::vector<Config>& configs, const std::string& taskName);
void printConfig(const Config& config); // Function to print a single config
void runSingleTask(const std::vector<Config>& configs, const std::string& taskName);
bool isValidTaskName(const std::string& taskName); // Validation function


// Global Configuration file name
const std::string CONFIG_FILE = "tasks.config";

int main(int argc, char* argv[]) {
    std::vector<Config> configs;

    if (!loadConfig(CONFIG_FILE, configs)) {
        std::cerr << "Error: Could not load configuration from " << CONFIG_FILE << ".  Using default configuration.\n";
    }
    

    if (argc == 1) {
        // Run all enabled tasks
        std::cout << "Running all enabled tasks. Press Ctrl+C to stop.\n";
        while (true) {
            for (auto& config : configs) {
                if (config.enabled) {
                    runTask(config);
                    if (config.run_once) {
                        config.enabled = false; // Disable after running once
                        saveConfig(CONFIG_FILE, configs);  //Persist the change.
                    }
                }
            }
            std::this_thread::sleep_for(std::chrono::seconds(1)); // Check tasks every second
        }
    } else {
        std::string command = argv[1];

        if (command == "help") {
            printHelp();
        } else if (command == "list") {
            listTasks(configs);
        } else if (command == "enable") {
            if (argc > 2) {
                enableTask(configs, argv[2]);
                saveConfig(CONFIG_FILE, configs);
            } else {
                std::cerr << "Error: Task name required.\n";
                printHelp();
            }
        } else if (command == "disable") {
            if (argc > 2) {
                disableTask(configs, argv[2]);
                saveConfig(CONFIG_FILE, configs);
            } else {
                std::cerr << "Error: Task name required.\n";
                printHelp();
            }
        } else if (command == "edit") {
            if (argc > 2) {
                editTask(configs, argv[2]);
                saveConfig(CONFIG_FILE, configs);
            } else {
                std::cerr << "Error: Task name required.\n";
                printHelp();
            }
        } else if (command == "add") {
            addTask(configs);
            saveConfig(CONFIG_FILE, configs);
        } else if (command == "remove") {
            if (argc > 2) {
                removeTask(configs, argv[2]);
                saveConfig(CONFIG_FILE, configs);
            } else {
                std::cerr << "Error: Task name required.\n";
                printHelp();
            }
        }
         else if (command == "run") {
            if (argc > 2) {
               runSingleTask(configs, argv[2]);
            } else {
                std::cerr << "Error: Task name required.\n";
                printHelp();
            }
        }
        else {
            std::cerr << "Error: Invalid command.\n";
            printHelp();
        }
    }

    return 0;
}


bool isValidTaskName(const std::string& taskName) {
    if (taskName.empty()) return false;
    for (char c : taskName) {
        if (!isalnum(c) && c != '_' && c != '-') {
            return false;
        }
    }
    return true;
}


bool loadConfig(const std::string& filename, std::vector<Config>& configs) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        // If the file doesn't exist, create a default configuration
        std::ofstream outfile(filename);
        if (!outfile.is_open()) {
            std::cerr << "Error creating config file.  Check permissions.\n";
            return false;
        }

        outfile << "task1:command=echo \"Hello, world!\",delay=5,run_once=false,verbose=true,enabled=true,log_file=task1.log,shell_command=false\n";
        outfile.close();

        // Now that we've created the file, try to load it again.
        file.open(filename);
        if (!file.is_open()) return false;
    }

    std::string line;
    while (std::getline(file, line)) {
        std::stringstream ss(line);
        std::string token;
        Config config;

        // Parse the task name
        std::getline(ss, token, ':');
        config.task_name = token;


        // Parse the attributes
        while (std::getline(ss, token, ',')) {
            size_t pos = token.find('=');
            if (pos != std::string::npos) {
                std::string key = token.substr(0, pos);
                std::string value = token.substr(pos + 1);

                if (key == "command") {
                    config.command = value;
                } else if (key == "delay") {
                    try {
                        config.delay = std::stoi(value);
                    } catch (const std::invalid_argument& e) {
                        std::cerr << "Error: Invalid delay value for task " << config.task_name << ". Using default value (0).\n";
                        config.delay = 0;
                    }
                } else if (key == "run_once") {
                    config.run_once = (value == "true");
                } else if (key == "verbose") {
                    config.verbose = (value == "true");
                } else if (key == "enabled") {
                    config.enabled = (value == "true");
                }
                 else if (key == "log_file") {
                    config.log_file = value;
                }
                 else if (key == "shell_command") {
                     config.shell_command = (value == "true");
                 }
                else {
                    std::cerr << "Warning: Unknown attribute '" << key << "' in task " << config.task_name << ".\n";
                }
            } else {
                std::cerr << "Warning: Invalid format in line: " << line << "\n";
            }
        }

        configs.push_back(config);
    }

    file.close();
    return true;
}

void runTask(const Config& config) {
    if (!config.enabled) return;

    if (config.verbose) {
        std::cout << "Running task: " << config.task_name << "\n";
    }

    std::string command_to_execute = config.command;
    if(config.shell_command){
        command_to_execute = "sh -c \"" + config.command + "\""; // Wrap the command for execution in the shell
    }

    std::ofstream logFile(config.log_file, std::ios::app);  // Open log file in append mode
    if (!logFile.is_open()) {
        std::cerr << "Error opening log file: " << config.log_file << "\n";
        return;
    }


    auto now = std::chrono::system_clock::now();
    auto time_now = std::chrono::system_clock::to_time_t(now);
    std::tm now_tm = *std::localtime(&time_now);
    char timestamp[30];
    std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now_tm);

    logFile << "[" << timestamp << "] Running command: " << config.command << "\n";



    int result = system(command_to_execute.c_str());

    if (result == 0) {
         logFile << "[" << timestamp << "] Task " << config.task_name << " completed successfully.\n";
        if (config.verbose) {
            std::cout << "Task " << config.task_name << " completed successfully.\n";
        }
    } else {
        logFile << "[" << timestamp << "] Task " << config.task_name << " failed with exit code: " << result << "\n";
        std::cerr << "Task " << config.task_name << " failed with exit code: " << result << "\n";
    }

    logFile.close();


    if (config.delay > 0) {
        if (config.verbose) {
            std::cout << "Waiting " << config.delay << " seconds before next run.\n";
        }
        std::this_thread::sleep_for(std::chrono::seconds(config.delay));
    }
}


void printHelp() {
    std::cout << "Task Automation CLI Tool\n";
    std::cout << "Usage: task_automation [command] [options]\n";
    std::cout << "Commands:\n";
    std::cout << "  help                  Display this help message.\n";
    std::cout << "  list                  List all tasks and their status.\n";
    std::cout << "  enable  <task_name>   Enable a task.\n";
    std::cout << "  disable <task_name>   Disable a task.\n";
    std::cout << "  edit    <task_name>   Edit a task's configuration.\n";
    std::cout << "  add                   Add a new task.\n";
    std::cout << "  remove  <task_name>   Remove a task.\n";
    std::cout << "  run     <task_name>   Run a single task immediately.\n";
    std::cout << "  (none)                Run all enabled tasks in a loop.\n";
}

void listTasks(const std::vector<Config>& configs) {
    std::cout << "Tasks:\n";
    std::cout << std::setw(20) << std::left << "Task Name" << std::setw(10) << "Enabled" << std::setw(10) << "Run Once" << std::setw(5) << "Delay" << std::endl;
    std::cout << std::setfill('-') << std::setw(50) << "-" << std::setfill(' ') << std::endl; // Separator line
    for (const auto& config : configs) {
        std::cout << std::setw(20) << std::left << config.task_name
                  << std::setw(10) << (config.enabled ? "Yes" : "No")
                  << std::setw(10) << (config.run_once ? "Yes" : "No")
                  << std::setw(5)  << config.delay << std::endl;
    }
}


void enableTask(std::vector<Config>& configs, const std::string& taskName) {
    auto it = std::find_if(configs.begin(), configs.end(), [&](const Config& c) {
        return c.task_name == taskName;
    });

    if (it != configs.end()) {
        it->enabled = true;
        std::cout << "Task '" << taskName << "' enabled.\n";
    } else {
        std::cerr << "Error: Task '" << taskName << "' not found.\n";
    }
}

void disableTask(std::vector<Config>& configs, const std::string& taskName) {
    auto it = std::find_if(configs.begin(), configs.end(), [&](const Config& c) {
        return c.task_name == taskName;
    });

    if (it != configs.end()) {
        it->enabled = false;
        std::cout << "Task '" << taskName << "' disabled.\n";
    } else {
        std::cerr << "Error: Task '" << taskName << "' not found.\n";
    }
}


void saveConfig(const std::string& filename, const std::vector<Config>& configs) {
    std::ofstream file(filename);
    if (!file.is_open()) {
        std::cerr << "Error: Could not open " << filename << " for writing.\n";
        return;
    }

    for (const auto& config : configs) {
        file << config.task_name << ":command=" << config.command
             << ",delay=" << config.delay
             << ",run_once=" << (config.run_once ? "true" : "false")
             << ",verbose=" << (config.verbose ? "true" : "false")
             << ",enabled=" << (config.enabled ? "true" : "false")
             << ",log_file=" << config.log_file
             << ",shell_command=" << (config.shell_command ? "true" : "false")
             << "\n";
    }

    file.close();
}



void editTask(std::vector<Config>& configs, const std::string& taskName) {
    auto it = std::find_if(configs.begin(), configs.end(), [&](const Config& c) {
        return c.task_name == taskName;
    });

    if (it == configs.end()) {
        std::cerr << "Error: Task '" << taskName << "' not found.\n";
        return;
    }

    Config& config = *it; // Get a reference to the config

    std::cout << "Editing task: " << config.task_name << "\n";
    printConfig(config); // Print current configuration

    std::cout << "Enter new values (leave blank to keep current value):\n";

    std::string input;

    std::cout << "Command (" << config.command << "): ";
    std::getline(std::cin, input);
    if (!input.empty()) {
        config.command = input;
    }

    std::cout << "Delay (" << config.delay << "): ";
    std::getline(std::cin, input);
    if (!input.empty()) {
        try {
            config.delay = std::stoi(input);
        } catch (const std::invalid_argument& e) {
            std::cerr << "Invalid input. Keeping original value.\n";
        }
    }

    std::cout << "Run Once (" << (config.run_once ? "true" : "false") << "): ";
    std::getline(std::cin, input);
    if (!input.empty()) {
        config.run_once = (input == "true");
    }

    std::cout << "Verbose (" << (config.verbose ? "true" : "false") << "): ";
    std::getline(std::cin, input);
    if (!input.empty()) {
        config.verbose = (input == "true");
    }

    std::cout << "Enabled (" << (config.enabled ? "true" : "false") << "): ";
    std::getline(std::cin, input);
    if (!input.empty()) {
        config.enabled = (input == "true");
    }

    std::cout << "Log File (" << config.log_file << "): ";
    std::getline(std::cin, input);
    if (!input.empty()) {
        config.log_file = input;
    }

     std::cout << "Run in shell (" << (config.shell_command ? "true" : "false") << "): ";
     std::getline(std::cin, input);
     if (!input.empty()) {
         config.shell_command = (input == "true");
     }

    std::cout << "Task '" << config.task_name << "' updated.\n";
    printConfig(config);
}


void printConfig(const Config& config) {
    std::cout << "  Task Name: " << config.task_name << "\n";
    std::cout << "  Command: " << config.command << "\n";
    std::cout << "  Delay: " << config.delay << "\n";
    std::cout << "  Run Once: " << (config.run_once ? "true" : "false") << "\n";
    std::cout << "  Verbose: " << (config.verbose ? "true" : "false") << "\n";
    std::cout << "  Enabled: " << (config.enabled ? "true" : "false") << "\n";
    std::cout << "  Log File: " << config.log_file << "\n";
    std::cout << "  Run in Shell: " << (config.shell_command ? "true" : "false") << "\n";
}



void addTask(std::vector<Config>& configs) {
    Config newConfig;

    std::cout << "Adding a new task.\n";

    std::cout << "Enter task name: ";
    std::getline(std::cin, newConfig.task_name);

    if (!isValidTaskName(newConfig.task_name)) {
        std::cerr << "Error: Invalid task name.  Task names must be alphanumeric and can contain underscores or hyphens.\n";
        return;
    }

    // Check for duplicate task names
    auto it = std::find_if(configs.begin(), configs.end(), [&](const Config& c) {
        return c.task_name == newConfig.task_name;
    });
    if (it != configs.end()) {
        std::cerr << "Error: Task with name '" << newConfig.task_name << "' already exists.\n";
        return;
    }



    std::cout << "Enter command: ";
    std::getline(std::cin, newConfig.command);

    std::string input;

    std::cout << "Enter delay (seconds): ";
    std::getline(std::cin, input);
    try {
        newConfig.delay = std::stoi(input);
    } catch (const std::invalid_argument& e) {
        std::cerr << "Invalid input. Setting delay to 0.\n";
        newConfig.delay = 0;
    }

    std::cout << "Run Once (true/false): ";
    std::getline(std::cin, input);
    newConfig.run_once = (input == "true");

    std::cout << "Verbose (true/false): ";
    std::getline(std::cin, input);
    newConfig.verbose = (input == "true");

    std::cout << "Enabled (true/false): ";
    std::getline(std::cin, input);
    newConfig.enabled = (input == "true");

    std::cout << "Enter Log File name: ";
    std::getline(std::cin, newConfig.log_file);

    std::cout << "Run in shell (true/false): ";
    std::getline(std::cin, input);
    newConfig.shell_command = (input == "true");


    configs.push_back(newConfig);

    std::cout << "Task '" << newConfig.task_name << "' added.\n";
    printConfig(newConfig);
}


void removeTask(std::vector<Config>& configs, const std::string& taskName) {
    auto it = std::remove_if(configs.begin(), configs.end(), [&](const Config& c) {
        return c.task_name == taskName;
    });

    if (it != configs.end()) {
        configs.erase(it, configs.end());
        std::cout << "Task '" << taskName << "' removed.\n";
    } else {
        std::cerr << "Error: Task '" << taskName << "' not found.\n";
    }
}


void runSingleTask(const std::vector<Config>& configs, const std::string& taskName) {
    auto it = std::find_if(configs.begin(), configs.end(), [&](const Config& c) {
        return c.task_name == taskName;
    });

    if (it != configs.end()) {
        runTask(*it);
    } else {
        std::cerr << "Error: Task '" << taskName << "' not found.\n";
    }
}
```
👁️ Viewed: 7

Comments