From 171543d920f04e7b85bddc1af446161b871b4ad9 Mon Sep 17 00:00:00 2001 From: Miguel Ribeiro Date: Wed, 5 Jun 2024 18:54:12 +0200 Subject: [PATCH 1/3] feat: add ntfy as notification method --- endpoints/cronjobs/sendnotifications.php | 62 +++++++++++++- endpoints/db/migrate.php | 3 + .../notifications/saventfynotifications.php | 84 +++++++++++++++++++ .../savepushovernotifications.php | 6 ++ .../notifications/testntfynotifications.php | 70 ++++++++++++++++ includes/version.php | 2 +- migrations/000021.php | 15 ++++ scripts/notifications.js | 37 ++++++++ settings.php | 47 +++++++++++ styles/dark-theme.css | 4 + 10 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 endpoints/notifications/saventfynotifications.php create mode 100644 endpoints/notifications/testntfynotifications.php create mode 100644 migrations/000021.php diff --git a/endpoints/cronjobs/sendnotifications.php b/endpoints/cronjobs/sendnotifications.php index 44c21186b..ad6d09d19 100644 --- a/endpoints/cronjobs/sendnotifications.php +++ b/endpoints/cronjobs/sendnotifications.php @@ -25,6 +25,7 @@ $webhookNotificationsEnabled = false; $pushoverNotificationsEnabled = false; $discordNotificationsEnabled = false; + $ntfyNotificationsEnabled = false; // Get notification settings (how many days before the subscription ends should the notification be sent) $query = "SELECT days FROM notification_settings WHERE user_id = :userId"; @@ -102,6 +103,19 @@ $pushover['token'] = $row["token"]; } + // Check if Nrfy notifications are enabled and get the settings + $query = "SELECT * FROM ntfy_notifications WHERE user_id = :userId"; + $stmt = $db->prepare($query); + $stmt->bindValue(':userId', $userId, SQLITE3_INTEGER); + $result = $stmt->execute(); + + if ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $ntfyNotificationsEnabled = $row['enabled']; + $ntfy['host'] = $row["host"]; + $ntfy['topic'] = $row["topic"]; + $ntfy['headers'] = $row["headers"]; + } + // Check if Webhook notifications are enabled and get the settings $query = "SELECT * FROM webhook_notifications WHERE user_id = :userId"; $stmt = $db->prepare($query); @@ -120,7 +134,9 @@ } } - $notificationsEnabled = $emailNotificationsEnabled || $gotifyNotificationsEnabled || $telegramNotificationsEnabled || $webhookNotificationsEnabled || $pushoverNotificationsEnabled || $discordNotificationsEnabled; + $notificationsEnabled = $emailNotificationsEnabled || $gotifyNotificationsEnabled || $telegramNotificationsEnabled || + $webhookNotificationsEnabled || $pushoverNotificationsEnabled || $discordNotificationsEnabled || + $ntfyNotificationsEnabled; // If no notifications are enabled, no need to run if (!$notificationsEnabled) { @@ -429,6 +445,50 @@ } } + // Ntfy notifications if enabled + if ($ntfyNotificationsEnabled) { + foreach ($notify as $userId => $perUser) { + // Get name of user from household table + $stmt = $db->prepare('SELECT * FROM household WHERE id = :userId'); + $stmt->bindValue(':userId', $userId, SQLITE3_INTEGER); + $result = $stmt->execute(); + $user = $result->fetchArray(SQLITE3_ASSOC); + + if ($user['name']) { + $message = $user['name'] . ", the following subscriptions are up for renewal:\n"; + } else { + $message = "The following subscriptions are up for renewal:\n"; + } + + foreach ($perUser as $subscription) { + $dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days"; + $message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n"; + } + + $headers = json_decode($ntfy["headers"], true); + $customheaders = array_map(function($key, $value) { + return "$key: $value"; + }, array_keys($headers), $headers); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $ntfy['host'] . '/' . $ntfy['topic']); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $message); + curl_setopt($ch, CURLOPT_HTTPHEADER, $customheaders); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + curl_close($ch); + + if ($response === false) { + echo "Error sending notifications: " . curl_error($ch) . "
"; + } else { + echo "Ntfy Notifications sent
"; + } + } + } + // Webhook notifications if enabled if ($webhookNotificationsEnabled) { // Get webhook payload and turn it into a json object diff --git a/endpoints/db/migrate.php b/endpoints/db/migrate.php index 99069b31d..e78947709 100644 --- a/endpoints/db/migrate.php +++ b/endpoints/db/migrate.php @@ -49,6 +49,9 @@ function errorHandler($severity, $message, $file, $line) { } foreach ($requiredMigrations as $migration) { + if (!file_exists($migration)) { + $migration = '../../' . $migration; + } require_once $migration; $stmtInsert = $db->prepare('INSERT INTO migrations (migration) VALUES (:migration)'); diff --git a/endpoints/notifications/saventfynotifications.php b/endpoints/notifications/saventfynotifications.php new file mode 100644 index 000000000..ec663bc99 --- /dev/null +++ b/endpoints/notifications/saventfynotifications.php @@ -0,0 +1,84 @@ + false, + "message" => translate('session_expired', $i18n) + ])); + } + + if ($_SERVER["REQUEST_METHOD"] === "POST") { + $postData = file_get_contents("php://input"); + $data = json_decode($postData, true); + + if ( + !isset($data["topic"]) || $data["topic"] == "" || + !isset($data["host"]) || $data["host"] == "" + ) { + $response = [ + "success" => false, + "message" => translate('fill_mandatory_fields', $i18n) + ]; + echo json_encode($response); + } else { + $enabled = $data["enabled"]; + $host = $data["host"]; + $topic = $data["topic"]; + $headers = $data["headers"]; + + $query = "SELECT COUNT(*) FROM ntfy_notifications WHERE user_id = :userId"; + $stmt = $db->prepare($query); + $stmt->bindParam(":userId", $userId, SQLITE3_INTEGER); + $result = $stmt->execute(); + + if ($result === false) { + $response = [ + "success" => false, + "message" => translate('error_saving_notifications', $i18n) + ]; + echo json_encode($response); + } else { + $row = $result->fetchArray(); + $count = $row[0]; + if ($count == 0) { + $query = "INSERT INTO ntfy_notifications (enabled, host, topic, headers, user_id) + VALUES (:enabled, :host, :topic, :headers, :userId)"; + } else { + $query = "UPDATE ntfy_notifications + SET enabled = :enabled, host = :host, topic = :topic, headers = :headers WHERE user_id = :userId"; + } + + $stmt = $db->prepare($query); + $stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER); + $stmt->bindValue(':host', $host, SQLITE3_TEXT); + $stmt->bindValue(':topic', $topic, SQLITE3_TEXT); + $stmt->bindValue(':headers', $headers, SQLITE3_TEXT); + $stmt->bindValue(':userId', $userId, SQLITE3_INTEGER); + + if ($stmt->execute()) { + $response = [ + "success" => true, + "message" => translate('notifications_settings_saved', $i18n) + ]; + echo json_encode($response); + } else { + $response = [ + "success" => false, + "message" => translate('error_saving_notifications', $i18n) + ]; + echo json_encode($response); + } + } + } + + } else { + $response = [ + "success" => false, + "message" => translate('invalid_request_method', $i18n) + ]; + echo json_encode($response); + } + +?> diff --git a/endpoints/notifications/savepushovernotifications.php b/endpoints/notifications/savepushovernotifications.php index f2295ce23..0c171de12 100644 --- a/endpoints/notifications/savepushovernotifications.php +++ b/endpoints/notifications/savepushovernotifications.php @@ -70,6 +70,12 @@ } } } + } else { + $response = [ + "success" => false, + "message" => translate('invalid_request_method', $i18n) + ]; + echo json_encode($response); } ?> \ No newline at end of file diff --git a/endpoints/notifications/testntfynotifications.php b/endpoints/notifications/testntfynotifications.php new file mode 100644 index 000000000..612d06186 --- /dev/null +++ b/endpoints/notifications/testntfynotifications.php @@ -0,0 +1,70 @@ + false, + "message" => translate('session_expired', $i18n) + ])); +} + +if ($_SERVER["REQUEST_METHOD"] === "POST") { + $postData = file_get_contents("php://input"); + $data = json_decode($postData, true); + + if ( + !isset($data["host"]) || $data["host"] == "" || + !isset($data["topic"]) || $data["topic"] == "" + ) { + $response = [ + "success" => false, + "message" => translate('fill_mandatory_fields', $i18n) + ]; + echo json_encode($response); + } else { + $host = $data["host"]; + $topic = $data["topic"]; + $headers = json_decode($data["headers"], true); + $customheaders = array_map(function($key, $value) { + return "$key: $value"; + }, array_keys($headers), $headers); + + $url = "$host/$topic"; + + // Set the message parameters + $message = translate('test_notification', $i18n); + + $ch = curl_init(); + + // Set the URL and other options + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $message); + curl_setopt($ch, CURLOPT_HTTPHEADER, $customheaders); + + // Execute the request + $response = curl_exec($ch); + + // Close the cURL session + curl_close($ch); + + // Check if the message was sent successfully + if ($response === false) { + die(json_encode([ + "success" => false, + "message" => translate('notification_failed', $i18n) + ])); + } else { + print_r($response); + } + + die(json_encode([ + "success" => true, + "message" => translate('notification_sent_successfuly', $i18n) + ])); + } + +} + +?> \ No newline at end of file diff --git a/includes/version.php b/includes/version.php index 1dc342cf0..13b60f72b 100644 --- a/includes/version.php +++ b/includes/version.php @@ -1,3 +1,3 @@ diff --git a/migrations/000021.php b/migrations/000021.php new file mode 100644 index 000000000..a849e80c2 --- /dev/null +++ b/migrations/000021.php @@ -0,0 +1,15 @@ +exec('CREATE TABLE IF NOT EXISTS ntfy_notifications ( + enabled BOOLEAN DEFAULT 0, + host TEXT DEFAULT "", + topic TEXT DEFAULT "", + headers TEXT DEFAULT "", + user_id INTEGER, + FOREIGN KEY (user_id) REFERENCES user(id) +)'); \ No newline at end of file diff --git a/scripts/notifications.js b/scripts/notifications.js index 3a816b5f1..bb67040dd 100644 --- a/scripts/notifications.js +++ b/scripts/notifications.js @@ -280,4 +280,41 @@ function testNotificationsDiscordButton() { }; makeFetchCall('endpoints/notifications/testdiscordnotifications.php', data, button); +} + +function testNotificationsNtfyButton() { + const button = document.getElementById("testNotificationsNtfy"); + button.disabled = true; + + const host = document.getElementById("ntfyhost").value; + const topic = document.getElementById("ntfytopic").value; + const headers = document.getElementById("ntfyheaders").value; + + + const data = { + host: host, + topic: topic, + headers: headers + }; + + makeFetchCall('endpoints/notifications/testntfynotifications.php', data, button); +} + +function saveNotificationsNtfyButton() { + const button = document.getElementById("saveNotificationsNtfy"); + button.disabled = true; + + const enabled = document.getElementById("ntfyenabled").checked ? 1 : 0; + const host = document.getElementById("ntfyhost").value; + const topic = document.getElementById("ntfytopic").value; + const headers = document.getElementById("ntfyheaders").value; + + const data = { + enabled: enabled, + host: host, + topic: topic, + headers: headers + }; + + makeFetchCall('endpoints/notifications/saventfynotifications.php', data, button); } \ No newline at end of file diff --git a/settings.php b/settings.php index 710641b48..9249ff196 100644 --- a/settings.php +++ b/settings.php @@ -309,6 +309,28 @@ $notificationsTelegram['chat_id'] = ""; } + // Ntfy notifications + $sql = "SELECT * FROM ntfy_notifications WHERE user_id = :userId LIMIT 1"; + $stmt = $db->prepare($sql); + $stmt->bindValue(':userId', $userId, SQLITE3_INTEGER); + $result = $stmt->execute(); + + $rowCount = 0; + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $notificationsNtfy['enabled'] = $row['enabled']; + $notificationsNtfy['host'] = $row['host']; + $notificationsNtfy['topic'] = $row['topic']; + $notificationsNtfy['headers'] = $row['headers']; + $rowCount++; + } + + if ($rowCount == 0) { + $notificationsNtfy['enabled'] = 0; + $notificationsNtfy['host'] = ""; + $notificationsNtfy['topic'] = ""; + $notificationsNtfy['headers'] = ""; + } + // Webhook notifications $sql = "SELECT * FROM webhook_notifications WHERE user_id = :userId LIMIT 1"; $stmt = $db->prepare($sql); @@ -537,6 +559,31 @@ +
+ +