Просмотр исходного кода

Add more notification channels

兔姬桑 4 лет назад
Родитель
Сommit
3f05e3d79d

+ 41 - 0
app/Channels/PushPlusChannel.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Channels;
+
+use Helpers;
+use Http;
+use Illuminate\Notifications\Notification;
+use Log;
+
+class PushPlusChannel
+{
+    public function send($notifiable, Notification $notification)
+    {
+        $message = $notification->toCustom($notifiable);
+
+        $response = Http::timeout(15)->post('https://www.pushplus.plus/send', [
+            'token'    => sysConfig('pushplus_token'),
+            'title'    => $message['title'],
+            'content'  => $message['content'],
+            'template' => 'markdown',
+        ]);
+
+        // 发送成功
+        if ($response->ok()) {
+            $ret = $response->json();
+            if ($ret['code'] === 200) {
+                Helpers::addNotificationLog($message['title'], $message['content'], 7);
+
+                return $ret;
+            }
+            // 发送失败
+            Helpers::addNotificationLog($message['title'], $message['content'], 7, 'admin', -1, $ret ? $ret['msg'] : '未知');
+
+            return false;
+        }
+        // 发送错误
+        Log::error('PushPlus消息推送异常:'.var_export($response, true));
+
+        return false;
+    }
+}

+ 1 - 1
app/Channels/ServerChanChannel.php

@@ -24,7 +24,7 @@ class ServerChanChannel
         // 一天仅可发送不超过500条
         if (Cache::get($cacheKey) < 500) {
             $response = Http::timeout(15)
-                ->get('https://sc.ftqq.com/'.sysConfig('server_chan_key').'.send?text='.$message['title'].'&desp='.urlencode($message['content']));
+                ->get('https://sctapi.ftqq.com/'.sysConfig('server_chan_key').'.send?title='.$message['title'].'&desp='.urlencode($message['content']));
         } else {
             Log::error('ServerChan消息推送异常:今日500条限额已耗尽!');
 

+ 36 - 0
app/Channels/TgChatChannel.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Channels;
+
+use Helpers;
+use Http;
+use Illuminate\Notifications\Notification;
+use Log;
+
+class TgChatChannel
+{
+    public function send($notifiable, Notification $notification)
+    {
+        $message = $notification->toCustom($notifiable);
+
+        $response = Http::timeout(15)->get('https://tgbot-red.vercel.app/api?token='.sysConfig('tg_chat_token').'&message='.$message['content']);
+
+        // 发送成功
+        if ($response->ok()) {
+            $ret = $response->json();
+            if ($ret['code'] === 200) {
+                Helpers::addNotificationLog($message['title'], $message['content'], 6);
+
+                return $ret;
+            }
+            // 发送失败
+            Helpers::addNotificationLog($message['title'], $message['content'], 6, 'admin', -1, $ret ? $ret['message'] : '未知');
+
+            return false;
+        }
+        // 发送错误
+        Log::error('TG酱消息推送异常:'.var_export($response, true));
+
+        return false;
+    }
+}

+ 62 - 0
app/Channels/WeChatChannel.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Channels;
+
+use Cache;
+use Helpers;
+use Http;
+use Illuminate\Notifications\Notification;
+use Log;
+
+class WeChatChannel
+{
+    private $access_token;
+
+    public function __construct()
+    {
+        if (Cache::has('wechat_access_token')) {
+            $this->access_token = Cache::get('wechat_access_token');
+        } else {
+            // https://work.weixin.qq.com/api/doc/90000/90135/91039
+            $response = Http::get('https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid='.sysConfig('wechat_cid').'&corpsecret='.sysConfig('wechat_secret'));
+            if ($response->ok() && isset($response->json()['access_token'])) {
+                Cache::put('wechat_access_token', $response->json()['access_token'], 7200); // 2小时
+            } else {
+                Log::error('Wechat消息推送异常:获取access_token失败!'.PHP_EOL.'携带访问参数:'.$response->body());
+                abort(400);
+            }
+        }
+    }
+
+    public function send($notifiable, Notification $notification)
+    {
+        $message = $notification->toCustom($notifiable);
+
+        $url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token='.$this->access_token;
+        $response = Http::timeout(15)->post($url, [
+            'touser'                   => '@all',
+            'agentid'                  => sysConfig('wechat_aid'),
+            'msgtype'                  => 'text',
+            'text'                     => ['content' => $message['content']],
+            'duplicate_check_interval' => 600,
+        ]);
+
+        // 发送成功
+        if ($response->ok()) {
+            $ret = $response->json();
+            if (! $ret['errcode'] && $ret['errmsg'] === 'ok') {
+                Helpers::addNotificationLog($message['title'], $message['content'], 5);
+
+                return $ret;
+            }
+            // 发送失败
+            Helpers::addNotificationLog($message['title'], $message['content'], 5, 'admin', -1, $ret ? $ret['errmsg'] : '未知');
+
+            return false;
+        }
+        // 发送错误
+        Log::error('Wechat消息推送异常:'.var_export($response, true));
+
+        return false;
+    }
+}

+ 1 - 1
app/Components/Helpers.php

@@ -156,7 +156,7 @@ class Helpers
      *
      * @return int
      */
-    public static function addNotificationLog(string $title, string $content, int $type, $address = 'admin', $status = 1, $error = ''): int
+    public static function addNotificationLog(string $title, string $content, int $type, string $address = 'admin', int $status = 1, string $error = ''): int
     {
         $log = new NotificationLog();
         $log->type = $type;

+ 12 - 0
app/Http/Controllers/Admin/SystemController.php

@@ -3,7 +3,10 @@
 namespace App\Http\Controllers\Admin;
 
 use App\Channels\BarkChannel;
+use App\Channels\PushPlusChannel;
 use App\Channels\ServerChanChannel;
+use App\Channels\TgChatChannel;
+use App\Channels\WeChatChannel;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\SystemRequest;
 use App\Models\Config;
@@ -164,6 +167,15 @@ class SystemController extends Controller
             case 'telegram':
                 Notification::sendNow(Auth::getUser(), new Custom($data[0], $data[1]), [TelegramChannel::class]);
                 break;
+            case 'weChat':
+                Notification::sendNow(Auth::getUser(), new Custom($data[0], $data[1]), [WeChatChannel::class]);
+                break;
+            case 'tgChat':
+                Notification::sendNow(Auth::getUser(), new Custom($data[0], $data[1]), [TgChatChannel::class]);
+                break;
+            case 'pushPlus':
+                Notification::sendNow(Auth::getUser(), new Custom($data[0], $data[1]), [PushPlusChannel::class]);
+                break;
             default:
                 return Response::json(['status' => 'fail', 'message' => '未知渠道']);
         }

+ 5 - 7
app/Http/Controllers/Gateway/AbstractPayment.php

@@ -59,19 +59,17 @@ abstract class AbstractPayment
      *
      * @return string md5加密后的数据
      */
-    protected function aliStyleSign(array $data, string $key, $filter = true): string
-    {
-        // 剃离sign,sign_type,空值
-        unset($data['sign'], $data['sign_type']);
+    protected function aliStyleSign(array $data, string $key, bool $filter = true): string
+    { // https://opendocs.alipay.com/open/common/104741
+        unset($data['sign'], $data['sign_type']); // 筛选 剃离sign,sign_type,空值
         if ($filter) {
             $data = array_filter($data);
         }
 
-        // 排序
-        ksort($data, SORT_STRING);
+        ksort($data, SORT_STRING); // 排序
         reset($data);
 
-        return md5(urldecode(http_build_query($data)).$key);
+        return md5(urldecode(http_build_query($data)).$key); // 拼接
     }
 
     protected function paymentReceived(string $tradeNo)

+ 6 - 0
app/Models/NotificationLog.php

@@ -11,4 +11,10 @@ class NotificationLog extends Model
 {
     protected $table = 'notification_log';
     protected $guarded = [];
+
+    // 通知类型
+    public function getTypeLabelAttribute(): string
+    {
+        return config('common.notification.labels')[$this->attributes['type']] ?? '未知';
+    }
 }

+ 2 - 4
app/Observers/ConfigObserver.php

@@ -9,10 +9,8 @@ class ConfigObserver
 {
     public function updated(Config $config) // 更新设定
     {
-        if (config('app.debug')) {
-            Artisan::call('optimize:clear');
-        } else {
-            Artisan::call('optimize:clear');
+        Artisan::call('optimize:clear');
+        if (! config('app.debug')) {
             Artisan::call('optimize');
         }
     }

+ 6 - 0
app/Providers/SettingServiceProvider.php

@@ -3,7 +3,10 @@
 namespace App\Providers;
 
 use App\Channels\BarkChannel;
+use App\Channels\PushPlusChannel;
 use App\Channels\ServerChanChannel;
+use App\Channels\TgChatChannel;
+use App\Channels\WeChatChannel;
 use App\Models\Config;
 use Cache;
 use Illuminate\Support\ServiceProvider;
@@ -70,7 +73,10 @@ class SettingServiceProvider extends ServiceProvider
                 'telegram'   => TelegramChannel::class,
                 'beary'      => BearyChatChannel::class,
                 'bark'       => BarkChannel::class,
+                'pushPlus'   => PushPlusChannel::class,
                 'serverChan' => ServerChanChannel::class,
+                'tgChat'     => TgChatChannel::class,
+                'weChat'     => WeChatChannel::class,
             ] as $key => $channel
         ) {
             $index = array_search($key, $channels, true);

+ 12 - 12
app/helpers.php

@@ -1,20 +1,20 @@
 <?php
 
-define('KB', 1024);
-define('MB', 1048576);
-define('GB', 1073741824);
-define('TB', 1099511627776);
-define('PB', 1125899906842624);
+const KB = 1024;
+const MB = 1048576;
+const GB = 1073741824;
+const TB = 1099511627776;
+const PB = 1125899906842624;
 
-define('Minute', 60);
-define('Hour', 3600);
-define('Day', 86400);
+const Minute = 60;
+const Hour = 3600;
+const Day = 86400;
 
-define('Mbps', 125000);
+const Mbps = 125000;
 
 // base64加密(处理URL)
 if (! function_exists('base64url_encode')) {
-    function base64url_encode($data)
+    function base64url_encode($data): string
     {
         return strtr(base64_encode($data), ['+' => '-', '/' => '_', '=' => '']);
     }
@@ -30,7 +30,7 @@ if (! function_exists('base64url_decode')) {
 
 // 根据流量值自动转换单位输出
 if (! function_exists('flowAutoShow')) {
-    function flowAutoShow($value)
+    function flowAutoShow($value): string
     {
         $value = abs($value);
         if ($value >= PB) {
@@ -59,7 +59,7 @@ if (! function_exists('flowAutoShow')) {
 
 // 秒转时间
 if (! function_exists('seconds2time')) {
-    function seconds2time($seconds)
+    function seconds2time($seconds): string
     {
         $day = floor($seconds / Day);
         $hour = floor(($seconds % Day) / Hour);

+ 14 - 1
config/common.php

@@ -26,7 +26,7 @@ return [
         ],
     ],
 
-    'oauth'          => [
+    'oauth' => [
         'labels' => [
             'facebook'  => 'Facebook',
             'twitter'   => 'Twitter',
@@ -48,10 +48,23 @@ return [
             'telegram'  => 'fa-telegram',
         ],
     ],
+
     'network_status' => [
         1 => '✔️ 通讯正常',
         2 => '🛑 海外阻断',
         3 => '🛑 国内阻断',
         4 => '❌ 断连',
     ],
+
+    'notification' => [
+        'labels' => [
+            1 => '邮件',
+            2 => 'ServerChan',
+            3 => 'Bark',
+            4 => 'Telegram',
+            5 => '微信企业',
+            6 => 'TG酱',
+            7 => 'PushPlus',
+        ],
+    ],
 ];

+ 27 - 0
database/migrations/2021_08_26_231620_more_notification.php

@@ -0,0 +1,27 @@
+<?php
+
+use App\Models\Config;
+use Illuminate\Database\Migrations\Migration;
+
+class MoreNotification extends Migration
+{
+    protected $configs = [
+        'wechat_aid',
+        'wechat_secret',
+        'wechat_cid',
+        'tg_chat_token',
+        'pushplus_token',
+    ];
+
+    public function up()
+    {
+        foreach ($this->configs as $config) {
+            Config::insert(['name' => $config]);
+        }
+    }
+
+    public function down()
+    {
+        Config::destroy($this->configs);
+    }
+}

+ 24 - 8
resources/views/admin/config/system.blade.php

@@ -154,11 +154,20 @@
                         </x-system.tab-pane>
                         <x-system.tab-pane id="notify">
                             <x-system.input-test title="SCKEY" :value="$server_chan_key" code="server_chan_key" help='启用ServerChan,请务必填入本值(<a href=https://sc.ftqq.com
-                                    target=_blank>申请SCKEY</a>)' holder="请到ServerChan申请" test="serverChan"/>
+                                    target=_blank>申请 SCKEY</a>)' holder="请到ServerChan申请" test="serverChan"/>
                             <x-system.input-test title="Bark设备号" :value="$bark_key" code="bark_key" holder="安装并打开Bark后取得" type="url"
                                                  help="推送消息到iOS设备,需要在iOS设备里装一个名为Bark的应用,取网址后的一长串代码,启用Bark,请务必填入本值" test="bark"/>
                             <x-system.input-test title="Telegram Token" :value="$telegram_token" code="telegram_token" help="找 <a href=https://t.me/BotFather
                                     target=_blank>@BotFather</a> 申请机器人" test="telegram"/>
+                            <x-system.input title="微信企业ID" :value="$wechat_cid" code="wechat_cid"
+                                            help="获取<a href=https://work.weixin.qq.com/wework_admin/frame#profile target=_blank>我的企业</a>中的企业ID"/>
+                            <x-system.input title="微信企业应用ID" :value="$wechat_aid" code="wechat_aid" holder="应用的AgentId"
+                                            help="在<a href=https://work.weixin.qq.com/wework_admin/frame#apps arget=_blank>应用管理</a>自建中创建应用 - AgentId"/>
+                            <x-system.input-test title="微信企业应用密钥" :value="$wechat_secret" code="wechat_secret" help='应用的Secret(可能需要下载企业微信才能查看)' holder="应用的Secret" test="weChat"/>
+                            <x-system.input-test title="TG酱Token" :value="$tg_chat_token" code="tg_chat_token" help='启用TG酱,请务必填入本值(<a href=https://t.me/realtgchat_bot
+                                    target=_blank>申请 Token</a>)' holder="请到Telegram申请" test="tgChat"/>
+                            <x-system.input-test title="PushPlus Token" :value="$pushplus_token" code="pushplus_token" help='启用PushPlus,请务必填入本值(<a href=https://www.pushplus.plus/push1.html
+                                    target=_blank>申请 Token</a>)' holder="请到ServerChan申请" test="pushPlus"/>
                             <x-system.switch title="PushBear" code="is_push_bear" :check="$is_push_bear"
                                              help='使用PushBear推送微信消息给用户(<a href="https://pushbear.ftqq.com/admin/#/signin" target="_blank">创建消息通道</a>)'/>
                             <x-system.input title="PushBear SendKey" :value="$push_bear_send_key" code="push_bear_send_key" help="启用PushBear,请务必填入本值" holder="创建消息通道后即可获取"/>
@@ -170,20 +179,25 @@
                             <x-system.select title="流量耗尽通知" code="data_exhaust_notification" help="通知用户流量即将耗尽" multiple="1" :list="['邮箱' => 'mail', '站内通知' => 'database']"/>
                             <x-system.input-limit title="流量警告阈值" code="traffic_warning_percent" :value="$traffic_warning_percent" unit="%" help="【流量耗尽通知】开始阈值,每日通知用户"/>
                             <x-system.select title="节点离线提醒" code="node_offline_notification" help="每10分钟检测节点离线并提醒管理员" multiple="1"
-                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' =>
+                                             'tgChat', 'PushPlus' => 'pushPlus']"/>
                             <x-system.input-limit title="离线提醒次数" code="offline_check_times" :value="$offline_check_times" unit="次" help="24小时内提醒n次后不再提醒"/>
                             <x-system.select title="节点阻断提醒" code="node_blocked_notification" help="每小时检测节点是否被阻断并提醒管理员" multiple="1"
-                                             :list="['邮箱' => 'mail', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' => 'tgChat', 'PushPlus'
+                                             => 'pushPlus']"/>
                             <x-system.input-limit title="阻断检测提醒" code="detection_check_times" :value="$detection_check_times" max="12" unit="次"
                                                   help="提醒N次后自动下线节点,为0时不限制,不超过12"/>
                             <x-system.select title="支付成功通知" code="payment_received_notification" help="用户支付订单后通知用户订单状态" multiple="1"
                                              :list="['邮箱' => 'mail', '站内通知' => 'database', 'Telegram' => 'telegram']"/>
                             <x-system.select title="工单关闭通知" code="ticket_closed_notification" help="工单关闭通知用户" multiple="1"
-                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' => 
+                                             'tgChat', 'PushPlus' => 'pushPlus']"/>
                             <x-system.select title="新工单通知" code="ticket_created_notification" help="新工单通知管理/用户,取决于谁创建了新工单" multiple="1"
-                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' =>
+                                             'tgChat', 'PushPlus' => 'pushPlus']"/>
                             <x-system.select title="工单回复通知" code="ticket_replied_notification" help="工单回复通知对方" multiple="1"
-                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' =>
+                                             'tgChat', 'PushPlus' => 'pushPlus']"/>
                         </x-system.tab-pane>
                         <x-system.tab-pane id="auto">
                             <x-system.switch title="自动清除日志" code="is_clear_log" :check="$is_clear_log" help='(推荐)启用后自动清除无用日志'/>
@@ -192,13 +206,15 @@
                             <x-system.input-limit title="订阅请求阈值" code="subscribe_ban_times" :value="$subscribe_ban_times" help="24小时内订阅链接请求次数限制"/>
                             <x-system.switch title="异常自动封号" code="is_traffic_ban" :check="$is_traffic_ban" help='1小时内流量超过异常阈值则自动封号(仅禁用代理)'/>
                             <x-system.select title="流量异常通知" code="data_anomaly_notification" help="1小时内流量超过异常阈值通知超管" multiple="1"
-                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' =>
+                                             'tgChat', 'PushPlus' => 'pushPlus']"/>
                             <x-system.input-limit title="流量异常阈值" code="traffic_ban_value" :value="$traffic_ban_value" min="1" unit="GB" help="1小时内超过该值,则触发自动封号"/>
                             <x-system.input-limit title="封号时长" code="traffic_ban_time" :value="$traffic_ban_time" unit="分钟" help="触发流量异常导致用户被封禁的时长,到期后自动解封"/>
                             <x-system.switch title="端口回收机制" code="auto_release_port" :check="$auto_release_port" help="被封禁/过期{{config('tasks.release_port')}}天的账号端口自动释放"/>
                             <x-system.switch title="过期自动封禁" code="is_ban_status" :check="$is_ban_status" help="(慎重)封禁整个账号会重置账号的所有数据且会导致用户无法登录,不开启状态下只封禁用户代理"/>
                             <x-system.select title="节点使用报告" code="node_daily_notification" help="报告各节点流量昨日消耗情况" multiple="1"
-                                             :list="['邮箱' => 'mail', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram']"/>
+                                             :list="['邮箱' => 'mail', 'ServerChan' => 'serverChan', 'Telegram' => 'telegram', '微信企业' => 'weChat', 'TG酱' =>
+                                             'tgChat', 'PushPlus' => 'pushPlus']"/>
                         </x-system.tab-pane>
                         <x-system.tab-pane id="other">
                             @if($errors->any())

+ 4 - 5
resources/views/admin/logs/notification.blade.php

@@ -16,10 +16,9 @@
                     <div class="form-group col-lg-2 col-sm-4">
                         <select class="form-control" name="type" id="type" onchange="this.form.submit()">
                             <option value="" hidden>类型</option>
-                            <option value="1">邮件</option>
-                            <option value="2">ServerChan</option>
-                            <option value="3">Bark</option>
-                            <option value="4">Telegram</option>
+                            @foreach(config('common.notification.labels') as $key => $value)
+                                <option value="{{$key}}">{{$value}}</option>
+                            @endforeach
                         </select>
                     </div>
                     <div class="form-group col-lg-1 col-sm-4 btn-group">
@@ -43,7 +42,7 @@
                     @foreach($notificationLogs as $log)
                         <tr>
                             <td> {{$log->id}} </td>
-                            <td> {{$log->type === 1 ? 'Email' : ($log->type === 2? 'ServerChan': 'Bark')}} </td>
+                            <td> {{$log->type_label}} </td>
                             <td> {{$log->address}} </td>
                             <td> {{$log->title}} </td>
                             <td> {{$log->content}} </td>