https://github.com/jrotty/CommentNotifier
* @package CommentNotifier
* @author 泽泽社长
* @version 1.2.9
* @link http://blog.zezeshe.com
*/
require dirname(__FILE__) . '/PHPMailer/PHPMailer.php';
require dirname(__FILE__) . '/PHPMailer/SMTP.php';
require dirname(__FILE__) . '/PHPMailer/Exception.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class Plugin implements PluginInterface
{
/** @var string 控制菜单链接 */
public static $panel = 'CommentNotifier/console.php';
/**
* 激活插件方法,如果激活失败,直接抛出异常
*
* @access public
* @return string
*/
public static function activate()
{
\Typecho\Plugin::factory('Widget_Feedback')->finishComment = __CLASS__ . '::resendMail'; // 前台提交评论完成接口
\Typecho\Plugin::factory('Widget_Comments_Edit')->finishComment = __CLASS__ . '::resendMail'; // 后台操作评论完成接口
\Typecho\Plugin::factory('Widget_Comments_Edit')->mark = __CLASS__ . '::mark'; // 后台标记评论状态完成接口
\Typecho\Plugin::factory('Widget_Service')->refinishComment = __CLASS__ . '::refinishComment';//异步接口
Helper::addPanel(1, self::$panel, '评论邮件提醒', '评论邮件提醒控制台', 'administrator');
return _t('请配置邮箱SMTP选项!');
}
/**
* 禁用插件方法,如果禁用失败,直接抛出异常
*
* @static
* @access public
* @return void
*/
public static function deactivate()
{
Helper::removePanel(1, self::$panel);
}
/**
* 获取插件配置面板
*
* @access public
* @param Form $form 配置面板
* @return void
*/
public static function config(Form $form)
{
// 记录log
$log = new Checkbox('log', ['log' => _t('记录日志')], 'log', _t('记录日志'), _t('启用后将当前目录生成一个log.txt 注:目录需有写入权限'));
$form->addInput($log);
$yibu = new Radio('yibu', array('0' => _t('不启用'), '1' => _t('启用'),), '0', _t('异步提交'), _t('注意:如你博客使用ajax提交评论请不要开启此项否则可能导致邮件无法发送!'));
$form->addInput($yibu);
$layout = new Layout();
$layout->html(_t('
邮件服务配置:
'));
$form->addItem($layout);
// SMTP服务地址
$STMPHost = new Text('STMPHost', NULL, 'smtp.qq.com', _t('SMTP服务器地址'), _t('如:smtp.163.com,smtp.gmail.com,smtp.exmail.qq.com,smtp.sohu.com,smtp.sina.com'));
$form->addInput($STMPHost->addRule('required', _t('SMTP服务器地址必填!')));
// SMTP用户名
$SMTPUserName = new Text('SMTPUserName', NULL, NULL, _t('SMTP登录用户'), _t('SMTP登录用户名,一般为邮箱地址'));
$form->addInput($SMTPUserName->addRule('required', _t('SMTP登录用户必填!')));
// 发件邮箱
$from = new Text('from', NULL, NULL, _t('SMTP邮箱地址'), _t('请填写用于发送邮件的邮箱,一般与SMTP登录用户名一致'));
$form->addInput($from->addRule('required', _t('发件邮箱必填!')));
// SMTP密码
$description = _t('一般为邮箱登录密码, 有特殊如: QQ邮箱有独立的SMTP密码. 可参考: ');
$description .= 'QQ邮箱 ';
$description .= '阿里邮箱 ';
$description .= 'Outlook邮箱 ';
$description .= '新浪邮箱 ';
$SMTPPassword = new Text('SMTPPassword', NULL, NULL, _t('SMTP登录密码'), $description);
$form->addInput($SMTPPassword->addRule('required', _t('SMTP登录密码必填!')));
// 服务器安全模式
$SMTPSecure = new Radio('SMTPSecure', array('' => _t('无安全加密'), 'ssl' => _t('SSL加密'), 'tls' => _t('TLS加密')), '', _t('SMTP加密模式'));
$form->addInput($SMTPSecure);
// SMTP server port
$SMTPPort = new Text('SMTPPort', NULL, '25', _t('SMTP服务端口'), _t('默认25 SSL为465 TLS为587'));
$form->addInput($SMTPPort);
$layout = new Layout();
$layout->html(_t('邮件信息配置:
'));
$form->addItem($layout);
// 发件人姓名
$fromName = new Text('fromName', NULL, NULL, _t('发件人姓名'), _t('发件人姓名'));
$form->addInput($fromName->addRule('required', _t('发件人姓名必填!')));
// 收件邮箱
$adminfrom = new Text('adminfrom', NULL, NULL, _t('站长收件邮箱'), _t('遇到待审核评论或文章作者邮箱为空时,评论提醒会发送到此邮箱地址!'));
$form->addInput($adminfrom->addRule('required', _t('收件邮箱必填!')));
// 模板
$template = new Text('template', NULL, 'default', _t('邮件模板选择'), _t('该项请不要在插件设置里填写,请到邮件模板列表页面选择模板启动!'));
$template->setAttribute('class', 'hidden');
$form->addInput($template);
}
/**
* 个人用户的配置面板
*
* @access public
* @param Form $form
* @return void
*/
public static function personalConfig(Form $form)
{
}
/**
* 插件实现方法
*
* @access public
* @return void
*/
public static function render()
{
}
/**
* @param $comment
* @return array
* @throws Typecho_Db_Exception
* 获取上级评论人
*/
public static function getParent($comment): array
{
$recipients = [];
$parent = Helper::widgetById('comments', $comment->parent);
$recipients = [
'name' => $parent->author,
'mail' => $parent->mail,
];
return $recipients;
}
/**
* @param $comment
* @return array
* @throws Typecho_Db_Exception
* 获取文章作者邮箱
*/
public static function getAuthor($comment): array
{
$CommentNotifier = Options::alloc()->plugin('CommentNotifier');
$recipients = [];
$db = Db::get();
$ae = $db->fetchRow($db->select()->from('table.users')->where('table.users.uid=?', $comment->ownerId));
if (empty($ae['mail'])) {
$ae['screenName'] = $CommentNotifier->fromName;
$ae['mail'] = $CommentNotifier->adminfrom;
}
$recipients = [
'name' => $ae['screenName'],
'mail' => $ae['mail'],
];
// 查询
return $recipients;
}
/**
* @param $comment
* @param Widget_Comments_Edit $edit
* @param $status
* @throws Typecho_Db_Exception
* @throws Typecho_Plugin_Exception
* 在后台标记评论状态时的回调
*/
public static function mark($comment, $edit, $status)
{
$recipients = [];
$CommentNotifier = Options::alloc()->plugin('CommentNotifier');
$from = $CommentNotifier->adminfrom; // 站长邮箱
// 在后台标记评论状态为[approved 审核通过]时, 发信给上级评论人或作者
if ($status == 'approved') {
$type = 0;
// 如果有上级
if ($edit->parent > 0) {
$recipients[] = self::getParent($edit);//获取上级评论信息
$type = 1;
} else {
$recipients[] = self::getAuthor($edit);//获取作者信息
}
// 如果自己回复自己的评论, 不做任何操作
if ($recipients[0]['mail'] == $edit->mail) {
return;
}
// 如果上级是博主, 不做任何操作
if ($recipients[0]['mail'] == $from) {
return;
}
//邮箱为空时就不发邮件
if (empty($recipients[0]['mail'])) {
return;
}
self::sendMail($edit, $recipients, $type);
}
}
/**
* @param Widget_Comments_Edit|Widget_Feedback $comment
* @throws Typecho_Db_Exception
* @throws Typecho_Plugin_Exception
* 评论/回复时的回调
*/
public static function refinishComment($comment)
{
$CommentNotifier = Options::alloc()->plugin('CommentNotifier');
$from = $CommentNotifier->adminfrom; // 站长邮箱
$fromName = $CommentNotifier->fromName; // 发件人
$recipients = [];
// 审核通过
if ($comment->status == 'approved') {
$type = 0;//0为无父级评论
// 不需要发信给博主
if ($comment->authorId != $comment->ownerId && $comment->mail != $from) {
$recipients[] = self::getAuthor($comment);//收到新评论后发送给文章作者
}
// 如果有上级
if ($comment->parent) {
$type = 1;//1为有父级评论
// 查询上级评论人
$parent = self::getParent($comment);//获取上级评论者邮箱
// 如果上级是博主和自己回复自己, 不需要发信
if ($parent['mail'] != $from && $parent['mail'] != $comment->mail) {
$recipients[] = $parent;
}
}
self::sendMail($comment, $recipients, $type);
} else {
// 如果所有评论必须经过审核, 通知博主审核评论
$recipients[] = ['name' => $fromName, 'mail' => $from];
self::sendMail($comment, $recipients, 2);//2为待审核评论
}
}
/**
* @param Widget_Comments_Edit|Widget_Feedback $comment
* @param array $recipients
* @param $type
*/
private static function sendMail($comment, array $recipients, $type)
{
if (empty($recipients)) return; // 没有收信人
try {
// 获取系统配置选项
$options = Options::alloc();
// 获取插件配置
$CommentNotifier = $options->plugin('CommentNotifier');
$from = $CommentNotifier->from; // 发件邮箱
$fromName = $CommentNotifier->fromName; // 发件人
// Server settings
$mail = new PHPMailer(false);
$mail->CharSet = PHPMailer::CHARSET_UTF8;
$mail->Encoding = PHPMailer::ENCODING_BASE64;
$mail->isSMTP();
$mail->Host = $CommentNotifier->STMPHost; // SMTP 服务地址
$mail->SMTPAuth = true; // 开启认证
$mail->Username = $CommentNotifier->SMTPUserName; // SMTP 用户名
$mail->Password = $CommentNotifier->SMTPPassword; // SMTP 密码
$mail->SMTPSecure = $CommentNotifier->SMTPSecure; // SMTP 加密类型 'ssl' or 'tls'.
$mail->Port = $CommentNotifier->SMTPPort; // SMTP 端口
$mail->setFrom($from, $fromName);
foreach ($recipients as $recipient) {
$mail->addAddress($recipient['mail'], $recipient['name']); // 发件人
}
if ($type == 1) {
$mail->Subject = '你在[' . $comment->title . ']的评论有了新的回复';
} elseif ($type == 2) {
$mail->Subject = '文章《' . $comment->title . '》有条待审评论';
} else {
$mail->Subject = '你的《' . $comment->title . '》文章有了新的评论';
}
$mail->isHTML(); // 邮件为HTML格式
// 邮件内容
$content = self::mailBody($comment, $options, $type);
$mail->Body = $content;
$mail->send();
// 记录日志
if ($CommentNotifier->log) {
$at = date('Y-m-d H:i:s');
if ($mail->isError()) {
$data = $at . ' ' . $mail->ErrorInfo; // 记录发信失败的日志
} else { // 记录发信成功的日志
$recipientNames = $recipientMails = '';
foreach ($recipients as $recipient) {
$recipientNames .= $recipient['name'] . ', ';
$recipientMails .= $recipient['mail'] . ', ';
}
$data = PHP_EOL . $at . ' 发送成功! ';
$data .= ' 发件人:' . $fromName;
$data .= ' 发件邮箱:' . $from;
$data .= ' 接收人:' . $recipientNames;
$data .= ' 接收邮箱:' . $recipientMails . PHP_EOL;
}
$fileName = dirname(__FILE__) . '/log.txt';
file_put_contents($fileName, $data, FILE_APPEND);
}
} catch (Exception $e) {
$fileName = dirname(__FILE__) . '/log.txt';
$str = "\nerror time: " . date('Y-m-d H:i:s') . "\n";
file_put_contents($fileName, $str, FILE_APPEND);
file_put_contents($fileName, $e, FILE_APPEND);
}
}
/**
* @param $comment
* @param $options
* @param $type
* @return string
* 很朴素的邮件风格
*/
private static function mailBody($comment, $options, $type): string
{
$commentAt = new Date($comment->created);
$commentAt = $commentAt->format('Y-m-d H:i:s');
$commentText = htmlspecialchars($comment->text);
$html = 'owner';
if ($type == 1) {
$html = 'guest';
} elseif ($type == 2) {
$html = 'notice';
}
$Pmail = '';
$Pname = '';
$Ptext = '';
if ($comment->parent) {
$parent = Helper::widgetById('comments', $comment->parent);
$Pmail = $parent->mail;
$Pname = $parent->author;
$Ptext = $parent->text;
}
$content = self::getTemplate($html);
$template = Options::alloc()->plugin('CommentNotifier')->template;
$search = array(
'{title}',//文章标题
'{time}',//评论发出时间
'{commentText}',//评论内容
'{author}',//评论人昵称
'{mail}',//评论者邮箱
'{permalink}',//评论楼层链接
'{siteUrl}',//网站地址
'{siteTitle}',//网站标题
'{Pname}',//父级评论昵称
'{Ptext}',//父级评论内容
'{Pmail}',//父级评论邮箱
'{url}',//当前模板文件夹路径
);
$replace = array(
$comment->title,
$commentAt,
$commentText,
$comment->author,
$comment->mail,
$comment->permalink,
$options->siteUrl,
$options->title,
$Pname,
$Ptext,
$Pmail,
Options::alloc()->pluginUrl . '/CommentNotifier/template/' . $template,
);
return str_replace($search, $replace, $content);
}
/**
* 获取评论模板
*
* @param template owner 为博主 guest 为访客
* @return false|string
*/
private static function getTemplate($template = 'owner')
{
$template .= '.html';
$templateDir = self::configStr('template', 'default');
$filePath = dirname(__FILE__) . '/template/' . $templateDir . '/' . $template;
if (!file_exists($filePath)) {//如果模板文件缺失就调用根目录下的default文件夹中用于垫底的模板
$filePath = dirname(__FILE__) . 'template/default/' . $template;
}
return file_get_contents($filePath);
}
public static function resendMail($comment)
{
if(Options::alloc()->plugin('CommentNotifier')->yibu==1){
Helper::requestService('refinishComment', $comment);
}else{
self::refinishComment($comment);
}
}
/**
* 从 Widget_Options 对象获取 Typecho 选项值(文本型)
* @param string $key 选项 Key
* @param mixed $default 默认值
* @param string $method 测空值方法
* @return string
*/
public static function configStr(string $key, $default = '', string $method = 'empty'): string
{
$value = Helper::options()->plugin('CommentNotifier')->$key;
if ($method === 'empty') {
return empty($value) ? $default : $value;
} else {
return call_user_func($method, $value) ? $default : $value;
}
}
}