add serverchan notification (#951)

* add serverchan notification

* Update endpoints/cronjobs/sendnotifications.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update endpoints/notifications/testserverchannotifications.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Miguel Ribeiro <k.d.mitnick@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Easy
2025-12-20 23:06:49 +08:00
committed by GitHub
parent 671763e78b
commit 279ddf61ce
10 changed files with 301 additions and 2 deletions

View File

@@ -174,6 +174,20 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" || $_SERVER["REQUEST_METHOD"] === "GET
$notification_settings['webhook_notifications'] = $webhook_notifications;
}
// Serverchan notifications
$query = "SELECT * FROM serverchan_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId);
$result = $stmt->execute();
$serverchan_notifications = $result->fetchArray(SQLITE3_ASSOC);
if ($serverchan_notifications) {
unset($serverchan_notifications['user_id']);
if (isset($serverchan_notifications['sendkey'])) {
$serverchan_notifications['sendkey'] = "********";
}
$notification_settings['serverchan_notifications'] = $serverchan_notifications;
}
$response = [
"success" => true,
"title" => "notification_settings",

View File

@@ -65,6 +65,7 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$mattermostNotificationsEnabled = false;
$discordNotificationsEnabled = false;
$ntfyNotificationsEnabled = false;
$serverchanNotificationsEnabled = 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";
@@ -196,9 +197,20 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$webhook['ignore_ssl'] = $row["ignore_ssl"];
}
// Check if Serverchan notifications are enabled and get the settings
$query = "SELECT * FROM serverchan_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$serverchanNotificationsEnabled = $row['enabled'];
$serverchan['sendkey'] = $row['sendkey'];
}
$notificationsEnabled = $emailNotificationsEnabled || $gotifyNotificationsEnabled || $telegramNotificationsEnabled ||
$webhookNotificationsEnabled || $pushoverNotificationsEnabled || $discordNotificationsEnabled ||$pushplusNotificationsEnabled||
$mattermostNotificationsEnabled || $ntfyNotificationsEnabled;
$webhookNotificationsEnabled || $pushoverNotificationsEnabled || $discordNotificationsEnabled || $pushplusNotificationsEnabled ||
$mattermostNotificationsEnabled || $ntfyNotificationsEnabled || $serverchanNotificationsEnabled;
// If no notifications are enabled, no need to run
if (!$notificationsEnabled) {
@@ -807,6 +819,62 @@ while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
}
}
// Serverchan notifications if enabled
if ($serverchanNotificationsEnabled) {
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);
$title = 'Wallos Notification';
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 = getDaysText($subscription['days']);
$message .= $subscription['name'] . " for " . $subscription['formatted_price'] . " (" . $dayText . ")\n";
}
// Build Serverchan request
$postdata = http_build_query(array('text' => $title, 'desp' => $message));
$sendkey = $serverchan['sendkey'];
if (strpos($sendkey, 'sctp') === 0) {
preg_match('/^sctp(\d+)t/', $sendkey, $matches);
$num = $matches[1] ?? '';
$url = "https://{$num}.push.ft07.com/send/{$sendkey}.send";
} else {
$url = "https://sctapi.ftqq.com/{$sendkey}.send";
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false || $httpCode >= 400) {
$errorMessage = $response === false ? curl_error($ch) : $httpCode;
curl_close($ch);
echo "Error sending Serverchan notifications: " . $errorMessage . "<br />";
} else {
curl_close($ch);
echo "Serverchan Notifications sent<br />";
}
}
}
} else {
if (php_sapi_name() !== 'cli') {
echo "Nothing to notify.<br />";

View File

@@ -0,0 +1,59 @@
<?php
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/validate_endpoint.php';
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (!isset($data["sendkey"]) || $data["sendkey"] == "") {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$sendkey = $data["sendkey"];
$query = "SELECT COUNT(*) FROM serverchan_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 serverchan_notifications (enabled, sendkey, user_id)
VALUES (:enabled, :sendkey, :userId)";
} else {
$query = "UPDATE serverchan_notifications
SET enabled = :enabled, sendkey = :sendkey WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':sendkey', $sendkey, 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);
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/validate_endpoint.php';
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
$enabled = $data["enabled"] ?? 0;
$sendkey = $data["sendkey"] ?? "";
if (!$enabled || $sendkey === "") {
echo json_encode([
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
]);
exit;
}
function sc_send($text, $desp = '', $key = '') {
$postdata = http_build_query(array('text' => $text, 'desp' => $desp));
if (strpos($key, 'sctp') === 0) {
preg_match('/^sctp(\d+)t/', $key, $matches);
$num = $matches[1] ?? '';
$url = "https://{$num}.push.ft07.com/send/{$key}.send";
} else {
$url = "https://sctapi.ftqq.com/{$key}.send";
}
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = @file_get_contents($url, false, $context);
return $result !== false ? $result : '';
}
$title = 'Wallos Notification Test';
$body = 'This is a test notification from Wallos via Serverchan.';
$result = sc_send($title, $body, $sendkey);
$info = json_decode($result, true);
$code = (is_array($info) && array_key_exists('code', $info)) ? $info['code'] : null;
if ($code === 0) {
echo json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]);
} else {
echo json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]);
}

View File

@@ -196,6 +196,8 @@ $i18n = [
"telegram_chat_id" => "Telegram Chat ID",
"pushplus" => "Pushplus",
"pushplus_token" => "Pushplus Token",
"serverchan" => "Serverchan",
"serverchan_sendkey" => "Serverchan SendKey",
"mattermost" => "Mattermost",
"mattermost_webhook_url" => "WebHook URL",
"mattermost_bot_username" => "Bot Username",

View File

@@ -204,6 +204,8 @@ $i18n = [
"telegram_chat_id" => "Telegram 聊天 ID",
"pushplus" => "Pushplus",
"pushplus_token" => "消息令牌或者是用户令牌",
"serverchan" => "Server酱",
"serverchan_sendkey" => "SendKey",
"mattermost" => "Mattermost",
"mattermost_webhook_url" => "Mattermost Webhook URL",
"mattermost_bot_username" => "Mattermost Bot 用户名",

View File

@@ -196,6 +196,8 @@ $i18n = [
"telegram_chat_id" => "Telegram 聊天 ID",
"pushplus" => "PushPlus",
"pushplus_token" => "消息令牌或者是用户令牌",
"serverchan" => "Server醬",
"serverchan_sendkey" => "SendKey",
"mattermost" => "Mattermost",
"mattermost_webhook_url" => "Mattermost Webhook 網址",
"mattermost_bot_username" => "Mattermost Bot 使用者名稱",

19
migrations/000042.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
/*
* This migration adds a table to store Serverchan notification settings
*/
$tableQuery = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='serverchan_notifications'");
$tableExists = $tableQuery->fetchArray(SQLITE3_ASSOC);
if (!$tableExists) {
$db->exec('CREATE TABLE serverchan_notifications (
enabled BOOLEAN DEFAULT 0,
sendkey TEXT DEFAULT "",
user_id INTEGER,
FOREIGN KEY (user_id) REFERENCES user(id)
)');
}
?>

View File

@@ -404,4 +404,34 @@ function saveNotificationsNtfyButton() {
};
makeFetchCall('endpoints/notifications/saventfynotifications.php', data, button);
}
function testNotificationsServerchanButton() {
const button = document.getElementById("testNotificationsServerchan");
button.disabled = true;
const enabled = document.getElementById("serverchanenabled").checked ? 1 : 0;
const sendkey = document.getElementById("serverchansendkey").value;
const data = {
enabled: enabled,
sendkey: sendkey
};
makeFetchCall('endpoints/notifications/testserverchannotifications.php', data, button);
}
function saveNotificationsServerchanButton() {
const button = document.getElementById("saveNotificationsServerchan");
button.disabled = true;
const enabled = document.getElementById("serverchanenabled").checked ? 1 : 0;
const sendkey = document.getElementById("serverchansendkey").value;
const data = {
enabled: enabled,
sendkey: sendkey
};
makeFetchCall('endpoints/notifications/saveserverchannotifications.php', data, button);
}

View File

@@ -264,6 +264,24 @@ $userData['currency_symbol'] = $currencies[$main_currency]['symbol'];
$notificationsMattermost['bot_icon_emoji'] = "";
}
// Serverchan notifications
$sql = "SELECT * FROM serverchan_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)) {
$notificationsServerchan['enabled'] = $row['enabled'];
$notificationsServerchan['sendkey'] = $row['sendkey'];
$rowCount++;
}
if ($rowCount == 0) {
$notificationsServerchan['enabled'] = 0;
$notificationsServerchan['sendkey'] = "";
}
// Ntfy notifications
$sql = "SELECT * FROM ntfy_notifications WHERE user_id = :userId LIMIT 1";
$stmt = $db->prepare($sql);
@@ -661,6 +679,34 @@ $userData['currency_symbol'] = $currencies[$main_currency]['symbol'];
</div>
</section>
<section class="account-notifications-section">
<header class="account-notification-section-header" onclick="openNotificationsSettings('serverchan');">
<h3>
<i class="fa-solid fa-paper-plane"></i>
<?= translate('serverchan', $i18n) ?>
</h3>
</header>
<div class="account-notification-section-settings" data-type="serverchan">
<div class="form-group-inline">
<input type="checkbox" id="serverchanenabled" name="serverchanenabled"
<?= $notificationsServerchan['enabled'] ? "checked" : "" ?>>
<label for="serverchanenabled" class="capitalize"><?= translate('enabled', $i18n) ?></label>
</div>
<div class="form-group-inline">
<input type="text" name="serverchansendkey" id="serverchansendkey" autocomplete="off"
placeholder="<?= translate('serverchan_sendkey', $i18n) ?>"
value="<?= $notificationsServerchan['sendkey'] ? $notificationsServerchan['sendkey'] : '' ?>" />
</div>
<div class="buttons">
<input type="button" class="secondary-button thin mobile-grow"
value="<?= translate('test', $i18n) ?>" id="testNotificationsServerchan"
onClick="testNotificationsServerchanButton()" />
<input type="submit" class="thin mobile-grow" value="<?= translate('save', $i18n) ?>"
id="saveNotificationsServerchan" onClick="saveNotificationsServerchanButton()" />
</div>
</div>
</section>
<section class="account-notifications-section">
<header class="account-notification-section-header" onclick="openNotificationsSettings('ntfy');">
<h3>