better config options, mvp for notifying based on current tasks
This commit is contained in:
parent
79d21bc3cb
commit
9c01297069
1
.gitignore
vendored
1
.gitignore
vendored
@ -55,6 +55,7 @@ bld/
|
||||
[Bb]uild/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
cmake-build-debug
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
|
@ -6,28 +6,59 @@
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include "const.hpp"
|
||||
|
||||
po::options_description get_current_tasks_options() {
|
||||
po::options_description options("Current Tasks");
|
||||
options.add_options()
|
||||
(CONFIG_NOTIFY.c_str(), po::bool_switch(), "send notification to notify")
|
||||
(CONFIG_HOST.c_str(), po::value<std::string>(), "ntfy hostname")
|
||||
(CONFIG_TOPIC.c_str(), po::value<std::string>(), "ntfy topic name")
|
||||
(CONFIG_TITLE.c_str(), po::value<std::string>(), "title for notifications")
|
||||
;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
std::shared_ptr<po::variables_map> init_config(int argc, const char *argv[])
|
||||
{
|
||||
try {
|
||||
po::options_description desc("Config");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
po::options_description visible_general("KC Config");
|
||||
visible_general.add_options()
|
||||
("help,h", "produce help message")
|
||||
("path,p", po::value<std::vector<std::string>>(), "set root path of knowledge base")
|
||||
("config", po::value<std::string>()->default_value("kc.ini"), "config file location")
|
||||
("out,o", po::value<std::string>()->default_value("."), "output file location")
|
||||
("command", po::value<std::string>(), "command to execute")
|
||||
("subargs", po::value<std::vector<std::string> >(), "Arguments for command")
|
||||
("index", po::value<int>()->default_value(1), "index")
|
||||
;
|
||||
|
||||
po::positional_options_description pos;
|
||||
pos.add("command", 1).add("subargs", -1);
|
||||
po::options_description hidden_general("Hidden");
|
||||
hidden_general.add_options()
|
||||
("command", po::value<std::string>(), "command to execute")
|
||||
;
|
||||
|
||||
po::positional_options_description positional;
|
||||
positional.add("command", 1);
|
||||
|
||||
auto current_tasks_options = get_current_tasks_options();
|
||||
|
||||
/////////
|
||||
// CMD
|
||||
/////////
|
||||
po::options_description cmdline_options;
|
||||
cmdline_options.add(desc);
|
||||
cmdline_options
|
||||
.add(visible_general)
|
||||
.add(hidden_general)
|
||||
.add(current_tasks_options)
|
||||
;
|
||||
|
||||
/////////
|
||||
// FILE
|
||||
/////////
|
||||
po::options_description config_file_options;
|
||||
config_file_options.add(desc);
|
||||
config_file_options
|
||||
.add(visible_general)
|
||||
.add(hidden_general)
|
||||
.add(current_tasks_options)
|
||||
;
|
||||
|
||||
////////////
|
||||
// PARSE
|
||||
@ -35,10 +66,10 @@ std::shared_ptr<po::variables_map> init_config(int argc, const char *argv[])
|
||||
|
||||
auto vm = std::make_shared<po::variables_map>();
|
||||
po::store(po::command_line_parser(argc, argv)
|
||||
.positional(positional)
|
||||
.options(cmdline_options)
|
||||
.positional(pos)
|
||||
// .allow_unregistered()
|
||||
.run(),
|
||||
.run(),
|
||||
*vm);
|
||||
|
||||
if (vm->contains("config"))
|
||||
@ -54,7 +85,19 @@ std::shared_ptr<po::variables_map> init_config(int argc, const char *argv[])
|
||||
po::notify(*vm);
|
||||
|
||||
if (vm->contains("help")) {
|
||||
std::cout << desc;
|
||||
|
||||
if (vm->count("command") == 1)
|
||||
{
|
||||
auto command = (*vm)["command"].as<std::string>();
|
||||
|
||||
if (command == CMD_CURRENT_TASKS) {
|
||||
std::cout << visible_general << std::endl << current_tasks_options;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << visible_general;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2,6 +2,31 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
///////////////
|
||||
/// COMMANDS
|
||||
///////////////
|
||||
|
||||
static const std::string CMD_VALIDATE_TASKS = "validate";
|
||||
static const std::string CMD_IMG_TASKS = "img";
|
||||
static const std::string CMD_PRINT_TASKS = "print";
|
||||
static const std::string CMD_CURRENT_TASKS = "current";
|
||||
static const std::string CMD_NET_TASKS = "net";
|
||||
|
||||
|
||||
///////////////
|
||||
/// CONFIG
|
||||
///////////////
|
||||
|
||||
static const std::string CONFIG_NOTIFY = "notify";
|
||||
static const std::string CONFIG_HOST = "host";
|
||||
static const std::string CONFIG_TOPIC = "topic";
|
||||
static const std::string CONFIG_TITLE = "title";
|
||||
|
||||
|
||||
///////////////
|
||||
/// REGEXES
|
||||
///////////////
|
||||
|
||||
static const std::string MD_LINK_REGEX = R"(\[.*?\]\(.*?\))";
|
||||
static const std::string MD_MD_LINK_REGEX = R"(\[.*?\]\(.*?\.md(#.*?)*\))";
|
||||
static const std::string MD_IMAGE_LINK_REGEX = R"(!\[.*?\]\(.*?\.png\))";
|
||||
|
21
src/main.cpp
21
src/main.cpp
@ -21,7 +21,7 @@
|
||||
void run_validate(const kc::AppContext &app_context);
|
||||
void run_img(const kc::AppContext &app_context);
|
||||
void run_print(const kc::AppContext &app_context);
|
||||
void run_current_tasks(const kc::AppContext &app_context);
|
||||
int run_current_tasks(const kc::AppContext &app_context);
|
||||
void run_test_net(const kc::AppContext &app_context);
|
||||
|
||||
|
||||
@ -42,27 +42,27 @@ int main(int argc, const char *argv[]) {
|
||||
const auto command = app_context.command();
|
||||
if (!command.empty())
|
||||
{
|
||||
if (command == "validate")
|
||||
if (command == CMD_VALIDATE_TASKS)
|
||||
{
|
||||
app_context.load_and_parse_cache(kc::LINKS);
|
||||
run_validate(app_context);
|
||||
}
|
||||
else if (command == "img")
|
||||
else if (command == CMD_IMG_TASKS)
|
||||
{
|
||||
app_context.load_and_parse_cache(kc::IMAGES);
|
||||
run_img(app_context);
|
||||
}
|
||||
else if (command == "print")
|
||||
else if (command == CMD_PRINT_TASKS)
|
||||
{
|
||||
app_context.load_and_parse_cache();
|
||||
run_print(app_context);
|
||||
}
|
||||
else if (command == "current")
|
||||
else if (command == CMD_CURRENT_TASKS)
|
||||
{
|
||||
app_context.load_and_parse_cache(kc::TASKS);
|
||||
run_current_tasks(app_context);
|
||||
return run_current_tasks(app_context);
|
||||
}
|
||||
else if (command == "net")
|
||||
else if (command == CMD_NET_TASKS)
|
||||
{
|
||||
// app_context.load_and_parse_cache(kc::TASKS);
|
||||
run_test_net(app_context);
|
||||
@ -106,13 +106,10 @@ void run_print(const kc::AppContext &app_context)
|
||||
}
|
||||
}
|
||||
|
||||
void run_current_tasks(const kc::AppContext &app_context)
|
||||
int run_current_tasks(const kc::AppContext &app_context)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Running \"Current Tasks\" command";
|
||||
for(const auto& file_cache : app_context.file_caches) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Finding current tasks in " << file_cache->get_root_path();
|
||||
kc::current_tasks(file_cache->get());
|
||||
}
|
||||
return kc::current_tasks(app_context);
|
||||
}
|
||||
|
||||
void run_test_net(const kc::AppContext &app_context)
|
||||
|
@ -1,9 +1,54 @@
|
||||
#include "http.hpp"
|
||||
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
// https://www.boost.org/doc/libs/1_87_0/libs/beast/example/http/client/sync-ssl/http_client_sync_ssl.cpp
|
||||
|
||||
namespace kc {
|
||||
std::shared_ptr<ssl::stream<beast::tcp_stream>> connect_stream(const std::string &host, int port) {
|
||||
|
||||
http::response<http::dynamic_body> read_response(const std::shared_ptr<ssl::stream<beast::tcp_stream>> &stream) {
|
||||
// This buffer is used for reading and must be persisted
|
||||
beast::flat_buffer buffer;
|
||||
|
||||
// Declare a container to hold the response
|
||||
http::response<http::dynamic_body> res;
|
||||
|
||||
// Receive the HTTP response
|
||||
BOOST_LOG_TRIVIAL(debug) << "Reading HTTP response";
|
||||
http::read(*stream, buffer, res);
|
||||
BOOST_LOG_TRIVIAL(debug) << "Received response from (" << res.result_int() << ")";
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void shutdown_stream(const std::shared_ptr<ssl::stream<beast::tcp_stream>> &stream) {
|
||||
// Gracefully close the socket
|
||||
beast::error_code ec;
|
||||
BOOST_LOG_TRIVIAL(debug) << "Gracefully shutting down stream";
|
||||
stream->shutdown(ec);
|
||||
if(ec == net::error::eof)
|
||||
{
|
||||
// Rationale:
|
||||
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
|
||||
ec = {};
|
||||
}
|
||||
if(ec)
|
||||
throw beast::system_error{ec};
|
||||
}
|
||||
|
||||
void request(http::verb method, const std::string &host, std::string target, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers) {
|
||||
request(method, host, target, 443, body, std::move(headers));
|
||||
}
|
||||
|
||||
void request(http::verb method, const std::string &host, std::string target, std::string body) {
|
||||
request(method, host, target, 443, body, nullptr);
|
||||
}
|
||||
|
||||
void request(http::verb method, const std::string &host, std::string target, int port, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Opening connection to [" << host << ":" << port << "]";
|
||||
|
||||
// The io_context is required for all I/O
|
||||
net::io_context ioc;
|
||||
|
||||
@ -28,55 +73,17 @@ std::shared_ptr<ssl::stream<beast::tcp_stream>> connect_stream(const std::string
|
||||
}
|
||||
|
||||
// Look up the domain name
|
||||
BOOST_LOG_TRIVIAL(debug) << "Resolving IP for [" << host << "]";
|
||||
auto const results = resolver.resolve(host, std::to_string(port));
|
||||
|
||||
// Make the connection on the IP address we get from a lookup
|
||||
BOOST_LOG_TRIVIAL(debug) << "Connecting stream [" << host << ":" << port << "]";
|
||||
beast::get_lowest_layer(*stream).connect(results);
|
||||
|
||||
// Perform the SSL handshake
|
||||
BOOST_LOG_TRIVIAL(debug) << "Performing TLS handshake [" << host << ":" << port << "]";
|
||||
stream->handshake(ssl::stream_base::client);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
http::response<http::dynamic_body> read_response(const std::shared_ptr<ssl::stream<beast::tcp_stream>> &stream) {
|
||||
// This buffer is used for reading and must be persisted
|
||||
beast::flat_buffer buffer;
|
||||
|
||||
// Declare a container to hold the response
|
||||
http::response<http::dynamic_body> res;
|
||||
|
||||
// Receive the HTTP response
|
||||
http::read(*stream, buffer, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void shutdown_stream(const std::shared_ptr<ssl::stream<beast::tcp_stream>> &stream) {
|
||||
// Gracefully close the socket
|
||||
beast::error_code ec;
|
||||
stream->shutdown(ec);
|
||||
if(ec == net::error::eof)
|
||||
{
|
||||
// Rationale:
|
||||
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
|
||||
ec = {};
|
||||
}
|
||||
if(ec)
|
||||
throw beast::system_error{ec};
|
||||
}
|
||||
|
||||
http::response<http::dynamic_body> request(http::verb method, const std::string &host, std::string target, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers) {
|
||||
return request(method, host, target, 443, body, std::move(headers));
|
||||
}
|
||||
|
||||
http::response<http::dynamic_body> request(http::verb method, const std::string &host, std::string target, std::string body) {
|
||||
return request(method, host, target, 443, body, nullptr);
|
||||
}
|
||||
|
||||
http::response<http::dynamic_body> request(http::verb method, const std::string &host, std::string target, int port, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers) {
|
||||
auto stream = connect_stream(host, port);
|
||||
|
||||
http::request<http::string_body> req{method, target, 11};
|
||||
req.set(http::field::host, host);
|
||||
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||
@ -92,14 +99,11 @@ http::response<http::dynamic_body> request(http::verb method, const std::string
|
||||
req.prepare_payload(); // set content-length based on the body
|
||||
|
||||
// Send the HTTP request to the remote host
|
||||
BOOST_LOG_TRIVIAL(debug) << "Writing HTTP request to stream [" << host << ":" << port << "]";
|
||||
http::write(*stream, req);
|
||||
|
||||
auto response = read_response(stream);
|
||||
|
||||
shutdown_stream(stream);
|
||||
|
||||
// Write the message to standard out
|
||||
std::cout << response << std::endl;
|
||||
return response;
|
||||
}
|
||||
}
|
@ -3,12 +3,8 @@
|
||||
#include <unordered_map>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||
@ -17,8 +13,8 @@ namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
||||
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||
|
||||
namespace kc {
|
||||
http::response<http::dynamic_body> request(http::verb method, const std::string &host, std::string target, std::string body);
|
||||
http::response<http::dynamic_body> request(http::verb method, const std::string &host, std::string target, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers);
|
||||
http::response<http::dynamic_body> request(http::verb method, const std::string &host, std::string target, int port, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers);
|
||||
void request(http::verb method, const std::string &host, std::string target, std::string body);
|
||||
void request(http::verb method, const std::string &host, std::string target, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers);
|
||||
void request(http::verb method, const std::string &host, std::string target, int port, std::string body, std::unique_ptr<std::unordered_map<std::string, std::string>> headers);
|
||||
}
|
||||
|
||||
|
@ -2,21 +2,67 @@
|
||||
#include "task.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
|
||||
#include "../logging.hpp"
|
||||
|
||||
namespace kc {
|
||||
|
||||
void current_tasks(const std::vector<std::shared_ptr<kc::FileContext>> &contexts)
|
||||
{
|
||||
auto date = day_clock::local_day();
|
||||
std::string get_notification_content(const std::vector<Task>& tasks) {
|
||||
std::vector<std::string> contents(tasks.size());
|
||||
|
||||
for (const auto &entry : contexts) {
|
||||
std::ranges::transform(tasks, contents.rbegin(), [](const Task &t) -> std::string { return t.get_content(); });
|
||||
|
||||
for (const auto &task : entry->tasks) {
|
||||
|
||||
if (is_current(task, date))
|
||||
std::cout << "(" << task.get_content() << ") : " << task.get_due_date() << " " << entry->file_entry->file_entry.path() << std::endl;
|
||||
}
|
||||
}
|
||||
return boost::join(contents, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
int current_tasks(const kc::AppContext &app_context)
|
||||
{
|
||||
auto date = day_clock::local_day();
|
||||
std::vector<Task> tasks;
|
||||
|
||||
for(const auto& file_cache : app_context.file_caches) {
|
||||
for (const auto &entry : file_cache->get()) {
|
||||
for (const auto &task : entry->tasks) {
|
||||
|
||||
if (is_current(task, date)) {
|
||||
tasks.emplace_back(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ranges::sort(tasks, [](Task a, Task b)
|
||||
{
|
||||
return a.get_due_date() < b.get_due_date();
|
||||
});
|
||||
|
||||
for (const auto& task : tasks) {
|
||||
std::cout << task.get_content() << " (" << task.get_due_date() << ")" << std::endl;
|
||||
}
|
||||
|
||||
if (app_context.config->contains(CONFIG_NOTIFY)) {
|
||||
if (!app_context.config->contains(CONFIG_HOST)) {
|
||||
print_and_log_error("No NTFY host provided");
|
||||
return 1;
|
||||
}
|
||||
if (!app_context.config->contains(CONFIG_TOPIC)) {
|
||||
print_and_log_error("No NTFY topic name provided");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto host_name = (*app_context.config)[CONFIG_HOST].as<std::string>();
|
||||
auto topic_name = (*app_context.config)[CONFIG_TOPIC].as<std::string>();
|
||||
|
||||
auto payload = get_notification_content(tasks);
|
||||
|
||||
auto notif = kc::Notification(topic_name, payload);
|
||||
|
||||
kc::notify(host_name, notif);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include "../parse/FileContext.hpp"
|
||||
#include "../appcontext.hpp"
|
||||
#include "../net/ntfy.hpp"
|
||||
|
||||
namespace kc {
|
||||
|
||||
void current_tasks(const std::vector<std::shared_ptr<kc::FileContext>> &contexts);
|
||||
int current_tasks(const kc::AppContext &app_context);
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user