Files
blog/usr/themes/HarmonyHues/core/theme-email.php
2026-03-21 17:04:18 +08:00

294 lines
10 KiB
PHP
Executable File

<?php
/**
* Harmony Hues主题 - 邮件通知
*
* @author 星语社长
* @link https://biibii.cn
* @update 2025-2-25 23:16:37
*/
if (! defined('__TYPECHO_ROOT_DIR__')) {
exit;
}
// 引入PHPMailer组件
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
require 'PHPMailer/PHPMailer.php';
require 'PHPMailer/SMTP.php';
require 'PHPMailer/Exception.php';
$options = Helper::options();
// 检查邮件配置是否完整
if (
$options->commentMail === 'on' &&
$options->commentMailHost &&
$options->commentMailPort &&
$options->commentMailFromName &&
$options->commentMailAccount &&
$options->commentMailPassword &&
$options->commentSMTPSecure
) {
// 注册评论完成时的回调
Typecho_Plugin::factory('Widget_Feedback')->finishComment = array('Email', 'send');
}
class Email
{
/**
* 发送评论通知的主入口
*
* @param Widget_Feedback $comment 评论对象
*/
public static function send($comment)
{
try {
$params = array(
'title' => htmlspecialchars($comment->title),
'postlink' => preg_replace('/\/comment-page-\d+#comment-\d+/', '', $comment->permalink),
'permalink' => htmlspecialchars($comment->permalink),
'author' => htmlspecialchars($comment->author),
'text' => self::processCommentText($comment->text),
'mail' => $comment->mail,
);
// 作者评论处理(博主回复)
if ($comment->authorId == $comment->ownerId) {
self::handleAuthorComment($comment, $params);
} else {
self::handleGuestComment($comment, $params);
}
} catch (Exception $e) {
error_log('[Email Plugin] Error: ' . $e->getMessage());
}
}
/**
* 初始化邮件发送器
* 新增SMTP主机连通性检测
*/
private static function initMailer()
{
try {
$options = Helper::options();
$host = $options->commentMailHost;
$port = $options->commentMailPort;
$timeout = 3;
$connection = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (! $connection) {
throw new Exception("SMTP主机不可达: {$host}:{$port} - {$errstr} ({$errno})");
}
fclose($connection);
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->SMTPAuth = true;
$mail->CharSet = 'UTF-8';
$mail->SMTPSecure = $options->commentSMTPSecure;
$mail->Host = $host;
$mail->Port = $port;
$mail->FromName = $options->commentMailFromName;
$mail->Username = $options->commentMailAccount;
$mail->From = $options->commentMailAccount;
$mail->Password = $options->commentMailPassword;
$mail->isHTML(true);
return $mail;
} catch (Exception $e) {
error_log('[Email Plugin] SMTP初始化失败: ' . $e->getMessage());
throw $e;
}
}
/**
* 处理评论内容
* - 转换图片标签为响应式展示
* - 处理表情符号
*
* @param string $content 原始评论内容
* @return string 处理后的HTML内容
*/
private static function processCommentText($content)
{
$content = preg_replace_callback(
'/!\[(.*?)\]\((.*?)\)/',
function ($matches) {
$alt = htmlspecialchars($matches[1]);
$siteUrl = Helper::options()->siteUrl;
$src = rtrim($siteUrl, '/') . '/' . ltrim($matches[2], '/');
return '<img style="display:block;width:auto;height:20rem;" src="' . $src . '" alt="' . $alt . '" />';
},
$content
);
if (function_exists('formatEmoji')) {
$content = formatEmoji($content, false);
}
return $content;
}
/**
* 构建邮件HTML正文
* 新增原评论内容展示区域
*
* @param string $title 邮件标题
* @param string $subtitle 邮件副标题
* @param string $content 当前评论内容
* @param string $originalContent 原评论内容(新增)
* @param string $permalink 评论链接
* @return string 完整的HTML邮件正文
*/
private static function buildEmailBody($title, $subtitle, $content, $originalContent, $permalink)
{
$options = Helper::options();
$siteUrl = rtrim($options->siteUrl, '/');
$favicon = $options->favicon;
if ($favicon && ! parse_url($favicon, PHP_URL_SCHEME)) {
$favicon = $siteUrl . '/' . ltrim($favicon, '/');
}
$originalSection = '';
if (! empty($originalContent)) {
$originalSection = <<<HTML
<div style="margin:15px 0;padding: 10px;border-radius: 3px;border: 1px solid #ededed;border-left: 3px solid #ddd;">
<div style="margin-bottom:5px;color:#666;">您原评论的内容:</div>
<div style="padding: 10px;margin-top: 10px;background-image: radial-gradient(transparent 2px, #eee 1px);background-size: 4px 4px;border-radius: 3px;">
{$originalContent}
</div>
</div>
HTML;
}
return <<<HTML
<div style="margin:5px;border-radius:10px;font-size:14px;color:#333;max-width:100%;box-shadow:0 1px 5px rgba(0,0,0,.15);border:2px solid #fff;overflow:hidden;word-break:break-all;">
<div style="padding:10px;font-size:16px;background-color:#e2ebf0;">{$title}</div>
<div style="background-color: #fff;padding:20px;font-size: 14px;color: #555;">
<div style="margin-bottom:10px;">{$subtitle}</div>
<div style="padding:10px;margin-bottom:10px;background-image:radial-gradient(transparent 2px,#eee 1px);background-size:4px 4px;border-radius:3px;">{$content}</div>
{$originalSection}
<div style="margin-bottom:10px;"><a style="display: inline-block;color: #333;text-decoration: none;background: #f3f3f3;border-radius: 8px;margin-top: 10px;padding: 5px 20px;" href="{$permalink}" target="_blank">查看详情</a></div>
<hr style="margin:15px 0;border:1px dashed #eee;">
<div style="display:flex;color:#999;flex-wrap:wrap;flex-direction:row;justify-content:space-between;align-items:flex-end;">
<span>注:此邮件由系统自动发送,请勿直接回复。<br>若此邮件不是您请求的,请忽略并删除!</span>
<a href="{$siteUrl}" target="_blank"><img style="width: auto;max-width:35px;height:35px;" src="{$favicon}" /></a>
</div>
</div>
</div>
HTML;
}
/**
* 处理博主回复(作者评论)
* - 博主回复其他用户评论时触发
* - 新增原评论内容展示
*/
private static function handleAuthorComment($comment, $params)
{
if ($comment->parent == 0) {
return;
}
$parentComment = self::getParentComment($comment->parent);
if (! $parentComment || $parentComment['mail'] == $comment->mail) {
return;
}
$originalText = self::processCommentText($parentComment['text']);
$mail = self::initMailer();
$mail->addAddress($parentComment['mail']);
$mail->Subject = "您在[{$params['title']}]的评论有了新的回复!";
$mail->Body = self::buildEmailBody(
$mail->Subject,
"{$params['author']}在《<a style=\"color:#0077FF;text-decoration:none;\" href=\"{$params['postlink']}\" target=\"_blank\">{$params['title']}</a>》上回复了您:",
$params['text'],
$originalText,
$params['permalink']
);
$mail->send();
}
/**
* 处理访客评论
* - 区分新评论和回复评论
*/
private static function handleGuestComment($comment, $params)
{
if ($comment->parent == 0) {
self::notifyAuthor($comment, $params);
} else {
self::notifyParent($comment, $params);
}
}
/**
* 通知文章作者有新评论
*/
private static function notifyAuthor($comment, $params)
{
$authorMail = self::getAuthorMail($comment->ownerId);
if (! $authorMail) {
return;
}
$mail = self::initMailer();
$mail->addAddress($authorMail);
$mail->Subject = "您的文章[{$params['title']}]收到一条新的评论!";
$mail->Body = self::buildEmailBody(
$mail->Subject,
"{$params['author']}在您的《<a style=\"color:#0077FF;text-decoration:none;\" href=\"{$params['postlink']}\" target=\"_blank\">{$params['title']}</a>》上发表评论:",
$params['text'],
'',
$params['permalink']
);
$mail->send();
}
/**
* 通知被回复的评论者
* - 新增原评论内容展示
*/
private static function notifyParent($comment, $params)
{
$parentComment = self::getParentComment($comment->parent);
if (! $parentComment || $parentComment['mail'] == $comment->mail) {
return;
}
$originalText = self::processCommentText($parentComment['text']);
$mail = self::initMailer();
$mail->addAddress($parentComment['mail']);
$mail->Subject = "您在[{$params['title']}]的评论有了新的回复!";
$mail->Body = self::buildEmailBody(
$mail->Subject,
"{$params['author']}在《<a style=\"color:#0077FF;text-decoration:none;\" href=\"{$params['postlink']}\" target=\"_blank\">{$params['title']}</a>》上回复了您:",
$params['text'],
$originalText,
$params['permalink']
);
$mail->send();
}
/**
* 获取父评论信息(增强版)
* 现在返回完整评论数据而不仅是邮箱
*
* @param int $parentId 父评论ID
* @return array|null 包含邮件和文本的数组
*/
private static function getParentComment($parentId)
{
$db = Typecho_Db::get();
return $db->fetchRow($db->select('mail', 'text')
->from('table.comments')
->where('coid = ?', $parentId));
}
/**
* 获取文章作者邮箱
*
* @param int $ownerId 用户ID
* @return string|null 邮箱地址
*/
private static function getAuthorMail($ownerId)
{
$db = Typecho_Db::get();
$result = $db->fetchRow($db->select('mail')
->from('table.users')
->where('uid = ?', $ownerId));
return $result['mail'] ?? null;
}
}