add support for mattermost notifications (#923)

Signed-off-by: Daniela Filipe Bento <danibento@overdestiny.com>
This commit is contained in:
Dani Bento
2025-10-12 11:42:39 +01:00
committed by GitHub
parent 98eeda70fb
commit 75c94ad15c
9 changed files with 372 additions and 2 deletions

View File

@@ -89,6 +89,8 @@ See instructions to run Wallos below.
- openssl
- sqlite3
- zip
- mbstring
- fpm
#### Docker

View File

@@ -61,6 +61,8 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$telegramNotificationsEnabled = false;
$webhookNotificationsEnabled = false;
$pushoverNotificationsEnabled = false;
$pushplusNotificationsEnabled = false;
$mattermostNotificationsEnabled = false;
$discordNotificationsEnabled = false;
$ntfyNotificationsEnabled = false;
@@ -74,7 +76,6 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$days = $row['days'];
}
// Check if email notifications are enabled and get the settings
$query = "SELECT * FROM email_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
@@ -141,6 +142,19 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$pushplusNotificationsEnabled = $row['enabled'];
$pushplus['token'] = $row["token"];
}
// Check if Mattermost notifications are enabled and get the settings
$query = "SELECT * FROM mattermost_notifications WHERE user_id = :userID";
$stmt = $db->prepare($query);
$stmt->bindValue(':userID', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$mattermostNotificationsEnabled = $row['enabled'];
$mattermost['webhook_url'] = $row['webhook_url'];
$mattermost['bot_username'] = $row['bot_username'];
$mattermost['bot_icon_emoji'] = $row['bot_icon_emoji'];
}
// Check if Pushover notifications are enabled and get the settings
$query = "SELECT * FROM pushover_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
@@ -184,7 +198,7 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$notificationsEnabled = $emailNotificationsEnabled || $gotifyNotificationsEnabled || $telegramNotificationsEnabled ||
$webhookNotificationsEnabled || $pushoverNotificationsEnabled || $discordNotificationsEnabled ||$pushplusNotificationsEnabled||
$ntfyNotificationsEnabled;
$mattermostNotificationsEnabled || $ntfyNotificationsEnabled;
// If no notifications are enabled, no need to run
if (!$notificationsEnabled) {
@@ -569,6 +583,69 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
}
}
// Mattermost notifications if enabled
if ($mattermostNotificationsEnabled) {
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);
// Build Message Content
$messageContent = "";
if ($user['name']) {
$messageContent = $user['name'] . ", the following subscriptions are up for renewal:\n";
} else {
$messageContent = "The following subscriptions are up for renewal:\n";
}
foreach ($perUser as $subscription) {
$dayText = getDaysText($subscription['days']);
$messageContent .= $subscription['name'] . " for " . $subscription['formatted_price'] . " (" . $dayText . ")\n";
}
// Prepare Mattermost Data
$webhook_url = $mattermost['webhook_url'];
$data = array(
'username' => $mattermost['bot_username'],
'icon_emoji' => $mattermost['bot_icon_emoji'],
'text' => mb_convert_encoding($messageContent, 'UTF-8', 'auto'),
);
$data_string = json_encode($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $webhook_url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json'
),
);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($result === false) {
echo "Error sending Mattermost notifications: " . curl_error($ch) . "<br />";
} else {
$resultData = json_decode($result, true);
if (isset($resultData['code']) && $resultData['code'] == 200) {
echo "Mattermost Notifications sent successfully<br />";
} else {
$errorMsg = isset($resultData['msg']) ? $resultData['msg'] : 'Unknown error';
echo "Mattermost API error: " . $errorMsg . "<br />";
}
}
curl_close($ch);
}
}
// Pushover notifications if enabled
if ($pushoverNotificationsEnabled) {
foreach ($notify as $userId => $perUser) {

View File

@@ -0,0 +1,72 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => 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["webhook_url"]) || $data["webhook_url"] == "") {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$webhook_url = $data["webhook_url"];
$bot_username = $data["bot_username"];
$bot_iconemoji = $data["bot_icon_emoji"];
$query = "SELECT COUNT(*) FROM mattermost_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 mattermost_notifications (enabled, webhook_url, user_id, bot_username, bot_icon_emoji)
VALUES (:enabled, :webhook_url, :userId, :bot_username, :bot_icon_emoji)";
} else {
$query = "UPDATE mattermost_notifications
SET enabled = :enabled, webhook_url = :webhook_url WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':webhook_url', $webhook_url, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$stmt->bindValue(':bot_username', $bot_username, SQLITE3_TEXT);
$stmt->bindValue(':bot_icon_emoji', $bot_iconemoji, SQLITE3_TEXT);
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);
}
}
}
}
?>

View File

@@ -0,0 +1,97 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => 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["webhook_url"]) || $data["webhook_url"] == "" ||
!isset($data["bot_username"]) || $data["bot_username"] == "" ||
!isset($data["bot_icon_emoji"]) || $data["bot_icon_emoji"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
// Set the message parameters
$title = translate('wallos_notification', $i18n);
$message = translate('test_notification', $i18n);
$webhook_url = $data["webhook_url"];
$bot_username = $data["bot_username"];
$bot_icon_emoji = $data["bot_icon_emoji"];
// Validate URL scheme
$parsedUrl = parse_url($webhook_url);
if (
!isset($parsedUrl['scheme']) ||
!in_array(strtolower($parsedUrl['scheme']), ['http', 'https']) ||
!filter_var($webhook_url, FILTER_VALIDATE_URL)
) {
die(json_encode([
"success" => false,
"message" => translate("error", $i18n)
]));
}
$postfields = [
'text' => $message,
];
if (!empty($bot_username)) {
$postfields['username'] = $bot_username;
}
if (!empty($bot_icon_emoji)) {
$postfields['icon_emoji'] = $bot_icon_emoji;
}
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, $webhook_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postfields));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 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 {
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
} else {
die(json_encode([
"success" => false,
"message" => translate("invalid_request_method", $i18n)
]));
}
?>

View File

@@ -196,6 +196,10 @@ $i18n = [
"telegram_chat_id" => "Telegram Chat ID",
"pushplus" => "Pushplus",
"pushplus_token" => "Pushplus Token",
"mattermost" => "Mattermost",
"mattermost_webhook_url" => "WebHook URL",
"mattermost_bot_username" => "Bot Username",
"mattermost_bot_icon_emoji" => "Bot Icon Emoji",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Request Method",

View File

@@ -195,6 +195,10 @@ $i18n = [
"telegram_chat_id" => "ID do Chat Telegram",
"pushplus" => "Pushplus",
"pushplus_token" => "Token do Pushplus",
"mattermost" => "Mattermost",
"mattermost_webhook_url" => "URL do Hook",
"mattermost_bot_username" => "Nome de Utilizador do Bot",
"mattermost_bot_icon_emoji" => "Icon Emoji do Bot",
"webhook" => "Webhook",
"webhook_url" => "URL do Webhook",
"request_method" => "Método de Pedido",

16
migrations/000041.php Executable file
View File

@@ -0,0 +1,16 @@
<?php
// This migration adds a mattermost_notifications table to store Mattermost notification settings.
$tableQuery = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='mattermost_notifications'");
$tableExists = $tableQuery->fetchArray(SQLITE3_ASSOC);
if ($tableExists === false) {
$db->exec("
CREATE TABLE mattermost_notifications (
enabled INTEGER NOT NULL DEFAULT 0,
user_id INTEGER,
webhook_url TEXT DEFAULT '',
bot_username TEXT DEFAULT '',
bot_icon_emoji TEXT DEFAULT ''
);
");
}

View File

@@ -216,6 +216,44 @@ function saveNotificationsPushPlusButton() {
makeFetchCall('endpoints/notifications/savepushplusnotifications.php', data, button);
}
function testNotificationsMattermostButton() {
const button = document.getElementById("testNotificationsMattermost");
button.disabled = true;
const enabled = document.getElementById("mattermostenabled").checked ? 1 : 0;
const webhook_url = document.getElementById("mattermostwebhookurl").value;
const bot_username = document.getElementById("mattermostbotusername").value;
const bot_icon_emoji = document.getElementById("mattermostboticonemoji").value;
const data = {
enabled: enabled,
webhook_url: webhook_url,
bot_username: bot_username,
bot_icon_emoji: bot_icon_emoji
};
makeFetchCall('endpoints/notifications/testmattermostnotifications.php', data, button);
}
function saveNotificationsMattermostButton() {
const button = document.getElementById("saveNotificationsMattermost");
button.disabled = true;
const enabled = document.getElementById("mattermostenabled").checked ? 1 : 0;
const webhook_url = document.getElementById("mattermostwebhookurl").value;
const bot_username = document.getElementById("mattermostbotusername").value;
const bot_icon_emoji = document.getElementById("mattermostboticonemoji").value;
const data = {
enabled: enabled,
webhook_url: webhook_url,
bot_username: bot_username,
bot_icon_emoji: bot_icon_emoji
};
makeFetchCall('endpoints/notifications/savemattermostnotifications.php', data, button);
}
function saveNotificationsGotifyButton() {
const button = document.getElementById("saveNotificationsGotify");
button.disabled = true;

View File

@@ -240,6 +240,28 @@ $userData['currency_symbol'] = $currencies[$main_currency]['symbol'];
$notificationsPushPlus['token'] = "";
}
// Mattermost notifications
$sql = "SELECT * FROM mattermost_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)) {
$notificationsMattermost['enabled'] = $row['enabled'];
$notificationsMattermost['webhook_url'] = $row['webhook_url'];
$notificationsMattermost['bot_username'] = $row['bot_username'];
$notificationsMattermost['bot_icon_emoji'] = $row['bot_icon_emoji'];
$rowCount++;
}
if ($rowCount == 0) {
$notificationsMattermost['enabled'] = 0;
$notificationsMattermost['webhook_url'] = "";
$notificationsMattermost['bot_username'] = "";
$notificationsMattermost['bot_icon_emoji'] = "";
}
// Ntfy notifications
$sql = "SELECT * FROM ntfy_notifications WHERE user_id = :userId LIMIT 1";
$stmt = $db->prepare($sql);
@@ -597,6 +619,44 @@ $userData['currency_symbol'] = $currencies[$main_currency]['symbol'];
</div>
</section>
<section class="account-notifications-section">
<header class="account-notification-section-header" onclick="openNotificationsSettings('mattermost');">
<h3>
<i class="fa-solid fa-bell"></i>
<?= translate('mattermost', $i18n) ?>
</h3>
</header>
<div class="account-notification-section-settings" data-type="mattermost">
<div class="form-group-inline">
<input type="checkbox" id="mattermostenabled" name="mattermostenabled"
<?= $notificationsMattermost['enabled'] ? "checked" : "" ?>>
<label for="mattermostenabled" class="capitalize"><?= translate('enabled', $i18n) ?></label>
</div>
<div class="form-group-inline">
<input type="text" name="mattermostwebhookurl" id="mattermostwebhookurl"
placeholder="<?= translate('mattermost_webhook_url', $i18n) ?>"
value="<?= $notificationsMattermost['webhook_url'] ? $notificationsMattermost['webhook_url'] : '' ?>" />
</div>
<div class="form-group-inline">
<input type="text" name="mattermostbotusername" id="mattermostbotusername"
placeholder="<?= translate('mattermost_bot_username', $i18n) ?>"
value="<?= $notificationsMattermost['bot_username'] ? $notificationsMattermost['bot_username'] : '' ?>" />
</div>
<div class="form-group-inline">
<input type="text" name="mattermostboticonemoji" id="mattermostboticonemoji"
placeholder="<?= translate('mattermost_bot_icon_emoji', $i18n) ?>"
value="<?= $notificationsMattermost['bot_icon_emoji'] ? $notificationsMattermost['bot_icon_emoji'] : '' ?>" />
</div>
<div class="buttons">
<input type="button" class="secondary-button thin mobile-grow"
value="<?= translate('test', $i18n) ?>" id="testNotificationsMattermost"
onClick="testNotificationsMattermostButton()" />
<input type="submit" class="thin mobile-grow" value="<?= translate('save', $i18n) ?>"
id="saveNotificationsMattermost" onClick="saveNotificationsMattermostButton()" />
</div>
</div>
</section>
<section class="account-notifications-section">
<header class="account-notification-section-header" onclick="openNotificationsSettings('ntfy');">
<h3>