受限于typecho博客没有通知,自己写了一个博客有评论就通知的gotify插件,脚本使用之前的python改的。这是效果

下载地址
插件+app+docker部署
https://wonder1999.lanzouu.com/iB6DK32mdwcf
代码
<?php
namespace TypechoPlugin\GotifyNotify;
use Typecho\Plugin\PluginInterface;
use Typecho\Widget\Helper\Form;
use Typecho\Widget\Helper\Form\Element\Text;
use Widget\Options;
use Typecho\Plugin\Exception as PluginException;
use Typecho\Widget\Exception as WidgetException;
use Typecho\Db\Exception as DbException;
use Utils;
if (!defined('__TYPECHO_ROOT_DIR__')) {
exit;
}
/**
* 当用户评论时通过 Gotify 发送通知
*
* @package GotifyNotify
* @author 白荼
* @version 1.3.0
* @link https://gotify.net
*/
class Plugin implements PluginInterface
{
/**
* 激活插件方法
*
* @access public
* @return void
* @throws PluginException
*/
public static function activate()
{
\Typecho\Plugin::factory('Widget_Feedback')->finishComment = __CLASS__ . '::requestService';
\Typecho\Plugin::factory('Widget_Comments_Edit')->finishComment = __CLASS__ . '::requestService';
\Typecho\Plugin::factory('Widget_Service')->sendGotify = __CLASS__ . '::sendGotify';
}
/**
* 禁用插件方法
*
* @access public
* @return void
* @throws PluginException
*/
public static function deactivate()
{
// 通常不需要显式移除
}
/**
* 获取插件配置面板
*
* @param Form $form 配置面板
* @access public
* @return void
*/
public static function config(Form $form)
{
// Gotify 服务器地址
$serverUrl = new Text('serverUrl', NULL, '', _t('Gotify 服务器地址'), _t('例如: http://your-gotify-server.com'));
$form->addInput($serverUrl->addRule('required', _t('Gotify 服务器地址不能为空')));
// 应用 Token
$appToken = new Text('appToken', NULL, '', _t('应用 Token'), _t('在 Gotify 中创建应用后获得的 Token'));
$form->addInput($appToken->addRule('required', _t('应用 Token 不能为空')));
// 通知标题
$title = new Text('title', NULL, '博客有新评论', _t('通知标题'), _t('收到新评论时的推送标题'));
$form->addInput($title);
// 消息优先级 (Priority) - 使用 Text 输入框
$priority = new Text('priority', NULL, '1', _t('消息优先级'), _t('设置 Gotify 消息的优先级 (自己在Gorify定义的Priority)'));
$form->addInput($priority);
}
/**
* 个人用户的配置面板
*
* @param Form $form
* @access public
* @return void
*/
public static function personalConfig(Form $form)
{
// 个人配置通常为空
}
/**
* 评论通知回调 - 触发异步服务
* 这个方法在评论完成后被调用
*
* @access public
* @param $comment (Widget_Comments_Edit 或 Widget_Feedback 的实例)
* @return void
* @throws PluginException
*/
public static function requestService($comment)
{
// 检查评论对象是否有效
if (!isset($comment->coid) || !$comment->have()) {
error_log("GotifyNotify: Invalid comment object received in requestService.");
return;
}
$coid = $comment->coid;
$options = Options::alloc()->plugin('GotifyNotify');
// 检查必要配置
if (empty($options->serverUrl) || empty($options->appToken)) {
error_log("GotifyNotify: Missing server URL or app token, skipping notification for comment ID {$coid}.");
return;
}
// 调用 Widget_Service 中注册的异步方法
// 将评论 ID 传递给异步服务
try {
error_log("GotifyNotify: Calling async service for comment ID {$coid}.");
Utils\Helper::requestService('sendGotify', $coid);
} catch (\Exception $e) {
error_log("GotifyNotify: Failed to call async service for comment ID {$coid}. Error: " . $e->getMessage());
}
}
/**
* 异步发送 Gotify 通知
* 这个方法由 Widget_Service 调用
*
* @param integer $coid 评论ID
* @access public
* @return void
* @throws WidgetException
* @throws DbException
*/
public static function sendGotify(int $coid)
{
error_log("GotifyNotify Service: Starting to process comment ID {$coid}.");
// 获取插件配置
$options = Options::alloc()->plugin('GotifyNotify');
// 移除了对 $options->enable 的检查,插件默认启用
// 重新获取评论对象
try {
$commentWidget = Utils\Helper::widgetById('comments', $coid);
} catch (WidgetException $e) {
error_log("GotifyNotify Service: Failed to get comment widget for ID {$coid}. Error: " . $e->getMessage());
return;
}
if (!$commentWidget->have()) {
error_log("GotifyNotify Service: Comment widget is empty for ID {$coid}.");
return;
}
// 检查必要配置
if (empty($options->serverUrl) || empty($options->appToken)) {
error_log("GotifyNotify Service: Missing configuration for comment ID {$coid}.");
return;
}
try {
// 构造通知内容
$title = $options->title ?: '博客有新评论';
$message = self::buildMessage($commentWidget); // 传入 widget 对象
// 获取优先级设置
$priority = $options->priority ?? '1'; // 默认优先级为 1
error_log("GotifyNotify Service: Prepared message for comment ID {$coid}. Title: {$title}, Priority: {$priority}");
// 发送通知,传入优先级
self::sendGotifyMessage($options->serverUrl, $options->appToken, $title, $message, $priority);
error_log("GotifyNotify Service: Successfully sent notification for comment ID {$coid}.");
} catch (\Exception $e) {
// 记录详细错误信息
error_log("GotifyNotify Service: Failed to send notification for comment ID {$coid}. Error: " . $e->getMessage());
error_log("GotifyNotify Service: Stack trace: " . $e->getTraceAsString());
}
}
/**
* 构造通知消息内容
*
* @param \Widget\Base\Comments $comment 评论对象
* @return string 消息内容
*/
private static function buildMessage($comment)
{
$content = '';
if (is_object($comment) && $comment->have()) {
// 从评论对象获取信息
$author = $comment->author ?? '匿名用户';
$mail = $comment->mail ?? '';
$url = $comment->url ?? '';
$text = $comment->text ?? '';
// $ip = $comment->ip ?? ''; // Widget\Base\Comments 通常不直接暴露 IP
$content = "作者: {$author}\n";
if (!empty($mail)) {
$content .= "邮箱: {$mail}\n";
}
if (!empty($url)) {
$content .= "网站: {$url}\n";
}
// if (!empty($ip)) {
// $content .= "IP: {$ip}\n";
// }
$content .= "内容: {$text}\n";
$content .= "时间: " . date('Y-m-d H:i:s', $comment->created) . "\n"; // 使用评论创建时间
$content .= "文章: " . $comment->title . "\n"; // 文章标题
$content .= "链接: " . $comment->permalink . "\n"; // 评论链接
} else {
// 兼容其他情况
$content = "收到新评论,请登录后台查看 (评论ID: " . ($comment->coid ?? 'unknown') . ")";
}
return $content;
}
/**
* 发送 Gotify 消息
*
* @param string $serverUrl Gotify 服务器地址
* @param string $appToken 应用 Token
* @param string $title 消息标题
* @param string $message 消息内容
* @param string $priority 消息优先级
* @throws \Exception
*/
private static function sendGotifyMessage($serverUrl, $appToken, $title, $message, $priority = '1')
{
// 清理 URL
$serverUrl = rtrim($serverUrl, '/');
// 构造请求 URL (使用查询参数传递 token)
$url = $serverUrl . '/message';
// 准备表单数据,包含 priority
$data = [
'title' => $title,
'message' => $message,
'priority' => $priority // 使用传入的优先级
];
// 准备查询参数
$params = ['token' => $appToken];
$fullUrl = $url . '?' . http_build_query($params);
error_log("GotifyNotify: Sending request to {$fullUrl}");
error_log("GotifyNotify: POST data: " . print_r($data, true));
// 使用 cURL 发送请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $fullUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // 发送表单数据
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 根据你的服务器 SSL 配置调整
curl_setopt($ch, CURLOPT_USERAGENT, 'Typecho GotifyNotify Plugin/1.3.0');
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
error_log("GotifyNotify: cURL Response Code: {$httpCode}");
error_log("GotifyNotify: cURL Response Body: {$response}");
error_log("GotifyNotify: cURL Error (if any): {$error}");
// 检查响应
if ($response === false) {
throw new \Exception('cURL error: ' . $error);
}
if ($httpCode >= 200 && $httpCode < 300) {
// 成功
error_log("GotifyNotify: Message sent successfully.");
// 可以进一步检查返回的 JSON
$result = json_decode($response, true);
if (!$result) {
error_log("GotifyNotify: Warning - Could not decode JSON response, but HTTP status was OK.");
}
return; // 成功返回
} else {
// 失败
throw new \Exception('HTTP error: ' . $httpCode . ', response: ' . $response);
}
}
}

2 条评论
针不戳,汇总到了`typecho.work`
感谢大佬!