stuff
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
config.toml
|
||||||
|
build/**
|
||||||
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(notification-pusher LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
message(FATAL_ERROR "Please configure with Clang: cmake -DCMAKE_CXX_COMPILER=clang++ ..")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(fmt REQUIRED)
|
||||||
|
find_package(spdlog REQUIRED)
|
||||||
|
find_package(PahoMqttCpp REQUIRED)
|
||||||
|
find_package(cpr REQUIRED)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} src/main.cpp
|
||||||
|
src/definitions.cpp
|
||||||
|
src/daemon.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||||
|
-Wall -Wextra -Wpedantic
|
||||||
|
-Wshadow -Wconversion
|
||||||
|
-O2
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
PRIVATE
|
||||||
|
fmt::fmt
|
||||||
|
spdlog::spdlog
|
||||||
|
PahoMqttCpp::paho-mqttpp3
|
||||||
|
cpr::cpr
|
||||||
|
)
|
||||||
|
|
||||||
75
src/daemon.cpp
Normal file
75
src/daemon.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include "daemon.hpp"
|
||||||
|
#include "definitions.hpp"
|
||||||
|
#include "json.hpp"
|
||||||
|
#include <cpr/api.h>
|
||||||
|
#include <cpr/body.h>
|
||||||
|
#include <cpr/cprtypes.h>
|
||||||
|
#include <cpr/response.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <mqtt/client.h>
|
||||||
|
#include <mqtt/exception.h>
|
||||||
|
#include <mqtt/message.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <cpr/cpr.h>
|
||||||
|
namespace daemon_notify {
|
||||||
|
void start(std::unique_ptr<mqtt::client> client, std::string topic) {
|
||||||
|
spdlog::info("Entered the notification daemon thread");
|
||||||
|
|
||||||
|
// Subscribe and start consuming
|
||||||
|
client->subscribe(topic, 1);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Wait for SUBACK
|
||||||
|
client->start_consuming();
|
||||||
|
|
||||||
|
|
||||||
|
// Consume messages with manual timeout to avoid indefinite blocking
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
mqtt::const_message_ptr potential_notification_message;
|
||||||
|
bool has_message = false;
|
||||||
|
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
auto timeout = std::chrono::seconds(2);
|
||||||
|
|
||||||
|
while ((std::chrono::steady_clock::now() - start) < timeout) {
|
||||||
|
has_message = client->try_consume_message(&potential_notification_message);
|
||||||
|
if (has_message) break;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_message) {
|
||||||
|
spdlog::trace("No message received in this interval.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::debug("Received mqtt message: {} on topic {}",
|
||||||
|
potential_notification_message->to_string(),
|
||||||
|
potential_notification_message->get_topic());
|
||||||
|
|
||||||
|
nlohmann::json parsed_json = nlohmann::json::parse(potential_notification_message->to_string());
|
||||||
|
definitions::Notification notif = parsed_json.get<definitions::Notification>();
|
||||||
|
|
||||||
|
cpr::Response notification_post_request = cpr::Post(
|
||||||
|
cpr::Url{fmt::format("https://ntfy.sh/{}", notif.topic)},
|
||||||
|
cpr::Header{{"Title", notif.title}},
|
||||||
|
cpr::Body{notif.body}
|
||||||
|
);
|
||||||
|
|
||||||
|
spdlog::debug("Status code: {}", notification_post_request.status_code);
|
||||||
|
}
|
||||||
|
catch (const mqtt::exception& e) {
|
||||||
|
spdlog::warn("Encountered mqtt exception: {}", e.what());
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::parse_error& e) {
|
||||||
|
spdlog::warn("Encountered JSON parsing exception: {}", e.what());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
spdlog::warn("Encountered std exception: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client->disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
6
src/daemon.hpp
Normal file
6
src/daemon.hpp
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <mqtt/client.h>
|
||||||
|
namespace daemon_notify{
|
||||||
|
void start(std::unique_ptr<mqtt::client> client, std::string topic);
|
||||||
|
}
|
||||||
5
src/definitions.cpp
Normal file
5
src/definitions.cpp
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include "definitions.hpp"
|
||||||
|
namespace definitions{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
12
src/definitions.hpp
Normal file
12
src/definitions.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "json.hpp"
|
||||||
|
namespace definitions{
|
||||||
|
struct Notification{
|
||||||
|
std::string title;
|
||||||
|
std::string body;
|
||||||
|
std::string topic;
|
||||||
|
|
||||||
|
};
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Notification, title, body, topic)
|
||||||
|
|
||||||
|
}
|
||||||
25526
src/json.hpp
Normal file
25526
src/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
104
src/main.cpp
Normal file
104
src/main.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <mqtt/client.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
#include "daemon.hpp"
|
||||||
|
#include "toml.hpp"
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
spdlog::set_level(spdlog::level::debug);
|
||||||
|
|
||||||
|
// Parse config.toml
|
||||||
|
auto config = toml::parse_file("config.toml");
|
||||||
|
uint client_counter = {0};
|
||||||
|
// Retrieve broker settings
|
||||||
|
std::string server_address = config["broker"]["address"].value_or("");
|
||||||
|
std::string client_id = config["broker"]["client_id"].value_or("");
|
||||||
|
std::string topic = config["broker"]["topic"].value_or("");
|
||||||
|
|
||||||
|
// Retrieve auth settings
|
||||||
|
std::string username = config["auth"]["username"].value_or("");
|
||||||
|
std::string password = config["auth"]["password"].value_or("");
|
||||||
|
|
||||||
|
if (server_address.empty() || client_id.empty() || topic.empty()) {
|
||||||
|
spdlog::error("Missing broker configuration in config.toml");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create MQTT client
|
||||||
|
mqtt::client client(server_address, fmt::format("{}-{}",client_id,client_counter++));
|
||||||
|
|
||||||
|
// Configure connection with login
|
||||||
|
mqtt::connect_options connOpts;
|
||||||
|
connOpts.set_clean_session(true);
|
||||||
|
|
||||||
|
if (!username.empty()) {
|
||||||
|
connOpts.set_user_name(username);
|
||||||
|
connOpts.set_password(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::info("Connecting to broker at {} with client ID '{}'", server_address, client_id);
|
||||||
|
client.connect(connOpts);
|
||||||
|
spdlog::info("Connected successfully.");
|
||||||
|
|
||||||
|
// Subscribe
|
||||||
|
client.subscribe(topic, 1);
|
||||||
|
spdlog::info("Subscribed to topic '{}'", topic);
|
||||||
|
|
||||||
|
// Publish a test message
|
||||||
|
auto message = fmt::format("Hello from {}", client_id);
|
||||||
|
client.publish(topic, message.data(), message.size(), 1, false);
|
||||||
|
spdlog::info("Published message: {}", message);
|
||||||
|
|
||||||
|
// Receive one message
|
||||||
|
spdlog::info("Waiting for incoming message...");
|
||||||
|
auto msg = client.consume_message();
|
||||||
|
if (msg) {
|
||||||
|
spdlog::info("Received message on topic '{}': {}",
|
||||||
|
msg->get_topic(), msg->to_string());
|
||||||
|
} else {
|
||||||
|
spdlog::warn("No message received.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect
|
||||||
|
client.disconnect();
|
||||||
|
spdlog::info("Disconnected from broker for the test attempt.");
|
||||||
|
// Start Notification Daemon (listen to that one topic, and then write to ntfy.sh)
|
||||||
|
spdlog::info("Starting notification daemon");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<mqtt::client> daemon_mqtt_client = std::make_unique<mqtt::client>(server_address, fmt::format("{}-{}", client_id, client_counter++));
|
||||||
|
daemon_mqtt_client->connect(connOpts);
|
||||||
|
std::thread daemon_thread(
|
||||||
|
[client = std::move(daemon_mqtt_client), topic]() mutable {
|
||||||
|
daemon_notify::start(std::move(client), topic);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
daemon_thread.join();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const toml::parse_error& err) {
|
||||||
|
spdlog::error("TOML parse error: {}", err.description());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const mqtt::exception& e) {
|
||||||
|
spdlog::error("MQTT Error: {}", e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
spdlog::error("Error: {}", e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
17880
src/toml.hpp
Normal file
17880
src/toml.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user