Browse Source

feat(XCat):可拓展自定义命令,去除重复\无用的方法

GeekQuerxy 5 years ago
parent
commit
11fbb0ed95

+ 83 - 0
src/Command/Backup.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Command;
+
+use App\Services\Mail;
+use App\Utils\Telegram;
+use Exception;
+use RuntimeException;
+
+class Backup extends Command
+{
+    public $description = ''
+        . '├─=: php xcat Backup [选项]' . PHP_EOL
+        . '│ ├─ full                    - 整体数据备份' . PHP_EOL
+        . '│ ├─ simple                  - 只备份核心数据' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if ($methodName == 'full') {
+                $this->backup(true);
+            } else {
+                $this->backup(false);
+            }
+        }
+    }
+
+    public function backup($full = false)
+    {
+        ini_set('memory_limit', '-1');
+        $to = $_ENV['auto_backup_email'];
+        if ($to == null) {
+            return false;
+        }
+        if (!mkdir('/tmp/ssmodbackup/') && !is_dir('/tmp/ssmodbackup/')) {
+            throw new RuntimeException(sprintf('Directory "%s" was not created', '/tmp/ssmodbackup/'));
+        }
+        $db_address_array = explode(':', $_ENV['db_host']);
+        if ($full) {
+            system('mysqldump --user=' . $_ENV['db_username'] . ' --password=' . $_ENV['db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . ' ' . $_ENV['db_database'] . ' > /tmp/ssmodbackup/mod.sql');
+        } else {
+            system(
+                'mysqldump --user=' . $_ENV['db_username'] . ' --password=' . $_ENV['db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . ' ' . $_ENV['db_database'] . ' announcement auto blockip bought code coupon disconnect_ip link login_ip payback radius_ban shop speedtest ss_invite_code ss_node ss_password_reset ticket unblockip user user_token email_verify detect_list relay paylist> /tmp/ssmodbackup/mod.sql',
+                $ret
+            );
+            system(
+                'mysqldump --opt --user=' . $_ENV['db_username'] . ' --password=' . $_ENV['db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . ' -d ' . $_ENV['db_database'] . ' alive_ip ss_node_info ss_node_online_log user_traffic_log detect_log telegram_session >> /tmp/ssmodbackup/mod.sql',
+                $ret
+            );
+            if ($_ENV['enable_radius'] == true) {
+                $db_address_array = explode(':', $_ENV['radius_db_host']);
+                system(
+                    'mysqldump --user=' . $_ENV['radius_db_user'] . ' --password=' . $_ENV['radius_db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . '' . $_ENV['radius_db_database'] . '> /tmp/ssmodbackup/radius.sql',
+                    $ret
+                );
+            }
+        }
+
+        system('cp ' . BASE_PATH . '/config/.config.php /tmp/ssmodbackup/configbak.php', $ret);
+        echo $ret;
+        system('zip -r /tmp/ssmodbackup.zip /tmp/ssmodbackup/* -P ' . $_ENV['auto_backup_passwd'], $ret);
+        $subject = $_ENV['appName'] . '-备份成功';
+        $text = '您好,系统已经为您自动备份,请查看附件,用您设定的密码解压。';
+        try {
+            Mail::send($to, $subject, 'news/backup.tpl', [
+                'text' => $text
+            ], [
+                '/tmp/ssmodbackup.zip'
+            ]);
+        } catch (Exception $e) {
+            echo $e->getMessage();
+        }
+        system('rm -rf /tmp/ssmodbackup', $ret);
+        system('rm /tmp/ssmodbackup.zip', $ret);
+
+        if ($_ENV['backup_notify'] == true) {
+            Telegram::Send('备份完毕了喵~今天又是安全祥和的一天呢。');
+        }
+    }
+}

+ 30 - 0
src/Command/ChenPay.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Command;
+
+use App\Services\Gateway\ChenPay as GatewayChenPay;
+
+class ChenPay extends Command
+{
+    public $description = ''
+        . '├─=: php xcat ChenPay [选项]' . PHP_EOL
+        . '│ ├─ wxpay                   - Wechat Pay' . PHP_EOL
+        . '│ ├─ alipay                  - AliPay' . PHP_EOL;
+
+    public function boot()
+    {
+        if (!isset($this->argv[2])) {
+            echo $this->description;
+        } else {
+            switch ($this->argv[2]) {
+                case ('alipay'):
+                    return (new GatewayChenPay())->AliPayListen();
+                case ('wxpay'):
+                    return (new GatewayChenPay())->WxPayListen();
+                default:
+                    echo '方法不存在.' . PHP_EOL;
+                    break;
+            }
+        }
+    }
+}

+ 20 - 0
src/Command/Command.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Command;
+
+/**
+ * - 可添加自定义命令
+ * - 如需使用 $argv 请继承此类
+ * - 无论是否继承都需实现 boot 方法
+ */
+abstract class Command
+{
+    public $argv;
+
+    public function __construct($argv)
+    {
+        $this->argv = $argv;
+    }
+
+    abstract public function boot();
+}

+ 114 - 0
src/Command/DetectBan.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace App\Command;
+
+use App\Models\{
+    User,
+    DetectLog,
+    DetectBanLog
+};
+
+class DetectBan extends Command
+{
+    public $description = '├─=: php xcat DetectBan      - 审计封禁定时任务' . PHP_EOL;
+
+    /**
+     * 审计封禁任务
+     */
+    public function boot()
+    {
+        if ($_ENV['enable_auto_detect_ban'] === false) {
+            return;
+        }
+        echo '审计封禁检查开始.' . PHP_EOL;
+        $new_logs = DetectLog::where('status', '=', 0)->orderBy('id', 'asc')->take($_ENV['auto_detect_ban_numProcess'])->get();
+        if (count($new_logs) != 0) {
+
+            $user_logs = [];
+            foreach ($new_logs as $log) {
+                // 分类各个用户的记录数量
+                if (!in_array($log->user_id, array_keys($user_logs))) {
+                    $user_logs[$log->user_id] = 0;
+                }
+                $user_logs[$log->user_id]++;
+                $log->status = 1;
+                $log->save();
+            }
+
+            foreach ($user_logs as $userid => $value) {
+                // 执行封禁
+                $user = User::find($userid);
+                if ($user == null) {
+                    continue;
+                }
+                $user->all_detect_number += $value;
+                $user->save();
+
+                if ($user->enable == 0 || ($user->is_admin && $_ENV['auto_detect_ban_allow_admin'] === true) || in_array($user->id, $_ENV['auto_detect_ban_allow_users'])) {
+                    // 如果用户已被封禁
+                    // 如果用户是管理员
+                    // 如果属于钦定用户
+                    // 则跳过
+                    continue;
+                }
+
+                if ($_ENV['auto_detect_ban_type'] == 1) {
+                    $last_DetectBanLog      = DetectBanLog::where('user_id', $userid)->orderBy('id', 'desc')->first();
+                    $last_all_detect_number = ($last_DetectBanLog == null ? 0 : (int) $last_DetectBanLog->all_detect_number);
+                    $detect_number          = ($user->all_detect_number - $last_all_detect_number);
+                    if ($detect_number >= $_ENV['auto_detect_ban_number']) {
+                        $last_detect_ban_time               = $user->last_detect_ban_time;
+                        $end_time                           = date('Y-m-d H:i:s');
+                        $user->enable                       = 0;
+                        $user->last_detect_ban_time         = $end_time;
+                        $user->save();
+                        $DetectBanLog                       = new DetectBanLog();
+                        $DetectBanLog->user_name            = $user->user_name;
+                        $DetectBanLog->user_id              = $user->id;
+                        $DetectBanLog->email                = $user->email;
+                        $DetectBanLog->detect_number        = $detect_number;
+                        $DetectBanLog->ban_time             = $_ENV['auto_detect_ban_time'];
+                        $DetectBanLog->start_time           = strtotime($last_detect_ban_time);
+                        $DetectBanLog->end_time             = strtotime($end_time);
+                        $DetectBanLog->all_detect_number    = $user->all_detect_number;
+                        $DetectBanLog->save();
+                    }
+                } else {
+                    $number = $user->all_detect_number;
+                    $tmp = 0;
+                    foreach ($_ENV['auto_detect_ban'] as $key => $value) {
+                        if ($number >= $key) {
+                            if ($key >= $tmp) {
+                                $tmp = $key;
+                            }
+                        }
+                    }
+                    if ($tmp != 0) {
+                        if ($_ENV['auto_detect_ban'][$tmp]['type'] == 'kill') {
+                            $user->kill_user();
+                        } else {
+                            $last_detect_ban_time               = $user->last_detect_ban_time;
+                            $end_time                           = date('Y-m-d H:i:s');
+                            $user->enable                       = 0;
+                            $user->last_detect_ban_time         = $end_time;
+                            $user->save();
+                            $DetectBanLog                       = new DetectBanLog();
+                            $DetectBanLog->user_name            = $user->user_name;
+                            $DetectBanLog->user_id              = $user->id;
+                            $DetectBanLog->email                = $user->email;
+                            $DetectBanLog->detect_number        = $number;
+                            $DetectBanLog->ban_time             = $_ENV['auto_detect_ban'][$tmp]['time'];
+                            $DetectBanLog->start_time           = strtotime('1989-06-04 00:05:00');
+                            $DetectBanLog->end_time             = strtotime($end_time);
+                            $DetectBanLog->all_detect_number    = $number;
+                            $DetectBanLog->save();
+                        }
+                    }
+                }
+            }
+        } else {
+            echo '- 暂无新记录.' . PHP_EOL;
+        }
+        echo '审计封禁检查结束.' . PHP_EOL;
+    }
+}

+ 115 - 0
src/Command/DetectGFW.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace App\Command;
+
+use App\Models\{
+    Node,
+    User
+};
+use App\Services\Config;
+use App\Utils\Telegram;
+
+class DetectGFW extends Command
+{
+    public $description = '├─=: php xcat DetectGFW      - 节点被墙检测定时任务' . PHP_EOL;
+
+    public function boot()
+    {
+        //节点被墙检测
+        $last_time = file_get_contents(BASE_PATH . '/storage/last_detect_gfw_time');
+        for ($count = 1; $count <= 12; $count++) {
+            if (time() - $last_time >= $_ENV['detect_gfw_interval']) {
+                $file_interval = fopen(BASE_PATH . '/storage/last_detect_gfw_time', 'wb');
+                fwrite($file_interval, time());
+                fclose($file_interval);
+                $nodes = Node::all();
+                $adminUser = User::where('is_admin', '=', '1')->get();
+                foreach ($nodes as $node) {
+                    if (
+                        $node->node_ip == '' ||
+                        $node->node_ip == null ||
+                        $node->online == false
+                    ) {
+                        continue;
+                    }
+                    $api_url = $_ENV['detect_gfw_url'];
+                    $api_url = str_replace(
+                        array('{ip}', '{port}'),
+                        array($node->node_ip, $_ENV['detect_gfw_port']),
+                        $api_url
+                    );
+                    //因为考虑到有v2ray之类的节点,所以不得不使用ip作为参数
+                    $result_tcping = false;
+                    $detect_time = $_ENV['detect_gfw_count'];
+                    for ($i = 1; $i <= $detect_time; $i++) {
+                        $json_tcping = json_decode(file_get_contents($api_url), true);
+                        if (eval('return ' . $_ENV['detect_gfw_judge'] . ';')) {
+                            $result_tcping = true;
+                            break;
+                        }
+                    }
+                    if ($result_tcping == false) {
+                        //被墙了
+                        echo ($node->id . ':false' . PHP_EOL);
+                        //判断有没有发送过邮件
+                        if ($node->gfw_block == true) {
+                            continue;
+                        }
+                        foreach ($adminUser as $user) {
+                            echo 'Send gfw mail to user: ' . $user->id . '-';
+                            $user->sendMail(
+                                $_ENV['appName'] . '-系统警告',
+                                'news/warn.tpl',
+                                [
+                                    'text' => '管理员您好,系统发现节点 ' . $node->name . ' 被墙了,请您及时处理。'
+                                ],
+                                []
+                            );
+                            $notice_text = str_replace(
+                                '%node_name%',
+                                $node->name,
+                                Config::getconfig('Telegram.string.NodeGFW')
+                            );
+                        }
+                        if (Config::getconfig('Telegram.bool.NodeGFW')) {
+                            Telegram::Send($notice_text);
+                        }
+                        $node->gfw_block = true;
+                        $node->save();
+                    } else {
+                        //没有被墙
+                        echo ($node->id . ':true' . PHP_EOL);
+                        if ($node->gfw_block == false) {
+                            continue;
+                        }
+                        foreach ($adminUser as $user) {
+                            echo 'Send gfw mail to user: ' . $user->id . '-';
+                            $user->sendMail(
+                                $_ENV['appName'] . '-系统提示',
+                                'news/warn.tpl',
+                                [
+                                    'text' => '管理员您好,系统发现节点 ' . $node->name . ' 溜出墙了。'
+                                ],
+                                []
+                            );
+                            $notice_text = str_replace(
+                                '%node_name%',
+                                $node->name,
+                                Config::getconfig('Telegram.string.NodeGFW_recover')
+                            );
+                        }
+                        if (Config::getconfig('Telegram.bool.NodeGFW_recover')) {
+                            Telegram::Send($notice_text);
+                        }
+                        $node->gfw_block = false;
+                        $node->save();
+                    }
+                }
+                break;
+            }
+
+            echo ($node->id . 'interval skip' . PHP_EOL);
+            sleep(3);
+        }
+    }
+}

+ 22 - 3
src/Command/ExtMail.php

@@ -5,9 +5,28 @@ namespace App\Command;
 
 use App\Models\User;
 
-class ExtMail
+class ExtMail extends Command
 {
-    public static function sendNoMail()
+    public $description = ''
+        . '├─=: php xcat ExtMail [选项]' . PHP_EOL
+        . '│ ├─ sendNoMail ' . PHP_EOL
+        . '│ ├─ sendOldMail' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if (method_exists($this, $methodName)) {
+                $this->$methodName();
+            } else {
+                echo '方法不存在.' . PHP_EOL;
+            }
+        }
+    }
+
+    public function sendNoMail()
     {
         $users = User::all();
         foreach ($users as $user) {
@@ -25,7 +44,7 @@ class ExtMail
         }
     }
 
-    public static function sendOldMail()
+    public function sendOldMail()
     {
         $users = User::all();
         foreach ($users as $user) {

+ 25 - 6
src/Command/FinanceMail.php

@@ -1,16 +1,35 @@
 <?php
 
-
 namespace App\Command;
 
 use App\Models\User;
 use App\Utils\Telegram;
-use Ozdemir\Datatables\Datatables;
 use App\Utils\DatatablesHelper;
+use Ozdemir\Datatables\Datatables;
 
-class FinanceMail
+class FinanceMail extends Command
 {
-    public static function sendFinanceMail_day()
+    public $description = ''
+        . '├─=: php xcat FinanceMail [选项]' . PHP_EOL
+        . '│ ├─ day                     - 日报' . PHP_EOL
+        . '│ ├─ week                    - 周报' . PHP_EOL
+        . '│ ├─ month                   - 月报' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if (method_exists($this, $methodName)) {
+                $this->$methodName();
+            } else {
+                echo '方法不存在.' . PHP_EOL;
+            }
+        }
+    }
+
+    public function day()
     {
         $datatables = new Datatables(new DatatablesHelper());
         $datatables->query(
@@ -85,7 +104,7 @@ class FinanceMail
         }
     }
 
-    public static function sendFinanceMail_week()
+    public function week()
     {
         $datatables = new Datatables(new DatatablesHelper());
         $datatables->query(
@@ -148,7 +167,7 @@ class FinanceMail
         }
     }
 
-    public static function sendFinanceMail_month()
+    public function month()
     {
         $datatables = new Datatables(new DatatablesHelper());
         $datatables->query(

+ 76 - 290
src/Command/Job.php

@@ -25,10 +25,7 @@ use App\Models\{
     TelegramSession,
     UserSubscribeLog
 };
-use App\Services\{
-    Mail,
-    Config
-};
+use App\Services\Config;
 use App\Utils\{
     GA,
     QQWry,
@@ -37,18 +34,43 @@ use App\Utils\{
     Telegram,
     DatatablesHelper
 };
-use Exception;
 use ArrayObject;
-use RuntimeException;
 use Ramsey\Uuid\Uuid;
 
-class Job
+class Job extends Command
 {
-    public static function syncnode()
+    public $description = ''
+        . '├─=: php xcat Job [选项]' . PHP_EOL
+        . '│ ├─ UserGa                  - 二次验证' . PHP_EOL
+        . '│ ├─ syncnode                - 更新节点 IP,每分钟' . PHP_EOL
+        . '│ ├─ DailyJob                - 每日任务' . PHP_EOL
+        . '│ ├─ CheckJob                - 检查任务,每分钟' . PHP_EOL
+        . '│ ├─ syncnasnode             - ???' . PHP_EOL
+        . '│ ├─ updatedownload          - 检查客户端更新' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if (method_exists($this, $methodName)) {
+                $this->$methodName();
+            } else {
+                echo '方法不存在.' . PHP_EOL;
+            }
+        }
+    }
+
+    /**
+     * 更新节点 IP,每分钟
+     *
+     * @return void
+     */
+    public function syncnode()
     {
         $nodes = Node::all();
         $allNodeID = [];
-
         foreach ($nodes as $node) {
             $allNodeID[] = $node->id;
             $nodeSort = [2, 5, 9, 999];     // 无需更新 IP 的节点类型
@@ -62,95 +84,18 @@ class Job
                 }
             }
         }
-
         // 删除无效的中转
         $allNodeID = implode(', ', $allNodeID);
         $datatables = new DatatablesHelper();
         $datatables->query('DELETE FROM `relay` WHERE `source_node_id` NOT IN(' . $allNodeID . ') OR `dist_node_id` NOT IN(' . $allNodeID . ')');
     }
 
-    public static function backup($full = false)
-    {
-        ini_set('memory_limit', '-1');
-        $to = $_ENV['auto_backup_email'];
-        if ($to == null) {
-            return false;
-        }
-        if (!mkdir('/tmp/ssmodbackup/') && !is_dir('/tmp/ssmodbackup/')) {
-            throw new RuntimeException(sprintf('Directory "%s" was not created', '/tmp/ssmodbackup/'));
-        }
-        $db_address_array = explode(':', $_ENV['db_host']);
-        if ($full) {
-            system('mysqldump --user=' . $_ENV['db_username'] . ' --password=' . $_ENV['db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . ' ' . $_ENV['db_database'] . ' > /tmp/ssmodbackup/mod.sql');
-        } else {
-            system(
-                'mysqldump --user=' . $_ENV['db_username'] . ' --password=' . $_ENV['db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . ' ' . $_ENV['db_database'] . ' announcement auto blockip bought code coupon disconnect_ip link login_ip payback radius_ban shop speedtest ss_invite_code ss_node ss_password_reset ticket unblockip user user_token email_verify detect_list relay paylist> /tmp/ssmodbackup/mod.sql',
-                $ret
-            );
-            system(
-                'mysqldump --opt --user=' . $_ENV['db_username'] . ' --password=' . $_ENV['db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . ' -d ' . $_ENV['db_database'] . ' alive_ip ss_node_info ss_node_online_log user_traffic_log detect_log telegram_session >> /tmp/ssmodbackup/mod.sql',
-                $ret
-            );
-            if ($_ENV['enable_radius'] == true) {
-                $db_address_array = explode(':', $_ENV['radius_db_host']);
-                system(
-                    'mysqldump --user=' . $_ENV['radius_db_user'] . ' --password=' . $_ENV['radius_db_password'] . ' --host=' . $db_address_array[0] . ' ' . (isset($db_address_array[1]) ? '-P ' . $db_address_array[1] : '') . '' . $_ENV['radius_db_database'] . '> /tmp/ssmodbackup/radius.sql',
-                    $ret
-                );
-            }
-        }
-
-        system('cp ' . BASE_PATH . '/config/.config.php /tmp/ssmodbackup/configbak.php', $ret);
-        echo $ret;
-        system('zip -r /tmp/ssmodbackup.zip /tmp/ssmodbackup/* -P ' . $_ENV['auto_backup_passwd'], $ret);
-        $subject = $_ENV['appName'] . '-备份成功';
-        $text = '您好,系统已经为您自动备份,请查看附件,用您设定的密码解压。';
-        try {
-            Mail::send($to, $subject, 'news/backup.tpl', [
-                'text' => $text
-            ], [
-                '/tmp/ssmodbackup.zip'
-            ]);
-        } catch (Exception $e) {
-            echo $e->getMessage();
-        }
-        system('rm -rf /tmp/ssmodbackup', $ret);
-        system('rm /tmp/ssmodbackup.zip', $ret);
-
-        if ($_ENV['backup_notify'] == true) {
-            Telegram::Send('备份完毕了喵~今天又是安全祥和的一天呢。');
-        }
-    }
-
-    public static function UserGa()
-    {
-        $users = User::all();
-        foreach ($users as $user) {
-            $ga = new GA();
-            $secret = $ga->createSecret();
-
-            $user->ga_token = $secret;
-            $user->save();
-        }
-        echo 'ok';
-    }
-
-    public static function syncnasnode()
-    {
-        $nodes = Node::all();
-        foreach ($nodes as $node) {
-            $rule = preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/", $node->server);
-            if (!$rule && (in_array($node->sort, array(0, 10, 12, 13)))) {
-                $ip = gethostbyname($node->server);
-                $node->node_ip = $ip;
-                $node->save();
-
-                Radius::AddNas($node->node_ip, $node->server);
-            }
-        }
-    }
-
-    public static function DailyJob()
+    /**
+     * 每日任务
+     *
+     * @return void
+     */
+    public function DailyJob()
     {
         ini_set('memory_limit', '-1');
         $nodes = Node::all();
@@ -263,16 +208,15 @@ class Job
             rename(BASE_PATH . '/storage/qqwry.dat.bak', BASE_PATH . '/storage/qqwry.dat');
         }
 
-        self::updatedownload();
+        $this->updatedownload();
     }
 
-    //   定时任务开启的情况下,每天自动检测有没有最新版的后端,github源来自Miku
-    public static function updatedownload()
-    {
-        system('cd ' . BASE_PATH . '/public/ssr-download/ && git pull https://github.com/xcxnig/ssr-download.git && git gc');
-    }
-
-    public static function CheckJob()
+    /**
+     * 检查任务,每分钟
+     *
+     * @return void
+     */
+    public function CheckJob()
     {
         //在线人数检测
         $users = User::where('node_connector', '>', 0)->get();
@@ -772,18 +716,14 @@ class Job
         }
 
         if ($_ENV['enable_telegram'] === true) {
-            self::Telegram();
-        }
-
-        if ($_ENV['enable_auto_detect_ban'] === true) {
-            self::DetectBan();
+            $this->Telegram();
         }
     }
 
     /**
      * Telegram 任务
      */
-    public static function Telegram(): void
+    public function Telegram(): void
     {
         # 删除 tg 消息
         $TelegramTasks = TelegramTasks::where('type', 1)->where('executetime', '<', time())->get();
@@ -795,199 +735,45 @@ class Job
     }
 
     /**
-     * 审计封禁任务
+     * 定时任务开启的情况下,每天自动检测有没有最新版的后端,github源来自Miku
+     *
+     * @return void
      */
-    public static function DetectBan(): void
+    public function updatedownload()
     {
-        echo '审计封禁检查开始.' . PHP_EOL;
-        $new_logs = DetectLog::where('status', '=', 0)->orderBy('id', 'asc')->take($_ENV['auto_detect_ban_numProcess'])->get();
-        if (count($new_logs) != 0) {
-
-            $user_logs = [];
-            foreach ($new_logs as $log) {
-                // 分类各个用户的记录数量
-                if (!in_array($log->user_id, array_keys($user_logs))) {
-                    $user_logs[$log->user_id] = 0;
-                }
-                $user_logs[$log->user_id]++;
-                $log->status = 1;
-                $log->save();
-            }
-
-            foreach ($user_logs as $userid => $value) {
-                // 执行封禁
-                $user = User::find($userid);
-                if ($user == null) {
-                    continue;
-                }
-                $user->all_detect_number += $value;
-                $user->save();
+        system('cd ' . BASE_PATH . '/public/ssr-download/ && git pull https://github.com/xcxnig/ssr-download.git && git gc');
+    }
 
-                if ($user->enable == 0 || ($user->is_admin && $_ENV['auto_detect_ban_allow_admin'] === true) || in_array($user->id, $_ENV['auto_detect_ban_allow_users'])) {
-                    // 如果用户已被封禁
-                    // 如果用户是管理员
-                    // 如果属于钦定用户
-                    // 则跳过
-                    continue;
-                }
+    /**
+     * 二次验证
+     *
+     * @return void
+     */
+    public function UserGa()
+    {
+        $users = User::all();
+        foreach ($users as $user) {
+            $ga = new GA();
+            $secret = $ga->createSecret();
 
-                if ($_ENV['auto_detect_ban_type'] == 1) {
-                    $last_DetectBanLog      = DetectBanLog::where('user_id', $userid)->orderBy('id', 'desc')->first();
-                    $last_all_detect_number = ($last_DetectBanLog == null ? 0 : (int) $last_DetectBanLog->all_detect_number);
-                    $detect_number          = ($user->all_detect_number - $last_all_detect_number);
-                    if ($detect_number >= $_ENV['auto_detect_ban_number']) {
-                        $last_detect_ban_time               = $user->last_detect_ban_time;
-                        $end_time                           = date('Y-m-d H:i:s');
-                        $user->enable                       = 0;
-                        $user->last_detect_ban_time         = $end_time;
-                        $user->save();
-                        $DetectBanLog                       = new DetectBanLog();
-                        $DetectBanLog->user_name            = $user->user_name;
-                        $DetectBanLog->user_id              = $user->id;
-                        $DetectBanLog->email                = $user->email;
-                        $DetectBanLog->detect_number        = $detect_number;
-                        $DetectBanLog->ban_time             = $_ENV['auto_detect_ban_time'];
-                        $DetectBanLog->start_time           = strtotime($last_detect_ban_time);
-                        $DetectBanLog->end_time             = strtotime($end_time);
-                        $DetectBanLog->all_detect_number    = $user->all_detect_number;
-                        $DetectBanLog->save();
-                    }
-                } else {
-                    $number = $user->all_detect_number;
-                    $tmp = 0;
-                    foreach ($_ENV['auto_detect_ban'] as $key => $value) {
-                        if ($number >= $key) {
-                            if ($key >= $tmp) {
-                                $tmp = $key;
-                            }
-                        }
-                    }
-                    if ($tmp != 0) {
-                        if ($_ENV['auto_detect_ban'][$tmp]['type'] == 'kill') {
-                            $user->kill_user();
-                        } else {
-                            $last_detect_ban_time               = $user->last_detect_ban_time;
-                            $end_time                           = date('Y-m-d H:i:s');
-                            $user->enable                       = 0;
-                            $user->last_detect_ban_time         = $end_time;
-                            $user->save();
-                            $DetectBanLog                       = new DetectBanLog();
-                            $DetectBanLog->user_name            = $user->user_name;
-                            $DetectBanLog->user_id              = $user->id;
-                            $DetectBanLog->email                = $user->email;
-                            $DetectBanLog->detect_number        = $number;
-                            $DetectBanLog->ban_time             = $_ENV['auto_detect_ban'][$tmp]['time'];
-                            $DetectBanLog->start_time           = strtotime('1989-06-04 00:05:00');
-                            $DetectBanLog->end_time             = strtotime($end_time);
-                            $DetectBanLog->all_detect_number    = $number;
-                            $DetectBanLog->save();
-                        }
-                    }
-                }
-            }
-        } else {
-            echo '- 暂无新记录.' . PHP_EOL;
+            $user->ga_token = $secret;
+            $user->save();
         }
-        echo '审计封禁检查结束.' . PHP_EOL;
+        echo 'ok';
     }
 
-    public static function detectGFW()
+    public function syncnasnode()
     {
-        //节点被墙检测
-        $last_time = file_get_contents(BASE_PATH . '/storage/last_detect_gfw_time');
-        for ($count = 1; $count <= 12; $count++) {
-            if (time() - $last_time >= $_ENV['detect_gfw_interval']) {
-                $file_interval = fopen(BASE_PATH . '/storage/last_detect_gfw_time', 'wb');
-                fwrite($file_interval, time());
-                fclose($file_interval);
-                $nodes = Node::all();
-                $adminUser = User::where('is_admin', '=', '1')->get();
-                foreach ($nodes as $node) {
-                    if (
-                        $node->node_ip == '' ||
-                        $node->node_ip == null ||
-                        $node->online == false
-                    ) {
-                        continue;
-                    }
-                    $api_url = $_ENV['detect_gfw_url'];
-                    $api_url = str_replace(
-                        array('{ip}', '{port}'),
-                        array($node->node_ip, $_ENV['detect_gfw_port']),
-                        $api_url
-                    );
-                    //因为考虑到有v2ray之类的节点,所以不得不使用ip作为参数
-                    $result_tcping = false;
-                    $detect_time = $_ENV['detect_gfw_count'];
-                    for ($i = 1; $i <= $detect_time; $i++) {
-                        $json_tcping = json_decode(file_get_contents($api_url), true);
-                        if (eval('return ' . $_ENV['detect_gfw_judge'] . ';')) {
-                            $result_tcping = true;
-                            break;
-                        }
-                    }
-                    if ($result_tcping == false) {
-                        //被墙了
-                        echo ($node->id . ':false' . PHP_EOL);
-                        //判断有没有发送过邮件
-                        if ($node->gfw_block == true) {
-                            continue;
-                        }
-                        foreach ($adminUser as $user) {
-                            echo 'Send gfw mail to user: ' . $user->id . '-';
-                            $user->sendMail(
-                                $_ENV['appName'] . '-系统警告',
-                                'news/warn.tpl',
-                                [
-                                    'text' => '管理员您好,系统发现节点 ' . $node->name . ' 被墙了,请您及时处理。'
-                                ],
-                                []
-                            );
-                            $notice_text = str_replace(
-                                '%node_name%',
-                                $node->name,
-                                Config::getconfig('Telegram.string.NodeGFW')
-                            );
-                        }
-                        if (Config::getconfig('Telegram.bool.NodeGFW')) {
-                            Telegram::Send($notice_text);
-                        }
-                        $node->gfw_block = true;
-                        $node->save();
-                    } else {
-                        //没有被墙
-                        echo ($node->id . ':true' . PHP_EOL);
-                        if ($node->gfw_block == false) {
-                            continue;
-                        }
-                        foreach ($adminUser as $user) {
-                            echo 'Send gfw mail to user: ' . $user->id . '-';
-                            $user->sendMail(
-                                $_ENV['appName'] . '-系统提示',
-                                'news/warn.tpl',
-                                [
-                                    'text' => '管理员您好,系统发现节点 ' . $node->name . ' 溜出墙了。'
-                                ],
-                                []
-                            );
-                            $notice_text = str_replace(
-                                '%node_name%',
-                                $node->name,
-                                Config::getconfig('Telegram.string.NodeGFW_recover')
-                            );
-                        }
-                        if (Config::getconfig('Telegram.bool.NodeGFW_recover')) {
-                            Telegram::Send($notice_text);
-                        }
-                        $node->gfw_block = false;
-                        $node->save();
-                    }
-                }
-                break;
-            }
+        $nodes = Node::all();
+        foreach ($nodes as $node) {
+            $rule = preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/", $node->server);
+            if (!$rule && (in_array($node->sort, array(0, 10, 12, 13)))) {
+                $ip = gethostbyname($node->server);
+                $node->node_ip = $ip;
+                $node->save();
 
-            echo ($node->id . 'interval skip' . PHP_EOL);
-            sleep(3);
+                Radius::AddNas($node->node_ip, $node->server);
+            }
         }
     }
 }

+ 17 - 15
src/Command/PortAutoChange.php

@@ -15,12 +15,14 @@ use App\Models\{
 };
 use App\Utils\URL;
 
-class PortAutoChange
+class PortAutoChange extends Command
 {
+    public $description = '├─=: php xcat PortAutoChange - 端口被墙则自动更换端口' . PHP_EOL;
+
     /**
      *  配置
      */
-    private const Config = [
+    private $Config = [
         // 取端口范围最小值,新的端口将是之间的随机数
         'port_min' => 23333,
 
@@ -38,7 +40,7 @@ class PortAutoChange
         'exception_node_id' => array(),
     ];
 
-    public static function index()
+    public function boot()
     {
         $gfw_port_nodes = [];
         $nodes = Node::where(
@@ -63,14 +65,14 @@ class PortAutoChange
             foreach ($mu_nodes as $mu_node) {
                 $mu_user = User::where('enable', 1)->where('is_multi_user', '<>', 0)->where('port', '=', $mu_node->server)->first();
                 if ($mu_user == null) continue;
-                $port = self::OutPort($node->server, $mu_node->server);
+                $port = $this->OutPort($node->server, $mu_node->server);
                 $api_url = $_ENV['detect_gfw_url'];
                 $api_url = str_replace(
                     array('{ip}', '{port}'),
                     array($node->node_ip, $port),
                     $api_url
                 );
-                $result_tcping = self::DetectPort($api_url);
+                $result_tcping = $this->DetectPort($api_url);
                 if ($result_tcping) continue;
                 $gfw_port_nodes[$mu_node->server][] = $node->id;
                 echo ('#' . $node->id . ' --- ' . $node->name . ' --- ' . $port . ' 端口不通' . PHP_EOL);
@@ -100,14 +102,14 @@ class PortAutoChange
                 ->where('type', '1')
                 ->get();
             for ($i = 0; $i <= 10; $i++) {
-                $new_port = rand((int) self::Config['port_min'], (int) self::Config['port_max']);
+                $new_port = rand((int) $this->Config['port_min'], (int) $this->Config['port_max']);
                 if (Node::where('sort', 9)->where('server', '=', $new_port)->first() == null && User::where('port', '=', $new_port)->first() == null) {
                     break;
                 }
             }
             $number = (count($array) / count($mu_port_nodes)) * 100;
-            if ($number >= self::Config['mu_node_port_change_percent']) {
-                echo ('超过百分比:' . $number . '%'. PHP_EOL);
+            if ($number >= $this->Config['mu_node_port_change_percent']) {
+                echo ('超过百分比:' . $number . '%' . PHP_EOL);
                 echo ('#' . $mu_node->id . ' - 单端口承载节点 - ' . $mu_node->name . ' - 更换了新的端口 ' . $new_port . PHP_EOL);
                 $mu_node->server = $new_port;
                 $mu_node->save();
@@ -115,13 +117,13 @@ class PortAutoChange
                 $mu_user->port = $new_port;
                 $mu_user->save();
                 foreach ($mu_port_nodes as $mu_port_node) {
-                    $node_port = self::OutPort($mu_port_node->server, $port);
-                    if (in_array($mu_port_node->id, $array) && !in_array($mu_port_node->id, self::Config['exception_node_id'])) {
+                    $node_port = $this->OutPort($mu_port_node->server, $port);
+                    if (in_array($mu_port_node->id, $array) && !in_array($mu_port_node->id, $this->Config['exception_node_id'])) {
                         if ($node_port != $port) {
                             if ($node_port == $new_port) {
                                 if (strpos($mu_port_node->server, ($port . '#')) !== false) {
                                     for ($i = 0; $i <= 10; $i++) {
-                                        $new_mu_node_port = rand((int) self::Config['port_min'], (int) self::Config['port_max']);
+                                        $new_mu_node_port = rand((int) $this->Config['port_min'], (int) $this->Config['port_max']);
                                         if ($new_mu_node_port != $new_port && Node::where('port', '=', $new_mu_node_port)->first() == null && User::where('port', '=', $new_mu_node_port)->first() == null) {
                                             break;
                                         }
@@ -160,9 +162,9 @@ class PortAutoChange
                 }
             } else {
                 foreach ($array as $node_id) {
-                    if (in_array($node_id, self::Config['exception_node_id'])) continue;
+                    if (in_array($node_id, $this->Config['exception_node_id'])) continue;
                     $node = Node::find($node_id);
-                    $node_port = self::OutPort($node->server, $port);
+                    $node_port = $this->OutPort($node->server, $port);
                     if ($node_port != $port) {
                         if (strpos($node->server, ('#' . $node_port)) !== false) {
                             echo ('#' . $node->id . ' - 节点 - ' . $node->name . ' - 端口从' . $node_port . '偏移到了新的端口 ' . $new_port . PHP_EOL);
@@ -186,7 +188,7 @@ class PortAutoChange
         }
     }
 
-    public static function OutPort($server, $mu_port)
+    public function OutPort($server, $mu_port)
     {
         $node_port = $mu_port;
         if (strpos($server, ';') !== false) {
@@ -215,7 +217,7 @@ class PortAutoChange
         return $node_port;
     }
 
-    public static function DetectPort($api_url)
+    public function DetectPort($api_url)
     {
         $result_tcping = false;
         $detect_time = $_ENV['detect_gfw_count'];

+ 4 - 12
src/Command/DailyMail.php → src/Command/SendDiaryMail.php

@@ -1,6 +1,5 @@
 <?php
 
-
 namespace App\Command;
 
 use App\Models\User;
@@ -10,9 +9,11 @@ use App\Utils\Telegram;
 use App\Utils\Tools;
 use App\Services\Analytics;
 
-class DailyMail
+class SendDiaryMail extends Command
 {
-    public static function sendDailyMail()
+    public $description = '├─=: php xcat SendDiaryMail  - 每日流量报告' . PHP_EOL;
+
+    public function boot()
     {
         $users = User::all();
         $logs = Ann::orderBy('id', 'desc')->get();
@@ -49,13 +50,4 @@ class DailyMail
             );
         }
     }
-
-    public static function reall()
-    {
-        $users = User::all();
-        foreach ($users as $user) {
-            $user->last_day_t = ($user->u + $user->d);
-            $user->save();
-        }
-    }
 }

+ 24 - 4
src/Command/SyncRadius.php

@@ -12,8 +12,29 @@ use App\Models\TrafficLog;
 use App\Utils\Tools;
 use App\Utils\Radius;
 
-class SyncRadius
+class SyncRadius extends Command
 {
+    public $description = ''
+        . '├─=: php xcat SyncRadius [选项]' . PHP_EOL
+        . '│ ├─ syncnas  ' . PHP_EOL
+        . '│ ├─ syncvpn  ' . PHP_EOL
+        . '│ ├─ syncusers' . PHP_EOL
+        . '│ ├─ synclogin' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if (method_exists($this, $methodName)) {
+                $this->$methodName();
+            } else {
+                echo '方法不存在.' . PHP_EOL;
+            }
+        }
+    }
+
     public static function synclogin()
     {
         if ($_ENV['enable_radius'] == false) {
@@ -83,7 +104,6 @@ class SyncRadius
         }  */
     }
 
-
     public static function syncvpn()
     {
         if ($_ENV['radius_db_host'] == '') {
@@ -194,9 +214,9 @@ class SyncRadius
             if ($oldmd5 != $md5) {
                 //Restart radius
                 $myfile = fopen(BASE_PATH . '/storage/nas.md5', 'wb+') or die('Unable to open file!');
-                echo('Restarting...');
+                echo ('Restarting...');
                 system('/bin/bash /sbin/service radiusd restart', $retval);
-                echo($retval);
+                echo ($retval);
                 $txt = $md5;
                 fwrite($myfile, $txt);
                 fclose($myfile);

+ 111 - 0
src/Command/Tool.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace App\Command;
+
+class Tool extends Command
+{
+    public $description = ''
+        . '├─=: php xcat Tool [选项]' . PHP_EOL
+        . '│ ├─ initQQWry               - 下载 IP 解析库' . PHP_EOL
+        . '│ ├─ setTelegram             - 设置 Telegram 机器人' . PHP_EOL
+        . '│ ├─ initdownload            - 下载 SSR 程序至服务器' . PHP_EOL
+        . '│ ├─ detectConfigs           - 检查数据库内新增的配置' . PHP_EOL
+        . '│ ├─ initdocuments           - 下载用户使用文档至服务器' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if (method_exists($this, $methodName)) {
+                $this->$methodName();
+            } else {
+                echo '方法不存在.' . PHP_EOL;
+            }
+        }
+    }
+
+    /**
+     * 设定 Telegram Bot
+     *
+     * @return void
+     */
+    public function setTelegram()
+    {
+        if ($_ENV['use_new_telegram_bot'] === true) {
+            $WebhookUrl = ($_ENV['baseUrl'] . '/telegram_callback?token=' . $_ENV['telegram_request_token']);
+            $telegram = new \Telegram\Bot\Api($_ENV['telegram_token']);
+            $telegram->removeWebhook();
+            if ($telegram->setWebhook(['url' => $WebhookUrl])) {
+                echo ('New Bot @' . $telegram->getMe()->getUsername() . ' 设置成功!');
+            }
+        } else {
+            $bot = new \TelegramBot\Api\BotApi($_ENV['telegram_token']);
+            $ch = curl_init();
+            curl_setopt($ch, CURLOPT_URL, sprintf('https://api.telegram.org/bot%s/deleteWebhook', $_ENV['telegram_token']));
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+            $deleteWebhookReturn = json_decode(curl_exec($ch));
+            curl_close($ch);
+            if ($deleteWebhookReturn->ok && $deleteWebhookReturn->result && $bot->setWebhook($_ENV['baseUrl'] . '/telegram_callback?token=' . $_ENV['telegram_request_token']) == 1) {
+                echo ('Old Bot 设置成功!' . PHP_EOL);
+            }
+        }
+    }
+
+    /**
+     * 下载客户端
+     *
+     * @return void
+     */
+    public function initdownload()
+    {
+        system('git clone --depth=3 https://github.com/xcxnig/ssr-download.git ' . BASE_PATH . '/public/ssr-download/ && git gc', $ret);
+        echo $ret;
+    }
+
+    /**
+     * 下载使用文档
+     *
+     * @return void
+     */
+    public function initdocuments()
+    {
+        system('git clone https://github.com/GeekQuerxy/PANEL_DOC.git ' . BASE_PATH . "/public/docs/", $ret);
+        echo $ret;
+    }
+
+    /**
+     * 下载 IP 库
+     *
+     * @return void
+     */
+    public function initQQWry()
+    {
+        echo ('开始下载纯真 IP 数据库....');
+        $qqwry = file_get_contents('https://qqwry.mirror.noc.one/QQWry.Dat?from=sspanel_uim');
+        if ($qqwry != '') {
+            $fp = fopen(BASE_PATH . '/storage/qqwry.dat', 'wb');
+            if ($fp) {
+                fwrite($fp, $qqwry);
+                fclose($fp);
+                echo ('纯真 IP 数据库下载成功!');
+            } else {
+                echo ('纯真 IP 数据库保存失败!');
+            }
+        } else {
+            echo ('下载失败!请重试,或在 https://github.com/SukkaW/qqwry-mirror/issues/new 反馈!');
+        }
+    }
+
+    /**
+     * 探测新增配置
+     *
+     * @return void
+     */
+    public function detectConfigs()
+    {
+        echo \App\Services\DefaultConfig::detectConfigs();
+    }
+}

+ 10 - 9
src/Command/Update.php

@@ -3,14 +3,15 @@
 namespace App\Command;
 
 use App\Services\DefaultConfig;
+use App\Utils\DatatablesHelper;
 use Illuminate\Database\Capsule\Manager as Capsule;
 use Illuminate\Database\Schema\Blueprint;
-use App\Services\Config;
-use App\Utils\DatatablesHelper;
 
-class Update
+class Update extends Command
 {
-    public static function update($xcat)
+    public $description = '├─=: php xcat Update         - 更新并迁移配置' . PHP_EOL;
+
+    public function boot()
     {
         global $_ENV;
         $copy_result = copy(BASE_PATH . '/config/.config.php', BASE_PATH . '/config/.config.php.bak');
@@ -31,7 +32,7 @@ class Update
         echo ('客户端升级结束' . PHP_EOL);
 
         echo ('开始升级 QQWry...' . PHP_EOL);
-        $xcat->initQQWry();
+        (new Tool($this->argv))->initQQWry();
         echo ('升级 QQWry结束' . PHP_EOL);
 
         echo (PHP_EOL);
@@ -41,8 +42,8 @@ class Update
 
         //执行版本升级
         $version_old = $_ENV['version'] ?? 0;
-        self::old_to_new($version_old);
-        self::addColumns('user', 'uuid', 'TEXT', TRUE, 'NULL', 'uuid', 'passwd');
+        $this->old_to_new($version_old);
+        $this->addColumns('user', 'uuid', 'TEXT', TRUE, 'NULL', 'uuid', 'passwd');
 
         //将旧config迁移到新config上
         $migrated = array();
@@ -140,7 +141,7 @@ class Update
         system('chown -R www:www ' . BASE_PATH . '/storage');
     }
 
-    public static function addColumns($table, $columu, $type, $isnull, $default, $comment, $after)
+    public function addColumns($table, $columu, $type, $isnull, $default, $comment, $after)
     {
         $datatables = new DatatablesHelper();
         $exists = $datatables->query("SELECT COUNT(*) as cc FROM information_schema.columns WHERE `table_schema` = '" . $_ENV['db_database'] . "' AND `table_name` = '" . $table . "' AND `column_name` = '" . $columu . "'");
@@ -151,7 +152,7 @@ class Update
         $datatables->query("ALTER TABLE `" . $table . "` ADD COLUMN `" . $columu . "` " . $type . $isnull . "DEFAULT " . $default . " COMMENT '" . $comment . "' AFTER `" . $after . "`");
     }
 
-    public static function old_to_new($version_old)
+    public function old_to_new($version_old)
     {
         if ($version_old < 2) {
             // 版本 2 开始

+ 202 - 0
src/Command/User.php

@@ -0,0 +1,202 @@
+<?php
+
+namespace App\Command;
+
+use App\Models\Relay;
+use App\Models\User as ModelsUser;
+use App\Services\Config;
+use App\Utils\GA;
+use App\Utils\Hash;
+use App\Utils\Tools;
+use Exception;
+
+class User extends Command
+{
+    public $description = ''
+        . '├─=: php xcat User [选项]' . PHP_EOL
+        . '│ ├─ getCookie               - 获取指定用户的 Cookie' . PHP_EOL
+        . '│ ├─ resetPort               - 重置单个用户端口' . PHP_EOL
+        . '│ ├─ createAdmin             - 创建管理员帐号' . PHP_EOL
+        . '│ ├─ resetAllPort            - 重置所有用户端口' . PHP_EOL
+        . '│ ├─ resetTraffic            - 重置所有用户流量' . PHP_EOL
+        . '│ ├─ cleanRelayRule          - 清除所有中转规则' . PHP_EOL;
+
+    public function boot()
+    {
+        if (count($this->argv) === 2) {
+            echo $this->description;
+        } else {
+            $methodName = $this->argv[2];
+            if (method_exists($this, $methodName)) {
+                $this->$methodName();
+            } else {
+                echo '方法不存在.' . PHP_EOL;
+            }
+        }
+    }
+
+    /**
+     * 重置用户端口
+     *
+     * @return void
+     */
+    public function resetPort()
+    {
+        fwrite(STDOUT, '请输入用户id: ');
+        $user        = ModelsUser::Where('id', '=', trim(fgets(STDIN)))->first();
+        if ($user !== null) {
+            $origin_port = $user->port;
+            $user->port  = Tools::getAvPort();
+            $relay_rules = Relay::where('user_id', $user->id)->where('port', $origin_port)->get();
+            foreach ($relay_rules as $rule) {
+                $rule->port = $user->port;
+                $rule->save();
+            }
+            if ($user->save()) {
+                echo '重置成功!' . PHP_EOL;
+            }
+        } else {
+            echo 'not found user.' . PHP_EOL;
+        }
+    }
+
+    /**
+     * 重置所有用户端口
+     *
+     * @return void
+     */
+    public function resetAllPort()
+    {
+        $users = ModelsUser::all();
+        foreach ($users as $user) {
+            $origin_port = $user->port;
+            $user->port  = Tools::getAvPort();
+            echo '$origin_port=' . $origin_port . '&$user->port=' . $user->port . PHP_EOL;
+            $user->save();
+        }
+    }
+
+    /**
+     * 重置所有用户流量
+     *
+     * @return void
+     */
+    public function resetTraffic()
+    {
+        try {
+            ModelsUser::where('enable', 1)->update([
+                'd'          => 0,
+                'u'          => 0,
+                'last_day_t' => 0,
+            ]);
+        } catch (Exception $e) {
+            echo $e->getMessage();
+            return;
+        }
+        echo 'reset traffic successful' . PHP_EOL;
+    }
+
+    /**
+     * 清理所有中转规则
+     *
+     * @return void
+     */
+    public function cleanRelayRule()
+    {
+        $rules = Relay::all();
+        foreach ($rules as $rule) {
+            echo ($rule->id . "\n");
+            if ($rule->source_node_id == 0) {
+                echo ($rule->id . "被删除!\n");
+                $rule->delete();
+                continue;
+            }
+            $ruleset = Relay::where('user_id', $rule->user_id)->orwhere('user_id', 0)->get();
+            $maybe_rule_id = Tools::has_conflict_rule($rule, $ruleset, $rule->id);
+            if ($maybe_rule_id != 0) {
+                echo ($rule->id . "被删除!\n");
+                $rule->delete();
+            }
+        }
+    }
+
+    /**
+     * 创建 Admin 账户
+     *
+     * @return void
+     */
+    public function createAdmin()
+    {
+        if (count($this->argv) === 3) {
+            echo 'add admin/ 创建管理员帐号.....';
+            // ask for input
+            fwrite(STDOUT, 'Enter your email/输入管理员邮箱: ');
+            // get input
+            $email = trim(fgets(STDIN));
+            // write input back
+            fwrite(STDOUT, "Enter password for: $email / 为 $email 添加密码: ");
+            $passwd = trim(fgets(STDIN));
+            echo "Email: $email, Password: $passwd! ";
+            fwrite(STDOUT, "Press [y] to create admin..... 按下[Y]确认来确认创建管理员账户..... \n");
+            $y = trim(fgets(STDIN));
+        } elseif (count($this->argv) === 5) {
+            [,,, $email, $passwd] = $this->argv;
+            $y = 'y';
+        }
+
+        if (strtolower($y) == 'y') {
+            echo 'start create admin account';
+            // create admin user
+            // do reg user
+            $user                   = new ModelsUser();
+            $user->user_name        = 'admin';
+            $user->email            = $email;
+            $user->pass             = Hash::passwordHash($passwd);
+            $user->passwd           = Tools::genRandomChar(6);
+            $user->port             = Tools::getLastPort() + 1;
+            $user->t                = 0;
+            $user->u                = 0;
+            $user->d                = 0;
+            $user->transfer_enable  = Tools::toGB((int) Config::getconfig('Register.string.defaultTraffic'));
+            $user->invite_num       = (int) Config::getconfig('Register.string.defaultInviteNum');
+            $user->ref_by           = 0;
+            $user->is_admin         = 1;
+            $user->expire_in        = date('Y-m-d H:i:s', time() + (int) Config::getconfig('Register.string.defaultExpire_in') * 86400);
+            $user->reg_date         = date('Y-m-d H:i:s');
+            $user->money            = 0;
+            $user->im_type          = 1;
+            $user->im_value         = '';
+            $user->class            = 0;
+            $user->plan             = 'A';
+            $user->node_speedlimit  = 0;
+            $user->theme            = $_ENV['theme'];
+
+            $ga                     = new GA();
+            $secret                 = $ga->createSecret();
+            $user->ga_token         = $secret;
+            $user->ga_enable        = 0;
+
+            if ($user->save()) {
+                echo 'Successful/添加成功!' . PHP_EOL;
+            } else {
+                echo '添加失败' . PHP_EOL;
+            }
+        } else {
+            echo 'cancel' . PHP_EOL;
+        }
+    }
+
+    /**
+     * 获取 USERID 的 Cookie
+     *
+     * @return void
+     */
+    public function getCookie()
+    {
+        if (count($this->argv) === 4) {
+            $user = ModelsUser::find($this->argv[3]);
+            $expire_in = 86400 + time();
+            echo Hash::cookieHash($user->pass, $expire_in) . ' ' . $expire_in;
+        }
+    }
+}

+ 0 - 367
src/Command/XCat.php

@@ -1,367 +0,0 @@
-<?php
-
-namespace App\Command;
-
-/***
- * Class XCat
- * @package App\Command
- */
-
-use App\Models\User;
-use App\Models\Relay;
-use App\Services\Gateway\ChenPay;
-use App\Utils\Hash;
-use App\Utils\Tools;
-use App\Services\Config;
-use App\Services\DefaultConfig;
-
-use App\Utils\GA;
-use Exception;
-use TelegramBot\Api\BotApi;
-
-class XCat
-{
-    public $argv;
-
-    public function __construct($argv)
-    {
-        $this->argv = $argv;
-    }
-
-    public function boot()
-    {
-        switch ($this->argv[1]) {
-            case ('alipay'):
-                return (new ChenPay())->AliPayListen();
-            case ('wxpay'):
-                return (new ChenPay())->WxPayListen();
-            case ('createAdmin'):
-                return $this->createAdmin();
-            case ('resetTraffic'):
-                return $this->resetTraffic();
-            case ('setTelegram'):
-                return $this->setTelegram();
-            case ('initQQWry'):
-                return $this->initQQWry();
-            case ('sendDiaryMail'):
-                return DailyMail::sendDailyMail();
-            case ('sendFinanceMail_day'):
-                return FinanceMail::sendFinanceMail_day();
-            case ('sendFinanceMail_week'):
-                return FinanceMail::sendFinanceMail_week();
-            case ('sendFinanceMail_month'):
-                return FinanceMail::sendFinanceMail_month();
-            case ('reall'):
-                return DailyMail::reall();
-            case ('syncusers'):
-                return SyncRadius::syncusers();
-            case ('synclogin'):
-                return SyncRadius::synclogin();
-            case ('syncvpn'):
-                return SyncRadius::syncvpn();
-            case ('nousers'):
-                return ExtMail::sendNoMail();
-            case ('oldusers'):
-                return ExtMail::sendOldMail();
-            case ('syncnode'):
-                return Job::syncnode();
-            case ('syncnasnode'):
-                return Job::syncnasnode();
-            case ('detectGFW'):
-                return Job::detectGFW();
-            case ('syncnas'):
-                return SyncRadius::syncnas();
-            case ('dailyjob'):
-                return Job::DailyJob();
-            case ('checkjob'):
-                return Job::CheckJob();
-            case ('userga'):
-                return Job::UserGa();
-            case ('backup'):
-                return Job::backup(false);
-            case ('backupfull'):
-                return Job::backup(true);
-            case ('initdownload'):
-                return $this->initdownload();
-            case ('updatedownload'):
-                return Job::updatedownload();
-            case ('cleanRelayRule'):
-                return $this->cleanRelayRule();
-            case ('resetPort'):
-                return $this->resetPort();
-            case ('resetAllPort'):
-                return $this->resetAllPort();
-            case ('update'):
-                return Update::update($this);
-            case ('sendDailyUsageByTG'):
-                return $this->sendDailyUsageByTG();
-            case ('npmbuild'):
-                return $this->npmbuild();
-            case ('getCookie'):
-                return $this->getCookie();
-            case ('detectConfigs'):
-                return $this->detectConfigs();
-            case ('portAutoChange'):
-                return PortAutoChange::index();
-            case ('initdocuments'):
-                return $this->initdocuments();
-            default:
-                return $this->defaultAction();
-        }
-    }
-
-    public function defaultAction()
-    {
-        echo (PHP_EOL . '用法: php xcat [选项]' . PHP_EOL);
-        echo ('常用选项:' . PHP_EOL);
-        echo ('  createAdmin - 创建管理员帐号' . PHP_EOL);
-        echo ('  setTelegram - 设置 Telegram 机器人' . PHP_EOL);
-        echo ('  cleanRelayRule - 清除所有中转规则' . PHP_EOL);
-        echo ('  resetPort - 重置单个用户端口' . PHP_EOL);
-        echo ('  resetAllPort - 重置所有用户端口' . PHP_EOL);
-        echo ('  initdownload - 下载 SSR 程序至服务器' . PHP_EOL);
-        echo ('  initQQWry - 下载 IP 解析库' . PHP_EOL);
-        echo ('  resetTraffic - 重置所有用户流量' . PHP_EOL);
-        echo ('  update - 更新并迁移配置' . PHP_EOL);
-        echo ('  detectConfigs - 检查数据库内新增的配置' . PHP_EOL);
-        echo ('  initdocuments - 下载用户使用文档至服务器' . PHP_EOL);
-        echo ('  portAutoChange - [实验]  SS 单端口被墙自动换' . PHP_EOL);
-    }
-
-    public function resetPort()
-    {
-        fwrite(STDOUT, '请输入用户id: ');
-        $user = User::Where('id', '=', trim(fgets(STDIN)))->first();
-        $origin_port = $user->port;
-
-        $user->port = Tools::getAvPort();
-
-        $relay_rules = Relay::where('user_id', $user->id)->where('port', $origin_port)->get();
-        foreach ($relay_rules as $rule) {
-            $rule->port = $user->port;
-            $rule->save();
-        }
-
-        if ($user->save()) {
-            echo "重置成功!\n";
-        }
-    }
-
-    public function resetAllPort()
-    {
-        $users = User::all();
-        foreach ($users as $user) {
-            $origin_port = $user->port;
-            $user->port = Tools::getAvPort();
-            echo '$origin_port=' . $origin_port . '&$user->port=' . $user->port . "\n";
-            $user->save();
-        }
-    }
-
-    public function cleanRelayRule()
-    {
-        $rules = Relay::all();
-        foreach ($rules as $rule) {
-            echo ($rule->id . "\n");
-            if ($rule->source_node_id == 0) {
-                echo ($rule->id . "被删除!\n");
-                $rule->delete();
-                continue;
-            }
-
-            $ruleset = Relay::where('user_id', $rule->user_id)->orwhere('user_id', 0)->get();
-            $maybe_rule_id = Tools::has_conflict_rule($rule, $ruleset, $rule->id);
-            if ($maybe_rule_id != 0) {
-                echo ($rule->id . "被删除!\n");
-                $rule->delete();
-            }
-        }
-    }
-
-    public function initdownload()
-    {
-        system('git clone --depth=3 https://github.com/xcxnig/ssr-download.git ' . BASE_PATH . '/public/ssr-download/ && git gc', $ret);
-        echo $ret;
-    }
-
-    public function createAdmin()
-    {
-        if (count($this->argv) === 2) {
-            echo 'add admin/ 创建管理员帐号.....';
-            // ask for input
-            fwrite(STDOUT, 'Enter your email/输入管理员邮箱: ');
-            // get input
-            $email = trim(fgets(STDIN));
-            // write input back
-            fwrite(STDOUT, "Enter password for: $email / 为 $email 添加密码: ");
-            $passwd = trim(fgets(STDIN));
-            echo "Email: $email, Password: $passwd! ";
-            fwrite(STDOUT, "Press [y] to create admin..... 按下[Y]确认来确认创建管理员账户..... \n");
-            $y = trim(fgets(STDIN));
-        } elseif (count($this->argv) === 4) {
-            [,, $email, $passwd] = $this->argv;
-            $y = 'y';
-        }
-
-        if (strtolower($y) == 'y') {
-            echo 'start create admin account';
-            // create admin user
-            // do reg user
-            $user                   = new User();
-            $user->user_name        = 'admin';
-            $user->email            = $email;
-            $user->pass             = Hash::passwordHash($passwd);
-            $user->passwd           = Tools::genRandomChar(6);
-            $user->port             = Tools::getLastPort() + 1;
-            $user->t                = 0;
-            $user->u                = 0;
-            $user->d                = 0;
-            $user->transfer_enable  = Tools::toGB((int) Config::getconfig('Register.string.defaultTraffic'));
-            $user->invite_num       = (int) Config::getconfig('Register.string.defaultInviteNum');
-            $user->ref_by           = 0;
-            $user->is_admin         = 1;
-            $user->expire_in        = date('Y-m-d H:i:s', time() + (int) Config::getconfig('Register.string.defaultExpire_in') * 86400);
-            $user->reg_date         = date('Y-m-d H:i:s');
-            $user->money            = 0;
-            $user->im_type          = 1;
-            $user->im_value         = '';
-            $user->class            = 0;
-            $user->plan             = 'A';
-            $user->node_speedlimit  = 0;
-            $user->theme            = $_ENV['theme'];
-
-            $ga                     = new GA();
-            $secret                 = $ga->createSecret();
-            $user->ga_token         = $secret;
-            $user->ga_enable        = 0;
-
-            if ($user->save()) {
-                echo "Successful/添加成功!\n";
-                return true;
-            }
-            echo '添加失败';
-            return false;
-        }
-        echo 'cancel';
-        return false;
-    }
-
-    public function resetTraffic()
-    {
-        try {
-            User::where('enable', 1)->update([
-                'd' => 0,
-                'u' => 0,
-                'last_day_t' => 0,
-            ]);
-        } catch (Exception $e) {
-            echo $e->getMessage();
-            return false;
-        }
-        return 'reset traffic successful';
-    }
-
-    public function setTelegram()
-    {
-        if ($_ENV['use_new_telegram_bot'] === true) {
-            $WebhookUrl = ($_ENV['baseUrl'] . '/telegram_callback?token=' . $_ENV['telegram_request_token']);
-            $telegram = new \Telegram\Bot\Api($_ENV['telegram_token']);
-            $telegram->removeWebhook();
-            if ($telegram->setWebhook(['url' => $WebhookUrl])) {
-                echo ('New Bot @' . $telegram->getMe()->getUsername() . ' 设置成功!');
-            }
-        } else {
-            $bot = new BotApi($_ENV['telegram_token']);
-            $ch = curl_init();
-            curl_setopt($ch, CURLOPT_URL, sprintf('https://api.telegram.org/bot%s/deleteWebhook', $_ENV['telegram_token']));
-            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
-            $deleteWebhookReturn = json_decode(curl_exec($ch));
-            curl_close($ch);
-            if ($deleteWebhookReturn->ok && $deleteWebhookReturn->result && $bot->setWebhook($_ENV['baseUrl'] . '/telegram_callback?token=' . $_ENV['telegram_request_token']) == 1) {
-                echo ('Old Bot 设置成功!' . PHP_EOL);
-            }
-        }
-    }
-
-    public function initQQWry()
-    {
-        echo ('开始下载纯真 IP 数据库....');
-        $qqwry = file_get_contents('https://qqwry.mirror.noc.one/QQWry.Dat?from=sspanel_uim');
-        if ($qqwry != '') {
-            $fp = fopen(BASE_PATH . '/storage/qqwry.dat', 'wb');
-            if ($fp) {
-                fwrite($fp, $qqwry);
-                fclose($fp);
-                echo ('纯真 IP 数据库下载成功!');
-            } else {
-                echo ('纯真 IP 数据库保存失败!');
-            }
-        } else {
-            echo ('下载失败!请重试,或在 https://github.com/SukkaW/qqwry-mirror/issues/new 反馈!');
-        }
-    }
-
-    public function sendDailyUsageByTG()
-    {
-        $bot = new BotApi($_ENV['telegram_token']);
-        $users = User::where('telegram_id', '>', 0)->get();
-        foreach ($users as $user) {
-            $u = $user->u;
-            $d = $user->d;
-            $last_day_t = $user->last_day_t;
-            $transfer_enable = $user->transfer_enable;
-            $reply_message = '您当前的流量状况:' . PHP_EOL .
-                sprintf(
-                    '今天已使用 %s %s%%',
-                    $user->TodayusedTraffic(),
-                    number_format(($u + $d - $last_day_t) / $transfer_enable * 100, 2)
-                ) . PHP_EOL .
-                sprintf(
-                    '今天前已使用 %s %s%%',
-                    $user->LastusedTraffic(),
-                    number_format($last_day_t / $transfer_enable * 100, 2)
-                ) . PHP_EOL .
-                sprintf(
-                    '剩余 %s %s%%',
-                    $user->unusedTraffic(),
-                    number_format(($transfer_enable - ($u + $d)) / $transfer_enable * 100, 2)
-                );
-            $bot->sendMessage(
-                $user->get_user_attributes('telegram_id'),
-                $reply_message,
-                $parseMode = null,
-                $disablePreview = false,
-                $replyToMessageId = null
-            );
-        }
-    }
-
-    public function npmbuild()
-    {
-        chdir(BASE_PATH . '/uim-index-dev');
-        system('npm install');
-        system('npm run build');
-        system('cp -u ../public/vuedist/index.html ../resources/views/material/index.tpl');
-    }
-
-    public function getCookie()
-    {
-        if (count($this->argv) === 3) {
-            $user = User::find($this->argv[2]);
-            $expire_in = 86400 + time();
-            echo Hash::cookieHash($user->pass, $expire_in) . ' ' . $expire_in;
-        }
-    }
-
-    public function initdocuments()
-    {
-        system('git clone https://github.com/GeekQu/PANEL_DOC.git ' . BASE_PATH . "/public/docs/", $ret);
-        echo $ret;
-    }
-
-    public function detectConfigs()
-    {
-        echo DefaultConfig::detectConfigs();
-    }
-}

+ 37 - 3
xcat

@@ -13,7 +13,41 @@ require __DIR__ . '/app/envload.php';
 Boot::setTime();
 Boot::bootDb();
 
-use App\Command\XCat;
+if (!isset($argv[1])) {
+    $commandPath     = BASE_PATH . '/src/Command/';
+    $allCommandFiles = glob($commandPath . '*.php');
+    $allCommands = str_replace(
+        [
+            $commandPath,
+            '.php'
+        ],
+        [
+            '\\App\\Command\\',
+            ''
+        ],
+        $allCommandFiles
+    );
+    echo PHP_EOL;
+    foreach ($allCommands as $commandClass) {
+        if ($commandClass == '\\App\\Command\\Command') {
+            continue;
+        }
+        if (!class_exists($commandClass)) {
+            continue;
+        }
+        $triggerObject = new $commandClass($argv);
+        if (!property_exists($triggerObject, 'description')) {
+            continue;
+        }
+        echo $triggerObject->description . PHP_EOL;
+    }
+    return;
+}
 
-$cat = new XCat($argv);
-$cat->boot();
+$classPath = '\\App\\Command\\' . $argv[1];
+if (class_exists($classPath)) {
+    $trigger = new $classPath($argv);
+    $trigger->boot();
+} else {
+    echo 'Unable to load class: ' . $classPath . PHP_EOL;
+}